Skip to content

bitcoin jets: wire c_jet_ptr for all Bitcoin jets - #388

Open
delta1 wants to merge 16 commits into
BlockstreamResearch:masterfrom
delta1:pr/bitcoin-jets-c-jet-ptr
Open

delta1 wants to merge 16 commits into
BlockstreamResearch:masterfrom
delta1:pr/bitcoin-jets-c-jet-ptr

Conversation

@delta1

@delta1 delta1 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Depends on #387

Completes the Bitcoin jet set by implementing c_jet_ptr -- the
pointer to each jet's C implementation that the Rust BitMachine uses
to execute it. Before this stack, every Bitcoin jet hit
unimplemented!() here, which meant RedeemNode::prune() could not
execute Bitcoin programs at all: pruning needs to run the program to
decide which case branches are dead and rewrite them into
assertl/assertr, and the script interpreter's CHECK_EXEC/CHECK_CASE
anti-DoS rules require that pruned form. So without this, Bitcoin
Simplicity spends could not be constructed.

Three commits:

  1. bitcoin jets: implement c_jet_ptr for pure/core jets
    Wires the jets that share C wrappers in jets_wrapper and ignore
    the environment argument (sha256 family, all_8, eq_256,
    verify) -- these work immediately since they're compatible with
    the placeholder () CJetEnvironment. Everything needing a real
    tx environment is left as an explicit unimplemented!().

  2. bitcoin jets: wire real tx environment and env-context jets
    Changes the Bitcoin JetEnvironment so c_jet_env() returns the
    real bitcoin CTxEnv instead of (), letting the Rust BitMachine
    execute tx-context jets against the C interpreter's live
    environment. Adds FFI declarations/wrappers for the env-context
    jets (sig_all_hash, lock-height/distance/duration checks,
    current_index, num_inputs, tx_hash, tap_env_hash) plus
    bip_0340_verify, and tests confirming the env is live.

  3. bitcoin jets: wire c_jet_ptr for all remaining jets
    Declares FFI bindings and c_jet_ptr for every jet still left
    unimplemented!(), each mapped to its rustsimplicity_0_8_bitcoin_*
    C symbol in bitcoinJets.c. After this, any Bitcoin Simplicity
    program can execute end-to-end through c_jet_ptr, and
    RedeemNode::prune() can produce valid pruned programs.

apoelstra and others added 16 commits October 5, 2026 14:28
Don't actually build bitcoin yet; just refactor build.rs
For dumb "C polymorphism" reasons we have two structures in the C code named
txEnv. We can call the corresponding Rust structures different things, but
we need to access them from the C file env.c, which provides some C wrappers
for FFI stuff.

Since you can't have two different structs with the same name in one compilation
unit, split it into two: add depend/bitcoin_env.c holding the Bitcoin txEnv
wrappers, alongside the existing depend/env.c for Elements.
This is the more correct trait. When we update the Jet trait we will
be forced to pick one, and we will pick Borrow.
Updates to BlockstreamResearch/simplicity#324

This PR does a couple things simultaneously:

* Runs vendor-simplicity.sh and update-jets.sh
* Updates the `Jet` trait to have associated transaction and environment types,
  and for the environment to be paramterized by the transaction type.
* Changes the Core environment from () to CoreEnv::<Infallible>
* Updates some fixed Core CMR/IHR vectors (this update to libsimplicity changes
  the benchmarks for the Core jets and thus changes these vectors)
* Uncomments the commented-out symbols in simplicity-sys/src/c_jets/c_env/bitcoin.rs
* Adds the "build bitcoin" lines to simplicity-sys/build.rs

The update to the `Jet` trait is a bit noisy but ultimately mechanical: everywhere
that we're generic over all J: Jet, we now also have to be generic over all
T: Borrow<J::Transaction>, which leads to some extra line noise especially in
unit tests where we have assert_* helper functions.

The last four points are tiny diffs, thanks to the previous preparatory commits.

