BitcoinII v31 replay protection
Purpose
Section titled “Purpose”This page traces the BitcoinII Core v31.1.0 replay-protection path from consensus parameters through validation, signature hashing, wallet signing, RPC, and PSBT handling.
Activation
Section titled “Activation”Mainnet v31.1.0 sets:
nReplayProtectionHeight = 57750nReplayProtectionForkId = 0x01324342U
Consensus::Params::SighashForkId(height) returns 0 below activation and the configured fork id at and above activation.
This means historical blocks retain the legacy BitcoinII signature-hash domain, while transactions intended for blocks at or above height 57750 use the BC2 replay-protection domain.
What the fork id does
Section titled “What the fork id does”The fork id is not serialized into the transaction itself. It is supplied to signature-hash calculation as an additional domain value.
In src/script/interpreter.cpp, legacy signature-hash serialization appends the ordinary sighash type and, when the fork id is non-zero, appends the replay-protection domain before hashing. A zero fork id preserves the historical digest.
The same domain is threaded through SegWit/Taproot-aware signing and checking through PrecomputedTransactionData::m_sighash_fork_id and the signature-checker interfaces.
Validation and mempool path
Section titled “Validation and mempool path”Mempool admission calculates the replay domain for the next block height, not merely the current tip. This is important at the activation boundary: a transaction admitted immediately before height 57750 must already be valid for the post-activation signature domain if it could be mined in the activation block.
validation.cpp sets m_sighash_fork_id from:
SighashForkId(active_chain_height + 1)The validation cache also commits the fork-id value into its cache key so a script result checked under one signature domain is not reused under another domain.
Immediately before activation, the node clears the mempool when the next block height equals nReplayProtectionHeight. This prevents transactions accepted under the legacy signature domain from surviving across the activation boundary.
Wallet and service path
Section titled “Wallet and service path”The chain interface exposes getSighashForkId(height) so wallet code can obtain the consensus-selected domain.
Wallet signing targets the next-block domain through CWallet::GetSighashForkId() and passes that value into transaction signing and PSBT processing.
Reviewed v31 paths include:
src/wallet/scriptpubkeyman.cpppassing the fork id into transaction signing;src/wallet/wallet.cpppassing it intoPrecomputePSBTData;- wallet spend RPC passing it into PSBT finalization/extraction;
- raw-transaction RPC calculating the next-block fork id for signing;
src/psbt.cppstoring the domain inPrecomputedTransactionData;- node PSBT analysis accepting a fork-id argument;
- external-signer code refusing to treat a post-fork signing flow as safe unless replay-protection sighash support is available.
Runtime boundary
Section titled “Runtime boundary”The 2026-09-11 Windows v31.1.0 PSBT validation directly exercised funding, wallet signing, PSBT finalization/decoding, raw-transaction decoding, local mempool admission, and local regtest submission.
That runtime test does not directly exercise the replay-domain switch. In v31.1.0, regtest does not override the consensus defaults of disabled activation and fork id zero. Testnet4 contains a height-119 setting, but it is a public test network and was deliberately not used for transaction submission. Mainnet replay behavior therefore remains source-confirmed rather than runtime-confirmed.
Exchange/integration implication
Section titled “Exchange/integration implication”An integrator must use the current BitcoinII Core signing/validation stack or otherwise reproduce the post-activation sighash domain correctly. Bitcoin-compatible address formats alone are not sufficient for transaction-signing compatibility.
The important boundary is signature digest compatibility, not address encoding.
What this review does not yet prove
Section titled “What this review does not yet prove”- No independent MoreBC2 transaction vector has yet been generated showing the pre/post-fork digest difference.
- The disposable-wallet v31.1.0 signing test covered the ordinary regtest domain, not the mainnet replay domain.
- Hardware/external signer compatibility has not been tested.
- Third-party libraries have not been audited for BC2 fork-id support.
Primary sources
Section titled “Primary sources”Pinned to v31.1.0:
src/kernel/chainparams.cppsrc/consensus/params.hsrc/validation.cppsrc/script/interpreter.hsrc/script/interpreter.cppsrc/script/sign.cppsrc/interfaces/chain.hsrc/node/interfaces.cppsrc/rpc/rawtransaction.cppsrc/rpc/rawtransaction_util.cppsrc/psbt.cppsrc/node/psbt.cppsrc/wallet/wallet.hsrc/wallet/wallet.cppsrc/wallet/scriptpubkeyman.cppsrc/wallet/rpc/spend.cppsrc/wallet/external_signer_scriptpubkeyman.cpp
Canonical tag: https://github.com/Bitcoin-II/BitcoinII-Core/tree/v31.1.0
Verification
Section titled “Verification”Status: Source-confirmed / Runtime PSBT coverage
Primary sources checked: Yes, v31.1.0 at commit 8daaf7b12e71d3646eed787f040bf2899a69dc1c
Notes: The activation, domain-selection, validation, wallet, RPC, and PSBT paths are source-backed. Ordinary regtest PSBT behavior is runtime-observed; replay-domain vectors and third-party compatibility remain open.