subcoin_script/lib.rs
1//! # Bitcoin Script Interpreter
2//!
3//! This crate implements a Bitcoin Script interpreter in Rust. It provides functionality to
4//! interpret and evaluate Bitcoin scripts, similar to the Bitcoin Core implementation, but with a
5//! focus on readability and compatibility with Rust. Performance optimizations will be pursued
6//! in future updates.
7//!
8//! ## Key Points
9//!
10//! - Some functions are directly ported from Bitcoin Core and may not follow typical Rust idioms.
11//! They are intentionally written in a way that is closer to the original C++ code to preserve
12//! functionality and logic.
13//!
14//! - Several components, including tests prior to the Taproot upgrade, are ported from the
15//! Parity-Bitcoin project to reuse their valuable work for Bitcoin's older features and standards.
16//!
17//! ## Caveats
18//!
19//! This library is **not widely used** and **lacks comprehensive tests**. As a result, **never use it for production use**!
20//! Please use it with caution, and only in non-critical applications or for experimentation purposes.
21
22mod constants;
23mod error;
24mod interpreter;
25mod num;
26mod opcode;
27mod signature_checker;
28pub mod solver;
29mod stack;
30#[cfg(test)]
31mod tests;
32
33use bitcoin::hashes::Hash;
34use bitcoin::{TapLeafHash, secp256k1};
35use bitflags::bitflags;
36
37pub use self::error::Error;
38pub use self::interpreter::verify_script;
39pub use self::signature_checker::{
40 NoSignatureCheck, SignatureChecker, SignatureError, TransactionSignatureChecker,
41};
42pub use self::solver::{TxoutType, solve};
43
44pub type H256 = bitcoin::hashes::sha256::Hash;
45pub type SchnorrSignature = bitcoin::taproot::Signature;
46
47/// Same semantic with [`bitcoin::ecdsa::Signature`] with the following differences:
48///
49/// - `sighash_type` uses u32 instead of [`bitcoin::EcdsaSighashType`].
50/// - Ensure lower S via `normalize_s()`.
51#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
52pub struct EcdsaSignature {
53 /// The underlying ECDSA Signature.
54 pub signature: secp256k1::ecdsa::Signature,
55 /// The corresponding hash type.
56 pub sighash_type: u32,
57}
58
59impl EcdsaSignature {
60 /// Constructs a [`EcdsaSignature`] from the full sig bytes.
61 ///
62 /// https://github.com/bitcoin/bitcoin/blob/82ba50513425bf0568d4f9456282dc9713132490/src/pubkey.cpp#L285
63 /// https://github.com/bitcoin/bitcoin/blob/82ba50513425bf0568d4f9456282dc9713132490/src/pubkey.cpp#L290
64 pub fn parse_der_lax(full_sig_bytes: &[u8]) -> Result<Self, bitcoin::ecdsa::Error> {
65 let (sighash_type, sig) = full_sig_bytes
66 .split_last()
67 .ok_or(bitcoin::ecdsa::Error::EmptySignature)?;
68 let sighash_type = *sighash_type as u32;
69
70 let mut signature = secp256k1::ecdsa::Signature::from_der_lax(sig)?;
71
72 // libsecp256k1's ECDSA verification requires lower-S signatures, which have
73 // not historically been enforced in Bitcoin, so normalize them first.
74 signature.normalize_s();
75
76 Ok(Self {
77 signature,
78 sighash_type,
79 })
80 }
81}
82
83bitflags! {
84 /// Script verification flags.
85 ///
86 /// https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/script/interpreter.h#L45
87 #[derive(Debug, Clone)]
88 pub struct VerifyFlags: u32 {
89 const NONE = 0;
90
91 /// Evaluate P2SH subscripts (BIP16).
92 const P2SH = 1 << 0;
93
94 /// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
95 /// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
96 /// (not used or intended as a consensus rule).
97 const STRICTENC = 1 << 1;
98
99 // Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
100 const DERSIG = 1 << 2;
101
102 // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
103 // (BIP62 rule 5)
104 const LOW_S = 1 << 3;
105
106 // verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
107 const NULLDUMMY = 1 << 4;
108
109 // Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
110 const SIGPUSHONLY = 1 << 5;
111
112 // Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
113 // pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
114 // any other push causes the script to fail (BIP62 rule 3).
115 // In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
116 const MINIMALDATA = 1 << 6;
117
118 // Discourage use of NOPs reserved for upgrades (NOP1-10)
119 //
120 // Provided so that nodes can avoid accepting or mining transactions
121 // containing executed NOP's whose meaning may change after a soft-fork,
122 // thus rendering the script invalid; with this flag set executing
123 // discouraged NOPs fails the script. This verification flag will never be
124 // a mandatory flag applied to scripts in a block. NOPs that are not
125 // executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
126 // NOPs that have associated forks to give them new meaning (CLTV, CSV)
127 // are not subject to this rule.
128 const DISCOURAGE_UPGRADABLE_NOPS = 1 << 7;
129
130 // Require that only a single stack element remains after evaluation. This changes the success criterion from
131 // "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
132 // "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
133 // (BIP62 rule 6)
134 // Note: CLEANSTACK should never be used without P2SH or WITNESS.
135 // Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
136 // consensus rules. It is automatic there and does not need this flag.
137 const CLEANSTACK = 1 << 8;
138
139 // Verify CHECKLOCKTIMEVERIFY
140 //
141 // See BIP65 for details.
142 const CHECKLOCKTIMEVERIFY = 1 << 9;
143
144 // support CHECKSEQUENCEVERIFY opcode
145 //
146 // See BIP112 for details
147 const CHECKSEQUENCEVERIFY = 1 << 10;
148
149 // Support segregated witness
150 const WITNESS = 1 << 11;
151
152 // Making v1-v16 witness program non-standard
153 const DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 1 << 12;
154
155 // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
156 //
157 // Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
158 // rules. It is automatic there and does not depend on this flag.
159 const MINIMALIF = 1 << 13;
160
161 // Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
162 const NULLFAIL = 1 << 14;
163
164 // Public keys in segregated witness scripts must be compressed
165 const WITNESS_PUBKEYTYPE = 1 << 15;
166
167 // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
168 const CONST_SCRIPTCODE = 1 << 16;
169
170 // Taproot/Tapscript validation (BIPs 341 & 342)
171 const TAPROOT = 1 << 17;
172
173 // Making unknown Taproot leaf versions non-standard
174 const DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = 1 << 18;
175
176 // Making unknown OP_SUCCESS non-standard
177 const DISCOURAGE_OP_SUCCESS = 1 << 19;
178
179 // Making unknown public key versions (in BIP 342 scripts) non-standard
180 const DISCOURAGE_UPGRADABLE_PUBKEYTYPE = 1 << 20;
181 }
182}
183
184impl VerifyFlags {
185 pub fn verify_minimaldata(&self) -> bool {
186 self.contains(Self::MINIMALDATA)
187 }
188
189 pub fn verify_sigpushonly(&self) -> bool {
190 self.contains(Self::SIGPUSHONLY)
191 }
192
193 pub fn verify_p2sh(&self) -> bool {
194 self.contains(Self::P2SH)
195 }
196
197 pub fn verify_witness(&self) -> bool {
198 self.contains(Self::WITNESS)
199 }
200
201 pub fn verify_discourage_upgradable_witness_program(&self) -> bool {
202 self.contains(Self::DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
203 }
204}
205
206/// Represents different signature verification schemes used in Bitcoin
207///
208/// https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/script/interpreter.h#L190
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub enum SigVersion {
211 /// Bare scripts and BIP16 P2SH-wrapped redeemscripts
212 Base,
213 /// Witness v0 (P2WPKH and P2WSH); see BIP 141
214 WitnessV0,
215 /// Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341
216 Taproot,
217 /// Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, script path spending,
218 /// leaf version 0xc0; see BIP 342
219 Tapscript,
220}
221
222// https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/script/interpreter.h#L198
223#[derive(Debug)]
224pub struct ScriptExecutionData {
225 /// Whether m_tapleaf_hash is initialized
226 pub tapleaf_hash_init: bool,
227 /// The tapleaf hash
228 pub tapleaf_hash: TapLeafHash,
229
230 /// Whether m_codeseparator_pos is initialized
231 pub codeseparator_pos_init: bool,
232 /// Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed)
233 pub codeseparator_pos: u32,
234
235 /// Whether m_annex_present and m_annex_hash are initialized
236 pub annex_init: bool,
237 /// Whether an annex is present
238 pub annex_present: bool,
239 /// Hash of the annex data
240 pub annex_hash: H256,
241 /// Annex data.
242 ///
243 /// We store the annex data for signature_checker.
244 pub annex: Option<Vec<u8>>,
245
246 /// Whether m_validation_weight_left is initialized
247 pub validation_weight_left_init: bool,
248 /// How much validation weight is left (decremented for every successful non-empty signature check)
249 pub validation_weight_left: i64,
250
251 /// The hash of the corresponding output
252 pub output_hash: Option<H256>,
253}
254
255impl Default for ScriptExecutionData {
256 fn default() -> Self {
257 Self {
258 tapleaf_hash_init: false,
259 tapleaf_hash: TapLeafHash::from_slice(H256::all_zeros().as_byte_array())
260 .expect("Static value must be correct; qed"),
261 codeseparator_pos_init: false,
262 codeseparator_pos: 0,
263 annex_init: false,
264 annex_present: false,
265 annex_hash: H256::all_zeros(),
266 annex: None,
267 validation_weight_left_init: false,
268 validation_weight_left: 0,
269 output_hash: None,
270 }
271 }
272}