The use of CoreEnv::<Infallible> as the core environment type is kinda fun. It
means that it is impossible to execute any code which attempts to access the
transaction in the environment. (No such code exists, since it would be nonsensical,
but now we have some assurance that it won't exist by accident in the future.)

Unfortunately this mixes mechanical and non-mechanical things in one commit. But
the mechanical changes are exclusively in simplicity-sys/depend/ and src/jet/init/
and the non-mechanical changes are exclusively outside of those files, so it
should be possible to review this.
Change the JetEnvironment impl for BitcoinEnv and ElementsEnv to be
generic over T: Borrow<Transaction> instead of fixed to Arc<Transaction>.
Update Policy::satisfy, get_satisfier, execute_successful,
execute_unsuccessful, and serialize test helpers to accept
ElementsEnv<impl Borrow<Transaction>>.

This allows callers to construct environments without Arc overhead (for
example, using a plain reference or owned value), and is required so
that BitcoinEnv::c_jet_env can return a real CTxEnv from a borrowed
transaction.
Now that we've updated the Jet trait to allow the transactions in environments
to be arbitrary T: Borrow<Transaction>, we don't need to use Arc everywhere.
In many cases we can use normal references.
This is a Rust type which can be used, among other things, to construct
the transaction environment needed by C jets.

Also provides accessors for the underlying transaction and input index,
both of which are used (in the Elements version of this struct) in the
policy satisfier.
rust-bitcoin 0.32 removed Witness::taproot_annex. Replace it with an
inline BIP341 annex extraction: the last witness element that begins
with 0x50 is the annex. This matches the Elements c_env helper and the
BIP341 specification.
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 that asserts every Bitcoin jet's cmr() matches
the 32-byte CMR recorded in primitiveJetNode.inc, and checks
representative cost() values. These guard the hardcoded tables against
drift from the canonical C 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.
Implement Bitcoin::c_jet_ptr for the jets that share C wrappers in
jets_wrapper (sha256 family, all_8, eq_256, verify). These wrappers
ignore the environment argument and pass null, so they work for the ()
CJetEnvironment used by the Bitcoin backend.

Before this change, c_jet_ptr was unimplemented!() for all Bitcoin jets.
RedeemNode::prune() could not execute Bitcoin programs, so it could not
rewrite dead case branches into assertl/assertr. Bitcoin spends therefore
could not produce the pruned program required by the interpreter's
CHECK_EXEC/CHECK_CASE anti-DOS rules.

Jets that require a real transaction environment are left with an
explicit unimplemented!() message until the bitcoin env wiring is done.
Change the Bitcoin JetEnvironment so CJetEnvironment is the real bitcoin
CTxEnv (not ()), returned by c_jet_env(). This allows the Rust BitMachine
to execute tx-context jets against the C interpreter's live transaction
environment, which RedeemNode::prune() requires to rewrite dead case
branches into asserts.

Add FFI declarations and safe wrappers in simplicity-sys c_env/bitcoin.rs
for: sig_all_hash, check_lock_height, check_lock_distance,
check_lock_duration, current_index, num_inputs, tx_hash, tap_env_hash.
These match the rustsimplicity_0_8_bitcoin_* symbols in bitcoinJets.c.

Implement c_jet_ptr for these jets and bip_0340_verify (which is env-free
and maps to jets_wrapper). Remaining jets are still unimplemented and fail
loudly.

Add tests: c_jet_env_is_the_tx_env, env_jets_execute_with_real_environment,
check_lock_height_executes_against_env, bip_0340_verify_ptr_resolves.
Declare FFI bindings and implement c_jet_ptr for all Bitcoin jets that
were previously left as unimplemented!(). Each jet maps to its
rustsimplicity_0_8_bitcoin_* C symbol in bitcoinJets.c.

After this commit, the Rust BitMachine can execute any Bitcoin Simplicity
program through c_jet_ptr, and RedeemNode::prune() can produce the
anti-DOS-clean pruned programs required by the script interpreter.
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.

2 participants