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