Skip to content

Enhance node functionality with BIP support and sync improvements#2

Open
metaphorics wants to merge 38 commits into
mainfrom
fix/runtime-readiness-sync-utxo
Open

Enhance node functionality with BIP support and sync improvements#2
metaphorics wants to merge 38 commits into
mainfrom
fix/runtime-readiness-sync-utxo

Conversation

@metaphorics
Copy link
Copy Markdown
Contributor

This pull request introduces new evidence verification gates for mainnet performance and MuHash parity, adds smoke tests for the G2 MuHash collection script, and improves header validation logic. It also bumps the bitcoin-rs-p2p crate version. The main focus is on strengthening test coverage and ensuring correctness of externally collected benchmarking and parity evidence.

New evidence verification gates:

  • Added the G14 performance budget verification gate in g14_perf_budgets.rs, which checks externally collected mainnet performance evidence against documented budgets for block validation throughput, UTXO commit latency, Electrum history latency, and memory usage.
  • Added the G2 MuHash parity verification gate in g02_ibd_muhash_parity.rs, which asserts that externally collected MuHash samples from bitcoin-rs and bitcoind match at required heights, enforcing strict evidence contracts.

Test improvements:

  • Introduced smoke tests for the G2 MuHash collection helper script in g2_bitcoind_muhash_script.rs, covering output format, normalization, and error handling.

Chain header validation:

  • Improved header acceptance in header_sync.rs by adding a check that prevents accepting headers with missing parents when the block tree is empty, unless the header is the network genesis block.
  • Minor test refactoring in header_sync_roundtrip.rs to import validate_header_nbits and other identifiers directly.

Dependency updates:

  • Bumped the bitcoin-rs-p2p dependency version from 0.1.0 to 0.1.1 in Cargo.toml.

The node defaults its data directory to .bitcoin-rs, which leaves runtime smoke data in the repository root as untracked files. Ignore the root data directory alongside other generated local artifacts.

ODIN-Gate: pass

Verification: git check-ignore -v .bitcoin-rs .bitcoin-rs/

Verification: git ls-files .bitcoin-rs

Verification: git status --short --ignored

Op: compress
Wire DNS seed resolution into startup and bound the shared outbound peer admission path so DNS bootstrap and RPC addnode cannot create unbounded queue or dial fanout. The drain tracks active outbound connection threads and suppresses duplicate active or connected addresses before spawning. RPC addnode now fails fast under queue pressure instead of blocking or persisting a failed add.

Verification:
- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check
- cargo test -p bitcoin-rs-rpc --no-default-features --features rocksdb,fjall,redb,mdbx addnode
- cargo test -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus dns_seed_bootstrap
- cargo test -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus outbound_
- cargo test -p bitcoin-rs-p2p --no-default-features --features rocksdb,fjall,redb peer_manager_resolves_configured_seeds
- cargo test -p bitcoin-rs-p2p --no-default-features --features rocksdb,fjall,redb system_dns_resolver_uses_configured_port_for_literal_hosts
- cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings
- cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus
- adversarial final review PASS

ODIN-Gate: pass
Op: extend
Add a narrow addr() scanobject subset to scantxoutset and back it with a read-only UTXO-set scriptPubKey scan. Unsupported descriptors and malformed scanobjects fail instead of being silently ignored.

Verification:

- cargo fmt --all -- --check

- cargo test -p bitcoin-rs-rpc --no-default-features --features "rocksdb,fjall,redb,mdbx" scantxoutset

- cargo test -p bitcoin-rs-utxo --no-default-features scan_script_pubkeys_returns_matching_live_outputs -- --exact

- cargo clippy --workspace --all-targets --no-default-features --features "rocksdb,fjall,redb,mdbx,bitcoinconsensus" -- -D warnings

- cargo test --workspace --no-fail-fast --no-default-features --features "rocksdb,fjall,redb,mdbx,bitcoinconsensus"

Op: extend
Correct headers-first sync so a fresh tree can only root the network genesis, block downloads walk forward from the applied tip, and block application extends the applied chain rather than the header tip. Bootstrap now applies the real genesis block before draining queued blocks or requesting block data, preserving applied_tip as a fully applied block cursor.

