Skip to content

Class BSV::Transaction::MerklePath

Inherits: Object

A BRC-74 merkle path (BUMP — Bitcoin Unified Merkle Path).

Encodes the proof that a transaction is included in a block by storing the minimum set of intermediate hashes needed to recompute the block's merkle root from a given transaction ID.

@example Parse a BUMP from hex and compute the merkle root

mp = BSV::Transaction::MerklePath.from_hex(bump_hex)
root_hex = mp.compute_root_hex(txid_hex)

Attributes

block_height [R]

  • @return [Integer] the block height this merkle path belongs to

path [R]

  • @return [Array>] tree levels, each an array of leaves

Public Class Methods

from_binary(data, offset = 0)

Deserialise a merkle path from BRC-74 binary format. - @param data [String] binary data - @param offset [Integer] byte offset to start reading from - @return [Array(MerklePath, Integer)] the merkle path and bytes consumed

from_hex(hex)

Deserialise a merkle path from a BRC-74 hex string. - @param hex [String] hex-encoded BUMP data - @return [MerklePath] the parsed merkle path

from_tsc(txid:, index:, nodes:, block_height:)

Construct a MerklePath from a TSC (Bitcoin SV "Transaction Status Check") merkle proof, as returned by the WhatsOnChain +/tx/{txid}/proof/tsc+ endpoint.

The TSC format gives a flat list of sibling hashes (leaf-to-root order), one per tree level. BRC-74 (BUMP) requires a multi-level structure with explicit tree positions. This method does the conversion. Getting it wrong (e.g. flat array in a single level) produces a BUMP that ARC rejects. - @param txid [String] hex-encoded transaction ID in display byte order - @param index [Integer] the transaction's position in the block - @param nodes [Array] sibling hashes leaf-to-root, each a 32-byte hex string in display byte order, or +""+ for a duplicate node - @param block_height [Integer] the block's height (TSC carries the block hash; the caller must look up the height separately) - @return* [MerklePath] a BRC-74 merkle path equivalent to the TSC proof

@example Convert a WoC TSC proof

tsc = JSON.parse(woc_response).first
mp = BSV::Transaction::MerklePath.from_tsc(
  txid:         tsc['txOrId'],
  index:        tsc['index'],
  nodes:        tsc['nodes'],
  block_height: 612_251
)
mp.compute_root_hex(tsc['txOrId']) #=> the block's merkle root

merkle_tree_parent(left, right)

Compute the parent hash of two sibling nodes. - @param left [String] 32-byte left child hash - @param right [String] 32-byte right child hash - @return [String] 32-byte parent hash (double-SHA-256 of concatenation)

Public Instance Methods

combine(other)

Merge another merkle path into this one.

Both paths must share the same block height and merkle root. After combining, this path contains the union of all leaves, trimmed to the minimum set required to prove every txid-flagged leaf. The trim matches the TS SDK's combine behaviour and prevents accumulation of unnecessary sibling hashes across repeated merges. - @param other [MerklePath] the path to merge in - @raise [ArgumentError] if block heights or merkle roots differ - @return [self] for chaining

compute_root(txid = nil)

Recompute the merkle root from this path and a transaction ID. - @param txid [String, nil] 32-byte txid in internal byte order (auto-detected if nil) - @raise [ArgumentError] if the txid is not found in the path - @return [String] 32-byte merkle root in internal byte order

compute_root_hex(txid_hex = nil)

Recompute the merkle root and return it as a hex string. - @param txid_hex [String, nil] hex-encoded txid (display order) - @return [String] hex-encoded merkle root (display order)

extract(txid_hashes)

Extract a minimal compound MerklePath covering only the specified transaction IDs.

Given a compound path (e.g. one merged from multiple single-leaf proofs in the same block), this method reconstructs the minimum set of sibling hashes at each tree level for every requested txid, assembles them into a new trimmed compound path, and verifies that the extracted path computes the same merkle root as the source.

The primary use case is +Transaction#to_beef+: when a BUMP loaded from a proof store carries +txid: true+ flags for transactions that are not part of the current BEEF bundle, extracting only the bundled txids strips the phantom flags (and the now-unneeded sibling nodes) from the serialised output. See issue #302 for background.

Matches the TS SDK's MerklePath.extract behaviour. - @param txid_hashes [Array] 32-byte txids in internal byte order (reverse of display order). To pass hex strings, use +txid_hexes.map { |h| [h].pack('H').reverse }+. - @raise [ArgumentError] if +txid_hashes+ is empty, any requested txid is not present in the source path's level 0, or the extracted path's root does not match the source root - @return* [MerklePath] a new trimmed compound path proving only the requested txids

initialize(block_height:, path:)

  • @param block_height [Integer] the block height
  • @param path [Array>] tree levels
  • @raise [ArgumentError] if block_height is negative, path is empty, any level is not an Array, or level 0 has no txid-flagged leaf
  • @return [MerklePath] a new instance of MerklePath

initialize_copy(source)

Produce an independent copy: a new MerklePath whose outer path array and each inner level array can be mutated (via {#combine}, {#trim}, {#extract}) without affecting the original. PathElements themselves are immutable and are shared between the original and the copy. - @param source [MerklePath] the MerklePath being copied from - @return [void]

to_binary()

Serialise the merkle path to BRC-74 binary format. - @return [String] binary BUMP data

to_hex()

Serialise the merkle path to a BRC-74 hex string. - @return [String] hex-encoded BUMP data

trim()

Remove all internal nodes that are not required by level zero txid-flagged leaves. Assumes the path has at least the minimum set of sibling hashes needed to prove every txid leaf. Leaves each level sorted by increasing offset.

This is the Ruby port of the TypeScript SDK's MerklePath.trim. It is called implicitly by {#combine} and {#extract} and rarely needs to be invoked directly. - @return [self] for chaining

verify(txid_hex, chain_tracker)

Verify that this merkle path is valid for a given transaction.

Computes the merkle root from the path and txid, then checks it against the blockchain via the provided chain tracker.

For coinbase transactions (offset 0 in the merkle tree), an additional maturity check is performed: the coinbase must have at least 100 confirmations before it is considered spendable/valid.

NOTE: The TS SDK has an inverted coinbase maturity check at MerklePath.ts:378 (this.blockHeight + 100 < height), which rejects mature coinbase transactions and accepts immature ones — the opposite of the intended behaviour. The correct logic is: reject when current_height - block_height < 100 (immature). - @param txid_hex [String] hex-encoded transaction ID (display order) - @param chain_tracker [ChainTracker] chain tracker to verify the root against - @return [Boolean] true if the computed root matches the block at this height