From eb23851326f3ae76643f85f3643d05b0894d653e Mon Sep 17 00:00:00 2001 From: Dan Hepworth Date: Tue, 6 Oct 2026 11:47:28 -0400 Subject: [PATCH 1/7] init: add -v2pqtransport and hybrid transport test options Make the hybrid post-quantum v2 transport configurable, off by default until the default-on change in part 9, through one configuration that the startup warnings, the startup line, every new connection and RPC share. - PQTransportConfig {v2_enabled, pq_requested, fail_first_packet} is computed once in AppInitParameterInteraction() from the effective -v2transport, -v2pqtransport and -test settings. NODE_P2P_V2 now comes from the same value. CConnman::Options::m_pq carries it, and CConnman::Init() derives m_pq_mode from it: NEGOTIATE only with both switches on. This is the only way the mode is set; the unit-test hook ConnmanTestMsg::SetPQMode is removed and net_pq_tests passes a configuration instead. - -v2pqtransport (DEFAULT_V2_PQ_TRANSPORT = false) has no effect with -v2transport=0. An explicit -v2pqtransport=1 then warns: "-v2pqtransport=1 has no effect because -v2transport=0. Set -v2transport=1 to use hybrid post-quantum v2 transport." An explicit 0 or the default never warns. - Booleans are read as integers, so -v2pqtransport=true is 0. A value that doesn't parse completely as an int, under InterpretBool's whitespace and sign rules, warns and names the value it is read as: true, 1abc and an overflowing number. An empty value and a negation are valid. InterpretBool itself is unchanged. - -test=pq_fail_first_packet (regtest) sets corrupt_shared_secret on inbound and outbound transports, so every hybrid handshake fails its key confirmation. -test=mlkem_portable (regtest) and the hidden -mlkemportable (debug category, all chains) force portable ML-KEM code through mlkem::InitializeRuntime(), called before the connection manager exists. - The startup line is always logged and names the active backends: "v2 pq: enabled arith= keccak=", "v2 pq: disabled (-v2pqtransport=0) ..." or "v2 pq: disabled (-v2transport=0) ..." when v2 is off, whatever the PQ switch says. - The local-fault warning names -mlkemportable as the remedy again while native code runs: " Restart with -mlkemportable to use the portable implementation." - MakeTransport() takes the CNodeOptions. rpc_pqtransport.py checks the switch from the command line, qbit.conf, -nov2pqtransport and across restarts on a live two-node connection ("v2 pq: switched"), the exact warnings and their absence, -help and -help-debug, all three startup lines, portable code from both options, the test options' regtest-only check, and that the test options print nothing to stderr. net_pq_tests' pq_config checks the mode of inbound and outbound connections for each configuration and the effect of fail_first_packet; the local-fault tests check the remedy appears only while native code runs. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit 19268db84f0a34f1296ddb27600bbf9d13ad8a29) --- src/common/args.cpp | 2 + src/init.cpp | 61 +++++++++- src/net.cpp | 21 ++-- src/net.h | 27 ++++- src/test/net_pq_tests.cpp | 60 ++++++++-- src/test/util/net.h | 2 - test/functional/rpc_pqtransport.py | 171 +++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 8 files changed, 324 insertions(+), 21 deletions(-) create mode 100755 test/functional/rpc_pqtransport.py diff --git a/src/common/args.cpp b/src/common/args.cpp index ce0b414cca..dbd5604ec2 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -745,6 +745,8 @@ const std::vector TEST_OPTIONS_DOC{ "reindex_after_failure_noninteractive_yes (When asked for a reindex after failure interactively, simulate as-if answered with 'yes')", "bip94 (enforce BIP94 consensus rules)", "disable_witness_pruning (disable witness pruning in regtest)", + "pq_fail_first_packet (corrupt this node's ML-KEM shared secret on every hybrid post-quantum v2 handshake, so the peer's key confirmation fails)", + "mlkem_portable (use the portable ML-KEM-1024 code instead of native code)", }; bool HasTestOption(const ArgsManager& args, const std::string& test_option) diff --git a/src/init.cpp b/src/init.cpp index 297a4f2824..1d3797c386 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -565,6 +566,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-onlynet=", "Make automatic outbound connections only to network (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-v2transport", strprintf("Support v2 transport (default: %u)", DEFAULT_V2_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-v2pqtransport", strprintf("Support hybrid post-quantum key exchange (ML-KEM-1024) in v2 transport. Has no effect with -v2transport=0 (default: %u)", DEFAULT_V2_PQ_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); @@ -649,6 +651,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-limitdescendantsize=", strprintf("Do not accept transactions if any ancestor would have more than kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT_KVB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-test=