Utilities used in the official BitVM implementation to generate Bitcoin Script. Heavily inspired by rust-bitcoin-script's inline macro.
This crate exports a script! macro which can be used to build structured Bitcoin scripts and compiled to the Script type from the bitcoin crate.
Example:
use bitcoin_script::bitcoin_script;let htlc_script = script!{OP_IFOP_SHA256 <digest> OP_EQUALVERIFYOP_DUPOP_SHA256 <seller_pubkey_hash>
OP_ELSE100OP_CSVOP_DROPOP_DUPOP_HASH160 <buyer_pubkey_hash>
OP_ENDIFOP_EQUALVERIFYOP_CHECKSIG};let script_buf = htlc_script.compile();Scripts are based on the standard syntax made up of opcodes, base-10 integers, or hex string literals. Additionally, Rust expressions can be interpolated in order to support dynamically capturing Rust variables or computing values (delimited by <angle brackets> or {curly brackets}). The script! macro can be nested.
Whitespace is ignored - scripts can be formatted in the author's preferred style.
All normal opcodes are available, in the form OP_X.
let script = script!(OP_CHECKSIGOP_VERIFY);Positive and negative 64-bit integer literals can be used, and will resolve to their most efficient encoding.
For example:
2will resolve toOP_PUSHNUM_2(0x52)255will resolve to a length-delimited varint:0x02ff00(note the extra zero byte, due to the way Bitcoin scripts use the most-significant bit to represent the sign)`
let script = script!(123 -456999999);Hex strings can be specified, prefixed with 0x.
let script = script!(0x0102030405060708090a0b0c0d0e0fOP_HASH160);Dynamic Rust expressions are supported inside the script, surrounded by angle brackets or in a code block. In many cases, this will just be a variable identifier, but this can also be a function call or arithmetic.
Rust expressions of the following types are supported:
i64Vec<u8>bitcoin::PublicKeybitcoin::XOnlyPublicKeybitcoin::ScriptBufStructuredScript
let bytes = vec![1,2,3];let script = script!{
<bytes> OP_CHECKSIGVERIFY
<2016*5> OP_CSV
<script! {OP_FALSEOP_TRUE}>
};For-loops and if-else-statements are supported inside the script and will be unrolled when the scripts are generated.
let loop_count = 10;let script = script!{for i in 0..loop_count {if i % 2 == 0{OP_ADD} else {OP_DUPOP_ADD}}};