1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! # Bitcoin Script Interpreter
//!
//! This crate implements a Bitcoin Script interpreter in Rust. It provides functionality to
//! interpret and evaluate Bitcoin scripts, similar to the Bitcoin Core implementation, but with a
//! focus on readability and compatibility with Rust. Performance optimizations will be pursued
//! in future updates.
//!
//! ## Key Points
//!
//! - Some functions are directly ported from Bitcoin Core and may not follow typical Rust idioms.
//!   They are intentionally written in a way that is closer to the original C++ code to preserve
//!   functionality and logic.
//!
//! - Several components, including tests prior to the Taproot upgrade, are ported from the
//!   Parity-Bitcoin project to reuse their valuable work for Bitcoin's older features and standards.
//!
//! ## Caveats
//!
//! This library is **not widely used** and **lacks comprehensive tests**. As a result, **never use it for production use**!
//! Please use it with caution, and only in non-critical applications or for experimentation purposes.

mod constants;
mod error;
mod interpreter;
mod num;
mod opcode;
mod signature_checker;
mod stack;
#[cfg(test)]
mod tests;

use bitcoin::hashes::Hash;
use bitcoin::{secp256k1, TapLeafHash};
use bitflags::bitflags;

pub use self::error::Error;
pub use self::interpreter::verify_script;
pub use self::signature_checker::{
    NoSignatureCheck, SignatureChecker, SignatureError, TransactionSignatureChecker,
};

pub type H256 = bitcoin::hashes::sha256::Hash;
pub type SchnorrSignature = bitcoin::taproot::Signature;

/// Same semantic with [`bitcoin::ecdsa::Signature`] with the following differences:
///
/// - `sighash_type` uses u32 instead of [`bitcoin::EcdsaSighashType`].
/// - Ensure lower S via `normalize_s()`.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct EcdsaSignature {
    /// The underlying ECDSA Signature.
    pub signature: secp256k1::ecdsa::Signature,
    /// The corresponding hash type.
    pub sighash_type: u32,
}

impl EcdsaSignature {
    /// Constructs a [`EcdsaSignature`] from the full sig bytes.
    ///
    /// https://github.com/bitcoin/bitcoin/blob/82ba50513425bf0568d4f9456282dc9713132490/src/pubkey.cpp#L285
    /// https://github.com/bitcoin/bitcoin/blob/82ba50513425bf0568d4f9456282dc9713132490/src/pubkey.cpp#L290
    pub fn parse_der_lax(full_sig_bytes: &[u8]) -> Result<Self, bitcoin::ecdsa::Error> {
        let (sighash_type, sig) = full_sig_bytes
            .split_last()
            .ok_or(bitcoin::ecdsa::Error::EmptySignature)?;
        let sighash_type = *sighash_type as u32;

        let mut signature = secp256k1::ecdsa::Signature::from_der_lax(sig)?;

        // libsecp256k1's ECDSA verification requires lower-S signatures, which have
        // not historically been enforced in Bitcoin, so normalize them first.
        signature.normalize_s();

        Ok(Self {
            signature,
            sighash_type,
        })
    }
}

