Skip to content

fix: BUMP txid leaf misplaced in Path[0] and nil deref in CalculateRootGivenTxid#89

Open
tngdomingo wants to merge 2 commits into
libsv:masterfrom
tngdomingo:fix/bump-txid-leaf-order
Open

fix: BUMP txid leaf misplaced in Path[0] and nil deref in CalculateRootGivenTxid#89
tngdomingo wants to merge 2 commits into
libsv:masterfrom
tngdomingo:fix/bump-txid-leaf-order

Conversation

@tngdomingo

Copy link
Copy Markdown

Bugs

1. NewBUMPFromMerkleTreeAndIndex misplaces the txid leaf in Path[0].
The position of the txid leaf (before or after its sibling) is decided by an oddTxIndex flag that is set whenever the offset at any tree level is odd, instead of only the leaf level's parity. For a transaction at the last (even) index of a block with an odd transaction count, Path[0] comes out as [duplicate, txid] — the padding leaf first.

2. CalculateRootGivenTxid panics on duplicate leaves.
While searching Path[0] for the txid it dereferences each leaf's Hash without a nil check. A duplicate leaf has a nil Hash, so any BUMP shaped as above (or any malformed BUMP with a duplicate leaf first) causes a nil-pointer panic.

Reproduction

Any block with an odd number of transactions, requesting the BUMP for the transaction at the last (even) index — e.g. an 11-tx block, tx index 10. Construction misorders the path; verification then segfaults. Found in production: ARC (Teranode) block processing crash-looped on such blocks, and the malformed BUMPs it had already stored crashed downstream consumers on parse-and-verify.

Fixes

  • Derive oddTxIndex from txIndex&1 before the level loop (leaf-level parity only).
  • Nil-check l.Hash in the txid search loop — this also protects consumers verifying previously-produced malformed BUMPs.

Test

TestNewBUMPFromMerkleTreeEverySizeAndIndex: every block size up to 33 transactions × every transaction index, each BUMP verified via CalculateRootGivenTxid against the root from BuildMerkleTreeStoreChainHash. Panics with the nil deref on current master; passes with the fixes. Full existing suite passes unchanged.

🤖 Generated with Claude Code

…otGivenTxid

NewBUMPFromMerkleTreeAndIndex decided the txid leaf's position in Path[0]
from an odd-offset flag accumulated across all tree levels instead of the
leaf level's parity. For a transaction at the last (even) index of a block
with an odd transaction count, Path[0] came out as [duplicate, txid].

CalculateRootGivenTxid then dereferenced the duplicate leaf's nil Hash
while searching for the txid and panicked. The nil check also protects
against previously produced malformed BUMPs.

Adds an exhaustive regression test covering every block size up to 33
transactions and every transaction position.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mergify mergify Bot added the bug-P3 label Jul 7, 2026
@mergify

mergify Bot commented Jul 7, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

CalculateRootGivenTxid resolves leaves by offset, so the exhaustive test
alone cannot detect a misordered Path[0]. Assert ascending offset order at
every level, plus a focused test pinning the exact bug shape: 11-tx block,
tx at index 10 must produce Path[0] = [txid, duplicate].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant