Add sighash methods - #58
Open
stringhandler wants to merge 1 commit into
Open
stringhandler wants to merge 1 commit into
stringhandler wants to merge 1 commit into
Conversation
Adds simf/sighash_elements.simf, which implements the six sighash modes on Elements: ALL, NONE, SINGLE, and their ANYONECANPAY variants. Each mode uses its own tagged hash, so a signature made in one mode can't be reused in another. Every mode also commits to the genesis block hash, which stops a signature from being replayed on another Elements chain. If there's no output at this input's index, SINGLE panics instead of signing the legacy fixed digest.
Hrom131
reviewed
Sep 29, 2026
Comment on lines
+2
to
+30
| /// sha256("SimplicityHL/sighash/v1/all") | ||
| fn tag_all() -> u256 { | ||
| 0x0e8e05b1734bb78560eec1e2153340c5da8a5d7dc936253996d70aeacc26923a | ||
| } | ||
|
|
||
| /// sha256("SimplicityHL/sighash/v1/none") | ||
| fn tag_none() -> u256 { | ||
| 0x23ec67ac4f3c0762d9f8b5966a067d1f871d3068fb23325022ea1c6545eb52ea | ||
| } | ||
|
|
||
| /// sha256("SimplicityHL/sighash/v1/single") | ||
| fn tag_single() -> u256 { | ||
| 0x1d20b67e40936bfa4472f96707752a4694bff7f699cd2efbc27d10bfa8ae97ed | ||
| } | ||
|
|
||
| /// sha256("SimplicityHL/sighash/v1/all_anyonecanpay") | ||
| fn tag_all_anyonecanpay() -> u256 { | ||
| 0x7b8e176217cf4be9d5ade17ac856ebfad72b2be99fc3199db092b08642bd4333 | ||
| } | ||
|
|
||
| /// sha256("SimplicityHL/sighash/v1/none_anyonecanpay") | ||
| fn tag_none_anyonecanpay() -> u256 { | ||
| 0xde3aea875d6db910ca2ce189cda1d67c7e1ceacde2f36fd95cb52aa17de7f23b | ||
| } | ||
|
|
||
| /// sha256("SimplicityHL/sighash/v1/single_anyonecanpay") | ||
| fn tag_single_anyonecanpay() -> u256 { | ||
| 0xac1b092763ce7c385be49d56642802f8b21ddd81deeca183f894f52879b9a981 | ||
| } |
Collaborator
There was a problem hiding this comment.
Do these tags correspond to an existing standard, or are they a new feature?
Hrom131
reviewed
Sep 29, 2026
Comment on lines
+98
to
+99
| /// All inputs, all outputs. The equivalent of `jet::sig_all_hash` — prefer that jet, which is | ||
| /// cheaper; this exists so all six modes read the same way. |
Collaborator
There was a problem hiding this comment.
How can the current function be equivalent to jet::sig_all_hash if jet::sig_all_hash does not contain the tag_all tag?
Hrom131
reviewed
Sep 29, 2026
Comment on lines
+32
to
+96
| /// Start a mode's hash, seeded with its tag twice (BIP-340 tagged-hash style). | ||
| fn mode_ctx(tag: u256) -> Ctx8 { | ||
| let ctx: Ctx8 = jet::sha_256_ctx_8_init(); | ||
| let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag); | ||
| jet::sha_256_ctx_8_add_32(ctx, tag) | ||
| } | ||
|
|
||
| /// nVersion and nLockTime, committed to by every mode — as in BIP-341, where both appear in | ||
| /// the message regardless of the flag. | ||
| fn add_common(ctx: Ctx8) -> Ctx8 { | ||
| let ctx: Ctx8 = jet::sha_256_ctx_8_add_4(ctx, jet::version()); | ||
| jet::sha_256_ctx_8_add_4(ctx, jet::tx_lock_time()) | ||
| } | ||
|
|
||
| /// Every input: outpoints, sequences, and the UTXOs they spend (values, assets, scripts). | ||
| /// The non-ANYONECANPAY case — no input may be added, removed or reordered after signing. | ||
| fn add_all_inputs(ctx: Ctx8) -> Ctx8 { | ||
| let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, jet::inputs_hash()); | ||
| jet::sha_256_ctx_8_add_32(ctx, jet::input_utxos_hash()) | ||
| } | ||
|
|
||
| /// This input alone. The ANYONECANPAY case — others may be added freely afterwards, which is | ||
| /// what lets a fee input be attached to a signature made months earlier. | ||
| fn add_current_input(ctx: Ctx8) -> Ctx8 { | ||
| let i: u32 = jet::current_index(); | ||
| let ctx: Ctx8 = match jet::input_hash(i) { | ||
| Some(h: u256) => jet::sha_256_ctx_8_add_32(ctx, h), | ||
| None => panic!(), | ||
| }; | ||
| match jet::input_utxo_hash(i) { | ||
| Some(h: u256) => jet::sha_256_ctx_8_add_32(ctx, h), | ||
| None => panic!(), | ||
| } | ||
| } | ||
|
|
||
| /// Every output. On Elements this covers assets, amounts, nonces and the range and surjection | ||
| /// proofs as well as the scripts. | ||
| fn add_all_outputs(ctx: Ctx8) -> Ctx8 { | ||
| jet::sha_256_ctx_8_add_32(ctx, jet::outputs_hash()) | ||
| } | ||
|
|
||
| /// The output at this input's index — SIGHASH_SINGLE's pairing rule. Aborts when there is no | ||
| /// such output, rather than signing a fixed digest the way legacy Bitcoin does. | ||
| fn add_current_output(ctx: Ctx8) -> Ctx8 { | ||
| match jet::output_hash(jet::current_index()) { | ||
| Some(h: u256) => jet::sha_256_ctx_8_add_32(ctx, h), | ||
| None => panic!(), | ||
| } | ||
| } | ||
|
|
||
| /// Close a mode: bind the transaction digest to this chain, this tapleaf, and this input's | ||
| /// position. Mirrors the outer structure of `jet::sig_all_hash`. | ||
| /// | ||
| /// `genesis_block_hash` is the Elements-only part — it stops a signature made for one Elements | ||
| /// chain from being replayed on another. The Bitcoin edition of this module omits it, because | ||
| /// Bitcoin's own sighash does not commit to a genesis hash either. | ||
| fn finish(ctx: Ctx8) -> u256 { | ||
| let tx: u256 = jet::sha_256_ctx_8_finalize(ctx); | ||
| let outer: Ctx8 = jet::sha_256_ctx_8_init(); | ||
| let outer: Ctx8 = jet::sha_256_ctx_8_add_32(outer, jet::genesis_block_hash()); | ||
| let outer: Ctx8 = jet::sha_256_ctx_8_add_32(outer, tx); | ||
| let outer: Ctx8 = jet::sha_256_ctx_8_add_32(outer, jet::tap_env_hash()); | ||
| let outer: Ctx8 = jet::sha_256_ctx_8_add_4(outer, jet::current_index()); | ||
| jet::sha_256_ctx_8_finalize(outer) | ||
| } |
Collaborator
There was a problem hiding this comment.
These functions help with SHA-256 and should go in a separate file. It’s also best to open a separate pull request to add them to Std
Hrom131
reviewed
Sep 29, 2026
Collaborator
There was a problem hiding this comment.
Could you also move the file to the lib directory and add the necessary tests for the new functions?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds simf/sighash_elements.simf, which implements the six sighash modes on Elements: ALL, NONE, SINGLE, and their ANYONECANPAY variants.
Each mode uses its own tagged hash, so a signature made in one mode can't be reused in another. Every mode also commits to the genesis block
hash, which stops a signature from being replayed on another Elements
chain. If there's no output at this input's index, SINGLE panics
instead of signing the legacy fixed digest.