bitcoin_script/lib.rs
1//! [](https://github.com/mappum/rust-bitcoin-script/actions?query=workflow%3ARust)
2//! [](https://crates.io/crates/bitcoin-script)
3//! [](https://docs.rs/bitcoin-script)
4//!
5//! **Bitcoin scripts inline in Rust.**
6//!
7//! ---
8//!
9//! ## Usage
10//!
11//! This crate exports a `bitcoin_script!` macro which can be used to build
12//! Bitcoin scripts. The macro returns the
13//! [`Script`](https://docs.rs/bitcoin/0.23.0/bitcoin/blockdata/script/struct.Script.html)
14//! type from the [`bitcoin`](https://github.com/rust-bitcoin/rust-bitcoin)
15//! crate.
16//!
17//! **Example:**
18//!
19//! ```rust
20//! #![feature(proc_macro_hygiene)]
21//!
22//! use bitcoin_script::bitcoin_script;
23//!
24//! # let digest = 0;
25//! # let seller_pubkey_hash = 0;
26//! # let buyer_pubkey_hash = 0;
27//!
28//! let htlc_script = bitcoin_script! {
29//! OP_IF
30//! OP_SHA256 <digest> OP_EQUALVERIFY OP_DUP OP_SHA256 <seller_pubkey_hash>
31//! OP_ELSE
32//! 100 OP_CSV OP_DROP OP_DUP OP_HASH160 <buyer_pubkey_hash>
33//! OP_ENDIF
34//! OP_EQUALVERIFY
35//! OP_CHECKSIG
36//! };
37//! ```
38//!
39//! **NOTE:** As of rustc 1.41, the Rust compiler prevents using procedural
40//! macros as expressions. To use this macro you'll need to be on nightly and
41//! add `#![feature(proc_macro_hygiene)]` to the root of your crate. This will
42//! be stablized in the near future, the PR can be found here:
43//! https://github.com/rust-lang/rust/pull/68717
44//!
45//! ### Syntax
46//!
47//! Scripts are based on the standard syntax made up of opcodes, base-10
48//! integers, or hex string literals. Additionally, Rust expressions can be
49//! interpolated in order to support dynamically capturing Rust variables or
50//! computing values (delimited by `<angle brackets>`).
51//!
52//! Whitespace is ignored - scripts can be formatted in the author's preferred
53//! style.
54//!
55//! #### Opcodes
56//!
57//! All normal opcodes are available, in the form `OP_X`.
58//!
59//! ```rust
60//! # #![feature(proc_macro_hygiene)]
61//! # use bitcoin_script::bitcoin_script;
62//! let script = bitcoin_script!(OP_CHECKSIG OP_VERIFY);
63//! ```
64//!
65//! #### Integer Literals
66//!
67//! Positive and negative 64-bit integer literals can be used, and will resolve to their most efficient encoding.
68//!
69//! For example:
70//! -`2` will resolve to `OP_PUSHNUM_2` (`0x52`)
71//! -`255` will 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)`
72//!
73//! ```rust
74//! # #![feature(proc_macro_hygiene)]
75//! # use bitcoin_script::bitcoin_script;
76//! let script = bitcoin_script!(123 -456 999999);
77//! ```
78//!
79//! #### Hex Literals
80//!
81//! Hex strings can be specified, prefixed with `0x`.
82//!
83//! ```rust
84//! # #![feature(proc_macro_hygiene)]
85//! # use bitcoin_script::bitcoin_script;
86//! let script = bitcoin_script!(
87//! 0x0102030405060708090a0b0c0d0e0f OP_HASH160
88//! );
89//! ```
90//!
91//! #### Escape Sequences
92//!
93//! Dynamic Rust expressions are supported inside the script, surrounded by angle brackets. In many cases, this will just be a variable identifier, but this can also be a function call or arithmetic.
94//!
95//! Rust expressions of the following types are supported:
96//!
97//! - `i64`
98//! - `Vec<u8>`
99//! - [`bitcoin::PublicKey`](https://docs.rs/bitcoin/0.23.0/bitcoin/util/key/struct.PublicKey.html)
100//!
101//! ```rust
102//! # #![feature(proc_macro_hygiene)]
103//! # use bitcoin_script::bitcoin_script;
104//! let bytes = vec![1, 2, 3];
105//!
106//! let script = bitcoin_script! {
107//! <bytes> OP_CHECKSIGVERIFY
108//!
109//! <2016 * 5> OP_CSV
110//! };
111//! ```
112
113#![feature(proc_macro_hygiene)]
114
115mod generate;
116mod parse;
117
118use generate::generate;
119use parse::parse;
120use proc_macro::TokenStream;
121use proc_macro_error::{proc_macro_error, set_dummy};
122use quote::quote;
123
124#[proc_macro]
125#[proc_macro_error]
126pub fn bitcoin_script(tokens: TokenStream) -> TokenStream {
127 set_dummy(quote!((::bitcoin::Script::new())));
128 generate(parse(tokens.into())).into()
129}