Import rejection tests now seed connected fixtures so validation assertions reach their intended consensus rules after the applied-tip predecessor guard.

Evidence:

- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check

- FEATURES='rocksdb,fjall,redb,mdbx,bitcoinconsensus'; cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features "" -- -D warnings

- FEATURES='rocksdb,fjall,redb,mdbx,bitcoinconsensus'; cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features ""

Op: correct

Restores: spec:headers-first sync keeps genesis height and downloads contiguous blocks after applied tip
Keep strict block application while making the sync layer tolerate P2P block delivery order. Received blocks are bounded, aged, and applied only when they match the next expected active-chain hash after the applied tip.\n\nVerification:\n- cargo fmt --manifest-path Cargo.toml --all -- --check\n- cargo test --manifest-path Cargo.toml -p bitcoin-rs-node --test sync_smoke --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus\n- cargo test --manifest-path Cargo.toml -p bitcoin-rs-node sync::tests --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus\n- cargo clippy --manifest-path Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings\n- cargo test --manifest-path Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus\n\nOp: correct\nRestores: spec:headers-first sync applies requested blocks in applied-tip order despite out-of-order P2P delivery
Match Bitcoin Core CScript integer encoding for the BIP34 coinbase-height prefix. Signet activates BIP34 at height 1, where Core expects OP_1 rather than raw pushdata 0101; live block 1 uses scriptSig 5151.

Verification:
- cargo fmt --manifest-path Cargo.toml --all -- --check
- cargo test --manifest-path Cargo.toml -p bitcoin-rs-consensus --no-default-features --features bitcoinconsensus bip34
- cargo clippy --manifest-path Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings
- cargo test --manifest-path Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus
- live signet smoke: getblockcount advanced to 16

Op: correct
Restores: spec:BIP34 coinbase height prefix matches Bitcoin Core CScript integer encoding on signet height 1
Add a raw-byte regression for the live signet height-one coinbase scriptSig prefix 5151 so the Core-compatible OP_1 BIP34 path is pinned without relying only on Builder-generated fixture bytes.

Verification:
- cargo fmt --manifest-path Cargo.toml --all -- --check
- cargo test --manifest-path Cargo.toml -p bitcoin-rs-consensus --no-default-features --features bitcoinconsensus bip34
- cargo clippy --manifest-path Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings
- cargo test --manifest-path Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct
Restores: test:signet_block_one_coinbase_prefix_passes
The bitcoin 0.32 Network enum is exhaustive for the five variants matched here, so the wildcard fallback is unreachable and makes kernel builds noisy. Remove it without changing any supported network mapping.

Verification:
- cargo fmt --manifest-path Cargo.toml --all -- --check
- cargo test --manifest-path Cargo.toml -p bitcoin-rs-consensus --no-default-features --features kernel -- --include-ignored
- cargo clippy --manifest-path Cargo.toml -p bitcoin-rs-consensus --all-targets --no-default-features --features kernel -- -D warnings

Op: correct
Restores: spec:kernel consensus sanity is warning-clean under the pinned bitcoin Network enum
Expose the mdbx backend feature from the index and filters crates and
forward the node mdbx feature to both runtime index users. The node state
module already instantiates txindex and filter index stores with MdbxStore;
the Cargo feature graph needs to say that directly.

Also make the node storage-backend invariant explicit: a node build with no
compiled storage backend is invalid. That path was already unusable and led
to cfg-stripped storage enums with confusing follow-on diagnostics.

Verification:
- cargo fmt --all -- --check
- FEATURES="rocksdb,fjall,redb,mdbx,bitcoinconsensus"; cargo build -p bitcoin-rs --bin bitcoin-rs --no-default-features --features "$FEATURES"
- cargo check -p bitcoin-rs-index --no-default-features --features mdbx
- cargo check -p bitcoin-rs-filters --no-default-features --features mdbx
- cargo check -p bitcoin-rs-node --no-default-features --features mdbx
- FEATURES="rocksdb,fjall,redb,mdbx,bitcoinconsensus"; cargo check -p bitcoin-rs-node --no-default-features --features "$FEATURES"

