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
24pub const OP_SMALL_INT_MIN_VAL: u8 = 1;
26pub const OP_SMALL_INT_MAX_VAL: u8 = 16;
28pub const OP_DATA_MIN_VAL: u8 = self::codes::OpData1;
30pub const OP_DATA_MAX_VAL: u8 = self::codes::OpData75;
32pub 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 fn value(&self) -> u8;
88 fn len(&self) -> usize;
90 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 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#[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#[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 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
217opcode_list! {
248
249 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 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 opcode OpToAltStack<0x6b, 1>(self, vm) {
421 let [item] = vm.dstack.pop_raw()?;
422 vm.astack.push_unmetered(item)
424 }
425
426 opcode OpFromAltStack<0x6c, 1>(self, vm) {
427 let last = vm.astack.pop()?;
428 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 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 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 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 opcode OpZkPrecompile<0xa6, 1>(self, vm) {
760 let tag = parse_tag(&mut vm.dstack)?;
762
763 vm.consume_script_units(tag.cost())?;
765
766 verify_zk(tag, &mut vm.dstack, &mut vm.runtime_resource_meter)?;
768
769 vm.dstack.push_item(true)?;
771 Ok(())
772 }
773
774 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 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 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 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 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 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 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 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 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 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 if stack_sequence & SEQUENCE_LOCK_TIME_DISABLED != 0 {
946 return Ok(());
947 }
948
949 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 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 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 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 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 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 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 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 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 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 return Err(TxScriptError::InvalidOpcode(format!("{self:?}")))
1393 };
1394 let [block]: [Hash; 1] = vm.dstack.pop_items()?; 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#[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 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 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), (0x800000, vec![0x7f, 0, 0], false), (0x800000, vec![0x7f, 0, 0, 0, 0, 0, 0, 0, 0], true), (LOCK_TIME_THRESHOLD * 2, vec![0x7f, 0, 0, 0], true), ] {
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 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 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), (0x800000, vec![0x7f, 0, 0], false), (0x800000, vec![0x7f, 0, 0, 0, 0, 0, 0, 0, 0], true), (1 << 63, vec![0x7f, 0, 0], true), ((1 << 63) | 0xffff, vec![0x7f, 0, 0], true), ] {
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 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 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 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 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 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 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 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 let test_cases = vec![
3792 (1, 0), (1, 1), (1, 2), (2, 1), (3, 2), (5, 3), (2, 4), ];
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], 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 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 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 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 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 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 {
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 {
3883 let output_mock = Kip10Mock {
3884 spk: create_mock_spk(1),
3885 amount: 99, };
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 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 {
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 {
3934 let input_mock = Kip10Mock {
3935 spk: spk.clone(),
3936 amount: 199, };
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 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 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 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 }; 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 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 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 assert_eq!(vm.execute(), Ok(()));
4021 }
4022
4023 #[test]
4024 fn test_output_spk() {
4025 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 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 {
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 {
4063 let input_mock = Kip10Mock { spk: spk.clone(), amount: 200 };
4064 let output_mock = Kip10Mock {
4065 spk: create_mock_spk(43), 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 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 {
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 {
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 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 let input_count_script =
4133 ScriptBuilder::new().add_op(OpTxInputCount).unwrap().add_i64(2).unwrap().add_op(OpEqual).unwrap().drain();
4134
4135 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 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 {
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 {
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 {
4178 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 {
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 assert!(matches!(err, TxScriptError::InvalidIndex(-1)));
4813 }
4814
4815 {
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 {
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(); 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 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 {
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); 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 {
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); 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}