1use crate::script::error::ScriptError;
8use crate::script::locking_script::LockingScript;
9use crate::script::op::Op;
10use crate::script::unlocking_script::UnlockingScript;
11use crate::transaction::transaction_input::TransactionInput;
12use crate::transaction::transaction_output::TransactionOutput;
13
14const DEFAULT_MEMORY_LIMIT: usize = 32 * 1024 * 1024;
16
17const MAX_OPS: usize = 100_000;
19
20pub struct SpendParams {
22 pub locking_script: LockingScript,
23 pub unlocking_script: UnlockingScript,
24 pub source_txid: String,
25 pub source_output_index: usize,
26 pub source_satoshis: u64,
27 pub transaction_version: u32,
28 pub transaction_lock_time: u32,
29 pub transaction_sequence: u32,
30 pub other_inputs: Vec<TransactionInput>,
31 pub other_outputs: Vec<TransactionOutput>,
32 pub input_index: usize,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq)]
37pub(crate) enum ScriptContext {
38 Unlocking,
39 Locking,
40}
41
42pub struct Spend {
47 pub(crate) locking_script: LockingScript,
49 pub(crate) unlocking_script: UnlockingScript,
50
51 pub(crate) source_txid: String,
53 pub(crate) source_output_index: usize,
54 pub(crate) source_satoshis: u64,
55 pub(crate) transaction_version: u32,
56 pub(crate) transaction_lock_time: u32,
57 pub(crate) transaction_sequence: u32,
58 pub(crate) other_inputs: Vec<TransactionInput>,
59 pub(crate) other_outputs: Vec<TransactionOutput>,
60 pub(crate) input_index: usize,
61
62 pub(crate) stack: Vec<Vec<u8>>,
64 pub(crate) alt_stack: Vec<Vec<u8>>,
65 pub(crate) if_stack: Vec<bool>,
66 pub(crate) context: ScriptContext,
67 pub(crate) program_counter: usize,
68 pub(crate) last_code_separator: Option<usize>,
69
70 pub(crate) memory_limit: usize,
72 pub(crate) stack_mem: usize,
73 pub(crate) alt_stack_mem: usize,
74
75 pub(crate) is_relaxed_override: bool,
77
78 pub(crate) ops_count: usize,
80}
81
82impl Spend {
83 pub fn new(params: SpendParams) -> Self {
85 Spend {
86 locking_script: params.locking_script,
87 unlocking_script: params.unlocking_script,
88 source_txid: params.source_txid,
89 source_output_index: params.source_output_index,
90 source_satoshis: params.source_satoshis,
91 transaction_version: params.transaction_version,
92 transaction_lock_time: params.transaction_lock_time,
93 transaction_sequence: params.transaction_sequence,
94 other_inputs: params.other_inputs,
95 other_outputs: params.other_outputs,
96 input_index: params.input_index,
97 stack: Vec::new(),
98 alt_stack: Vec::new(),
99 if_stack: Vec::new(),
100 context: ScriptContext::Unlocking,
101 program_counter: 0,
102 last_code_separator: None,
103 memory_limit: DEFAULT_MEMORY_LIMIT,
104 stack_mem: 0,
105 alt_stack_mem: 0,
106 is_relaxed_override: false,
107 ops_count: 0,
108 }
109 }
110
111 pub fn validate(&mut self) -> Result<bool, ScriptError> {
115 self.context = ScriptContext::Unlocking;
117 self.program_counter = 0;
118 loop {
119 let done = self.step()?;
120 if done {
121 break;
122 }
123 }
124
125 if !self.is_relaxed() && !self.unlocking_script.is_push_only() {
127 return Err(ScriptError::PushOnlyViolation);
128 }
129
130 self.context = ScriptContext::Locking;
132 self.program_counter = 0;
133 self.last_code_separator = None;
134 loop {
135 let done = self.step()?;
136 if done {
137 break;
138 }
139 }
140
141 if !self.if_stack.is_empty() {
143 return Err(ScriptError::InvalidScript(
144 "unbalanced IF/ENDIF".to_string(),
145 ));
146 }
147
148 if !self.is_relaxed() && self.stack.len() != 1 {
150 return Err(ScriptError::CleanStackViolation);
151 }
152
153 if self.stack.is_empty() {
155 return Ok(false);
156 }
157
158 let top = self.stack.last().unwrap();
160 Ok(Self::stack_to_bool(top))
161 }
162
163 pub fn step(&mut self) -> Result<bool, ScriptError> {
168 let chunks = match self.context {
169 ScriptContext::Unlocking => self.unlocking_script.chunks(),
170 ScriptContext::Locking => self.locking_script.chunks(),
171 };
172
173 if self.program_counter >= chunks.len() {
175 match self.context {
176 ScriptContext::Unlocking => {
177 return Ok(true);
179 }
180 ScriptContext::Locking => {
181 return Ok(true);
182 }
183 }
184 }
185
186 let chunk = chunks[self.program_counter].clone();
187 let op = chunk.op;
188
189 let in_exec = self.if_stack.iter().all(|&v| v);
191
192 if !in_exec {
193 match op {
195 Op::OpIf | Op::OpNotIf | Op::OpVerIf | Op::OpVerNotIf => {
196 self.if_stack.push(false);
198 }
199 Op::OpElse => {
200 if let Some(last) = self.if_stack.last_mut() {
201 *last = !*last;
202 } else {
203 return Err(ScriptError::InvalidScript(
204 "OP_ELSE without OP_IF".to_string(),
205 ));
206 }
207 }
208 Op::OpEndIf => {
209 self.if_stack.pop().ok_or_else(|| {
210 ScriptError::InvalidScript("OP_ENDIF without OP_IF".to_string())
211 })?;
212 }
213 _ => {
214 }
216 }
217 self.program_counter += 1;
218 return Ok(false);
219 }
220
221 self.ops_count += 1;
223 if self.ops_count > MAX_OPS {
224 return Err(ScriptError::InvalidScript(
225 "exceeded maximum operation count".to_string(),
226 ));
227 }
228
229 self.execute_opcode(op, &chunk)?;
231 self.program_counter += 1;
232
233 Ok(false)
234 }
235
236 pub fn is_relaxed(&self) -> bool {
242 self.transaction_version > 1 || self.is_relaxed_override
243 }
244
245 pub fn set_relaxed_override(&mut self, v: bool) {
247 self.is_relaxed_override = v;
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::script::locking_script::LockingScript;
255 use crate::script::unlocking_script::UnlockingScript;
256
257 fn make_spend(unlocking_asm: &str, locking_asm: &str) -> Spend {
259 Spend::new(SpendParams {
260 locking_script: LockingScript::from_asm(locking_asm),
261 unlocking_script: UnlockingScript::from_asm(unlocking_asm),
262 source_txid: "00".repeat(32),
263 source_output_index: 0,
264 source_satoshis: 0,
265 transaction_version: 1,
266 transaction_lock_time: 0,
267 transaction_sequence: 0xffffffff,
268 other_inputs: vec![],
269 other_outputs: vec![],
270 input_index: 0,
271 })
272 }
273
274 fn make_relaxed_spend(unlocking_asm: &str, locking_asm: &str) -> Spend {
276 Spend::new(SpendParams {
277 locking_script: LockingScript::from_asm(locking_asm),
278 unlocking_script: UnlockingScript::from_asm(unlocking_asm),
279 source_txid: "00".repeat(32),
280 source_output_index: 0,
281 source_satoshis: 0,
282 transaction_version: 2, transaction_lock_time: 0,
284 transaction_sequence: 0xffffffff,
285 other_inputs: vec![],
286 other_outputs: vec![],
287 input_index: 0,
288 })
289 }
290
291 #[test]
292 fn test_simple_push_only_script() {
293 let mut spend = make_spend("OP_1", "");
296 let result = spend.validate().unwrap();
297 assert!(result, "OP_1 should leave true on stack");
298 }
299
300 #[test]
301 fn test_op1_pushes_one() {
302 let mut spend = make_spend("OP_1", "");
303 spend.validate().unwrap();
304 assert_eq!(spend.stack, vec![vec![1u8]]);
305 }
306
307 #[test]
308 fn test_op_numbers() {
309 for n in 2..=16u8 {
311 let asm = format!("OP_{}", n);
312 let mut spend = make_spend(&asm, "");
313 spend.validate().unwrap();
314 assert_eq!(spend.stack, vec![vec![n]], "OP_{} should push [{}]", n, n);
315 }
316 }
317
318 #[test]
319 fn test_op_0_pushes_empty() {
320 let mut spend = make_relaxed_spend("0 OP_1", "");
322 spend.validate().unwrap();
323 assert_eq!(spend.stack.len(), 2);
324 assert_eq!(spend.stack[0], Vec::<u8>::new());
325 assert_eq!(spend.stack[1], vec![1u8]);
326 }
327
328 #[test]
329 fn test_op_1negate() {
330 let mut spend = make_spend("-1", "OP_1 OP_ADD");
331 let result = spend.validate().unwrap();
333 assert!(!result, "-1 + 1 = 0 should be false");
334 }
335
336 #[test]
337 fn test_if_else_endif_true_branch() {
338 let mut spend = make_spend("OP_1", "OP_IF OP_2 OP_ELSE OP_3 OP_ENDIF");
340 let result = spend.validate().unwrap();
341 assert!(result);
342 assert_eq!(spend.stack, vec![vec![2u8]]);
343 }
344
345 #[test]
346 fn test_if_else_endif_false_branch() {
347 let mut spend = make_spend("0", "OP_IF OP_2 OP_ELSE OP_3 OP_ENDIF");
348 let result = spend.validate().unwrap();
349 assert!(result);
350 assert_eq!(spend.stack, vec![vec![3u8]]);
351 }
352
353 #[test]
354 fn test_nested_if() {
355 let mut spend = make_spend("OP_1 OP_1", "OP_IF OP_IF OP_5 OP_ENDIF OP_ENDIF");
357 let result = spend.validate().unwrap();
358 assert!(result);
359 assert_eq!(spend.stack, vec![vec![5u8]]);
360 }
361
362 #[test]
363 fn test_op_verify_true() {
364 let mut spend = make_spend("OP_1", "OP_VERIFY OP_1");
365 let result = spend.validate().unwrap();
366 assert!(result);
367 }
368
369 #[test]
370 fn test_op_verify_false() {
371 let mut spend = make_spend("0", "OP_VERIFY OP_1");
372 let result = spend.validate();
373 assert!(result.is_err());
374 }
375
376 #[test]
377 fn test_op_return_fails() {
378 let mut spend = make_spend("OP_1", "OP_RETURN");
379 let result = spend.validate();
380 assert!(result.is_err());
381 }
382
383 #[test]
384 fn test_memory_limit_exceeded() {
385 let mut spend = make_spend("OP_1", "");
387 spend.memory_limit = 0;
388 let result = spend.validate();
389 assert!(matches!(result, Err(ScriptError::MemoryLimitExceeded)));
390 }
391
392 #[test]
393 fn test_stack_underflow() {
394 let mut spend = make_spend("", "OP_DUP");
397 let result = spend.validate();
398 assert!(result.is_err(), "expected error but got {:?}", result);
399 }
400
401 #[test]
402 fn test_stack_to_bool() {
403 assert!(!Spend::stack_to_bool(&[]));
404 assert!(!Spend::stack_to_bool(&[0]));
405 assert!(!Spend::stack_to_bool(&[0, 0]));
406 assert!(!Spend::stack_to_bool(&[0x80])); assert!(!Spend::stack_to_bool(&[0, 0x80])); assert!(Spend::stack_to_bool(&[1]));
409 assert!(Spend::stack_to_bool(&[0, 1]));
410 assert!(Spend::stack_to_bool(&[0x81])); }
412
413 #[test]
414 fn test_clean_stack_violation() {
415 let mut spend = make_spend("OP_1 OP_2", "");
417 let result = spend.validate();
418 assert!(matches!(result, Err(ScriptError::CleanStackViolation)));
419 }
420
421 #[test]
422 fn test_relaxed_mode_no_clean_stack() {
423 let mut spend = make_relaxed_spend("OP_1 OP_2", "");
425 let result = spend.validate().unwrap();
426 assert!(result); }
428
429 #[test]
430 fn test_push_only_violation() {
431 let mut spend = make_spend("", "OP_1");
433 spend.unlocking_script = UnlockingScript::from_asm("OP_DUP");
435 spend.stack.push(vec![1]);
437 let result = spend.validate();
438 assert!(matches!(result, Err(ScriptError::PushOnlyViolation)));
439 }
440
441 #[test]
442 fn test_step_api() {
443 let mut spend = make_spend("OP_1 OP_2", "OP_ADD");
444
445 assert!(!spend.step().unwrap()); assert_eq!(spend.stack, vec![vec![1u8]]);
448
449 assert!(!spend.step().unwrap()); assert_eq!(spend.stack, vec![vec![1u8], vec![2u8]]);
451
452 assert!(spend.step().unwrap()); spend.context = ScriptContext::Locking;
456 spend.program_counter = 0;
457
458 assert!(!spend.step().unwrap()); assert_eq!(spend.stack, vec![vec![3u8]]);
460
461 assert!(spend.step().unwrap()); }
463
464 #[test]
465 fn test_is_relaxed() {
466 let spend = make_spend("", "");
467 assert!(!spend.is_relaxed()); let spend2 = make_relaxed_spend("", "");
470 assert!(spend2.is_relaxed()); let mut spend3 = make_spend("", "");
473 spend3.set_relaxed_override(true);
474 assert!(spend3.is_relaxed()); }
476
477 #[test]
478 fn test_op_notif() {
479 let mut spend = make_spend("0", "OP_NOTIF OP_5 OP_ENDIF");
481 let result = spend.validate().unwrap();
482 assert!(result);
483 assert_eq!(spend.stack, vec![vec![5u8]]);
484 }
485
486 #[test]
491 fn test_op_add() {
492 let mut spend = make_spend("OP_3 OP_4", "OP_ADD OP_7 OP_EQUAL");
493 assert!(spend.validate().unwrap());
494 }
495
496 #[test]
497 fn test_op_sub() {
498 let mut spend = make_spend("OP_5 OP_3", "OP_SUB OP_2 OP_EQUAL");
499 assert!(spend.validate().unwrap());
500 }
501
502 #[test]
503 fn test_op_mul() {
504 let mut spend = make_spend("OP_3 OP_4", "OP_MUL OP_12 OP_EQUAL");
505 assert!(spend.validate().unwrap());
506 }
507
508 #[test]
509 fn test_op_div() {
510 let mut spend = make_spend("OP_6 OP_3", "OP_DIV OP_2 OP_EQUAL");
511 assert!(spend.validate().unwrap());
512 }
513
514 #[test]
515 fn test_op_mod() {
516 let mut spend = make_spend("OP_7 OP_3", "OP_MOD OP_1 OP_EQUAL");
517 assert!(spend.validate().unwrap());
518 }
519
520 #[test]
521 fn test_op_div_by_zero() {
522 let mut spend = make_spend("OP_5 0", "OP_DIV");
523 assert!(spend.validate().is_err());
524 }
525
526 #[test]
527 fn test_op_equal() {
528 let mut spend = make_spend("OP_3 OP_3", "OP_EQUAL");
529 assert!(spend.validate().unwrap());
530 }
531
532 #[test]
533 fn test_op_equal_false() {
534 let mut spend = make_spend("OP_3 OP_4", "OP_EQUAL");
535 assert!(!spend.validate().unwrap());
536 }
537
538 #[test]
539 fn test_op_equalverify() {
540 let mut spend = make_spend("OP_3 OP_3", "OP_EQUALVERIFY OP_1");
541 assert!(spend.validate().unwrap());
542 }
543
544 #[test]
545 fn test_op_equalverify_fail() {
546 let mut spend = make_spend("OP_3 OP_4", "OP_EQUALVERIFY OP_1");
547 assert!(spend.validate().is_err());
548 }
549
550 #[test]
551 fn test_op_dup_hash160_equalverify() {
552 use crate::primitives::hash::hash160;
555 let pubkey_data = vec![0x04; 33]; let hash = hash160(&pubkey_data);
557 let hash_hex: String = hash.iter().map(|b| format!("{:02x}", b)).collect();
558 let pubkey_hex: String = pubkey_data.iter().map(|b| format!("{:02x}", b)).collect();
559
560 let locking = format!("OP_DUP OP_HASH160 {} OP_EQUALVERIFY OP_DROP OP_1", hash_hex);
561 let mut spend = make_spend(&pubkey_hex, &locking);
562 assert!(spend.validate().unwrap());
563 }
564
565 #[test]
566 fn test_op_cat() {
567 let mut spend = make_relaxed_spend("01 02", "OP_CAT 0102 OP_EQUAL");
570 assert!(spend.validate().unwrap());
571 }
572
573 #[test]
574 fn test_op_split() {
575 let mut spend =
577 make_relaxed_spend("010203 OP_1", "OP_SPLIT 0203 OP_EQUALVERIFY 01 OP_EQUAL");
578 assert!(spend.validate().unwrap());
579 }
580
581 #[test]
582 fn test_op_size() {
583 let mut spend = make_relaxed_spend("010203", "OP_SIZE OP_3 OP_EQUALVERIFY OP_1");
584 assert!(spend.validate().unwrap());
585 }
586
587 #[test]
588 fn test_op_sha256() {
589 let mut spend = make_relaxed_spend("0", "OP_SHA256 OP_SIZE");
591 spend.validate().unwrap();
592 assert_eq!(spend.stack.len(), 2);
593 assert_eq!(spend.stack[0].len(), 32);
594 }
595
596 #[test]
597 fn test_op_hash160() {
598 let mut spend = make_relaxed_spend("01", "OP_HASH160 OP_SIZE");
600 spend.validate().unwrap();
601 assert_eq!(spend.stack[0].len(), 20);
602 }
603
604 #[test]
605 fn test_op_hash256() {
606 let mut spend = make_relaxed_spend("01", "OP_HASH256 OP_SIZE");
608 spend.validate().unwrap();
609 assert_eq!(spend.stack[0].len(), 32);
610 }
611
612 #[test]
613 fn test_op_toaltstack_fromaltstack() {
614 let mut spend = make_spend("OP_1", "OP_TOALTSTACK OP_FROMALTSTACK");
615 assert!(spend.validate().unwrap());
616 assert_eq!(spend.stack, vec![vec![1u8]]);
617 }
618
619 #[test]
620 fn test_op_depth() {
621 let mut spend = make_spend(
625 "OP_1 OP_2 OP_3",
626 "OP_DEPTH OP_3 OP_EQUALVERIFY OP_DROP OP_DROP OP_DROP OP_1",
627 );
628 let result = spend.validate();
629 assert!(result.is_ok(), "test_op_depth failed: {:?}", result);
630 }
631
632 #[test]
633 fn test_op_swap() {
634 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_SWAP");
635 spend.validate().unwrap();
636 assert_eq!(spend.stack, vec![vec![2u8], vec![1u8]]);
637 }
638
639 #[test]
640 fn test_op_rot() {
641 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3", "OP_ROT");
642 spend.validate().unwrap();
643 assert_eq!(spend.stack, vec![vec![2u8], vec![3u8], vec![1u8]]);
644 }
645
646 #[test]
647 fn test_op_over() {
648 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_OVER");
649 spend.validate().unwrap();
650 assert_eq!(spend.stack, vec![vec![1u8], vec![2u8], vec![1u8]]);
651 }
652
653 #[test]
654 fn test_op_nip() {
655 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_NIP");
656 spend.validate().unwrap();
657 assert_eq!(spend.stack, vec![vec![2u8]]);
658 }
659
660 #[test]
661 fn test_op_tuck() {
662 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_TUCK");
663 spend.validate().unwrap();
664 assert_eq!(spend.stack, vec![vec![2u8], vec![1u8], vec![2u8]]);
665 }
666
667 #[test]
668 fn test_op_pick() {
669 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3 OP_2", "OP_PICK");
670 spend.validate().unwrap();
671 assert_eq!(
672 spend.stack,
673 vec![vec![1u8], vec![2u8], vec![3u8], vec![1u8]]
674 );
675 }
676
677 #[test]
678 fn test_op_roll() {
679 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3 OP_2", "OP_ROLL");
680 spend.validate().unwrap();
681 assert_eq!(spend.stack, vec![vec![2u8], vec![3u8], vec![1u8]]);
682 }
683
684 #[test]
685 fn test_op_2dup() {
686 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_2DUP");
687 spend.validate().unwrap();
688 assert_eq!(
689 spend.stack,
690 vec![vec![1u8], vec![2u8], vec![1u8], vec![2u8]]
691 );
692 }
693
694 #[test]
695 fn test_op_3dup() {
696 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3", "OP_3DUP");
697 spend.validate().unwrap();
698 assert_eq!(spend.stack.len(), 6);
699 }
700
701 #[test]
702 fn test_op_2drop() {
703 let mut spend = make_spend("OP_1 OP_2 OP_3", "OP_2DROP");
704 assert!(spend.validate().unwrap());
705 assert_eq!(spend.stack, vec![vec![1u8]]);
706 }
707
708 #[test]
709 fn test_op_2swap() {
710 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3 OP_4", "OP_2SWAP");
711 spend.validate().unwrap();
712 assert_eq!(
713 spend.stack,
714 vec![vec![3u8], vec![4u8], vec![1u8], vec![2u8]]
715 );
716 }
717
718 #[test]
719 fn test_op_ifdup_true() {
720 let mut spend = make_relaxed_spend("OP_1", "OP_IFDUP");
721 spend.validate().unwrap();
722 assert_eq!(spend.stack, vec![vec![1u8], vec![1u8]]);
723 }
724
725 #[test]
726 fn test_op_ifdup_false() {
727 let mut spend = make_relaxed_spend("0", "OP_IFDUP");
728 spend.validate().unwrap();
729 assert_eq!(spend.stack, vec![Vec::<u8>::new()]);
730 }
731
732 #[test]
733 fn test_op_lessthan() {
734 let mut spend = make_spend("OP_1 OP_2", "OP_LESSTHAN");
735 assert!(spend.validate().unwrap());
736 }
737
738 #[test]
739 fn test_op_greaterthan() {
740 let mut spend = make_spend("OP_3 OP_2", "OP_GREATERTHAN");
741 assert!(spend.validate().unwrap());
742 }
743
744 #[test]
745 fn test_op_within() {
746 let mut spend = make_spend("OP_3 OP_2 OP_5", "OP_WITHIN");
748 assert!(spend.validate().unwrap());
749 }
750
751 #[test]
752 fn test_op_within_false() {
753 let mut spend = make_spend("OP_5 OP_2 OP_5", "OP_WITHIN");
755 assert!(!spend.validate().unwrap());
756 }
757
758 #[test]
759 fn test_op_min() {
760 let mut spend = make_spend("OP_3 OP_5", "OP_MIN OP_3 OP_EQUAL");
761 assert!(spend.validate().unwrap());
762 }
763
764 #[test]
765 fn test_op_max() {
766 let mut spend = make_spend("OP_3 OP_5", "OP_MAX OP_5 OP_EQUAL");
767 assert!(spend.validate().unwrap());
768 }
769
770 #[test]
771 fn test_op_booland() {
772 let mut spend = make_spend("OP_1 OP_1", "OP_BOOLAND");
773 assert!(spend.validate().unwrap());
774
775 let mut spend2 = make_spend("OP_1 0", "OP_BOOLAND");
776 assert!(!spend2.validate().unwrap());
777 }
778
779 #[test]
780 fn test_op_boolor() {
781 let mut spend = make_spend("0 0", "OP_BOOLOR");
782 assert!(!spend.validate().unwrap());
783
784 let mut spend2 = make_spend("OP_1 0", "OP_BOOLOR");
785 assert!(spend2.validate().unwrap());
786 }
787
788 #[test]
789 fn test_op_abs() {
790 let mut spend = make_spend("-1", "OP_ABS OP_1 OP_EQUAL");
792 assert!(spend.validate().unwrap());
793 }
794
795 #[test]
796 fn test_op_not() {
797 let mut spend = make_spend("0", "OP_NOT"); assert!(spend.validate().unwrap());
799
800 let mut spend2 = make_spend("OP_1", "OP_NOT"); assert!(!spend2.validate().unwrap());
802 }
803
804 #[test]
805 fn test_op_0notequal() {
806 let mut spend = make_spend("OP_5", "OP_0NOTEQUAL");
807 assert!(spend.validate().unwrap());
808
809 let mut spend2 = make_spend("0", "OP_0NOTEQUAL");
810 assert!(!spend2.validate().unwrap());
811 }
812
813 #[test]
814 fn test_op_negate() {
815 let mut spend = make_spend("OP_5", "OP_NEGATE OP_ABS OP_5 OP_EQUAL");
816 assert!(spend.validate().unwrap());
817 }
818
819 #[test]
820 fn test_op_1add_1sub() {
821 let mut spend = make_spend("OP_5", "OP_1ADD OP_6 OP_EQUAL");
822 assert!(spend.validate().unwrap());
823
824 let mut spend2 = make_spend("OP_5", "OP_1SUB OP_4 OP_EQUAL");
825 assert!(spend2.validate().unwrap());
826 }
827
828 #[test]
829 fn test_op_numequal() {
830 let mut spend = make_spend("OP_3 OP_3", "OP_NUMEQUAL");
831 assert!(spend.validate().unwrap());
832 }
833
834 #[test]
835 fn test_op_numequalverify() {
836 let mut spend = make_spend("OP_3 OP_3", "OP_NUMEQUALVERIFY OP_1");
837 assert!(spend.validate().unwrap());
838 }
839
840 #[test]
841 fn test_op_numnotequal() {
842 let mut spend = make_spend("OP_3 OP_4", "OP_NUMNOTEQUAL");
843 assert!(spend.validate().unwrap());
844 }
845
846 #[test]
847 fn test_op_invert() {
848 let mut spend = make_relaxed_spend("00", "OP_INVERT");
850 spend.validate().unwrap();
851 assert_eq!(spend.stack, vec![vec![0xff]]);
852 }
853
854 #[test]
855 fn test_op_and() {
856 let mut spend = make_relaxed_spend("ff 0f", "OP_AND");
857 spend.validate().unwrap();
858 assert_eq!(spend.stack, vec![vec![0x0f]]);
859 }
860
861 #[test]
862 fn test_op_or() {
863 let mut spend = make_relaxed_spend("f0 0f", "OP_OR");
864 spend.validate().unwrap();
865 assert_eq!(spend.stack, vec![vec![0xff]]);
866 }
867
868 #[test]
869 fn test_op_xor() {
870 let mut spend = make_relaxed_spend("ff ff", "OP_XOR");
871 spend.validate().unwrap();
872 assert_eq!(spend.stack, vec![vec![0x00]]);
873 }
874
875 #[test]
876 fn test_nested_if_deep() {
877 let mut spend = make_spend(
879 "OP_1 OP_1 OP_1",
880 "OP_IF OP_IF OP_IF OP_7 OP_ENDIF OP_ENDIF OP_ENDIF",
881 );
882 assert!(spend.validate().unwrap());
883 assert_eq!(spend.stack, vec![vec![7u8]]);
884 }
885
886 #[test]
887 fn test_op_codeseparator() {
888 let mut spend = make_spend("OP_1", "OP_CODESEPARATOR");
890 assert!(spend.validate().unwrap());
891 }
892
893 #[test]
894 fn test_chronicle_op_substr() {
895 let mut spend = make_relaxed_spend("0102030405 OP_1 OP_2", "OP_SUBSTR 0203 OP_EQUAL");
897 assert!(spend.validate().unwrap());
898 }
899
900 #[test]
901 fn test_chronicle_op_left() {
902 let mut spend = make_relaxed_spend("01020304 OP_2", "OP_LEFT 0102 OP_EQUAL");
903 assert!(spend.validate().unwrap());
904 }
905
906 #[test]
907 fn test_chronicle_op_right() {
908 let mut spend = make_relaxed_spend("01020304 OP_2", "OP_RIGHT 0304 OP_EQUAL");
909 assert!(spend.validate().unwrap());
910 }
911
912 #[test]
913 fn test_op_ripemd160() {
914 let mut spend = make_relaxed_spend("01", "OP_RIPEMD160 OP_SIZE");
915 spend.validate().unwrap();
916 assert_eq!(spend.stack[0].len(), 20);
917 }
918
919 #[test]
920 fn test_op_sha1() {
921 let mut spend = make_relaxed_spend("01", "OP_SHA1 OP_SIZE");
922 spend.validate().unwrap();
923 assert_eq!(spend.stack[0].len(), 20);
924 }
925
926 #[test]
927 fn test_op_checksig_empty_sig_fails() {
928 let mut spend = make_relaxed_spend("0 01", "OP_CHECKSIG");
930 spend.validate().unwrap();
931 assert_eq!(spend.stack, vec![Vec::<u8>::new()]);
933 }
934
935 #[test]
936 fn test_relaxed_mode_gating() {
937 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3", "");
939 let result = spend.validate().unwrap();
940 assert!(result); let mut spend2 = make_spend("OP_1 OP_2 OP_3", "");
944 assert!(matches!(
945 spend2.validate(),
946 Err(ScriptError::CleanStackViolation)
947 ));
948 }
949
950 #[test]
951 fn test_op_disabled_2mul() {
952 let mut spend = make_spend("OP_1", "OP_2MUL");
953 let result = spend.validate();
954 assert!(matches!(result, Err(ScriptError::DisabledOpcode(_))));
955 }
956
957 #[test]
958 fn test_op_disabled_2div() {
959 let mut spend = make_spend("OP_1", "OP_2DIV");
960 let result = spend.validate();
961 assert!(matches!(result, Err(ScriptError::DisabledOpcode(_))));
962 }
963
964 fn parse_test_asm(asm: &str) -> crate::script::script::Script {
973 use crate::script::script::Script;
974 use crate::script::script_chunk::ScriptChunk;
975
976 let asm = asm.trim();
977 if asm.is_empty() {
978 return Script::new();
979 }
980
981 let mut chunks = Vec::new();
982 let tokens: Vec<&str> = asm.split_whitespace().collect();
983 let mut i = 0;
984
985 while i < tokens.len() {
986 let token = tokens[i];
987
988 if token == "0" {
990 chunks.push(ScriptChunk::new_opcode(Op::Op0));
991 i += 1;
992 continue;
993 }
994 if token == "-1" {
995 chunks.push(ScriptChunk::new_opcode(Op::Op1Negate));
996 i += 1;
997 continue;
998 }
999
1000 if token.starts_with('\'') {
1002 let text = if token.ends_with('\'') && token.len() > 1 {
1003 &token[1..token.len() - 1]
1004 } else {
1005 let mut s = token[1..].to_string();
1007 loop {
1008 i += 1;
1009 if i >= tokens.len() {
1010 break;
1011 }
1012 s.push(' ');
1013 s.push_str(tokens[i]);
1014 if tokens[i].ends_with('\'') {
1015 s.truncate(s.len() - 1);
1016 break;
1017 }
1018 }
1019 i += 1;
1020 let data = s.into_bytes();
1021 let len = data.len();
1022 let op_byte = if len < 0x4c { len as u8 } else { 0x4c };
1023 chunks.push(ScriptChunk::new_raw(op_byte, Some(data)));
1024 continue;
1025 };
1026 let data = text.as_bytes().to_vec();
1027 let len = data.len();
1028 let op_byte = if len < 0x4c { len as u8 } else { 0x4c };
1029 chunks.push(ScriptChunk::new_raw(op_byte, Some(data)));
1030 i += 1;
1031 continue;
1032 }
1033
1034 if token.starts_with("0x") || token.starts_with("0X") {
1036 let hex = &token[2..];
1037 if let Ok(push_len) = usize::from_str_radix(hex, 16) {
1038 if push_len > 0 && push_len <= 0x4e && i + 1 < tokens.len() {
1040 let next = tokens[i + 1];
1041 if next.starts_with("0x") || next.starts_with("0X") {
1042 let data_hex = &next[2..];
1043 if let Ok(data) = hex_decode(data_hex) {
1044 chunks.push(ScriptChunk::new_raw(push_len as u8, Some(data)));
1045 i += 2;
1046 continue;
1047 }
1048 }
1049 }
1050 if push_len <= 0xff {
1052 let op = Op::from(push_len as u8);
1053 chunks.push(ScriptChunk::new_raw(push_len as u8, None));
1054 let _ = op; i += 1;
1056 continue;
1057 }
1058 }
1059 i += 1;
1060 continue;
1061 }
1062
1063 if token == "PUSHDATA1" || token == "OP_PUSHDATA1" {
1065 if i + 2 < tokens.len() {
1066 let _len_hex = tokens[i + 1].strip_prefix("0x").unwrap_or(tokens[i + 1]);
1067 let data_hex = tokens[i + 2].strip_prefix("0x").unwrap_or(tokens[i + 2]);
1068 if let Ok(data) = hex_decode(data_hex) {
1069 chunks.push(ScriptChunk::new_raw(0x4c, Some(data)));
1070 i += 3;
1071 continue;
1072 }
1073 }
1074 i += 1;
1075 continue;
1076 }
1077 if token == "PUSHDATA2" || token == "OP_PUSHDATA2" {
1078 if i + 2 < tokens.len() {
1079 let data_hex = tokens[i + 2].strip_prefix("0x").unwrap_or(tokens[i + 2]);
1080 if let Ok(data) = hex_decode(data_hex) {
1081 chunks.push(ScriptChunk::new_raw(0x4d, Some(data)));
1082 i += 3;
1083 continue;
1084 }
1085 }
1086 i += 1;
1087 continue;
1088 }
1089 if token == "PUSHDATA4" || token == "OP_PUSHDATA4" {
1090 if i + 2 < tokens.len() {
1091 let data_hex = tokens[i + 2].strip_prefix("0x").unwrap_or(tokens[i + 2]);
1092 if let Ok(data) = hex_decode(data_hex) {
1093 chunks.push(ScriptChunk::new_raw(0x4e, Some(data)));
1094 i += 3;
1095 continue;
1096 }
1097 }
1098 i += 1;
1099 continue;
1100 }
1101
1102 if let Some(op) =
1104 Op::from_name(token).or_else(|| Op::from_name(&format!("OP_{}", token)))
1105 {
1106 chunks.push(ScriptChunk::new_opcode(op));
1107 i += 1;
1108 continue;
1109 }
1110
1111 if let Ok(n) = token.parse::<i64>() {
1113 use crate::primitives::big_number::BigNumber;
1114 let bn = BigNumber::from_number(n);
1115 let data = bn.to_script_num();
1116 if data.is_empty() {
1117 chunks.push(ScriptChunk::new_opcode(Op::Op0));
1118 } else {
1119 let len = data.len();
1120 let op_byte = if len < 0x4c { len as u8 } else { 0x4c };
1121 chunks.push(ScriptChunk::new_raw(op_byte, Some(data)));
1122 }
1123 i += 1;
1124 continue;
1125 }
1126
1127 i += 1;
1129 }
1130
1131 Script::from_chunks(chunks)
1132 }
1133
1134 fn hex_decode(hex: &str) -> Result<Vec<u8>, ()> {
1135 if hex.len() % 2 != 0 {
1136 return Err(());
1137 }
1138 let mut bytes = Vec::with_capacity(hex.len() / 2);
1139 for i in (0..hex.len()).step_by(2) {
1140 match u8::from_str_radix(&hex[i..i + 2], 16) {
1141 Ok(b) => bytes.push(b),
1142 Err(_) => return Err(()),
1143 }
1144 }
1145 Ok(bytes)
1146 }
1147
1148 #[test]
1149 fn test_script_tests_json() {
1150 let json_str = include_str!("../../test-vectors/script_tests.json");
1151 let entries: Vec<serde_json::Value> =
1152 serde_json::from_str(json_str).expect("failed to parse script_tests.json");
1153
1154 let mut passed = 0;
1155 let mut failed = 0;
1156 let mut skipped = 0;
1157
1158 for entry in &entries {
1159 let arr = match entry.as_array() {
1160 Some(a) => a,
1161 None => continue,
1162 };
1163
1164 if arr.len() < 4 {
1166 skipped += 1;
1167 continue;
1168 }
1169
1170 let (sig_asm, pubkey_asm, flags_str, expected) = if arr[0].is_array() {
1172 if arr.len() < 5 {
1174 skipped += 1;
1175 continue;
1176 }
1177 (
1178 arr[1].as_str().unwrap_or(""),
1179 arr[2].as_str().unwrap_or(""),
1180 arr[3].as_str().unwrap_or(""),
1181 arr[4].as_str().unwrap_or(""),
1182 )
1183 } else {
1184 (
1185 arr[0].as_str().unwrap_or(""),
1186 arr[1].as_str().unwrap_or(""),
1187 arr[2].as_str().unwrap_or(""),
1188 arr[3].as_str().unwrap_or(""),
1189 )
1190 };
1191
1192 let flags: Vec<&str> = if flags_str.is_empty() {
1194 vec![]
1195 } else {
1196 flags_str.split(',').collect()
1197 };
1198
1199 let has_strictenc = flags.contains(&"STRICTENC");
1200 let has_utxo_after_genesis = flags.contains(&"UTXO_AFTER_GENESIS");
1201 let has_p2sh = flags.contains(&"P2SH");
1202 let _has_sigpushonly = flags.contains(&"SIGPUSHONLY");
1203 let _has_minimaldata = flags.contains(&"MINIMALDATA");
1204
1205 if has_utxo_after_genesis || has_p2sh {
1208 skipped += 1;
1209 continue;
1210 }
1211
1212 let unlocking_script = UnlockingScript::from_script(parse_test_asm(sig_asm));
1214 let locking_script = LockingScript::from_script(parse_test_asm(pubkey_asm));
1215
1216 let version = if has_strictenc { 1u32 } else { 2u32 };
1219
1220 let mut spend = Spend::new(SpendParams {
1221 locking_script,
1222 unlocking_script,
1223 source_txid: "00".repeat(32),
1224 source_output_index: 0,
1225 source_satoshis: 0,
1226 transaction_version: version,
1227 transaction_lock_time: 0,
1228 transaction_sequence: 0xffffffff,
1229 other_inputs: vec![],
1230 other_outputs: vec![],
1231 input_index: 0,
1232 });
1233
1234 let result = spend.validate();
1235
1236 let expected_ok = expected == "OK";
1237
1238 match (expected_ok, &result) {
1239 (true, Ok(true)) => passed += 1,
1240 (true, Ok(false)) => {
1241 failed += 1;
1243 }
1244 (true, Err(_)) => {
1245 failed += 1;
1246 }
1247 (false, Err(_)) => passed += 1,
1248 (false, Ok(false)) => passed += 1, (false, Ok(true)) => {
1250 failed += 1;
1251 }
1252 }
1253 }
1254
1255 println!(
1256 "script_tests.json: {} passed, {} failed, {} skipped",
1257 passed, failed, skipped
1258 );
1259
1260 let total_run = passed + failed;
1262 let pass_rate = if total_run > 0 {
1263 (passed as f64 / total_run as f64) * 100.0
1264 } else {
1265 0.0
1266 };
1267 println!("Pass rate: {:.1}% ({}/{})", pass_rate, passed, total_run);
1268
1269 assert!(
1271 pass_rate >= 50.0,
1272 "script_tests.json pass rate too low: {:.1}% ({}/{})",
1273 pass_rate,
1274 passed,
1275 total_run
1276 );
1277 }
1278}