Op: correct
Restores: spec:node mdbx runtime index feature graph matches MdbxStore use
Replace the ignored no-op G14 performance gate with a fail-closed verifier for externally collected mainnet evidence. The gate stays ignored because it does not run live IBD benchmarks itself; explicit runs now require scenario metadata, bind the evidence SHA to the current git HEAD, and enforce the documented G14 thresholds.\n\nVerification:\n- cargo fmt --all -- --check\n- cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings\n- cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus\n- G14 matrix: no-env/stale-sha/budget-breach/NaN fail; threshold evidence passes\n\nOp: correct\nRestores: spec:G14 performance budgets fail closed when explicit evidence is absent and reject supplied evidence outside PLAN.md thresholds
Add regression coverage for two contextual nBits rejection seams: block import rejects a non-retarget child with changed nBits and leaves the accepted tip unchanged, and header sync rejects a retarget-boundary header that keeps stale parent nBits when the computed retarget value differs.

Op: correct

Restores: spec:contextual nBits validation rejects non-retarget continuity and retarget-boundary mismatches before accepting the candidate
Add an exact gettxoutsetinfo(["muhash"]) assertion for the fresh empty UTXO set using Bitcoin Core's known empty MuHash digest.

Op: correct

Restores: spec:gettxoutsetinfo muhash exposes the Core-compatible empty UTXO-set digest
Convert the ignored G2 parity placeholder into a fail-closed verifier for externally collected bitcoin-rs and bitcoind MuHash samples. Evidence must bind to current HEAD, a clean tracked tree, mainnet IBD, bitcoind, MuHash, the 10,000-block cadence, and exact sample equality.

Verification:

- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check

- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

- dirty-tree G2 ignored run fails closed on tracked modifications

Op: correct

Restores: spec:G2 full IBD MuHash parity gate fails closed when explicit evidence is absent and rejects stale, malformed, incomplete, or mismatched samples
Extend the existing out-of-order block sync smoke test to wire the UTXO listener the same way production handles do and assert the externally visible CoinStats snapshot after buffered block application.

This is a bounded sync-boundary invariant test. It does not prove live mainnet IBD MuHash parity, spend/removal parity, restart persistence, reorg handling, or performance superiority.

Verification:

- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check

- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct

Restores: spec:block-sync buffered apply publishes the same CoinStats/MuHash state through ApplyHandles that UTXO listener updates produce
Add a deterministic snapshot fixture for the UTXO removal listener path. The test commits two outputs, removes one, checks the listener CoinStats/MuHash state against direct CoinStats mutation, and checks the snapshot trailer uses the post-removal MuHash.

This is a bounded UTXO/coinstats mutation invariant. It does not prove live block spend application, reorg handling, restart persistence, mainnet IBD parity, or performance superiority.

Verification:

- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-coinstats --test snapshot_with_muhash

- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-coinstats --test snapshot_with_muhash -- -D warnings

- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check

- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct

Restores: spec:coinstats snapshot trailers track UTXO listener MuHash after committed removals
Add a bounded BlockSync smoke test that applies a mature coinbase spend into a non-coinbase output, then spends that output, all through the real inbound header/block path. Assert the spent UTXOs disappear, the final output remains live, and the shared CoinStats/MuHash snapshot matches an independently replayed model with removals.

Evidence:

- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check

- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-node --test sync_smoke --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-node --test sync_smoke --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct

Restores: spec:block-sync/apply_block spend removal updates shared UTXO and CoinStats/MuHash through listener path
Add an opt-in node-local G2 MuHash sampler that writes verifier-compatible sample evidence for applied blocks. Require the sample path and positive target tip together so interval-only files cannot masquerade as G2 evidence.

Startup reads and validates existing samples before node storage/ZMQ setup. Runtime write failures stay telemetry-only after tip publication so sampler I/O cannot pretend the block apply rolled back.

Evidence:

- cargo fmt --all -- --check

- cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: extend
Add a bounded Bitcoin Core sample normalizer for the existing G2 verifier evidence contract. The helper calls an operator-managed bitcoin-cli gettxoutsetinfo muhash query for the required G2 heights and emits height:muhash text without starting bitcoind, running IBD, or changing verifier semantics.

Verification: bash -n scripts/collect-g2-bitcoind-muhash-samples.sh; git diff --check; cargo fmt --all -- --check; cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings; cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: extend
Limit the node mdbx feature to the backend forwards required by node-facing filters and index support. The broader runtime-backend forwarding was review sprawl, not a load-bearing part of this fix.

Evidence: cargo fmt --all -- --check; cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings; cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus.

Op: correct

Restores: spec:node mdbx feature forwards only required filters/index backend support
Create the configured node data directory before opening the optional G2 MuHash sampler. Sample files under the data directory are valid configs; sampler validation must not reject them because the node data directory has not been created yet.

Evidence: cargo fmt --all -- --check; cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings; cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus.

Op: correct

Restores: spec:G2 sample paths under node data_dir remain valid configs
Connect block transactions against an ordered block-local UTXO view so later non-coinbase transactions can spend earlier same-block outputs without admitting forward references, same-block coinbase spends, or stale BIP68 metadata. Net same-block created-and-spent outputs before committing the block changes.

Evidence: cargo test -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus same_block --lib; cargo test -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus coinbase_maturity --lib; cargo test -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus bip68 --lib; cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings; cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus.

Op: correct

Restores: spec:block validation observes prior non-coinbase same-block outputs while preserving BIP68 and coinbase-maturity invariants
Reject coinbase transactions whose scriptSig length is outside the base CheckTransaction 2..=100 byte range. Wire the same invariant into node block transaction verification without running full coinbase prevout/script checks.

Verification:

- cargo test -p bitcoin-rs-consensus --no-default-features --features bitcoinconsensus coinbase_script_sig

- cargo test -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus verify_block_transactions_rejects_bad_coinbase_script_sig

- cargo fmt --all -- --check

- cargo clippy --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings

- cargo test --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct

Restores: spec: Bitcoin CheckTransaction coinbase scriptSig length 2..=100
Reject inbound headers payloads whose CompactSize count exceeds the Bitcoin protocol maximum before allocating or decoding header entries.

Evidence:
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-p2p --no-default-features --test wire_codec -- --nocapture
- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check
- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct
Restores: spec: Bitcoin P2P headers message count <= 2000
Restore block-local transaction verification visibility for coinbase outputs after validating their scriptSig size. Same-block coinbase spends now pass the script/prevout phase and remain rejected by the coinbase maturity pass.

Evidence:
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus verify_block_transactions_defers_same_block_coinbase_spend_to_maturity -- --nocapture
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-node --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus verify_block_transactions_rejects_bad_coinbase_script_sig -- --nocapture
- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check
- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus

Op: correct
Restores: spec: block-local transaction verification overlay includes earlier same-block coinbase outputs
Reject Core-style merkle tree mutations where equal sibling hashes are paired before odd-tail duplication. This matches CheckMerkleRoot's bad-txns-duplicate invariant without turning every duplicate txid into a non-contextual block-rule failure.

Evidence:
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-consensus --no-default-features --features bitcoinconsensus duplicate_transaction_ids_are_rejected_even_with_matching_merkle_root -- --nocapture
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-consensus --no-default-features --features bitcoinconsensus duplicate_transaction_ids_without_merkle_mutation_reach_later_validation -- --nocapture
- cargo clippy --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --all-targets --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus -- -D warnings
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --workspace --no-fail-fast --no-default-features --features rocksdb,fjall,redb,mdbx,bitcoinconsensus
- cargo fmt --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml --all -- --check
- cargo test --manifest-path /home/alpha/dev/bitcoin-rs/bitcoin-rs/Cargo.toml -p bitcoin-rs-consensus --no-default-features --features kernel -- --include-ignored

