Skip to content

RIP-25: Post-Quantum Signatures via ML-DSA-44 (Witness v2)#1281

Open
ALENOC wants to merge 12 commits intoRavenProject:masterfrom
ALENOC:feature/rip25-pq-hybrid
Open

RIP-25: Post-Quantum Signatures via ML-DSA-44 (Witness v2)#1281
ALENOC wants to merge 12 commits intoRavenProject:masterfrom
ALENOC:feature/rip25-pq-hybrid

Conversation

@ALENOC
Copy link
Copy Markdown

@ALENOC ALENOC commented Apr 6, 2026

Summary

Implements RIP-25: post-quantum transaction signing for Ravencoin using ML-DSA-44 (NIST FIPS 204) via a new witness version 2 soft fork.

  • New PQ addresses (witness v2) use ML-DSA-44 exclusively — quantum-resistant from day one
  • Existing ECDSA addresses (witness v0) continue working unchanged
  • Gradual migration: users send funds from old to new addresses at their own pace

Changes (49 files, ~2200 lines)

Area Key changes
Crypto crypto/mldsa.h/cpp — ML-DSA-44 wrapper around liboqs (FIPS 204), deterministic keygen from seed
Keys pqkey.h/cppCPQKey/CPQPubKey classes (1312B pubkey, 2560B privkey, 2420B signature)
Script Witness v2 validation (2-element stack: [sig, pk], SHA256(pk)==program), SIGVERSION_WITNESS_V2_PQ, WitnessSigOps for v2
Consensus BIP9 soft fork (bit 11, 85% threshold), phased block weight 8→12→16 MWU, PQ_WITNESS_SCALE_FACTOR=8
Wallet getnewpqaddress RPC, full PQ key persistence (WritePQKey/WriteCryptedPQKey), encryption support (mapCryptedPQKeys, EncryptKeys/Unlock for PQ), PQ-aware fee estimation
Addresses Bech32m encoding (BIP350) for witness v2: rvn1z... (mainnet), trvn1z... (testnet), rcrt1z... (regtest)
Policy TX_WITNESS_V2_PQ_KEYHASH standard type, PQ-aware dust threshold, PQ weight discount (8x), IsWitnessStandard 2-element check
Network NODE_PQ_HYBRID service flag (bit 5), 16 MB protocol message limit
Build liboqs integration (configure.ac --with-liboqs, depends/packages/liboqs.mk)
Tests 20 unit tests in test/pqkey_tests.cpp — all passing

Build & test

./autogen.sh
./configure --with-liboqs
make -j$(nproc)
# 20/20 PQ unit tests pass:
src/test/test_raven --run_test=pqkey_tests

Regtest verification

Full regtest test suite — all scenarios passing:

# Test Result
1 Stress: 50 PQ sends 50/50 OK, all confirmed in 1 block
2 Chain: PQ→PQ 10 hops 10/10 OK, each hop signs and confirms
3 Mixed ECDSA+PQ same block OK, 4 tx (2 ECDSA + 2 PQ) coexist
4 Reorg (invalidateblock) OK, PQ tx returns to mempool, re-confirmed after reconsiderblock
5 Restart persistence OK, PQ keys survive restart, ML-DSA signing works
6 Backup/restore wallet OK, balance restored, PQ signing works after restore
7 Wallet encryption OK, locked→rejects, unlock→PQ signs, re-lock→rejects
8 validateaddress + listunspent OK, ispqaddress:true, witness_version:2, spendable:true
9 Block stress (100 PQ txs) 100/100 OK, block 114 KB / 179K WU, mempool cleared
10 Fee policy OK, PQ transactions relay with default fees (8x witness discount + correct fee estimation)

Full specification: doc/RIP-0025-PQ-Signatures.md

