Bitcoin-parity audit and fixes

Every disclosed Bitcoin / Bitcoin Core consensus bug class, mapped to the equivalent attack on the eWatts reference implementation and closed. Last pass: 2026-09-12.

12 / 12
Parity tests defended
9
Bug classes fixed
0
Known open exploits

Method

The reference implementation in Rust is tested against the attack classes that forced hard forks or emergency releases in Bitcoin. Each class gets a test that rebuilds the exploit against the real eWatts types and asserts the outcome. The suite is a regression gate: if any fix is reverted, the suite fails.

cargo test --test bitcoin_parity_suite -- --nocapture
# 12 tests, all must report DEFENDED

What was broken, and what changed

Ordered by severity. Each row is one confirmed exploit before the fix.

ClassSeverityRoot causeFixWhere
CVE-2018-17144 Critical A transaction could reference the same UTXO twice (same or different key image). The input counted twice, so 5,000 in produced 9,000 out. Value was minted from a duplicated input. Reject duplicate (previous_tx_hash, output_index) pairs inside a single transaction, in both the pre-check and the spend path. state.rs fixed
CVE-2010-5139 Critical A transaction with no inputs, placed outside index 0, was applied as if it were the coinbase. Arbitrary value minted with no UTXO consumed. Only the coinbase (index 0) may have empty inputs. Any other no-input transaction is rejected at validation and at apply time. state.rs fixed
CVE-2010-5139 Critical Coinbase output sum used unchecked u64 addition. Two large outputs overflowed the sum: panic in debug, wrap in release, breaking the emission guard. Checked arithmetic (try_fold) for every emission and coinbase sum, in both state and consensus. state.rs, consensus.rs fixed
CVE-2012-2459 High Merkle root self-paired the odd leaf, so [A,B,C] and [A,B,C,C] hashed to the same root. Two different bodies, one header commitment. RFC 6962-style body root: domain-separated leaves (tx vs commitment), domain-separated internal nodes, odd node carried up instead of duplicated. Duplicating a transaction now changes the root. block.rs fixed
CVE-2024-52912 High No timestamp rule. A block could carry any timestamp, including u64::MAX/2 or a value before its parent, allowing timewarp and difficulty manipulation across the window. Median-time-past over the last 11 blocks, plus a bounded future drift of 2 hours relative to the parent, enforced in consensus. consensus.rs fixed
CVE-2019-25220 High The difficulty and timestamp gate lived only on the P2P ingress path. Any other writer (orphan resolution, sync ingest, reload) could persist a block that failed consensus, enabling low-difficulty chain spam. A cheap parent-linked gate (difficulty equals parent, timestamp rule) now runs inside ChainStore::add_block for every writer, plus an add_block_validated entry point for full validation. chain.rs fixed
CVE-2024-35202 Medium Compact block reconstruction dropped the body commitments and skipped the merkle root check, returning a body that could not hash to its own header. Commitments travel in the compact block and are restored; reconstruction verifies the body root against the header and rejects a mismatch. p2p.rs fixed
CVE-2025-54605 Medium The raw block save path wrote anything it was handed, including blocks that fail consensus, filling disk with invalid data. The persistence boundary validates the block (body root, proof binding, emission bounds) before writing. Invalid blocks never reach disk, from any path. store.rs fixed
Body commitments High The commitment set (bandwidth declarations that drive emission) was not covered by the merkle root or the proof hash. A relay could swap a validly signed commitment for another and change who gets paid, unauthenticated. The body root now commits the transactions and the commitments, with separate leaf domains. The same root binds the header, so swapping a commitment changes the header. block.rs, consensus.rs, main.rs fixed

Regression evidence

Output from the parity suite after the fixes. Each line is one attack class, rebuilt against the current code.

[CVE-2018-17144]  duplicate input in one tx        -> DEFENDED  (validate + spend rejected)
[CVE-2018-17144b] same utxo spent twice in a block -> DEFENDED  (second spend: UTXO not found)
[CVE-2010-5139]   no-input tx outside index 0      -> DEFENDED  (only the coinbase may create value)
[CVE-2010-5139]   coinbase output sum overflow     -> DEFENDED  (Coinbase amount overflow)
[CVE-2012-2459]   duplicate tx vs merkle root      -> DEFENDED  (roots differ)
[CVE-2019-25220]  low-difficulty block to store   -> DEFENDED  (consensus + store both reject)
[CVE-2024-52912]  timestamp rule                  -> DEFENDED  (future drift + median-time-past)
[CVE-2024-35202]  compact reconstruction           -> DEFENDED  (body committed, commitments carried)
[CVE-2025-54605]  raw save path                   -> DEFENDED  (invalid block not persisted)
[CVE-2025-46598]  output count amplification      -> DEFENDED  (linear, ~1ms for 50k outputs)
[CVE-2024-52911]  validation cache (N/A)          -> DEFENDED  (no script engine, no tx cache)
[body-commitments] commitments in the body root  -> DEFENDED  (swapped commitment rejected)

Full report: repository, audit/BITCOIN-PARITY-2026-09-12.md.


What this covers, and what it does not

Covered

Consensus-level value creation, input double-spending, body authentication, timewarp, chain ingest gating, and persistence boundaries. These are the classes that have cost Bitcoin hard forks.

Not covered here

  • Signature-layer post-quantum migration (roadmap: hybrid ed25519 + ML-DSA or FALCON activation gate).
  • The privacy layer (MLSAG, stealth addresses, Pedersen commitments) has no mature post-quantum equivalent; the current stack is curve25519-based.
  • Network-layer DoS beyond the bounded orphan pool and the token-bucket rate limiter.
  • Formal verification. The suite is adversarial and regression-based, not a proof of correctness.