Skip to main content

kaspa_txscript/opcodes/
mod.rs

1#[macro_use]
2mod macros;
3use crate::MAX_SCRIPT_ELEMENT_SIZE;
4use crate::zk_precompiles::{parse_tag, verify_zk};
5use crate::{
6    LOCK_TIME_THRESHOLD, MAX_TX_IN_SEQUENCE_NUM, NO_COST_OPCODE, SEQUENCE_LOCK_TIME_DISABLED, SEQUENCE_LOCK_TIME_MASK, ScriptSource,
7    SpkEncoding, TxScriptEngine, TxScriptError,
8    data_stack::{OpcodeData, StackEntry, serialize_i64},
9};
10use blake2b_simd::Params;
11use kaspa_consensus_core::hashing::sighash::SigHashReusedValues;
12use kaspa_consensus_core::hashing::sighash_type::SigHashType;
13use kaspa_consensus_core::mass::ScriptUnits;
14use kaspa_consensus_core::tx::VerifiableTransaction;
15use kaspa_hashes::Hash;
16use kaspa_hashes::ZERO_HASH;
17use kaspa_utils::hex::FromHex;
18use sha2::{Digest, Sha256};
19use std::{
20    fmt::{Debug, Formatter},
21    num::TryFromIntError,
22};
23
24/// First value in the range formed by the "small integer" Op# opcodes
25pub const OP_SMALL_INT_MIN_VAL: u8 = 1;
26/// Last value in the range formed by the "small integer" Op# opcodes
27pub const OP_SMALL_INT_MAX_VAL: u8 = 16;
28/// First value in the range formed by OpData# opcodes (where opcode == value)
29pub const OP_DATA_MIN_VAL: u8 = self::codes::OpData1;
30/// Last value in the range formed by OpData# opcodes (where opcode == value)
31pub const OP_DATA_MAX_VAL: u8 = self::codes::OpData75;
32/// Minus 1 value
33pub const OP_1_NEGATE_VAL: u8 = 0x81;
34
35#[derive(Debug, PartialEq, Eq)]
36pub(crate) enum OpCond {
37    False,
38    True,
39    Skip,
40}
41
42impl OpCond {
43    pub fn negate(&self) -> OpCond {
44        match self {
45            OpCond::True => OpCond::False,
46            OpCond::False => OpCond::True,
47            OpCond::Skip => OpCond::Skip,
48        }
49    }
50}
51
52type OpCodeResult = Result<(), TxScriptError>;
53
54#[derive(Copy, Clone)]
55enum HashOpcodePricing {
56    Blake2b,
57    Blake3,
58    Sha256,
59}
60
61impl HashOpcodePricing {
62    const fn units_per_byte(self) -> u64 {
63        match self {
64            Self::Blake2b => 2,
65            Self::Blake3 => 1,
66            Self::Sha256 => 1,
67        }
68    }
69
70    fn script_units_for_data(self, data_len: usize) -> ScriptUnits {
71        ScriptUnits((data_len as u64).saturating_mul(self.units_per_byte()))
72    }
73}
74
75pub(crate) struct OpCode<const CODE: u8> {
76    data: Vec<u8>,
77}
78
79impl<const CODE: u8> Debug for OpCode<CODE> {
80    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81        write!(f, "Opcode<{:#2x}>{{ data:{:?} }}", CODE, self.data)
82    }
83}
84
85pub trait OpCodeMetadata: Debug {
86    // Opcode number
87    fn value(&self) -> u8;
88    // length of data
89    fn len(&self) -> usize;
90    // Conditional should be executed also is not in branch
91    fn is_conditional(&self) -> bool;
92
93    fn is_disabled(&self) -> bool;
94    fn always_illegal(&self) -> bool;
95    fn is_push_opcode(&self) -> bool;
96    fn get_data(&self) -> &[u8];
97
98    fn is_empty(&self) -> bool {
99        self.len() == 0
100    }
101}
102
103pub trait OpCodeExecution<T: VerifiableTransaction, Reused: SigHashReusedValues> {
104    fn empty() -> Result<Box<dyn OpCodeImplementation<T, Reused>>, TxScriptError>
105    where
106        Self: Sized;
107    #[allow(clippy::new_ret_no_self)]
108    fn new(data: Vec<u8>) -> Result<Box<dyn OpCodeImplementation<T, Reused>>, TxScriptError>
109    where
110        Self: Sized;
111
112    fn execute(&self, vm: &mut TxScriptEngine<T, Reused>) -> OpCodeResult;
113}
114
115pub trait OpcodeSerialization {
116    fn serialize(&self) -> Vec<u8>;
117    fn deserialize<'i, I: Iterator<Item = &'i u8>, T: VerifiableTransaction, Reused: SigHashReusedValues>(
118        it: &mut I,
119    ) -> Result<Box<dyn OpCodeImplementation<T, Reused>>, TxScriptError>
120    where
121        Self: Sized;
122}
123
124pub trait OpCodeImplementation<T: VerifiableTransaction, Reused: SigHashReusedValues>:
125    OpCodeExecution<T, Reused> + OpCodeMetadata + OpcodeSerialization + std::fmt::Display
126{
127}
128
129impl<const CODE: u8> OpCodeMetadata for OpCode<CODE> {
130    fn value(&self) -> u8 {
131        CODE
132    }
133
134    fn is_disabled(&self) -> bool {
135        matches!(CODE, codes::OpLeft | codes::OpRight | codes::Op2Mul | codes::Op2Div | codes::OpLShift | codes::OpRShift)
136    }
137
138    fn always_illegal(&self) -> bool {
139        matches!(CODE, codes::OpVerIf | codes::OpVerNotIf)
140    }
141
142    fn is_push_opcode(&self) -> bool {
143        CODE <= NO_COST_OPCODE
144    }
145
146    fn len(&self) -> usize {
147        self.data.len()
148    }
149
150    // TODO: add it to opcode specification
151    fn is_conditional(&self) -> bool {
152        self.value() >= 0x63 && self.value() <= 0x68
153    }
154
155    fn get_data(&self) -> &[u8] {
156        &self.data
157    }
158}
159
160// Helpers for some opcodes with shared data
161// Literal opcode data is already paid for in script size, so it does not consume script units.
162#[inline]
163fn push_literal_data<T: VerifiableTransaction, Reused: SigHashReusedValues>(
164    data: Vec<u8>,
165    vm: &mut TxScriptEngine<T, Reused>,
166) -> OpCodeResult {
167    vm.dstack.push_unmetered(data.into())
168}
169
170#[inline]
171fn push_data<T: VerifiableTransaction, Reused: SigHashReusedValues>(
172    data: Vec<u8>,
173    vm: &mut TxScriptEngine<T, Reused>,
174) -> OpCodeResult {
175    vm.dstack.push(data.into())
176}
177
178// Literal opcode numbers are already paid for in script size, so this push does not consume script units.
179#[inline]
180fn push_literal_number<T: VerifiableTransaction, Reused: SigHashReusedValues>(
181    number: i64,
182    vm: &mut TxScriptEngine<T, Reused>,
183) -> OpCodeResult {
184    vm.dstack.push_item_unmetered(number)
185}
186
187#[inline]
188fn push_number<T: VerifiableTransaction, Reused: SigHashReusedValues>(
189    number: i64,
190    vm: &mut TxScriptEngine<T, Reused>,
191) -> OpCodeResult {
192    vm.dstack.push_item(number)?;
193    Ok(())
194}
195
196fn substring(data: &[u8], start: usize, end: usize) -> Result<Vec<u8>, TxScriptError> {
197    let diff = end.checked_sub(start).ok_or(TxScriptError::InvalidRange { start, end })?;
198    // Since this function is only used post-Toccata, we can use the post-Toccata max size here.
199    if diff > MAX_SCRIPT_ELEMENT_SIZE {
200        return Err(TxScriptError::ElementTooBig(diff, MAX_SCRIPT_ELEMENT_SIZE));
201    }
202    data.get(start..end).map(<[u8]>::to_vec).ok_or(TxScriptError::OutOfBoundsSubstring(start, end, data.len()))
203}
204
205pub(crate) fn i32_to_usize(value: i32) -> Result<usize, TxScriptError> {
206    value.try_into().map_err(|_| TxScriptError::InvalidIndex(value))
207}
208
209pub(crate) fn i32s_to_usizes<const N: usize>(arr: [i32; N]) -> Result<[usize; N], TxScriptError> {
210    let mut out = [0usize; N];
211    for i in 0..N {
212        out[i] = i32_to_usize(arr[i])?;
213    }
214    Ok(out)
215}
216
217/*
218The following is the implementation and metadata of all opcodes. Each opcode has unique
219number (and template system makes it impossible to use two opcodes), length specification,
220and execution code.
221
222The syntax is as follows:
223```
224opcode OpCodeName<id, length>(self, vm) {
225    code;
226    output
227}
228// OR
229opcode OpCodeName<id, length>(self, vm) statement
230
231// in case of an opcode alias
232opcode |OpCodeAlias| OpCodeName<id, length>(self, vm) {
233    code;
234    output
235}
236// OR
237opcode |OpCodeAlias| OpCodeName<id, length>(self, vm) statement
238```
239
240Length specification is either a number (for fixed length) or a unsigned integer type
241(for var length).
242The execution code is implementing OpCodeImplementation. You can access the engine using the `vm`
243variable.
244
245Implementation details in `opcodes/macros.rs`.
246*/
247opcode_list! {
248
249    // Data push opcodes.
250    opcode |Op0| OpFalse<0x00, 1>(self , vm) {
251        push_literal_data(vec![], vm)
252    }
253
254    opcode OpData1<0x01, 2>(self, vm) push_literal_data(self.data.clone(), vm)
255    opcode OpData2<0x02, 3>(self, vm) push_literal_data(self.data.clone(), vm)
256    opcode OpData3<0x03, 4>(self, vm) push_literal_data(self.data.clone(), vm)
257    opcode OpData4<0x04, 5>(self, vm) push_literal_data(self.data.clone(), vm)
258    opcode OpData5<0x05, 6>(self, vm) push_literal_data(self.data.clone(), vm)
259    opcode OpData6<0x06, 7>(self, vm) push_literal_data(self.data.clone(), vm)
260    opcode OpData7<0x07, 8>(self, vm) push_literal_data(self.data.clone(), vm)
261    opcode OpData8<0x08, 9>(self, vm) push_literal_data(self.data.clone(), vm)
262    opcode OpData9<0x09, 10>(self, vm) push_literal_data(self.data.clone(), vm)
263    opcode OpData10<0x0a, 11>(self, vm) push_literal_data(self.data.clone(), vm)
264    opcode OpData11<0x0b, 12>(self, vm) push_literal_data(self.data.clone(), vm)
265    opcode OpData12<0x0c, 13>(self, vm) push_literal_data(self.data.clone(), vm)
266    opcode OpData13<0x0d, 14>(self, vm) push_literal_data(self.data.clone(), vm)
267    opcode OpData14<0x0e, 15>(self, vm) push_literal_data(self.data.clone(), vm)
268    opcode OpData15<0x0f, 16>(self, vm) push_literal_data(self.data.clone(), vm)
269    opcode OpData16<0x10, 17>(self, vm) push_literal_data(self.data.clone(), vm)
270    opcode OpData17<0x11, 18>(self, vm) push_literal_data(self.data.clone(), vm)
271    opcode OpData18<0x12, 19>(self, vm) push_literal_data(self.data.clone(), vm)
272    opcode OpData19<0x13, 20>(self, vm) push_literal_data(self.data.clone(), vm)
273    opcode OpData20<0x14, 21>(self, vm) push_literal_data(self.data.clone(), vm)
274    opcode OpData21<0x15, 22>(self, vm) push_literal_data(self.data.clone(), vm)
275    opcode OpData22<0x16, 23>(self, vm) push_literal_data(self.data.clone(), vm)
276    opcode OpData23<0x17, 24>(self, vm) push_literal_data(self.data.clone(), vm)
277    opcode OpData24<0x18, 25>(self, vm) push_literal_data(self.data.clone(), vm)
278    opcode OpData25<0x19, 26>(self, vm) push_literal_data(self.data.clone(), vm)
279    opcode OpData26<0x1a, 27>(self, vm) push_literal_data(self.data.clone(), vm)
280    opcode OpData27<0x1b, 28>(self, vm) push_literal_data(self.data.clone(), vm)
281    opcode OpData28<0x1c, 29>(self, vm) push_literal_data(self.data.clone(), vm)
282    opcode OpData29<0x1d, 30>(self, vm) push_literal_data(self.data.clone(), vm)
283    opcode OpData30<0x1e, 31>(self, vm) push_literal_data(self.data.clone(), vm)
284    opcode OpData31<0x1f, 32>(self, vm) push_literal_data(self.data.clone(), vm)
285    opcode OpData32<0x20, 33>(self, vm) push_literal_data(self.data.clone(), vm)
286    opcode OpData33<0x21, 34>(self, vm) push_literal_data(self.data.clone(), vm)
287    opcode OpData34<0x22, 35>(self, vm) push_literal_data(self.data.clone(), vm)
288    opcode OpData35<0x23, 36>(self, vm) push_literal_data(self.data.clone(), vm)
289    opcode OpData36<0x24, 37>(self, vm) push_literal_data(self.data.clone(), vm)
290    opcode OpData37<0x25, 38>(self, vm) push_literal_data(self.data.clone(), vm)
291    opcode OpData38<0x26, 39>(self, vm) push_literal_data(self.data.clone(), vm)
292    opcode OpData39<0x27, 40>(self, vm) push_literal_data(self.data.clone(), vm)
293    opcode OpData40<0x28, 41>(self, vm) push_literal_data(self.data.clone(), vm)
294    opcode OpData41<0x29, 42>(self, vm) push_literal_data(self.data.clone(), vm)
295    opcode OpData42<0x2a, 43>(self, vm) push_literal_data(self.data.clone(), vm)
296    opcode OpData43<0x2b, 44>(self, vm) push_literal_data(self.data.clone(), vm)
297    opcode OpData44<0x2c, 45>(self, vm) push_literal_data(self.data.clone(), vm)
298    opcode OpData45<0x2d, 46>(self, vm) push_literal_data(self.data.clone(), vm)
299    opcode OpData46<0x2e, 47>(self, vm) push_literal_data(self.data.clone(), vm)
300    opcode OpData47<0x2f, 48>(self, vm) push_literal_data(self.data.clone(), vm)
301    opcode OpData48<0x30, 49>(self, vm) push_literal_data(self.data.clone(), vm)
302    opcode OpData49<0x31, 50>(self, vm) push_literal_data(self.data.clone(), vm)
303    opcode OpData50<0x32, 51>(self, vm) push_literal_data(self.data.clone(), vm)
304    opcode OpData51<0x33, 52>(self, vm) push_literal_data(self.data.clone(), vm)
305    opcode OpData52<0x34, 53>(self, vm) push_literal_data(self.data.clone(), vm)
306    opcode OpData53<0x35, 54>(self, vm) push_literal_data(self.data.clone(), vm)
307    opcode OpData54<0x36, 55>(self, vm) push_literal_data(self.data.clone(), vm)
308    opcode OpData55<0x37, 56>(self, vm) push_literal_data(self.data.clone(), vm)
309    opcode OpData56<0x38, 57>(self, vm) push_literal_data(self.data.clone(), vm)
310    opcode OpData57<0x39, 58>(self, vm) push_literal_data(self.data.clone(), vm)
311    opcode OpData58<0x3a, 59>(self, vm) push_literal_data(self.data.clone(), vm)
312    opcode OpData59<0x3b, 60>(self, vm) push_literal_data(self.data.clone(), vm)
313    opcode OpData60<0x3c, 61>(self, vm) push_literal_data(self.data.clone(), vm)
314    opcode OpData61<0x3d, 62>(self, vm) push_literal_data(self.data.clone(), vm)
315    opcode OpData62<0x3e, 63>(self, vm) push_literal_data(self.data.clone(), vm)
316    opcode OpData63<0x3f, 64>(self, vm) push_literal_data(self.data.clone(), vm)
317    opcode OpData64<0x40, 65>(self, vm) push_literal_data(self.data.clone(), vm)
318    opcode OpData65<0x41, 66>(self, vm) push_literal_data(self.data.clone(), vm)
319    opcode OpData66<0x42, 67>(self, vm) push_literal_data(self.data.clone(), vm)
320    opcode OpData67<0x43, 68>(self, vm) push_literal_data(self.data.clone(), vm)
321    opcode OpData68<0x44, 69>(self, vm) push_literal_data(self.data.clone(), vm)
322    opcode OpData69<0x45, 70>(self, vm) push_literal_data(self.data.clone(), vm)
323    opcode OpData70<0x46, 71>(self, vm) push_literal_data(self.data.clone(), vm)
324    opcode OpData71<0x47, 72>(self, vm) push_literal_data(self.data.clone(), vm)
325    opcode OpData72<0x48, 73>(self, vm) push_literal_data(self.data.clone(), vm)
326    opcode OpData73<0x49, 74>(self, vm) push_literal_data(self.data.clone(), vm)
327    opcode OpData74<0x4a, 75>(self, vm) push_literal_data(self.data.clone(), vm)
328    opcode OpData75<0x4b, 76>(self, vm) push_literal_data(self.data.clone(), vm)
329    opcode OpPushData1<0x4c, u8>(self, vm) push_literal_data(self.data.clone(), vm)
330    opcode OpPushData2<0x4d, u16>(self, vm) push_literal_data(self.data.clone(), vm)
331    opcode OpPushData4<0x4e, u32>(self, vm) push_literal_data(self.data.clone(), vm)
332
333    opcode Op1Negate<0x4f, 1>(self, vm) push_literal_number(-1, vm)
334
335    opcode OpReserved<0x50, 1>(self, vm) Err(TxScriptError::OpcodeReserved(format!("{self:?}")))
336
337    opcode |Op1| OpTrue<0x51, 1>(self, vm) push_literal_number(1, vm)
338    opcode Op2<0x52, 1>(self, vm) push_literal_number(2, vm)
339    opcode Op3<0x53, 1>(self, vm) push_literal_number(3, vm)
340    opcode Op4<0x54, 1>(self, vm) push_literal_number(4, vm)
341    opcode Op5<0x55, 1>(self, vm) push_literal_number(5, vm)
342    opcode Op6<0x56, 1>(self, vm) push_literal_number(6, vm)
343    opcode Op7<0x57, 1>(self, vm) push_literal_number(7, vm)
344    opcode Op8<0x58, 1>(self, vm) push_literal_number(8, vm)
345    opcode Op9<0x59, 1>(self, vm) push_literal_number(9, vm)
346    opcode Op10<0x5a, 1>(self, vm) push_literal_number(10, vm)
347    opcode Op11<0x5b, 1>(self, vm) push_literal_number(11, vm)
348    opcode Op12<0x5c, 1>(self, vm) push_literal_number(12, vm)
349    opcode Op13<0x5d, 1>(self, vm) push_literal_number(13, vm)
350    opcode Op14<0x5e, 1>(self, vm) push_literal_number(14, vm)
351    opcode Op15<0x5f, 1>(self, vm) push_literal_number(15, vm)
352    opcode Op16<0x60, 1>(self, vm) push_literal_number(16, vm)
353
354    // Control opcodes.
355    opcode OpNop<0x61, 1>(self, vm) Ok(())
356    opcode OpVer<0x62, 1>(self, vm) Err(TxScriptError::OpcodeReserved(format!("{self:?}")))
357
358    opcode OpIf<0x63, 1>(self, vm) {
359        let cond = if vm.is_executing() {
360            let [cond]: [bool; 1] = vm.dstack.pop_items()?;
361            if cond {
362                OpCond::True
363            } else {
364                OpCond::False
365            }
366        } else {
367            OpCond::Skip
368        };
369
370        vm.cond_stack.push(cond);
371        Ok(())
372    }
373
374    opcode OpNotIf<0x64, 1>(self, vm) {
375        let cond = if vm.is_executing() {
376            let [cond]: [bool; 1] = vm.dstack.pop_items()?;
377            if cond {
378                OpCond::False
379            } else {
380                OpCond::True
381            }
382        }else{
383            OpCond::Skip
384        };
385
386        vm.cond_stack.push(cond);
387        Ok(())
388    }
389
390    opcode OpVerIf<0x65, 1>(self, vm) Err(TxScriptError::OpcodeReserved(format!("{self:?}")))
391    opcode OpVerNotIf<0x66, 1>(self, vm) Err(TxScriptError::OpcodeReserved(format!("{self:?}")))
392
393    opcode OpElse<0x67, 1>(self, vm) {
394        if let Some(cond) = vm.cond_stack.last_mut() {
395            *cond = cond.negate();
396            Ok(())
397        } else {
398            Err(TxScriptError::InvalidState("condition stack empty".to_string()))
399        }
400    }
401
402    opcode OpEndIf<0x68, 1>(self, vm) {
403        match vm.cond_stack.pop() {
404            None => Err(TxScriptError::InvalidState("condition stack empty".to_string())),
405            _ => Ok(())
406        }
407    }
408
409    opcode OpVerify<0x69, 1>(self, vm) {
410        let [result]: [bool; 1] = vm.dstack.pop_items()?;
411        match result {
412            true => Ok(()),
413            false => Err(TxScriptError::VerifyError)
414        }
415    }
416
417    opcode OpReturn<0x6a, 1>(self, vm) Err(TxScriptError::EarlyReturn)
418
419    // Stack opcodes.
420    opcode OpToAltStack<0x6b, 1>(self, vm) {
421        let [item] = vm.dstack.pop_raw()?;
422        // Pure move between stacks, so we don't consume script units.
423        vm.astack.push_unmetered(item)
424    }
425
426    opcode OpFromAltStack<0x6c, 1>(self, vm) {
427        let last = vm.astack.pop()?;
428        // Pure move between stacks, so we don't consume script units.
429        vm.dstack.push_unmetered(last)
430    }
431
432    opcode Op2Drop<0x6d, 1>(self, vm) vm.dstack.drop_items::<2>()
433    opcode Op2Dup<0x6e, 1>(self, vm) vm.dstack.dup_items::<2>()
434    opcode Op3Dup<0x6f, 1>(self, vm) vm.dstack.dup_items::<3>()
435    opcode Op2Over<0x70, 1>(self, vm) vm.dstack.over_items::<2>()
436    opcode Op2Rot<0x71, 1>(self, vm) vm.dstack.rot_items::<2>()
437    opcode Op2Swap<0x72, 1>(self, vm) vm.dstack.swap_items::<2>()
438
439    opcode OpIfDup<0x73, 1>(self, vm) {
440        let [result] = vm.dstack.peek_raw()?;
441        if <StackEntry as OpcodeData<bool>>::deserialize(&result)? {
442            vm.dstack.push(result)?;
443        }
444        Ok(())
445    }
446
447    opcode OpDepth<0x74, 1>(self, vm) push_number(vm.dstack.len() as i64, vm)
448
449    opcode OpDrop<0x75, 1>(self, vm) vm.dstack.drop_items::<1>()
450    opcode OpDup<0x76, 1>(self, vm) vm.dstack.dup_items::<1>()
451
452    opcode OpNip<0x77, 1>(self, vm) {
453        match vm.dstack.len() >= 2 {
454            true => {
455                vm.dstack.remove(vm.dstack.len()-2);
456                Ok(())
457            }
458            false => Err(TxScriptError::InvalidStackOperation(2, vm.dstack.len())),
459        }
460    }
461
462    opcode OpOver<0x78, 1>(self, vm) vm.dstack.over_items::<1>()
463
464    opcode OpPick<0x79, 1>(self, vm) {
465        let [loc]: [i32; 1] = vm.dstack.pop_items()?;
466        if  loc < 0 || loc as usize >= vm.dstack.len() {
467            return Err(TxScriptError::InvalidState("pick at an invalid location".to_string()));
468        }
469        vm.dstack.push(vm.dstack[vm.dstack.len()-(loc as usize)-1].clone())
470    }
471
472    opcode OpRoll<0x7a, 1>(self, vm) {
473        let [loc]: [i32; 1] = vm.dstack.pop_items()?;
474        if  loc < 0 || loc as usize >= vm.dstack.len() {
475            return Err(TxScriptError::InvalidState("roll at an invalid location".to_string()));
476        }
477        vm.dstack.roll(loc as usize)
478    }
479
480    opcode OpRot<0x7b, 1>(self, vm) vm.dstack.rot_items::<1>()
481    opcode OpSwap<0x7c, 1>(self, vm) vm.dstack.swap_items::<1>()
482
483    opcode OpTuck<0x7d, 1>(self, vm) {
484        match vm.dstack.len() >= 2 {
485            true => {
486                vm.dstack.insert(vm.dstack.len()-2, vm.dstack.last().expect("We have at least two items").clone())?;
487                Ok(())
488            }
489            false => Err(TxScriptError::InvalidStackOperation(2, vm.dstack.len()))
490        }
491    }
492
493    // Splice opcodes.
494    opcode OpCat<0x7e, 1>(self, vm) {
495        let b = vm.dstack.pop()?;
496        let a = vm.dstack.pop()?;
497        let mut r = a;
498        r.extend_from_slice(&b);
499        vm.dstack.push(r)
500    }
501
502    opcode OpSubstr<0x7f, 1>(self, vm) {
503        let [start, end] = vm.dstack.pop_items()?;
504        let data = vm.dstack.pop()?;
505        let [start, end] = i32s_to_usizes([start, end])?;
506        let substr = substring(&data, start, end)?;
507        vm.dstack.push(substr.into())
508    }
509
510    opcode OpLeft<0x80, 1>(self, vm) Err(TxScriptError::OpcodeDisabled(format!("{self:?}")))
511    opcode OpRight<0x81, 1>(self, vm) Err(TxScriptError::OpcodeDisabled(format!("{self:?}")))
512
513    opcode OpSize<0x82, 1>(self, vm) {
514        match vm.dstack.last() {
515            Some(last) => {
516                vm.dstack.push_item(i64::try_from(last.len()).map_err(|e| TxScriptError::NumberTooBig(e.to_string()))?)?;
517                Ok(())
518            },
519            None => Err(TxScriptError::InvalidStackOperation(1, 0))
520        }
521    }
522
523    // Bitwise logic opcodes.
524    opcode OpInvert<0x83, 1>(self, vm) {
525        let data = vm.dstack.pop()?;
526        let r: StackEntry = data.into_iter().map(|b| !b).collect();
527        vm.dstack.push(r)
528    }
529
530    opcode OpAnd<0x84, 1>(self, vm) {
531        let b = vm.dstack.pop()?;
532        let a = vm.dstack.pop()?;
533        if a.len() != b.len() {
534            return Err(TxScriptError::InvalidState("AND operands must be of equal length".to_string()));
535        }
536        let r: StackEntry = a.into_iter().zip(b.into_iter()).map(|(a_byte, b_byte)| a_byte & b_byte).collect();
537        vm.dstack.push(r)
538    }
539
540    opcode OpOr<0x85, 1>(self, vm) {
541        let b = vm.dstack.pop()?;
542        let a = vm.dstack.pop()?;
543        if a.len() != b.len() {
544            return Err(TxScriptError::InvalidState("OR operands must be of equal length".to_string()));
545        }
546        let r: StackEntry = a.into_iter().zip(b.into_iter()).map(|(a_byte, b_byte)| a_byte | b_byte).collect();
547        vm.dstack.push(r)
548    }
549
550    opcode OpXor<0x86, 1>(self, vm) {
551        let b = vm.dstack.pop()?;
552        let a = vm.dstack.pop()?;
553        if a.len() != b.len() {
554            return Err(TxScriptError::InvalidState("XOR operands must be of equal length".to_string()));
555        }
556        let r: StackEntry = a.into_iter().zip(b.into_iter()).map(|(a_byte, b_byte)| a_byte ^ b_byte).collect();
557        vm.dstack.push(r)
558    }
559
560    opcode OpEqual<0x87, 1>(self, vm) {
561        match vm.dstack.len() >= 2 {
562            true => {
563                let pair = vm.dstack.split_off(vm.dstack.len() - 2);
564                match pair[0] == pair[1] {
565                    true => vm.dstack.push(([1]).as_slice().into()),
566                    false => vm.dstack.push(([]).as_slice().into()),
567                }
568            }
569            false => Err(TxScriptError::InvalidStackOperation(2, vm.dstack.len()))
570        }
571    }
572
573    opcode OpEqualVerify<0x88, 1>(self, vm) {
574        match vm.dstack.len() >= 2 {
575            true => {
576                let pair = vm.dstack.split_off(vm.dstack.len() - 2);
577                match pair[0] == pair[1] {
578                    true => Ok(()),
579                    false => Err(TxScriptError::VerifyError),
580                }
581            }
582            false => Err(TxScriptError::InvalidStackOperation(2, vm.dstack.len()))
583        }
584    }
585
586    opcode OpReserved1<0x89, 1>(self, vm) Err(TxScriptError::OpcodeReserved(format!("{self:?}")))
587    opcode OpReserved2<0x8a, 1>(self, vm) Err(TxScriptError::OpcodeReserved(format!("{self:?}")))
588
589    // Numeric related opcodes.
590    opcode Op1Add<0x8b, 1>(self, vm) {
591        let [value]: [i64; 1] = vm.dstack.pop_items()?;
592        let r = value.checked_add(1).ok_or_else(|| TxScriptError::NumberTooBig("Result of addition exceeds 64-bit signed integer range".to_string()))?;
593        vm.dstack.push_item(r)?;
594        Ok(())
595    }
596
597    opcode Op1Sub<0x8c, 1>(self, vm) {
598        let [value]: [i64; 1] = vm.dstack.pop_items()?;
599        let r = value.checked_sub(1).ok_or_else(|| TxScriptError::NumberTooBig("Result of subtraction exceeds 64-bit signed integer range".to_string()))?;
600        vm.dstack.push_item(r)?;
601        Ok(())
602    }
603
604    opcode Op2Mul<0x8d, 1>(self, vm) Err(TxScriptError::OpcodeDisabled(format!("{self:?}")))
605    opcode Op2Div<0x8e, 1>(self, vm) Err(TxScriptError::OpcodeDisabled(format!("{self:?}")))
606
607    opcode OpNegate<0x8f, 1>(self, vm) {
608        let [value]: [i64; 1] = vm.dstack.pop_items()?;
609        let r = value.checked_neg().ok_or_else(|| TxScriptError::NumberTooBig("Negation result exceeds 64-bit signed integer range".to_string()))?;
610        vm.dstack.push_item(r)?;
611        Ok(())
612    }
613
614    opcode OpAbs<0x90, 1>(self, vm) {
615        let [ value ]: [i64; 1] = vm.dstack.pop_items()?;
616        let r = value.checked_abs().ok_or_else(|| TxScriptError::NumberTooBig("Absolute value exceeds 64-bit signed integer range".to_string()))?;
617        vm.dstack.push_item(r)?;
618        Ok(())
619    }
620
621    opcode OpNot<0x91, 1>(self, vm) {
622        let [ m ]: [i64; 1] = vm.dstack.pop_items()?;
623        let r = (m == 0) as i64;
624        vm.dstack.push_item(r)?;
625        Ok(())
626    }
627
628    opcode Op0NotEqual<0x92, 1>(self, vm) {
629        let [ m ]: [i64; 1] = vm.dstack.pop_items()?;
630        let r = (m != 0) as i64;
631        vm.dstack.push_item(r)?;
632        Ok(())
633    }
634
635    opcode OpAdd<0x93, 1>(self, vm) {
636        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
637        let r = a.checked_add(b).ok_or_else(|| TxScriptError::NumberTooBig("Sum exceeds 64-bit signed integer range".to_string()))?;
638        vm.dstack.push_item(r)?;
639        Ok(())
640    }
641
642    opcode OpSub<0x94, 1>(self, vm) {
643        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
644        let r = a.checked_sub(b).ok_or_else(|| TxScriptError::NumberTooBig("Difference exceeds 64-bit signed integer range".to_string()))?;
645        vm.dstack.push_item(r)?;
646        Ok(())
647    }
648
649    opcode OpMul<0x95, 1>(self, vm) {
650        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
651        let r = a.checked_mul(b).ok_or_else(|| TxScriptError::NumberTooBig("Product exceeds 64-bit signed integer range".to_string()))?;
652        vm.dstack.push_item(r)?;
653        Ok(())
654    }
655
656    opcode OpDiv<0x96, 1>(self, vm) {
657        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
658        let r = a.checked_div(b).ok_or_else(|| TxScriptError::InvalidState("Quotient exceeds 64-bit signed integer range, or there was a division by zero".to_string()))?;
659        vm.dstack.push_item(r)?;
660        Ok(())
661    }
662
663    opcode OpMod<0x97, 1>(self, vm) {
664        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
665        let r = a.checked_rem(b).ok_or_else(|| TxScriptError::InvalidState("Illegal modulo by zero".to_string()))?;
666        vm.dstack.push_item(r)?;
667        Ok(())
668    }
669
670    opcode OpLShift<0x98, 1>(self, vm) Err(TxScriptError::OpcodeDisabled(format!("{self:?}")))
671    opcode OpRShift<0x99, 1>(self, vm) Err(TxScriptError::OpcodeDisabled(format!("{self:?}")))
672
673    opcode OpBoolAnd<0x9a, 1>(self, vm) {
674        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
675        let r = ((a != 0) && (b != 0)) as i64;
676        vm.dstack.push_item(r)?;
677        Ok(())
678    }
679
680    opcode OpBoolOr<0x9b, 1>(self, vm) {
681        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
682        let r = ((a != 0) || (b != 0)) as i64;
683        vm.dstack.push_item(r)?;
684        Ok(())
685    }
686
687    opcode OpNumEqual<0x9c, 1>(self, vm) {
688        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
689        let r = (a == b) as i64;
690        vm.dstack.push_item(r)?;
691        Ok(())
692    }
693
694    opcode OpNumEqualVerify<0x9d, 1>(self, vm) {
695        let [a,b]: [i64; 2] = vm.dstack.pop_items()?;
696        match a == b {
697            true => Ok(()),
698            false => Err(TxScriptError::VerifyError)
699        }
700    }
701
702    opcode OpNumNotEqual<0x9e, 1>(self, vm) {
703        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
704        let r = (a != b) as i64;
705        vm.dstack.push_item(r)?;
706        Ok(())
707    }
708
709    opcode OpLessThan<0x9f, 1>(self, vm) {
710        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
711        let r = (a < b) as i64;
712        vm.dstack.push_item(r)?;
713        Ok(())
714    }
715
716    opcode OpGreaterThan<0xa0, 1>(self, vm) {
717        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
718        let r = (a > b) as i64;
719        vm.dstack.push_item(r)?;
720        Ok(())
721    }
722
723    opcode OpLessThanOrEqual<0xa1, 1>(self, vm) {
724        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
725        let r = (a <= b) as i64;
726        vm.dstack.push_item(r)?;
727        Ok(())
728    }
729
730    opcode OpGreaterThanOrEqual<0xa2, 1>(self, vm) {
731        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
732        let r = (a >= b) as i64;
733        vm.dstack.push_item(r)?;
734        Ok(())
735    }
736
737    opcode OpMin<0xa3, 1>(self, vm) {
738        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
739        let r = a.min(b);
740        vm.dstack.push_item(r)?;
741        Ok(())
742    }
743
744    opcode OpMax<0xa4, 1>(self, vm) {
745        let [ a, b ]: [i64; 2] = vm.dstack.pop_items()?;
746        let r = a.max(b);
747        vm.dstack.push_item(r)?;
748        Ok(())
749    }
750
751    opcode OpWithin<0xa5, 1>(self, vm) {
752        let [ x, l, u ]: [i64; 3] = vm.dstack.pop_items()?;
753        let r = (x >= l && x < u) as i64;
754        vm.dstack.push_item(r)?;
755        Ok(())
756    }
757
758    // ZK precompile opcodes.
759    opcode OpZkPrecompile<0xa6, 1>(self, vm) {
760        // Parse the ZK Precompile tag
761        let tag = parse_tag(&mut vm.dstack)?;
762
763        // Consume the tag cost
764        vm.consume_script_units(tag.cost())?;
765
766        // Verify the ZK proof
767        verify_zk(tag, &mut vm.dstack, &mut vm.runtime_resource_meter)?;
768
769        // If no errors, push true to the stack
770        vm.dstack.push_item(true)?;
771        Ok(())
772    }
773
774    // Crypto opcodes.
775    opcode OpBlake2bWithKey<0xa7, 1>(self, vm) {
776        let [data, key] = vm.dstack.pop_raw()?;
777        if key.len() > blake2b_simd::KEYBYTES {
778            return Err(TxScriptError::ElementTooBig(key.len(), blake2b_simd::KEYBYTES))
779        }
780        vm.consume_script_units(HashOpcodePricing::Blake2b.script_units_for_data(data.len()))?;
781        let hash = Params::new().hash_length(32).key(&key).to_state().update(&data).finalize();
782        vm.dstack.push(hash.as_bytes().into())
783    }
784
785    opcode OpSHA256<0xa8, 1>(self, vm) {
786        let [last] = vm.dstack.pop_raw()?;
787        vm.consume_script_units(HashOpcodePricing::Sha256.script_units_for_data(last.len()))?;
788        let mut hasher = Sha256::new();
789        hasher.update(last);
790        vm.dstack.push(hasher.finalize().as_slice().into())
791    }
792
793    opcode OpCheckMultiSigECDSA<0xa9, 1>(self, vm) {
794        vm.op_check_multisig_schnorr_or_ecdsa(true)
795    }
796
797    opcode OpBlake2b<0xaa, 1>(self, vm) {
798        let [last] = vm.dstack.pop_raw()?;
799        vm.consume_script_units(HashOpcodePricing::Blake2b.script_units_for_data(last.len()))?;
800        let hash = Params::new().hash_length(32).to_state().update(&last).finalize();
801        vm.dstack.push(hash.as_bytes().into())
802    }
803
804    opcode OpCheckSigECDSA<0xab, 1>(self, vm) {
805        let [mut sig, key] = vm.dstack.pop_raw()?;
806        // Hash type
807        match sig.pop() {
808            Some(typ) => {
809                let hash_type = SigHashType::from_u8(typ).map_err(|e| TxScriptError::InvalidSigHashType(typ))?;
810                match vm.check_ecdsa_signature(hash_type, key.as_slice(), sig.as_slice()) {
811                    Ok(valid) => {
812                        vm.dstack.push_item(valid)?;
813                        Ok(())
814                    },
815                    Err(e) => {
816                        Err(e)
817                    }
818                }
819            }
820            None => {
821                vm.dstack.push_item(false)?;
822                Ok(())
823            }
824        }
825    }
826
827    opcode OpCheckSig<0xac, 1>(self, vm) {
828        let [mut sig, key] = vm.dstack.pop_raw()?;
829        // Hash type
830        match sig.pop() {
831            Some(typ) => {
832                let hash_type = SigHashType::from_u8(typ).map_err(|e| TxScriptError::InvalidSigHashType(typ))?;
833                match vm.check_schnorr_signature(hash_type, key.as_slice(), sig.as_slice()) {
834                    Ok(valid) => {
835                        vm.dstack.push_item(valid)?;
836                        Ok(())
837                    },
838                    Err(e) => {
839                        Err(e)
840                    }
841                }
842            }
843            None => {
844                vm.dstack.push_item(false)?;
845                Ok(())
846            }
847        }
848    }
849
850    opcode OpCheckSigVerify<0xad, 1>(self, vm) {
851        // TODO: when changing impl to array based, change this too
852        OpCheckSig{data: self.data.clone()}.execute(vm)?;
853        let [valid]: [bool; 1] = vm.dstack.pop_items()?;
854        match valid {
855            true => Ok(()),
856            false => Err(TxScriptError::VerifyError)
857        }
858    }
859
860    opcode OpCheckMultiSig<0xae, 1>(self, vm) {
861        vm.op_check_multisig_schnorr_or_ecdsa(false)
862    }
863
864    opcode OpCheckMultiSigVerify<0xaf, 1>(self, vm) {
865        // TODO: when changing impl to array based, change this too
866        OpCheckMultiSig{data: self.data.clone()}.execute(vm)?;
867        let [valid]: [bool; 1] = vm.dstack.pop_items()?;
868        match valid {
869            true => Ok(()),
870            false => Err(TxScriptError::VerifyError)
871        }
872    }
873
874    opcode OpCheckLockTimeVerify<0xb0, 1>(self, vm) {
875        match vm.script_source {
876            ScriptSource::TxInput {input, tx, ..} => {
877                let [mut lock_time_bytes] = vm.dstack.pop_raw()?;
878
879                // Make sure lockTimeBytes is exactly 8 bytes.
880                // If more - return ErrNumberTooBig
881                // If less - pad with 0's
882                if lock_time_bytes.len() > 8 {
883                    return Err(TxScriptError::NumberTooBig(format!("lockTime value represented as {lock_time_bytes:x?} is longer then 8 bytes")))
884                }
885                lock_time_bytes.resize(8, 0);
886                let stack_lock_time = u64::from_le_bytes(lock_time_bytes.as_slice().try_into().expect("checked vector size"));
887
888                // The lock time field of a transaction is either a DAA score at
889                // which the transaction is finalized or a timestamp depending on if the
890                // value is before the constants.LockTimeThreshold. When it is under the
891                // threshold it is a DAA score.
892                if !(
893                    (tx.tx().lock_time < LOCK_TIME_THRESHOLD && stack_lock_time < LOCK_TIME_THRESHOLD) ||
894                    (tx.tx().lock_time >= LOCK_TIME_THRESHOLD && stack_lock_time >= LOCK_TIME_THRESHOLD)
895                ){
896                    return Err(TxScriptError::UnsatisfiedLockTime(format!("mismatched locktime types -- tx locktime {}, stack locktime {}", tx.tx().lock_time, stack_lock_time)))
897                }
898
899                if stack_lock_time > tx.tx().lock_time {
900                    return Err(TxScriptError::UnsatisfiedLockTime(format!("locktime requirement not satisfied -- locktime is greater than the transaction locktime: {} > {}", stack_lock_time, tx.tx().lock_time)))
901                }
902
903                // The lock time feature can also be disabled, thereby bypassing
904                // OP_CHECKLOCKTIMEVERIFY, if every transaction input has been finalized by
905                // setting its sequence to the maximum value (constants.MaxTxInSequenceNum). This
906                // condition would result in the transaction being allowed into the blockDAG
907                // making the opcode ineffective.
908                //
909                // This condition is prevented by enforcing that the input being used by
910                // the opcode is unlocked (its sequence number is less than the max
911                // value). This is sufficient to prove correctness without having to
912                // check every input.
913                //
914                // NOTE: This implies that even if the transaction is not finalized due to
915                // another input being unlocked, the opcode execution will still fail when the
916                // input being used by the opcode is locked.
917                if input.sequence == MAX_TX_IN_SEQUENCE_NUM {
918                    return Err(TxScriptError::UnsatisfiedLockTime("transaction input is finalized".to_string()));
919                }
920                Ok(())
921            }
922            _ => Err(TxScriptError::InvalidSource("LockTimeVerify only applies to transaction inputs".to_string()))
923        }
924    }
925
926    opcode OpCheckSequenceVerify<0xb1, 1>(self, vm) {
927        match vm.script_source {
928            ScriptSource::TxInput {input, tx, ..} => {
929                let [mut sequence_bytes] = vm.dstack.pop_raw()?;
930
931                // Make sure sequenceBytes is exactly 8 bytes.
932                // If more - return ErrNumberTooBig
933                // If less - pad with 0's
934                if sequence_bytes.len() > 8 {
935                    return Err(TxScriptError::NumberTooBig(format!("lockTime value represented as {sequence_bytes:x?} is longer then 8 bytes")))
936                }
937                // Don't use makeScriptNum here, since sequence is not an actual number, minimal encoding rules don't apply to it,
938                // and is more convenient to be represented as an unsigned int.
939                sequence_bytes.resize(8, 0);
940                let stack_sequence = u64::from_le_bytes(sequence_bytes.as_slice().try_into().expect("ensured size checks"));
941
942                // To provide for future soft-fork extensibility, if the
943                // operand has the disabled lock-time flag set,
944                // CHECKSEQUENCEVERIFY behaves as a NOP.
945                if stack_sequence & SEQUENCE_LOCK_TIME_DISABLED != 0 {
946                    return Ok(());
947                }
948
949                // Sequence numbers with their most significant bit set are not
950                // consensus constrained. Testing that the transaction's sequence
951                // number does not have this bit set prevents using this property
952                // to get around a CHECKSEQUENCEVERIFY check.
953                if input.sequence & SEQUENCE_LOCK_TIME_DISABLED != 0 {
954                    return Err(TxScriptError::UnsatisfiedLockTime(format!("transaction sequence has sequence locktime disabled bit set: {:#x}", input.sequence)));
955                }
956
957                // Mask off non-consensus bits before doing comparisons.
958                if (stack_sequence & SEQUENCE_LOCK_TIME_MASK) > (input.sequence & SEQUENCE_LOCK_TIME_MASK) {
959                    return Err(TxScriptError::UnsatisfiedLockTime(format!("locktime requirement not satisfied -- locktime is greater than the transaction locktime: {} > {}", stack_sequence & SEQUENCE_LOCK_TIME_MASK, input.sequence & SEQUENCE_LOCK_TIME_MASK)))
960                }
961                Ok(())
962            }
963            _ => Err(TxScriptError::InvalidSource("LockTimeVerify only applies to transaction inputs".to_string()))
964        }
965    }
966
967    // Introspection opcodes
968    // Transaction level opcodes (following Transaction struct field order)
969    opcode OpTxVersion<0xb2, 1>(self, vm) {
970        match vm.script_source {
971            ScriptSource::TxInput{tx, ..} => {
972                push_number(tx.tx().version as i64, vm)
973            },
974            _ => Err(TxScriptError::InvalidSource("OpTxVersion only applies to transaction inputs".to_string()))
975        }
976
977
978    }
979    opcode OpTxInputCount<0xb3, 1>(self, vm) {
980        match vm.script_source {
981            ScriptSource::TxInput{tx, ..} => {
982                push_number(tx.inputs().len() as i64, vm)
983            },
984            _ => Err(TxScriptError::InvalidSource("OpInputCount only applies to transaction inputs".to_string()))
985        }
986    }
987    opcode OpTxOutputCount<0xb4, 1>(self, vm) {
988        match vm.script_source {
989            ScriptSource::TxInput{tx, ..} => {
990                push_number(tx.outputs().len() as i64, vm)
991            },
992            _ => Err(TxScriptError::InvalidSource("OpOutputCount only applies to transaction inputs".to_string()))
993        }
994    }
995    opcode OpTxLockTime<0xb5, 1>(self, vm) {
996        match vm.script_source {
997            ScriptSource::TxInput{tx, ..} => {
998                push_number(tx.tx().lock_time as i64, vm)
999            },
1000            _ => Err(TxScriptError::InvalidSource("OpTxLockTime only applies to transaction inputs".to_string()))
1001        }
1002
1003
1004    }
1005    opcode OpTxSubnetId<0xb6, 1>(self, vm) {
1006        match vm.script_source {
1007            ScriptSource::TxInput{tx, ..} => {
1008                push_data(tx.tx().subnetwork_id.into(), vm)
1009            },
1010            _ => Err(TxScriptError::InvalidSource("OpTxSubnetId only applies to transaction inputs".to_string()))
1011        }
1012
1013
1014    }
1015    opcode OpTxGas<0xb7, 1>(self, vm) {
1016        match vm.script_source {
1017            ScriptSource::TxInput{tx, ..} => {
1018                push_number(tx.tx().gas as i64, vm)
1019            },
1020            _ => Err(TxScriptError::InvalidSource("OpTxGas only applies to transaction inputs".to_string()))
1021        }
1022
1023
1024    }
1025    opcode OpTxPayloadSubstr<0xb8, 1>(self, vm) {
1026        match vm.script_source {
1027            ScriptSource::TxInput{tx, ..} => {
1028                let [start, end] = i32s_to_usizes(vm.dstack.pop_items()?)?;
1029                let substr = substring(&tx.tx().payload, start, end)?;
1030                push_data(substr, vm)
1031            },
1032            _ => Err(TxScriptError::InvalidSource("OpTxPayloadSubstr only applies to transaction inputs".to_string()))
1033        }
1034
1035
1036    }
1037    // Input related opcodes (following TransactionInput struct field order)
1038    opcode OpTxInputIndex<0xb9, 1>(self, vm) {
1039        match vm.script_source {
1040            ScriptSource::TxInput{idx, ..} => {
1041                push_number(idx as i64, vm)
1042            },
1043            _ => Err(TxScriptError::InvalidSource("OpInputIndex only applies to transaction inputs".to_string()))
1044        }
1045    }
1046    opcode OpOutpointTxId<0xba, 1>(self, vm) {
1047        match vm.script_source {
1048            ScriptSource::TxInput{tx, ..} => {
1049                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1050                let idx = i32_to_usize(idx)?;
1051                let input = tx.inputs().get(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1052                push_data(input.previous_outpoint.transaction_id.as_bytes().into(), vm)
1053            },
1054            _ => Err(TxScriptError::InvalidSource("OpOutpointTxId only applies to transaction inputs".to_string()))
1055        }
1056
1057
1058    }
1059    opcode OpOutpointIndex<0xbb, 1>(self, vm) {
1060        match vm.script_source {
1061            ScriptSource::TxInput{tx, ..} => {
1062                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1063                let idx = i32_to_usize(idx)?;
1064                let input = tx.inputs().get(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1065                push_number(input.previous_outpoint.index as i64, vm)
1066            },
1067            _ => Err(TxScriptError::InvalidSource("OpOutpointIndex only applies to transaction inputs".to_string()))
1068        }
1069
1070
1071    }
1072    opcode OpTxInputScriptSigSubstr<0xbc, 1>(self, vm) {
1073        match vm.script_source {
1074            ScriptSource::TxInput{tx, ..} => {
1075                let [idx, start, end] = i32s_to_usizes(vm.dstack.pop_items()?)?;
1076                let input = tx.inputs().get(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1077                let substr = substring(&input.signature_script, start, end)?;
1078                push_data(substr, vm)
1079            },
1080            _ => Err(TxScriptError::InvalidSource("OpTxInputScriptSigSubstr only applies to transaction inputs".to_string()))
1081        }
1082
1083
1084    }
1085    opcode OpTxInputSeq<0xbd, 1>(self, vm) {
1086        match vm.script_source {
1087            ScriptSource::TxInput{tx, ..} => {
1088                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1089                let idx = i32_to_usize(idx)?;
1090                let input = tx.inputs().get(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1091                // sequence is used as a bitflag field, so push as raw bytes (minimal number encoding doesn't apply). See CheckSequenceVerify for more details.
1092                push_data(input.sequence.to_le_bytes().into(), vm)
1093            },
1094            _ => Err(TxScriptError::InvalidSource("OpTxInputSeq only applies to transaction inputs".to_string()))
1095        }
1096
1097
1098    }
1099    // UTXO related opcodes (following UtxoEntry struct field order)
1100    opcode OpTxInputAmount<0xbe, 1>(self, vm) {
1101        match vm.script_source {
1102            ScriptSource::TxInput{tx, ..} => {
1103                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1104                let utxo = usize::try_from(idx).ok()
1105                    .and_then(|idx| tx.utxo(idx))
1106                    .ok_or_else(|| TxScriptError::InvalidInputIndex(idx, tx.inputs().len()))?;
1107                push_number(utxo.amount.try_into().map_err(|e: TryFromIntError| TxScriptError::NumberTooBig(e.to_string()))?, vm)
1108            },
1109            _ => Err(TxScriptError::InvalidSource("OpInputAmount only applies to transaction inputs".to_string()))
1110        }
1111    }
1112    opcode OpTxInputSpk<0xbf, 1>(self, vm) {
1113        match vm.script_source {
1114            ScriptSource::TxInput{tx, ..} => {
1115                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1116                let utxo = usize::try_from(idx).ok()
1117                    .and_then(|idx| tx.utxo(idx))
1118                    .ok_or_else(|| TxScriptError::InvalidInputIndex(idx, tx.inputs().len()))?;
1119                vm.dstack.push(utxo.script_public_key.to_bytes().into())
1120            },
1121            _ => Err(TxScriptError::InvalidSource("OpInputSpk only applies to transaction inputs".to_string()))
1122        }
1123    }
1124    opcode OpTxInputDaaScore<0xc0, 1>(self, vm) {
1125        match vm.script_source {
1126            ScriptSource::TxInput{tx, ..} => {
1127                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1128                let idx = i32_to_usize(idx)?;
1129                let utxo = tx.utxo(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1130                push_number(utxo.block_daa_score as i64, vm)
1131            },
1132            _ => Err(TxScriptError::InvalidSource("OpTxInputDaaScore only applies to transaction inputs".to_string()))
1133        }
1134
1135
1136    }
1137    opcode OpTxInputIsCoinbase<0xc1, 1>(self, vm) {
1138        match vm.script_source {
1139            ScriptSource::TxInput{tx, ..} => {
1140                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1141                let idx = i32_to_usize(idx)?;
1142                let utxo = tx.utxo(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1143                push_number(utxo.is_coinbase as i64, vm)
1144            },
1145            _ => Err(TxScriptError::InvalidSource("OpTxInputIsCoinbase only applies to transaction inputs".to_string()))
1146        }
1147
1148
1149    }
1150    // Output related opcodes (following TransactionOutput struct field order)
1151    opcode OpTxOutputAmount<0xc2, 1>(self, vm) {
1152        match vm.script_source {
1153            ScriptSource::TxInput{tx, ..} => {
1154                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1155                let output = usize::try_from(idx).ok()
1156                    .and_then(|idx| tx.outputs().get(idx))
1157                    .ok_or_else(|| TxScriptError::InvalidOutputIndex(idx, tx.outputs().len()))?;
1158                push_number(output.value.try_into().map_err(|e: TryFromIntError| TxScriptError::NumberTooBig(e.to_string()))?, vm)
1159            },
1160            _ => Err(TxScriptError::InvalidSource("OpTxOutputAmount only applies to transaction inputs".to_string()))
1161        }
1162    }
1163    opcode OpTxOutputSpk<0xc3, 1>(self, vm) {
1164        match vm.script_source {
1165            ScriptSource::TxInput{tx, ..} => {
1166                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1167                let output = usize::try_from(idx).ok()
1168                    .and_then(|idx| tx.outputs().get(idx))
1169                    .ok_or_else(|| TxScriptError::InvalidOutputIndex(idx, tx.outputs().len()))?;
1170                vm.dstack.push(output.script_public_key.to_bytes().into())
1171            },
1172            _ => Err(TxScriptError::InvalidSource("OpTxOutputSpk only applies to transaction inputs".to_string()))
1173        }
1174    }
1175
1176    opcode OpTxPayloadLen<0xc4, 1>(self, vm) {
1177        match vm.script_source {
1178            ScriptSource::TxInput{tx, ..} => {
1179                push_number(tx.tx().payload.len() as i64, vm)
1180            },
1181            _ => Err(TxScriptError::InvalidSource("OpTxPayloadLen only applies to transaction inputs".to_string()))
1182        }
1183
1184
1185    }
1186
1187    opcode OpTxInputSpkLen<0xc5, 1>(self, vm) {
1188        match vm.script_source {
1189            ScriptSource::TxInput{tx, ..} => {
1190                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1191                let idx = i32_to_usize(idx)?;
1192                let utxo = tx.utxo(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1193                // TODO: Consider adding a method to ScriptPublicKey for getting length directly, instead of converting to bytes first.
1194                let len = utxo.script_public_key.to_bytes().len() as i64;
1195                push_number(len, vm)
1196            },
1197            _ => Err(TxScriptError::InvalidSource("OpTxInputSpkLen only applies to transaction inputs".to_string()))
1198        }
1199
1200
1201    }
1202
1203    opcode OpTxInputSpkSubstr<0xc6, 1>(self, vm) {
1204        match vm.script_source {
1205            ScriptSource::TxInput{tx, ..} => {
1206                let [idx, start, end] = i32s_to_usizes(vm.dstack.pop_items()?)?;
1207                let utxo = tx.utxo(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1208                let spk_bytes = utxo.script_public_key.to_bytes();
1209                let substr = substring(&spk_bytes, start, end)?;
1210                push_data(substr, vm)
1211            },
1212            _ => Err(TxScriptError::InvalidSource("OpTxInputSpkSubstr only applies to transaction inputs".to_string()))
1213        }
1214
1215
1216    }
1217
1218    opcode OpTxOutputSpkLen<0xc7, 1>(self, vm) {
1219        match vm.script_source {
1220            ScriptSource::TxInput{tx, ..} => {
1221                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1222                let idx = i32_to_usize(idx)?;
1223                let output = tx.outputs().get(idx).ok_or_else(|| TxScriptError::InvalidOutputIndex(idx as i32, tx.outputs().len()))?;
1224                // TODO: Consider adding a method to ScriptPublicKey for getting length directly, instead of converting to bytes first.
1225                let len = output.script_public_key.to_bytes().len() as i64;
1226                push_number(len, vm)
1227            },
1228            _ => Err(TxScriptError::InvalidSource("OpTxOutputSpkLen only applies to transaction inputs".to_string()))
1229        }
1230
1231
1232    }
1233
1234    opcode OpTxOutputSpkSubstr<0xc8, 1>(self, vm) {
1235        match vm.script_source {
1236            ScriptSource::TxInput{tx, ..} => {
1237                let [idx, start, end] = i32s_to_usizes(vm.dstack.pop_items()?)?;
1238                let output = tx.outputs().get(idx).ok_or_else(|| TxScriptError::InvalidOutputIndex(idx as i32, tx.outputs().len()))?;
1239                let spk_bytes = output.script_public_key.to_bytes();
1240                let substr = substring(&spk_bytes, start, end)?;
1241                push_data(substr, vm)
1242            },
1243            _ => Err(TxScriptError::InvalidSource("OpTxOutputSpkSubstr only applies to transaction inputs".to_string()))
1244        }
1245
1246
1247    }
1248
1249    opcode OpTxInputScriptSigLen<0xc9, 1>(self, vm) {
1250        match vm.script_source {
1251            ScriptSource::TxInput{tx, ..} => {
1252                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1253                let idx = i32_to_usize(idx)?;
1254                let input = tx.inputs().get(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1255                let len = input.signature_script.len() as i64;
1256                push_number(len, vm)
1257            },
1258            _ => Err(TxScriptError::InvalidSource("OpTxInputScriptSigLen only applies to transaction inputs".to_string()))
1259        }
1260
1261
1262    }
1263    opcode OpUnknown202<0xca, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1264    opcode OpAuthOutputCount<0xcb, 1>(self, vm) {
1265        match vm.script_source {
1266            ScriptSource::TxInput{tx, ..} => {
1267                let [input_idx]: [i32; 1] = vm.dstack.pop_items()?;
1268                let input_idx = i32_to_usize(input_idx)?;
1269                if input_idx >= tx.inputs().len() {
1270                    return Err(TxScriptError::InvalidInputIndex(input_idx.try_into().expect("casted above"), tx.inputs().len()));
1271                }
1272                let count = vm.covenants_ctx.num_auth_outputs(input_idx);
1273                push_number(count as i64, vm)
1274            },
1275            _ => Err(TxScriptError::InvalidSource("OpAuthOutputCount only applies to transaction inputs".to_string()))
1276        }
1277
1278
1279    }
1280
1281    opcode OpAuthOutputIdx<0xcc, 1>(self, vm) {
1282        match vm.script_source {
1283            ScriptSource::TxInput{tx, ..} => {
1284                let [input_idx, k] = i32s_to_usizes(vm.dstack.pop_items()?)?;
1285                if input_idx >= tx.inputs().len() {
1286                    return Err(TxScriptError::InvalidInputIndex(input_idx.try_into().expect("casted above"), tx.inputs().len()));
1287                }
1288                let output_idx = vm.covenants_ctx.auth_output_index(input_idx, k)?;
1289                push_number(output_idx as i64, vm)
1290            },
1291            _ => Err(TxScriptError::InvalidSource("OpAuthOutputIdx only applies to transaction inputs".to_string()))
1292        }
1293
1294
1295    }
1296
1297    opcode OpNum2Bin<0xcd, 1>(self, vm) {
1298        let [size]: [i32; 1] = vm.dstack.pop_items()?;
1299        let size = i32_to_usize(size)?;
1300        if size > 8 {
1301            return Err(TxScriptError::NotMinimalData(format!("NUM2BIN target size {size} exceeds 8 bytes")));
1302        }
1303        let [num]: [i64; 1] = vm.dstack.pop_items()?;
1304        let r = serialize_i64(num, Some(size))?;
1305        vm.dstack.push(r)
1306
1307
1308    }
1309
1310    opcode OpBin2Num<0xce, 1>(self, vm) {
1311        // pop_items deserializes the stack item to `i64`, while `push_number` pushes it back as minimally encoded bytes.
1312        let [num]: [i64; 1] = vm.dstack.pop_items()?;
1313        push_number(num, vm)
1314
1315
1316    }
1317
1318    opcode OpInputCovenantId<0xcf, 1>(self, vm) {
1319        match vm.script_source {
1320            ScriptSource::TxInput{tx, ..} => {
1321                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1322                let idx = i32_to_usize(idx)?;
1323                let utxo = tx.utxo(idx).ok_or_else(|| TxScriptError::InvalidInputIndex(idx as i32, tx.inputs().len()))?;
1324                let covenant_id = utxo.covenant_id.unwrap_or(ZERO_HASH);
1325                push_data(covenant_id.as_bytes().into(), vm)
1326            },
1327            _ => Err(TxScriptError::InvalidSource("OpInputCovenantId only applies to transaction inputs".to_string()))
1328        }
1329
1330
1331    }
1332
1333    opcode OpCovInputCount<0xd0, 1>(self, vm) {
1334        match vm.script_source {
1335            ScriptSource::TxInput{tx, ..} => {
1336                let [covenant_id]: [Hash; 1] = vm.dstack.pop_items()?;
1337                let count = vm.covenants_ctx.num_covenant_inputs(covenant_id);
1338                push_number(count as i64, vm)
1339            },
1340            _ => Err(TxScriptError::InvalidSource("OpCovInputCount only applies to transaction inputs".to_string()))
1341        }
1342
1343
1344    }
1345
1346    opcode OpCovInputIdx<0xd1, 1>(self, vm) {
1347        match vm.script_source {
1348            ScriptSource::TxInput{tx, ..} => {
1349                let [k]: [i32; 1] = vm.dstack.pop_items()?;
1350                let k = i32_to_usize(k)?;
1351                let [covenant_id]: [Hash; 1] = vm.dstack.pop_items()?;
1352                let idx = vm.covenants_ctx.covenant_input_index(covenant_id, k)?;
1353                push_number(idx as i64, vm)
1354            },
1355            _ => Err(TxScriptError::InvalidSource("OpCovInputIdx only applies to transaction inputs".to_string()))
1356        }
1357
1358
1359    }
1360
1361    opcode OpCovOutputCount<0xd2, 1>(self, vm) {
1362        match vm.script_source {
1363            ScriptSource::TxInput{tx, ..} => {
1364                let [covenant_id]: [Hash; 1] = vm.dstack.pop_items()?;
1365                let count = vm.covenants_ctx.num_covenant_outputs(covenant_id);
1366                push_number(count as i64, vm)
1367            },
1368            _ => Err(TxScriptError::InvalidSource("OpCovOutputCount only applies to transaction inputs".to_string()))
1369        }
1370
1371
1372    }
1373
1374    opcode OpCovOutputIdx<0xd3, 1>(self, vm) {
1375        match vm.script_source {
1376            ScriptSource::TxInput{tx, ..} => {
1377                let [k]: [i32; 1] = vm.dstack.pop_items()?;
1378                let k = i32_to_usize(k)?;
1379                let [covenant_id]: [Hash; 1] = vm.dstack.pop_items()?;
1380                let idx = vm.covenants_ctx.covenant_output_index(covenant_id, k)?;
1381                push_number(idx as i64, vm)
1382            },
1383            _ => Err(TxScriptError::InvalidSource("OpCovOutputIdx only applies to transaction inputs".to_string()))
1384        }
1385
1386
1387    }
1388
1389    opcode OpChainblockSeqCommit<0xd4, 1>(self, vm) {
1390        let Some(seq_commit_accessor) = vm.ctx.seq_commit_accessor else {
1391            // seq_commit_access is none only if the opcode is not enabled
1392            return Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1393        };
1394        let [block]: [Hash; 1] = vm.dstack.pop_items()?; // TODO: We actually could convert slice ref into hash ref if it was repr(transparent)
1395        match seq_commit_accessor.is_chain_ancestor_from_pov(block) {
1396            None => return Err(TxScriptError::BlockAlreadyPruned(block.to_string())),
1397            Some(false) => return Err(TxScriptError::BlockNotSelected(block.to_string())),
1398            Some(true) => {}
1399        };
1400        let commitment = seq_commit_accessor.seq_commitment_within_depth(block)
1401            .ok_or_else(|| TxScriptError::BlockIsTooDeep(block.to_string()))?;
1402        vm.dstack.push_item(commitment)?;
1403        Ok(())
1404    }
1405
1406    opcode OpOutputCovenantId<0xd5, 1>(self, vm) {
1407        match vm.script_source {
1408            ScriptSource::TxInput{tx, ..} => {
1409                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1410                let idx = i32_to_usize(idx)?;
1411                let output = tx.outputs().get(idx).ok_or_else(|| TxScriptError::InvalidOutputIndex(idx as i32, tx.outputs().len()))?;
1412                let covenant_id = output.covenant.map(|c| c.covenant_id).unwrap_or(ZERO_HASH);
1413                push_data(covenant_id.as_bytes().into(), vm)
1414            },
1415            _ => Err(TxScriptError::InvalidSource("OpOutputCovenantId only applies to transaction inputs".to_string()))
1416        }
1417
1418
1419    }
1420
1421    opcode OpOutputAuthorizingInput<0xd6, 1>(self, vm) {
1422        match vm.script_source {
1423            ScriptSource::TxInput{tx, ..} => {
1424                let [idx]: [i32; 1] = vm.dstack.pop_items()?;
1425                let idx = i32_to_usize(idx)?;
1426                let output = tx.outputs().get(idx).ok_or_else(|| TxScriptError::InvalidOutputIndex(idx as i32, tx.outputs().len()))?;
1427                let auth_input_idx = output.covenant.as_ref()
1428                    .map(|c| c.authorizing_input as i64)
1429                    .unwrap_or(-1i64);
1430                push_number(auth_input_idx, vm)
1431            },
1432            _ => Err(TxScriptError::InvalidSource("OpOutputAuthorizingInput only applies to transaction inputs".to_string()))
1433        }
1434
1435
1436    }
1437
1438    opcode OpCheckSigFromStack<0xd7, 1>(self, vm) {
1439        let [signature, msg_hash, pubkey] = vm.dstack.pop_raw()?;
1440        let msg_hash = Hash::try_from(msg_hash.as_slice()).map_err(|_| TxScriptError::InvalidState("message hash must be 32 bytes".to_string()))?;
1441        let is_valid = vm.check_schnorr_signature_with_msg_hash(&pubkey, &signature, |_| msg_hash)?;
1442        vm.dstack.push_item(is_valid)
1443
1444
1445    }
1446
1447    opcode OpCheckSigFromStackECDSA<0xd8, 1>(self, vm) {
1448        let [signature, msg_hash, pubkey] = vm.dstack.pop_raw()?;
1449        let msg_hash = Hash::try_from(msg_hash.as_slice()).map_err(|_| TxScriptError::InvalidState("message hash must be 32 bytes".to_string()))?;
1450        let is_valid = vm.check_ecdsa_signature_with_msg_hash(&pubkey, &signature, |_| msg_hash)?;
1451        vm.dstack.push_item(is_valid)
1452
1453
1454    }
1455
1456    opcode OpBlake3<0xd9, 1>(self, vm) {
1457        let [data] = vm.dstack.pop_raw()?;
1458        vm.consume_script_units(HashOpcodePricing::Blake3.script_units_for_data(data.len()))?;
1459        let hash = blake3::hash(&data);
1460        vm.dstack.push(hash.as_slice().into())
1461
1462
1463    }
1464
1465    opcode OpBlake3WithKey<0xda, 1>(self, vm) {
1466        let [data, key] = vm.dstack.pop_raw()?;
1467        let key: &[u8; blake3::KEY_LEN] = key.as_slice().try_into().map_err(|_| {
1468            TxScriptError::MalformedPush(blake3::KEY_LEN, key.len())
1469        })?;
1470        vm.consume_script_units(HashOpcodePricing::Blake3.script_units_for_data(data.len()))?;
1471        let hash = blake3::keyed_hash(key, &data);
1472        vm.dstack.push(hash.as_slice().into())
1473
1474
1475    }
1476
1477    opcode OpUnknown219<0xdb, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1478    opcode OpUnknown220<0xdc, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1479    opcode OpUnknown221<0xdd, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1480    opcode OpUnknown222<0xde, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1481    opcode OpUnknown223<0xdf, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1482    opcode OpUnknown224<0xe0, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1483    opcode OpUnknown225<0xe1, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1484    opcode OpUnknown226<0xe2, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1485    opcode OpUnknown227<0xe3, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1486    opcode OpUnknown228<0xe4, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1487    opcode OpUnknown229<0xe5, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1488    opcode OpUnknown230<0xe6, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1489    opcode OpUnknown231<0xe7, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1490    opcode OpUnknown232<0xe8, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1491    opcode OpUnknown233<0xe9, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1492    opcode OpUnknown234<0xea, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1493    opcode OpUnknown235<0xeb, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1494    opcode OpUnknown236<0xec, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1495    opcode OpUnknown237<0xed, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1496    opcode OpUnknown238<0xee, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1497    opcode OpUnknown239<0xef, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1498    opcode OpUnknown240<0xf0, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1499    opcode OpUnknown241<0xf1, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1500    opcode OpUnknown242<0xf2, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1501    opcode OpUnknown243<0xf3, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1502    opcode OpUnknown244<0xf4, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1503    opcode OpUnknown245<0xf5, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1504    opcode OpUnknown246<0xf6, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1505    opcode OpUnknown247<0xf7, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1506    opcode OpUnknown248<0xf8, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1507    opcode OpUnknown249<0xf9, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1508
1509    opcode OpSmallInteger<0xfa, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1510    opcode OpPubKeys<0xfb, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1511    opcode OpUnknown252<0xfc, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1512    opcode OpPubKeyHash<0xfd, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1513    opcode OpPubKey<0xfe, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1514    opcode OpInvalidOpCode<0xff, 1>(self, vm) Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1515}
1516
1517// converts an opcode from the list of Op0 to Op16 to its associated value
1518#[allow(clippy::borrowed_box)]
1519pub fn to_small_int<T: VerifiableTransaction, Reused: SigHashReusedValues>(opcode: &Box<dyn OpCodeImplementation<T, Reused>>) -> u8 {
1520    let value = opcode.value();
1521    if value == codes::OpFalse {
1522        return 0;
1523    }
1524
1525    assert!((codes::OpTrue..codes::Op16).contains(&value), "expected op codes between from the list of Op0 to Op16");
1526    value - (codes::OpTrue - 1)
1527}
1528
1529#[cfg(test)]
1530mod test {
1531    use crate::caches::Cache;
1532    use crate::data_stack::{Stack, StackEntry};
1533    use crate::opcodes::{OpCodeExecution, OpCodeImplementation};
1534    use crate::{
1535        EngineContext, EngineFlags, LOCK_TIME_THRESHOLD, TxScriptEngine, TxScriptError, opcodes, pay_to_address_script, script_to_str,
1536    };
1537    use kaspa_addresses::{Address, Prefix, Version};
1538    use kaspa_consensus_core::constants::{SOMPI_PER_KASPA, TX_VERSION};
1539    use kaspa_consensus_core::hashing::sighash::SigHashReusedValuesUnsync;
1540    use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE;
1541    use kaspa_consensus_core::tx::{
1542        PopulatedTransaction, ScriptPublicKey, Transaction, TransactionInput, TransactionOutpoint, TransactionOutput, UtxoEntry,
1543        VerifiableTransaction,
1544    };
1545
1546    struct TestCase<'a> {
1547        init: Vec<Vec<u8>>,
1548        code: Box<dyn OpCodeImplementation<PopulatedTransaction<'a>, SigHashReusedValuesUnsync>>,
1549        dstack: Vec<Vec<u8>>,
1550    }
1551
1552    struct ErrorTestCase<'a> {
1553        init: Vec<Vec<u8>>,
1554        code: Box<dyn OpCodeImplementation<PopulatedTransaction<'a>, SigHashReusedValuesUnsync>>,
1555        error: TxScriptError,
1556    }
1557
1558    fn run_success_test_cases(tests: Vec<TestCase>) {
1559        run_success_test_cases_with_flags(tests, Default::default());
1560    }
1561
1562    fn run_success_test_cases_with_flags(tests: Vec<TestCase>, flags: EngineFlags) {
1563        let cache = Cache::new(10_000);
1564        let reused_values = SigHashReusedValuesUnsync::new();
1565        let ctx = EngineContext::new(&cache).with_reused(&reused_values);
1566        for TestCase { init, code, dstack } in tests {
1567            let init: Stack = init.into();
1568            let dstack = dstack.into();
1569            let mut vm = TxScriptEngine::new(ctx, flags);
1570            vm.dstack = init.clone();
1571            code.execute(&mut vm).unwrap_or_else(|_| panic!("Opcode {} should not fail", code.value()));
1572            assert_eq!(vm.dstack, dstack, "OpCode {} Pushed wrong value", code.value());
1573        }
1574    }
1575
1576    fn run_error_test_cases(tests: Vec<ErrorTestCase>) {
1577        run_error_test_cases_with_flags(tests, Default::default());
1578    }
1579
1580    fn run_error_test_cases_with_flags(tests: Vec<ErrorTestCase>, flags: EngineFlags) {
1581        let cache = Cache::new(10_000);
1582        let reused_values = SigHashReusedValuesUnsync::new();
1583        let ctx = EngineContext::new(&cache).with_reused(&reused_values);
1584        for ErrorTestCase { init, code, error } in tests {
1585            let mut vm = TxScriptEngine::new(ctx, flags);
1586            vm.dstack = init.clone().into();
1587            assert_eq!(
1588                code.execute(&mut vm)
1589                    .expect_err(format!("Opcode {} should have errored (init: {:?})", code.value(), init.clone()).as_str()),
1590                error,
1591                "Opcode {} returned wrong error {:?}",
1592                code.value(),
1593                init
1594            );
1595        }
1596    }
1597
1598    #[test]
1599    fn test_opcode_disabled() {
1600        let tests: Vec<Box<dyn OpCodeImplementation<PopulatedTransaction, SigHashReusedValuesUnsync>>> = vec![
1601            opcodes::OpLeft::empty().expect("Should accept empty"),
1602            opcodes::OpRight::empty().expect("Should accept empty"),
1603            opcodes::Op2Mul::empty().expect("Should accept empty"),
1604            opcodes::Op2Div::empty().expect("Should accept empty"),
1605            opcodes::OpLShift::empty().expect("Should accept empty"),
1606            opcodes::OpRShift::empty().expect("Should accept empty"),
1607        ];
1608
1609        let cache = Cache::new(10_000);
1610        let reused_values = SigHashReusedValuesUnsync::new();
1611        let ctx = EngineContext::new(&cache).with_reused(&reused_values);
1612        let mut vm = TxScriptEngine::new(ctx, Default::default());
1613
1614        for pop in tests {
1615            match pop.execute(&mut vm) {
1616                Err(TxScriptError::OpcodeDisabled(_)) => {}
1617                _ => panic!("Opcode {pop:?} should be disabled"),
1618            }
1619        }
1620    }
1621
1622    #[test]
1623    fn test_opcode_reserved() {
1624        let tests: Vec<Box<dyn OpCodeImplementation<PopulatedTransaction, SigHashReusedValuesUnsync>>> = vec![
1625            opcodes::OpReserved::empty().expect("Should accept empty"),
1626            opcodes::OpVer::empty().expect("Should accept empty"),
1627            opcodes::OpVerIf::empty().expect("Should accept empty"),
1628            opcodes::OpVerNotIf::empty().expect("Should accept empty"),
1629            opcodes::OpReserved1::empty().expect("Should accept empty"),
1630            opcodes::OpReserved2::empty().expect("Should accept empty"),
1631        ];
1632
1633        let cache = Cache::new(10_000);
1634        let reused_values = SigHashReusedValuesUnsync::new();
1635        let ctx = EngineContext::new(&cache).with_reused(&reused_values);
1636        let mut vm = TxScriptEngine::new(ctx, Default::default());
1637
1638        for pop in tests {
1639            match pop.execute(&mut vm) {
1640                Err(TxScriptError::OpcodeReserved(_)) => {}
1641                _ => panic!("Opcode {pop:?} should be disabled"),
1642            }
1643        }
1644    }
1645
1646    #[test]
1647    fn test_opcode_invalid() {
1648        let tests: Vec<Box<dyn OpCodeImplementation<PopulatedTransaction, SigHashReusedValuesUnsync>>> = vec![
1649            opcodes::OpUnknown202::empty().expect("Should accept empty"),
1650            opcodes::OpUnknown219::empty().expect("Should accept empty"),
1651            opcodes::OpUnknown220::empty().expect("Should accept empty"),
1652            opcodes::OpUnknown221::empty().expect("Should accept empty"),
1653            opcodes::OpUnknown222::empty().expect("Should accept empty"),
1654            opcodes::OpUnknown223::empty().expect("Should accept empty"),
1655            opcodes::OpUnknown224::empty().expect("Should accept empty"),
1656            opcodes::OpUnknown225::empty().expect("Should accept empty"),
1657            opcodes::OpUnknown226::empty().expect("Should accept empty"),
1658            opcodes::OpUnknown227::empty().expect("Should accept empty"),
1659            opcodes::OpUnknown228::empty().expect("Should accept empty"),
1660            opcodes::OpUnknown229::empty().expect("Should accept empty"),
1661            opcodes::OpUnknown230::empty().expect("Should accept empty"),
1662            opcodes::OpUnknown231::empty().expect("Should accept empty"),
1663            opcodes::OpUnknown232::empty().expect("Should accept empty"),
1664            opcodes::OpUnknown233::empty().expect("Should accept empty"),
1665            opcodes::OpUnknown234::empty().expect("Should accept empty"),
1666            opcodes::OpUnknown235::empty().expect("Should accept empty"),
1667            opcodes::OpUnknown236::empty().expect("Should accept empty"),
1668            opcodes::OpUnknown237::empty().expect("Should accept empty"),
1669            opcodes::OpUnknown238::empty().expect("Should accept empty"),
1670            opcodes::OpUnknown239::empty().expect("Should accept empty"),
1671            opcodes::OpUnknown240::empty().expect("Should accept empty"),
1672            opcodes::OpUnknown241::empty().expect("Should accept empty"),
1673            opcodes::OpUnknown242::empty().expect("Should accept empty"),
1674            opcodes::OpUnknown243::empty().expect("Should accept empty"),
1675            opcodes::OpUnknown244::empty().expect("Should accept empty"),
1676            opcodes::OpUnknown245::empty().expect("Should accept empty"),
1677            opcodes::OpUnknown246::empty().expect("Should accept empty"),
1678            opcodes::OpUnknown247::empty().expect("Should accept empty"),
1679            opcodes::OpUnknown248::empty().expect("Should accept empty"),
1680            opcodes::OpUnknown249::empty().expect("Should accept empty"),
1681        ];
1682
1683        let cache = Cache::new(10_000);
1684        let reused_values = SigHashReusedValuesUnsync::new();
1685        let ctx = EngineContext::new(&cache).with_reused(&reused_values);
1686        let mut vm = TxScriptEngine::new(ctx, Default::default());
1687
1688        for pop in tests {
1689            match pop.execute(&mut vm) {
1690                Err(TxScriptError::InvalidOpcode(_)) => {}
1691                _ => panic!("Opcode {pop:?} should be disabled"),
1692            }
1693        }
1694    }
1695
1696    #[test]
1697    fn test_opcode_to_string_without_data() {
1698        let opcode: Box<dyn OpCodeImplementation<PopulatedTransaction, SigHashReusedValuesUnsync>> =
1699            opcodes::OpTrue::empty().expect("Should accept empty");
1700        assert_eq!(opcode.to_string(), "OpTrue");
1701    }
1702
1703    #[test]
1704    fn test_opcode_to_string_with_data() {
1705        let opcode: Box<dyn OpCodeImplementation<PopulatedTransaction, SigHashReusedValuesUnsync>> =
1706            opcodes::OpData2::new(vec![0xab, 0xcd]).expect("Should accept data");
1707        assert_eq!(opcode.to_string(), "OpData2 0xabcd");
1708    }
1709
1710    #[test]
1711    fn test_script_builder_roundtrip_to_joined_opcode_string() {
1712        use opcodes::codes::{OpBlake2b, OpCheckSig, OpDrop, OpDup, OpEqualVerify, OpVerify};
1713
1714        let mut builder = crate::script_builder::ScriptBuilder::new();
1715        builder
1716            .add_op(OpDup)
1717            .unwrap()
1718            .add_data(&[0x02, 0xab, 0xcd])
1719            .unwrap()
1720            .add_op(OpBlake2b)
1721            .unwrap()
1722            .add_data(b"kaspa")
1723            .unwrap()
1724            .add_op(OpEqualVerify)
1725            .unwrap()
1726            .add_i64(-1)
1727            .unwrap()
1728            .add_i64(16)
1729            .unwrap()
1730            .add_op(OpCheckSig)
1731            .unwrap()
1732            .add_data(&[0xde, 0xad, 0xbe, 0xef])
1733            .unwrap()
1734            .add_op(OpDrop)
1735            .unwrap()
1736            .add_op(OpVerify)
1737            .unwrap();
1738
1739        let script = builder.drain();
1740
1741        assert_eq!(
1742            script_to_str(&script).unwrap(),
1743            "OpDup OpData3 0x02abcd OpBlake2b OpData5 0x6b61737061 OpEqualVerify Op1Negate Op16 OpCheckSig OpData4 0xdeadbeef OpDrop OpVerify"
1744        );
1745    }
1746
1747    #[test]
1748    fn test_push_data() {
1749        run_success_test_cases(vec![
1750            TestCase { code: opcodes::OpFalse::empty().expect("Should accept empty"), dstack: vec![vec![]], init: Default::default() },
1751            TestCase {
1752                code: opcodes::OpData1::new([1u8; 1].to_vec()).expect("Valid opcode"),
1753                dstack: vec![[1u8; 1].to_vec()],
1754                init: Default::default(),
1755            },
1756            TestCase {
1757                code: opcodes::OpData2::new([1u8; 2].to_vec()).expect("Valid opcode"),
1758                dstack: vec![[1u8; 2].to_vec()],
1759                init: Default::default(),
1760            },
1761            TestCase {
1762                code: opcodes::OpData3::new([1u8; 3].to_vec()).expect("Valid opcode"),
1763                dstack: vec![[1u8; 3].to_vec()],
1764                init: Default::default(),
1765            },
1766            TestCase {
1767                code: opcodes::OpData4::new([1u8; 4].to_vec()).expect("Valid opcode"),
1768                dstack: vec![[1u8; 4].to_vec()],
1769                init: Default::default(),
1770            },
1771            TestCase {
1772                code: opcodes::OpData5::new([1u8; 5].to_vec()).expect("Valid opcode"),
1773                dstack: vec![[1u8; 5].to_vec()],
1774                init: Default::default(),
1775            },
1776            TestCase {
1777                code: opcodes::OpData6::new([1u8; 6].to_vec()).expect("Valid opcode"),
1778                dstack: vec![[1u8; 6].to_vec()],
1779                init: Default::default(),
1780            },
1781            TestCase {
1782                code: opcodes::OpData7::new([1u8; 7].to_vec()).expect("Valid opcode"),
1783                dstack: vec![[1u8; 7].to_vec()],
1784                init: Default::default(),
1785            },
1786            TestCase {
1787                code: opcodes::OpData8::new([1u8; 8].to_vec()).expect("Valid opcode"),
1788                dstack: vec![[1u8; 8].to_vec()],
1789                init: Default::default(),
1790            },
1791            TestCase {
1792                code: opcodes::OpData9::new([1u8; 9].to_vec()).expect("Valid opcode"),
1793                dstack: vec![[1u8; 9].to_vec()],
1794                init: Default::default(),
1795            },
1796            TestCase {
1797                code: opcodes::OpData10::new([1u8; 10].to_vec()).expect("Valid opcode"),
1798                dstack: vec![[1u8; 10].to_vec()],
1799                init: Default::default(),
1800            },
1801            TestCase {
1802                code: opcodes::OpData11::new([1u8; 11].to_vec()).expect("Valid opcode"),
1803                dstack: vec![[1u8; 11].to_vec()],
1804                init: Default::default(),
1805            },
1806            TestCase {
1807                code: opcodes::OpData12::new([1u8; 12].to_vec()).expect("Valid opcode"),
1808                dstack: vec![[1u8; 12].to_vec()],
1809                init: Default::default(),
1810            },
1811            TestCase {
1812                code: opcodes::OpData13::new([1u8; 13].to_vec()).expect("Valid opcode"),
1813                dstack: vec![[1u8; 13].to_vec()],
1814                init: Default::default(),
1815            },
1816            TestCase {
1817                code: opcodes::OpData14::new([1u8; 14].to_vec()).expect("Valid opcode"),
1818                dstack: vec![[1u8; 14].to_vec()],
1819                init: Default::default(),
1820            },
1821            TestCase {
1822                code: opcodes::OpData15::new([1u8; 15].to_vec()).expect("Valid opcode"),
1823                dstack: vec![[1u8; 15].to_vec()],
1824                init: Default::default(),
1825            },
1826            TestCase {
1827                code: opcodes::OpData16::new([1u8; 16].to_vec()).expect("Valid opcode"),
1828                dstack: vec![[1u8; 16].to_vec()],
1829                init: Default::default(),
1830            },
1831            TestCase {
1832                code: opcodes::OpData17::new([1u8; 17].to_vec()).expect("Valid opcode"),
1833                dstack: vec![[1u8; 17].to_vec()],
1834                init: Default::default(),
1835            },
1836            TestCase {
1837                code: opcodes::OpData18::new([1u8; 18].to_vec()).expect("Valid opcode"),
1838                dstack: vec![[1u8; 18].to_vec()],
1839                init: Default::default(),
1840            },
1841            TestCase {
1842                code: opcodes::OpData19::new([1u8; 19].to_vec()).expect("Valid opcode"),
1843                dstack: vec![[1u8; 19].to_vec()],
1844                init: Default::default(),
1845            },
1846            TestCase {
1847                code: opcodes::OpData20::new([1u8; 20].to_vec()).expect("Valid opcode"),
1848                dstack: vec![[1u8; 20].to_vec()],
1849                init: Default::default(),
1850            },
1851            TestCase {
1852                code: opcodes::OpData21::new([1u8; 21].to_vec()).expect("Valid opcode"),
1853                dstack: vec![[1u8; 21].to_vec()],
1854                init: Default::default(),
1855            },
1856            TestCase {
1857                code: opcodes::OpData22::new([1u8; 22].to_vec()).expect("Valid opcode"),
1858                dstack: vec![[1u8; 22].to_vec()],
1859                init: Default::default(),
1860            },
1861            TestCase {
1862                code: opcodes::OpData23::new([1u8; 23].to_vec()).expect("Valid opcode"),
1863                dstack: vec![[1u8; 23].to_vec()],
1864                init: Default::default(),
1865            },
1866            TestCase {
1867                code: opcodes::OpData24::new([1u8; 24].to_vec()).expect("Valid opcode"),
1868                dstack: vec![[1u8; 24].to_vec()],
1869                init: Default::default(),
1870            },
1871            TestCase {
1872                code: opcodes::OpData25::new([1u8; 25].to_vec()).expect("Valid opcode"),
1873                dstack: vec![[1u8; 25].to_vec()],
1874                init: Default::default(),
1875            },
1876            TestCase {
1877                code: opcodes::OpData26::new([1u8; 26].to_vec()).expect("Valid opcode"),
1878                dstack: vec![[1u8; 26].to_vec()],
1879                init: Default::default(),
1880            },
1881            TestCase {
1882                code: opcodes::OpData27::new([1u8; 27].to_vec()).expect("Valid opcode"),
1883                dstack: vec![[1u8; 27].to_vec()],
1884                init: Default::default(),
1885            },
1886            TestCase {
1887                code: opcodes::OpData28::new([1u8; 28].to_vec()).expect("Valid opcode"),
1888                dstack: vec![[1u8; 28].to_vec()],
1889                init: Default::default(),
1890            },
1891            TestCase {
1892                code: opcodes::OpData29::new([1u8; 29].to_vec()).expect("Valid opcode"),
1893                dstack: vec![[1u8; 29].to_vec()],
1894                init: Default::default(),
1895            },
1896            TestCase {
1897                code: opcodes::OpData30::new([1u8; 30].to_vec()).expect("Valid opcode"),
1898                dstack: vec![[1u8; 30].to_vec()],
1899                init: Default::default(),
1900            },
1901            TestCase {
1902                code: opcodes::OpData31::new([1u8; 31].to_vec()).expect("Valid opcode"),
1903                dstack: vec![[1u8; 31].to_vec()],
1904                init: Default::default(),
1905            },
1906            TestCase {
1907                code: opcodes::OpData32::new([1u8; 32].to_vec()).expect("Valid opcode"),
1908                dstack: vec![[1u8; 32].to_vec()],
1909                init: Default::default(),
1910            },
1911            TestCase {
1912                code: opcodes::OpData33::new([1u8; 33].to_vec()).expect("Valid opcode"),
1913                dstack: vec![[1u8; 33].to_vec()],
1914                init: Default::default(),
1915            },
1916            TestCase {
1917                code: opcodes::OpData34::new([1u8; 34].to_vec()).expect("Valid opcode"),
1918                dstack: vec![[1u8; 34].to_vec()],
1919                init: Default::default(),
1920            },
1921            TestCase {
1922                code: opcodes::OpData35::new([1u8; 35].to_vec()).expect("Valid opcode"),
1923                dstack: vec![[1u8; 35].to_vec()],
1924                init: Default::default(),
1925            },
1926            TestCase {
1927                code: opcodes::OpData36::new([1u8; 36].to_vec()).expect("Valid opcode"),
1928                dstack: vec![[1u8; 36].to_vec()],
1929                init: Default::default(),
1930            },
1931            TestCase {
1932                code: opcodes::OpData37::new([1u8; 37].to_vec()).expect("Valid opcode"),
1933                dstack: vec![[1u8; 37].to_vec()],
1934                init: Default::default(),
1935            },
1936            TestCase {
1937                code: opcodes::OpData38::new([1u8; 38].to_vec()).expect("Valid opcode"),
1938                dstack: vec![[1u8; 38].to_vec()],
1939                init: Default::default(),
1940            },
1941            TestCase {
1942                code: opcodes::OpData39::new([1u8; 39].to_vec()).expect("Valid opcode"),
1943                dstack: vec![[1u8; 39].to_vec()],
1944                init: Default::default(),
1945            },
1946            TestCase {
1947                code: opcodes::OpData40::new([1u8; 40].to_vec()).expect("Valid opcode"),
1948                dstack: vec![[1u8; 40].to_vec()],
1949                init: Default::default(),
1950            },
1951            TestCase {
1952                code: opcodes::OpData41::new([1u8; 41].to_vec()).expect("Valid opcode"),
1953                dstack: vec![[1u8; 41].to_vec()],
1954                init: Default::default(),
1955            },
1956            TestCase {
1957                code: opcodes::OpData42::new([1u8; 42].to_vec()).expect("Valid opcode"),
1958                dstack: vec![[1u8; 42].to_vec()],
1959                init: Default::default(),
1960            },
1961            TestCase {
1962                code: opcodes::OpData43::new([1u8; 43].to_vec()).expect("Valid opcode"),
1963                dstack: vec![[1u8; 43].to_vec()],
1964                init: Default::default(),
1965            },
1966            TestCase {
1967                code: opcodes::OpData44::new([1u8; 44].to_vec()).expect("Valid opcode"),
1968                dstack: vec![[1u8; 44].to_vec()],
1969                init: Default::default(),
1970            },
1971            TestCase {
1972                code: opcodes::OpData45::new([1u8; 45].to_vec()).expect("Valid opcode"),
1973                dstack: vec![[1u8; 45].to_vec()],
1974                init: Default::default(),
1975            },
1976            TestCase {
1977                code: opcodes::OpData46::new([1u8; 46].to_vec()).expect("Valid opcode"),
1978                dstack: vec![[1u8; 46].to_vec()],
1979                init: Default::default(),
1980            },
1981            TestCase {
1982                code: opcodes::OpData47::new([1u8; 47].to_vec()).expect("Valid opcode"),
1983                dstack: vec![[1u8; 47].to_vec()],
1984                init: Default::default(),
1985            },
1986            TestCase {
1987                code: opcodes::OpData48::new([1u8; 48].to_vec()).expect("Valid opcode"),
1988                dstack: vec![[1u8; 48].to_vec()],
1989                init: Default::default(),
1990            },
1991            TestCase {
1992                code: opcodes::OpData49::new([1u8; 49].to_vec()).expect("Valid opcode"),
1993                dstack: vec![[1u8; 49].to_vec()],
1994                init: Default::default(),
1995            },
1996            TestCase {
1997                code: opcodes::OpData50::new([1u8; 50].to_vec()).expect("Valid opcode"),
1998                dstack: vec![[1u8; 50].to_vec()],
1999                init: Default::default(),
2000            },
2001            TestCase {
2002                code: opcodes::OpData51::new([1u8; 51].to_vec()).expect("Valid opcode"),
2003                dstack: vec![[1u8; 51].to_vec()],
2004                init: Default::default(),
2005            },
2006            TestCase {
2007                code: opcodes::OpData52::new([1u8; 52].to_vec()).expect("Valid opcode"),
2008                dstack: vec![[1u8; 52].to_vec()],
2009                init: Default::default(),
2010            },
2011            TestCase {
2012                code: opcodes::OpData53::new([1u8; 53].to_vec()).expect("Valid opcode"),
2013                dstack: vec![[1u8; 53].to_vec()],
2014                init: Default::default(),
2015            },
2016            TestCase {
2017                code: opcodes::OpData54::new([1u8; 54].to_vec()).expect("Valid opcode"),
2018                dstack: vec![[1u8; 54].to_vec()],
2019                init: Default::default(),
2020            },
2021            TestCase {
2022                code: opcodes::OpData55::new([1u8; 55].to_vec()).expect("Valid opcode"),
2023                dstack: vec![[1u8; 55].to_vec()],
2024                init: Default::default(),
2025            },
2026            TestCase {
2027                code: opcodes::OpData56::new([1u8; 56].to_vec()).expect("Valid opcode"),
2028                dstack: vec![[1u8; 56].to_vec()],
2029                init: Default::default(),
2030            },
2031            TestCase {
2032                code: opcodes::OpData57::new([1u8; 57].to_vec()).expect("Valid opcode"),
2033                dstack: vec![[1u8; 57].to_vec()],
2034                init: Default::default(),
2035            },
2036            TestCase {
2037                code: opcodes::OpData58::new([1u8; 58].to_vec()).expect("Valid opcode"),
2038                dstack: vec![[1u8; 58].to_vec()],
2039                init: Default::default(),
2040            },
2041            TestCase {
2042                code: opcodes::OpData59::new([1u8; 59].to_vec()).expect("Valid opcode"),
2043                dstack: vec![[1u8; 59].to_vec()],
2044                init: Default::default(),
2045            },
2046            TestCase {
2047                code: opcodes::OpData60::new([1u8; 60].to_vec()).expect("Valid opcode"),
2048                dstack: vec![[1u8; 60].to_vec()],
2049                init: Default::default(),
2050            },
2051            TestCase {
2052                code: opcodes::OpData61::new([1u8; 61].to_vec()).expect("Valid opcode"),
2053                dstack: vec![[1u8; 61].to_vec()],
2054                init: Default::default(),
2055            },
2056            TestCase {
2057                code: opcodes::OpData62::new([1u8; 62].to_vec()).expect("Valid opcode"),
2058                dstack: vec![[1u8; 62].to_vec()],
2059                init: Default::default(),
2060            },
2061            TestCase {
2062                code: opcodes::OpData63::new([1u8; 63].to_vec()).expect("Valid opcode"),
2063                dstack: vec![[1u8; 63].to_vec()],
2064                init: Default::default(),
2065            },
2066            TestCase {
2067                code: opcodes::OpData64::new([1u8; 64].to_vec()).expect("Valid opcode"),
2068                dstack: vec![[1u8; 64].to_vec()],
2069                init: Default::default(),
2070            },
2071            TestCase {
2072                code: opcodes::OpData65::new([1u8; 65].to_vec()).expect("Valid opcode"),
2073                dstack: vec![[1u8; 65].to_vec()],
2074                init: Default::default(),
2075            },
2076            TestCase {
2077                code: opcodes::OpData66::new([1u8; 66].to_vec()).expect("Valid opcode"),
2078                dstack: vec![[1u8; 66].to_vec()],
2079                init: Default::default(),
2080            },
2081            TestCase {
2082                code: opcodes::OpData67::new([1u8; 67].to_vec()).expect("Valid opcode"),
2083                dstack: vec![[1u8; 67].to_vec()],
2084                init: Default::default(),
2085            },
2086            TestCase {
2087                code: opcodes::OpData68::new([1u8; 68].to_vec()).expect("Valid opcode"),
2088                dstack: vec![[1u8; 68].to_vec()],
2089                init: Default::default(),
2090            },
2091            TestCase {
2092                code: opcodes::OpData69::new([1u8; 69].to_vec()).expect("Valid opcode"),
2093                dstack: vec![[1u8; 69].to_vec()],
2094                init: Default::default(),
2095            },
2096            TestCase {
2097                code: opcodes::OpData70::new([1u8; 70].to_vec()).expect("Valid opcode"),
2098                dstack: vec![[1u8; 70].to_vec()],
2099                init: Default::default(),
2100            },
2101            TestCase {
2102                code: opcodes::OpData71::new([1u8; 71].to_vec()).expect("Valid opcode"),
2103                dstack: vec![[1u8; 71].to_vec()],
2104                init: Default::default(),
2105            },
2106            TestCase {
2107                code: opcodes::OpData72::new([1u8; 72].to_vec()).expect("Valid opcode"),
2108                dstack: vec![[1u8; 72].to_vec()],
2109                init: Default::default(),
2110            },
2111            TestCase {
2112                code: opcodes::OpData73::new([1u8; 73].to_vec()).expect("Valid opcode"),
2113                dstack: vec![[1u8; 73].to_vec()],
2114                init: Default::default(),
2115            },
2116            TestCase {
2117                code: opcodes::OpData74::new([1u8; 74].to_vec()).expect("Valid opcode"),
2118                dstack: vec![[1u8; 74].to_vec()],
2119                init: Default::default(),
2120            },
2121            TestCase {
2122                code: opcodes::OpData75::new([1u8; 75].to_vec()).expect("Valid opcode"),
2123                dstack: vec![[1u8; 75].to_vec()],
2124                init: Default::default(),
2125            },
2126            TestCase {
2127                code: opcodes::OpPushData1::new([1u8; 76].to_vec()).expect("Valid opcode"),
2128                dstack: vec![[1u8; 76].to_vec()],
2129                init: Default::default(),
2130            },
2131            TestCase {
2132                code: opcodes::OpPushData2::new([1u8; 0x100].to_vec()).expect("Valid opcode"),
2133                dstack: vec![[1u8; 0x100].to_vec()],
2134                init: Default::default(),
2135            },
2136            TestCase {
2137                code: opcodes::OpPushData4::new([1u8; 0x10000].to_vec()).expect("Valid opcode"),
2138                dstack: vec![[1u8; 0x10000].to_vec()],
2139                init: Default::default(),
2140            },
2141        ]);
2142    }
2143
2144    #[test]
2145    fn test_push_num() {
2146        run_success_test_cases(vec![
2147            TestCase {
2148                code: opcodes::Op1Negate::empty().expect("Should accept empty"),
2149                dstack: vec![vec![0x81]],
2150                init: Default::default(),
2151            },
2152            TestCase { code: opcodes::Op1::empty().expect("Should accept empty"), dstack: vec![vec![1]], init: Default::default() },
2153            TestCase { code: opcodes::Op2::empty().expect("Should accept empty"), dstack: vec![vec![2]], init: Default::default() },
2154            TestCase { code: opcodes::Op3::empty().expect("Should accept empty"), dstack: vec![vec![3]], init: Default::default() },
2155            TestCase { code: opcodes::Op4::empty().expect("Should accept empty"), dstack: vec![vec![4]], init: Default::default() },
2156            TestCase { code: opcodes::Op5::empty().expect("Should accept empty"), dstack: vec![vec![5]], init: Default::default() },
2157            TestCase { code: opcodes::Op6::empty().expect("Should accept empty"), dstack: vec![vec![6]], init: Default::default() },
2158            TestCase { code: opcodes::Op7::empty().expect("Should accept empty"), dstack: vec![vec![7]], init: Default::default() },
2159            TestCase { code: opcodes::Op8::empty().expect("Should accept empty"), dstack: vec![vec![8]], init: Default::default() },
2160            TestCase { code: opcodes::Op9::empty().expect("Should accept empty"), dstack: vec![vec![9]], init: Default::default() },
2161            TestCase { code: opcodes::Op10::empty().expect("Should accept empty"), dstack: vec![vec![10]], init: Default::default() },
2162            TestCase { code: opcodes::Op11::empty().expect("Should accept empty"), dstack: vec![vec![11]], init: Default::default() },
2163            TestCase { code: opcodes::Op12::empty().expect("Should accept empty"), dstack: vec![vec![12]], init: Default::default() },
2164            TestCase { code: opcodes::Op13::empty().expect("Should accept empty"), dstack: vec![vec![13]], init: Default::default() },
2165            TestCase { code: opcodes::Op14::empty().expect("Should accept empty"), dstack: vec![vec![14]], init: Default::default() },
2166            TestCase { code: opcodes::Op15::empty().expect("Should accept empty"), dstack: vec![vec![15]], init: Default::default() },
2167            TestCase { code: opcodes::Op16::empty().expect("Should accept empty"), dstack: vec![vec![16]], init: Default::default() },
2168        ]);
2169    }
2170
2171    #[test]
2172    fn test_uniary_num_ops() {
2173        run_success_test_cases(vec![
2174            TestCase { code: opcodes::Op1Add::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![1]] },
2175            TestCase { code: opcodes::Op1Add::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![vec![2]] },
2176            TestCase {
2177                code: opcodes::Op1Add::empty().expect("Should accept empty"),
2178                init: vec![vec![2, 1]],
2179                dstack: vec![vec![3, 1]],
2180            },
2181            TestCase { code: opcodes::Op1Add::empty().expect("Should accept empty"), init: vec![vec![0x81]], dstack: vec![vec![]] },
2182            TestCase { code: opcodes::Op1Sub::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![0x81]] },
2183            TestCase { code: opcodes::Op1Sub::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![vec![]] },
2184            TestCase { code: opcodes::Op1Sub::empty().expect("Should accept empty"), init: vec![vec![2]], dstack: vec![vec![1]] },
2185            TestCase {
2186                code: opcodes::Op1Sub::empty().expect("Should accept empty"),
2187                init: vec![vec![3, 1]],
2188                dstack: vec![vec![2, 1]],
2189            },
2190            TestCase { code: opcodes::OpNegate::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![]] },
2191            TestCase { code: opcodes::OpNegate::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![vec![0x81]] },
2192            TestCase { code: opcodes::OpNegate::empty().expect("Should accept empty"), init: vec![vec![0x81]], dstack: vec![vec![1]] },
2193            TestCase {
2194                code: opcodes::OpNegate::empty().expect("Should accept empty"),
2195                init: vec![vec![3, 1]],
2196                dstack: vec![vec![3, 0x81]],
2197            },
2198            TestCase { code: opcodes::OpAbs::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![]] },
2199            TestCase { code: opcodes::OpAbs::empty().expect("Should accept empty"), init: vec![vec![3, 1]], dstack: vec![vec![3, 1]] },
2200            TestCase {
2201                code: opcodes::OpAbs::empty().expect("Should accept empty"),
2202                init: vec![vec![3, 0x81]],
2203                dstack: vec![vec![3, 1]],
2204            },
2205            TestCase { code: opcodes::OpAbs::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![vec![1]] },
2206            TestCase { code: opcodes::OpAbs::empty().expect("Should accept empty"), init: vec![vec![0x81]], dstack: vec![vec![1]] },
2207            TestCase {
2208                code: opcodes::OpAbs::empty().expect("Should accept empty"),
2209                init: vec![vec![1, 1, 0x82]],
2210                dstack: vec![vec![1, 1, 2]],
2211            },
2212            TestCase { code: opcodes::OpNot::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![1]] },
2213            TestCase { code: opcodes::OpNot::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![vec![]] },
2214            TestCase { code: opcodes::OpNot::empty().expect("Should accept empty"), init: vec![vec![1, 2, 3]], dstack: vec![vec![]] },
2215            TestCase { code: opcodes::Op0NotEqual::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![]] },
2216            TestCase { code: opcodes::Op0NotEqual::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![vec![1]] },
2217            TestCase { code: opcodes::Op0NotEqual::empty().expect("Should accept empty"), init: vec![vec![2]], dstack: vec![vec![1]] },
2218            TestCase {
2219                code: opcodes::Op0NotEqual::empty().expect("Should accept empty"),
2220                init: vec![vec![1, 2, 3]],
2221                dstack: vec![vec![1]],
2222            },
2223        ]);
2224    }
2225
2226    #[test]
2227    fn test_binary_num_ops() {
2228        run_success_test_cases(vec![
2229            TestCase { code: opcodes::OpAdd::empty().expect("Should accept empty"), init: vec![vec![], vec![]], dstack: vec![vec![]] },
2230            TestCase {
2231                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2232                init: vec![vec![], vec![1]],
2233                dstack: vec![vec![1]],
2234            },
2235            TestCase {
2236                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2237                init: vec![vec![1], vec![]],
2238                dstack: vec![vec![1]],
2239            },
2240            TestCase {
2241                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2242                init: vec![vec![1], vec![1]],
2243                dstack: vec![vec![2]],
2244            },
2245            TestCase {
2246                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2247                init: vec![vec![0x81], vec![1]],
2248                dstack: vec![vec![]],
2249            },
2250            TestCase {
2251                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2252                init: vec![vec![0x7f], vec![1]],
2253                dstack: vec![vec![0x80, 0]],
2254            },
2255            TestCase {
2256                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2257                init: vec![vec![0x80, 0], vec![0x80, 0]],
2258                dstack: vec![vec![0, 1]],
2259            },
2260            TestCase {
2261                code: opcodes::OpAdd::empty().expect("Should accept empty"),
2262                init: vec![vec![0xff, 0], vec![1]],
2263                dstack: vec![vec![0, 1]],
2264            },
2265            TestCase { code: opcodes::OpSub::empty().expect("Should accept empty"), init: vec![vec![], vec![]], dstack: vec![vec![]] },
2266            TestCase {
2267                code: opcodes::OpSub::empty().expect("Should accept empty"),
2268                init: vec![vec![], vec![1]],
2269                dstack: vec![vec![0x81]],
2270            },
2271            TestCase {
2272                code: opcodes::OpSub::empty().expect("Should accept empty"),
2273                init: vec![vec![1], vec![]],
2274                dstack: vec![vec![1]],
2275            },
2276            TestCase {
2277                code: opcodes::OpSub::empty().expect("Should accept empty"),
2278                init: vec![vec![1], vec![1]],
2279                dstack: vec![vec![]],
2280            },
2281            TestCase {
2282                code: opcodes::OpSub::empty().expect("Should accept empty"),
2283                init: vec![vec![0x81], vec![1]],
2284                dstack: vec![vec![0x82]],
2285            },
2286            TestCase {
2287                code: opcodes::OpSub::empty().expect("Should accept empty"),
2288                init: vec![vec![0x80, 0], vec![1]],
2289                dstack: vec![vec![0x7f]],
2290            },
2291            TestCase {
2292                code: opcodes::OpSub::empty().expect("Should accept empty"),
2293                init: vec![vec![0, 1], vec![0x80, 0]],
2294                dstack: vec![vec![0x80, 0]],
2295            },
2296            TestCase {
2297                code: opcodes::OpSub::empty().expect("Should accept empty"),
2298                init: vec![vec![0, 1], vec![1]],
2299                dstack: vec![vec![0xff, 0]],
2300            },
2301            TestCase {
2302                code: opcodes::OpMax::empty().expect("Should accept empty"),
2303                init: vec![vec![0, 1], vec![1]],
2304                dstack: vec![vec![0, 1]],
2305            },
2306            TestCase {
2307                code: opcodes::OpMax::empty().expect("Should accept empty"),
2308                init: vec![vec![1], vec![0, 1]],
2309                dstack: vec![vec![0, 1]],
2310            },
2311            TestCase {
2312                code: opcodes::OpMax::empty().expect("Should accept empty"),
2313                init: vec![vec![0, 0x81], vec![1]],
2314                dstack: vec![vec![1]],
2315            },
2316            TestCase {
2317                code: opcodes::OpMin::empty().expect("Should accept empty"),
2318                init: vec![vec![0, 1], vec![1]],
2319                dstack: vec![vec![1]],
2320            },
2321            TestCase {
2322                code: opcodes::OpMin::empty().expect("Should accept empty"),
2323                init: vec![vec![1], vec![0, 1]],
2324                dstack: vec![vec![1]],
2325            },
2326            TestCase {
2327                code: opcodes::OpMin::empty().expect("Should accept empty"),
2328                init: vec![vec![0, 0x81], vec![1]],
2329                dstack: vec![vec![0, 0x81]],
2330            },
2331        ]);
2332    }
2333
2334    #[test]
2335    fn test_logical_ops() {
2336        run_success_test_cases(vec![
2337            TestCase {
2338                code: opcodes::OpEqual::empty().expect("Should accept empty"),
2339                init: vec![vec![], vec![]],
2340                dstack: vec![vec![1]],
2341            },
2342            TestCase {
2343                code: opcodes::OpEqual::empty().expect("Should accept empty"),
2344                init: vec![vec![0, 1, 1, 0], vec![0, 1, 1, 0]],
2345                dstack: vec![vec![1]],
2346            },
2347            TestCase {
2348                code: opcodes::OpEqual::empty().expect("Should accept empty"),
2349                init: vec![vec![], vec![0]],
2350                dstack: vec![vec![]],
2351            },
2352            TestCase {
2353                code: opcodes::OpEqual::empty().expect("Should accept empty"),
2354                init: vec![vec![0, 1, 1, 0], vec![0, 1, 1, 1]],
2355                dstack: vec![vec![]],
2356            },
2357            TestCase {
2358                code: opcodes::OpBoolAnd::empty().expect("Should accept empty"),
2359                init: vec![vec![], vec![]],
2360                dstack: vec![vec![]],
2361            },
2362            TestCase {
2363                code: opcodes::OpBoolAnd::empty().expect("Should accept empty"),
2364                init: vec![vec![1], vec![]],
2365                dstack: vec![vec![]],
2366            },
2367            TestCase {
2368                code: opcodes::OpBoolAnd::empty().expect("Should accept empty"),
2369                init: vec![vec![], vec![1]],
2370                dstack: vec![vec![]],
2371            },
2372            TestCase {
2373                code: opcodes::OpBoolAnd::empty().expect("Should accept empty"),
2374                init: vec![vec![1], vec![1]],
2375                dstack: vec![vec![1]],
2376            },
2377            TestCase {
2378                code: opcodes::OpBoolAnd::empty().expect("Should accept empty"),
2379                init: vec![vec![1], vec![0x81]],
2380                dstack: vec![vec![1]],
2381            },
2382            TestCase {
2383                code: opcodes::OpBoolOr::empty().expect("Should accept empty"),
2384                init: vec![vec![], vec![]],
2385                dstack: vec![vec![]],
2386            },
2387            TestCase {
2388                code: opcodes::OpBoolOr::empty().expect("Should accept empty"),
2389                init: vec![vec![1], vec![]],
2390                dstack: vec![vec![1]],
2391            },
2392            TestCase {
2393                code: opcodes::OpBoolOr::empty().expect("Should accept empty"),
2394                init: vec![vec![], vec![1]],
2395                dstack: vec![vec![1]],
2396            },
2397            TestCase {
2398                code: opcodes::OpBoolOr::empty().expect("Should accept empty"),
2399                init: vec![vec![1], vec![1]],
2400                dstack: vec![vec![1]],
2401            },
2402            TestCase {
2403                code: opcodes::OpBoolOr::empty().expect("Should accept empty"),
2404                init: vec![vec![0x81], vec![1]],
2405                dstack: vec![vec![1]],
2406            },
2407            TestCase {
2408                code: opcodes::OpBoolOr::empty().expect("Should accept empty"),
2409                init: vec![vec![0x81], vec![1]],
2410                dstack: vec![vec![1]],
2411            },
2412            TestCase {
2413                code: opcodes::OpNumEqual::empty().expect("Should accept empty"),
2414                init: vec![vec![1], vec![1]],
2415                dstack: vec![vec![1]],
2416            },
2417            TestCase {
2418                code: opcodes::OpNumEqual::empty().expect("Should accept empty"),
2419                init: vec![vec![], vec![1]],
2420                dstack: vec![vec![]],
2421            },
2422            TestCase {
2423                code: opcodes::OpNumEqual::empty().expect("Should accept empty"),
2424                init: vec![vec![0x81], vec![1]],
2425                dstack: vec![vec![]],
2426            },
2427            TestCase {
2428                code: opcodes::OpNumEqual::empty().expect("Should accept empty"),
2429                init: vec![vec![], vec![]],
2430                dstack: vec![vec![1]],
2431            },
2432            TestCase {
2433                code: opcodes::OpNumNotEqual::empty().expect("Should accept empty"),
2434                init: vec![vec![1], vec![1]],
2435                dstack: vec![vec![]],
2436            },
2437            TestCase {
2438                code: opcodes::OpNumNotEqual::empty().expect("Should accept empty"),
2439                init: vec![vec![], vec![1]],
2440                dstack: vec![vec![1]],
2441            },
2442            TestCase {
2443                code: opcodes::OpNumNotEqual::empty().expect("Should accept empty"),
2444                init: vec![vec![0x81], vec![1]],
2445                dstack: vec![vec![1]],
2446            },
2447            TestCase {
2448                code: opcodes::OpNumNotEqual::empty().expect("Should accept empty"),
2449                init: vec![vec![], vec![]],
2450                dstack: vec![vec![]],
2451            },
2452            TestCase {
2453                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2454                init: vec![vec![1], vec![1]],
2455                dstack: vec![vec![]],
2456            },
2457            TestCase {
2458                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2459                init: vec![vec![], vec![1]],
2460                dstack: vec![vec![1]],
2461            },
2462            TestCase {
2463                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2464                init: vec![vec![0x81], vec![1]],
2465                dstack: vec![vec![1]],
2466            },
2467            TestCase {
2468                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2469                init: vec![vec![], vec![]],
2470                dstack: vec![vec![]],
2471            },
2472            TestCase {
2473                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2474                init: vec![vec![1], vec![]],
2475                dstack: vec![vec![]],
2476            },
2477            TestCase {
2478                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2479                init: vec![vec![], vec![0x81]],
2480                dstack: vec![vec![]],
2481            },
2482            TestCase {
2483                code: opcodes::OpLessThan::empty().expect("Should accept empty"),
2484                init: vec![vec![1], vec![0x81]],
2485                dstack: vec![vec![]],
2486            },
2487            TestCase {
2488                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2489                init: vec![vec![1], vec![1]],
2490                dstack: vec![vec![1]],
2491            },
2492            TestCase {
2493                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2494                init: vec![vec![], vec![1]],
2495                dstack: vec![vec![1]],
2496            },
2497            TestCase {
2498                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2499                init: vec![vec![0x81], vec![1]],
2500                dstack: vec![vec![1]],
2501            },
2502            TestCase {
2503                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2504                init: vec![vec![], vec![]],
2505                dstack: vec![vec![1]],
2506            },
2507            TestCase {
2508                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2509                init: vec![vec![1], vec![]],
2510                dstack: vec![vec![]],
2511            },
2512            TestCase {
2513                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2514                init: vec![vec![], vec![0x81]],
2515                dstack: vec![vec![]],
2516            },
2517            TestCase {
2518                code: opcodes::OpLessThanOrEqual::empty().expect("Should accept empty"),
2519                init: vec![vec![1], vec![0x81]],
2520                dstack: vec![vec![]],
2521            },
2522            TestCase {
2523                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2524                init: vec![vec![1], vec![1]],
2525                dstack: vec![vec![]],
2526            },
2527            TestCase {
2528                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2529                init: vec![vec![], vec![1]],
2530                dstack: vec![vec![]],
2531            },
2532            TestCase {
2533                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2534                init: vec![vec![0x81], vec![1]],
2535                dstack: vec![vec![]],
2536            },
2537            TestCase {
2538                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2539                init: vec![vec![], vec![]],
2540                dstack: vec![vec![]],
2541            },
2542            TestCase {
2543                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2544                init: vec![vec![1], vec![]],
2545                dstack: vec![vec![1]],
2546            },
2547            TestCase {
2548                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2549                init: vec![vec![], vec![0x81]],
2550                dstack: vec![vec![1]],
2551            },
2552            TestCase {
2553                code: opcodes::OpGreaterThan::empty().expect("Should accept empty"),
2554                init: vec![vec![1], vec![0x81]],
2555                dstack: vec![vec![1]],
2556            },
2557            TestCase {
2558                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2559                init: vec![vec![1], vec![1]],
2560                dstack: vec![vec![1]],
2561            },
2562            TestCase {
2563                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2564                init: vec![vec![], vec![1]],
2565                dstack: vec![vec![]],
2566            },
2567            TestCase {
2568                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2569                init: vec![vec![0x81], vec![1]],
2570                dstack: vec![vec![]],
2571            },
2572            TestCase {
2573                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2574                init: vec![vec![], vec![]],
2575                dstack: vec![vec![1]],
2576            },
2577            TestCase {
2578                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2579                init: vec![vec![1], vec![]],
2580                dstack: vec![vec![1]],
2581            },
2582            TestCase {
2583                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2584                init: vec![vec![], vec![0x81]],
2585                dstack: vec![vec![1]],
2586            },
2587            TestCase {
2588                code: opcodes::OpGreaterThanOrEqual::empty().expect("Should accept empty"),
2589                init: vec![vec![1], vec![0x81]],
2590                dstack: vec![vec![1]],
2591            },
2592        ]);
2593    }
2594
2595    #[test]
2596    fn test_opdepth() {
2597        run_success_test_cases(vec![
2598            TestCase { code: opcodes::OpDepth::empty().expect("Should accept empty"), init: vec![], dstack: vec![vec![]] },
2599            TestCase {
2600                code: opcodes::OpDepth::empty().expect("Should accept empty"),
2601                init: vec![vec![]],
2602                dstack: vec![vec![], vec![1]],
2603            },
2604            TestCase {
2605                code: opcodes::OpDepth::empty().expect("Should accept empty"),
2606                init: vec![vec![1], vec![2], vec![3]],
2607                dstack: vec![vec![1], vec![2], vec![3], vec![3]],
2608            },
2609        ]);
2610    }
2611
2612    #[test]
2613    fn test_opdrop() {
2614        run_success_test_cases(vec![
2615            TestCase { code: opcodes::OpDrop::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![] },
2616            TestCase {
2617                code: opcodes::OpDrop::empty().expect("Should accept empty"),
2618                init: vec![vec![], vec![1]],
2619                dstack: vec![vec![]],
2620            },
2621            TestCase {
2622                code: opcodes::OpDrop::empty().expect("Should accept empty"),
2623                init: vec![vec![1], vec![2], vec![3], vec![3]],
2624                dstack: vec![vec![1], vec![2], vec![3]],
2625            },
2626        ]);
2627
2628        run_error_test_cases(vec![ErrorTestCase {
2629            code: opcodes::OpDrop::empty().expect("Should accept empty"),
2630            init: vec![],
2631            error: TxScriptError::InvalidStackOperation(1, 0),
2632        }])
2633    }
2634
2635    #[test]
2636    fn test_op2drop() {
2637        run_success_test_cases(vec![
2638            TestCase { code: opcodes::Op2Drop::empty().expect("Should accept empty"), init: vec![vec![], vec![1]], dstack: vec![] },
2639            TestCase {
2640                code: opcodes::Op2Drop::empty().expect("Should accept empty"),
2641                init: vec![vec![1], vec![2], vec![3], vec![3]],
2642                dstack: vec![vec![1], vec![2]],
2643            },
2644        ]);
2645
2646        run_error_test_cases(vec![
2647            ErrorTestCase {
2648                code: opcodes::Op2Drop::empty().expect("Should accept empty"),
2649                init: vec![],
2650                error: TxScriptError::InvalidStackOperation(2, 0),
2651            },
2652            ErrorTestCase {
2653                code: opcodes::Op2Drop::empty().expect("Should accept empty"),
2654                init: vec![vec![]],
2655                error: TxScriptError::InvalidStackOperation(2, 1),
2656            },
2657        ])
2658    }
2659
2660    #[test]
2661    fn test_opdup() {
2662        run_success_test_cases(vec![
2663            TestCase { code: opcodes::OpDup::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![], vec![]] },
2664            TestCase {
2665                code: opcodes::OpDup::empty().expect("Should accept empty"),
2666                init: vec![vec![], vec![1]],
2667                dstack: vec![vec![], vec![1], vec![1]],
2668            },
2669            TestCase {
2670                code: opcodes::OpDup::empty().expect("Should accept empty"),
2671                init: vec![vec![1], vec![2], vec![3], vec![3]],
2672                dstack: vec![vec![1], vec![2], vec![3], vec![3], vec![3]],
2673            },
2674        ]);
2675
2676        run_error_test_cases(vec![ErrorTestCase {
2677            code: opcodes::OpDup::empty().expect("Should accept empty"),
2678            init: vec![],
2679            error: TxScriptError::InvalidStackOperation(1, 0),
2680        }])
2681    }
2682
2683    #[test]
2684    fn test_op2dup() {
2685        run_success_test_cases(vec![
2686            TestCase {
2687                code: opcodes::Op2Dup::empty().expect("Should accept empty"),
2688                init: vec![vec![], vec![1]],
2689                dstack: vec![vec![], vec![1], vec![], vec![1]],
2690            },
2691            TestCase {
2692                code: opcodes::Op2Dup::empty().expect("Should accept empty"),
2693                init: vec![vec![1], vec![2], vec![3]],
2694                dstack: vec![vec![1], vec![2], vec![3], vec![2], vec![3]],
2695            },
2696        ]);
2697
2698        run_error_test_cases(vec![
2699            ErrorTestCase {
2700                code: opcodes::Op2Dup::empty().expect("Should accept empty"),
2701                init: vec![],
2702                error: TxScriptError::InvalidStackOperation(2, 0),
2703            },
2704            ErrorTestCase {
2705                code: opcodes::Op2Dup::empty().expect("Should accept empty"),
2706                init: vec![vec![]],
2707                error: TxScriptError::InvalidStackOperation(2, 1),
2708            },
2709        ]);
2710    }
2711
2712    #[test]
2713    fn test_op3dup() {
2714        run_success_test_cases(vec![
2715            TestCase {
2716                code: opcodes::Op3Dup::empty().expect("Should accept empty"),
2717                init: vec![vec![0x81], vec![], vec![1]],
2718                dstack: vec![vec![0x81], vec![], vec![1], vec![0x81], vec![], vec![1]],
2719            },
2720            TestCase {
2721                code: opcodes::Op3Dup::empty().expect("Should accept empty"),
2722                init: vec![vec![1], vec![2], vec![3]],
2723                dstack: vec![vec![1], vec![2], vec![3], vec![1], vec![2], vec![3]],
2724            },
2725        ]);
2726
2727        run_error_test_cases(vec![
2728            ErrorTestCase {
2729                code: opcodes::Op3Dup::empty().expect("Should accept empty"),
2730                init: vec![],
2731                error: TxScriptError::InvalidStackOperation(3, 0),
2732            },
2733            ErrorTestCase {
2734                code: opcodes::Op3Dup::empty().expect("Should accept empty"),
2735                init: vec![vec![]],
2736                error: TxScriptError::InvalidStackOperation(3, 1),
2737            },
2738            ErrorTestCase {
2739                code: opcodes::Op3Dup::empty().expect("Should accept empty"),
2740                init: vec![vec![], vec![]],
2741                error: TxScriptError::InvalidStackOperation(3, 2),
2742            },
2743        ]);
2744    }
2745
2746    #[test]
2747    fn test_opnip() {
2748        run_success_test_cases(vec![
2749            TestCase {
2750                code: opcodes::OpNip::empty().expect("Should accept empty"),
2751                init: vec![vec![], vec![1]],
2752                dstack: vec![vec![1]],
2753            },
2754            TestCase {
2755                code: opcodes::OpNip::empty().expect("Should accept empty"),
2756                init: vec![vec![1], vec![]],
2757                dstack: vec![vec![]],
2758            },
2759            TestCase {
2760                code: opcodes::OpNip::empty().expect("Should accept empty"),
2761                init: vec![vec![2], vec![], vec![1]],
2762                dstack: vec![vec![2], vec![1]],
2763            },
2764        ]);
2765
2766        run_error_test_cases(vec![
2767            ErrorTestCase {
2768                code: opcodes::OpNip::empty().expect("Should accept empty"),
2769                init: vec![],
2770                error: TxScriptError::InvalidStackOperation(2, 0),
2771            },
2772            ErrorTestCase {
2773                code: opcodes::OpNip::empty().expect("Should accept empty"),
2774                init: vec![vec![]],
2775                error: TxScriptError::InvalidStackOperation(2, 1),
2776            },
2777        ]);
2778    }
2779
2780    #[test]
2781    fn test_opover() {
2782        run_success_test_cases(vec![
2783            TestCase {
2784                code: opcodes::OpOver::empty().expect("Should accept empty"),
2785                init: vec![vec![], vec![1]],
2786                dstack: vec![vec![], vec![1], vec![]],
2787            },
2788            TestCase {
2789                code: opcodes::OpOver::empty().expect("Should accept empty"),
2790                init: vec![vec![1], vec![]],
2791                dstack: vec![vec![1], vec![], vec![1]],
2792            },
2793            TestCase {
2794                code: opcodes::OpOver::empty().expect("Should accept empty"),
2795                init: vec![vec![2], vec![], vec![1]],
2796                dstack: vec![vec![2], vec![], vec![1], vec![]],
2797            },
2798        ]);
2799
2800        run_error_test_cases(vec![
2801            ErrorTestCase {
2802                code: opcodes::OpOver::empty().expect("Should accept empty"),
2803                init: vec![],
2804                error: TxScriptError::InvalidStackOperation(2, 0),
2805            },
2806            ErrorTestCase {
2807                code: opcodes::OpOver::empty().expect("Should accept empty"),
2808                init: vec![vec![]],
2809                error: TxScriptError::InvalidStackOperation(2, 1),
2810            },
2811        ]);
2812    }
2813
2814    #[test]
2815    fn test_op2over() {
2816        run_success_test_cases(vec![
2817            TestCase {
2818                code: opcodes::Op2Over::empty().expect("Should accept empty"),
2819                init: vec![vec![0x81], vec![2], vec![], vec![1]],
2820                dstack: vec![vec![0x81], vec![2], vec![], vec![1], vec![0x81], vec![2]],
2821            },
2822            TestCase {
2823                code: opcodes::Op2Over::empty().expect("Should accept empty"),
2824                init: vec![vec![], vec![0x81], vec![2], vec![], vec![1]],
2825                dstack: vec![vec![], vec![0x81], vec![2], vec![], vec![1], vec![0x81], vec![2]],
2826            },
2827        ]);
2828
2829        run_error_test_cases(vec![
2830            ErrorTestCase {
2831                code: opcodes::Op2Over::empty().expect("Should accept empty"),
2832                init: vec![],
2833                error: TxScriptError::InvalidStackOperation(4, 0),
2834            },
2835            ErrorTestCase {
2836                code: opcodes::Op2Over::empty().expect("Should accept empty"),
2837                init: vec![vec![]],
2838                error: TxScriptError::InvalidStackOperation(4, 1),
2839            },
2840            ErrorTestCase {
2841                code: opcodes::Op2Over::empty().expect("Should accept empty"),
2842                init: vec![vec![], vec![]],
2843                error: TxScriptError::InvalidStackOperation(4, 2),
2844            },
2845            ErrorTestCase {
2846                code: opcodes::Op2Over::empty().expect("Should accept empty"),
2847                init: vec![vec![], vec![], vec![]],
2848                error: TxScriptError::InvalidStackOperation(4, 3),
2849            },
2850        ]);
2851    }
2852
2853    #[test]
2854    fn test_oppick() {
2855        run_success_test_cases(vec![
2856            TestCase {
2857                code: opcodes::OpPick::empty().expect("Should accept empty"),
2858                init: vec![vec![], vec![]],
2859                dstack: vec![vec![], vec![]],
2860            },
2861            TestCase {
2862                code: opcodes::OpPick::empty().expect("Should accept empty"),
2863                init: vec![vec![2], vec![], vec![1]],
2864                dstack: vec![vec![2], vec![], vec![2]],
2865            },
2866            TestCase {
2867                code: opcodes::OpPick::empty().expect("Should accept empty"),
2868                init: vec![vec![5], vec![4], vec![3], vec![], vec![2]],
2869                dstack: vec![vec![5], vec![4], vec![3], vec![], vec![4]],
2870            },
2871        ]);
2872
2873        run_error_test_cases(vec![
2874            ErrorTestCase {
2875                code: opcodes::OpPick::empty().expect("Should accept empty"),
2876                init: vec![vec![5], vec![4], vec![3], vec![], vec![4]],
2877                error: TxScriptError::InvalidState("pick at an invalid location".to_string()),
2878            },
2879            ErrorTestCase {
2880                code: opcodes::OpPick::empty().expect("Should accept empty"),
2881                init: vec![vec![5], vec![4], vec![3], vec![], vec![0x81]],
2882                error: TxScriptError::InvalidState("pick at an invalid location".to_string()),
2883            },
2884        ])
2885    }
2886
2887    #[test]
2888    fn test_oproll() {
2889        run_success_test_cases(vec![
2890            TestCase {
2891                code: opcodes::OpRoll::empty().expect("Should accept empty"),
2892                init: vec![vec![], vec![]],
2893                dstack: vec![vec![]],
2894            },
2895            TestCase {
2896                code: opcodes::OpRoll::empty().expect("Should accept empty"),
2897                init: vec![vec![2], vec![], vec![1]],
2898                dstack: vec![vec![], vec![2]],
2899            },
2900            TestCase {
2901                code: opcodes::OpRoll::empty().expect("Should accept empty"),
2902                init: vec![vec![5], vec![4], vec![3], vec![], vec![2]],
2903                dstack: vec![vec![5], vec![3], vec![], vec![4]],
2904            },
2905        ]);
2906
2907        run_error_test_cases(vec![
2908            ErrorTestCase {
2909                code: opcodes::OpRoll::empty().expect("Should accept empty"),
2910                init: vec![vec![5], vec![4], vec![3], vec![], vec![4]],
2911                error: TxScriptError::InvalidState("roll at an invalid location".to_string()),
2912            },
2913            ErrorTestCase {
2914                code: opcodes::OpRoll::empty().expect("Should accept empty"),
2915                init: vec![vec![5], vec![4], vec![3], vec![], vec![0x81]],
2916                error: TxScriptError::InvalidState("roll at an invalid location".to_string()),
2917            },
2918        ])
2919    }
2920
2921    #[test]
2922    fn test_oprot() {
2923        run_success_test_cases(vec![
2924            TestCase {
2925                code: opcodes::OpRot::empty().expect("Should accept empty"),
2926                init: vec![vec![1], vec![2], vec![3]],
2927                dstack: vec![vec![2], vec![3], vec![1]],
2928            },
2929            TestCase {
2930                code: opcodes::OpRot::empty().expect("Should accept empty"),
2931                init: vec![vec![], vec![1], vec![2], vec![3]],
2932                dstack: vec![vec![], vec![2], vec![3], vec![1]],
2933            },
2934        ]);
2935
2936        run_error_test_cases(vec![
2937            ErrorTestCase {
2938                code: opcodes::OpRot::empty().expect("Should accept empty"),
2939                init: vec![vec![2], vec![3]],
2940                error: TxScriptError::InvalidStackOperation(3, 2),
2941            },
2942            ErrorTestCase {
2943                code: opcodes::OpRot::empty().expect("Should accept empty"),
2944                init: vec![vec![3]],
2945                error: TxScriptError::InvalidStackOperation(3, 1),
2946            },
2947            ErrorTestCase {
2948                code: opcodes::OpRot::empty().expect("Should accept empty"),
2949                init: vec![],
2950                error: TxScriptError::InvalidStackOperation(3, 0),
2951            },
2952        ]);
2953    }
2954
2955    #[test]
2956    fn test_op2rot() {
2957        run_success_test_cases(vec![
2958            TestCase {
2959                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2960                init: vec![vec![1], vec![2], vec![3], vec![4], vec![5], vec![6]],
2961                dstack: vec![vec![3], vec![4], vec![5], vec![6], vec![1], vec![2]],
2962            },
2963            TestCase {
2964                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2965                init: vec![vec![], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6]],
2966                dstack: vec![vec![], vec![3], vec![4], vec![5], vec![6], vec![1], vec![2]],
2967            },
2968        ]);
2969
2970        run_error_test_cases(vec![
2971            ErrorTestCase {
2972                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2973                init: vec![vec![1], vec![2], vec![3], vec![4], vec![5]],
2974                error: TxScriptError::InvalidStackOperation(6, 5),
2975            },
2976            ErrorTestCase {
2977                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2978                init: vec![vec![1], vec![2], vec![3], vec![4]],
2979                error: TxScriptError::InvalidStackOperation(6, 4),
2980            },
2981            ErrorTestCase {
2982                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2983                init: vec![vec![1], vec![2], vec![3]],
2984                error: TxScriptError::InvalidStackOperation(6, 3),
2985            },
2986            ErrorTestCase {
2987                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2988                init: vec![vec![1], vec![2]],
2989                error: TxScriptError::InvalidStackOperation(6, 2),
2990            },
2991            ErrorTestCase {
2992                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2993                init: vec![vec![1]],
2994                error: TxScriptError::InvalidStackOperation(6, 1),
2995            },
2996            ErrorTestCase {
2997                code: opcodes::Op2Rot::empty().expect("Should accept empty"),
2998                init: vec![],
2999                error: TxScriptError::InvalidStackOperation(6, 0),
3000            },
3001        ]);
3002    }
3003
3004    #[test]
3005    fn test_opswap() {
3006        run_success_test_cases(vec![
3007            TestCase {
3008                code: opcodes::OpSwap::empty().expect("Should accept empty"),
3009                init: vec![vec![1], vec![2]],
3010                dstack: vec![vec![2], vec![1]],
3011            },
3012            TestCase {
3013                code: opcodes::OpSwap::empty().expect("Should accept empty"),
3014                init: vec![vec![], vec![1], vec![5]],
3015                dstack: vec![vec![], vec![5], vec![1]],
3016            },
3017        ]);
3018
3019        run_error_test_cases(vec![
3020            ErrorTestCase {
3021                code: opcodes::OpSwap::empty().expect("Should accept empty"),
3022                init: vec![vec![1]],
3023                error: TxScriptError::InvalidStackOperation(2, 1),
3024            },
3025            ErrorTestCase {
3026                code: opcodes::OpSwap::empty().expect("Should accept empty"),
3027                init: vec![],
3028                error: TxScriptError::InvalidStackOperation(2, 0),
3029            },
3030        ]);
3031    }
3032
3033    #[test]
3034    fn test_op2swap() {
3035        run_success_test_cases(vec![
3036            TestCase {
3037                code: opcodes::Op2Swap::empty().expect("Should accept empty"),
3038                init: vec![vec![1], vec![2], vec![3], vec![4]],
3039                dstack: vec![vec![3], vec![4], vec![1], vec![2]],
3040            },
3041            TestCase {
3042                code: opcodes::Op2Swap::empty().expect("Should accept empty"),
3043                init: vec![vec![], vec![1], vec![2], vec![3], vec![4]],
3044                dstack: vec![vec![], vec![3], vec![4], vec![1], vec![2]],
3045            },
3046        ]);
3047
3048        run_error_test_cases(vec![
3049            ErrorTestCase {
3050                code: opcodes::Op2Swap::empty().expect("Should accept empty"),
3051                init: vec![vec![], vec![2], vec![1]],
3052                error: TxScriptError::InvalidStackOperation(4, 3),
3053            },
3054            ErrorTestCase {
3055                code: opcodes::Op2Swap::empty().expect("Should accept empty"),
3056                init: vec![vec![], vec![1]],
3057                error: TxScriptError::InvalidStackOperation(4, 2),
3058            },
3059            ErrorTestCase {
3060                code: opcodes::Op2Swap::empty().expect("Should accept empty"),
3061                init: vec![vec![1]],
3062                error: TxScriptError::InvalidStackOperation(4, 1),
3063            },
3064            ErrorTestCase {
3065                code: opcodes::Op2Swap::empty().expect("Should accept empty"),
3066                init: vec![],
3067                error: TxScriptError::InvalidStackOperation(4, 0),
3068            },
3069        ]);
3070    }
3071
3072    #[test]
3073    fn test_optuck() {
3074        run_success_test_cases(vec![
3075            TestCase {
3076                code: opcodes::OpTuck::empty().expect("Should accept empty"),
3077                init: vec![vec![1], vec![2]],
3078                dstack: vec![vec![2], vec![1], vec![2]],
3079            },
3080            TestCase {
3081                code: opcodes::OpTuck::empty().expect("Should accept empty"),
3082                init: vec![vec![3], vec![9], vec![2]],
3083                dstack: vec![vec![3], vec![2], vec![9], vec![2]],
3084            },
3085        ]);
3086
3087        run_error_test_cases(vec![
3088            ErrorTestCase {
3089                code: opcodes::OpTuck::empty().expect("Should accept empty"),
3090                init: vec![vec![3]],
3091                error: TxScriptError::InvalidStackOperation(2, 1),
3092            },
3093            ErrorTestCase {
3094                code: opcodes::OpTuck::empty().expect("Should accept empty"),
3095                init: vec![],
3096                error: TxScriptError::InvalidStackOperation(2, 0),
3097            },
3098        ]);
3099    }
3100
3101    #[test]
3102    fn test_opequalverify() {
3103        run_success_test_cases(vec![
3104            TestCase {
3105                code: opcodes::OpEqualVerify::empty().expect("Should accept empty"),
3106                init: vec![vec![], vec![]],
3107                dstack: vec![],
3108            },
3109            TestCase {
3110                code: opcodes::OpEqualVerify::empty().expect("Should accept empty"),
3111                init: vec![vec![], vec![1, 0, 1], vec![1, 0, 1]],
3112                dstack: vec![vec![]],
3113            },
3114            TestCase {
3115                code: opcodes::OpNumEqualVerify::empty().expect("Should accept empty"),
3116                init: vec![vec![], vec![]],
3117                dstack: vec![],
3118            },
3119            TestCase {
3120                code: opcodes::OpNumEqualVerify::empty().expect("Should accept empty"),
3121                init: vec![vec![], vec![0, 0, 1], vec![0, 0, 1]],
3122                dstack: vec![vec![]],
3123            },
3124        ]);
3125
3126        run_error_test_cases(vec![
3127            ErrorTestCase {
3128                code: opcodes::OpEqualVerify::empty().expect("Should accept empty"),
3129                init: vec![vec![], vec![2, 0, 1], vec![1, 0, 1]],
3130                error: TxScriptError::VerifyError,
3131            },
3132            ErrorTestCase {
3133                code: opcodes::OpEqualVerify::empty().expect("Should accept empty"),
3134                init: vec![vec![1], vec![]],
3135                error: TxScriptError::VerifyError,
3136            },
3137            ErrorTestCase {
3138                code: opcodes::OpNumEqualVerify::empty().expect("Should accept empty"),
3139                init: vec![vec![], vec![2, 0, 1], vec![1, 0, 1]],
3140                error: TxScriptError::VerifyError,
3141            },
3142            ErrorTestCase {
3143                code: opcodes::OpNumEqualVerify::empty().expect("Should accept empty"),
3144                init: vec![vec![1], vec![]],
3145                error: TxScriptError::VerifyError,
3146            },
3147        ]);
3148    }
3149
3150    #[test]
3151    fn test_opsize() {
3152        run_success_test_cases(vec![
3153            TestCase {
3154                code: opcodes::OpSize::empty().expect("Should accept empty"),
3155                init: vec![vec![]],
3156                dstack: vec![vec![], vec![]],
3157            },
3158            TestCase {
3159                code: opcodes::OpSize::empty().expect("Should accept empty"),
3160                init: vec![vec![5]],
3161                dstack: vec![vec![5], vec![1]],
3162            },
3163            TestCase {
3164                code: opcodes::OpSize::empty().expect("Should accept empty"),
3165                init: vec![vec![0x80, 1]],
3166                dstack: vec![vec![0x80, 1], vec![2]],
3167            },
3168        ]);
3169
3170        run_error_test_cases(vec![ErrorTestCase {
3171            code: opcodes::OpSize::empty().expect("Should accept empty"),
3172            init: vec![],
3173            error: TxScriptError::InvalidStackOperation(1, 0),
3174        }]);
3175    }
3176
3177    #[test]
3178    fn test_opwithin() {
3179        run_success_test_cases(vec![
3180            TestCase {
3181                code: opcodes::OpWithin::empty().expect("Should accept empty"),
3182                init: vec![vec![], vec![], vec![1]],
3183                dstack: vec![vec![1]],
3184            },
3185            TestCase {
3186                code: opcodes::OpWithin::empty().expect("Should accept empty"),
3187                init: vec![vec![], vec![], vec![]],
3188                dstack: vec![vec![]],
3189            },
3190            TestCase {
3191                code: opcodes::OpWithin::empty().expect("Should accept empty"),
3192                init: vec![vec![0x81], vec![0x91], vec![1]],
3193                dstack: vec![vec![1]],
3194            },
3195        ]);
3196
3197        run_error_test_cases(vec![
3198            ErrorTestCase {
3199                code: opcodes::OpWithin::empty().expect("Should accept empty"),
3200                init: vec![vec![], vec![]],
3201                error: TxScriptError::InvalidStackOperation(3, 2),
3202            },
3203            ErrorTestCase {
3204                code: opcodes::OpWithin::empty().expect("Should accept empty"),
3205                init: vec![vec![]],
3206                error: TxScriptError::InvalidStackOperation(3, 1),
3207            },
3208            ErrorTestCase {
3209                code: opcodes::OpWithin::empty().expect("Should accept empty"),
3210                init: vec![],
3211                error: TxScriptError::InvalidStackOperation(3, 0),
3212            },
3213        ]);
3214    }
3215
3216    #[test]
3217    fn test_opsha256() {
3218        // Some test vectors from https://www.dlitz.net/crypto/shad256-test-vectors/
3219        run_success_test_cases(vec![
3220            TestCase {
3221                code: opcodes::OpSHA256::empty().expect("Should accept empty"),
3222                init: vec![vec![]],
3223                dstack: vec![b"\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55".to_vec()],
3224            },
3225            TestCase {
3226                code: opcodes::OpSHA256::empty().expect("Should accept empty"),
3227                init: vec![b"abc".to_vec()],
3228                dstack: vec![b"\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad".to_vec()],
3229            },
3230            TestCase {
3231                code: opcodes::OpSHA256::empty().expect("Should accept empty"),
3232                init: vec![b"\xde\x18\x89\x41\xa3\x37\x5d\x3a\x8a\x06\x1e\x67\x57\x6e\x92\x6d".to_vec()],
3233                dstack: vec![b"\x06\x7c\x53\x12\x69\x73\x5c\xa7\xf5\x41\xfd\xac\xa8\xf0\xdc\x76\x30\x5d\x3c\xad\xa1\x40\xf8\x93\x72\xa4\x10\xfe\x5e\xff\x6e\x4d".to_vec()],
3234            },
3235        ]);
3236
3237        run_error_test_cases(vec![ErrorTestCase {
3238            code: opcodes::OpSHA256::empty().expect("Should accept empty"),
3239            init: vec![],
3240            error: TxScriptError::InvalidStackOperation(1, 0),
3241        }]);
3242    }
3243
3244    #[test]
3245    fn test_opblake2b() {
3246        run_success_test_cases(vec![
3247            TestCase {
3248                code: opcodes::OpBlake2b::empty().expect("Should accept empty"),
3249                init: vec![b"".to_vec()],
3250                dstack: vec![b"\x0e\x57\x51\xc0\x26\xe5\x43\xb2\xe8\xab\x2e\xb0\x60\x99\xda\xa1\xd1\xe5\xdf\x47\x77\x8f\x77\x87\xfa\xab\x45\xcd\xf1\x2f\xe3\xa8".to_vec()],
3251            },
3252            TestCase {
3253                code: opcodes::OpBlake2b::empty().expect("Should accept empty"),
3254                init: vec![b"abc".to_vec()],
3255                dstack: vec![b"\xbd\xdd\x81\x3c\x63\x42\x39\x72\x31\x71\xef\x3f\xee\x98\x57\x9b\x94\x96\x4e\x3b\xb1\xcb\x3e\x42\x72\x62\xc8\xc0\x68\xd5\x23\x19".to_vec()],
3256            },
3257        ]);
3258
3259        run_error_test_cases(vec![ErrorTestCase {
3260            code: opcodes::OpBlake2b::empty().expect("Should accept empty"),
3261            init: vec![],
3262            error: TxScriptError::InvalidStackOperation(1, 0),
3263        }]);
3264    }
3265
3266    #[test]
3267    fn test_opnop() {
3268        run_success_test_cases(vec![TestCase {
3269            code: opcodes::OpNop::empty().expect("Should accept empty"),
3270            init: vec![vec![], vec![1], vec![2]],
3271            dstack: vec![vec![], vec![1], vec![2]],
3272        }]);
3273    }
3274
3275    #[derive(Clone)]
3276    struct VerifiableTransactionMock(Transaction);
3277
3278    impl VerifiableTransaction for VerifiableTransactionMock {
3279        fn tx(&self) -> &Transaction {
3280            &self.0
3281        }
3282
3283        fn populated_input(&self, _index: usize) -> (&TransactionInput, &UtxoEntry) {
3284            unimplemented!()
3285        }
3286        fn utxo(&self, _index: usize) -> Option<&UtxoEntry> {
3287            unimplemented!()
3288        }
3289    }
3290
3291    fn make_mock_transaction(lock_time: u64) -> (VerifiableTransactionMock, TransactionInput, UtxoEntry) {
3292        let dummy_prev_out = TransactionOutpoint::new(kaspa_hashes::Hash::from_u64_word(1), 1);
3293        let dummy_sig_script = vec![0u8; 65];
3294        let dummy_tx_input = TransactionInput::new(dummy_prev_out, dummy_sig_script, 10, 1);
3295        let addr_hash = vec![1u8; 32];
3296
3297        let addr = Address::new(Prefix::Testnet, Version::PubKey, &addr_hash);
3298        let dummy_script_public_key = pay_to_address_script(&addr);
3299        let dummy_tx_out = TransactionOutput::new(SOMPI_PER_KASPA, dummy_script_public_key);
3300
3301        let tx = VerifiableTransactionMock(Transaction::new(
3302            TX_VERSION + 1,
3303            vec![dummy_tx_input.clone()],
3304            vec![dummy_tx_out.clone()],
3305            lock_time,
3306            SUBNETWORK_ID_NATIVE,
3307            0,
3308            vec![],
3309        ));
3310        let utxo_entry = UtxoEntry::new(0, ScriptPublicKey::default(), 0, false, None);
3311        (tx, dummy_tx_input, utxo_entry)
3312    }
3313
3314    #[test]
3315    fn test_opchecklocktimeverify() {
3316        // Everything we need to build a script source
3317        let (base_tx, input, utxo_entry) = make_mock_transaction(1);
3318
3319        let sig_cache = Cache::new(10_000);
3320        let reused_values = SigHashReusedValuesUnsync::new();
3321        let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3322
3323        let code = opcodes::OpCheckLockTimeVerify::empty().expect("Should accept empty");
3324
3325        for (tx_lock_time, lock_time, should_fail) in [
3326            (1u64, vec![], false),                                // Case 1: 0 = locktime < txLockTime
3327            (0x800000, vec![0x7f, 0, 0], false),                  // Case 2: 0 < locktime < txLockTime
3328            (0x800000, vec![0x7f, 0, 0, 0, 0, 0, 0, 0, 0], true), // Case 3: locktime too big
3329            (LOCK_TIME_THRESHOLD * 2, vec![0x7f, 0, 0, 0], true), // Case 4: lock times are inconsistent
3330        ] {
3331            let mut tx = base_tx.clone();
3332            tx.0.lock_time = tx_lock_time;
3333            let mut vm = TxScriptEngine::from_transaction_input(&tx, &input, 0, &utxo_entry, ctx, Default::default());
3334            vm.dstack = vec![lock_time.clone()].into();
3335            match code.execute(&mut vm) {
3336                // Message is based on the should_fail values
3337                Ok(()) => assert!(
3338                    !should_fail,
3339                    "Opcode {} must fail (tx_lock_time: {}, lock_time: {:?})",
3340                    code.value(),
3341                    tx_lock_time,
3342                    lock_time
3343                ),
3344                Err(e) => assert!(
3345                    should_fail,
3346                    "Opcode {} should not fail. Got {} (tx_lock_time: {}, lock_time: {:?})",
3347                    code.value(),
3348                    e,
3349                    tx_lock_time,
3350                    lock_time
3351                ),
3352            }
3353        }
3354    }
3355
3356    #[test]
3357    fn test_opchecksequencerify() {
3358        // Everything we need to build a script source
3359        let (tx, base_input, utxo_entry) = make_mock_transaction(1);
3360
3361        let sig_cache = Cache::new(10_000);
3362        let reused_values = SigHashReusedValuesUnsync::new();
3363        let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3364
3365        let code = opcodes::OpCheckSequenceVerify::empty().expect("Should accept empty");
3366
3367        for (tx_sequence, sequence, should_fail) in [
3368            (1u64, vec![], false),                                // Case 1: 0 = sequence < tx_sequence
3369            (0x800000, vec![0x7f, 0, 0], false),                  // Case 2: 0 < sequence < tx_sequence
3370            (0x800000, vec![0x7f, 0, 0, 0, 0, 0, 0, 0, 0], true), // Case 3: sequence too big
3371            (1 << 63, vec![0x7f, 0, 0], true),                    // Case 4: disabled
3372            ((1 << 63) | 0xffff, vec![0x7f, 0, 0], true),         // Case 5: another disabled
3373        ] {
3374            let mut input = base_input.clone();
3375            input.sequence = tx_sequence;
3376            let mut vm = TxScriptEngine::from_transaction_input(&tx, &input, 0, &utxo_entry, ctx, Default::default());
3377            vm.dstack = vec![sequence.clone()].into();
3378            match code.execute(&mut vm) {
3379                // Message is based on the should_fail values
3380                Ok(()) => {
3381                    assert!(!should_fail, "Opcode {} must fail (tx_sequence: {}, sequence: {:?})", code.value(), tx_sequence, sequence)
3382                }
3383                Err(e) => assert!(
3384                    should_fail,
3385                    "Opcode {} should not fail. Got {} (tx_sequence: {}, sequence: {:?})",
3386                    code.value(),
3387                    e,
3388                    tx_sequence,
3389                    sequence
3390                ),
3391            }
3392        }
3393    }
3394
3395    #[test]
3396    fn test_opreturn() {
3397        run_error_test_cases(vec![ErrorTestCase {
3398            code: opcodes::OpReturn::empty().expect("Should accept empty"),
3399            init: vec![],
3400            error: TxScriptError::EarlyReturn,
3401        }]);
3402    }
3403
3404    #[test]
3405    fn test_opverify() {
3406        run_success_test_cases(vec![
3407            TestCase { code: opcodes::OpVerify::empty().expect("Should accept empty"), init: vec![vec![1]], dstack: vec![] },
3408            TestCase { code: opcodes::OpVerify::empty().expect("Should accept empty"), init: vec![vec![0x81]], dstack: vec![] },
3409            TestCase { code: opcodes::OpVerify::empty().expect("Should accept empty"), init: vec![vec![0x80, 0]], dstack: vec![] },
3410            TestCase {
3411                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3412                init: vec![vec![1, 0, 0, 0, 0]],
3413                dstack: vec![],
3414            },
3415        ]);
3416
3417        run_error_test_cases(vec![
3418            ErrorTestCase {
3419                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3420                init: vec![vec![0, 0, 0, 0x80]],
3421                error: TxScriptError::VerifyError,
3422            },
3423            ErrorTestCase {
3424                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3425                init: vec![vec![0, 0, 0, 0]],
3426                error: TxScriptError::VerifyError,
3427            },
3428            ErrorTestCase {
3429                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3430                init: vec![vec![0x80]],
3431                error: TxScriptError::VerifyError,
3432            },
3433            ErrorTestCase {
3434                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3435                init: vec![vec![0]],
3436                error: TxScriptError::VerifyError,
3437            },
3438            ErrorTestCase {
3439                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3440                init: vec![vec![]],
3441                error: TxScriptError::VerifyError,
3442            },
3443            ErrorTestCase {
3444                code: opcodes::OpVerify::empty().expect("Should accept empty"),
3445                init: vec![],
3446                error: TxScriptError::InvalidStackOperation(1, 0),
3447            },
3448        ])
3449    }
3450
3451    #[test]
3452    fn test_opifdup() {
3453        run_success_test_cases(vec![
3454            TestCase {
3455                code: opcodes::OpIfDup::empty().expect("Should accept empty"),
3456                init: vec![vec![1]],
3457                dstack: vec![vec![1], vec![1]],
3458            },
3459            TestCase {
3460                code: opcodes::OpIfDup::empty().expect("Should accept empty"),
3461                init: vec![vec![0x80, 0]],
3462                dstack: vec![vec![0x80, 0], vec![0x80, 0]],
3463            },
3464            TestCase { code: opcodes::OpIfDup::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![]] },
3465            TestCase {
3466                code: opcodes::OpIfDup::empty().expect("Should accept empty"),
3467                init: vec![vec![0x80]],
3468                dstack: vec![vec![0x80]],
3469            },
3470            TestCase {
3471                code: opcodes::OpIfDup::empty().expect("Should accept empty"),
3472                init: vec![vec![0, 0x80]],
3473                dstack: vec![vec![0, 0x80]],
3474            },
3475            TestCase { code: opcodes::OpIfDup::empty().expect("Should accept empty"), init: vec![vec![]], dstack: vec![vec![]] },
3476        ])
3477    }
3478
3479    mod kip10 {
3480        use super::*;
3481        use crate::{
3482            SpkEncoding,
3483            data_stack::OpcodeData,
3484            opcodes::{codes::*, push_number},
3485            pay_to_script_hash_script,
3486            script_builder::ScriptBuilder,
3487        };
3488        use kaspa_consensus_core::tx::MutableTransaction;
3489
3490        #[derive(Clone, Debug)]
3491        struct Kip10Mock {
3492            spk: ScriptPublicKey,
3493            amount: u64,
3494        }
3495
3496        fn create_mock_spk(value: u8) -> ScriptPublicKey {
3497            let pub_key = vec![value; 32];
3498            let addr = Address::new(Prefix::Testnet, Version::PubKey, &pub_key);
3499            pay_to_address_script(&addr)
3500        }
3501
3502        fn kip_10_tx_mock(inputs: Vec<Kip10Mock>, outputs: Vec<Kip10Mock>) -> (Transaction, Vec<UtxoEntry>) {
3503            let dummy_prev_out = TransactionOutpoint::new(kaspa_hashes::Hash::from_u64_word(1), 1);
3504            let dummy_sig_script = vec![0u8; 65];
3505            let (utxos, tx_inputs) = inputs
3506                .into_iter()
3507                .map(|Kip10Mock { spk, amount }| {
3508                    (
3509                        UtxoEntry::new(amount, spk, 0, false, None),
3510                        TransactionInput::new(dummy_prev_out, dummy_sig_script.clone(), 10, 0),
3511                    )
3512                })
3513                .unzip();
3514
3515            let tx_out = outputs.into_iter().map(|Kip10Mock { spk, amount }| TransactionOutput::new(amount, spk));
3516
3517            let tx = Transaction::new(TX_VERSION + 1, tx_inputs, tx_out.collect(), 0, SUBNETWORK_ID_NATIVE, 0, vec![]);
3518            (tx, utxos)
3519        }
3520
3521        #[derive(Debug)]
3522        struct TestGroup {
3523            name: &'static str,
3524            test_cases: Vec<TestCase>,
3525        }
3526
3527        #[derive(Debug)]
3528        enum Operation {
3529            InputSpk,
3530            OutputSpk,
3531            InputAmount,
3532            OutputAmount,
3533        }
3534
3535        #[derive(Debug)]
3536        enum TestCase {
3537            Successful { operation: Operation, index: i64, expected_result: ExpectedResult },
3538            Incorrect { operation: Operation, index: Option<i64>, expected_error: TxScriptError },
3539        }
3540
3541        #[derive(Debug)]
3542        struct ExpectedResult {
3543            expected_spk: Option<Vec<u8>>,
3544            expected_amount: Option<Vec<u8>>,
3545        }
3546
3547        fn execute_test_group(group: &TestGroup) {
3548            let input_spk1 = create_mock_spk(1);
3549            let input_spk2 = create_mock_spk(2);
3550            let output_spk1 = create_mock_spk(3);
3551            let output_spk2 = create_mock_spk(4);
3552
3553            let inputs = vec![
3554                Kip10Mock { spk: input_spk1.clone(), amount: 1111 },
3555                Kip10Mock { spk: input_spk2.clone(), amount: 2222 },
3556                Kip10Mock { spk: create_mock_spk(5), amount: 5555 },
3557            ];
3558            let outputs =
3559                vec![Kip10Mock { spk: output_spk1.clone(), amount: 3333 }, Kip10Mock { spk: output_spk2.clone(), amount: 4444 }];
3560
3561            let (tx, utxo_entries) = kip_10_tx_mock(inputs, outputs);
3562            let tx = PopulatedTransaction::new(&tx, utxo_entries);
3563            let sig_cache = Cache::new(10_000);
3564            let reused_values = SigHashReusedValuesUnsync::new();
3565            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3566
3567            for current_idx in 0..tx.inputs().len() {
3568                let mut vm = TxScriptEngine::from_transaction_input(
3569                    &tx,
3570                    &tx.inputs()[current_idx],
3571                    current_idx,
3572                    tx.utxo(current_idx).unwrap(),
3573                    ctx,
3574                    Default::default(),
3575                );
3576
3577                // Check input index opcode first
3578                let op_input_idx = opcodes::OpTxInputIndex::empty().expect("Should accept empty");
3579
3580                let mut expected = vm.dstack.clone();
3581                expected.push_item(current_idx as i64).unwrap();
3582                op_input_idx.execute(&mut vm).unwrap();
3583                assert_eq!(vm.dstack, expected);
3584                vm.dstack.clear();
3585
3586                // Prepare opcodes
3587                let op_input_spk = opcodes::OpTxInputSpk::empty().expect("Should accept empty");
3588                let op_output_spk = opcodes::OpTxOutputSpk::empty().expect("Should accept empty");
3589                let op_input_amount = opcodes::OpTxInputAmount::empty().expect("Should accept empty");
3590                let op_output_amount = opcodes::OpTxOutputAmount::empty().expect("Should accept empty");
3591
3592                // Execute each test case
3593                for test_case in &group.test_cases {
3594                    match test_case {
3595                        TestCase::Successful { operation, index, expected_result } => {
3596                            push_number(*index, &mut vm).unwrap();
3597                            let result = match operation {
3598                                Operation::InputSpk => op_input_spk.execute(&mut vm),
3599                                Operation::OutputSpk => op_output_spk.execute(&mut vm),
3600                                Operation::InputAmount => op_input_amount.execute(&mut vm),
3601                                Operation::OutputAmount => op_output_amount.execute(&mut vm),
3602                            };
3603                            assert!(result.is_ok());
3604
3605                            // Check the result matches expectations
3606                            if let Some(ref expected_spk) = expected_result.expected_spk {
3607                                assert_eq!(vm.dstack.inner(), vec![StackEntry::from_vec(expected_spk.clone())]);
3608                            }
3609                            if let Some(ref expected_amount) = expected_result.expected_amount {
3610                                assert_eq!(vm.dstack.inner(), vec![StackEntry::from_vec(expected_amount.clone())]);
3611                            }
3612                            vm.dstack.clear();
3613                        }
3614                        TestCase::Incorrect { operation, index, expected_error } => {
3615                            if let Some(idx) = index {
3616                                push_number(*idx, &mut vm).unwrap();
3617                            }
3618
3619                            let result = match operation {
3620                                Operation::InputSpk => op_input_spk.execute(&mut vm),
3621                                Operation::OutputSpk => op_output_spk.execute(&mut vm),
3622                                Operation::InputAmount => op_input_amount.execute(&mut vm),
3623                                Operation::OutputAmount => op_output_amount.execute(&mut vm),
3624                            };
3625
3626                            assert_eq!(result, Err(expected_error.clone()));
3627                            vm.dstack.clear();
3628                        }
3629                    }
3630                }
3631            }
3632        }
3633
3634        #[test]
3635        fn test_unary_introspection_ops() {
3636            let test_groups = vec![
3637                TestGroup {
3638                    name: "Valid input indices",
3639                    test_cases: vec![
3640                        TestCase::Successful {
3641                            operation: Operation::InputSpk,
3642                            index: 0,
3643                            expected_result: ExpectedResult {
3644                                expected_spk: Some(create_mock_spk(1).to_bytes()),
3645                                expected_amount: None,
3646                            },
3647                        },
3648                        TestCase::Successful {
3649                            operation: Operation::InputSpk,
3650                            index: 1,
3651                            expected_result: ExpectedResult {
3652                                expected_spk: Some(create_mock_spk(2).to_bytes()),
3653                                expected_amount: None,
3654                            },
3655                        },
3656                        TestCase::Successful {
3657                            operation: Operation::InputAmount,
3658                            index: 0,
3659                            expected_result: ExpectedResult {
3660                                expected_spk: None,
3661                                expected_amount: Some(OpcodeData::<i64>::serialize(&1111).unwrap()),
3662                            },
3663                        },
3664                        TestCase::Successful {
3665                            operation: Operation::InputAmount,
3666                            index: 1,
3667                            expected_result: ExpectedResult {
3668                                expected_spk: None,
3669                                expected_amount: Some(OpcodeData::<i64>::serialize(&2222).unwrap()),
3670                            },
3671                        },
3672                    ],
3673                },
3674                TestGroup {
3675                    name: "Valid output indices",
3676                    test_cases: vec![
3677                        TestCase::Successful {
3678                            operation: Operation::OutputSpk,
3679                            index: 0,
3680                            expected_result: ExpectedResult {
3681                                expected_spk: Some(create_mock_spk(3).to_bytes()),
3682                                expected_amount: None,
3683                            },
3684                        },
3685                        TestCase::Successful {
3686                            operation: Operation::OutputSpk,
3687                            index: 1,
3688                            expected_result: ExpectedResult {
3689                                expected_spk: Some(create_mock_spk(4).to_bytes()),
3690                                expected_amount: None,
3691                            },
3692                        },
3693                        TestCase::Successful {
3694                            operation: Operation::OutputAmount,
3695                            index: 0,
3696                            expected_result: ExpectedResult {
3697                                expected_spk: None,
3698                                expected_amount: Some(OpcodeData::<i64>::serialize(&3333).unwrap()),
3699                            },
3700                        },
3701                        TestCase::Successful {
3702                            operation: Operation::OutputAmount,
3703                            index: 1,
3704                            expected_result: ExpectedResult {
3705                                expected_spk: None,
3706                                expected_amount: Some(OpcodeData::<i64>::serialize(&4444).unwrap()),
3707                            },
3708                        },
3709                    ],
3710                },
3711                TestGroup {
3712                    name: "Error cases",
3713                    test_cases: vec![
3714                        TestCase::Incorrect {
3715                            operation: Operation::InputAmount,
3716                            index: None,
3717                            expected_error: TxScriptError::InvalidStackOperation(1, 0),
3718                        },
3719                        TestCase::Incorrect {
3720                            operation: Operation::InputAmount,
3721                            index: Some(-1),
3722                            expected_error: TxScriptError::InvalidInputIndex(-1, 3),
3723                        },
3724                        TestCase::Incorrect {
3725                            operation: Operation::InputAmount,
3726                            index: Some(3),
3727                            expected_error: TxScriptError::InvalidInputIndex(3, 3),
3728                        },
3729                        TestCase::Incorrect {
3730                            operation: Operation::OutputAmount,
3731                            index: None,
3732                            expected_error: TxScriptError::InvalidStackOperation(1, 0),
3733                        },
3734                        TestCase::Incorrect {
3735                            operation: Operation::OutputAmount,
3736                            index: Some(-1),
3737                            expected_error: TxScriptError::InvalidOutputIndex(-1, 2),
3738                        },
3739                        TestCase::Incorrect {
3740                            operation: Operation::OutputAmount,
3741                            index: Some(2),
3742                            expected_error: TxScriptError::InvalidOutputIndex(2, 2),
3743                        },
3744                        TestCase::Incorrect {
3745                            operation: Operation::OutputSpk,
3746                            index: Some(2),
3747                            expected_error: TxScriptError::InvalidOutputIndex(2, 2),
3748                        },
3749                    ],
3750                },
3751            ];
3752
3753            for group in test_groups {
3754                println!("Running test group: {}", group.name);
3755                execute_test_group(&group);
3756            }
3757        }
3758        fn create_mock_tx(input_count: usize, output_count: usize) -> (Transaction, Vec<UtxoEntry>) {
3759            let dummy_prev_out = TransactionOutpoint::new(kaspa_hashes::Hash::from_u64_word(1), 1);
3760            let dummy_sig_script = vec![0u8; 65];
3761
3762            // Create inputs with different SPKs and amounts
3763            let inputs: Vec<Kip10Mock> =
3764                (0..input_count).map(|i| Kip10Mock { spk: create_mock_spk(i as u8), amount: 1000 + i as u64 }).collect();
3765
3766            // Create outputs with different SPKs and amounts
3767            let outputs: Vec<Kip10Mock> =
3768                (0..output_count).map(|i| Kip10Mock { spk: create_mock_spk((100 + i) as u8), amount: 2000 + i as u64 }).collect();
3769
3770            let (utxos, tx_inputs): (Vec<_>, Vec<_>) = inputs
3771                .into_iter()
3772                .map(|Kip10Mock { spk, amount }| {
3773                    (
3774                        UtxoEntry::new(amount, spk, 0, false, None),
3775                        TransactionInput::new(dummy_prev_out, dummy_sig_script.clone(), 10, 0),
3776                    )
3777                })
3778                .unzip();
3779
3780            let tx_outputs: Vec<_> =
3781                outputs.into_iter().map(|Kip10Mock { spk, amount }| TransactionOutput::new(amount, spk)).collect();
3782
3783            let tx = Transaction::new(TX_VERSION + 1, tx_inputs, tx_outputs, 0, SUBNETWORK_ID_NATIVE, 0, vec![]);
3784
3785            (tx, utxos)
3786        }
3787
3788        #[test]
3789        fn test_op_input_output_count() {
3790            // Test cases with different input/output combinations
3791            let test_cases = vec![
3792                (1, 0), // Minimum inputs, no outputs
3793                (1, 1), // Minimum inputs, one output
3794                (1, 2), // Minimum inputs, multiple outputs
3795                (2, 1), // Multiple inputs, one output
3796                (3, 2), // Multiple inputs, multiple outputs
3797                (5, 3), // More inputs than outputs
3798                (2, 4), // More outputs than inputs
3799            ];
3800
3801            for (input_count, output_count) in test_cases {
3802                let (tx, utxo_entries) = create_mock_tx(input_count, output_count);
3803                let tx = PopulatedTransaction::new(&tx, utxo_entries);
3804                let sig_cache = Cache::new(10_000);
3805                let reused_values = SigHashReusedValuesUnsync::new();
3806                let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3807
3808                let mut vm = TxScriptEngine::from_transaction_input(
3809                    &tx,
3810                    &tx.inputs()[0], // Use first input
3811                    0,
3812                    tx.utxo(0).unwrap(),
3813                    ctx,
3814                    Default::default(),
3815                );
3816
3817                let op_input_count = opcodes::OpTxInputCount::empty().expect("Should accept empty");
3818                let op_output_count = opcodes::OpTxOutputCount::empty().expect("Should accept empty");
3819
3820                // Test input count
3821                op_input_count.execute(&mut vm).unwrap();
3822                assert_eq!(
3823                    vm.dstack.inner(),
3824                    vec![<StackEntry as OpcodeData<i64>>::serialize(&(input_count as i64)).unwrap()],
3825                    "Input count mismatch for {} inputs",
3826                    input_count
3827                );
3828                vm.dstack.clear();
3829
3830                // Test output count
3831                op_output_count.execute(&mut vm).unwrap();
3832                assert_eq!(
3833                    vm.dstack.inner(),
3834                    vec![<StackEntry as OpcodeData<i64>>::serialize(&(output_count as i64)).unwrap()],
3835                    "Output count mismatch for {} outputs",
3836                    output_count
3837                );
3838                vm.dstack.clear();
3839            }
3840        }
3841
3842        #[test]
3843        fn test_output_amount() {
3844            // Create script: 0 OP_OUTPUTAMOUNT 100 EQUAL
3845            let redeem_script = ScriptBuilder::new()
3846                .add_op(Op0)
3847                .unwrap()
3848                .add_op(OpTxOutputAmount)
3849                .unwrap()
3850                .add_i64(100)
3851                .unwrap()
3852                .add_op(OpEqual)
3853                .unwrap()
3854                .drain();
3855
3856            let spk = pay_to_script_hash_script(&redeem_script);
3857
3858            // Create transaction with output amount 100
3859            let input_mock = Kip10Mock { spk: spk.clone(), amount: 200 };
3860            let output_mock = Kip10Mock { spk: create_mock_spk(1), amount: 100 };
3861
3862            let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock.clone()], vec![output_mock]);
3863            let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
3864
3865            // Set signature script to push redeem script
3866            tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
3867
3868            let tx = tx.as_verifiable();
3869            let sig_cache = Cache::new(10_000);
3870            let reused_values = SigHashReusedValuesUnsync::new();
3871            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3872
3873            // Test success case
3874            {
3875                let mut vm =
3876                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
3877
3878                assert_eq!(vm.execute(), Ok(()));
3879            }
3880
3881            // Test failure case with wrong amount
3882            {
3883                let output_mock = Kip10Mock {
3884                    spk: create_mock_spk(1),
3885                    amount: 99, // Wrong amount
3886                };
3887                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock.clone()], vec![output_mock]);
3888                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
3889                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
3890
3891                let tx = tx.as_verifiable();
3892                let mut vm =
3893                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
3894
3895                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse));
3896            }
3897        }
3898
3899        #[test]
3900        fn test_input_amount() {
3901            // Create script: 0 OP_INPUTAMOUNT 200 EQUAL
3902            let redeem_script = ScriptBuilder::new()
3903                .add_op(Op0)
3904                .unwrap()
3905                .add_op(OpTxInputAmount)
3906                .unwrap()
3907                .add_i64(200)
3908                .unwrap()
3909                .add_op(OpEqual)
3910                .unwrap()
3911                .drain();
3912            let sig_cache = Cache::new(10_000);
3913            let reused_values = SigHashReusedValuesUnsync::new();
3914            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3915            let spk = pay_to_script_hash_script(&redeem_script);
3916
3917            // Test success case
3918            {
3919                let input_mock = Kip10Mock { spk: spk.clone(), amount: 200 };
3920                let output_mock = Kip10Mock { spk: create_mock_spk(1), amount: 100 };
3921
3922                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock], vec![output_mock]);
3923                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
3924                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
3925                let tx = tx.as_verifiable();
3926                let mut vm =
3927                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
3928
3929                assert_eq!(vm.execute(), Ok(()));
3930            }
3931
3932            // Test failure case
3933            {
3934                let input_mock = Kip10Mock {
3935                    spk: spk.clone(),
3936                    amount: 199, // Wrong amount
3937                };
3938                let output_mock = Kip10Mock { spk: create_mock_spk(1), amount: 100 };
3939
3940                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock], vec![output_mock]);
3941                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
3942                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
3943
3944                let tx = tx.as_verifiable();
3945                let mut vm =
3946                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
3947
3948                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse));
3949            }
3950        }
3951
3952        #[test]
3953        fn test_input_spk_basic() {
3954            let sig_cache = Cache::new(10_000);
3955            let reused_values = SigHashReusedValuesUnsync::new();
3956            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3957
3958            // Create script: 0 OP_INPUTSPK OpNop
3959            // Just verify that OpInputSpk pushes something onto stack
3960            let redeem_script = ScriptBuilder::new().add_ops(&[Op0, OpTxInputSpk, OpNop]).unwrap().drain();
3961            let spk = pay_to_script_hash_script(&redeem_script);
3962
3963            let (tx, utxo_entries) = kip_10_tx_mock(vec![Kip10Mock { spk, amount: 100 }], vec![]);
3964            let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
3965            tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
3966
3967            let tx = tx.as_verifiable();
3968            let mut vm = TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
3969
3970            // OpInputSpk should push input's SPK onto stack, making it non-empty
3971            assert_eq!(vm.execute(), Ok(()));
3972        }
3973
3974        #[test]
3975        fn test_input_spk_different() {
3976            let sig_cache = Cache::new(10_000);
3977            let reused_values = SigHashReusedValuesUnsync::new();
3978            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
3979
3980            // Create script: 0 OP_INPUTSPK 1 OP_INPUTSPK OP_EQUAL OP_NOT
3981            // Verifies that two different inputs have different SPKs
3982            let redeem_script = ScriptBuilder::new().add_ops(&[Op0, OpTxInputSpk, Op1, OpTxInputSpk, OpEqual, OpNot]).unwrap().drain();
3983            let spk = pay_to_script_hash_script(&redeem_script);
3984            let input_mock1 = Kip10Mock { spk, amount: 100 };
3985            let input_mock2 = Kip10Mock { spk: create_mock_spk(2), amount: 100 }; // Different SPK
3986
3987            let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock1, input_mock2], vec![]);
3988            let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
3989            tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
3990
3991            let tx = tx.as_verifiable();
3992            let mut vm = TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
3993
3994            // Should succeed because the SPKs are different
3995            assert_eq!(vm.execute(), Ok(()));
3996        }
3997
3998        #[test]
3999        fn test_input_spk_same() {
4000            let sig_cache = Cache::new(10_000);
4001            let reused_values = SigHashReusedValuesUnsync::new();
4002            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
4003
4004            // Create script: 0 OP_INPUTSPK 1 OP_INPUTSPK OP_EQUAL
4005            // Verifies that two inputs with same SPK are equal
4006            let redeem_script = ScriptBuilder::new().add_ops(&[Op0, OpTxInputSpk, Op1, OpTxInputSpk, OpEqual]).unwrap().drain();
4007
4008            let spk = pay_to_script_hash_script(&redeem_script);
4009            let input_mock1 = Kip10Mock { spk: spk.clone(), amount: 100 };
4010            let input_mock2 = Kip10Mock { spk, amount: 100 };
4011
4012            let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock1, input_mock2], vec![]);
4013            let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
4014            tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4015
4016            let tx = tx.as_verifiable();
4017            let mut vm = TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4018
4019            // Should succeed because both SPKs are identical
4020            assert_eq!(vm.execute(), Ok(()));
4021        }
4022
4023        #[test]
4024        fn test_output_spk() {
4025            // Create unique SPK to check
4026            let expected_spk = create_mock_spk(42);
4027            let expected_spk_bytes = expected_spk.to_bytes();
4028            let sig_cache = Cache::new(10_000);
4029            let reused_values = SigHashReusedValuesUnsync::new();
4030            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
4031            // Create script: 0 OP_OUTPUTSPK <expected_spk_bytes> EQUAL
4032            let redeem_script = ScriptBuilder::new()
4033                .add_op(Op0)
4034                .unwrap()
4035                .add_op(OpTxOutputSpk)
4036                .unwrap()
4037                .add_data(&expected_spk_bytes)
4038                .unwrap()
4039                .add_op(OpEqual)
4040                .unwrap()
4041                .drain();
4042
4043            let spk = pay_to_script_hash_script(&redeem_script);
4044
4045            // Test success case
4046            {
4047                let input_mock = Kip10Mock { spk: spk.clone(), amount: 200 };
4048                let output_mock = Kip10Mock { spk: expected_spk.clone(), amount: 100 };
4049
4050                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock], vec![output_mock]);
4051                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
4052                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4053
4054                let tx = tx.as_verifiable();
4055                let mut vm =
4056                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4057
4058                assert_eq!(vm.execute(), Ok(()));
4059            }
4060
4061            // Test failure case
4062            {
4063                let input_mock = Kip10Mock { spk: spk.clone(), amount: 200 };
4064                let output_mock = Kip10Mock {
4065                    spk: create_mock_spk(43), // Different SPK
4066                    amount: 100,
4067                };
4068
4069                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock], vec![output_mock]);
4070                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
4071                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4072
4073                let tx = tx.as_verifiable();
4074                let mut vm =
4075                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4076
4077                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse));
4078            }
4079        }
4080
4081        #[test]
4082        fn test_input_index() {
4083            // Create script: OP_INPUTINDEX 0 EQUAL
4084            let redeem_script =
4085                ScriptBuilder::new().add_op(OpTxInputIndex).unwrap().add_i64(0).unwrap().add_op(OpEqual).unwrap().drain();
4086
4087            let spk = pay_to_script_hash_script(&redeem_script);
4088            let sig_cache = Cache::new(10_000);
4089            let reused_values = SigHashReusedValuesUnsync::new();
4090            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
4091            // Test first input (success case)
4092            {
4093                let input_mock = Kip10Mock { spk: spk.clone(), amount: 200 };
4094                let output_mock = Kip10Mock { spk: create_mock_spk(1), amount: 100 };
4095
4096                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock], vec![output_mock]);
4097                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
4098                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4099
4100                let tx = tx.as_verifiable();
4101                let mut vm =
4102                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4103
4104                assert_eq!(vm.execute(), Ok(()));
4105            }
4106
4107            // Test second input (failure case)
4108            {
4109                let input_mock1 = Kip10Mock { spk: create_mock_spk(1), amount: 100 };
4110                let input_mock2 = Kip10Mock { spk: spk.clone(), amount: 200 };
4111                let output_mock = Kip10Mock { spk: create_mock_spk(2), amount: 100 };
4112
4113                let (tx, utxo_entries) = kip_10_tx_mock(vec![input_mock1, input_mock2], vec![output_mock]);
4114                let mut tx = MutableTransaction::with_entries(tx, utxo_entries);
4115                tx.tx.inputs[1].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4116
4117                let tx = tx.as_verifiable();
4118                let mut vm =
4119                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[1], 1, tx.utxo(1).unwrap(), ctx, Default::default());
4120
4121                // Should fail because script expects index 0 but we're at index 1
4122                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse));
4123            }
4124        }
4125
4126        #[test]
4127        fn test_counts() {
4128            let sig_cache = Cache::new(10_000);
4129            let reused_values = SigHashReusedValuesUnsync::new();
4130            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
4131            // Test OpInputCount: "OP_INPUTCOUNT 2 EQUAL"
4132            let input_count_script =
4133                ScriptBuilder::new().add_op(OpTxInputCount).unwrap().add_i64(2).unwrap().add_op(OpEqual).unwrap().drain();
4134
4135            // Test OpOutputCount: "OP_OUTPUTCOUNT 3 EQUAL"
4136            let output_count_script =
4137                ScriptBuilder::new().add_op(OpTxOutputCount).unwrap().add_i64(3).unwrap().add_op(OpEqual).unwrap().drain();
4138
4139            let input_spk = pay_to_script_hash_script(&input_count_script);
4140            let output_spk = pay_to_script_hash_script(&output_count_script);
4141
4142            // Create transaction with 2 inputs and 3 outputs
4143            let input_mock1 = Kip10Mock { spk: input_spk.clone(), amount: 100 };
4144            let input_mock2 = Kip10Mock { spk: output_spk.clone(), amount: 200 };
4145            let output_mock1 = Kip10Mock { spk: create_mock_spk(1), amount: 50 };
4146            let output_mock2 = Kip10Mock { spk: create_mock_spk(2), amount: 100 };
4147            let output_mock3 = Kip10Mock { spk: create_mock_spk(3), amount: 150 };
4148
4149            let (tx, utxo_entries) =
4150                kip_10_tx_mock(vec![input_mock1.clone(), input_mock2.clone()], vec![output_mock1, output_mock2, output_mock3]);
4151
4152            // Test InputCount
4153            {
4154                let mut tx = MutableTransaction::with_entries(tx.clone(), utxo_entries.clone());
4155                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&input_count_script).unwrap().drain();
4156
4157                let tx = tx.as_verifiable();
4158                let mut vm =
4159                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4160
4161                assert_eq!(vm.execute(), Ok(()));
4162            }
4163
4164            // Test OutputCount
4165            {
4166                let mut tx = MutableTransaction::with_entries(tx.clone(), utxo_entries.clone());
4167                tx.tx.inputs[1].signature_script = ScriptBuilder::new().add_data(&output_count_script).unwrap().drain();
4168
4169                let tx = tx.as_verifiable();
4170                let mut vm =
4171                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[1], 1, tx.utxo(1).unwrap(), ctx, Default::default());
4172
4173                assert_eq!(vm.execute(), Ok(()));
4174            }
4175
4176            // Test failure cases with wrong counts
4177            {
4178                // Wrong input count script: "OP_INPUTCOUNT 3 EQUAL"
4179                let wrong_input_count_script =
4180                    ScriptBuilder::new().add_op(OpTxInputCount).unwrap().add_i64(3).unwrap().add_op(OpEqual).unwrap().drain();
4181
4182                let mut tx = MutableTransaction::with_entries(tx.clone(), utxo_entries.clone());
4183                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&wrong_input_count_script).unwrap().drain();
4184
4185                let tx = tx.as_verifiable();
4186                let mut vm =
4187                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4188
4189                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse));
4190            }
4191
4192            {
4193                // Wrong output count script: "OP_OUTPUTCOUNT 2 EQUAL"
4194                let wrong_output_count_script =
4195                    ScriptBuilder::new().add_op(OpTxOutputCount).unwrap().add_i64(2).unwrap().add_op(OpEqual).unwrap().drain();
4196
4197                let mut tx = MutableTransaction::with_entries(tx.clone(), utxo_entries.clone());
4198                tx.tx.inputs[1].signature_script = ScriptBuilder::new().add_data(&wrong_output_count_script).unwrap().drain();
4199
4200                let tx = tx.as_verifiable();
4201                let mut vm =
4202                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[1], 1, tx.utxo(1).unwrap(), ctx, Default::default());
4203
4204                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse));
4205            }
4206        }
4207    }
4208
4209    #[cfg(test)]
4210    mod introspection {
4211        use super::*;
4212        use crate::covenants::CovenantsContext;
4213        use crate::script_builder::{ScriptBuilder, ScriptBuilderResult};
4214        use crate::{EngineCtx, MAX_SCRIPT_ELEMENT_SIZE, pay_to_script_hash_script};
4215        use crate::{SpkEncoding, opcodes::codes};
4216        use kaspa_consensus_core::hashing::sighash::SigHashReusedValuesUnsync;
4217        use kaspa_consensus_core::subnets::SubnetworkId;
4218        use kaspa_consensus_core::tx::{
4219            CovenantBinding, MutableTransaction, PopulatedTransaction, ScriptPublicKey, Transaction, TransactionInput,
4220            TransactionOutpoint, TransactionOutput, UtxoEntry,
4221        };
4222        use kaspa_hashes::{Hash, ZERO_HASH};
4223        use kaspa_txscript_errors::CovenantsError;
4224
4225        fn payload_bytes(len: usize) -> Vec<u8> {
4226            (0..len).map(|i| (i % 256) as u8).collect()
4227        }
4228
4229        fn base_transaction(payload_len: usize) -> (Transaction, Vec<UtxoEntry>) {
4230            let version: u16 = 5;
4231            let lock_time: u64 = 123;
4232            let subnetwork_id = SubnetworkId::from_bytes([9u8; 20]);
4233            let gas: u64 = 777;
4234            let payload = payload_bytes(payload_len);
4235
4236            let sig_script_0 = ScriptBuilder::new().add_data(&[0xaa, 0xbb, 0xcc]).expect("sig script build").drain();
4237            let sig_script_1 = ScriptBuilder::new().add_data(&[0x11]).expect("sig script build").drain();
4238
4239            let inputs = vec![
4240                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), sig_script_0, 0x1122334455667788, 0),
4241                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 1), sig_script_1, 0x0fedcba987654321, 0),
4242            ];
4243
4244            let output_spk_0 = ScriptBuilder::new().add_ops(&[codes::OpTrue, codes::Op2, codes::Op3]).expect("spk build").drain();
4245            let output_spk_1 = ScriptBuilder::new().add_ops(&[codes::Op4, codes::Op5]).expect("spk build").drain();
4246
4247            let outputs = vec![
4248                TransactionOutput::new(11, ScriptPublicKey::new(0, output_spk_0.into())),
4249                TransactionOutput::new(22, ScriptPublicKey::new(0, output_spk_1.into())),
4250            ];
4251
4252            let mut tx = Transaction::new(version, inputs, outputs, lock_time, subnetwork_id, gas, payload);
4253            tx.finalize();
4254
4255            let utxo_spk_0 = ScriptBuilder::new().add_ops(&[codes::OpTrue, codes::Op1]).expect("spk build").drain();
4256            let utxo_spk_1 = ScriptBuilder::new().add_ops(&[codes::Op2, codes::Op3]).expect("spk build").drain();
4257
4258            let entries = vec![
4259                UtxoEntry::new(1000, ScriptPublicKey::new(0, utxo_spk_0.into()), 0, true, None),
4260                UtxoEntry::new(2000, ScriptPublicKey::new(0, utxo_spk_1.into()), 0, false, None),
4261            ];
4262
4263            (tx, entries)
4264        }
4265
4266        fn create_transaction_with_covenant() -> (Transaction, Vec<UtxoEntry>) {
4267            let version: u16 = 5;
4268            let lock_time: u64 = 0;
4269            let subnetwork_id = SubnetworkId::from_bytes([9u8; 20]);
4270            let gas: u64 = 0;
4271            let payload = payload_bytes(0);
4272
4273            let sig_script = ScriptBuilder::new().add_op(codes::OpTrue).expect("sig script build").drain();
4274            let inputs = vec![
4275                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), sig_script.clone(), 0, 0),
4276                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 1), sig_script.clone(), 0, 0),
4277                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 2), sig_script.clone(), 0, 0),
4278                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 3), sig_script.clone(), 0, 0),
4279                TransactionInput::new(TransactionOutpoint::new(Hash::default(), 4), sig_script, 0, 0),
4280            ];
4281
4282            let spk = ScriptBuilder::new().add_op(codes::OpTrue).expect("spk build").drain();
4283            let outputs = vec![
4284                TransactionOutput {
4285                    value: 11,
4286                    script_public_key: ScriptPublicKey::new(0, spk.clone().into()),
4287                    covenant: Some(CovenantBinding { authorizing_input: 0, covenant_id: Hash::from_u64_word(1) }),
4288                },
4289                TransactionOutput {
4290                    value: 22,
4291                    script_public_key: ScriptPublicKey::new(0, spk.clone().into()),
4292                    covenant: Some(CovenantBinding { authorizing_input: 3, covenant_id: Hash::from_u64_word(1) }),
4293                },
4294                TransactionOutput {
4295                    value: 33,
4296                    script_public_key: ScriptPublicKey::new(0, spk.clone().into()),
4297                    covenant: Some(CovenantBinding { authorizing_input: 1, covenant_id: Hash::from_u64_word(2) }),
4298                },
4299                TransactionOutput::new(44, ScriptPublicKey::new(0, spk.clone().into())),
4300                TransactionOutput {
4301                    value: 55,
4302                    script_public_key: ScriptPublicKey::new(0, spk.clone().into()),
4303                    covenant: Some(CovenantBinding { authorizing_input: 3, covenant_id: Hash::from_u64_word(1) }),
4304                },
4305                TransactionOutput {
4306                    value: 66,
4307                    script_public_key: ScriptPublicKey::new(0, spk.into()),
4308                    covenant: Some(CovenantBinding { authorizing_input: 4, covenant_id: Hash::from_u64_word(2) }),
4309                },
4310            ];
4311
4312            let mut tx = Transaction::new(version, inputs, outputs, lock_time, subnetwork_id, gas, payload);
4313            tx.finalize();
4314
4315            let utxo_spk = ScriptBuilder::new().add_op(codes::OpTrue).expect("spk build").drain();
4316            let entries = vec![
4317                UtxoEntry::new(1000, ScriptPublicKey::new(0, utxo_spk.clone().into()), 0, false, Some(Hash::from_u64_word(1))),
4318                UtxoEntry::new(1000, ScriptPublicKey::new(0, utxo_spk.clone().into()), 0, false, Some(Hash::from_u64_word(2))),
4319                UtxoEntry::new(1000, ScriptPublicKey::new(0, utxo_spk.clone().into()), 0, false, Some(Hash::from_u64_word(1))),
4320                UtxoEntry::new(1000, ScriptPublicKey::new(0, utxo_spk.clone().into()), 0, false, Some(Hash::from_u64_word(1))),
4321                UtxoEntry::new(1000, ScriptPublicKey::new(0, utxo_spk.into()), 0, false, Some(Hash::from_u64_word(2))),
4322            ];
4323
4324            (tx, entries)
4325        }
4326
4327        fn script<F>(build: F) -> Vec<u8>
4328        where
4329            F: FnOnce(&mut ScriptBuilder) -> ScriptBuilderResult<&mut ScriptBuilder>,
4330        {
4331            let mut sb = ScriptBuilder::new();
4332            // Add a drop at the start to remove the initial sigscript data
4333            sb.add_op(codes::OpDrop).expect("builder failure");
4334            build(&mut sb).expect("builder failure");
4335            sb.drain()
4336        }
4337
4338        fn run_script(tx: &Transaction, mut entries: Vec<UtxoEntry>, idx: usize, script: Vec<u8>) -> Result<(), TxScriptError> {
4339            entries[idx].script_public_key = ScriptPublicKey::new(0, script.into());
4340            let populated_tx = PopulatedTransaction::new(tx, entries);
4341            let sig_cache = Cache::new(10_000);
4342            let reused_values = SigHashReusedValuesUnsync::new();
4343            let covenants_ctx = CovenantsContext::from_tx(&populated_tx)?;
4344            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values).with_covenants_ctx(&covenants_ctx);
4345            let mut vm = TxScriptEngine::from_transaction_input(
4346                &populated_tx,
4347                &populated_tx.tx.inputs[idx],
4348                idx,
4349                &populated_tx.entries[idx],
4350                ctx,
4351                Default::default(),
4352            );
4353            vm.execute()
4354        }
4355
4356        #[test]
4357        fn tx_level_introspection() {
4358            let (tx, entries) = base_transaction(100);
4359            let (tx_large, entries_large) = base_transaction(600);
4360            let expected_subnet: Vec<u8> = tx.subnetwork_id.into();
4361
4362            let spk_version = script(|sb| sb.add_op(codes::OpTxVersion)?.add_i64(tx.version as i64)?.add_op(codes::OpEqual));
4363            run_script(&tx, entries.clone(), 0, spk_version).expect("tx version");
4364
4365            let spk_lock_time = script(|sb| sb.add_op(codes::OpTxLockTime)?.add_i64(tx.lock_time as i64)?.add_op(codes::OpEqual));
4366            run_script(&tx, entries.clone(), 0, spk_lock_time).expect("tx locktime");
4367
4368            let spk_subnet = script(|sb| sb.add_op(codes::OpTxSubnetId)?.add_data(&expected_subnet)?.add_op(codes::OpEqual));
4369            run_script(&tx, entries.clone(), 0, spk_subnet).expect("tx subnet id");
4370
4371            let spk_gas = script(|sb| sb.add_op(codes::OpTxGas)?.add_i64(tx.gas as i64)?.add_op(codes::OpEqual));
4372            run_script(&tx, entries.clone(), 0, spk_gas).expect("tx gas");
4373
4374            let spk_payload_len =
4375                script(|sb| sb.add_op(codes::OpTxPayloadLen)?.add_i64(tx.payload.len() as i64)?.add_op(codes::OpEqual));
4376            run_script(&tx, entries.clone(), 0, spk_payload_len).expect("tx payload len");
4377
4378            let expected_payload_full = tx.payload.clone();
4379            let spk_payload_substr_full = script(|sb| {
4380                sb.add_i64(0)?
4381                    .add_op(codes::OpTxPayloadLen)?
4382                    .add_op(codes::OpTxPayloadSubstr)?
4383                    .add_data(&expected_payload_full)?
4384                    .add_op(codes::OpEqual)
4385            });
4386            run_script(&tx, entries.clone(), 0, spk_payload_substr_full).expect("payload substr full");
4387
4388            let expected_substr = tx.payload[1..4].to_vec();
4389            let spk_payload_substr = script(|sb| {
4390                sb.add_i64(1)?.add_i64(4)?.add_op(codes::OpTxPayloadSubstr)?.add_data(&expected_substr)?.add_op(codes::OpEqual)
4391            });
4392            run_script(&tx, entries.clone(), 0, spk_payload_substr).expect("payload substr ok");
4393
4394            let spk_payload_substr_oob = script(|sb| sb.add_i64(1000)?.add_i64(1002)?.add_op(codes::OpTxPayloadSubstr));
4395            let err =
4396                run_script(&tx_large, entries_large.clone(), 0, spk_payload_substr_oob).expect_err("payload substr out of bounds");
4397            assert!(matches!(err, TxScriptError::OutOfBoundsSubstring(_, _, _)));
4398
4399            let spk_payload_substr_too_long =
4400                script(|sb| sb.add_i64(0)?.add_i64(MAX_SCRIPT_ELEMENT_SIZE as i64 + 1)?.add_op(codes::OpTxPayloadSubstr));
4401            let err = run_script(&tx_large, entries_large.clone(), 0, spk_payload_substr_too_long).expect_err("payload substr >520");
4402            assert!(matches!(err, TxScriptError::ElementTooBig(_, _)));
4403        }
4404
4405        #[test]
4406        fn input_input_output_introspection() {
4407            let (tx, entries) = base_transaction(40);
4408            let input_spk_bytes_1 = entries[1].script_public_key.to_bytes();
4409            let out_spk_bytes_0 = tx.outputs[0].script_public_key.to_bytes();
4410            let sig_script_0 = tx.inputs[0].signature_script.clone();
4411
4412            let spk_input_spk_len = script(|sb| {
4413                sb.add_i64(1)?.add_op(codes::OpTxInputSpkLen)?.add_i64(input_spk_bytes_1.len() as i64)?.add_op(codes::OpEqual)
4414            });
4415            run_script(&tx, entries.clone(), 0, spk_input_spk_len).expect("input spk len");
4416
4417            let expected_spk_full = input_spk_bytes_1.clone();
4418            let spk_input_spk_substr_full = script(|sb| {
4419                sb.add_i64(1)?
4420                    .add_i64(0)?
4421                    .add_i64(1)?
4422                    .add_op(codes::OpTxInputSpkLen)?
4423                    .add_op(codes::OpTxInputSpkSubstr)?
4424                    .add_data(&expected_spk_full)?
4425                    .add_op(codes::OpEqual)
4426            });
4427            run_script(&tx, entries.clone(), 0, spk_input_spk_substr_full).expect("input spk substr full");
4428
4429            let expected_spk_suffix = input_spk_bytes_1[1..].to_vec();
4430            let spk_input_spk_substr_suffix = script(|sb| {
4431                sb.add_i64(1)?
4432                    .add_i64(1)?
4433                    .add_i64(1)?
4434                    .add_op(codes::OpTxInputSpkLen)?
4435                    .add_op(codes::OpTxInputSpkSubstr)?
4436                    .add_data(&expected_spk_suffix)?
4437                    .add_op(codes::OpEqual)
4438            });
4439            run_script(&tx, entries.clone(), 0, spk_input_spk_substr_suffix).expect("input spk substr suffix");
4440
4441            let spk_input_spk_substr_oob = script(|sb| sb.add_i64(1)?.add_i64(100)?.add_i64(101)?.add_op(codes::OpTxInputSpkSubstr));
4442            let err = run_script(&tx, entries.clone(), 0, spk_input_spk_substr_oob).expect_err("input spk substr oob");
4443            assert!(matches!(err, TxScriptError::OutOfBoundsSubstring(_, _, _)));
4444
4445            let spk_output_spk_len = script(|sb| {
4446                sb.add_i64(0)?.add_op(codes::OpTxOutputSpkLen)?.add_i64(out_spk_bytes_0.len() as i64)?.add_op(codes::OpEqual)
4447            });
4448            run_script(&tx, entries.clone(), 0, spk_output_spk_len).expect("output spk len");
4449
4450            let expected_out_spk_full = out_spk_bytes_0.clone();
4451            let spk_output_spk_substr_full = script(|sb| {
4452                sb.add_i64(0)?
4453                    .add_i64(0)?
4454                    .add_i64(0)?
4455                    .add_op(codes::OpTxOutputSpkLen)?
4456                    .add_op(codes::OpTxOutputSpkSubstr)?
4457                    .add_data(&expected_out_spk_full)?
4458                    .add_op(codes::OpEqual)
4459            });
4460            run_script(&tx, entries.clone(), 0, spk_output_spk_substr_full).expect("output spk substr full");
4461
4462            let expected_out_spk_tail = out_spk_bytes_0[1..3].to_vec();
4463            let spk_output_spk_substr_tail = script(|sb| {
4464                sb.add_i64(0)?
4465                    .add_i64(1)?
4466                    .add_i64(3)?
4467                    .add_op(codes::OpTxOutputSpkSubstr)?
4468                    .add_data(&expected_out_spk_tail)?
4469                    .add_op(codes::OpEqual)
4470            });
4471            run_script(&tx, entries.clone(), 0, spk_output_spk_substr_tail).expect("output spk substr tail");
4472
4473            let spk_input_sig_len = script(|sb| {
4474                sb.add_i64(0)?.add_op(codes::OpTxInputScriptSigLen)?.add_i64(sig_script_0.len() as i64)?.add_op(codes::OpEqual)
4475            });
4476            run_script(&tx, entries.clone(), 0, spk_input_sig_len).expect("input sig len");
4477
4478            let expected_sig_full = sig_script_0.clone();
4479            let spk_input_sig_substr_full = script(|sb| {
4480                sb.add_i64(0)?
4481                    .add_i64(0)?
4482                    .add_i64(0)?
4483                    .add_op(codes::OpTxInputScriptSigLen)?
4484                    .add_op(codes::OpTxInputScriptSigSubstr)?
4485                    .add_data(&expected_sig_full)?
4486                    .add_op(codes::OpEqual)
4487            });
4488            run_script(&tx, entries.clone(), 0, spk_input_sig_substr_full).expect("input sig substr full");
4489
4490            let expected_sig_substr = sig_script_0[1..3].to_vec();
4491            let spk_input_sig_substr = script(|sb| {
4492                sb.add_i64(0)?
4493                    .add_i64(1)?
4494                    .add_i64(3)?
4495                    .add_op(codes::OpTxInputScriptSigSubstr)?
4496                    .add_data(&expected_sig_substr)?
4497                    .add_op(codes::OpEqual)
4498            });
4499            run_script(&tx, entries.clone(), 0, spk_input_sig_substr).expect("input sig substr");
4500
4501            let spk_outpoint_txid = script(|sb| {
4502                sb.add_i64(1)?
4503                    .add_op(codes::OpOutpointTxId)?
4504                    .add_data(&tx.inputs[1].previous_outpoint.transaction_id.as_bytes())?
4505                    .add_op(codes::OpEqual)
4506            });
4507            run_script(&tx, entries.clone(), 0, spk_outpoint_txid).expect("outpoint txid");
4508
4509            let spk_outpoint_index = script(|sb| sb.add_i64(1)?.add_op(codes::OpOutpointIndex)?.add_i64(1)?.add_op(codes::OpEqual));
4510            run_script(&tx, entries.clone(), 0, spk_outpoint_index).expect("outpoint index");
4511
4512            let seq_bytes = tx.inputs[0].sequence.to_le_bytes();
4513            let spk_seq = script(|sb| sb.add_i64(0)?.add_op(codes::OpTxInputSeq)?.add_data(&seq_bytes)?.add_op(codes::OpEqual));
4514            run_script(&tx, entries.clone(), 0, spk_seq).expect("input seq");
4515
4516            let spk_is_coinbase_true =
4517                script(|sb| sb.add_i64(0)?.add_op(codes::OpTxInputIsCoinbase)?.add_i64(1)?.add_op(codes::OpEqual));
4518            run_script(&tx, entries.clone(), 0, spk_is_coinbase_true).expect("is coinbase true");
4519
4520            let spk_is_coinbase_false =
4521                script(|sb| sb.add_i64(1)?.add_op(codes::OpTxInputIsCoinbase)?.add_i64(0)?.add_op(codes::OpEqual));
4522            run_script(&tx, entries, 0, spk_is_coinbase_false).expect("is coinbase false");
4523        }
4524
4525        #[test]
4526        fn cov_output_count() {
4527            let (tx, entries) = create_transaction_with_covenant();
4528
4529            for (input_idx, expected_count) in [(0, 1), (1, 1), (2, 0), (3, 2), (4, 1)] {
4530                let spk = script(|sb| {
4531                    sb.add_i64(input_idx)?.add_op(codes::OpAuthOutputCount)?.add_i64(expected_count)?.add_op(codes::OpEqual)
4532                });
4533                run_script(&tx, entries.clone(), 0, spk).expect("cov output count");
4534            }
4535
4536            let spk_invalid = script(|sb| sb.add_i64(7)?.add_op(codes::OpAuthOutputCount));
4537            let err = run_script(&tx, entries, 0, spk_invalid).expect_err("cov output count invalid input");
4538            assert!(matches!(err, TxScriptError::InvalidInputIndex(7, 5)));
4539        }
4540
4541        #[test]
4542        fn auth_output_idx() {
4543            let (tx, entries) = create_transaction_with_covenant();
4544
4545            for (input_idx, authorized_idx, expected_output_idx) in [(0, 0, 0), (1, 0, 2), (3, 0, 1), (3, 1, 4), (4, 0, 5)] {
4546                let spk = script(|sb| {
4547                    sb.add_i64(input_idx)?
4548                        .add_i64(authorized_idx)?
4549                        .add_op(codes::OpAuthOutputIdx)?
4550                        .add_i64(expected_output_idx)?
4551                        .add_op(codes::OpEqual)
4552                });
4553                run_script(&tx, entries.clone(), 0, spk).unwrap();
4554            }
4555
4556            let spk_missing = script(|sb| sb.add_i64(0)?.add_i64(2)?.add_op(codes::OpAuthOutputIdx));
4557            let err = run_script(&tx, entries, 0, spk_missing).expect_err("cov output idx missing");
4558            assert!(matches!(err, TxScriptError::CovenantsError(CovenantsError::InvalidAuthCovOutIndex(2, 0, 1))));
4559        }
4560
4561        #[test]
4562        fn covenant_id_opcodes() {
4563            let (tx, entries) = create_transaction_with_covenant();
4564            let covenant_id_1 = Hash::from_u64_word(1);
4565            let covenant_id_2 = Hash::from_u64_word(2);
4566            let covenant_id_3 = Hash::from_u64_word(3);
4567
4568            // OpInputCovenantId for inputs with covenant ids
4569            let spk_input_cov_id_0 = script(|sb| {
4570                sb.add_i64(0)?.add_op(codes::OpInputCovenantId)?.add_data(&covenant_id_1.as_bytes())?.add_op(codes::OpEqual)
4571            });
4572            run_script(&tx, entries.clone(), 0, spk_input_cov_id_0).unwrap();
4573
4574            let spk_input_cov_id_1 = script(|sb| {
4575                sb.add_i64(1)?.add_op(codes::OpInputCovenantId)?.add_data(&covenant_id_2.as_bytes())?.add_op(codes::OpEqual)
4576            });
4577            run_script(&tx, entries.clone(), 0, spk_input_cov_id_1).unwrap();
4578
4579            let spk_input_cov_id_3 = script(|sb| {
4580                sb.add_i64(3)?.add_op(codes::OpInputCovenantId)?.add_data(&covenant_id_1.as_bytes())?.add_op(codes::OpEqual)
4581            });
4582            run_script(&tx, entries.clone(), 0, spk_input_cov_id_3).unwrap();
4583
4584            // OpOutputCovenantId for outputs with covenant ids
4585            let spk_output_cov_id_0 = script(|sb| {
4586                sb.add_i64(0)?.add_op(codes::OpOutputCovenantId)?.add_data(&covenant_id_1.as_bytes())?.add_op(codes::OpEqual)
4587            });
4588            run_script(&tx, entries.clone(), 0, spk_output_cov_id_0).unwrap();
4589
4590            let spk_output_cov_id_2 = script(|sb| {
4591                sb.add_i64(2)?.add_op(codes::OpOutputCovenantId)?.add_data(&covenant_id_2.as_bytes())?.add_op(codes::OpEqual)
4592            });
4593            run_script(&tx, entries.clone(), 0, spk_output_cov_id_2).unwrap();
4594
4595            // OpOutputAuthorizingInput for outputs with covenant bindings
4596            let spk_output_auth_input_0 =
4597                script(|sb| sb.add_i64(0)?.add_op(codes::OpOutputAuthorizingInput)?.add_i64(0)?.add_op(codes::OpEqual));
4598            run_script(&tx, entries.clone(), 0, spk_output_auth_input_0).unwrap();
4599
4600            let spk_output_auth_input_1 =
4601                script(|sb| sb.add_i64(1)?.add_op(codes::OpOutputAuthorizingInput)?.add_i64(3)?.add_op(codes::OpEqual));
4602            run_script(&tx, entries.clone(), 0, spk_output_auth_input_1).unwrap();
4603
4604            // OpCovInputCount
4605            let spk_cov_in_count_1 = script(|sb| {
4606                sb.add_data(&covenant_id_1.as_bytes())?.add_op(codes::OpCovInputCount)?.add_i64(3)?.add_op(codes::OpEqual)
4607            });
4608            run_script(&tx, entries.clone(), 0, spk_cov_in_count_1).unwrap();
4609
4610            let spk_cov_in_count_2 = script(|sb| {
4611                sb.add_data(&covenant_id_2.as_bytes())?.add_op(codes::OpCovInputCount)?.add_i64(2)?.add_op(codes::OpEqual)
4612            });
4613            run_script(&tx, entries.clone(), 0, spk_cov_in_count_2).unwrap();
4614
4615            let spk_cov_in_count_0 = script(|sb| {
4616                sb.add_data(&covenant_id_3.as_bytes())?.add_op(codes::OpCovInputCount)?.add_i64(0)?.add_op(codes::OpEqual)
4617            });
4618            run_script(&tx, entries.clone(), 0, spk_cov_in_count_0).unwrap();
4619
4620            // OpCovInputIdx
4621            let spk_cov_in_idx_0 = script(|sb| {
4622                sb.add_data(&covenant_id_1.as_bytes())?.add_i64(0)?.add_op(codes::OpCovInputIdx)?.add_i64(0)?.add_op(codes::OpEqual)
4623            });
4624            run_script(&tx, entries.clone(), 0, spk_cov_in_idx_0).unwrap();
4625
4626            let spk_cov_in_idx_3 = script(|sb| {
4627                sb.add_data(&covenant_id_1.as_bytes())?.add_i64(2)?.add_op(codes::OpCovInputIdx)?.add_i64(3)?.add_op(codes::OpEqual)
4628            });
4629            run_script(&tx, entries.clone(), 0, spk_cov_in_idx_3).unwrap();
4630
4631            let spk_cov_in_idx_oob = script(|sb| sb.add_data(&covenant_id_1.as_bytes())?.add_i64(3)?.add_op(codes::OpCovInputIdx));
4632            let err = run_script(&tx, entries.clone(), 0, spk_cov_in_idx_oob).expect_err("cov input idx oob");
4633            assert!(matches!(err, TxScriptError::CovenantsError(CovenantsError::InvalidCovInIndex(_, _))));
4634
4635            // OpCovOutputCount
4636            let spk_cov_out_count_1 = script(|sb| {
4637                sb.add_data(&covenant_id_1.as_bytes())?.add_op(codes::OpCovOutputCount)?.add_i64(3)?.add_op(codes::OpEqual)
4638            });
4639            run_script(&tx, entries.clone(), 0, spk_cov_out_count_1).unwrap();
4640
4641            let spk_cov_out_count_2 = script(|sb| {
4642                sb.add_data(&covenant_id_2.as_bytes())?.add_op(codes::OpCovOutputCount)?.add_i64(2)?.add_op(codes::OpEqual)
4643            });
4644            run_script(&tx, entries.clone(), 0, spk_cov_out_count_2).unwrap();
4645            let spk_cov_out_count_0 = script(|sb| {
4646                sb.add_data(&covenant_id_3.as_bytes())?.add_op(codes::OpCovOutputCount)?.add_i64(0)?.add_op(codes::OpEqual)
4647            });
4648            run_script(&tx, entries.clone(), 0, spk_cov_out_count_0).unwrap();
4649
4650            // OpCovOutputIdx
4651            let spk_cov_out_idx_0 = script(|sb| {
4652                sb.add_data(&covenant_id_1.as_bytes())?.add_i64(0)?.add_op(codes::OpCovOutputIdx)?.add_i64(0)?.add_op(codes::OpEqual)
4653            });
4654            run_script(&tx, entries.clone(), 0, spk_cov_out_idx_0).unwrap();
4655
4656            let spk_cov_out_idx_4 = script(|sb| {
4657                sb.add_data(&covenant_id_1.as_bytes())?.add_i64(2)?.add_op(codes::OpCovOutputIdx)?.add_i64(4)?.add_op(codes::OpEqual)
4658            });
4659            run_script(&tx, entries.clone(), 0, spk_cov_out_idx_4).unwrap();
4660
4661            let spk_cov_out_idx_5 = script(|sb| {
4662                sb.add_data(&covenant_id_2.as_bytes())?.add_i64(1)?.add_op(codes::OpCovOutputIdx)?.add_i64(5)?.add_op(codes::OpEqual)
4663            });
4664            run_script(&tx, entries.clone(), 0, spk_cov_out_idx_5).unwrap();
4665
4666            let spk_cov_out_idx_oob = script(|sb| sb.add_data(&covenant_id_1.as_bytes())?.add_i64(3)?.add_op(codes::OpCovOutputIdx));
4667            let err = run_script(&tx, entries.clone(), 0, spk_cov_out_idx_oob).expect_err("cov out idx oob");
4668            assert!(matches!(err, TxScriptError::CovenantsError(CovenantsError::InvalidCovOutIndex(_, _))));
4669
4670            let spk_output_cov_id_oob = script(|sb| sb.add_i64(6)?.add_op(codes::OpOutputCovenantId));
4671            let err = run_script(&tx, entries.clone(), 0, spk_output_cov_id_oob).expect_err("output covenant id oob");
4672            assert_eq!(err, TxScriptError::InvalidOutputIndex(6, 6));
4673
4674            let spk_output_auth_input_oob = script(|sb| sb.add_i64(6)?.add_op(codes::OpOutputAuthorizingInput));
4675            let err = run_script(&tx, entries, 0, spk_output_auth_input_oob).expect_err("output authorizing input oob");
4676            assert_eq!(err, TxScriptError::InvalidOutputIndex(6, 6));
4677
4678            // OpInputCovenantId when covenant id is None (returns zero hash)
4679            let (tx_no_cov, entries_no_cov) = base_transaction(0);
4680            let spk_input_cov_none =
4681                script(|sb| sb.add_i64(0)?.add_op(codes::OpInputCovenantId)?.add_data(&ZERO_HASH.as_bytes())?.add_op(codes::OpEqual));
4682            run_script(&tx_no_cov, entries_no_cov.clone(), 0, spk_input_cov_none).unwrap();
4683
4684            // OpOutputCovenantId when covenant id is None (returns zero hash)
4685            let spk_output_cov_none =
4686                script(|sb| sb.add_i64(0)?.add_op(codes::OpOutputCovenantId)?.add_data(&ZERO_HASH.as_bytes())?.add_op(codes::OpEqual));
4687            run_script(&tx_no_cov, entries_no_cov.clone(), 0, spk_output_cov_none).unwrap();
4688
4689            // OpOutputAuthorizingInput when covenant is None (returns -1)
4690            let spk_output_auth_none =
4691                script(|sb| sb.add_i64(0)?.add_op(codes::OpOutputAuthorizingInput)?.add_i64(-1)?.add_op(codes::OpEqual));
4692            run_script(&tx_no_cov, entries_no_cov.clone(), 0, spk_output_auth_none).unwrap();
4693
4694            // OpInputCovenantId out-of-bounds
4695            let spk_input_cov_oob = script(|sb| sb.add_i64(5)?.add_op(codes::OpInputCovenantId));
4696            let err = run_script(&tx_no_cov, entries_no_cov, 0, spk_input_cov_oob).expect_err("input covenant id oob");
4697            assert!(matches!(err, TxScriptError::InvalidInputIndex(5, 2)));
4698        }
4699
4700        #[test]
4701        fn error_paths_for_indices_and_sizes() {
4702            let (tx, entries) = base_transaction(600);
4703
4704            let spk_bad_input_index = script(|sb| sb.add_i64(5)?.add_op(codes::OpTxInputSpkLen));
4705            let err = run_script(&tx, entries.clone(), 0, spk_bad_input_index).expect_err("invalid input index");
4706            assert!(matches!(err, TxScriptError::InvalidInputIndex(_, _)));
4707
4708            let spk_bad_output_index = script(|sb| sb.add_i64(9)?.add_op(codes::OpTxOutputSpkLen));
4709            let err = run_script(&tx, entries.clone(), 0, spk_bad_output_index).expect_err("invalid output index");
4710            assert!(matches!(err, TxScriptError::InvalidOutputIndex(_, _)));
4711
4712            // Large input SPK to trigger ElementTooBig via substring length
4713            let mut large_entries = entries.clone();
4714            large_entries[1].script_public_key = ScriptPublicKey::new(0, vec![0u8; MAX_SCRIPT_ELEMENT_SIZE + 1].into());
4715            let spk_large_spk_substr =
4716                script(|sb| sb.add_i64(1)?.add_i64(0)?.add_i64(MAX_SCRIPT_ELEMENT_SIZE as i64 + 1)?.add_op(codes::OpTxInputSpkSubstr));
4717            let err = run_script(&tx, large_entries.clone(), 0, spk_large_spk_substr).expect_err("input spk substr too long");
4718            assert!(matches!(err, TxScriptError::ElementTooBig(_, _)));
4719        }
4720
4721        #[test]
4722        fn test_op_tx_input_daa_score() {
4723            // Test: Get DAA score for each input
4724            for (input_idx, expected_daa_score) in [(0, 12345), (1, 67890), (2, 99999)] {
4725                let mut redeem_script = ScriptBuilder::new();
4726                redeem_script
4727                    .add_i64(input_idx)
4728                    .unwrap()
4729                    .add_op(codes::OpTxInputDaaScore)
4730                    .unwrap()
4731                    .add_i64(expected_daa_score)
4732                    .unwrap()
4733                    .add_op(codes::OpEqual)
4734                    .unwrap();
4735                let redeem_script = redeem_script.drain();
4736
4737                let spk = pay_to_script_hash_script(&redeem_script);
4738
4739                let inputs = vec![
4740                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), vec![0xaa], 0, 0),
4741                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 1), vec![0xbb], 0, 0),
4742                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 2), vec![0xcc], 0, 0),
4743                ];
4744
4745                let output_spk = ScriptBuilder::new().add_op(codes::OpTrue).expect("spk build").drain();
4746                let outputs = vec![TransactionOutput::new(100, ScriptPublicKey::new(0, output_spk.into()))];
4747
4748                let mut tx = Transaction::new(1, inputs, outputs, 0, Default::default(), 0, vec![]);
4749                tx.finalize();
4750
4751                let input_spk = ScriptBuilder::new().add_op(codes::OpTrue).expect("spk build").drain();
4752                let entries = vec![
4753                    UtxoEntry::new(1000, ScriptPublicKey::new(0, input_spk.clone().into()), 12345, false, None),
4754                    UtxoEntry::new(2000, ScriptPublicKey::new(0, input_spk.clone().into()), 67890, false, None),
4755                    UtxoEntry::new(3000, ScriptPublicKey::new(0, input_spk.into()), 99999, false, None),
4756                ];
4757
4758                let mut tx = MutableTransaction::with_entries(tx, entries.clone());
4759                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4760
4761                // Update the SPK of input 0 to be P2SH
4762                tx.entries[0].as_mut().unwrap().script_public_key = spk;
4763
4764                let tx = tx.as_verifiable();
4765                let sig_cache = Cache::new(10_000);
4766                let reused_values = SigHashReusedValuesUnsync::new();
4767
4768                let mut vm = TxScriptEngine::from_transaction_input(
4769                    &tx,
4770                    &tx.inputs()[0],
4771                    0,
4772                    tx.utxo(0).unwrap(),
4773                    EngineCtx::new(&sig_cache).with_reused(&reused_values),
4774                    Default::default(),
4775                );
4776
4777                vm.execute().unwrap_or_else(|_| panic!("input {} daa score", input_idx));
4778            }
4779
4780            // Test: Invalid input index (negative)
4781            {
4782                let mut redeem_script = ScriptBuilder::new();
4783                redeem_script.add_i64(-1).unwrap().add_op(codes::OpTxInputDaaScore).unwrap();
4784                let redeem_script = redeem_script.drain();
4785
4786                let spk = pay_to_script_hash_script(&redeem_script);
4787
4788                let input = TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), vec![], 0, 0);
4789                let output = TransactionOutput::new(100, ScriptPublicKey::new(0, vec![codes::OpTrue].into()));
4790                let mut tx = Transaction::new(1, vec![input], vec![output], 0, Default::default(), 0, vec![]);
4791                tx.finalize();
4792
4793                let utxo_entry = UtxoEntry::new(1000, spk, 12345, false, None);
4794                let mut tx = MutableTransaction::with_entries(tx, vec![utxo_entry]);
4795                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4796
4797                let tx = tx.as_verifiable();
4798                let sig_cache = Cache::new(10_000);
4799                let reused_values = SigHashReusedValuesUnsync::new();
4800
4801                let mut vm = TxScriptEngine::from_transaction_input(
4802                    &tx,
4803                    &tx.inputs()[0],
4804                    0,
4805                    tx.utxo(0).unwrap(),
4806                    EngineCtx::new(&sig_cache).with_reused(&reused_values),
4807                    Default::default(),
4808                );
4809
4810                let err = vm.execute().expect_err("should fail with negative index");
4811                // Negative index fails at i32_to_usize conversion
4812                assert!(matches!(err, TxScriptError::InvalidIndex(-1)));
4813            }
4814
4815            // Test: Invalid input index (out of bounds)
4816            {
4817                let mut redeem_script = ScriptBuilder::new();
4818                redeem_script.add_i64(3).unwrap().add_op(codes::OpTxInputDaaScore).unwrap();
4819                let redeem_script = redeem_script.drain();
4820
4821                let spk = pay_to_script_hash_script(&redeem_script);
4822
4823                let inputs = vec![
4824                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), vec![], 0, 0),
4825                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 1), vec![], 0, 0),
4826                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 2), vec![], 0, 0),
4827                ];
4828                let output = TransactionOutput::new(100, ScriptPublicKey::new(0, vec![codes::OpTrue].into()));
4829                let mut tx = Transaction::new(1, inputs, vec![output], 0, Default::default(), 0, vec![]);
4830                tx.finalize();
4831
4832                let input_spk = ScriptBuilder::new().add_op(codes::OpTrue).expect("spk build").drain();
4833                let entries = vec![
4834                    UtxoEntry::new(1000, spk, 12345, false, None),
4835                    UtxoEntry::new(2000, ScriptPublicKey::new(0, input_spk.clone().into()), 67890, false, None),
4836                    UtxoEntry::new(3000, ScriptPublicKey::new(0, input_spk.into()), 99999, false, None),
4837                ];
4838
4839                let mut tx = MutableTransaction::with_entries(tx, entries);
4840                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4841
4842                let tx = tx.as_verifiable();
4843                let sig_cache = Cache::new(10_000);
4844                let reused_values = SigHashReusedValuesUnsync::new();
4845
4846                let mut vm = TxScriptEngine::from_transaction_input(
4847                    &tx,
4848                    &tx.inputs()[0],
4849                    0,
4850                    tx.utxo(0).unwrap(),
4851                    EngineCtx::new(&sig_cache).with_reused(&reused_values),
4852                    Default::default(),
4853                );
4854
4855                let err = vm.execute().expect_err("should fail with out of bounds index");
4856                assert!(matches!(err, TxScriptError::InvalidInputIndex(3, 3)));
4857            }
4858
4859            // Test: Compare DAA scores between inputs
4860            {
4861                let mut redeem_script = ScriptBuilder::new();
4862                redeem_script
4863                    .add_i64(0)
4864                    .unwrap()
4865                    .add_op(codes::OpTxInputDaaScore)
4866                    .unwrap()
4867                    .add_i64(1)
4868                    .unwrap()
4869                    .add_op(codes::OpTxInputDaaScore)
4870                    .unwrap()
4871                    .add_op(codes::OpLessThan)
4872                    .unwrap(); // 12345 < 67890
4873                let redeem_script = redeem_script.drain();
4874
4875                let spk = pay_to_script_hash_script(&redeem_script);
4876
4877                let inputs = vec![
4878                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), vec![], 0, 0),
4879                    TransactionInput::new(TransactionOutpoint::new(Hash::default(), 1), vec![], 0, 0),
4880                ];
4881                let output = TransactionOutput::new(100, ScriptPublicKey::new(0, vec![codes::OpTrue].into()));
4882                let mut tx = Transaction::new(1, inputs, vec![output], 0, Default::default(), 0, vec![]);
4883                tx.finalize();
4884
4885                let input_spk = ScriptBuilder::new().add_op(codes::OpTrue).expect("spk build").drain();
4886                let entries = vec![
4887                    UtxoEntry::new(1000, spk, 12345, false, None),
4888                    UtxoEntry::new(2000, ScriptPublicKey::new(0, input_spk.into()), 67890, false, None),
4889                ];
4890
4891                let mut tx = MutableTransaction::with_entries(tx, entries);
4892                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4893
4894                let tx = tx.as_verifiable();
4895                let sig_cache = Cache::new(10_000);
4896                let reused_values = SigHashReusedValuesUnsync::new();
4897
4898                let mut vm = TxScriptEngine::from_transaction_input(
4899                    &tx,
4900                    &tx.inputs()[0],
4901                    0,
4902                    tx.utxo(0).unwrap(),
4903                    EngineCtx::new(&sig_cache).with_reused(&reused_values),
4904                    Default::default(),
4905                );
4906
4907                vm.execute().expect("compare daa scores");
4908            }
4909        }
4910        #[test]
4911        fn test_op_tx_input_daa_score_in_script() {
4912            let sig_cache = Cache::new(10_000);
4913            let reused_values = SigHashReusedValuesUnsync::new();
4914            let ctx = EngineContext::new(&sig_cache).with_reused(&reused_values);
4915
4916            // Script: Require that input 0 must be from block DAA score >= 50000
4917            let redeem_script = ScriptBuilder::new()
4918                .add_i64(0)
4919                .unwrap()
4920                .add_op(codes::OpTxInputDaaScore)
4921                .unwrap()
4922                .add_i64(50000)
4923                .unwrap()
4924                .add_op(codes::OpGreaterThanOrEqual)
4925                .unwrap()
4926                .drain();
4927
4928            let spk = pay_to_script_hash_script(&redeem_script);
4929
4930            // Test: Input with DAA score above threshold (should pass)
4931            {
4932                let input = TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), vec![], 0, 0);
4933                let output = TransactionOutput::new(100, ScriptPublicKey::new(0, vec![codes::OpTrue].into()));
4934                let mut tx = Transaction::new(1, vec![input.clone()], vec![output], 0, Default::default(), 0, vec![]);
4935                tx.finalize();
4936
4937                let utxo_entry = UtxoEntry::new(1000, spk.clone(), 60000, false, None); // DAA score 60000
4938                let mut tx = MutableTransaction::with_entries(tx, vec![utxo_entry.clone()]);
4939                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4940
4941                let tx = tx.as_verifiable();
4942                let mut vm =
4943                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4944                assert_eq!(vm.execute(), Ok(()), "Should pass with DAA score 60000 >= 50000");
4945            }
4946
4947            // Test: Input with DAA score below threshold (should fail)
4948            {
4949                let input = TransactionInput::new(TransactionOutpoint::new(Hash::default(), 0), vec![], 0, 0);
4950                let output = TransactionOutput::new(100, ScriptPublicKey::new(0, vec![codes::OpTrue].into()));
4951                let mut tx = Transaction::new(1, vec![input.clone()], vec![output], 0, Default::default(), 0, vec![]);
4952                tx.finalize();
4953
4954                let utxo_entry = UtxoEntry::new(1000, spk.clone(), 40000, false, None); // DAA score 40000
4955                let mut tx = MutableTransaction::with_entries(tx, vec![utxo_entry.clone()]);
4956                tx.tx.inputs[0].signature_script = ScriptBuilder::new().add_data(&redeem_script).unwrap().drain();
4957
4958                let tx = tx.as_verifiable();
4959                let mut vm =
4960                    TxScriptEngine::from_transaction_input(&tx, &tx.inputs()[0], 0, tx.utxo(0).unwrap(), ctx, Default::default());
4961                assert_eq!(vm.execute(), Err(TxScriptError::EvalFalse), "Should fail with DAA score 40000 < 50000");
4962            }
4963        }
4964    }
4965}