Test plan

  • Build completes successfully with liboqs
  • All 20 PQ unit tests pass (pqkey_tests)
  • Regtest: generate PQ addresses, send/receive transactions
  • Regtest: PQ → ECDSA, PQ → PQ confirmed with ML-DSA-44 signatures
  • Stress test: 50 concurrent PQ sends, 100 PQ txs in one block
  • Chain test: 10-hop PQ→PQ transfer chain
  • Mixed blocks: ECDSA and PQ transactions in same block
  • Reorg: PQ transactions survive invalidateblock/reconsiderblock
  • Wallet persistence: PQ keys survive node restart
  • Wallet backup/restore: PQ keys survive backup and restore cycle
  • Wallet encryption: encrypt/lock/unlock/sign cycle with PQ keys
  • Fee policy: PQ transactions relay with default fees (8x witness discount)
  • validateaddress: reports ispqaddress, witness_version, witness_program
  • listunspent: PQ UTXOs shown as spendable: true
  • Testnet deployment and extended testing
  • IBD performance with PQ blocks
  • Peer compatibility: upgraded and non-upgraded nodes coexist

ALENOC added 12 commits April 5, 2026 13:00
Proof-of-concept implementation for RIP-25: Post-Quantum Hybrid
Signatures via ML-DSA-44 (FIPS 204).

New files:
- doc/RIP-0025-PQ-Hybrid-Signatures.md: Full RIP specification
- src/crypto/mldsa.h/cpp: ML-DSA-44 wrapper (PoC simulation,
  production will use liboqs)
- src/pqkey.h/cpp: CHybridKey and CHybridPubKey classes implementing
  AND-composition hybrid ECDSA+ML-DSA-44 signatures
- src/test/pqkey_tests.cpp: 20 unit tests covering keygen,
  sign/verify roundtrip, wrong key/message/signature rejection,
  partial signature rejection, serialization, and determinism

See: RavenProject#1280
…s rules

Implements the complete RIP-25 specification across the Ravencoin codebase:

- Consensus: BIP9 deployment (bit 11, 85% threshold) for mainnet/testnet/regtest,
  phased block weight increase (8→12→16 MWU), PQ witness discount (8x)
- Script: Witness v2 validation in VerifyWitnessProgram with AND-composition
  (both ECDSA + ML-DSA-44 must verify), new SIGVERSION_WITNESS_V2_PQ
- Policy: TX_WITNESS_V2_PQ_KEYHASH standard type, witness v2 standardness
  checks, PQ-aware dust threshold calculation
- Network: NODE_PQ_HYBRID service flag (bit 5), increased protocol message
  size limit (16MB) for PQ witness data
- Signing: Witness v2 signing framework in sign.cpp with hybrid key support
- Build: Added mldsa.cpp/h to crypto library, pqkey.cpp to common library,
  pqkey_tests.cpp to test suite
Add overview section explaining the post-quantum hybrid signature
implementation, key changes across consensus/script/policy/network
layers, current PoC status, and link to full RIP specification.
Replace the HMAC-SHA512 proof-of-concept simulation in mldsa.cpp with
real ML-DSA-44 (FIPS 204) calls via liboqs (Open Quantum Safe).

Changes:
- src/crypto/mldsa.cpp: Now uses OQS_SIG_ml_dsa_44 for keygen, sign,
  and verify. Includes compile-time static_asserts to ensure size
  constants match liboqs. Supports deterministic keygen from seed
  via OQS_SIG_ml_dsa_44_generate_keypair_from_seed when available.
- src/crypto/mldsa.h: Updated documentation, removed PoC references.
- configure.ac: Added --with-liboqs flag, PKG_CHECK_MODULES for liboqs
  with fallback to AC_CHECK_LIB/AC_CHECK_HEADER.
- src/Makefile.am: Added LIBOQS_CFLAGS/LIBOQS_LIBS to crypto library
  and all binary link lines (ravend, raven-cli, raven-tx).
- src/Makefile.test.include: Added LIBOQS_LIBS to test linker.
- depends/packages/liboqs.mk: New depends package for cross-compilation.
- depends/packages/packages.mk: Added liboqs to default packages.
- README.md: Updated status to complete, added build instructions.
Witness v2 now uses ML-DSA-44 exclusively instead of requiring both
ECDSA + ML-DSA-44. Old ECDSA addresses (witness v0) continue working
unchanged. Users gradually migrate funds to quantum-resistant addresses.

