Nerva Core Release v0.2.1.0

Nerva Core Software v0.2.1.0 Released

Nerva Legacy Reborn, Point Release 1, v0.2.1.0 has been released.

This release brings several quality-of-life improvements and bug fixes to the daemon and wallet:

  • New CLI options for DNS and block tracking
  • Mining thread count indicator in daemon status
  • Wallet stability fixes
  • Multi-arch Docker images for expanded platform support
  • Android build β€” Nerva core software can now run on mobile devices!
  • CI/build infrastructure improvements since v0.2.0.0

Not a hard fork, so no deadline β€” but upgrading is recommended.

πŸ”— https://github.com/nerva-project/nerva/releases/tag/v0.2.1.0

πŸ”— https://nerva.one/#downloads

Thank you to everyone who made this release possible!

NervaOne Wallet and Miner

NervaOne v0.8.5.0 Released

NervaOne Wallet and Miner, version 0.8.5.0 has just been released.

New functionality includes the ability to run as wallet only by connecting to remote node and Net Hash Threshold. It also includes some bug fixes and other minor improvements.

You can download it from NervaOne GitHub

Or from Nerva’s website: https://nerva.one/#downloads

Thank you everybody who helped make this release possible.

Nerva docs refresh

Let’s Update Nerva’s Documentation

We need to update our docs at https://docs.nerva.one/