Op: correct
Restores: spec: CheckMerkleRoot rejects duplicate-subtree merkle mutations
Reject inventory-vector payloads above the existing MAX_INV_PER_MSG limit at the wire decoder before entry decoding. Covers inv, getdata, and notfound payloads that decode as Vec<Inventory>; the dispatch getdata cap remains as defense-in-depth.

Op: correct

Restores: spec: Bitcoin P2P inventory-vector messages are capped at 50,000 entries at the wire validation boundary
Reject getblocks requests whose block locator exceeds the existing MAX_LOCATOR_HASHES bound, mirroring the getheaders dispatch validation while keeping locator-length logic in one helper.

Op: correct

Restores: spec: inbound getblocks locator hashes are capped at MAX_LOCATOR_HASHES
Reject getheaders and getblocks locator payloads above MAX_LOCATOR_HASHES at the wire decoder before locator vector allocation. Keep dispatch locator checks as defense-in-depth and preserve the dispatch constant path via re-export.

Op: correct

Restores: spec: inbound locator-bearing P2P messages are capped at MAX_LOCATOR_HASHES at the wire validation boundary
Reject addr and addrv2 payloads above MAX_ADDR_MESSAGE_COUNT at the wire decoder before address vector allocation. This keeps address-relay behavior unchanged while enforcing the protocol message-size boundary.

Op: correct

Restores: spec: inbound addr and addrv2 messages are capped at 1000 address entries at the wire validation boundary
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens node correctness and evidence-driven verification by adding G2/G14 “external evidence” gates, improving block/header sync robustness, extending RPC/UTXO functionality for script-based UTXO scanning, tightening consensus validation (coinbase scriptSig bounds + merkle mutation detection), and hardening P2P message decoding limits. It also bumps bitcoin-rs-p2p to 0.1.1 and introduces supporting tests and helper scripts.

Changes:

  • Add externally-validated evidence gates for G2 (MuHash parity) and G14 (mainnet performance budgets), plus a helper script + smoke tests to collect/normalize Core MuHash samples.
  • Improve node sync/apply correctness: bootstrap genesis when needed, buffer out-of-order blocks, refine filter generation for same-block spends, and add DNS seed bootstrap + bounded outbound queue behavior.
  • Extend RPC/UTXO support: implement scantxoutset addr()-based scanning backed by a new UTXO scan_script_pubkeys API.

Reviewed changes

Copilot reviewed 41 out of 44 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/collect-g2-bitcoind-muhash-samples.sh Adds a helper to collect/normalize bitcoind MuHash samples at contract heights.
crates/utxo/tests/commit_roundtrip.rs Adds a test covering new scriptPubKey scan behavior.
crates/utxo/src/shard.rs Implements per-shard scriptPubKey scanning into an aggregate scan result.
crates/utxo/src/set.rs Introduces UtxoScan/ScannedUtxo and set/view scan APIs.
crates/utxo/src/lib.rs Re-exports new scan types from the utxo crate.
crates/rpc/tests/handler_smoke.rs Adds RPC smoke coverage for empty MuHash and getblockfilter not-found behavior; adjusts fixture setup.
crates/rpc/src/handlers/wallet.rs Implements addr()-only scantxoutset backed by UTXO script scanning + tests.
crates/rpc/src/handlers/network.rs Switches addnode outbound enqueue to try_send with explicit full/disconnected errors + tests.
crates/rpc/src/handlers/chain.rs Makes getblockfilter return NotFound when filter/header rows are missing.
crates/p2p/tests/wire_codec.rs Adds decoder bound tests for inv/getdata/notfound/addr/addrv2/locator/headers.
crates/p2p/src/wire.rs Enforces payload element caps when decoding several P2P messages; adds constants.
crates/p2p/src/peer.rs Adds compact-block negotiation state and a system DNS resolver implementation + tests.
crates/p2p/src/lib.rs Re-exports SystemDnsResolver.
crates/p2p/src/fsm.rs Records inbound sendcmpct preferences during handshake/ready states + tests.
crates/p2p/src/dispatch.rs Applies locator bounds to getheaders/getblocks at dispatch level + tests; re-exports locator cap.
crates/p2p/Cargo.toml Bumps crate version to 0.1.1.
crates/node/tests/sync_smoke.rs Adds sync tests for buffering out-of-order blocks, coin-stats/UTXO tracking, and G2 sample writing/config validation.
crates/node/tests/config_layered.rs Adds layered-config tests for G2 MuHash sample path/tip-height precedence and validation.
crates/node/src/sync.rs Buffers out-of-order blocks, bootstraps genesis on empty applied tip, and refines getdata selection/pending marking.
crates/node/src/state.rs Adds bounded outbound queue, config validation on open, and wires in G2 MuHash sampling.
crates/node/src/run.rs Adds DNS seed bootstrap worker and outbound connection drain controls (active limit, handle reaping).
crates/node/src/lib.rs Adds the new internal g2_muhash module.
crates/node/src/import.rs Refactors import tests to ensure genesis is imported first; adds nBits mismatch rejection test.
crates/node/src/g2_muhash.rs Implements persistent G2 MuHash sample writer with contract validation and tests.
crates/node/src/config.rs Adds g2_muhash_samples/g2_muhash_tip_height config options, layering, and validation.
crates/node/src/apply.rs Improves applied-predecessor logic, same-block spend handling (UTXO view/filter/changes), coinbase scriptSig validation, and optional G2 sampling.
crates/node/Cargo.toml Adjusts backend feature wiring (notably mdbx feature list) and adds tempfile dev-dependency.
crates/index/Cargo.toml Adds an mdbx feature mapping to storage mdbx.
crates/filters/Cargo.toml Adds an mdbx feature mapping to storage mdbx.
crates/consensus/src/verify_tx.rs Adds consensus check for coinbase scriptSig size bounds + tests.
crates/consensus/src/verify_block.rs Implements explicit merkle-root calculation + merkle mutation detection + tests.
crates/consensus/src/lib.rs Exposes new verify function and introduces ConsensusError::MerkleMutation and CoinbaseScriptSigSize.
crates/consensus/src/kernel.rs Makes kernel chain-type mapping explicit for all bitcoin::Network variants.
crates/consensus/src/bip34.rs Uses script::Builder::push_int for BIP34 height-prefix validation; expands tests for small-height encodings.
crates/coinstats/tests/snapshot_with_muhash.rs Refactors txout helper and adds removal tracking test for MuHash/snapshot trailer.
crates/chain/tests/header_sync_roundtrip.rs Expands header-sync tests: empty-tree root rejection, retarget nBits mismatch, and genesis inclusion handling.
crates/chain/src/header_sync.rs Prevents accepting non-genesis headers as the first header in an empty BlockTree.
Cargo.toml Bumps workspace bitcoin-rs-p2p dependency version to 0.1.1.
Cargo.lock Updates lockfile for bitcoin-rs-p2p 0.1.1 and adds tempfile where needed.
bin/bitcoin-rs/tests/gates/g14_perf_budgets.rs Adds an ignored gate that validates external mainnet performance evidence via env contract.
bin/bitcoin-rs/tests/gates/g02_ibd_muhash_parity.rs Adds an ignored gate that validates external MuHash parity evidence via env contract.
bin/bitcoin-rs/tests/g2_bitcoind_muhash_script.rs Adds smoke tests for the MuHash collection script (format, normalization, error paths).
bin/bitcoin-rs/Cargo.toml Adds tempfile dev-dependency for new script smoke tests.
.gitignore Ignores /.bitcoin-rs/ directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/node/src/sync.rs
Comment thread crates/rpc/src/handlers/wallet.rs Outdated
Comment thread bin/bitcoin-rs/tests/g2_bitcoind_muhash_script.rs
@metaphorics
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6082045535

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/rpc/src/handlers/network.rs
Comment thread crates/rpc/src/handlers/wallet.rs Outdated
Fix stale block-buffer pruning on idle ticks, enforce scantxoutset scanobject contracts, preserve addnode pinning when the outbound queue is full, and gate Unix-only G2 helper tests.

Op: correct

