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