Here are some things that we should change, if anybody wants to work on them.

  • Refresh CLI Guide. Make it a little clearer with maybe new screenshots https://docs.nerva.one/guides/cli/
  • Add Getting started on Android instructions to that CLI Guide using Termux, or something else if there is another way.
  • Add Public Node instructions to NervaOne page: ⁠ https://docs.nerva.one/guides/nervaone/
  • Refresh NervaOne page, the pages have changed and there are some new features.
  • Anything else that you think we should update.

    Best way to do that is to clone this repo https://github.com/nerva-project/nerva-docs

    make changes and open pull request. If somebody wants to work on any of those, this will give you an opportunity to become Nerva contributor.

    Avalonia 12 crash issue

    Avalonia 12 Crashes on Older Windows Versions

    Avalonia 12 crashes on Windows Server 2012 R2 and other older systems so NervaOne Is staying on Avalonia 11.

    The error looks like this in the Windows Problem Reports dialog:

    Problem Event Name:    APPCRASH
    Application Name:      NervaOneDesktop.exe
    Fault Module Name:     KERNELBASE.dll
    Exception Code:        e0434352

      This is a .NET runtime exception (0xe0434352 = CLR exception code) that surfaces as an unhandled crash at process startup, before the application window even opens. The root cause is not a bug in NervaOne itself — it is a dependency change introduced in Avalonia 12.

      Root Cause: Avalonia 12 Bundles SkiaSharp 3.119+, Which Requires DirectX 12

      Avalonia 12 upgraded its rendering backend from SkiaSharp 2.x to SkiaSharp 3.119+. This new version of SkiaSharp dropped support for DirectX 11 and now requires DirectX 12 as its Windows rendering backend.

      DirectX 12 requires Windows 10 (version 1507 or later) at minimum. Any system running an older version of Windows does not have DirectX 12 and cannot load the SkiaSharp native library. The result is a hard crash at startup — no fallback, no error message, just APPCRASH.

      The Avalonia team is aware of this. The issue was reported on GitHub (avaloniaui/Avalonia #20710) and was officially closed as “by-design.” There is no planned fix. Avalonia 12’s minimum supported platform on Windows is Windows 10 build 19045 (22H2).

      GitHub issue: https://github.com/AvaloniaUI/Avalonia/issues/20710

      Affected Operating Systems

      Any Windows version that shipped before DirectX 12 is affected. This includes:

      Operating SystemDirectX VersionAffected?
      Windows Server 2012 R2DirectX 11.1YES – crashes
      Windows Server 2012DirectX 11.0YES – crashes
      Windows 8.1DirectX 11.1YES – crashes
      Windows 8DirectX 11.0YES – crashes
      Windows 7DirectX 11.0YES – crashes
      Windows Server 2008 R2DirectX 11.0YES – crashes
      Windows Server 2008DirectX 10.1YES – crashes
      Windows 10 early builds (pre-1507)DirectX 11.3YES – crashes
      Windows 10 (1507 / build 10240+)DirectX 12Works
      Windows Server 2016 and laterDirectX 12Works

      Android

      Avalonia 12 also drops older Android support. The officially supported Android tier now requires Android 12 (API level 31) or higher. Devices running Android 8, 9, 10, or 11 may encounter rendering issues or crashes.

      Linux

      Linux systems without Vulkan support or with very old Mesa drivers may also experience startup failures under Avalonia 12’s new rendering stack.

      What We Observed

      We confirmed this after trying to run NervaOne on VPS servers with Windows Server 2012 R2. The crash is 100% reproducible:

      • NervaOne built with Avalonia 11.3.14 -> starts and runs correctly
      • NervaOne built with Avalonia 12.0.x -> crashes immediately on startup

      What NervaOne is Doing

      NervaOne will NOT be upgrading to Avalonia 12.

      We are staying on Avalonia 11.3.14 (the latest 11.x patch release) to maintain compatibility with:

      • Windows Server 2012 R2 and older Windows Server editions
      • Windows 8.1 and earlier
      • Older Android devices (Android 8+)
      • Low-end VPS environments

      We take compatibility seriously. Many Nerva users run NervaOne on modest hardware and server environments, and we do not want an upstream rendering dependency to silently break the app for those users.

      We will revisit the Avalonia 12 upgrade path if the team introduces a software fallback renderer for DirectX 11 systems. Until then, Avalonia 11.3.14 is stable and fully functional for our needs.

      Technical Reference

      NervaOne is an open-source wallet and miner for the Nerva (XNV) cryptocurrency.
      Source code: https://github.com/nerva-project/NervaOneWalletMiner

      Potential future hardforks

      Potential Future Nerva Hard Forks

      Who’s willing to help make this happen?

      This builds on the blog post from 2 days ago. That could be HF13:

      https://nerva.one/nerva-changes-with-ai-agent/

      No actual code for potential HF14 and HF15 yet. Just a plan for now.

      Conversation with AI agent:

      Can you look at the privacy setting that Nerva currently offers and compare it to Monero?

      Let me explore the codebase to understand Nerva’s privacy implementation.

      Agent: Nerva vs Monero privacy feature analysis

      Analyze the Nerva cryptocurrency codebase at d:\Code\Crypto\Nerva\nerva for privacy features. I need a thorough comparison vs Monero. Specifically look for:

      • 1. RingCT implementation β€” what ring size is used? Where is it configured? (look in cryptonote_config.h, src/ringct/, any MIN_MIXIN or ring size constants)
      • 2. Stealth addresses β€” is one-time address generation present? (src/crypto/crypto.cpp, crypto.h)
      • 3. Bulletproofs β€” are range proofs present and what version? (src/ringct/rctTypes.h, rctSigs.cpp)
      • 4. Dandelion++ β€” is tx propagation privacy present? (search for “dandelion” in src/)
      • 5. View keys / spend keys β€” standard dual-key model? (src/crypto/)
      • 6. Any Nerva-specific privacy changes vs vanilla Monero (search for “NERVA” or “XNV” comments near privacy code)
      • 7. Transaction output structure β€” MLSAG vs CLSAG? (src/ringct/)
      • 8. Any removed or disabled privacy features compared to Monero baseline

      Search thoroughly β€” check config files, ringct/, crypto/, cryptonote_core/, p2p/ directories. Report what’s present, what’s absent, and any relevant constants/line numbers.

      Nerva vs Monero Privacy Comparison

      What Nerva Has (Same as Monero)

      FeatureNervaMonero (current)Notes
      Stealth addressesYesYesIdentical implementation
      RingCTYesYesAll non-coinbase txs mandatory
      Range proofsBulletproofs v1 & v2Bulletproofs+Nerva lacks the optimized v3
      Dandelion++YesYesSame network-layer tx privacy
      View keys / spend keysYesYesDual-key model, watch-only wallets
      SubaddressesYesYes
      Payment ID encryptionYesYes

      Key Gaps vs Current Monero

      1. Ring size: 5 vs 16
      Nerva’s DEFAULT_MIXIN = 4 gives ring size 5. Monero upgraded to ring size 16 in 2022. A larger ring makes statistical decoy analysis harder β€” with ring size 5, tracing heuristics are significantly more effective.

      2. MLSAG instead of CLSAG
      Nerva still uses the older MLSAG signature scheme. Monero migrated to CLSAG (Concise Linkable Spontaneous Anonymous Group) in October 2020. CLSAG reduces tx size by ~25% and is considered more efficient, but both provide the same theoretical privacy guarantee.

      3. Bulletproofs v2 instead of Bulletproofs+
      Monero upgraded to Bulletproofs+ in August 2022, which are ~5–7% faster to verify and ~6% smaller. Nerva is on the older v2.

      4. No Triptych / Seraphis roadmap features
      Monero is working toward Seraphis/Jamtis which would further increase ring sizes and improve privacy. Nerva has no equivalent research effort.


      Bottom Line

      Nerva’s privacy model is solid but dated β€” roughly equivalent to Monero circa 2020. The stealth address + RingCT + Dandelion++ foundation is sound. The practical weaknesses are the small ring size (5) and MLSAG vs CLSAG. These are the highest-leverage improvements if you wanted to close the gap with Monero’s current privacy posture.

      What changes would you make to update/improve Nerva’s privacy?

      Three changes, ranked by impact:

      1. Ring size 5 β†’ 16 (highest priority)
      This is the single biggest practical privacy improvement. Small rings are vulnerable to EAE (Eve-Alice-Eve) tracing and chain-reaction deanonymization. Monero’s analysis showed ring size 11+ breaks most known heuristics; they settled on 16. The cost is ~3x larger ring signature data per tx, which increases blockchain growth rate and sync time β€” already a concern for Nerva given the post-930k slowdown.

      2. MLSAG β†’ CLSAG
      Well-proven migration β€” Monero did it in October 2020, the code is battle-tested. You’d get ~25% smaller ring signatures and faster verification. This partially offsets the size increase from a larger ring. The implementation effort is moderate: the Monero CLSAG code could be backported directly since Nerva is a fork.

      3. Bulletproofs+ (replacing v2)
      Smallest gain of the three β€” roughly 6% smaller range proofs, 5–7% faster verification. Worth doing if you’re already touching the ringct layer for CLSAG, since it’s a contained change with no protocol-level downside.


      The natural order would be: CLSAG + Bulletproofs+ together (they touch the same layer), then ring size increase as a separate hardfork since it has the largest chain-size impact and needs its own performance analysis against Nerva’s already-slow sync.

      Want me to sketch out what the implementation would look like, or discuss the sync impact first?

      Sketch out the implementation.

      Good β€” CLSAG doesn’t exist in this codebase at all, which means it’s a full backport, not a modification. Here’s the complete implementation sketch:


      Implementation Plan: 3 Privacy Upgrades

      Overview

      These are delivered as two sequential hardforks to keep each change isolated and auditable:

      • HF14 β€” CLSAG + Bulletproofs+
      • HF15 β€” Ring size 16 (separate fork because it has chain-size implications that deserve independent community discussion)

      Change 1: CLSAG (replaces MLSAG for new transactions)

      CLSAG doesn’t exist here. The full implementation lives in Monero’s src/ringct/clsag.h and clsag.cpp as of their October 2020 hardfork. This is a direct file copy + wiring job.

      New files to add:

      src/ringct/clsag.h
      src/ringct/clsag.cpp

      The Monero implementation exports four functions β€” these are exactly what you’d copy:

      // clsag.h
      namespace rct {
          clsag proveRctCLSAGSimple(const key &message, const ctkeyV &pubs,
                                     const ctkey &inSk, const key &a,
                                     const key &Cout, const keyV *mscout,
                                     const keyV *mspk, const multisig_kLRki *kLRki,
                                     key *mscout_key, unsigned int index,
                                     hw::device &hwdev);
      
          bool verRctCLSAGSimple(const key &message, const clsag &sig,
                                  const ctkeyV &pubs, const key &C_offset);
      }

      src/ringct/rctTypes.h changes:

      Add the new RCT type (after line 236) and the clsag struct:

      // After RCTTypeBulletproof2 = 5
      RCTTypeCLSAG = 6,              // HF14: CLSAG + Bulletproofs+

      Add the clsag struct alongside mgSig:

      struct clsag {
          keyV s;     // scalars
          key c1;     // initial challenge
          key I;      // key image
          key D;      // auxiliary key image
      };

      Update serialize_rctsig_base to accept RCTTypeCLSAG in the type validation check (line 259-261).

      src/ringct/rctSigs.h changes:

      Add the clsag struct serialization and wire proveRctCLSAGSimple / verRctCLSAGSimple into the existing genRctSimple / verRctNonSemanticsSimple dispatch:

      // In genRctSimple β€” add CLSAG branch
      if (rct_config.clsag) {
          rv.p.CLSAGs.push_back(proveRctCLSAGSimple(...));
      } else {
          rv.p.MGs.push_back(MLSAG_Gen(...));
      }
      
      // In verRctNonSemanticsSimple β€” add CLSAG branch  
      if (rv.type == RCTTypeCLSAG) {
          return verRctCLSAGSimple(message, rv.p.CLSAGs[i], rv.mixRing[i], ...);
      }

      src/ringct/CMakeLists.txt: Add clsag.cpp to sources.


      Change 2: Bulletproofs+ (replaces Bulletproofs v2 range proofs)

      Same story β€” backport from Monero’s bulletproofs_plus.h / bulletproofs_plus.cc (added August 2022).

      New files:

      src/ringct/bulletproofs_plus.h
      src/ringct/bulletproofs_plus.cc

      The interface mirrors existing bulletproofs:

      // bulletproofs_plus.h
      namespace rct {
          BulletproofPlus bulletproof_plus_PROVE(const std::vector<uint64_t> &v,
                                                  const rct::keyV &gamma);
          bool bulletproof_plus_VERIFY(const std::vector<const BulletproofPlus*> &proofs);
      }

      src/ringct/rctTypes.h: Add the BulletproofPlus struct (structurally similar to Bulletproof but with different field counts β€” copy exactly from Monero).

      src/ringct/rctSigs.cpp: In genRctSimple, wire the range proof type selection:

      if (rct_config.range_proof_type == RangeProofPaddedBulletproofPlus) {
          rv.p.bulletproofs_plus = bulletproof_plus_PROVE(outamounts, outSk_masks);
      } else {
          // existing bulletproof v2 path
      }

      In verRctSemanticsSimple, add the corresponding verify dispatch.

      src/cryptonote_config.h: Add the activation height:

      #define CLSAG_BULLETPROOFPLUS_FORK_HEIGHT   <HF14_height>

      Change 3: Ring size 16

      This is the simplest code change but has the largest chain impact.

      src/cryptonote_config.h:

      // Keep old values for historical validation
      #define DEFAULT_MIXIN                   4       // pre-HF15
      #define DEFAULT_RINGSIZE                DEFAULT_MIXIN + 1
      
      // HF15 values
      #define DEFAULT_MIXIN_HF15              15
      #define DEFAULT_RINGSIZE_HF15           DEFAULT_MIXIN_HF15 + 1
      #define RING_SIZE_FORK_HEIGHT           <HF15_height>

      src/wallet/wallet2.cpp β€” the two DEFAULT_MIXIN + 1 histogram calls at lines 9992/9998 need to become height-aware:

      const uint64_t min_ring = height >= RING_SIZE_FORK_HEIGHT
          ? DEFAULT_MIXIN_HF15 + 1 : DEFAULT_MIXIN + 1;
      return select_available_outputs_from_histogram(min_ring, ...);

      src/wallet/wallet_rpc_server.cpp β€” the 5 DEFAULT_MIXIN calls at lines 956, 1008, 1426, 1480 need the same height-aware mixin value passed in.

      src/cryptonote_core/blockchain.cpp β€” check_tx_inputs needs a minimum ring size enforcement gate at HF15:

      if (hf_version >= HF15_VERSION) {
          if (key_offsets.size() < DEFAULT_RINGSIZE_HF15) {
              MERROR_VER("Ring size too small for HF15: " << key_offsets.size());
              return false;
          }
      }

      Hardfork table additions in src/cryptonote_config.h

      // existing:  {13, 1100000}  -- CryptoNight-NX
      {14, <height>},   // CLSAG + Bulletproofs+
      {15, <height>},   // Ring size 16

      Effort estimate

      ChangeComplexityNotes
      CLSAGHigh~700 lines of crypto code to backport; must match Monero’s exact hash domain separation or verification breaks
      Bulletproofs+Medium-High~1200 lines; the inner product argument changes significantly from v2
      Ring size 16LowConfig + 6 call sites; chain-size modeling needed before picking activation height

      The natural order is to do CLSAG and Bulletproofs+ together as one PR (they activate at the same hardfork and both touch rctSigs), then ring size as a follow-on.

      Want me to implement this?

      Yesterday, you did CryptoNight-NX algo change to make Nerva’s algo more ASIC/GPU resistant. Where would you put that in the hard forks?

      It’s already slotted as HF13 from the previous session β€” the hardfork table in src/cryptonote_config.h has {13, 1100000} as a placeholder.

      The full sequence would be:

      HFChangeHeight
      HF12Last existing Nerva fork~930,000
      HF13CryptoNight-NX (ASIC/GPU resistance)1,100,000 (placeholder)
      HF14CLSAG + Bulletproofs+ (privacy upgrade)TBD
      HF15Ring size 16 (anonymity set)TBD

      The ordering makes sense for two reasons: mining algorithm changes should stabilize before touching the transaction signing layer, and CLSAG needs to be live before increasing ring size so the larger rings don’t make tx size even worse with MLSAG.

      The 1,100,000 height for HF13 is still a placeholder β€” you’d want to confirm what the current chain height is and give the community enough lead time (typically 2-4 weeks) before setting it in stone.

      Nerva to Monero algo comparison

      Nerva Changes with AI Agent

      Just how capable are AI coding agents? What started as a few curious questions ended up producing hardfork consensus changes designed to make Nerva’s algorithm more resistant to non-CPU miners:
      https://github.com/R0BC0D3R/nerva/commit/ab285cfc5b589281f147cc339dbd7b05eb1bafc7

      Enjoy

      How is this project mining pool resistant?

      Let me explore the codebase to find the pool resistance mechanisms.

      Agent:Explore pool resistance mechanisms

      In the Nerva cryptocurrency project at d:\Code\Crypto\Nerva\nerva, find all code and documentation related to mining pool resistance. Look for: solo mining enforcement, pool detection/blocking, algorithm design choices that prevent pooled mining (e.g. "one-cpu-one-vote", Cryptonight variants, DAA), any consensus rules or protocol-level restrictions against pools, and relevant comments or config. Search for terms like "pool", "solo", "asic", "RandomX", "CryptonightR", "one-cpu", "pool-resistant", "stratum". Report what you find with file paths and line numbers. Thoroughness: very thorough.

      Nerva’s pool resistance operates on multiple layers:


      How Nerva is Mining Pool Resistant

      1. Architectural Requirement (the core mechanism)

      Each miner must hold a full copy of the blockchain. Traditional mining pools work by distributing work to miners who don’t run full nodes β€” Nerva’s PoW algorithm pulls parameters directly from blockchain data, so you can’t participate in mining without a full node. This makes the typical “pool server hands out work, collects shares” model architecturally impossible.

      2. Algorithm Randomization (makes work-splitting hard)

      The CryptoNight variants use per-block randomized parameters derived from block height, nonce, and blockchain history:

      • v10Β (tx_utils.cpp:727-766): Mersenne Twister PRNG seeded from blockchain data produces randomized block sizes and 4 parameters (xx,Β yy,Β zz,Β ww) per block
      • v11+Β (tx_utils.cpp:687-725): HC128 stream cipher seeded from the blob hash drives per-block scratchpad layout
      • Height-dependent iteration count:Β (height + 1) % 64Β varies work per block β€” a pool can’t pre-compute or batch this

      3. Memory-Hard Scratchpad

      src/crypto/hash-ops.h:91-93 defines a 1 MB scratchpad (CN_SCRATCHPAD_MEMORY = 1048576). The sequential AES operations over this scratchpad:

      • Prevent efficient GPU parallelization (each thread needs 1 MB of fast cache)
      • Prevent ASIC optimization
      • Favor standard CPUs with AES-NI

      4. No Stratum / Pool Protocol

      There is simply no Stratum server or pool share-accounting code anywhere in the codebase. The miner (src/cryptonote_basic/miner.cpp) supports only solo mining with an optional donation percentage β€” no multi-miner address aggregation.

      5. Stated Design Intent

      README.md:18-22 explicitly: “Each miner requires a copy of the blockchain, hence there is no support for pool mining.” This is enforced at the protocol level, not just policy.


      In short: It’s not just a rule against pools β€” the PoW algorithm physically requires blockchain data that only a full node has, making pool-style work distribution impossible without each “pool member” running a full node (at which point they’re just solo mining anyway).

      How does this compare to Monero’s RandomX algo?

      This is a good comparison to make β€” the two algorithms share goals (CPU-friendly, ASIC/GPU-resistant) but take fundamentally different approaches.


      Nerva CryptoNight Variants vs. Monero RandomX

      Core Design Philosophy

      Nerva (CryptoNight v9–v12)Monero (RandomX)
      ApproachFixed AES scratchpad + randomized parametersRandom program execution in a VM
      Scratchpad1 MB2 GB dataset / 256 MB scratchpad (fast mode)
      Pool-compatibleNo β€” requires full blockchain DBYes β€” Monero has pools
      Blockchain dependencyYes β€” parameters pulled from chain DBNo β€” seeded from block template hash only
      ASIC resistancePer-block parameter randomizationRandom instruction mix defeats fixed circuits
      GPU resistanceSequential AES, 1 MB cache per threadBranch-heavy random programs defeat SIMD

      How RandomX Works (the key difference)

      RandomX generates a random program (roughly 256 instructions) from a seed derived from the block template hash, then JIT-compiles and executes it in a sandboxed VM. The 2 GB dataset is computed from the seed and kept in RAM; the 256 MB scratchpad is what each hash uses actively.

      Critically: the seed is just the block hash prefix β€” no blockchain DB access required. A pool server can generate valid work units and hand them to miners who never touch the chain. This is why Monero has thriving pools.

      How Nerva Differs

      Nerva’s resistance is architectural rather than algorithmic:

      • v11+ algorithm callsΒ db.get_cna_v2_data()Β andΒ db.get_cna_v5_data()Β (src/cryptonote_core/cryptonote_tx_utils.cpp:696-708) β€” these pull salt data from the live blockchain database, not just the block header
      • A pool serverΒ cannotΒ generate these parameters without running a full node, and if it does run a full node, each “pool member” still needs chain access to verify and submit β€” functionally collapsing back to solo mining

      RandomX doesn’t have this property at all. It’s designed to be efficiently verifiable (light mode uses 256 MB) and distributable.

      Trade-offs

      RandomX is stronger as an ASIC/GPU deterrent β€” the 2 GB memory requirement and random program execution are much harder to optimize in hardware than Nerva’s 1 MB AES scratchpad. GPU farms can mine Nerva with some efficiency loss; RandomX’s branch-heavy random code punishes GPUs far more severely.

      Nerva is stronger as a pool deterrent β€” RandomX does nothing to prevent pools; Nerva’s blockchain-data dependency makes pools architecturally impossible without each participant running a full node.

      Nerva’s 1 MB scratchpad is a notable weakness relative to RandomX. The smaller scratchpad means modern CPUs with large L2/L3 caches handle it easily, but GPUs with high bandwidth and many cores can still parallelize it more than RandomX allows.

      Summary

      RandomX solves “how do we keep ASICs and GPUs out?” better. Nerva solves “how do we keep pools out?” in a way RandomX never attempted. They’re complementary approaches to different problems β€” Nerva sacrifices pool-compatibility resistance for pool impossibility, at the cost of a less aggressive memory footprint than RandomX.

      What would you change to make Nerva’s algo more ASIC/GPU resistant?

      The single highest-impact change would be dramatically increasing the scratchpad size β€” from 1 MB to something in the 4–8 MB range. GPUs parallelize by running thousands of threads simultaneously; each thread needs its own scratchpad, so a larger scratchpad multiplies VRAM consumption per-thread and kills GPU occupancy. The trade-off is that it also hurts low-end CPUs (older laptops, small caches), which conflicts with Nerva’s accessibility goal of “mine on your CPU.”

      The second change I’d make is adding random program execution on top of the existing chain-data dependency β€” essentially a hybrid: keep the blockchain-DB seeding (which preserves pool resistance), but replace the fixed AES scratchpad loop with a RandomX-style JIT-compiled random program seeded from that chain data. Fixed instruction sequences, even with randomized parameters, are still vulnerable to custom silicon; truly random code execution makes ASIC design economically infeasible because the circuit has to be general-purpose. The cost is implementation complexity and a larger attack surface in the VM/JIT layer.

      The combination β€” large scratchpad + random programs seeded from chain data β€” would give you RandomX-level ASIC/GPU resistance while preserving Nerva’s unique pool-resistance property that RandomX doesn’t have. Want me to sketch out what a hybrid implementation would look like in the codebase?

      Sure, sketch it out.

      Good β€” I have a clear picture of the full stack. Here’s the sketch:


      Proposed: CryptoNight-NX (v13) β€” Hybrid Architecture

      The idea is to keep Nerva’s chain-data seeding (pool resistance) and bolt on a RandomX-style random program loop in place of the fixed AES scratchpad body. Call it CryptoNight-NX (“NX” = Nerva + eXecution).


      Layer 1 β€” Seed Construction (unchanged, keeps pool resistance)

      This part stays exactly as v11 does it in cryptonote_tx_utils.cpp:687-724:

      // In get_block_longhash_v13()
      uint64_t stable_height = height - 256;
      
      // Pull chain-rooted salt β€” requires full blockchain DB (pool resistance)
      db.get_cna_v2_data(&context->random_values, stable_height, CN_SCRATCHPAD_MEMORY);
      db.get_cna_v5_data(context->salt, &rng_state, stable_height);
      
      // Per-nonce seed: blob hash XOR'd with chain salt
      crypto::hash blob_hash;
      get_blob_hash(blob, blob_hash);
      // XOR blob_hash with first 32 bytes of chain salt -> unique seed per nonce+height
      uint8_t seed[32];
      for (int i = 0; i < 32; i++)
          seed[i] = blob_hash.data[i] ^ context->salt[i];

      The seed is now height-rooted (chain data) and nonce-rooted (blob hash). A pool server without a full node cannot compute it.


      Layer 2 β€” Program Generation (new, ASIC/GPU resistance)

      Add a lightweight VM that generates a random program from the seed. This lives in a new file src/crypto/cnx_vm.h:

      #define CNX_PROGRAM_SIZE   512    // instructions per program
      #define CNX_REG_COUNT      8      // general-purpose registers
      #define CNX_SCRATCHPAD_MB  4      // 4 MB β€” kills GPU occupancy
      
      typedef enum {
          CNX_OP_IADD_RS,   // r[a] += r[b] << shift
          CNX_OP_ISUB,      // r[a] -= r[b]
          CNX_OP_IMUL,      // r[a] *= r[b]          (64-bit)
          CNX_OP_IXOR,      // r[a] ^= r[b]
          CNX_OP_IROR,      // r[a] = ror64(r[a], r[b] & 63)
          CNX_OP_CBRANCH,   // conditional branch     (GPU divergence)
          CNX_OP_SCRATCHPAD_READ,   // r[a] = scratchpad[addr % size]
          CNX_OP_SCRATCHPAD_WRITE,  // scratchpad[addr % size] = r[a]
          CNX_OP_AES_ROUND, // one AES round (keeps AES-NI advantage on CPUs)
          CNX_OP_COUNT
      } cnx_opcode_t;
      
      typedef struct {
          cnx_opcode_t op;
          uint8_t dst, src;   // register indices
          uint32_t imm;       // immediate / address modifier
      } cnx_instruction_t;
      
      typedef struct {
          cnx_instruction_t instructions[CNX_PROGRAM_SIZE];
      } cnx_program_t;
      
      // Deterministically generate program from seed
      void cnx_generate_program(cnx_program_t *prog, const uint8_t seed[32]);
      
      // Execute program against scratchpad, returns hash
      void cnx_execute(cnx_program_t *prog, uint8_t *scratchpad,
                       size_t scratchpad_size, uint64_t registers[CNX_REG_COUNT],
                       char *hash_out);

      Key design choices:

      • CNX_OP_CBRANCHΒ β€” conditional branches cause GPU warp divergence; different threads in a warp take different paths, serializing execution and killing throughput
      • CNX_OP_AES_ROUNDΒ β€” keeps AES-NI advantage on CPUs; GPUs lack AES-NI hardware
      • 4 MB scratchpadΒ β€” 4Γ— v11’s 1 MB; a GPU running 2048 threads simultaneously needs 8 GB VRAM just for scratchpads, exceeding most consumer cards
      • SCRATCHPAD_READ/WRITEΒ β€” random memory access pattern defeats GPU cache hierarchy

      Layer 3 β€” The Hash Function (new cn_slow_hash_v13)

      In slow-hash.c, add:

      void cn_slow_hash_v13(cn_hash_context_t *context,
                            const void *data, size_t length,
                            char *hash,
                            const uint8_t chain_seed[32])
      {
          // 1. Keccak the input blob -> initial state (same as existing CN)
          union cn_slow_hash_state state;
          hash_process(&state.hs, data, length);
      
          // 2. XOR initial scratchpad fill with chain salt (existing technique)
          //    This binds the scratchpad state to chain history
          uint8_t *sp = context->scratchpad;  // 4 MB, allocated in context
          aes_expand_key(state.k, aes_key);
          for (size_t i = 0; i < CNX_SCRATCHPAD_MB * 1024 * 1024; i += 128) {
              aes_pseudo_round(sp + i, aes_key);
              // XOR with chain salt (wraps at CN_SALT_MEMORY)
              for (int j = 0; j < 128; j++)
                  sp[i + j] ^= context->salt[(i + j) % CN_SALT_MEMORY];
          }
      
          // 3. Generate random program from combined seed
          cnx_program_t prog;
          cnx_generate_program(&prog, chain_seed);  // chain_seed = chain XOR blob
      
          // 4. Execute program N times, mutating scratchpad
          uint64_t regs[CNX_REG_COUNT];
          memcpy(regs, state.k, sizeof(regs));
      
          for (int iter = 0; iter < 2048; iter++)
              cnx_execute(&prog, sp, CNX_SCRATCHPAD_MB * 1024 * 1024, regs, NULL);
      
          // 5. Final AES pass + Keccak -> output hash (same as existing CN)
          // ... existing finalization ...
          extra_hashes[state.hs.b[0] & 3](&state, 200, hash);
      }

      Layer 4 β€” Hardfork Wiring

      Three small changes:

      cryptonote_config.h β€” add v13 to the fork table:

      {12, 930000},
      {13, TBD}     // CryptoNight-NX

      cryptonote_tx_utils.cpp:665-677 β€” add case to the switch:

      case 13:
          return get_block_longhash_v13(context, db, blob, res, height);

      hash-ops.h:91 β€” update scratchpad constant (or add a new one):

      #define CN_SCRATCHPAD_MEMORY_V13  (4 * 1024 * 1024)   // 4 MB

      The cn_hash_context_t in hash-ops.h:113-124 already heap-allocates the scratchpad, so increasing its size for v13 contexts is straightforward.


      What This Buys You

      Threatv11 defensev13 defense
      GPU farms1 MB per thread (weak)4 MB + branch divergence (strong)
      ASICRandomized AES paramsTruly random instruction mix
      PoolsChain DB dependencyUnchanged β€” still pool-impossible
      FPGAsFixed AES circuitVariable program defeats fixed logic
      Verification costSame as miningSame as mining (no light mode needed β€” no pools anyway)

      Biggest Risk

      The VM execution in cnx_execute must be deterministic across all platforms and compilers β€” any UB or platform-specific behavior breaks consensus. The existing cn_slow_hash.c is careful about this (packed structs, explicit integer widths). cnx_vm.h needs the same discipline. I’d write it in pure C with no floats, no UB, and explicit uint64_t everywhere.

      Want me to start implementing any of the layers?

      Can you create a new branch under my repository: https://github.com/R0BC0D3R/nerva And make the 4 layer implementation that you outlined above? Let’s see what you got.

      Branch is live at https://github.com/R0BC0D3R/nerva. Here’s what landed:


      What’s in feat/cryptonight-nx (1 commit, 529 lines)

      FileChange
      src/crypto/cnx_vm.hNew β€” VM types, 9 opcodes, cnx_program_t, function declarations
      src/crypto/cnx_vm.cNew β€” HC128-based program generator + deterministic VM executor
      src/crypto/hash-ops.hCN_SCRATCHPAD_MEMORY_V13 (4 MB), cnx_scratchpad field on context, cn_slow_hash_v13 declaration
      src/crypto/slow-hash.cContext alloc/free for 4 MB scratchpad; cn_slow_hash_v13 in both HW-AES and SW-AES paths
      src/cryptonote_core/cryptonote_tx_utils.cppget_block_longhash_v13() + case split (11/12 β†’ v11, 13+ β†’ v13)
      src/cryptonote_config.h{13, 1100000} hardfork entry (placeholder height β€” confirm before mainnet)
      src/crypto/CMakeLists.txtcnx_vm.c and cnx_vm.h added to build

      The sandbox blocked make release-static-win64 -j4 (no linker available in this environment), so run that yourself from your MSYS2 terminal to verify it compiles clean. The activation height 1100000 is a placeholder β€” you’ll want to set a real target before any testnet deployment.

      https://github.com/R0BC0D3R/nerva/commits/feat/cryptonight-nx