Restores: spec:PR #2 review-comment contracts for sync buffer pruning, scantxoutset inputs, addnode persistence, and Unix-only helper tests
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5fea2b4ced

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/rpc/src/handlers/wallet.rs Outdated
@metaphorics
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 44 changed files in this pull request and generated 3 comments.

Comment thread crates/node/Cargo.toml
Comment thread crates/utxo/src/shard.rs
Comment thread crates/rpc/src/handlers/wallet.rs
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly strengthens the node's consensus and synchronization logic by introducing stricter Merkle root mutation checks, coinbase scriptSig size validation, and an out-of-order block buffer. It also implements MuHash sampling for IBD parity verification, address-based UTXO scanning for the scantxoutset RPC, and DNS seed bootstrapping for the P2P layer. The review feedback focuses on optimizing the synchronization logic by reducing redundant locking and pruning operations within the block buffer and ensuring that block header statuses are correctly updated to Active upon successful application.

Comment thread crates/node/src/apply.rs
Comment thread crates/node/src/sync.rs
Comment thread crates/node/src/sync.rs
Enable bitcoin-rs-storage/mdbx when the node mdbx feature is selected so cfg(feature = "mdbx") code can reference MdbxStore.

Op: correct

Restores: spec:node mdbx feature exposes the storage MDBX backend it directly uses
Reject scantxoutset start calls that omit scanobjects and remove the stale whole-set summary test that contradicted the RPC scan contract.

Op: correct

Restores: spec:scantxoutset start requires non-empty scanobjects for descriptor scans
Remove the unused direct workspace dependency declaration for crossbeam-skiplist. The crate remains in Cargo.lock through fjall/lsm-tree, which is the live owner of that transitive dependency.

Op: compress
@metaphorics
Copy link
Copy Markdown
Contributor Author

@copilot review

@metaphorics
Copy link
Copy Markdown
Contributor Author

@codex review

@metaphorics
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant enhancements to consensus validation, node synchronization, and RPC functionality. Key changes include the implementation of Merkle tree mutation checks to address CVE-2012-2459, coinbase scriptSig size enforcement, and a more robust block synchronization process that buffers out-of-order blocks. Additionally, the scantxoutset RPC now supports address scanning, and new infrastructure for DNS seed bootstrapping and G2 MuHash sampling has been added. Feedback highlights a performance optimization opportunity in the UTXO scanning logic, suggesting the use of a HashSet to reduce lookup complexity from linear to logarithmic.