Key changes:
- pqkey.h/cpp: Replace CHybridKey/CHybridPubKey with CPQKey/CPQPubKey
- interpreter.cpp: 2-element witness stack [mldsa_sig, mldsa_pk]
- script_error.h/cpp: PQ-specific error codes
- bech32.h/cpp: New Bech32m encoding for witness v2 addresses (BIP350)
- standard.h/cpp: WitnessV2PQDestination type, GetScriptForWitnessV2PQ
- base58.cpp: EncodeDestination/DecodeDestination with bech32m support
- chainparams: Bech32m HRP (rvn/trvn/rcrt) for PQ addresses
- keystore.h: PQ key maps (PQKeyMap, PQPubKeyMap) in CBasicKeyStore
- sign.h/cpp: ML-DSA signing via TransactionSignatureCreator
- ismine.cpp: IsMine support for TX_WITNESS_V2_PQ_KEYHASH
- policy.cpp: Update IsWitnessStandard for 2-element PQ stack
- rpcwallet.cpp: Add getnewpqaddress RPC command
- init.cpp: Advertise NODE_PQ_HYBRID service flag
- Tests rewritten for ML-DSA-44 only design
- RIP document and README updated
- Wallet DB: WritePQKey/WriteCryptedPQKey for persistent PQ key storage,
  ReadKeyValue handlers for "pqkey"/"cpqkey", IsKeyType updated
- Encryption: CCryptoKeyStore PQ key encryption/decryption via
  mapCryptedPQKeys, AddCryptedPQKey, EncryptKeys handles PQ keys,
  Unlock verifies PQ keys for PQ-only wallets
- Wallet: CWallet::AddPQKeyPubKey persists to disk, AddCryptedPQKey
  persists encrypted keys, LoadPQKey/LoadCryptedPQKey for DB loading
- Sigops: WitnessSigOps returns 1 for witness v2 (32-byte program)
…gops files

Sync section 6.3 to reflect all 45 modified files including wallet DB
persistence (walletdb.h/cpp), wallet encryption (crypter.h/cpp),
wallet integration (wallet.h/cpp), interpreter.h, versionbits.cpp,
packages.mk, and Makefile.test.include.
Fix compilation errors: CAffectedKeysVisitor, Witnessifier,
DescribeAddressVisitor, TestAddrTypeVisitor, and TestPayloadVisitor
all need an operator() for the new WitnessV2PQDestination variant type.
- mldsa.cpp: Use liboqs internal keypair_internal() for deterministic
  keygen from seed via weak symbol linkage (FIPS 204 Section 6.1).
  All 20 PQ unit tests now pass.
- init.cpp: Fix boost 1.83 signal disconnect incompatibility with
  function pointer (use connection object instead).
- lockedpool.cpp: Add missing <stdexcept> include for gcc 13.
- cuckoocache_tests.cpp: Add missing <deque> include for gcc 13.
SignatureHash must use SIGVERSION_WITNESS_V2_PQ (not SIGVERSION_WITNESS_V0)
in both signing (sign.cpp) and verification (interpreter.cpp) paths.
Also route SIGVERSION_WITNESS_V2_PQ through the BIP143-style sighash
computation (same branch as witness v0).

Verified end-to-end on regtest: ECDSA→PQ and PQ→ECDSA transactions
both confirm successfully with ML-DSA-44 signatures.
Two fixes for PQ transactions being rejected with "min relay fee not met":

1. GetTransactionWeight (consensus/validation.h): Apply PQ_WITNESS_SCALE_FACTOR
   (8x) discount to ML-DSA-44 witness data. Standard segwit witness counts at
   1 WU/byte; PQ witness v2 now counts at 0.5 WU/byte, halving the effective
   virtual size of PQ witness data (~588 vs ~1035 vbytes for a typical PQ tx).

2. DummySignTx (wallet/wallet.h): ProduceSignature fails for PQ inputs during
   dummy signing because VerifyScript cannot verify all-zeros ML-DSA data. The
   wallet fell back to a 256-byte scriptSig with no witness, severely
   underestimating PQ transaction size. Now detects witness v2 PQ outputs and
   inserts correctly-sized dummy witness (2420B sig + 1312B pk).

Tested on regtest: ECDSA→PQ, PQ→ECDSA, PQ→PQ all relay and confirm with
default fee settings (no manual settxfee required).
Update section 4.1 with weight calculation formula, effective virtual
size table, and wallet fee estimation (DummySignTx) details. Add
consensus/validation.h to the file table in section 6.3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant