Skip to main content

bsv_script/interpreter/
thread.rs

1//! Script execution thread — the core interpreter engine.
2
3use crate::opcodes::*;
4use crate::Script;
5
6use super::config::Config;
7use super::error::{InterpreterError, InterpreterErrorCode};
8use super::flags::ScriptFlags;
9use super::ops_crypto::HashType;
10use super::parsed_opcode::*;
11use super::scriptnum::*;
12use super::stack::*;
13use super::TxContext;
14
15/// Conditional execution constants.
16const OP_COND_FALSE: i32 = 0;
17const OP_COND_TRUE: i32 = 1;
18
19/// The execution thread for the script interpreter.
20pub struct Thread<'a> {
21    /// The main data stack used during script execution.
22    pub dstack: Stack,
23    /// The alternate stack used by OP_TOALTSTACK and OP_FROMALTSTACK.
24    pub astack: Stack,
25    /// Stack tracking nested IF/ELSE/ENDIF conditional execution state.
26    pub else_stack: BoolStack,
27    /// Interpreter configuration with pre/post-genesis limits.
28    pub cfg: Config,
29    /// The parsed scripts to execute (unlocking, locking, and optionally P2SH).
30    pub scripts: Vec<ParsedScript>,
31    /// Stack of conditional execution flags for nested IF/ELSE blocks.
32    pub cond_stack: Vec<i32>,
33    /// Saved copy of the data stack after the first (unlocking) script for BIP16.
34    pub saved_first_stack: Vec<Vec<u8>>,
35    /// Index of the currently executing script in the scripts array.
36    pub script_idx: usize,
37    /// Offset of the currently executing opcode within the current script.
38    pub script_off: usize,
39    /// Offset of the most recent OP_CODESEPARATOR in the current script.
40    pub last_code_sep: usize,
41    /// Running count of non-push opcodes executed (checked against max_ops).
42    pub num_ops: usize,
43    /// Active script verification flags controlling interpreter behavior.
44    pub flags: ScriptFlags,
45    /// Whether BIP16 (P2SH) evaluation is active for this execution.
46    pub bip16: bool,
47    /// Whether post-genesis rules are active (relaxed limits, OP_RETURN behavior).
48    pub after_genesis: bool,
49    /// Whether an OP_RETURN has been encountered in post-genesis mode.
50    pub early_return_after_genesis: bool,
51    /// Optional transaction context for signature and locktime verification.
52    pub tx_context: Option<&'a dyn TxContext>,
53    /// The transaction input index being verified.
54    pub input_idx: usize,
55}
56
57impl<'a> Thread<'a> {
58    /// Create a new execution thread from unlocking and locking scripts.
59    ///
60    /// Validates script sizes, parses both scripts, and initializes the
61    /// execution environment with the appropriate flags and configuration.
62    pub fn new(
63        unlocking_script: &Script,
64        locking_script: &Script,
65        flags: ScriptFlags,
66        tx_context: Option<&'a dyn TxContext>,
67        input_idx: usize,
68    ) -> Result<Self, InterpreterError> {
69        let after_genesis = flags.has_flag(ScriptFlags::UTXO_AFTER_GENESIS);
70        let cfg = if after_genesis {
71            Config::after_genesis()
72        } else {
73            Config::before_genesis()
74        };
75
76        let mut actual_flags = flags;
77
78        // ForkID implies strict encoding
79        if actual_flags.has_flag(ScriptFlags::ENABLE_SIGHASH_FORKID) {
80            actual_flags.add_flag(ScriptFlags::VERIFY_STRICT_ENCODING);
81        }
82
83        // Clean stack requires BIP16
84        if actual_flags.has_flag(ScriptFlags::VERIFY_CLEAN_STACK)
85            && !actual_flags.has_flag(ScriptFlags::BIP16)
86        {
87            return Err(InterpreterError::new(
88                InterpreterErrorCode::InvalidFlags,
89                "invalid scriptflag combination".to_string(),
90            ));
91        }
92
93        let verify_minimal_data = actual_flags.has_flag(ScriptFlags::VERIFY_MINIMAL_DATA);
94
95        // Validate script sizes
96        if unlocking_script.to_bytes().len() > cfg.max_script_size() {
97            return Err(InterpreterError::new(
98                InterpreterErrorCode::ScriptTooBig,
99                format!(
100                    "unlocking script size {} is larger than the max allowed size {}",
101                    unlocking_script.to_bytes().len(),
102                    cfg.max_script_size()
103                ),
104            ));
105        }
106        if locking_script.to_bytes().len() > cfg.max_script_size() {
107            return Err(InterpreterError::new(
108                InterpreterErrorCode::ScriptTooBig,
109                format!(
110                    "locking script size {} is larger than the max allowed size {}",
111                    locking_script.to_bytes().len(),
112                    cfg.max_script_size()
113                ),
114            ));
115        }
116
117        // Empty scripts = eval false
118        if unlocking_script.to_bytes().is_empty() && locking_script.to_bytes().is_empty() {
119            return Err(InterpreterError::new(
120                InterpreterErrorCode::EvalFalse,
121                "false stack entry at end of script execution".to_string(),
122            ));
123        }
124
125        let error_on_checksig = tx_context.is_none();
126
127        let uscript = parse_script(unlocking_script, error_on_checksig)?;
128        let lscript = parse_script(locking_script, error_on_checksig)?;
129
130        // Verify sig push only
131        if actual_flags.has_flag(ScriptFlags::VERIFY_SIG_PUSH_ONLY) && !is_push_only(&uscript) {
132            return Err(InterpreterError::new(
133                InterpreterErrorCode::NotPushOnly,
134                "signature script is not push only".to_string(),
135            ));
136        }
137
138        let bip16 = actual_flags.has_flag(ScriptFlags::BIP16) && locking_script.is_p2sh();
139        if bip16 && !is_push_only(&uscript) {
140            return Err(InterpreterError::new(
141                InterpreterErrorCode::NotPushOnly,
142                "pay to script hash is not push only".to_string(),
143            ));
144        }
145
146        let scripts = vec![uscript, lscript];
147        let mut script_idx = 0;
148
149        // Skip empty unlocking script
150        if unlocking_script.to_bytes().is_empty() {
151            script_idx = 1;
152        }
153
154        let max_num_len = cfg.max_script_number_length();
155
156        let thread = Thread {
157            dstack: Stack::new(max_num_len, after_genesis, verify_minimal_data),
158            astack: Stack::new(max_num_len, after_genesis, verify_minimal_data),
159            else_stack: BoolStack::new(),
160            cfg,
161            scripts,
162            cond_stack: Vec::new(),
163            saved_first_stack: Vec::new(),
164            script_idx,
165            script_off: 0,
166            last_code_sep: 0,
167            num_ops: 0,
168            flags: actual_flags,
169            bip16,
170            after_genesis,
171            early_return_after_genesis: false,
172            tx_context,
173            input_idx,
174        };
175
176        Ok(thread)
177    }
178
179    /// Check if a specific script verification flag is set.
180    pub fn has_flag(&self, flag: ScriptFlags) -> bool {
181        self.flags.has_flag(flag)
182    }
183
184    /// Check if any of the given script verification flags are set.
185    pub fn has_any(&self, flags: &[ScriptFlags]) -> bool {
186        self.flags.has_any(flags)
187    }
188
189    /// Return true if the current conditional branch is executing.
190    pub fn is_branch_executing(&self) -> bool {
191        self.cond_stack.is_empty() || *self.cond_stack.last().unwrap() == OP_COND_TRUE
192    }
193
194    /// Return true if the given opcode should be executed in the current state.
195    pub fn should_exec(&self, pop: &ParsedOpcode) -> bool {
196        if !self.after_genesis {
197            return true;
198        }
199        let cf = self.cond_stack.iter().all(|&v| v != OP_COND_FALSE);
200        cf && (!self.early_return_after_genesis || pop.opcode == OP_RETURN)
201    }
202
203    /// Execute all scripts.
204    pub fn execute(&mut self) -> Result<(), InterpreterError> {
205        loop {
206            let done = self.step()?;
207            if done {
208                break;
209            }
210        }
211        self.check_error_condition(true)
212    }
213
214    /// Execute one step. Returns true if execution is complete.
215    pub fn step(&mut self) -> Result<bool, InterpreterError> {
216        // Valid PC check
217        if self.script_idx >= self.scripts.len() {
218            return Err(InterpreterError::new(
219                InterpreterErrorCode::InvalidProgramCounter,
220                format!(
221                    "past input scripts {}:{} {}:xxxx",
222                    self.script_idx,
223                    self.script_off,
224                    self.scripts.len()
225                ),
226            ));
227        }
228        if self.script_off >= self.scripts[self.script_idx].len() {
229            return Err(InterpreterError::new(
230                InterpreterErrorCode::InvalidProgramCounter,
231                format!(
232                    "past input scripts {}:{} {}:{:04}",
233                    self.script_idx,
234                    self.script_off,
235                    self.script_idx,
236                    self.scripts[self.script_idx].len()
237                ),
238            ));
239        }
240
241        let opcode = self.scripts[self.script_idx][self.script_off].clone();
242
243        if let Err(e) = self.execute_opcode(&opcode) {
244            if e.code == InterpreterErrorCode::Ok {
245                // Early success (OP_RETURN after genesis)
246                self.shift_script();
247                return Ok(self.script_idx >= self.scripts.len());
248            }
249            return Err(e);
250        }
251
252        self.script_off += 1;
253
254        // Stack size check
255        let combined = self.dstack.depth() + self.astack.depth();
256        if combined > self.cfg.max_stack_size() as i32 {
257            return Err(InterpreterError::new(
258                InterpreterErrorCode::StackOverflow,
259                format!(
260                    "combined stack size {} > max allowed {}",
261                    combined,
262                    self.cfg.max_stack_size()
263                ),
264            ));
265        }
266
267        if self.script_off < self.scripts[self.script_idx].len() {
268            return Ok(false);
269        }
270
271        // End of script - check conditionals
272        if !self.cond_stack.is_empty() {
273            return Err(InterpreterError::new(
274                InterpreterErrorCode::UnbalancedConditional,
275                "end of script reached in conditional execution".to_string(),
276            ));
277        }
278
279        // Alt stack doesn't persist between scripts
280        self.astack.clear();
281
282        // Move to next script
283        self.shift_script();
284
285        // BIP16 handling
286        if self.bip16 && !self.after_genesis && self.script_idx <= 2 {
287            match self.script_idx {
288                1 => {
289                    self.saved_first_stack = self.dstack.get_stack();
290                }
291                2 => {
292                    self.check_error_condition(false)?;
293                    let scr_bytes = self.saved_first_stack.last().cloned().unwrap_or_default();
294                    let scr = Script::from_bytes(&scr_bytes);
295                    let pops = parse_script(&scr, false)?;
296                    self.scripts.push(pops);
297                    let len = self.saved_first_stack.len();
298                    let new_stack = self.saved_first_stack[..len.saturating_sub(1)].to_vec();
299                    self.dstack.set_stack(new_stack);
300                }
301                _ => {}
302            }
303        }
304
305        // Skip zero-length scripts
306        if self.script_idx < self.scripts.len()
307            && self.script_off >= self.scripts[self.script_idx].len()
308        {
309            self.script_idx += 1;
310        }
311
312        self.last_code_sep = 0;
313        if self.script_idx >= self.scripts.len() {
314            return Ok(true);
315        }
316
317        Ok(false)
318    }
319
320    fn shift_script(&mut self) {
321        self.num_ops = 0;
322        self.script_off = 0;
323        self.script_idx += 1;
324        self.early_return_after_genesis = false;
325    }
326
327    fn check_error_condition(&mut self, final_script: bool) -> Result<(), InterpreterError> {
328        if self.dstack.depth() < 1 {
329            return Err(InterpreterError::new(
330                InterpreterErrorCode::EmptyStack,
331                "stack empty at end of script execution".to_string(),
332            ));
333        }
334
335        if final_script
336            && self.has_flag(ScriptFlags::VERIFY_CLEAN_STACK)
337            && self.dstack.depth() != 1
338        {
339            return Err(InterpreterError::new(
340                InterpreterErrorCode::CleanStack,
341                format!(
342                    "stack contains {} unexpected items",
343                    self.dstack.depth() - 1
344                ),
345            ));
346        }
347
348        let v = self.dstack.pop_bool()?;
349        if !v {
350            return Err(InterpreterError::new(
351                InterpreterErrorCode::EvalFalse,
352                "false stack entry at end of script execution".to_string(),
353            ));
354        }
355
356        Ok(())
357    }
358
359    fn execute_opcode(&mut self, pop: &ParsedOpcode) -> Result<(), InterpreterError> {
360        // Element size check
361        if pop.data.len() > self.cfg.max_script_element_size() {
362            return Err(InterpreterError::new(
363                InterpreterErrorCode::ElementTooBig,
364                format!(
365                    "element size {} exceeds max allowed size {}",
366                    pop.data.len(),
367                    self.cfg.max_script_element_size()
368                ),
369            ));
370        }
371
372        let exec = self.should_exec(pop);
373
374        // Disabled opcodes fail on program counter
375        if pop.is_disabled() && (!self.after_genesis || exec) {
376            return Err(InterpreterError::new(
377                InterpreterErrorCode::DisabledOpcode,
378                format!("attempt to execute disabled opcode {}", pop.name()),
379            ));
380        }
381
382        // Always-illegal opcodes
383        if pop.always_illegal() && !self.after_genesis {
384            return Err(InterpreterError::new(
385                InterpreterErrorCode::ReservedOpcode,
386                format!("attempt to execute reserved opcode {}", pop.name()),
387            ));
388        }
389
390        // Count non-push operations
391        if pop.opcode > OP_16 {
392            self.num_ops += 1;
393            if self.num_ops > self.cfg.max_ops() {
394                return Err(InterpreterError::new(
395                    InterpreterErrorCode::TooManyOperations,
396                    format!("exceeded max operation limit of {}", self.cfg.max_ops()),
397                ));
398            }
399        }
400
401        // Not executing and not conditional => skip
402        if !self.is_branch_executing() && !pop.is_conditional() {
403            return Ok(());
404        }
405
406        // Minimal data push check
407        if self.dstack.verify_minimal_data
408            && self.is_branch_executing()
409            && pop.opcode <= OP_PUSHDATA4
410            && exec
411        {
412            pop.enforce_minimum_data_push()?;
413        }
414
415        // If we already hit OP_RETURN, skip non-conditionals
416        if !exec && !pop.is_conditional() {
417            return Ok(());
418        }
419
420        // Dispatch
421        self.dispatch_opcode(pop)
422    }
423
424    fn dispatch_opcode(&mut self, pop: &ParsedOpcode) -> Result<(), InterpreterError> {
425        match pop.opcode {
426            OP_FALSE => {
427                self.dstack.push_byte_array(vec![]);
428                Ok(())
429            }
430            op if (OP_DATA_1..=OP_DATA_75).contains(&op) => {
431                self.dstack.push_byte_array(pop.data.clone());
432                Ok(())
433            }
434            OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 => {
435                self.dstack.push_byte_array(pop.data.clone());
436                Ok(())
437            }
438            OP_1NEGATE => {
439                self.dstack.push_int(&ScriptNumber::new(-1, self.after_genesis));
440                Ok(())
441            }
442            OP_RESERVED => self.op_reserved(pop),
443            op if (OP_1..=OP_16).contains(&op) => {
444                self.dstack.push_byte_array(vec![op - (OP_1 - 1)]);
445                Ok(())
446            }
447            OP_NOP => Ok(()),
448            OP_VER => self.op_reserved(pop),
449            OP_IF => self.op_if(pop),
450            OP_NOTIF => self.op_notif(pop),
451            OP_VERIF | OP_VERNOTIF => self.op_ver_conditional(pop),
452            OP_ELSE => self.op_else(pop),
453            OP_ENDIF => self.op_endif(pop),
454            OP_VERIFY => self.op_verify(pop),
455            OP_RETURN => self.op_return(),
456
457            // Locktime
458            OP_CHECKLOCKTIMEVERIFY => self.op_check_locktime_verify(),
459            OP_CHECKSEQUENCEVERIFY => self.op_check_sequence_verify(),
460
461            // Stack ops
462            OP_TOALTSTACK => self.op_to_alt_stack(),
463            OP_FROMALTSTACK => self.op_from_alt_stack(),
464            OP_2DROP => self.dstack.drop_n(2),
465            OP_2DUP => self.dstack.dup_n(2),
466            OP_3DUP => self.dstack.dup_n(3),
467            OP_2OVER => self.dstack.over_n(2),
468            OP_2ROT => self.dstack.rot_n(2),
469            OP_2SWAP => self.dstack.swap_n(2),
470            OP_IFDUP => self.op_ifdup(),
471            OP_DEPTH => {
472                let d = self.dstack.depth();
473                self.dstack.push_int(&ScriptNumber::new(d as i64, self.after_genesis));
474                Ok(())
475            }
476            OP_DROP => self.dstack.drop_n(1),
477            OP_DUP => self.dstack.dup_n(1),
478            OP_NIP => self.dstack.nip_n_discard(1),
479            OP_OVER => self.dstack.over_n(1),
480            OP_PICK => self.op_pick(),
481            OP_ROLL => self.op_roll(),
482            OP_ROT => self.dstack.rot_n(1),
483            OP_SWAP => self.dstack.swap_n(1),
484            OP_TUCK => self.dstack.tuck(),
485
486            // Splice
487            OP_CAT => self.op_cat(),
488            OP_SPLIT => self.op_split(),
489            OP_NUM2BIN => self.op_num2bin(),
490            OP_BIN2NUM => self.op_bin2num(),
491            OP_SIZE => self.op_size(),
492
493            // Bitwise
494            OP_INVERT => self.op_invert(),
495            OP_AND => self.op_bitwise(|a, b| a & b),
496            OP_OR => self.op_bitwise(|a, b| a | b),
497            OP_XOR => self.op_bitwise(|a, b| a ^ b),
498            OP_EQUAL => self.op_equal(),
499            OP_EQUALVERIFY => self.op_equalverify(pop),
500            OP_RESERVED1 | OP_RESERVED2 => self.op_reserved(pop),
501
502            // Arithmetic
503            OP_1ADD => self.op_unary_int(|m| { m.incr(); }),
504            OP_1SUB => self.op_unary_int(|m| { m.decr(); }),
505            OP_2MUL | OP_2DIV => Err(InterpreterError::new(
506                InterpreterErrorCode::DisabledOpcode,
507                format!("attempt to execute disabled opcode {}", pop.name()),
508            )),
509            OP_NEGATE => self.op_unary_int(|m| { m.neg(); }),
510            OP_ABS => self.op_unary_int(|m| { m.abs(); }),
511            OP_NOT => self.op_not(),
512            OP_0NOTEQUAL => self.op_0notequal(),
513            OP_ADD => self.op_add(),
514            OP_SUB => self.op_sub(),
515            OP_MUL => self.op_mul(),
516            OP_DIV => self.op_div(),
517            OP_MOD => self.op_mod(),
518            OP_LSHIFT => self.op_lshift(),
519            OP_RSHIFT => self.op_rshift(),
520            OP_BOOLAND => self.op_bool_binop(|a, b| !a.is_zero() && !b.is_zero()),
521            OP_BOOLOR => self.op_bool_binop(|a, b| !a.is_zero() || !b.is_zero()),
522            OP_NUMEQUAL => self.op_bool_binop(|a, b| a.equal(b)),
523            OP_NUMEQUALVERIFY => self.op_numequalverify(pop),
524            OP_NUMNOTEQUAL => self.op_bool_binop(|a, b| !a.equal(b)),
525            OP_LESSTHAN => self.op_bool_binop(|a, b| a.less_than(b)),
526            OP_GREATERTHAN => self.op_bool_binop(|a, b| a.greater_than(b)),
527            OP_LESSTHANOREQUAL => self.op_bool_binop(|a, b| a.less_than_or_equal(b)),
528            OP_GREATERTHANOREQUAL => self.op_bool_binop(|a, b| a.greater_than_or_equal(b)),
529            OP_MIN => self.op_min(),
530            OP_MAX => self.op_max(),
531            OP_WITHIN => self.op_within(),
532
533            // Crypto
534            OP_RIPEMD160 => self.op_hash(HashType::Ripemd160),
535            OP_SHA1 => self.op_hash(HashType::Sha1),
536            OP_SHA256 => self.op_hash(HashType::Sha256),
537            OP_HASH160 => self.op_hash(HashType::Hash160),
538            OP_HASH256 => self.op_hash(HashType::Hash256),
539            OP_CODESEPARATOR => {
540                self.last_code_sep = self.script_off;
541                Ok(())
542            }
543            OP_CHECKSIG => self.op_checksig(),
544            OP_CHECKSIGVERIFY => self.op_checksigverify(pop),
545            OP_CHECKMULTISIG => self.op_checkmultisig(),
546            OP_CHECKMULTISIGVERIFY => self.op_checkmultisigverify(pop),
547
548            // NOP opcodes
549            OP_NOP1 | OP_NOP4 | OP_NOP5 | OP_NOP6 | OP_NOP7 | OP_NOP8 | OP_NOP9
550            | OP_NOP10 => {
551                if self.has_flag(ScriptFlags::DISCOURAGE_UPGRADABLE_NOPS) {
552                    return Err(InterpreterError::new(
553                        InterpreterErrorCode::DiscourageUpgradableNOPs,
554                        format!(
555                            "OP_NOP{} reserved for soft-fork upgrades",
556                            pop.opcode - (OP_NOP1 - 1)
557                        ),
558                    ));
559                }
560                Ok(())
561            }
562
563            // All unknown/invalid opcodes
564            _ => Err(InterpreterError::new(
565                InterpreterErrorCode::ReservedOpcode,
566                format!("attempt to execute invalid opcode {}", pop.name()),
567            )),
568        }
569    }
570}