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 if self.if_stack.pop().is_none() {
210 return Err(ScriptError::InvalidScript(
211 "OP_ENDIF without OP_IF".to_string(),
212 ));
213 }
214 }
215 _ => {
216 }
218 }
219 self.program_counter += 1;
220 return Ok(false);
221 }
222
223 self.ops_count += 1;
225 if self.ops_count > MAX_OPS {
226 return Err(ScriptError::InvalidScript(
227 "exceeded maximum operation count".to_string(),
228 ));
229 }
230
231 self.execute_opcode(op, &chunk)?;
233 self.program_counter += 1;
234
235 Ok(false)
236 }
237
238 pub fn is_relaxed(&self) -> bool {
244 self.transaction_version > 1 || self.is_relaxed_override
245 }
246
247 pub fn set_relaxed_override(&mut self, v: bool) {
249 self.is_relaxed_override = v;
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::script::locking_script::LockingScript;
257 use crate::script::unlocking_script::UnlockingScript;
258
259 fn make_spend(unlocking_asm: &str, locking_asm: &str) -> Spend {
261 Spend::new(SpendParams {
262 locking_script: LockingScript::from_asm(locking_asm),
263 unlocking_script: UnlockingScript::from_asm(unlocking_asm),
264 source_txid: "00".repeat(32),
265 source_output_index: 0,
266 source_satoshis: 0,
267 transaction_version: 1,
268 transaction_lock_time: 0,
269 transaction_sequence: 0xffffffff,
270 other_inputs: vec![],
271 other_outputs: vec![],
272 input_index: 0,
273 })
274 }
275
276 fn make_relaxed_spend(unlocking_asm: &str, locking_asm: &str) -> Spend {
278 Spend::new(SpendParams {
279 locking_script: LockingScript::from_asm(locking_asm),
280 unlocking_script: UnlockingScript::from_asm(unlocking_asm),
281 source_txid: "00".repeat(32),
282 source_output_index: 0,
283 source_satoshis: 0,
284 transaction_version: 2, transaction_lock_time: 0,
286 transaction_sequence: 0xffffffff,
287 other_inputs: vec![],
288 other_outputs: vec![],
289 input_index: 0,
290 })
291 }
292
293 #[test]
294 fn test_simple_push_only_script() {
295 let mut spend = make_spend("OP_1", "");
298 let result = spend.validate().unwrap();
299 assert!(result, "OP_1 should leave true on stack");
300 }
301
302 #[test]
303 fn test_op1_pushes_one() {
304 let mut spend = make_spend("OP_1", "");
305 spend.validate().unwrap();
306 assert_eq!(spend.stack, vec![vec![1u8]]);
307 }
308
309 #[test]
310 fn test_op_numbers() {
311 for n in 2..=16u8 {
313 let asm = format!("OP_{}", n);
314 let mut spend = make_spend(&asm, "");
315 spend.validate().unwrap();
316 assert_eq!(spend.stack, vec![vec![n]], "OP_{} should push [{}]", n, n);
317 }
318 }
319
320 #[test]
321 fn test_op_0_pushes_empty() {
322 let mut spend = make_relaxed_spend("0 OP_1", "");
324 spend.validate().unwrap();
325 assert_eq!(spend.stack.len(), 2);
326 assert_eq!(spend.stack[0], Vec::<u8>::new());
327 assert_eq!(spend.stack[1], vec![1u8]);
328 }
329
330 #[test]
331 fn test_op_1negate() {
332 let mut spend = make_spend("-1", "OP_1 OP_ADD");
333 let result = spend.validate().unwrap();
335 assert!(!result, "-1 + 1 = 0 should be false");
336 }
337
338 #[test]
339 fn test_if_else_endif_true_branch() {
340 let mut spend = make_spend("OP_1", "OP_IF OP_2 OP_ELSE OP_3 OP_ENDIF");
342 let result = spend.validate().unwrap();
343 assert!(result);
344 assert_eq!(spend.stack, vec![vec![2u8]]);
345 }
346
347 #[test]
348 fn test_if_else_endif_false_branch() {
349 let mut spend = make_spend("0", "OP_IF OP_2 OP_ELSE OP_3 OP_ENDIF");
350 let result = spend.validate().unwrap();
351 assert!(result);
352 assert_eq!(spend.stack, vec![vec![3u8]]);
353 }
354
355 #[test]
356 fn test_nested_if() {
357 let mut spend = make_spend("OP_1 OP_1", "OP_IF OP_IF OP_5 OP_ENDIF OP_ENDIF");
359 let result = spend.validate().unwrap();
360 assert!(result);
361 assert_eq!(spend.stack, vec![vec![5u8]]);
362 }
363
364 #[test]
365 fn test_op_verify_true() {
366 let mut spend = make_spend("OP_1", "OP_VERIFY OP_1");
367 let result = spend.validate().unwrap();
368 assert!(result);
369 }
370
371 #[test]
372 fn test_op_verify_false() {
373 let mut spend = make_spend("0", "OP_VERIFY OP_1");
374 let result = spend.validate();
375 assert!(result.is_err());
376 }
377
378 #[test]
379 fn test_op_return_fails() {
380 let mut spend = make_spend("OP_1", "OP_RETURN");
381 let result = spend.validate();
382 assert!(result.is_err());
383 }
384
385 #[test]
386 fn test_memory_limit_exceeded() {
387 let mut spend = make_spend("OP_1", "");
389 spend.memory_limit = 0;
390 let result = spend.validate();
391 assert!(matches!(result, Err(ScriptError::MemoryLimitExceeded)));
392 }
393
394 #[test]
395 fn test_stack_underflow() {
396 let mut spend = make_spend("", "OP_DUP");
399 let result = spend.validate();
400 assert!(result.is_err(), "expected error but got {:?}", result);
401 }
402
403 #[test]
404 fn test_stack_to_bool() {
405 assert!(!Spend::stack_to_bool(&[]));
406 assert!(!Spend::stack_to_bool(&[0]));
407 assert!(!Spend::stack_to_bool(&[0, 0]));
408 assert!(!Spend::stack_to_bool(&[0x80])); assert!(!Spend::stack_to_bool(&[0, 0x80])); assert!(Spend::stack_to_bool(&[1]));
411 assert!(Spend::stack_to_bool(&[0, 1]));
412 assert!(Spend::stack_to_bool(&[0x81])); }
414
415 #[test]
416 fn test_clean_stack_violation() {
417 let mut spend = make_spend("OP_1 OP_2", "");
419 let result = spend.validate();
420 assert!(matches!(result, Err(ScriptError::CleanStackViolation)));
421 }
422
423 #[test]
424 fn test_relaxed_mode_no_clean_stack() {
425 let mut spend = make_relaxed_spend("OP_1 OP_2", "");
427 let result = spend.validate().unwrap();
428 assert!(result); }
430
431 #[test]
432 fn test_push_only_violation() {
433 let mut spend = make_spend("", "OP_1");
435 spend.unlocking_script = UnlockingScript::from_asm("OP_DUP");
437 spend.stack.push(vec![1]);
439 let result = spend.validate();
440 assert!(matches!(result, Err(ScriptError::PushOnlyViolation)));
441 }
442
443 #[test]
444 fn test_step_api() {
445 let mut spend = make_spend("OP_1 OP_2", "OP_ADD");
446
447 assert!(!spend.step().unwrap()); assert_eq!(spend.stack, vec![vec![1u8]]);
450
451 assert!(!spend.step().unwrap()); assert_eq!(spend.stack, vec![vec![1u8], vec![2u8]]);
453
454 assert!(spend.step().unwrap()); spend.context = ScriptContext::Locking;
458 spend.program_counter = 0;
459
460 assert!(!spend.step().unwrap()); assert_eq!(spend.stack, vec![vec![3u8]]);
462
463 assert!(spend.step().unwrap()); }
465
466 #[test]
467 fn test_is_relaxed() {
468 let spend = make_spend("", "");
469 assert!(!spend.is_relaxed()); let spend2 = make_relaxed_spend("", "");
472 assert!(spend2.is_relaxed()); let mut spend3 = make_spend("", "");
475 spend3.set_relaxed_override(true);
476 assert!(spend3.is_relaxed()); }
478
479 #[test]
480 fn test_op_notif() {
481 let mut spend = make_spend("0", "OP_NOTIF OP_5 OP_ENDIF");
483 let result = spend.validate().unwrap();
484 assert!(result);
485 assert_eq!(spend.stack, vec![vec![5u8]]);
486 }
487
488 #[test]
493 fn test_op_add() {
494 let mut spend = make_spend("OP_3 OP_4", "OP_ADD OP_7 OP_EQUAL");
495 assert!(spend.validate().unwrap());
496 }
497
498 #[test]
499 fn test_op_sub() {
500 let mut spend = make_spend("OP_5 OP_3", "OP_SUB OP_2 OP_EQUAL");
501 assert!(spend.validate().unwrap());
502 }
503
504 #[test]
505 fn test_op_mul() {
506 let mut spend = make_spend("OP_3 OP_4", "OP_MUL OP_12 OP_EQUAL");
507 assert!(spend.validate().unwrap());
508 }
509
510 #[test]
511 fn test_op_div() {
512 let mut spend = make_spend("OP_6 OP_3", "OP_DIV OP_2 OP_EQUAL");
513 assert!(spend.validate().unwrap());
514 }
515
516 #[test]
517 fn test_op_mod() {
518 let mut spend = make_spend("OP_7 OP_3", "OP_MOD OP_1 OP_EQUAL");
519 assert!(spend.validate().unwrap());
520 }
521
522 #[test]
523 fn test_op_div_by_zero() {
524 let mut spend = make_spend("OP_5 0", "OP_DIV");
525 assert!(spend.validate().is_err());
526 }
527
528 #[test]
529 fn test_op_equal() {
530 let mut spend = make_spend("OP_3 OP_3", "OP_EQUAL");
531 assert!(spend.validate().unwrap());
532 }
533
534 #[test]
535 fn test_op_equal_false() {
536 let mut spend = make_spend("OP_3 OP_4", "OP_EQUAL");
537 assert!(!spend.validate().unwrap());
538 }
539
540 #[test]
541 fn test_op_equalverify() {
542 let mut spend = make_spend("OP_3 OP_3", "OP_EQUALVERIFY OP_1");
543 assert!(spend.validate().unwrap());
544 }
545
546 #[test]
547 fn test_op_equalverify_fail() {
548 let mut spend = make_spend("OP_3 OP_4", "OP_EQUALVERIFY OP_1");
549 assert!(spend.validate().is_err());
550 }
551
552 #[test]
553 fn test_op_dup_hash160_equalverify() {
554 use crate::primitives::hash::hash160;
557 let pubkey_data = vec![0x04; 33]; let hash = hash160(&pubkey_data);
559 let hash_hex: String = hash.iter().map(|b| format!("{:02x}", b)).collect();
560 let pubkey_hex: String = pubkey_data.iter().map(|b| format!("{:02x}", b)).collect();
561
562 let locking = format!("OP_DUP OP_HASH160 {} OP_EQUALVERIFY OP_DROP OP_1", hash_hex);
563 let mut spend = make_spend(&pubkey_hex, &locking);
564 assert!(spend.validate().unwrap());
565 }
566
567 #[test]
568 fn test_op_cat() {
569 let mut spend = make_relaxed_spend("01 02", "OP_CAT 0102 OP_EQUAL");
572 assert!(spend.validate().unwrap());
573 }
574
575 #[test]
576 fn test_op_split() {
577 let mut spend =
579 make_relaxed_spend("010203 OP_1", "OP_SPLIT 0203 OP_EQUALVERIFY 01 OP_EQUAL");
580 assert!(spend.validate().unwrap());
581 }
582
583 #[test]
584 fn test_op_size() {
585 let mut spend = make_relaxed_spend("010203", "OP_SIZE OP_3 OP_EQUALVERIFY OP_1");
586 assert!(spend.validate().unwrap());
587 }
588
589 #[test]
590 fn test_op_sha256() {
591 let mut spend = make_relaxed_spend("0", "OP_SHA256 OP_SIZE");
593 spend.validate().unwrap();
594 assert_eq!(spend.stack.len(), 2);
595 assert_eq!(spend.stack[0].len(), 32);
596 }
597
598 #[test]
599 fn test_op_hash160() {
600 let mut spend = make_relaxed_spend("01", "OP_HASH160 OP_SIZE");
602 spend.validate().unwrap();
603 assert_eq!(spend.stack[0].len(), 20);
604 }
605
606 #[test]
607 fn test_op_hash256() {
608 let mut spend = make_relaxed_spend("01", "OP_HASH256 OP_SIZE");
610 spend.validate().unwrap();
611 assert_eq!(spend.stack[0].len(), 32);
612 }
613
614 #[test]
615 fn test_op_toaltstack_fromaltstack() {
616 let mut spend = make_spend("OP_1", "OP_TOALTSTACK OP_FROMALTSTACK");
617 assert!(spend.validate().unwrap());
618 assert_eq!(spend.stack, vec![vec![1u8]]);
619 }
620
621 #[test]
622 fn test_op_depth() {
623 let mut spend = make_spend(
627 "OP_1 OP_2 OP_3",
628 "OP_DEPTH OP_3 OP_EQUALVERIFY OP_DROP OP_DROP OP_DROP OP_1",
629 );
630 let result = spend.validate();
631 assert!(result.is_ok(), "test_op_depth failed: {:?}", result);
632 }
633
634 #[test]
635 fn test_op_swap() {
636 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_SWAP");
637 spend.validate().unwrap();
638 assert_eq!(spend.stack, vec![vec![2u8], vec![1u8]]);
639 }
640
641 #[test]
642 fn test_op_rot() {
643 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3", "OP_ROT");
644 spend.validate().unwrap();
645 assert_eq!(spend.stack, vec![vec![2u8], vec![3u8], vec![1u8]]);
646 }
647
648 #[test]
649 fn test_op_over() {
650 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_OVER");
651 spend.validate().unwrap();
652 assert_eq!(spend.stack, vec![vec![1u8], vec![2u8], vec![1u8]]);
653 }
654
655 #[test]
656 fn test_op_nip() {
657 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_NIP");
658 spend.validate().unwrap();
659 assert_eq!(spend.stack, vec![vec![2u8]]);
660 }
661
662 #[test]
663 fn test_op_tuck() {
664 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_TUCK");
665 spend.validate().unwrap();
666 assert_eq!(spend.stack, vec![vec![2u8], vec![1u8], vec![2u8]]);
667 }
668
669 #[test]
670 fn test_op_pick() {
671 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3 OP_2", "OP_PICK");
672 spend.validate().unwrap();
673 assert_eq!(
674 spend.stack,
675 vec![vec![1u8], vec![2u8], vec![3u8], vec![1u8]]
676 );
677 }
678
679 #[test]
680 fn test_op_roll() {
681 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3 OP_2", "OP_ROLL");
682 spend.validate().unwrap();
683 assert_eq!(spend.stack, vec![vec![2u8], vec![3u8], vec![1u8]]);
684 }
685
686 #[test]
687 fn test_op_2dup() {
688 let mut spend = make_relaxed_spend("OP_1 OP_2", "OP_2DUP");
689 spend.validate().unwrap();
690 assert_eq!(
691 spend.stack,
692 vec![vec![1u8], vec![2u8], vec![1u8], vec![2u8]]
693 );
694 }
695
696 #[test]
697 fn test_op_3dup() {
698 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3", "OP_3DUP");
699 spend.validate().unwrap();
700 assert_eq!(spend.stack.len(), 6);
701 }
702
703 #[test]
704 fn test_op_2drop() {
705 let mut spend = make_spend("OP_1 OP_2 OP_3", "OP_2DROP");
706 assert!(spend.validate().unwrap());
707 assert_eq!(spend.stack, vec![vec![1u8]]);
708 }
709
710 #[test]
711 fn test_op_2swap() {
712 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3 OP_4", "OP_2SWAP");
713 spend.validate().unwrap();
714 assert_eq!(
715 spend.stack,
716 vec![vec![3u8], vec![4u8], vec![1u8], vec![2u8]]
717 );
718 }
719
720 #[test]
721 fn test_op_ifdup_true() {
722 let mut spend = make_relaxed_spend("OP_1", "OP_IFDUP");
723 spend.validate().unwrap();
724 assert_eq!(spend.stack, vec![vec![1u8], vec![1u8]]);
725 }
726
727 #[test]
728 fn test_op_ifdup_false() {
729 let mut spend = make_relaxed_spend("0", "OP_IFDUP");
730 spend.validate().unwrap();
731 assert_eq!(spend.stack, vec![Vec::<u8>::new()]);
732 }
733
734 #[test]
735 fn test_op_lessthan() {
736 let mut spend = make_spend("OP_1 OP_2", "OP_LESSTHAN");
737 assert!(spend.validate().unwrap());
738 }
739
740 #[test]
741 fn test_op_greaterthan() {
742 let mut spend = make_spend("OP_3 OP_2", "OP_GREATERTHAN");
743 assert!(spend.validate().unwrap());
744 }
745
746 #[test]
747 fn test_op_within() {
748 let mut spend = make_spend("OP_3 OP_2 OP_5", "OP_WITHIN");
750 assert!(spend.validate().unwrap());
751 }
752
753 #[test]
754 fn test_op_within_false() {
755 let mut spend = make_spend("OP_5 OP_2 OP_5", "OP_WITHIN");
757 assert!(!spend.validate().unwrap());
758 }
759
760 #[test]
761 fn test_op_min() {
762 let mut spend = make_spend("OP_3 OP_5", "OP_MIN OP_3 OP_EQUAL");
763 assert!(spend.validate().unwrap());
764 }
765
766 #[test]
767 fn test_op_max() {
768 let mut spend = make_spend("OP_3 OP_5", "OP_MAX OP_5 OP_EQUAL");
769 assert!(spend.validate().unwrap());
770 }
771
772 #[test]
773 fn test_op_booland() {
774 let mut spend = make_spend("OP_1 OP_1", "OP_BOOLAND");
775 assert!(spend.validate().unwrap());
776
777 let mut spend2 = make_spend("OP_1 0", "OP_BOOLAND");
778 assert!(!spend2.validate().unwrap());
779 }
780
781 #[test]
782 fn test_op_boolor() {
783 let mut spend = make_spend("0 0", "OP_BOOLOR");
784 assert!(!spend.validate().unwrap());
785
786 let mut spend2 = make_spend("OP_1 0", "OP_BOOLOR");
787 assert!(spend2.validate().unwrap());
788 }
789
790 #[test]
791 fn test_op_abs() {
792 let mut spend = make_spend("-1", "OP_ABS OP_1 OP_EQUAL");
794 assert!(spend.validate().unwrap());
795 }
796
797 #[test]
798 fn test_op_not() {
799 let mut spend = make_spend("0", "OP_NOT"); assert!(spend.validate().unwrap());
801
802 let mut spend2 = make_spend("OP_1", "OP_NOT"); assert!(!spend2.validate().unwrap());
804 }
805
806 #[test]
807 fn test_op_0notequal() {
808 let mut spend = make_spend("OP_5", "OP_0NOTEQUAL");
809 assert!(spend.validate().unwrap());
810
811 let mut spend2 = make_spend("0", "OP_0NOTEQUAL");
812 assert!(!spend2.validate().unwrap());
813 }
814
815 #[test]
816 fn test_op_negate() {
817 let mut spend = make_spend("OP_5", "OP_NEGATE OP_ABS OP_5 OP_EQUAL");
818 assert!(spend.validate().unwrap());
819 }
820
821 #[test]
822 fn test_op_1add_1sub() {
823 let mut spend = make_spend("OP_5", "OP_1ADD OP_6 OP_EQUAL");
824 assert!(spend.validate().unwrap());
825
826 let mut spend2 = make_spend("OP_5", "OP_1SUB OP_4 OP_EQUAL");
827 assert!(spend2.validate().unwrap());
828 }
829
830 #[test]
831 fn test_op_numequal() {
832 let mut spend = make_spend("OP_3 OP_3", "OP_NUMEQUAL");
833 assert!(spend.validate().unwrap());
834 }
835
836 #[test]
837 fn test_op_numequalverify() {
838 let mut spend = make_spend("OP_3 OP_3", "OP_NUMEQUALVERIFY OP_1");
839 assert!(spend.validate().unwrap());
840 }
841
842 #[test]
843 fn test_op_numnotequal() {
844 let mut spend = make_spend("OP_3 OP_4", "OP_NUMNOTEQUAL");
845 assert!(spend.validate().unwrap());
846 }
847
848 #[test]
849 fn test_op_invert() {
850 let mut spend = make_relaxed_spend("00", "OP_INVERT");
852 spend.validate().unwrap();
853 assert_eq!(spend.stack, vec![vec![0xff]]);
854 }
855
856 #[test]
857 fn test_op_and() {
858 let mut spend = make_relaxed_spend("ff 0f", "OP_AND");
859 spend.validate().unwrap();
860 assert_eq!(spend.stack, vec![vec![0x0f]]);
861 }
862
863 #[test]
864 fn test_op_or() {
865 let mut spend = make_relaxed_spend("f0 0f", "OP_OR");
866 spend.validate().unwrap();
867 assert_eq!(spend.stack, vec![vec![0xff]]);
868 }
869
870 #[test]
871 fn test_op_xor() {
872 let mut spend = make_relaxed_spend("ff ff", "OP_XOR");
873 spend.validate().unwrap();
874 assert_eq!(spend.stack, vec![vec![0x00]]);
875 }
876
877 #[test]
878 fn test_nested_if_deep() {
879 let mut spend = make_spend(
881 "OP_1 OP_1 OP_1",
882 "OP_IF OP_IF OP_IF OP_7 OP_ENDIF OP_ENDIF OP_ENDIF",
883 );
884 assert!(spend.validate().unwrap());
885 assert_eq!(spend.stack, vec![vec![7u8]]);
886 }
887
888 #[test]
889 fn test_op_codeseparator() {
890 let mut spend = make_spend("OP_1", "OP_CODESEPARATOR");
892 assert!(spend.validate().unwrap());
893 }
894
895 #[test]
896 fn test_chronicle_op_substr() {
897 let mut spend = make_relaxed_spend("0102030405 OP_1 OP_2", "OP_SUBSTR 0203 OP_EQUAL");
899 assert!(spend.validate().unwrap());
900 }
901
902 #[test]
903 fn test_chronicle_op_left() {
904 let mut spend = make_relaxed_spend("01020304 OP_2", "OP_LEFT 0102 OP_EQUAL");
905 assert!(spend.validate().unwrap());
906 }
907
908 #[test]
909 fn test_chronicle_op_right() {
910 let mut spend = make_relaxed_spend("01020304 OP_2", "OP_RIGHT 0304 OP_EQUAL");
911 assert!(spend.validate().unwrap());
912 }
913
914 #[test]
915 fn test_op_ripemd160() {
916 let mut spend = make_relaxed_spend("01", "OP_RIPEMD160 OP_SIZE");
917 spend.validate().unwrap();
918 assert_eq!(spend.stack[0].len(), 20);
919 }
920
921 #[test]
922 fn test_op_sha1() {
923 let mut spend = make_relaxed_spend("01", "OP_SHA1 OP_SIZE");
924 spend.validate().unwrap();
925 assert_eq!(spend.stack[0].len(), 20);
926 }
927
928 #[test]
929 fn test_op_checksig_empty_sig_fails() {
930 let mut spend = make_relaxed_spend("0 01", "OP_CHECKSIG");
932 spend.validate().unwrap();
933 assert_eq!(spend.stack, vec![Vec::<u8>::new()]);
935 }
936
937 #[test]
938 fn test_relaxed_mode_gating() {
939 let mut spend = make_relaxed_spend("OP_1 OP_2 OP_3", "");
941 let result = spend.validate().unwrap();
942 assert!(result); let mut spend2 = make_spend("OP_1 OP_2 OP_3", "");
946 assert!(matches!(
947 spend2.validate(),
948 Err(ScriptError::CleanStackViolation)
949 ));
950 }
951
952 #[test]
953 fn test_op_disabled_2mul() {
954 let mut spend = make_spend("OP_1", "OP_2MUL");
955 let result = spend.validate();
956 assert!(matches!(result, Err(ScriptError::DisabledOpcode(_))));
957 }
958
959 #[test]
960 fn test_op_disabled_2div() {
961 let mut spend = make_spend("OP_1", "OP_2DIV");
962 let result = spend.validate();
963 assert!(matches!(result, Err(ScriptError::DisabledOpcode(_))));
964 }
965
966 fn parse_test_asm(asm: &str) -> crate::script::script::Script {
975 use crate::script::script::Script;
976 use crate::script::script_chunk::ScriptChunk;
977
978 let asm = asm.trim();
979 if asm.is_empty() {
980 return Script::new();
981 }
982
983 let mut chunks = Vec::new();
984 let tokens: Vec<&str> = asm.split_whitespace().collect();
985 let mut i = 0;
986
987 while i < tokens.len() {
988 let token = tokens[i];
989
990 if token == "0" {
992 chunks.push(ScriptChunk::new_opcode(Op::Op0));
993 i += 1;
994 continue;
995 }
996 if token == "-1" {
997 chunks.push(ScriptChunk::new_opcode(Op::Op1Negate));
998 i += 1;
999 continue;
1000 }
1001
1002 if token.starts_with('\'') {
1004 let text = if token.ends_with('\'') && token.len() > 1 {
1005 &token[1..token.len() - 1]
1006 } else {
1007 let mut s = token[1..].to_string();
1009 loop {
1010 i += 1;
1011 if i >= tokens.len() {
1012 break;
1013 }
1014 s.push(' ');
1015 s.push_str(tokens[i]);
1016 if tokens[i].ends_with('\'') {
1017 s.truncate(s.len() - 1);
1018 break;
1019 }
1020 }
1021 i += 1;
1022 let data = s.into_bytes();
1023 let len = data.len();
1024 let op_byte = if len < 0x4c { len as u8 } else { 0x4c };
1025 chunks.push(ScriptChunk::new_raw(op_byte, Some(data)));
1026 continue;
1027 };
1028 let data = text.as_bytes().to_vec();
1029 let len = data.len();
1030 let op_byte = if len < 0x4c { len as u8 } else { 0x4c };
1031 chunks.push(ScriptChunk::new_raw(op_byte, Some(data)));
1032 i += 1;
1033 continue;
1034 }
1035
1036 if token.starts_with("0x") || token.starts_with("0X") {
1038 let hex = &token[2..];
1039 if let Ok(push_len) = usize::from_str_radix(hex, 16) {
1040 if push_len > 0 && push_len <= 0x4e && i + 1 < tokens.len() {
1042 let next = tokens[i + 1];
1043 if next.starts_with("0x") || next.starts_with("0X") {
1044 let data_hex = &next[2..];
1045 if let Ok(data) = hex_decode(data_hex) {
1046 chunks.push(ScriptChunk::new_raw(push_len as u8, Some(data)));
1047 i += 2;
1048 continue;
1049 }
1050 }
1051 }
1052 if push_len <= 0xff {
1054 let op = Op::from(push_len as u8);
1055 chunks.push(ScriptChunk::new_raw(push_len as u8, None));
1056 let _ = op; i += 1;
1058 continue;
1059 }
1060 }
1061 i += 1;
1062 continue;
1063 }
1064
1065 if token == "PUSHDATA1" || token == "OP_PUSHDATA1" {
1067 if i + 2 < tokens.len() {
1068 let _len_hex = tokens[i + 1].strip_prefix("0x").unwrap_or(tokens[i + 1]);
1069 let data_hex = tokens[i + 2].strip_prefix("0x").unwrap_or(tokens[i + 2]);
1070 if let Ok(data) = hex_decode(data_hex) {
1071 chunks.push(ScriptChunk::new_raw(0x4c, Some(data)));
1072 i += 3;
1073 continue;
1074 }
1075 }
1076 i += 1;
1077 continue;
1078 }
1079 if token == "PUSHDATA2" || token == "OP_PUSHDATA2" {
1080 if i + 2 < tokens.len() {
1081 let data_hex = tokens[i + 2].strip_prefix("0x").unwrap_or(tokens[i + 2]);
1082 if let Ok(data) = hex_decode(data_hex) {
1083 chunks.push(ScriptChunk::new_raw(0x4d, Some(data)));
1084 i += 3;
1085 continue;
1086 }
1087 }
1088 i += 1;
1089 continue;
1090 }
1091 if token == "PUSHDATA4" || token == "OP_PUSHDATA4" {
1092 if i + 2 < tokens.len() {
1093 let data_hex = tokens[i + 2].strip_prefix("0x").unwrap_or(tokens[i + 2]);
1094 if let Ok(data) = hex_decode(data_hex) {
1095 chunks.push(ScriptChunk::new_raw(0x4e, Some(data)));
1096 i += 3;
1097 continue;
1098 }
1099 }
1100 i += 1;
1101 continue;
1102 }
1103
1104 if let Some(op) =
1106 Op::from_name(token).or_else(|| Op::from_name(&format!("OP_{}", token)))
1107 {
1108 chunks.push(ScriptChunk::new_opcode(op));
1109 i += 1;
1110 continue;
1111 }
1112
1113 if let Ok(n) = token.parse::<i64>() {
1115 use crate::primitives::big_number::BigNumber;
1116 let bn = BigNumber::from_number(n);
1117 let data = bn.to_script_num();
1118 if data.is_empty() {
1119 chunks.push(ScriptChunk::new_opcode(Op::Op0));
1120 } else {
1121 let len = data.len();
1122 let op_byte = if len < 0x4c { len as u8 } else { 0x4c };
1123 chunks.push(ScriptChunk::new_raw(op_byte, Some(data)));
1124 }
1125 i += 1;
1126 continue;
1127 }
1128
1129 i += 1;
1131 }
1132
1133 Script::from_chunks(chunks)
1134 }
1135
1136 fn hex_decode(hex: &str) -> Result<Vec<u8>, ()> {
1137 if hex.len() % 2 != 0 {
1138 return Err(());
1139 }
1140 let mut bytes = Vec::with_capacity(hex.len() / 2);
1141 for i in (0..hex.len()).step_by(2) {
1142 match u8::from_str_radix(&hex[i..i + 2], 16) {
1143 Ok(b) => bytes.push(b),
1144 Err(_) => return Err(()),
1145 }
1146 }
1147 Ok(bytes)
1148 }
1149
1150 #[test]
1151 fn test_script_tests_json() {
1152 let json_str = include_str!("../../test-vectors/script_tests.json");
1153 let entries: Vec<serde_json::Value> =
1154 serde_json::from_str(json_str).expect("failed to parse script_tests.json");
1155
1156 let mut passed = 0;
1157 let mut failed = 0;
1158 let mut skipped = 0;
1159
1160 for entry in &entries {
1161 let arr = match entry.as_array() {
1162 Some(a) => a,
1163 None => continue,
1164 };
1165
1166 if arr.len() < 4 {
1168 skipped += 1;
1169 continue;
1170 }
1171
1172 let (sig_asm, pubkey_asm, flags_str, expected) = if arr[0].is_array() {
1174 if arr.len() < 5 {
1176 skipped += 1;
1177 continue;
1178 }
1179 (
1180 arr[1].as_str().unwrap_or(""),
1181 arr[2].as_str().unwrap_or(""),
1182 arr[3].as_str().unwrap_or(""),
1183 arr[4].as_str().unwrap_or(""),
1184 )
1185 } else {
1186 (
1187 arr[0].as_str().unwrap_or(""),
1188 arr[1].as_str().unwrap_or(""),
1189 arr[2].as_str().unwrap_or(""),
1190 arr[3].as_str().unwrap_or(""),
1191 )
1192 };
1193
1194 let flags: Vec<&str> = if flags_str.is_empty() {
1196 vec![]
1197 } else {
1198 flags_str.split(',').collect()
1199 };
1200
1201 let has_strictenc = flags.contains(&"STRICTENC");
1202 let has_utxo_after_genesis = flags.contains(&"UTXO_AFTER_GENESIS");
1203 let has_p2sh = flags.contains(&"P2SH");
1204 let _has_sigpushonly = flags.contains(&"SIGPUSHONLY");
1205 let _has_minimaldata = flags.contains(&"MINIMALDATA");
1206
1207 if has_utxo_after_genesis || has_p2sh {
1210 skipped += 1;
1211 continue;
1212 }
1213
1214 let unlocking_script = UnlockingScript::from_script(parse_test_asm(sig_asm));
1216 let locking_script = LockingScript::from_script(parse_test_asm(pubkey_asm));
1217
1218 let version = if has_strictenc { 1u32 } else { 2u32 };
1221
1222 let mut spend = Spend::new(SpendParams {
1223 locking_script,
1224 unlocking_script,
1225 source_txid: "00".repeat(32),
1226 source_output_index: 0,
1227 source_satoshis: 0,
1228 transaction_version: version,
1229 transaction_lock_time: 0,
1230 transaction_sequence: 0xffffffff,
1231 other_inputs: vec![],
1232 other_outputs: vec![],
1233 input_index: 0,
1234 });
1235
1236 let result = spend.validate();
1237
1238 let expected_ok = expected == "OK";
1239
1240 match (expected_ok, &result) {
1241 (true, Ok(true)) => passed += 1,
1242 (true, Ok(false)) => {
1243 failed += 1;
1245 }
1246 (true, Err(_)) => {
1247 failed += 1;
1248 }
1249 (false, Err(_)) => passed += 1,
1250 (false, Ok(false)) => passed += 1, (false, Ok(true)) => {
1252 failed += 1;
1253 }
1254 }
1255 }
1256
1257 println!(
1258 "script_tests.json: {} passed, {} failed, {} skipped",
1259 passed, failed, skipped
1260 );
1261
1262 let total_run = passed + failed;
1264 let pass_rate = if total_run > 0 {
1265 (passed as f64 / total_run as f64) * 100.0
1266 } else {
1267 0.0
1268 };
1269 println!("Pass rate: {:.1}% ({}/{})", pass_rate, passed, total_run);
1270
1271 assert!(
1273 pass_rate >= 50.0,
1274 "script_tests.json pass rate too low: {:.1}% ({}/{})",
1275 pass_rate,
1276 passed,
1277 total_run
1278 );
1279 }
1280}