| Crates.io | hal-simplicity |
| lib.rs | hal-simplicity |
| version | 0.2.0 |
| created_at | 2025-07-30 17:37:12.920847+00 |
| updated_at | 2026-01-06 20:27:53.616308+00 |
| description | hal-simplicity: a Simplicity extension of hal |
| homepage | https://github.com/BlockstreamResearch/hal-simplicity/ |
| repository | https://github.com/BlockstreamResearch/hal-simplicity/ |
| max_upload_size | |
| id | 1773859 |
| size | 245,024 |
This is a fork of Steven Roose's hal-elements which in turn is an extension of his Bitcoin tool hal.
$ cargo install --locked hal hal-simplicity
You can also run it directly with cargo run -- <command>.
Consider transaction e54d31ce544b65a3768d7dc44a9caf1142eb1ce9bb46707f5a83cb1ccf9b77f9 on Liquid Testnet, which spends a simple "pay to public key" Simplicity program.
We can obtain the raw transaction from the Block Explorer by replacing /nojs/ with
/api/ in the above URL and adding /hex to the end, as so:
020000000101b33e0e092f2f229bb472f7ac15b22783908ebf5b70d9fefa13fc627979b2ca6c0000000000000000000201499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000182b800160014b58c22151f4ba159e2255767472ac89137e8183001499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000003e8000000000000000004609bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779fd90afd7201e4fba0509b4df120e1d320451f14172c46476646daf8d0d6da80e84c986cc5e073f80ed4dcf0210284187248126ac8e671544245742660022ae160c5e14b09ec0c2a17584bf5c548c85961c02b6efc010c03109ad2420c3f00140b16ab91cd75dcbc1e84ea7a320719cbfc6dc95e5194f9eca996d55a7b2d768c511e2a310e1806240a1241b70a35627302ef7da851f75a1f471748121a2b6978930a58ccaee2309401bd1b6e9fcbb0018601881a80e12071190284906e2a37159c2a162cdba0e67e0aad66c82658ec0c7f2a5a2cc38c3f61a892acd0da3a133ff9ead668873dc60c0310b5b0730445fea038d226980c2e6f7e4be9e895848d1fd97f2100db43004cb4eaddefc50601885c078170e6f13a1848e019ef88de2e7a3c1561d1828b3be0f290def9feebf54da94249472c0c0312050920fc8238dc861438a059b630e6ef256702d23cf92f32979f4fcd9ff3909cf7b32538aafb0e3a23ec40079b1d130c03103785c207e4c8201c5a072580e4e0207fd424f70498ef2fb6dd05ffbb7368dc796e6c47f24404e0b1ff138cfce89a7a21bff5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d20000000000
We see that this transaction spends the transaction output with outpoint
6ccab2797962fc13fafed9705bbf8e908327b215acf772b49b222f2f090e3eb3:0, which contains
the Simplicity program, and that this is the 0th input of the transaction. From its
witness stack, we see (reading bottom to top):
bff5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d27fd424f70498ef2fb6dd05ffbb7368dc796e6c47f24404e0b1ff138cfce89a7ae4fba...9bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779fd90a,
which for this program we know consists of a public key 9bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964
and a signature e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779.If you are unsure which components of the witness stack are what, you can attempt to parse each one of them as a Simplicity program. When we get the correct one, it will parse and produce a CMR which appears elsewhere in the witness stack:
$ hal-simplicity simplicity info e4fba0509b4df120e1d320451f14172c46476646daf8d0d6da80e84c986cc5e073f80ed4dcf0210284187248126ac8e671544245742660022ae160c5e14b09ec0c2a17584bf5c548c85961c02b6efc010c03109ad2420c3f00140b16ab91cd75dcbc1e84ea7a320719cbfc6dc95e5194f9eca996d55a7b2d768c511e2a310e1806240a1241b70a35627302ef7da851f75a1f471748121a2b6978930a58ccaee2309401bd1b6e9fcbb0018601881a80e12071190284906e2a37159c2a162cdba0e67e0aad66c82658ec0c7f2a5a2cc38c3f61a892acd0da3a133ff9ead668873dc60c0310b5b0730445fea038d226980c2e6f7e4be9e895848d1fd97f2100db43004cb4eaddefc50601885c078170e6f13a1848e019ef88de2e7a3c1561d1828b3be0f290def9feebf54da94249472c0c0312050920fc8238dc861438a059b630e6ef256702d23cf92f32979f4fcd9ff3909cf7b32538aafb0e3a23ec40079b1d130c03103785c207e4c8201c5a072580e4e0
{
"jets": "core",
"commit_base64": "5PugUJtN8SDh0yBFHxQXLEZHZkba+NDW2oDoTJhsxeBz+A7U3PAhAoQYckgSasjmcVRCRXQmYAIq4WDF4UsJ7AwqF1hL9cVIyFlhwCtu/AEMAxCa0kIMPwAUCxarkc113LwehOp6MgcZy/xtyV5RlPnsqZbVWnstdoxRHioxDhgGJAoSQbcKNWJzAu99qFH3Wh9HF0gSGitpeJMKWMyu4jCUAb0bbp/LsAGGAYgagOEgcRkChJBuKjcVnCoWLNug5n4KrWbIJljsDH8qWizDjD9hqJKs0No6Ez/56tZohz3GDAMQtbBzBEX+oDjSJpgMLm9+S+nolYSNH9l/IQDbQwBMtOrd78UGAYhcB4Fw5vE6GEjgGe+I3i56PBVh0YKLO+DykN75/uv1TalCSUcsDAMSBQkg/II43IYUOKBZtjDm7yVnAtI8+S8yl59PzZ/zkJz3syU4qvsOOiPsQAebHRMMAxA3hcIH5MggHFoHJYDk4A==",
"commit_decode": "(witness & iden); (((unit; const 0xbe241c3a6408a3e282e588c8ecc8db5f1a1adb501d09930d98bc0e7f01da9b9e ) & iden); (((IOH; ((((false & unit); assertl drop jet_sha_256_ctx_8_init ) & iden); ((((false & (OH & IH)); assertl drop jet_sha_256_ctx_8_add_32 ) & iden); ((false & OH); assertl drop jet_sha_256_ctx_8_finalize )))) & iden); ((((false & ((false & (OH & IOH)); assertl drop jet_eq_256 )); assertl drop jet_verify ) & ((((false & unit); assertl drop jet_sig_all_hash ) & iden); ((false & ((IIIOH & OH) & witness )); assertl drop jet_bip_0340_verify ))); IH)))",
"type_arrow": "1 → 1",
"cmr": "7fd424f70498ef2fb6dd05ffbb7368dc796e6c47f24404e0b1ff138cfce89a7a",
"liquid_address_unconf": "ex1pyuvwaqedernfdc7c6qf7r67en3szas6s0sdegzq3jxduhj4mhles29dz23",
"liquid_testnet_address_unconf": "tex1pyuvwaqedernfdc7c6qf7r67en3szas6s0sdegzq3jxduhj4mhlestul9m7",
"is_redeem": false
}
This gives us the canonical base64 encoding of the program, which the Block Explorer does not provide,
and lets us confirm the CMR. We can then guess which part of the witness stack is the witness for
the Simplicity program, by passing various blobs as the second argument of simplicity info:
$ hal-simplicity simplicity info 5PugUJtN8SDh0yBFHxQXLEZHZkba+NDW2oDoTJhsxeBz+A7U3PAhAoQYckgSasjmcVRCRXQmYAIq4WDF4UsJ7AwqF1hL9cVIyFlhwCtu/AEMAxCa0kIMPwAUCxarkc113LwehOp6MgcZy/xtyV5RlPnsqZbVWnstdoxRHioxDhgGJAoSQbcKNWJzAu99qFH3Wh9HF0gSGitpeJMKWMyu4jCUAb0bbp/LsAGGAYgagOEgcRkChJBuKjcVnCoWLNug5n4KrWbIJljsDH8qWizDjD9hqJKs0No6Ez/56tZohz3GDAMQtbBzBEX+oDjSJpgMLm9+S+nolYSNH9l/IQDbQwBMtOrd78UGAYhcB4Fw5vE6GEjgGe+I3i56PBVh0YKLO+DykN75/uv1TalCSUcsDAMSBQkg/II43IYUOKBZtjDm7yVnAtI8+S8yl59PzZ/zkJz3syU4qvsOOiPsQAebHRMMAxA3hcIH5MggHFoHJYDk4A== 9bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779fd90a`
{
"jets": "core",
"commit_base64": "5PugUJtN8SDh0yBFHxQXLEZHZkba+NDW2oDoTJhsxeBz+A7U3PAhAoQYckgSasjmcVRCRXQmYAIq4WDF4UsJ7AwqF1hL9cVIyFlhwCtu/AEMAxCa0kIMPwAUCxarkc113LwehOp6MgcZy/xtyV5RlPnsqZbVWnstdoxRHioxDhgGJAoSQbcKNWJzAu99qFH3Wh9HF0gSGitpeJMKWMyu4jCUAb0bbp/LsAGGAYgagOEgcRkChJBuKjcVnCoWLNug5n4KrWbIJljsDH8qWizDjD9hqJKs0No6Ez/56tZohz3GDAMQtbBzBEX+oDjSJpgMLm9+S+nolYSNH9l/IQDbQwBMtOrd78UGAYhcB4Fw5vE6GEjgGe+I3i56PBVh0YKLO+DykN75/uv1TalCSUcsDAMSBQkg/II43IYUOKBZtjDm7yVnAtI8+S8yl59PzZ/zkJz3syU4qvsOOiPsQAebHRMMAxA3hcIH5MggHFoHJYDk4A==",
"commit_decode": "(witness & iden); (((unit; const 0xbe241c3a6408a3e282e588c8ecc8db5f1a1adb501d09930d98bc0e7f01da9b9e ) & iden); (((IOH; ((((false & unit); assertl drop jet_sha_256_ctx_8_init ) & iden); ((((false & (OH & IH)); assertl drop jet_sha_256_ctx_8_add_32 ) & iden); ((false & OH); assertl drop jet_sha_256_ctx_8_finalize )))) & iden); ((((false & ((false & (OH & IOH)); assertl drop jet_eq_256 )); assertl drop jet_verify ) & ((((false & unit); assertl drop jet_sig_all_hash ) & iden); ((false & ((IIIOH & OH) & witness )); assertl drop jet_bip_0340_verify ))); IH)))",
"type_arrow": "1 → 1",
"cmr": "7fd424f70498ef2fb6dd05ffbb7368dc796e6c47f24404e0b1ff138cfce89a7a",
"liquid_address_unconf": "ex1pyuvwaqedernfdc7c6qf7r67en3szas6s0sdegzq3jxduhj4mhles29dz23",
"liquid_testnet_address_unconf": "tex1pyuvwaqedernfdc7c6qf7r67en3szas6s0sdegzq3jxduhj4mhlestul9m7",
"is_redeem": true,
"redeem_base64": "5PugUJtN8SDh0yBFHxQXLEZHZkba+NDW2oDoTJhsxeBz+A7U3PAhAoQYckgSasjmcVRCRXQmYAIq4WDF4UsJ7AwqF1hL9cVIyFlhwCtu/AEMAxCa0kIMPwAUCxarkc113LwehOp6MgcZy/xtyV5RlPnsqZbVWnstdoxRHioxDhgGJAoSQbcKNWJzAu99qFH3Wh9HF0gSGitpeJMKWMyu4jCUAb0bbp/LsAGGAYgagOEgcRkChJBuKjcVnCoWLNug5n4KrWbIJljsDH8qWizDjD9hqJKs0No6Ez/56tZohz3GDAMQtbBzBEX+oDjSJpgMLm9+S+nolYSNH9l/IQDbQwBMtOrd78UGAYhcB4Fw5vE6GEjgGe+I3i56PBVh0YKLO+DykN75/uv1TalCSUcsDAMSBQkg/II43IYUOKBZtjDm7yVnAtI8+S8yl59PzZ/zkJz3syU4qvsOOiPsQAebHRMMAxA3hcIH5MggHFoHJYDk4A==",
"witness_hex": "9bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779fd90a",
"amr": "463d72225db55f4bb9811a9d4d921854ef8893fa92b6358f6d47719ecfb1ced5",
"ihr": "8d3f7748cf509656920ecdb9cb424cac4f55f5e9cc0a1083f98fa87e74fca3e1"
}
We see that the witness parsed correctly and we now get some extra fields in the output. (Though be aware that any blob of the correct size would have been accepted; this serves as a sanity check but is not a reliable way to determine what the correct witness for a program is.)
Next, we look at the input transaction, 6ccab2797962fc13fafed9705bbf8e908327b215acf772b49b222f2f090e3eb3. Its 0th output has:
51202718ee832dc8e696e3d8d013e1ebd99c602ec3507c1b940811919bcbcabbbff3144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49Putting all this data together, we can invoke simplicity sighash:
$ hal-simplicity simplicity sighash 020000000101b33e0e092f2f229bb472f7ac15b22783908ebf5b70d9fefa13fc627979b2ca6c0000000000000000000201499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000182b800160014b58c22151f4ba159e2255767472ac89137e8183001499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000003e8000000000000000004609bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779fd90afd7201e4fba0509b4df120e1d320451f14172c46476646daf8d0d6da80e84c986cc5e073f80ed4dcf0210284187248126ac8e671544245742660022ae160c5e14b09ec0c2a17584bf5c548c85961c02b6efc010c03109ad2420c3f00140b16ab91cd75dcbc1e84ea7a320719cbfc6dc95e5194f9eca996d55a7b2d768c511e2a310e1806240a1241b70a35627302ef7da851f75a1f471748121a2b6978930a58ccaee2309401bd1b6e9fcbb0018601881a80e12071190284906e2a37159c2a162cdba0e67e0aad66c82658ec0c7f2a5a2cc38c3f61a892acd0da3a133ff9ead668873dc60c0310b5b0730445fea038d226980c2e6f7e4be9e895848d1fd97f2100db43004cb4eaddefc50601885c078170e6f13a1848e019ef88de2e7a3c1561d1828b3be0f290def9feebf54da94249472c0c0312050920fc8238dc861438a059b630e6ef256702d23cf92f32979f4fcd9ff3909cf7b32538aafb0e3a23ec40079b1d130c03103785c207e4c8201c5a072580e4e0207fd424f70498ef2fb6dd05ffbb7368dc796e6c47f24404e0b1ff138cfce89a7a21bff5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d20000000000 0 7fd424f70498ef2fb6dd05ffbb7368dc796e6c47f24404e0b1ff138cfce89a7a bff5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d2 -i '51202718ee832dc8e696e3d8d013e1ebd99c602ec3507c1b940811919bcbcabbbff3:144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49:0.001' -p 9bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964 -s e09e91b2ee81dd61d97ec6e83bfdb03c28f79e0e7038a98964ea5c29cde0b2319878a86dc9e5a0d00269215a43754755a6e173246ad7d330eb82d27e779fd90a`
{
"sighash": "d832133eba9525e9e452752e6b2193b3c71084af75832b385a4c108f8100947d",
"signature": null,
"valid_signature": true
}
Which tells us the SIGHASH_ALL sighash for the transaction input, as well as validating the signature
we provided with the public key we provided, which confirms that our witness was well-formed.
If we had not provided the public key and signature, this command would have simply output the sighash
with no further information. If we had instead provided a secret key with -s, the command would have
produced a signature for us.
Create Simplicity addresses
hal-simplicity address create <program>
Inspect Simplicity addresses
hal-simplicity address inspect <address>
Generate a random private/public keypair
hal-simplicity keypair generate
Parse a base64-encoded Simplicity program and decode it
hal-simplicity simplicity info <base64-program>
Compute sighash for a Simplicity transaction input (draft PR #9)
hal-simplicity simplicity sighash <tx-hex> <input-index> <cmr> <control-block> -i <input-utxo> [-g <genesis-hash>] [-s <secret-key>]
Create a raw Simplicity transaction from JSON
hal-simplicity tx create <tx-info-json>
hal-simplicity tx create --raw-stdout <tx-info-json>
Decode a raw Simplicity transaction to JSON
hal-simplicity tx decode <tx-hex>
Create a raw block from JSON
hal-simplicity block create <block-info-json>
hal-simplicity block create --raw-stdout <block-info-json>
Decode a Simplicity block
hal-simplicity block decode <block-hex>