test: adapt the functional test framework for ReddCoin PoSV#401
Merged
Conversation
…work - Update test_node.py PRIV_KEYS with ReddCoin regtest addresses - Replace hardcoded Bitcoin testnet address in rpc_signrawtransaction.py - All private keys remain the same, only addresses updated for ReddCoin compatibility
…t the test Replace all references to bitcoin.conf with reddcoin.conf in utility functions Update test_framework.py to use reddcoin binaries and conf - Replace bitcoind/bitcoin-cli with reddcoind/reddcoin-cli - Update TMPDIR_PREFIX to reddcoin_func_test_ - Update environment variables to REDDCOIND/REDDCOINCLI - Update help text references from bitcoin to reddcoin
Updates test framework address encoding/decoding for Reddcoin: - Change P2PKH versions: mainnet=61 (R prefix), regtest=122 (r prefix) - Change bech32 HRP: mainnet="rdd", regtest="rcrt" - Update unspendable addresses for regtest - Update P2WSH OP_TRUE address for regtest - Update unit tests to use Reddcoin versions
Modifies TestNode.generate() to advance mock time by 60 seconds before each block generation. This is required for Reddcoin PoS where coins need age to be eligible for staking (nStakeMinAge = 10s on regtest). Generates blocks one-at-a-time with time advancement between each, ensuring sufficient coinage for probabilistic PoS validation.
ReddCoin requires a loaded wallet for PoS block generation after nLastPowHeight (89 in regtest). The cache initialization generates 199 blocks, requiring PoS support for blocks 90+. Changes: - Remove -disablewallet from cache node initialization - Create default wallet after node starts for PoS block generation
Updates test framework to support Reddcoin's Proof-of-Stake consensus: - Add txindex requirement for all test nodes (required by CreateCoinStake) - Create legacy wallets to support importprivkey for staking - Import test framework keys into cache node and test nodes - Generate cache blocks one-at-a-time with 60s spacing for coin age - Set mock time 300s past tip to ensure coins have sufficient age - Set wallet RPC context for generatetoaddress support - Remove IBD block 200 generation (replaced by PoS-aware tests) Cache now successfully generates 110 PoS blocks (blocks 90-199).
Adds helper function to advance mock time for PoS coin age requirements. The function advances mock time on test nodes to age UTXOs, which is required for Reddcoin PoS staking (nStakeMinAge = 10s on regtest). Default advancement is 60 seconds to ensure sufficient coinage.
Updated test framework and functional tests to use ReddCoin-specific naming conventions instead of Bitcoin references. Changes: - Log messages: "bitcoind" → "reddcoind" - Error messages: "bitcoind" → "reddcoind" - Binary names: bitcoin-cli/bitcoin-tx → reddcoin-cli/reddcoin-tx - Database error messages reference correct binary name Files updated: - feature_asmap.py: Updated log messages for asmap tests - feature_filelock.py: Updated file locking error messages - rpc_signer.py: Updated signer error message - test_node.py: Updated node startup log message - get_previous_releases.py: Updated binary file names This ensures test output and error messages accurately reflect ReddCoin instead of Bitcoin.
- Add mocktime tracking to TestNode class for sync_time() support - Update set_node_times() to track mocktime on each node - Fix advance_time_for_pos() to use tracked mocktime when set, allowing multiple calls without generating blocks - Update generate() to use max of current mocktime and block time to avoid resetting advanced time
Fixes init_wallet to respect test's descriptor wallet preference instead of forcing legacy wallets for all tests. PoS-specific features (importprivkey) are now conditional on legacy wallet usage. Changes: - Use self.options.descriptors if set, otherwise default to False - Only import private keys when using legacy wallets - Prevents breaking tests that explicitly require descriptor wallets This allows both PoS tests (needing legacy) and HD wallet tests (needing descriptors) to coexist.
ReddCoin transactions include an nTime field (absent in Bitcoin) that is critical for PoS functionality. Added full support to the Python test framework's CTransaction class. Changes: - Add nTime to __slots__ and initialize to current time (required non-zero) - Serialize/deserialize nTime after nLockTime when nVersion > 1 - Set CURRENT_VERSION = 2 to match ReddCoin standard transactions - Support in both witness and non-witness serialization paths Transaction format: nVersion | [witness] | vin | vout | nLockTime | nTime (if nVersion > 1) Rules: - nTime only present when nVersion > POW_TX_VERSION (1) - nTime included in txid/wtxid but NOT in signature hash (BIP143/341) - All test transactions now default to nVersion=2 with nTime field This unblocks CSV/SegWit/Taproot functional testing which create transactions manually, ensuring the test framework matches ReddCoin Core's transaction format exactly.
Updates the test framework MAX_MONEY constant from Bitcoin's 21 million to ReddCoin's maximum supply of 92,233,720,368 RDD. This ensures test validation logic matches the actual consensus rules.
Updated test framework P2P protocol version constants to match ReddCoin's current protocol version 80016 (from Bitcoin's 70016). Changes: - MIN_P2P_VERSION_SUPPORTED: 60001 → 80016 - P2P_VERSION: 70016 → 80016 This ensures test framework P2P connections are compatible with ReddCoin nodes and prevents protocol version mismatch errors during testing.
Update P2P message magic bytes to match ReddCoin chain parameters for mainnet, testnet3, regtest, and signet.
Update the unspendable P2WSH address constant to use the correct
bech32 checksum for ReddCoin's 'rcrt' HRP (human readable part).
The previous checksum 'ajapww' was invalid for the rcrt prefix.
Regenerated using encode_segwit_address('rcrt', 0, bytes(32)).
Add vchBlockSig field to CBlock class for Proof-of-Stake blocks. PoS blocks (nVersion > 2) include a block signature that must be serialized and deserialized correctly in functional tests. Changes: - Add POW_BLOCK_VERSION constant (version 2) - Add vchBlockSig slot to CBlock - Serialize/deserialize block signature for PoS blocks - Update __repr__ to show signature when present
Update CBlock validation to handle Proof-of-Stake blocks: - solve(): Skip nonce-based mining for PoS blocks (version > POW_BLOCK_VERSION) - is_valid(): Skip hash difficulty check for PoS blocks validated by signature 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Update create_block() and create_coinbase() to properly handle Proof-of-Stake blocks in the test framework. Changes to create_block(): - Detect PoS blocks by checking version > POW_BLOCK_VERSION (2) - Validate that PoS block templates contain valid coinstake - Create empty coinbase for PoS blocks (matching miner.cpp) - Add transactions from template including coinstake Changes to create_coinbase(): - Add outputScriptPubKey parameter for explicit control - Support empty vout[0] for PoS blocks (0 value, empty script) - Maintain PoW behavior for normal blocks New function sign_pos_block(): - Sign PoS block hash with ECDSA - Store DER-encoded signature in vchBlockSig Update script_BIP34_coinbase_height(): - Use OP_0 instead of OP_1 to match ReddCoin's miner.cpp
ReddCoin's compact blocks include block signature in serialization: - Add vchBlockSig field to P2PHeaderAndShortIDs class - Deserialize/serialize vchBlockSig after nonce, before shortids - Matches ReddCoin's CBlockHeaderAndShortTxIDs format in src/blockencodings.h
Add PoS block signature (vchBlockSig) to HeaderAndShortIDs class for BIP 152 compact block serialization. ReddCoin PoS blocks include a vchBlockSig field that must be preserved when creating compact blocks. Without this, compact block deserialization fails because the signature is missing from the reconstructed block. Changes: - Add vchBlockSig to HeaderAndShortIDs.__slots__ - Initialize vchBlockSig in __init__ and copy from P2P messages - Include vchBlockSig in to_p2p() serialization - Extract vchBlockSig from block in initialize_from_block() This ensures compact blocks can be properly created and reconstructed for PoS blocks with signatures.
Fix PoS block timestamp mismatch in test framework's create_block(). When getblocktemplate creates a PoS block, it sets: - coinstake.nTime = T (part of PoS proof, cannot be modified) - block.nTime = T + δ (curtime from template) This causes "bad-cs-time, coinstake timestamp violation" errors because PoS validation requires: block.nTime == coinstake.nTime The coinstake timestamp is authoritative (part of the signed PoS proof), so we must sync the block header timestamp to match it, not vice versa. This fix ensures all blocks created from templates have matching timestamps and pass PoS validation.
ReddCoin transactions include an nTime field, but the legacy signature hash (pre-BIP143) must NOT include nTime. This matches the C++ CTransactionSignatureSerializer::Serialize() implementation. Changes: - messages.py: Add serialize_for_legacy_sighash() method to CTransaction that serializes only nVersion, vin, vout, nLockTime (excludes nTime) - script.py: Update LegacySignatureHash() to use the new method This fix is required for p2p_segwit.py pre-activation tests where legacy signing is used before SegWit (BIP143) activation.
Adjust vsize from Bitcoin values (96/168) to Reddcoin values (100/172) to account for the 4-byte nTime field serialized when nVersion > 1.
- Add scrypt hash computation for PoW validation - Add scrypt256 field to CBlockHeader for PoW target comparison - Update solve() to use scrypt256 instead of sha256 for mining - Fallback to SHA256 if scrypt module not available
- Generate blocks while keeping all nodes' mocktime synchronized - Sync mocktime across all nodes every N blocks (default: 10) - Retry up to max_tries times on "no valid coinstake found" errors - Advance time by 60 seconds between retry attempts
- Add sync_time() to set all nodes to the maximum tracked mocktime - Prevents P2P disconnections caused by time drift between nodes - Call sync_time() from sync_all() to automatically sync time - Required for ReddCoin PoS block timestamp and coin age calculations
In PoS phase (after block 89 on regtest), use node.generate() and send UTXOs to MiniWallet address since generatetodescriptor requires PoW mining. This allows MiniWallet-based tests to work in both PoW and PoS phases.
ReddCoin regtest uses nCoinbaseMaturity = 60 (see src/chainparams.cpp), not 100 like Bitcoin. Update the test framework constant to match.
Add COINBASE_REWARD (545000000) for regtest premine blocks 1-10 and NORMAL_BLOCK_REWARD (300000) for blocks 11+. These values are used by wallet tests to verify expected balances.
Skip importing deterministic coinbase privkeys when setup_clean_chain is True, since there are no cache blocks to spend in that case.
PoS block generation is probabilistic and may fail with "no valid coinstake found". Add automatic retry with time advancement to make tests more reliable. This eliminates the need for manual retry loops in individual tests. - Add pos_retry_attempts parameter (default: 10) - Advance mocktime by 60s between retry attempts - Only retry on "no valid coinstake found" errors
Change PoS block detection from version-based to coinstake-based. BIP9 version bits signaling can set block version to 536870912 (0x20000000) even for PoW blocks, which incorrectly triggered PoS handling when using version > POW_BLOCK_VERSION (2). Now detect PoS by checking if the first transaction in the template is actually a coinstake (has inputs, first input not null, 2+ outputs, first output empty) rather than relying on the block version number.
Use importdescriptors() RPC for descriptor wallets instead of importprivkey() which only works with legacy wallets. Key insight: getdescriptorinfo() returns the public key version of the descriptor, but we need to keep the private key for import. Solution: use the checksum from getdescriptorinfo() but keep the original descriptor with the WIF private key intact.
Replace shared key import approach with explicit funding transactions. Previously all 3 cache keys were imported into every node, causing multiple nodes to see the same coinbase rewards. Now: - Generate all cache blocks to PRIV_KEYS[0] only - Fund nodes 1-3 via chained transactions from a single mature UTXO - Each node gets 70 UTXOs of 2.5M RDD for staking - Raise ancestor/descendant chain limits during cache generation - Remove redundant cache key imports from init_wallet() This gives each node isolated coin ownership while preserving sufficient staking balance across all nodes.
get_wallet_rpc() uses `self.rpc / wallet_path` to construct wallet RPC URLs. When self.rpc is an RPCOverloadWrapper (set during PoS wallet context), the / operator fails because the wrapper lacks __truediv__. Delegate to the underlying AuthServiceProxy.
ReddCoin uses P2SH version byte 5 for both mainnet and regtest (Bitcoin uses 196 for regtest). Update scripthash_to_p2sh() and correct the unspendable address descriptor checksum for rcrt1q.
Update TIME_GENESIS_BLOCK from Bitcoin's 1296688602 to ReddCoin's 1642570147. Tests that set mocktime relative to genesis (e.g. rpc_blockchain.py) fail with time-too-new errors using the wrong value.
CTransaction defaults nTime to time.time() (real system time), but PoS blocks use mocktime. A tx with a future nTime gets rejected. Use the node's tip block time instead.
Store _base_rpc before overwriting n.rpc with wallet-scoped proxy in init_wallet(). Use __dict__ access to avoid TestNode.__getattr__ intercepting attribute lookups (which creates bogus RPC proxies). get_wallet_rpc() now uses _base_rpc as the base URL, preventing double wallet paths like /wallet//wallet/wmulti. Also track n.mocktime alongside n.setmocktime() so framework code that reads mocktime directly stays in sync.
When a node stops and restarts, _base_rpc retained the old RPC connection with stale cookie credentials. The second init_wallet() call would skip updating _base_rpc (due to the conditional guard), causing get_wallet_rpc() to use the stale connection and trigger "incorrect password" authentication errors. Fix by clearing _base_rpc in is_node_stopped() and unconditionally capturing n.rpc in init_wallet(), since at that point n.rpc is always the fresh base connection.
Add time_sync_callback to TestNode so generate() syncs mocktime to all nodes after every block, preventing drift that breaks P2P relay. Also increase PoS retry spacing from 60s to 300s to widen the coinstake search window on retries.
Extend cache Phase 2 to fund nodes 4-5 from a second premine UTXO, giving them staking-ready coins for tests like wallet_address_types.py.
When mocktime is set, frozen time prevents outbound trickle relay Poisson timers from firing, blocking inv message delivery. Advance mocktime by 5 seconds each poll iteration so transactions propagate.
When nodes are stopped mid-test (e.g., wallet_basic stops node 3), sync_time() would crash trying to call setmocktime on nodes without RPC connections. Filter by node.running and node.rpc_connected.
Extend RPCOverloadWrapper.createwallet() with wallet_type, mnemonic, mnemonic_passphrase, language, and entropy_bits parameters. Use named args in CLI mode when BIP39 params are set to avoid null string issues with sparse positional parameters.
Prune mode is incompatible with -txindex required by PoS.
…equence The original feature_bip68_sequence.py has been adapted for PoS. Remove the temporary _pos variant and restore the original filename.
Signet chain type is not supported in ReddCoin. The node crashes on startup with -signet. Comment out until signet support is added.
AuthServiceProxy shares a single HTTP connection across all of a node's method calls. If a prior call's response read is interrupted under heavy system load (a slow/timed-out or truncated response) and the caller swallows the error, the shared connection is left in the _CS_REQ_SENT state and never reset. The next RPC on that node then fails with http.client.CannotSendRequest, even though the node itself is healthy and still serving requests. This surfaces intermittently in long PoS functional tests (e.g. feature_taproot.py) on loaded machines. Handle CannotSendRequest/ResponseNotReady in _request the same way as BrokenPipeError/ConnectionResetError: close the stale connection and retry once. CannotSendRequest is raised before any bytes of the retried request are sent, so the retry cannot double-execute the RPC.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
The PoS mocktime-advance blocks in init_wallet() and TestNode.generate() caught a bare `except Exception: pass`, which silently swallowed every error (Codacy Try/Except/Pass). Narrow both to the JSONRPCException these RPC calls actually raise when there is no tip to read yet, and log the skip at debug instead of passing silently, so genuine errors propagate.
reddink
force-pushed
the
pr/test-framework
branch
from
July 16, 2026 02:34
5ec7e39 to
1c4bb25
Compare
The RPCOverloadWrapper.createwallet wrapper declared passphrase='' and tested `if passphrase != ''`, which Codacy/Bandit flags as a possible hardcoded password (B107/B105). Default it to None and test `is not None`, matching how every other optional param in the method is already handled (disable_private_keys, blank, avoid_reuse, ...). Behaviour is unchanged: an unset passphrase still results in an unencrypted wallet.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adapts the Bitcoin Core functional test framework so it can drive a ReddCoin
Proof-of-Stake-Velocity (PoSV) node. Upstream's harness assumes a pure
Proof-of-Work chain with instant
generate()mining, no block signatures, notransaction
nTime, and Bitcoin network parameters — none of which hold forReddCoin. This PR teaches the framework about PoS block creation and signing,
mocktime-driven staking, ReddCoin's network constants and address formats, and
the wallet/cache setup PoS requires.
This is test-infrastructure only (no
src/changes) and is the foundationthe adapted functional tests depend on — the per-test adaptations
(
pr/test-adaptations) build on the helpers introduced here.What's included
PoS block support in the harness (
blocktools.py,messages.py,script.py)vchBlockSig) support, includingCBlock.solve()/is_valid()awareness of PoS blocks and coinstake handling.nTimefield support on theCTransactiontest class andserialize_for_legacy_sighash()for ReddCoin's nTime sighash handling.vchBlockSigin compact-block serialization;create_block()syncs the blocktimestamp to the coinstake and detects PoS vs BIP9 version bits.
Mocktime-driven staking (
test_framework.py,test_node.py,util.py)advance_time_for_pos()utility.generate()with PoS retry logic and time advancement;sync_time()to keepmocktime aligned across nodes;
sync_mempoolsadvances mocktime for tricklerelay.
ReddCoin network parameters & address formats (
messages.py,address.py, etc.)MAGIC_BYTES, P2P protocol version80016,MAX_MONEY, block-rewardconstants,
COINBASE_MATURITY = 60, regtest genesis block time.ADDRESS_BCRT1_UNSPENDABLEchecksum,and
bitcoin.conf→reddcoin.confthroughout.Wallet & cache setup (
test_framework.py,wallet.py)fund nodes 4–5 for 6-node tests; only import cache privkeys when using the cache.
init_wallet(); BIP39 params on thecreatewalletwrapper;__truediv__onRPCOverloadWrapperfor wallet RPCpaths.
Robustness fixes (
authproxy.py,test_node.py)CannotSendRequest/ResponseNotReadyunderload; clean up stale
_base_rpcon node stop to prevent auth failures.exceptclauses ininit_wallet()andTestNode.generate()toJSONRPCExceptionand log the skip, instead of asilent
except Exception: pass(Codacy Try/Except/Pass).createwalletwrapper'spassphrasetoNone(tested withis not None) instead of'', clearing a Bandit hardcoded-password falsepositive and matching the method's other optional params.
Test runner (
test_runner.py)skip
feature_signet, disablefeature_blockfilterindex_prune, and swapfeature_bip68_sequence_posfor the adaptedfeature_bip68_sequence.Testing
Framework-level change; exercised by the full functional suite. The harness
changes here are what allow the PoS-adapted tests to run at all — see
pr/test-adaptations, which registers and adapts the individual test cases ontop of this branch.
Compatibility / scope
src/changes — test tooling only; node behaviour is untouched.pr/test-adaptationsdepends on the helpers added here andshould merge after this (they overlap only in
test_runner.py, a one-hunkregistration-list merge).
Notes
55 commits, 22 files,
test/only. Rebased cleanly ontodevelop.