Skip to content

Commit 5aeb0ec

Browse files
sgbettclaude
andcommitted
test: genuinely exercise late-bound BUMP path in from_beef spec
The previous spec invoked merge_bump after merge_transaction, which triggers F5.6's retroactive upgrade. The ancestor then landed on-wire as FORMAT_RAW_TX_AND_BUMP and the parser populated merkle_path at read time — before find_atomic_transaction ever ran. The spec passed but would still have passed with the pre-#468 from_beef implementation (bare subject extraction without find_atomic_transaction routing). Rewrite to bypass merge_bump: push the BUMP directly into beef.bumps so the ancestor stays FORMAT_RAW_TX on-wire. Add a sanity assertion that the serialised ancestor.format is FORMAT_RAW_TX, so a future change that reintroduces the F5.6 path silently would fail the spec. Verified the spec now fails when from_beef is reverted to the old `beef.transactions.reverse.find(&:transaction)&.transaction` shape. Ref: remote review on PR #473, bug_001_1. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b60743d commit 5aeb0ec

File tree

1 file changed

+22
-6
lines changed

1 file changed

+22
-6
lines changed

gem/bsv-sdk/spec/bsv/transaction/transaction_spec.rb

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -793,16 +793,22 @@ def add_input(child_tx, parent_tx)
793793
end
794794

795795
it 'attaches late-bound BUMPs on FORMAT_RAW_TX ancestors' do
796-
# Build a transaction with no merkle_path (raw tx only).
796+
# This spec proves `from_beef` routes through `find_atomic_transaction`
797+
# for the late-bind case: ancestor stored on-wire as FORMAT_RAW_TX
798+
# (has_bump=0) while its BUMP lives in @bumps separately.
799+
#
800+
# We deliberately bypass `merge_bump`, which runs F5.6's retroactive
801+
# upgrade and would promote the ancestor to FORMAT_RAW_TX_AND_BUMP —
802+
# that path is already tested elsewhere and doesn't exercise
803+
# `find_atomic_transaction`'s late-bind because the parser wires
804+
# `merkle_path` at read time from `has_bump=1`.
797805
ancestor = described_class.new
798806
ancestor.add_output(BSV::Transaction::TransactionOutput.new(satoshis: 1500, locking_script: lock))
799807

800808
subject = described_class.new
801809
subject.add_output(BSV::Transaction::TransactionOutput.new(satoshis: 500, locking_script: lock))
802810
add_input(subject, ancestor)
803811

804-
# Build a BEEF manually: ancestor as FORMAT_RAW_TX, then add a BUMP
805-
# whose level-0 leaf matches the ancestor's txid — the late-bound case.
806812
# PathElement hashes use internal (wire) byte order: txid.reverse.
807813
ancestor_txid_internal = ancestor.txid.reverse
808814
sibling_hash = BSV::Primitives::Digest.sha256('late_sibling')
@@ -814,16 +820,26 @@ def add_input(child_tx, parent_tx)
814820

815821
beef = BSV::Transaction::Beef.new
816822
beef.merge_transaction(subject)
817-
# merge_bump after merge_transaction to trigger the F5.6 retroactive upgrade
818-
# from FORMAT_RAW_TX to FORMAT_RAW_TX_AND_BUMP for the ancestor entry.
819-
beef.merge_bump(bump)
823+
# Push the BUMP directly into @bumps, bypassing merge_bump so F5.6 does
824+
# NOT fire. The ancestor entry stays FORMAT_RAW_TX on-wire.
825+
beef.bumps << bump
820826
binary = beef.to_binary
821827

828+
# Sanity: the serialised ancestor must be FORMAT_RAW_TX (has_bump=0).
829+
# If this fails the spec is no longer exercising the late-bind path.
830+
reparsed = BSV::Transaction::Beef.from_binary(binary)
831+
ancestor_entry = reparsed.transactions.find { |bt| bt.transaction&.txid == ancestor.txid }
832+
expect(ancestor_entry.format).to eq(BSV::Transaction::Beef::FORMAT_RAW_TX)
833+
822834
result = described_class.from_beef(binary)
823835
expect(result).not_to be_nil
824836
ancestor_recovered = result.inputs[0].source_transaction
825837
expect(ancestor_recovered).not_to be_nil
826838
expect(ancestor_recovered.merkle_path).not_to be_nil
839+
# Proof the BUMP really came from late-bind: the wired merkle_path must
840+
# be the same object (or equal by block_height + leaf hash) as the one
841+
# we pushed into @bumps.
842+
expect(ancestor_recovered.merkle_path.block_height).to eq(850_000)
827843
end
828844

829845
it 'returns the subject tx for an Atomic BEEF (subject_txid set)' do

0 commit comments

Comments
 (0)