Skip to content

bitcoin jets: implement Bitcoin jet cost and CMR tables; split cost padding API - #405

Draft
delta1 wants to merge 4 commits into
BlockstreamResearch:masterfrom
delta1:pr/bitcoin-jets-cost-padding-cjetptr
Draft

delta1 wants to merge 4 commits into
BlockstreamResearch:masterfrom
delta1:pr/bitcoin-jets-cost-padding-cjetptr

Conversation

@delta1

@delta1 delta1 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Includes and supercedes #386 #387 #388

Implements the rest of the Bitcoin jet environment from #331: the cost() and cmr() stubs for Bitcoin jets are replaced with full tables derived from the canonical primitiveJetNode.inc in libsimplicity, covering all 428 jets in the Bitcoin jet set.

Splits Cost::get_padding into a chain-agnostic get_padding_size plus a chain-specific get_padding_bytes. While Elements uses an annex item ([0x50] + zeros), Bitcoin must pad with a plain all-zero stack item.

Also declares the FFI bindings for the env-context Bitcoin jets (with tests exercising them against a real transaction environment) and drops the now-unnecessary Arcs in environments.

Closes the remaining gaps in RedeemNode::prune(): the Rust Bit Machine can now execute Bitcoin jets against a real CTxEnv in order to rewrite dead case branches into asserts.

Draft until I figure out how to resolve this with upstream code generation for simplicity-sys/src/c_jets/c_env/bitcoin.rs

Replace the unimplemented!() stub in Bitcoin::cmr() with a match table
of 32-byte CMR values. Each entry is derived from the canonical
primitiveJetNode.inc in libsimplicity. The table covers all jets in the
Bitcoin jet set.

The cost() method remains a stub pending a separate commit.
Replace the unimplemented!() cost() stub for Bitcoin jets with a match
table derived from primitiveJetNode.inc (.cost values, milliweight).

Add a #[cfg(test)] module with two exhaustive self-checks, both derived
from the canonical primitiveJetNode.inc in libsimplicity:

- cmr_matches_spec asserts all 428 Bitcoin jet CMRs.
- cost_matches_spec asserts all 428 Bitcoin jet costs.

Both tables are hardcoded copies of C data, so these tests are what
detects drift from the canonical source.
Add Cost::get_padding_size, which returns the chain-agnostic byte length
of the witness stack item required to bring a program cost within budget.
The required length depends only on the item's serialized size, not its
content, and is the same for Bitcoin and Elements.

Add Cost::get_padding_bytes, which returns the chain-specific bytes that
fill that length:
- Bitcoin (cfg bitcoin, not elements): a plain all-zero item. A leading
  0x50 byte would be misread as an annex, so Bitcoin must not use one.
- Elements (cfg elements): an annex item: [0x50] followed by zeros.

Deprecate Cost::get_padding (Elements annex form), which now delegates
to get_padding_bytes. Make get_budget and is_budget_valid chain-agnostic
by computing the witness-stack serialized length with explicit CompactSize
math instead of the elements consensus encoder.

The padding arithmetic itself is unchanged; only the surrounding API is
split. Extend the Bitcoin test with the CompactSize boundary deficits
(254, 255, 65 539, 65 540), where the length prefix grows and the smallest
covering item is one byte larger than the deficit would suggest.
Declare the FFI bindings for the Bitcoin jets whose C implementations
read the transaction environment (sig_all_hash, check_lock_*,
current_index, num_inputs, tx_hash, tap_env_hash, annex_hash, the
tapbranch/tapleaf/taptweak builders, and the remaining tx-context jets
in bitcoinJets.c). These match the rustsimplicity_0_8_bitcoin_* symbols
already generated in jets_ffi for the elements backend.

c_jet_ptr itself is unchanged: the Bitcoin backend routes every jet,
including these, through the existing jets_wrapper wrappers, which
forward the Rust CTxEnv reference to the same C symbols. The new
declarations are not separately bound in the generated jet table.

Add src/jet/bitcoin/tests.rs, which exercises the environment path
end to end against a real transaction:

- c_jet_env_is_the_tx_env checks that the JetEnvironment hands out the
  same CTxEnv the backend was built with, rather than a unit value.
- env_jets_execute_with_real_environment runs an env-context jet through
  the Rust Bit Machine and checks it observes the live environment.
- bip_0340_verify_ptr_resolves covers the one env-free crypto jet, which
  resolves through the shared wrapper.
- check_lock_height_executes_against_env executes a jet that reads the
  environment and asserts on its result.

These guard the property that RedeemNode::prune() depends on: the Rust
Bit Machine must be able to execute Bitcoin jets against a real
transaction environment in order to rewrite dead case branches into
asserts.
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.

1 participant