Comment thread crates/utxo/src/shard.rs
Comment on lines +164 to +186
&self,
scripts: &[ScriptBuf],
scan: &mut UtxoScan,
) -> Result<(), UtxoError> {
let cell = self.inner.read();
cell.with_dependent(|_arena, table| {
for record in &table.table {
for output in record.iter_outputs() {
scan.txouts = scan.txouts.saturating_add(1);
let script = script_slice(table, output).ok_or(UtxoError::CorruptArena)?;
if scripts.iter().any(|target| target.as_bytes() == script) {
scan.unspents.push(ScannedUtxo {
outpoint: OutPoint::new(record.txid(), output.vout),
txout: txout_from_parts(output.value, script),
coinbase: output.coinbase,
height: output.height,
});
}
}
}
Ok(())
})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The scan_script_pubkeys implementation performs an $O(N_{utxo} \times N_{scripts})$ operation by iterating over all provided scripts for every output in the UTXO set. For large UTXO sets and multiple scan objects in scantxoutset, this will be significantly slower than necessary. Converting the scripts slice to a HashSet (or BTreeSet for byte slices) before starting the shard iteration would reduce the complexity to $O(N_{utxo} \times \log(N_{scripts}))$.

References
  1. Identify performance bottlenecks and optimize for efficiency by avoiding unnecessary nested iterations over large data structures.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff754c9cd9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +286 to +287
Err(TrySendError::Full(_) | TrySendError::Disconnected(_)) if persist => {}
Err(TrySendError::Full(_)) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Surface outbound enqueue failures for addnode "add"

Do not ignore TrySendError::Full/Disconnected for persistent add requests here: this now returns success even when no connection attempt was queued. In this codebase, added_nodes is only used for RPC reporting (repo-wide search shows no reconnect worker consuming it), so swallowing the send failure means addnode ... add can silently do nothing in production when the queue is full or closed.

Useful? React with 👍 / 👎.

Comment thread crates/node/src/sync.rs
Comment on lines 79 to +80
self.drain_inbound_headers();
self.ensure_genesis_tip();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bootstrap genesis before draining inbound headers

This ordering regresses initial header acceptance with the new empty-tree root rule in accept_headers (validate_empty_tree_root): if a peer header batch arrives while the tree is still empty, drain_inbound_headers runs first and rejects non-genesis headers as missing-parent, then drops that batch. Seed genesis before header draining so early/unsolicited headers messages are not discarded.

Useful? React with 👍 / 👎.

…builds

Agent-Logs-Url: https://github.com/gosuda/bitcoin-rs/sessions/3a7e582e-4853-4ec4-8f46-66e71f330c89

Co-authored-by: metaphorics <152830360+metaphorics@users.noreply.github.com>
@metaphorics
Copy link
Copy Markdown
Contributor Author

@codex review

@metaphorics
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant enhancements to the node's block processing and synchronization logic, including the implementation of performance and parity gates (G2 and G14), improved handling of same-block spends, and out-of-order block buffering. It also adds support for BIP158 filter generation during block application, DNS seed bootstrapping for P2P connections, and the scantxoutset RPC method for address scanning. Feedback from the review highlights the use of the unstable let_chains feature which may break builds on stable Rust, and suggests using saturating_duration_since for more robust time calculations with Instant.

Comment on lines +209 to +213
if let Some(parent) = parent_dir(path)
&& let Ok(dir) = std::fs::File::open(parent)
{
let _ = dir.sync_all();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of the let_chains feature (&& let) is currently unstable in Rust. Unless this project explicitly requires a nightly toolchain with this feature enabled, this will cause a compilation error on stable Rust. It is safer to use nested if let statements.

Suggested change
if let Some(parent) = parent_dir(path)
&& let Ok(dir) = std::fs::File::open(parent)
{
let _ = dir.sync_all();
}
if let Some(parent) = parent_dir(path) {
if let Ok(dir) = std::fs::File::open(parent) {
let _ = dir.sync_all();
}
}

Comment thread crates/node/src/sync.rs
}

fn prune_received_blocks(received: &mut HashMap<Hash256, ReceivedBlock>, now: Instant) {
received.retain(|_hash, entry| now.duration_since(entry.received_at) < RECEIVED_BLOCK_TIMEOUT);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using duration_since on Instant can panic if the clock is not monotonic (though Instant aims to be). Using saturating_duration_since is more defensive and prevents potential panics in edge cases.

Suggested change
received.retain(|_hash, entry| now.duration_since(entry.received_at) < RECEIVED_BLOCK_TIMEOUT);
received.retain(|_hash, entry| now.saturating_duration_since(entry.received_at) < RECEIVED_BLOCK_TIMEOUT);

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 46 changed files in this pull request and generated 2 comments.

Comment on lines +77 to +90
fn scanobjects_param(params: &Value) -> Result<&sonic_rs::Array, RpcError> {
let array = params_array(params)?;
let Some(scanobjects) = array.get(1) else {
return Err(RpcError::InvalidParams(
"scanobjects are required for scantxoutset start",
));
};
let scanobjects = scanobjects
.as_array()
.ok_or(RpcError::InvalidType("scanobjects must be an array"))?;
if scanobjects.is_empty() {
return Err(RpcError::InvalidParams("scanobjects must not be empty"));
}
Ok(scanobjects)
Comment thread crates/node/src/sync.rs
Comment on lines +34 to +42
/// Time after which a received out-of-order block is discarded.
const RECEIVED_BLOCK_TIMEOUT: Duration = Duration::from_mins(1);
/// Maximum number of received blocks waiting for their predecessor.
const RECEIVED_BLOCK_BUDGET: usize = 128;

struct ReceivedBlock {
block: bitcoin::Block,
received_at: Instant,
}
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.

3 participants