1use std::cell::RefCell;
8
9use super::chunk::ScriptChunk;
10use super::op::*;
11use crate::primitives::{from_hex, to_hex};
12use crate::Result;
13
14#[derive(Debug, Clone)]
19pub struct Script {
20 chunks: RefCell<Vec<ScriptChunk>>,
22 parsed: RefCell<bool>,
24 raw_bytes_cache: RefCell<Option<Vec<u8>>>,
26 hex_cache: RefCell<Option<String>>,
28}
29
30impl Script {
31 pub fn new() -> Self {
33 Self {
34 chunks: RefCell::new(Vec::new()),
35 parsed: RefCell::new(true),
36 raw_bytes_cache: RefCell::new(None),
37 hex_cache: RefCell::new(None),
38 }
39 }
40
41 pub fn from_chunks(chunks: Vec<ScriptChunk>) -> Self {
43 Self {
44 chunks: RefCell::new(chunks),
45 parsed: RefCell::new(true),
46 raw_bytes_cache: RefCell::new(None),
47 hex_cache: RefCell::new(None),
48 }
49 }
50
51 pub fn from_asm(asm: &str) -> Result<Self> {
61 let mut chunks = Vec::new();
62
63 if asm.is_empty() {
64 return Ok(Self::from_chunks(chunks));
65 }
66
67 let tokens: Vec<&str> = asm.split(' ').filter(|s| !s.is_empty()).collect();
68 let mut i = 0;
69
70 while i < tokens.len() {
71 let token = tokens[i];
72
73 let op_code_num = name_to_opcode(token);
75
76 if token == "0" {
78 chunks.push(ScriptChunk::new_opcode(OP_0));
79 i += 1;
80 } else if token == "-1" {
81 chunks.push(ScriptChunk::new_opcode(OP_1NEGATE));
82 i += 1;
83 } else if op_code_num.is_none() {
84 let mut hex = token.to_string();
86 #[allow(unknown_lints, clippy::manual_is_multiple_of)]
87 if hex.len() % 2 != 0 {
88 hex = format!("0{}", hex);
89 }
90
91 let data = from_hex(&hex)?;
92 let len = data.len();
93
94 let op = if len < OP_PUSHDATA1 as usize {
95 len as u8
96 } else if len < 256 {
97 OP_PUSHDATA1
98 } else if len < 65536 {
99 OP_PUSHDATA2
100 } else {
101 OP_PUSHDATA4
102 };
103
104 chunks.push(ScriptChunk::new(op, Some(data)));
105 i += 1;
106 } else if let Some(op) = op_code_num {
107 if op == OP_PUSHDATA1 || op == OP_PUSHDATA2 || op == OP_PUSHDATA4 {
109 if i + 2 < tokens.len() {
111 let data = from_hex(tokens[i + 2])?;
112 chunks.push(ScriptChunk::new(op, Some(data)));
113 i += 3;
114 } else {
115 chunks.push(ScriptChunk::new_opcode(op));
116 i += 1;
117 }
118 } else {
119 chunks.push(ScriptChunk::new_opcode(op));
120 i += 1;
121 }
122 } else {
123 i += 1;
124 }
125 }
126
127 Ok(Self::from_chunks(chunks))
128 }
129
130 pub fn from_hex(hex: &str) -> Result<Self> {
140 if hex.is_empty() {
141 return Ok(Self::new());
142 }
143
144 let bin = from_hex(hex)?;
145 let raw_bytes = bin.clone();
146 let hex_lower = hex.to_lowercase();
147
148 Ok(Self {
149 chunks: RefCell::new(Vec::new()),
150 parsed: RefCell::new(false),
151 raw_bytes_cache: RefCell::new(Some(raw_bytes)),
152 hex_cache: RefCell::new(Some(hex_lower)),
153 })
154 }
155
156 pub fn from_binary(bin: &[u8]) -> Result<Self> {
166 Ok(Self {
167 chunks: RefCell::new(Vec::new()),
168 parsed: RefCell::new(false),
169 raw_bytes_cache: RefCell::new(Some(bin.to_vec())),
170 hex_cache: RefCell::new(None),
171 })
172 }
173
174 fn ensure_parsed(&self) {
176 if *self.parsed.borrow() {
177 return;
178 }
179
180 if let Some(ref bytes) = *self.raw_bytes_cache.borrow() {
181 *self.chunks.borrow_mut() = Self::parse_chunks(bytes);
182 }
183 *self.parsed.borrow_mut() = true;
184 }
185
186 pub fn truncated_push(&self) -> Option<usize> {
199 truncated_push_index(&self.to_binary())
200 }
201
202 fn parse_chunks(bytes: &[u8]) -> Vec<ScriptChunk> {
203 let mut chunks = Vec::new();
204 let length = bytes.len();
205 let mut pos = 0;
206 let mut in_conditional_block = 0i32;
207
208 while pos < length {
209 let op = bytes[pos];
210 pos += 1;
211
212 if op == OP_RETURN && in_conditional_block == 0 {
214 let data = if pos < length {
216 Some(bytes[pos..].to_vec())
217 } else {
218 None
219 };
220 chunks.push(ScriptChunk::new(op, data));
221 break;
222 }
223
224 if op == OP_IF || op == OP_NOTIF || op == OP_VERIF || op == OP_VERNOTIF {
226 in_conditional_block += 1;
227 } else if op == OP_ENDIF {
228 in_conditional_block = (in_conditional_block - 1).max(0);
229 }
230
231 if op > 0 && op < OP_PUSHDATA1 {
233 let len = op as usize;
234 let end = (pos + len).min(length);
235 let data = bytes[pos..end].to_vec();
236 chunks.push(ScriptChunk::new(op, Some(data)));
237 pos = end;
238 } else if op == OP_PUSHDATA1 {
239 let len = if pos < length { bytes[pos] as usize } else { 0 };
240 pos = (pos + 1).min(length);
246 let end = (pos + len).min(length);
247 let data = bytes[pos..end].to_vec();
248 chunks.push(ScriptChunk::new(op, Some(data)));
249 pos = end;
250 } else if op == OP_PUSHDATA2 {
251 let b0 = if pos < length { bytes[pos] as usize } else { 0 };
252 let b1 = if pos + 1 < length {
253 bytes[pos + 1] as usize
254 } else {
255 0
256 };
257 let len = b0 | (b1 << 8);
258 pos = (pos + 2).min(length);
259 let end = (pos + len).min(length);
260 let data = bytes[pos..end].to_vec();
261 chunks.push(ScriptChunk::new(op, Some(data)));
262 pos = end;
263 } else if op == OP_PUSHDATA4 {
264 let b0 = if pos < length { bytes[pos] as u32 } else { 0 };
265 let b1 = if pos + 1 < length {
266 bytes[pos + 1] as u32
267 } else {
268 0
269 };
270 let b2 = if pos + 2 < length {
271 bytes[pos + 2] as u32
272 } else {
273 0
274 };
275 let b3 = if pos + 3 < length {
276 bytes[pos + 3] as u32
277 } else {
278 0
279 };
280 let len = (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) as usize;
281 pos = (pos + 4).min(length);
282 let end = (pos + len).min(length);
283 let data = bytes[pos..end].to_vec();
284 chunks.push(ScriptChunk::new(op, Some(data)));
285 pos = end;
286 } else {
287 chunks.push(ScriptChunk::new_opcode(op));
288 }
289 }
290
291 chunks
292 }
293
294 pub fn to_asm(&self) -> String {
296 self.ensure_parsed();
297 let chunks = self.chunks.borrow();
298 let parts: Vec<String> = chunks.iter().map(|c| c.to_asm()).collect();
299 parts.join(" ")
300 }
301
302 pub fn to_hex(&self) -> String {
304 if let Some(ref hex) = *self.hex_cache.borrow() {
305 return hex.clone();
306 }
307
308 let bytes = self.to_binary();
309 let hex = to_hex(&bytes);
310 *self.hex_cache.borrow_mut() = Some(hex.clone());
311 hex
312 }
313
314 pub fn to_binary(&self) -> Vec<u8> {
316 if let Some(ref bytes) = *self.raw_bytes_cache.borrow() {
317 return bytes.clone();
318 }
319
320 self.ensure_parsed();
321 let bytes = self.serialize_chunks_to_bytes();
322 *self.raw_bytes_cache.borrow_mut() = Some(bytes.clone());
323 bytes
324 }
325
326 fn compute_serialized_length(chunks: &[ScriptChunk]) -> usize {
328 let mut total = 0;
329 for chunk in chunks {
330 total += 1; if let Some(ref data) = chunk.data {
332 let len = data.len();
333 if chunk.op == OP_RETURN {
334 total += len;
335 break;
336 }
337 if chunk.op < OP_PUSHDATA1 {
338 total += len;
339 } else if chunk.op == OP_PUSHDATA1 {
340 total += 1 + len;
341 } else if chunk.op == OP_PUSHDATA2 {
342 total += 2 + len;
343 } else if chunk.op == OP_PUSHDATA4 {
344 total += 4 + len;
345 }
346 }
347 }
348 total
349 }
350
351 fn serialize_chunks_to_bytes(&self) -> Vec<u8> {
353 let chunks = self.chunks.borrow();
354 let total_length = Self::compute_serialized_length(&chunks);
355 let mut bytes = Vec::with_capacity(total_length);
356
357 for chunk in chunks.iter() {
358 bytes.push(chunk.op);
359
360 if let Some(ref data) = chunk.data {
361 if chunk.op == OP_RETURN {
362 bytes.extend_from_slice(data);
363 break;
364 }
365
366 if chunk.op < OP_PUSHDATA1 {
367 bytes.extend_from_slice(data);
368 } else if chunk.op == OP_PUSHDATA1 {
369 bytes.push(data.len() as u8);
370 bytes.extend_from_slice(data);
371 } else if chunk.op == OP_PUSHDATA2 {
372 let len = data.len() as u16;
373 bytes.push((len & 0xff) as u8);
374 bytes.push(((len >> 8) & 0xff) as u8);
375 bytes.extend_from_slice(data);
376 } else if chunk.op == OP_PUSHDATA4 {
377 let len = data.len() as u32;
378 bytes.push((len & 0xff) as u8);
379 bytes.push(((len >> 8) & 0xff) as u8);
380 bytes.push(((len >> 16) & 0xff) as u8);
381 bytes.push(((len >> 24) & 0xff) as u8);
382 bytes.extend_from_slice(data);
383 }
384 }
385 }
386
387 bytes
388 }
389
390 fn invalidate_caches(&self) {
392 *self.raw_bytes_cache.borrow_mut() = None;
393 *self.hex_cache.borrow_mut() = None;
394 }
395
396 pub fn write_script(&mut self, script: &Script) -> &mut Self {
398 self.ensure_parsed();
402 self.invalidate_caches();
403 script.ensure_parsed();
404 {
405 let mut chunks = self.chunks.borrow_mut();
406 chunks.extend(script.chunks.borrow().iter().cloned());
407 }
408 self
409 }
410
411 pub fn write_opcode(&mut self, op: u8) -> &mut Self {
413 self.ensure_parsed();
417 self.invalidate_caches();
418 self.chunks.borrow_mut().push(ScriptChunk::new_opcode(op));
419 self
420 }
421
422 #[allow(clippy::panic)] pub fn write_bin(&mut self, bin: &[u8]) -> &mut Self {
429 self.ensure_parsed();
433 self.invalidate_caches();
434
435 let len = bin.len();
436 let op: u8;
437 let data: Option<Vec<u8>>;
438
439 if len == 0 {
440 op = OP_0;
441 data = None;
442 } else if len < OP_PUSHDATA1 as usize {
443 op = len as u8;
444 data = Some(bin.to_vec());
445 } else if len < 256 {
446 op = OP_PUSHDATA1;
447 data = Some(bin.to_vec());
448 } else if len < 65536 {
449 op = OP_PUSHDATA2;
450 data = Some(bin.to_vec());
451 } else if (len as u64) < 0x100000000 {
452 op = OP_PUSHDATA4;
453 data = Some(bin.to_vec());
454 } else {
455 panic!("Data too large to push");
456 }
457
458 self.chunks.borrow_mut().push(ScriptChunk::new(op, data));
459 self
460 }
461
462 pub fn write_number(&mut self, num: i64) -> &mut Self {
467 self.ensure_parsed();
471 self.invalidate_caches();
472
473 if num == 0 {
474 self.chunks.borrow_mut().push(ScriptChunk::new_opcode(OP_0));
475 } else if num == -1 {
476 self.chunks
477 .borrow_mut()
478 .push(ScriptChunk::new_opcode(OP_1NEGATE));
479 } else if (1..=16).contains(&num) {
480 let op = (OP_1 as i64 + num - 1) as u8;
481 self.chunks.borrow_mut().push(ScriptChunk::new_opcode(op));
482 } else {
483 let data = Self::encode_script_number(num);
485 let len = data.len();
486 let op = if len < OP_PUSHDATA1 as usize {
487 len as u8
488 } else if len < 256 {
489 OP_PUSHDATA1
490 } else {
491 OP_PUSHDATA2
492 };
493 self.chunks
494 .borrow_mut()
495 .push(ScriptChunk::new(op, Some(data)));
496 }
497
498 self
499 }
500
501 fn encode_script_number(num: i64) -> Vec<u8> {
503 if num == 0 {
504 return vec![];
505 }
506
507 let negative = num < 0;
508 let mut abs_val = num.unsigned_abs();
509 let mut result = Vec::new();
510
511 while abs_val > 0 {
512 result.push((abs_val & 0xff) as u8);
513 abs_val >>= 8;
514 }
515
516 if result.last().is_some_and(|&b| b & 0x80 != 0) {
519 result.push(if negative { 0x80 } else { 0x00 });
520 } else if negative {
521 if let Some(last) = result.last_mut() {
523 *last |= 0x80;
524 }
525 }
526
527 result
528 }
529
530 pub fn remove_codeseparators(&mut self) -> &mut Self {
532 self.ensure_parsed();
536 self.invalidate_caches();
537
538 {
539 let mut chunks = self.chunks.borrow_mut();
540 chunks.retain(|chunk| chunk.op != OP_CODESEPARATOR);
541 }
542
543 self
544 }
545
546 pub fn find_and_delete(&mut self, script: &Script) -> &mut Self {
548 self.ensure_parsed();
552 self.invalidate_caches();
553 script.ensure_parsed();
554
555 let target_hex = script.to_hex();
556 {
557 let mut chunks = self.chunks.borrow_mut();
558
559 chunks.retain(|chunk| {
560 let chunk_script = Script::from_chunks(vec![chunk.clone()]);
561 chunk_script.to_hex() != target_hex
562 });
563 }
564
565 self
566 }
567
568 pub fn is_push_only(&self) -> bool {
570 self.ensure_parsed();
571 let chunks = self.chunks.borrow();
572 chunks.iter().all(|chunk| chunk.op <= OP_16)
573 }
574
575 pub fn chunks(&self) -> Vec<ScriptChunk> {
577 self.ensure_parsed();
578 self.chunks.borrow().clone()
579 }
580
581 pub fn len(&self) -> usize {
583 self.ensure_parsed();
584 self.chunks.borrow().len()
585 }
586
587 pub fn is_empty(&self) -> bool {
589 self.len() == 0
590 }
591
592 pub fn set_chunk_opcode(&mut self, index: usize, op: u8) -> &mut Self {
594 self.ensure_parsed();
598 self.invalidate_caches();
599 {
600 let mut chunks = self.chunks.borrow_mut();
601 if index < chunks.len() {
602 chunks[index] = ScriptChunk::new_opcode(op);
603 }
604 }
605 self
606 }
607
608 pub fn is_locking_script(&self) -> bool {
611 false
612 }
613
614 pub fn is_unlocking_script(&self) -> bool {
617 false
618 }
619
620 pub fn is_p2pkh(&self) -> bool {
623 let chunks = self.chunks();
624 chunks.len() == 5
625 && chunks[0].op == OP_DUP
626 && chunks[1].op == OP_HASH160
627 && chunks[2]
628 .data
629 .as_ref()
630 .map(|d| d.len() == 20)
631 .unwrap_or(false)
632 && chunks[3].op == OP_EQUALVERIFY
633 && chunks[4].op == OP_CHECKSIG
634 }
635
636 pub fn is_p2pk(&self) -> bool {
639 let chunks = self.chunks();
640 chunks.len() == 2
641 && chunks[0]
642 .data
643 .as_ref()
644 .map(|d| d.len() == 33 || d.len() == 65)
645 .unwrap_or(false)
646 && chunks[1].op == OP_CHECKSIG
647 }
648
649 pub fn is_p2sh(&self) -> bool {
652 let chunks = self.chunks();
653 chunks.len() == 3
654 && chunks[0].op == OP_HASH160
655 && chunks[1]
656 .data
657 .as_ref()
658 .map(|d| d.len() == 20)
659 .unwrap_or(false)
660 && chunks[2].op == OP_EQUAL
661 }
662
663 pub fn is_data(&self) -> bool {
665 let chunks = self.chunks();
666 if chunks.is_empty() {
667 return false;
668 }
669 if chunks[0].op == OP_RETURN {
671 return true;
672 }
673 chunks.len() >= 2 && chunks[0].op == OP_FALSE && chunks[1].op == OP_RETURN
675 }
676
677 pub fn is_safe_data_carrier(&self) -> bool {
682 let chunks = self.chunks();
683 chunks.len() >= 2 && chunks[0].op == OP_FALSE && chunks[1].op == OP_RETURN
684 }
685
686 pub fn is_multisig(&self) -> Option<(u8, u8)> {
689 let chunks = self.chunks();
690 if chunks.len() < 4 {
691 return None;
692 }
693
694 if chunks.last()?.op != OP_CHECKMULTISIG {
696 return None;
697 }
698
699 let n = Self::opcode_to_small_int(chunks[chunks.len() - 2].op)?;
701
702 let m = Self::opcode_to_small_int(chunks[0].op)?;
704
705 if chunks.len() != (n as usize) + 3 {
707 return None;
708 }
709
710 for chunk in chunks.iter().take((n as usize) + 1).skip(1) {
712 let data = chunk.data.as_ref()?;
713 if data.len() != 33 && data.len() != 65 {
714 return None;
715 }
716 }
717
718 Some((m, n))
719 }
720
721 pub fn opcode_to_small_int(op: u8) -> Option<u8> {
723 if (OP_1..=OP_16).contains(&op) {
724 Some(op - OP_1 + 1)
725 } else {
726 None
727 }
728 }
729
730 pub fn extract_pubkey_hash(&self) -> Option<[u8; 20]> {
732 if !self.is_p2pkh() {
733 return None;
734 }
735 let chunks = self.chunks();
736 let data = chunks[2].data.as_ref()?;
737 let mut hash = [0u8; 20];
738 hash.copy_from_slice(data);
739 Some(hash)
740 }
741
742 pub fn get_public_key(&self) -> Option<Vec<u8>> {
749 if !self.is_p2pk() {
750 return None;
751 }
752 let chunks = self.chunks();
753 chunks[0].data.clone()
754 }
755
756 pub fn get_public_key_hex(&self) -> Option<String> {
763 self.get_public_key()
764 .map(|bytes| crate::primitives::to_hex(&bytes))
765 }
766}
767
768impl Default for Script {
769 fn default() -> Self {
770 Self::new()
771 }
772}
773
774fn truncated_push_index(bytes: &[u8]) -> Option<usize> {
778 let length = bytes.len();
779 let mut pos = 0;
780 let mut index = 0usize;
781 let mut in_conditional_block = 0i32;
782 while pos < length {
783 let op = bytes[pos];
784 pos += 1;
785 if op == OP_RETURN && in_conditional_block == 0 {
786 return None;
787 }
788 if op == OP_IF || op == OP_NOTIF || op == OP_VERIF || op == OP_VERNOTIF {
789 in_conditional_block += 1;
790 } else if op == OP_ENDIF {
791 in_conditional_block = (in_conditional_block - 1).max(0);
792 }
793 let declared: Option<usize> = if op > 0 && op < OP_PUSHDATA1 {
794 Some(op as usize)
795 } else if op == OP_PUSHDATA1 {
796 if pos + 1 > length {
797 return Some(index);
798 }
799 let n = bytes[pos] as usize;
800 pos += 1;
801 Some(n)
802 } else if op == OP_PUSHDATA2 {
803 if pos + 2 > length {
804 return Some(index);
805 }
806 let n = bytes[pos] as usize | ((bytes[pos + 1] as usize) << 8);
807 pos += 2;
808 Some(n)
809 } else if op == OP_PUSHDATA4 {
810 if pos + 4 > length {
811 return Some(index);
812 }
813 let n = bytes[pos] as usize
814 | ((bytes[pos + 1] as usize) << 8)
815 | ((bytes[pos + 2] as usize) << 16)
816 | ((bytes[pos + 3] as usize) << 24);
817 pos += 4;
818 Some(n)
819 } else {
820 None
821 };
822 if let Some(n) = declared {
823 if pos + n > length {
824 return Some(index);
825 }
826 pos += n;
827 }
828 index += 1;
829 }
830 None
831}
832
833impl PartialEq for Script {
834 fn eq(&self, other: &Self) -> bool {
835 self.to_binary() == other.to_binary()
836 }
837}
838
839impl Eq for Script {}
840
841#[cfg(test)]
842mod tests {
843 use super::*;
844
845 #[test]
846 fn test_new_empty_script() {
847 let script = Script::new();
848 assert!(script.is_empty());
849 assert_eq!(script.to_hex(), "");
850 assert_eq!(script.to_asm(), "");
851 }
852
853 #[test]
854 fn test_from_hex() {
855 let hex = "76a914000000000000000000000000000000000000000088ac";
857 let script = Script::from_hex(hex).unwrap();
858 assert_eq!(script.to_hex(), hex);
859 }
860
861 #[test]
862 fn test_from_binary() {
863 let bin = vec![0x76, 0xa9]; let script = Script::from_binary(&bin).unwrap();
865 assert_eq!(script.to_binary(), bin);
866 }
867
868 #[test]
869 fn test_from_asm_simple() {
870 let asm = "OP_DUP OP_HASH160";
871 let script = Script::from_asm(asm).unwrap();
872 assert_eq!(script.to_hex(), "76a9");
873 }
874
875 #[test]
876 fn test_from_asm_with_data() {
877 let asm =
878 "OP_DUP OP_HASH160 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG";
879 let script = Script::from_asm(asm).unwrap();
880 assert_eq!(
881 script.to_hex(),
882 "76a914000000000000000000000000000000000000000088ac"
883 );
884 }
885
886 #[test]
887 fn test_from_asm_special_values() {
888 let script = Script::from_asm("0").unwrap();
889 assert_eq!(script.to_hex(), "00");
890
891 let script = Script::from_asm("-1").unwrap();
892 assert_eq!(script.to_hex(), "4f");
893 }
894
895 #[test]
896 fn test_to_asm() {
897 let hex = "76a914000000000000000000000000000000000000000088ac";
898 let script = Script::from_hex(hex).unwrap();
899 let asm = script.to_asm();
900 assert!(asm.contains("OP_DUP"));
901 assert!(asm.contains("OP_HASH160"));
902 assert!(asm.contains("OP_EQUALVERIFY"));
903 assert!(asm.contains("OP_CHECKSIG"));
904 }
905
906 #[test]
907 fn test_write_opcode() {
908 let mut script = Script::new();
909 script.write_opcode(OP_DUP).write_opcode(OP_HASH160);
910 assert_eq!(script.to_hex(), "76a9");
911 }
912
913 #[test]
914 fn test_write_bin() {
915 let mut script = Script::new();
916 script.write_bin(&[0x01, 0x02, 0x03]);
917 assert_eq!(script.to_hex(), "03010203");
918 }
919
920 #[test]
921 fn test_write_bin_empty() {
922 let mut script = Script::new();
923 script.write_bin(&[]);
924 assert_eq!(script.to_hex(), "00"); }
926
927 #[test]
928 fn test_write_number() {
929 let mut script = Script::new();
930 script.write_number(0);
931 assert_eq!(script.to_hex(), "00"); let mut script = Script::new();
934 script.write_number(-1);
935 assert_eq!(script.to_hex(), "4f"); let mut script = Script::new();
938 script.write_number(1);
939 assert_eq!(script.to_hex(), "51"); let mut script = Script::new();
942 script.write_number(16);
943 assert_eq!(script.to_hex(), "60"); }
945
946 #[test]
947 fn test_write_number_large() {
948 let mut script = Script::new();
949 script.write_number(127);
950 assert_eq!(script.to_hex(), "017f");
951
952 let mut script = Script::new();
953 script.write_number(128);
954 assert_eq!(script.to_hex(), "028000"); let mut script = Script::new();
957 script.write_number(-127);
958 assert_eq!(script.to_hex(), "01ff");
959 }
960
961 #[test]
962 fn test_is_push_only() {
963 let script = Script::from_asm("OP_1 OP_2 OP_3").unwrap();
964 assert!(script.is_push_only());
965
966 let script = Script::from_asm("OP_DUP OP_HASH160").unwrap();
967 assert!(!script.is_push_only());
968 }
969
970 #[test]
971 fn test_remove_codeseparators() {
972 let mut script =
973 Script::from_asm("OP_DUP OP_CODESEPARATOR OP_HASH160 OP_CODESEPARATOR").unwrap();
974 script.remove_codeseparators();
975 assert_eq!(script.to_asm(), "OP_DUP OP_HASH160");
976 }
977
978 #[test]
979 fn test_find_and_delete() {
980 let mut script = Script::from_asm("OP_DUP OP_HASH160 OP_DUP").unwrap();
981 let target = Script::from_asm("OP_DUP").unwrap();
982 script.find_and_delete(&target);
983 assert_eq!(script.to_asm(), "OP_HASH160");
984 }
985
986 #[test]
987 fn test_chunks() {
988 let script = Script::from_asm("OP_DUP OP_HASH160").unwrap();
989 let chunks = script.chunks();
990 assert_eq!(chunks.len(), 2);
991 assert_eq!(chunks[0].op, OP_DUP);
992 assert_eq!(chunks[1].op, OP_HASH160);
993 }
994
995 #[test]
996 fn test_write_script() {
997 let mut script1 = Script::from_asm("OP_DUP").unwrap();
998 let script2 = Script::from_asm("OP_HASH160").unwrap();
999 script1.write_script(&script2);
1000 assert_eq!(script1.to_asm(), "OP_DUP OP_HASH160");
1001 }
1002
1003 #[test]
1004 fn test_op_return_parsing() {
1005 let hex = "6a0568656c6c6f"; let script = Script::from_hex(hex).unwrap();
1008 let chunks = script.chunks();
1009 assert_eq!(chunks.len(), 1);
1010 assert_eq!(chunks[0].op, OP_RETURN);
1011 assert!(chunks[0].data.is_some());
1013 }
1014
1015 #[test]
1016 fn test_pushdata1() {
1017 let data = vec![0u8; 100];
1019 let mut script = Script::new();
1020 script.write_bin(&data);
1021 let bytes = script.to_binary();
1022 assert_eq!(bytes[0], OP_PUSHDATA1);
1023 assert_eq!(bytes[1], 100);
1024 assert_eq!(&bytes[2..], &data[..]);
1025 }
1026
1027 #[test]
1028 fn test_pushdata2() {
1029 let data = vec![0u8; 300];
1031 let mut script = Script::new();
1032 script.write_bin(&data);
1033 let bytes = script.to_binary();
1034 assert_eq!(bytes[0], OP_PUSHDATA2);
1035 assert_eq!(bytes[1], (300 & 0xff) as u8);
1036 assert_eq!(bytes[2], ((300 >> 8) & 0xff) as u8);
1037 assert_eq!(&bytes[3..], &data[..]);
1038 }
1039
1040 #[test]
1041 fn test_roundtrip_hex() {
1042 let hex = "76a914000000000000000000000000000000000000000088ac";
1043 let script = Script::from_hex(hex).unwrap();
1044 assert_eq!(script.to_hex(), hex);
1045 }
1046
1047 #[test]
1048 fn test_equality() {
1049 let script1 = Script::from_hex("76a9").unwrap();
1050 let script2 = Script::from_asm("OP_DUP OP_HASH160").unwrap();
1051 assert_eq!(script1, script2);
1052 }
1053}
1054
1055#[cfg(test)]
1056mod script_type_tests {
1057 use super::*;
1058
1059 #[test]
1060 fn test_is_p2pkh() {
1061 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1063 assert!(p2pkh.is_p2pkh());
1064
1065 let wrong_len = Script::from_hex("76a9140000000000000000000088ac").unwrap();
1067 assert!(!wrong_len.is_p2pkh());
1068
1069 let no_dup = Script::from_hex("a914000000000000000000000000000000000000000088ac").unwrap();
1071 assert!(!no_dup.is_p2pkh());
1072
1073 let empty = Script::new();
1075 assert!(!empty.is_p2pkh());
1076 }
1077
1078 #[test]
1079 fn test_is_p2pk() {
1080 let mut p2pk_compressed = Script::new();
1082 p2pk_compressed.write_bin(&[0x02; 33]);
1083 p2pk_compressed.write_opcode(OP_CHECKSIG);
1084 assert!(p2pk_compressed.is_p2pk());
1085
1086 let mut p2pk_uncompressed = Script::new();
1088 p2pk_uncompressed.write_bin(&[0x04; 65]);
1089 p2pk_uncompressed.write_opcode(OP_CHECKSIG);
1090 assert!(p2pk_uncompressed.is_p2pk());
1091
1092 let mut wrong_len = Script::new();
1094 wrong_len.write_bin(&[0x02; 32]);
1095 wrong_len.write_opcode(OP_CHECKSIG);
1096 assert!(!wrong_len.is_p2pk());
1097
1098 let mut no_checksig = Script::new();
1100 no_checksig.write_bin(&[0x02; 33]);
1101 assert!(!no_checksig.is_p2pk());
1102 }
1103
1104 #[test]
1105 fn test_is_p2sh() {
1106 let p2sh = Script::from_hex("a914000000000000000000000000000000000000000087").unwrap();
1108 assert!(p2sh.is_p2sh());
1109
1110 let wrong_len = Script::from_hex("a91400000000000000000087").unwrap();
1112 assert!(!wrong_len.is_p2sh());
1113
1114 let equalverify =
1116 Script::from_hex("a914000000000000000000000000000000000000000088").unwrap();
1117 assert!(!equalverify.is_p2sh());
1118 }
1119
1120 #[test]
1121 fn test_is_data() {
1122 let op_return = Script::from_asm("OP_RETURN").unwrap();
1124 assert!(op_return.is_data());
1125
1126 let mut op_return_data = Script::new();
1128 op_return_data.write_opcode(OP_RETURN);
1129 op_return_data.write_bin(b"hello");
1130 assert!(op_return_data.is_data());
1131
1132 let safe_data = Script::from_asm("OP_FALSE OP_RETURN").unwrap();
1134 assert!(safe_data.is_data());
1135
1136 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1138 assert!(!p2pkh.is_data());
1139
1140 let empty = Script::new();
1142 assert!(!empty.is_data());
1143 }
1144
1145 #[test]
1146 fn test_is_safe_data_carrier() {
1147 let safe = Script::from_asm("OP_FALSE OP_RETURN").unwrap();
1149 assert!(safe.is_safe_data_carrier());
1150
1151 let safe_data = Script::from_hex("006a0568656c6c6f").unwrap();
1153 assert!(safe_data.is_safe_data_carrier());
1154
1155 let bare = Script::from_asm("OP_RETURN").unwrap();
1157 assert!(!bare.is_safe_data_carrier());
1158
1159 let empty = Script::new();
1161 assert!(!empty.is_safe_data_carrier());
1162
1163 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1165 assert!(!p2pkh.is_safe_data_carrier());
1166 }
1167
1168 #[test]
1169 fn test_is_multisig() {
1170 let mut multisig = Script::new();
1172 multisig.write_opcode(OP_2);
1173 multisig.write_bin(&[0x02; 33]); multisig.write_bin(&[0x03; 33]); multisig.write_bin(&[0x02; 33]); multisig.write_opcode(OP_3);
1177 multisig.write_opcode(OP_CHECKMULTISIG);
1178
1179 let result = multisig.is_multisig();
1180 assert!(result.is_some());
1181 let (m, n) = result.unwrap();
1182 assert_eq!(m, 2);
1183 assert_eq!(n, 3);
1184
1185 let mut one_of_one = Script::new();
1187 one_of_one.write_opcode(OP_1);
1188 one_of_one.write_bin(&[0x02; 33]);
1189 one_of_one.write_opcode(OP_1);
1190 one_of_one.write_opcode(OP_CHECKMULTISIG);
1191
1192 let result = one_of_one.is_multisig();
1193 assert!(result.is_some());
1194 let (m, n) = result.unwrap();
1195 assert_eq!(m, 1);
1196 assert_eq!(n, 1);
1197
1198 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1200 assert!(p2pkh.is_multisig().is_none());
1201
1202 let mut wrong_count = Script::new();
1204 wrong_count.write_opcode(OP_2);
1205 wrong_count.write_bin(&[0x02; 33]); wrong_count.write_opcode(OP_2);
1207 wrong_count.write_opcode(OP_CHECKMULTISIG);
1208 assert!(wrong_count.is_multisig().is_none());
1209 }
1210
1211 #[test]
1212 fn test_opcode_to_small_int() {
1213 assert_eq!(Script::opcode_to_small_int(OP_1), Some(1));
1214 assert_eq!(Script::opcode_to_small_int(OP_2), Some(2));
1215 assert_eq!(Script::opcode_to_small_int(OP_16), Some(16));
1216 assert_eq!(Script::opcode_to_small_int(OP_0), None);
1217 assert_eq!(Script::opcode_to_small_int(OP_DUP), None);
1218 }
1219
1220 #[test]
1221 fn test_extract_pubkey_hash() {
1222 let p2pkh = Script::from_hex("76a914000102030405060708090a0b0c0d0e0f1011121388ac").unwrap();
1224 let hash = p2pkh.extract_pubkey_hash();
1225 assert!(hash.is_some());
1226 let hash = hash.unwrap();
1227 assert_eq!(
1228 hash,
1229 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
1230 );
1231
1232 let p2sh = Script::from_hex("a914000000000000000000000000000000000000000087").unwrap();
1234 assert!(p2sh.extract_pubkey_hash().is_none());
1235 }
1236
1237 #[test]
1238 fn test_is_push_only_comprehensive() {
1239 let push_only = Script::from_asm("OP_1 OP_2 OP_3").unwrap();
1241 assert!(push_only.is_push_only());
1242
1243 let data_push = Script::from_hex("03010203").unwrap(); assert!(data_push.is_push_only());
1245
1246 let with_ops = Script::from_asm("OP_DUP OP_HASH160").unwrap();
1248 assert!(!with_ops.is_push_only());
1249 }
1250
1251 #[test]
1252 fn test_get_public_key() {
1253 let pubkey_bytes = [0x02u8; 33];
1255 let mut p2pk = Script::new();
1256 p2pk.write_bin(&pubkey_bytes);
1257 p2pk.write_opcode(OP_CHECKSIG);
1258
1259 let result = p2pk.get_public_key();
1260 assert!(result.is_some());
1261 assert_eq!(result.unwrap(), pubkey_bytes.to_vec());
1262
1263 let pubkey_bytes_uncompressed = [0x04u8; 65];
1265 let mut p2pk_uncompressed = Script::new();
1266 p2pk_uncompressed.write_bin(&pubkey_bytes_uncompressed);
1267 p2pk_uncompressed.write_opcode(OP_CHECKSIG);
1268
1269 let result = p2pk_uncompressed.get_public_key();
1270 assert!(result.is_some());
1271 assert_eq!(result.unwrap().len(), 65);
1272
1273 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1275 assert!(p2pkh.get_public_key().is_none());
1276
1277 let empty = Script::new();
1279 assert!(empty.get_public_key().is_none());
1280 }
1281
1282 #[test]
1283 fn test_get_public_key_hex() {
1284 let pubkey_bytes = [0x02u8; 33];
1285 let mut p2pk = Script::new();
1286 p2pk.write_bin(&pubkey_bytes);
1287 p2pk.write_opcode(OP_CHECKSIG);
1288
1289 let hex = p2pk.get_public_key_hex();
1290 assert!(hex.is_some());
1291 assert_eq!(hex.unwrap(), crate::primitives::to_hex(&pubkey_bytes));
1292
1293 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1295 assert!(p2pkh.get_public_key_hex().is_none());
1296 }
1297}