bitflags! {
    /// Script verification flags.
    ///
    /// https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/script/interpreter.h#L45
    #[derive(Debug, Clone)]
    pub struct VerifyFlags: u32 {
        const NONE = 0;

        /// Evaluate P2SH subscripts (BIP16).
        const P2SH = 1 << 0;

        /// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
        /// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
        /// (not used or intended as a consensus rule).
        const STRICTENC = 1 << 1;

        // Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
        const DERSIG = 1 << 2;

        // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
        // (BIP62 rule 5)
        const LOW_S = 1 << 3;

        // verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
        const NULLDUMMY = 1 << 4;

        // Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
        const SIGPUSHONLY = 1 << 5;

        // Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
        // pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
        // any other push causes the script to fail (BIP62 rule 3).
        // In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
        const MINIMALDATA = 1 << 6;

        // Discourage use of NOPs reserved for upgrades (NOP1-10)
        //
        // Provided so that nodes can avoid accepting or mining transactions
        // containing executed NOP's whose meaning may change after a soft-fork,
        // thus rendering the script invalid; with this flag set executing
        // discouraged NOPs fails the script. This verification flag will never be
        // a mandatory flag applied to scripts in a block. NOPs that are not
        // executed, e.g.  within an unexecuted IF ENDIF block, are *not* rejected.
        // NOPs that have associated forks to give them new meaning (CLTV, CSV)
        // are not subject to this rule.
        const DISCOURAGE_UPGRADABLE_NOPS = 1 << 7;

        // Require that only a single stack element remains after evaluation. This changes the success criterion from
        // "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
        // "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
        // (BIP62 rule 6)
        // Note: CLEANSTACK should never be used without P2SH or WITNESS.
        // Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
        //       consensus rules. It is automatic there and does not need this flag.
        const CLEANSTACK = 1 << 8;

        // Verify CHECKLOCKTIMEVERIFY
        //
        // See BIP65 for details.
        const CHECKLOCKTIMEVERIFY = 1 << 9;

        // support CHECKSEQUENCEVERIFY opcode
        //
        // See BIP112 for details
        const CHECKSEQUENCEVERIFY = 1 << 10;

        // Support segregated witness
        const WITNESS = 1 << 11;

        // Making v1-v16 witness program non-standard
        const DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 1 << 12;

        // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
        //
        // Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
        //       rules. It is automatic there and does not depend on this flag.
        const MINIMALIF = 1 << 13;

        // Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
        const NULLFAIL = 1 << 14;

        // Public keys in segregated witness scripts must be compressed
        const WITNESS_PUBKEYTYPE = 1 << 15;

        // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
        const CONST_SCRIPTCODE = 1 << 16;

        // Taproot/Tapscript validation (BIPs 341 & 342)
        const TAPROOT = 1 << 17;

        // Making unknown Taproot leaf versions non-standard
        const DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = 1 << 18;

        // Making unknown OP_SUCCESS non-standard
        const DISCOURAGE_OP_SUCCESS = 1 << 19;

        // Making unknown public key versions (in BIP 342 scripts) non-standard
        const DISCOURAGE_UPGRADABLE_PUBKEYTYPE = 1 << 20;
    }
}

impl VerifyFlags {
    pub fn verify_minimaldata(&self) -> bool {
        self.contains(Self::MINIMALDATA)
    }

    pub fn verify_sigpushonly(&self) -> bool {
        self.contains(Self::SIGPUSHONLY)
    }

    pub fn verify_p2sh(&self) -> bool {
        self.contains(Self::P2SH)
    }

    pub fn verify_witness(&self) -> bool {
        self.contains(Self::WITNESS)
    }

    pub fn verify_discourage_upgradable_witness_program(&self) -> bool {
        self.contains(Self::DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
    }
}

/// Represents different signature verification schemes used in Bitcoin
///
/// https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/script/interpreter.h#L190
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SigVersion {
    /// Bare scripts and BIP16 P2SH-wrapped redeemscripts
    Base,
    /// Witness v0 (P2WPKH and P2WSH); see BIP 141
    WitnessV0,
    /// Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341
    Taproot,
    /// Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, script path spending,
    /// leaf version 0xc0; see BIP 342
    Tapscript,
}

// https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/script/interpreter.h#L198
#[derive(Debug)]
pub struct ScriptExecutionData {
    /// Whether m_tapleaf_hash is initialized
    pub tapleaf_hash_init: bool,
    /// The tapleaf hash
    pub tapleaf_hash: TapLeafHash,

    /// Whether m_codeseparator_pos is initialized
    pub codeseparator_pos_init: bool,
    /// Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed)
    pub codeseparator_pos: u32,

    /// Whether m_annex_present and m_annex_hash are initialized
    pub annex_init: bool,
    /// Whether an annex is present
    pub annex_present: bool,
    /// Hash of the annex data
    pub annex_hash: H256,
    /// Annex data.
    ///
    /// We store the annex data for signature_checker.
    pub annex: Option<Vec<u8>>,

    /// Whether m_validation_weight_left is initialized
    pub validation_weight_left_init: bool,
    /// How much validation weight is left (decremented for every successful non-empty signature check)
    pub validation_weight_left: i64,

    /// The hash of the corresponding output
    pub output_hash: Option<H256>,
}

impl Default for ScriptExecutionData {
    fn default() -> Self {
        Self {
            tapleaf_hash_init: false,
            tapleaf_hash: TapLeafHash::from_slice(H256::all_zeros().as_byte_array())
                .expect("Static value must be correct; qed"),
            codeseparator_pos_init: false,
            codeseparator_pos: 0,
            annex_init: false,
            annex_present: false,
            annex_hash: H256::all_zeros(),
            annex: None,
            validation_weight_left_init: false,
            validation_weight_left: 0,
            output_hash: None,
        }
    }
}