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 pub fn write_bin(&mut self, bin: &[u8]) -> &mut Self {
424 self.ensure_parsed();
428 self.invalidate_caches();
429
430 let len = bin.len();
431 let op: u8;
432 let data: Option<Vec<u8>>;
433
434 if len == 0 {
435 op = OP_0;
436 data = None;
437 } else if len < OP_PUSHDATA1 as usize {
438 op = len as u8;
439 data = Some(bin.to_vec());
440 } else if len < 256 {
441 op = OP_PUSHDATA1;
442 data = Some(bin.to_vec());
443 } else if len < 65536 {
444 op = OP_PUSHDATA2;
445 data = Some(bin.to_vec());
446 } else if (len as u64) < 0x100000000 {
447 op = OP_PUSHDATA4;
448 data = Some(bin.to_vec());
449 } else {
450 panic!("Data too large to push");
451 }
452
453 self.chunks.borrow_mut().push(ScriptChunk::new(op, data));
454 self
455 }
456
457 pub fn write_number(&mut self, num: i64) -> &mut Self {
462 self.ensure_parsed();
466 self.invalidate_caches();
467
468 if num == 0 {
469 self.chunks.borrow_mut().push(ScriptChunk::new_opcode(OP_0));
470 } else if num == -1 {
471 self.chunks
472 .borrow_mut()
473 .push(ScriptChunk::new_opcode(OP_1NEGATE));
474 } else if (1..=16).contains(&num) {
475 let op = (OP_1 as i64 + num - 1) as u8;
476 self.chunks.borrow_mut().push(ScriptChunk::new_opcode(op));
477 } else {
478 let data = Self::encode_script_number(num);
480 let len = data.len();
481 let op = if len < OP_PUSHDATA1 as usize {
482 len as u8
483 } else if len < 256 {
484 OP_PUSHDATA1
485 } else {
486 OP_PUSHDATA2
487 };
488 self.chunks
489 .borrow_mut()
490 .push(ScriptChunk::new(op, Some(data)));
491 }
492
493 self
494 }
495
496 fn encode_script_number(num: i64) -> Vec<u8> {
498 if num == 0 {
499 return vec![];
500 }
501
502 let negative = num < 0;
503 let mut abs_val = num.unsigned_abs();
504 let mut result = Vec::new();
505
506 while abs_val > 0 {
507 result.push((abs_val & 0xff) as u8);
508 abs_val >>= 8;
509 }
510
511 if result.last().is_some_and(|&b| b & 0x80 != 0) {
514 result.push(if negative { 0x80 } else { 0x00 });
515 } else if negative {
516 if let Some(last) = result.last_mut() {
518 *last |= 0x80;
519 }
520 }
521
522 result
523 }
524
525 pub fn remove_codeseparators(&mut self) -> &mut Self {
527 self.ensure_parsed();
531 self.invalidate_caches();
532
533 {
534 let mut chunks = self.chunks.borrow_mut();
535 chunks.retain(|chunk| chunk.op != OP_CODESEPARATOR);
536 }
537
538 self
539 }
540
541 pub fn find_and_delete(&mut self, script: &Script) -> &mut Self {
543 self.ensure_parsed();
547 self.invalidate_caches();
548 script.ensure_parsed();
549
550 let target_hex = script.to_hex();
551 {
552 let mut chunks = self.chunks.borrow_mut();
553
554 chunks.retain(|chunk| {
555 let chunk_script = Script::from_chunks(vec![chunk.clone()]);
556 chunk_script.to_hex() != target_hex
557 });
558 }
559
560 self
561 }
562
563 pub fn is_push_only(&self) -> bool {
565 self.ensure_parsed();
566 let chunks = self.chunks.borrow();
567 chunks.iter().all(|chunk| chunk.op <= OP_16)
568 }
569
570 pub fn chunks(&self) -> Vec<ScriptChunk> {
572 self.ensure_parsed();
573 self.chunks.borrow().clone()
574 }
575
576 pub fn len(&self) -> usize {
578 self.ensure_parsed();
579 self.chunks.borrow().len()
580 }
581
582 pub fn is_empty(&self) -> bool {
584 self.len() == 0
585 }
586
587 pub fn set_chunk_opcode(&mut self, index: usize, op: u8) -> &mut Self {
589 self.ensure_parsed();
593 self.invalidate_caches();
594 {
595 let mut chunks = self.chunks.borrow_mut();
596 if index < chunks.len() {
597 chunks[index] = ScriptChunk::new_opcode(op);
598 }
599 }
600 self
601 }
602
603 pub fn is_locking_script(&self) -> bool {
606 false
607 }
608
609 pub fn is_unlocking_script(&self) -> bool {
612 false
613 }
614
615 pub fn is_p2pkh(&self) -> bool {
618 let chunks = self.chunks();
619 chunks.len() == 5
620 && chunks[0].op == OP_DUP
621 && chunks[1].op == OP_HASH160
622 && chunks[2]
623 .data
624 .as_ref()
625 .map(|d| d.len() == 20)
626 .unwrap_or(false)
627 && chunks[3].op == OP_EQUALVERIFY
628 && chunks[4].op == OP_CHECKSIG
629 }
630
631 pub fn is_p2pk(&self) -> bool {
634 let chunks = self.chunks();
635 chunks.len() == 2
636 && chunks[0]
637 .data
638 .as_ref()
639 .map(|d| d.len() == 33 || d.len() == 65)
640 .unwrap_or(false)
641 && chunks[1].op == OP_CHECKSIG
642 }
643
644 pub fn is_p2sh(&self) -> bool {
647 let chunks = self.chunks();
648 chunks.len() == 3
649 && chunks[0].op == OP_HASH160
650 && chunks[1]
651 .data
652 .as_ref()
653 .map(|d| d.len() == 20)
654 .unwrap_or(false)
655 && chunks[2].op == OP_EQUAL
656 }
657
658 pub fn is_data(&self) -> bool {
660 let chunks = self.chunks();
661 if chunks.is_empty() {
662 return false;
663 }
664 if chunks[0].op == OP_RETURN {
666 return true;
667 }
668 chunks.len() >= 2 && chunks[0].op == OP_FALSE && chunks[1].op == OP_RETURN
670 }
671
672 pub fn is_safe_data_carrier(&self) -> bool {
677 let chunks = self.chunks();
678 chunks.len() >= 2 && chunks[0].op == OP_FALSE && chunks[1].op == OP_RETURN
679 }
680
681 pub fn is_multisig(&self) -> Option<(u8, u8)> {
684 let chunks = self.chunks();
685 if chunks.len() < 4 {
686 return None;
687 }
688
689 if chunks.last()?.op != OP_CHECKMULTISIG {
691 return None;
692 }
693
694 let n = Self::opcode_to_small_int(chunks[chunks.len() - 2].op)?;
696
697 let m = Self::opcode_to_small_int(chunks[0].op)?;
699
700 if chunks.len() != (n as usize) + 3 {
702 return None;
703 }
704
705 for chunk in chunks.iter().take((n as usize) + 1).skip(1) {
707 let data = chunk.data.as_ref()?;
708 if data.len() != 33 && data.len() != 65 {
709 return None;
710 }
711 }
712
713 Some((m, n))
714 }
715
716 pub fn opcode_to_small_int(op: u8) -> Option<u8> {
718 if (OP_1..=OP_16).contains(&op) {
719 Some(op - OP_1 + 1)
720 } else {
721 None
722 }
723 }
724
725 pub fn extract_pubkey_hash(&self) -> Option<[u8; 20]> {
727 if !self.is_p2pkh() {
728 return None;
729 }
730 let chunks = self.chunks();
731 let data = chunks[2].data.as_ref()?;
732 let mut hash = [0u8; 20];
733 hash.copy_from_slice(data);
734 Some(hash)
735 }
736
737 pub fn get_public_key(&self) -> Option<Vec<u8>> {
744 if !self.is_p2pk() {
745 return None;
746 }
747 let chunks = self.chunks();
748 chunks[0].data.clone()
749 }
750
751 pub fn get_public_key_hex(&self) -> Option<String> {
758 self.get_public_key()
759 .map(|bytes| crate::primitives::to_hex(&bytes))
760 }
761}
762
763impl Default for Script {
764 fn default() -> Self {
765 Self::new()
766 }
767}
768
769fn truncated_push_index(bytes: &[u8]) -> Option<usize> {
773 let length = bytes.len();
774 let mut pos = 0;
775 let mut index = 0usize;
776 let mut in_conditional_block = 0i32;
777 while pos < length {
778 let op = bytes[pos];
779 pos += 1;
780 if op == OP_RETURN && in_conditional_block == 0 {
781 return None;
782 }
783 if op == OP_IF || op == OP_NOTIF || op == OP_VERIF || op == OP_VERNOTIF {
784 in_conditional_block += 1;
785 } else if op == OP_ENDIF {
786 in_conditional_block = (in_conditional_block - 1).max(0);
787 }
788 let declared: Option<usize> = if op > 0 && op < OP_PUSHDATA1 {
789 Some(op as usize)
790 } else if op == OP_PUSHDATA1 {
791 if pos + 1 > length {
792 return Some(index);
793 }
794 let n = bytes[pos] as usize;
795 pos += 1;
796 Some(n)
797 } else if op == OP_PUSHDATA2 {
798 if pos + 2 > length {
799 return Some(index);
800 }
801 let n = bytes[pos] as usize | ((bytes[pos + 1] as usize) << 8);
802 pos += 2;
803 Some(n)
804 } else if op == OP_PUSHDATA4 {
805 if pos + 4 > length {
806 return Some(index);
807 }
808 let n = bytes[pos] as usize
809 | ((bytes[pos + 1] as usize) << 8)
810 | ((bytes[pos + 2] as usize) << 16)
811 | ((bytes[pos + 3] as usize) << 24);
812 pos += 4;
813 Some(n)
814 } else {
815 None
816 };
817 if let Some(n) = declared {
818 if pos + n > length {
819 return Some(index);
820 }
821 pos += n;
822 }
823 index += 1;
824 }
825 None
826}
827
828impl PartialEq for Script {
829 fn eq(&self, other: &Self) -> bool {
830 self.to_binary() == other.to_binary()
831 }
832}
833
834impl Eq for Script {}
835
836#[cfg(test)]
837mod tests {
838 use super::*;
839
840 #[test]
841 fn test_new_empty_script() {
842 let script = Script::new();
843 assert!(script.is_empty());
844 assert_eq!(script.to_hex(), "");
845 assert_eq!(script.to_asm(), "");
846 }
847
848 #[test]
849 fn test_from_hex() {
850 let hex = "76a914000000000000000000000000000000000000000088ac";
852 let script = Script::from_hex(hex).unwrap();
853 assert_eq!(script.to_hex(), hex);
854 }
855
856 #[test]
857 fn test_from_binary() {
858 let bin = vec![0x76, 0xa9]; let script = Script::from_binary(&bin).unwrap();
860 assert_eq!(script.to_binary(), bin);
861 }
862
863 #[test]
864 fn test_from_asm_simple() {
865 let asm = "OP_DUP OP_HASH160";
866 let script = Script::from_asm(asm).unwrap();
867 assert_eq!(script.to_hex(), "76a9");
868 }
869
870 #[test]
871 fn test_from_asm_with_data() {
872 let asm =
873 "OP_DUP OP_HASH160 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG";
874 let script = Script::from_asm(asm).unwrap();
875 assert_eq!(
876 script.to_hex(),
877 "76a914000000000000000000000000000000000000000088ac"
878 );
879 }
880
881 #[test]
882 fn test_from_asm_special_values() {
883 let script = Script::from_asm("0").unwrap();
884 assert_eq!(script.to_hex(), "00");
885
886 let script = Script::from_asm("-1").unwrap();
887 assert_eq!(script.to_hex(), "4f");
888 }
889
890 #[test]
891 fn test_to_asm() {
892 let hex = "76a914000000000000000000000000000000000000000088ac";
893 let script = Script::from_hex(hex).unwrap();
894 let asm = script.to_asm();
895 assert!(asm.contains("OP_DUP"));
896 assert!(asm.contains("OP_HASH160"));
897 assert!(asm.contains("OP_EQUALVERIFY"));
898 assert!(asm.contains("OP_CHECKSIG"));
899 }
900
901 #[test]
902 fn test_write_opcode() {
903 let mut script = Script::new();
904 script.write_opcode(OP_DUP).write_opcode(OP_HASH160);
905 assert_eq!(script.to_hex(), "76a9");
906 }
907
908 #[test]
909 fn test_write_bin() {
910 let mut script = Script::new();
911 script.write_bin(&[0x01, 0x02, 0x03]);
912 assert_eq!(script.to_hex(), "03010203");
913 }
914
915 #[test]
916 fn test_write_bin_empty() {
917 let mut script = Script::new();
918 script.write_bin(&[]);
919 assert_eq!(script.to_hex(), "00"); }
921
922 #[test]
923 fn test_write_number() {
924 let mut script = Script::new();
925 script.write_number(0);
926 assert_eq!(script.to_hex(), "00"); let mut script = Script::new();
929 script.write_number(-1);
930 assert_eq!(script.to_hex(), "4f"); let mut script = Script::new();
933 script.write_number(1);
934 assert_eq!(script.to_hex(), "51"); let mut script = Script::new();
937 script.write_number(16);
938 assert_eq!(script.to_hex(), "60"); }
940
941 #[test]
942 fn test_write_number_large() {
943 let mut script = Script::new();
944 script.write_number(127);
945 assert_eq!(script.to_hex(), "017f");
946
947 let mut script = Script::new();
948 script.write_number(128);
949 assert_eq!(script.to_hex(), "028000"); let mut script = Script::new();
952 script.write_number(-127);
953 assert_eq!(script.to_hex(), "01ff");
954 }
955
956 #[test]
957 fn test_is_push_only() {
958 let script = Script::from_asm("OP_1 OP_2 OP_3").unwrap();
959 assert!(script.is_push_only());
960
961 let script = Script::from_asm("OP_DUP OP_HASH160").unwrap();
962 assert!(!script.is_push_only());
963 }
964
965 #[test]
966 fn test_remove_codeseparators() {
967 let mut script =
968 Script::from_asm("OP_DUP OP_CODESEPARATOR OP_HASH160 OP_CODESEPARATOR").unwrap();
969 script.remove_codeseparators();
970 assert_eq!(script.to_asm(), "OP_DUP OP_HASH160");
971 }
972
973 #[test]
974 fn test_find_and_delete() {
975 let mut script = Script::from_asm("OP_DUP OP_HASH160 OP_DUP").unwrap();
976 let target = Script::from_asm("OP_DUP").unwrap();
977 script.find_and_delete(&target);
978 assert_eq!(script.to_asm(), "OP_HASH160");
979 }
980
981 #[test]
982 fn test_chunks() {
983 let script = Script::from_asm("OP_DUP OP_HASH160").unwrap();
984 let chunks = script.chunks();
985 assert_eq!(chunks.len(), 2);
986 assert_eq!(chunks[0].op, OP_DUP);
987 assert_eq!(chunks[1].op, OP_HASH160);
988 }
989
990 #[test]
991 fn test_write_script() {
992 let mut script1 = Script::from_asm("OP_DUP").unwrap();
993 let script2 = Script::from_asm("OP_HASH160").unwrap();
994 script1.write_script(&script2);
995 assert_eq!(script1.to_asm(), "OP_DUP OP_HASH160");
996 }
997
998 #[test]
999 fn test_op_return_parsing() {
1000 let hex = "6a0568656c6c6f"; let script = Script::from_hex(hex).unwrap();
1003 let chunks = script.chunks();
1004 assert_eq!(chunks.len(), 1);
1005 assert_eq!(chunks[0].op, OP_RETURN);
1006 assert!(chunks[0].data.is_some());
1008 }
1009
1010 #[test]
1011 fn test_pushdata1() {
1012 let data = vec![0u8; 100];
1014 let mut script = Script::new();
1015 script.write_bin(&data);
1016 let bytes = script.to_binary();
1017 assert_eq!(bytes[0], OP_PUSHDATA1);
1018 assert_eq!(bytes[1], 100);
1019 assert_eq!(&bytes[2..], &data[..]);
1020 }
1021
1022 #[test]
1023 fn test_pushdata2() {
1024 let data = vec![0u8; 300];
1026 let mut script = Script::new();
1027 script.write_bin(&data);
1028 let bytes = script.to_binary();
1029 assert_eq!(bytes[0], OP_PUSHDATA2);
1030 assert_eq!(bytes[1], (300 & 0xff) as u8);
1031 assert_eq!(bytes[2], ((300 >> 8) & 0xff) as u8);
1032 assert_eq!(&bytes[3..], &data[..]);
1033 }
1034
1035 #[test]
1036 fn test_roundtrip_hex() {
1037 let hex = "76a914000000000000000000000000000000000000000088ac";
1038 let script = Script::from_hex(hex).unwrap();
1039 assert_eq!(script.to_hex(), hex);
1040 }
1041
1042 #[test]
1043 fn test_equality() {
1044 let script1 = Script::from_hex("76a9").unwrap();
1045 let script2 = Script::from_asm("OP_DUP OP_HASH160").unwrap();
1046 assert_eq!(script1, script2);
1047 }
1048}
1049
1050#[cfg(test)]
1051mod script_type_tests {
1052 use super::*;
1053
1054 #[test]
1055 fn test_is_p2pkh() {
1056 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1058 assert!(p2pkh.is_p2pkh());
1059
1060 let wrong_len = Script::from_hex("76a9140000000000000000000088ac").unwrap();
1062 assert!(!wrong_len.is_p2pkh());
1063
1064 let no_dup = Script::from_hex("a914000000000000000000000000000000000000000088ac").unwrap();
1066 assert!(!no_dup.is_p2pkh());
1067
1068 let empty = Script::new();
1070 assert!(!empty.is_p2pkh());
1071 }
1072
1073 #[test]
1074 fn test_is_p2pk() {
1075 let mut p2pk_compressed = Script::new();
1077 p2pk_compressed.write_bin(&[0x02; 33]);
1078 p2pk_compressed.write_opcode(OP_CHECKSIG);
1079 assert!(p2pk_compressed.is_p2pk());
1080
1081 let mut p2pk_uncompressed = Script::new();
1083 p2pk_uncompressed.write_bin(&[0x04; 65]);
1084 p2pk_uncompressed.write_opcode(OP_CHECKSIG);
1085 assert!(p2pk_uncompressed.is_p2pk());
1086
1087 let mut wrong_len = Script::new();
1089 wrong_len.write_bin(&[0x02; 32]);
1090 wrong_len.write_opcode(OP_CHECKSIG);
1091 assert!(!wrong_len.is_p2pk());
1092
1093 let mut no_checksig = Script::new();
1095 no_checksig.write_bin(&[0x02; 33]);
1096 assert!(!no_checksig.is_p2pk());
1097 }
1098
1099 #[test]
1100 fn test_is_p2sh() {
1101 let p2sh = Script::from_hex("a914000000000000000000000000000000000000000087").unwrap();
1103 assert!(p2sh.is_p2sh());
1104
1105 let wrong_len = Script::from_hex("a91400000000000000000087").unwrap();
1107 assert!(!wrong_len.is_p2sh());
1108
1109 let equalverify =
1111 Script::from_hex("a914000000000000000000000000000000000000000088").unwrap();
1112 assert!(!equalverify.is_p2sh());
1113 }
1114
1115 #[test]
1116 fn test_is_data() {
1117 let op_return = Script::from_asm("OP_RETURN").unwrap();
1119 assert!(op_return.is_data());
1120
1121 let mut op_return_data = Script::new();
1123 op_return_data.write_opcode(OP_RETURN);
1124 op_return_data.write_bin(b"hello");
1125 assert!(op_return_data.is_data());
1126
1127 let safe_data = Script::from_asm("OP_FALSE OP_RETURN").unwrap();
1129 assert!(safe_data.is_data());
1130
1131 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1133 assert!(!p2pkh.is_data());
1134
1135 let empty = Script::new();
1137 assert!(!empty.is_data());
1138 }
1139
1140 #[test]
1141 fn test_is_safe_data_carrier() {
1142 let safe = Script::from_asm("OP_FALSE OP_RETURN").unwrap();
1144 assert!(safe.is_safe_data_carrier());
1145
1146 let safe_data = Script::from_hex("006a0568656c6c6f").unwrap();
1148 assert!(safe_data.is_safe_data_carrier());
1149
1150 let bare = Script::from_asm("OP_RETURN").unwrap();
1152 assert!(!bare.is_safe_data_carrier());
1153
1154 let empty = Script::new();
1156 assert!(!empty.is_safe_data_carrier());
1157
1158 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1160 assert!(!p2pkh.is_safe_data_carrier());
1161 }
1162
1163 #[test]
1164 fn test_is_multisig() {
1165 let mut multisig = Script::new();
1167 multisig.write_opcode(OP_2);
1168 multisig.write_bin(&[0x02; 33]); multisig.write_bin(&[0x03; 33]); multisig.write_bin(&[0x02; 33]); multisig.write_opcode(OP_3);
1172 multisig.write_opcode(OP_CHECKMULTISIG);
1173
1174 let result = multisig.is_multisig();
1175 assert!(result.is_some());
1176 let (m, n) = result.unwrap();
1177 assert_eq!(m, 2);
1178 assert_eq!(n, 3);
1179
1180 let mut one_of_one = Script::new();
1182 one_of_one.write_opcode(OP_1);
1183 one_of_one.write_bin(&[0x02; 33]);
1184 one_of_one.write_opcode(OP_1);
1185 one_of_one.write_opcode(OP_CHECKMULTISIG);
1186
1187 let result = one_of_one.is_multisig();
1188 assert!(result.is_some());
1189 let (m, n) = result.unwrap();
1190 assert_eq!(m, 1);
1191 assert_eq!(n, 1);
1192
1193 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1195 assert!(p2pkh.is_multisig().is_none());
1196
1197 let mut wrong_count = Script::new();
1199 wrong_count.write_opcode(OP_2);
1200 wrong_count.write_bin(&[0x02; 33]); wrong_count.write_opcode(OP_2);
1202 wrong_count.write_opcode(OP_CHECKMULTISIG);
1203 assert!(wrong_count.is_multisig().is_none());
1204 }
1205
1206 #[test]
1207 fn test_opcode_to_small_int() {
1208 assert_eq!(Script::opcode_to_small_int(OP_1), Some(1));
1209 assert_eq!(Script::opcode_to_small_int(OP_2), Some(2));
1210 assert_eq!(Script::opcode_to_small_int(OP_16), Some(16));
1211 assert_eq!(Script::opcode_to_small_int(OP_0), None);
1212 assert_eq!(Script::opcode_to_small_int(OP_DUP), None);
1213 }
1214
1215 #[test]
1216 fn test_extract_pubkey_hash() {
1217 let p2pkh = Script::from_hex("76a914000102030405060708090a0b0c0d0e0f1011121388ac").unwrap();
1219 let hash = p2pkh.extract_pubkey_hash();
1220 assert!(hash.is_some());
1221 let hash = hash.unwrap();
1222 assert_eq!(
1223 hash,
1224 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
1225 );
1226
1227 let p2sh = Script::from_hex("a914000000000000000000000000000000000000000087").unwrap();
1229 assert!(p2sh.extract_pubkey_hash().is_none());
1230 }
1231
1232 #[test]
1233 fn test_is_push_only_comprehensive() {
1234 let push_only = Script::from_asm("OP_1 OP_2 OP_3").unwrap();
1236 assert!(push_only.is_push_only());
1237
1238 let data_push = Script::from_hex("03010203").unwrap(); assert!(data_push.is_push_only());
1240
1241 let with_ops = Script::from_asm("OP_DUP OP_HASH160").unwrap();
1243 assert!(!with_ops.is_push_only());
1244 }
1245
1246 #[test]
1247 fn test_get_public_key() {
1248 let pubkey_bytes = [0x02u8; 33];
1250 let mut p2pk = Script::new();
1251 p2pk.write_bin(&pubkey_bytes);
1252 p2pk.write_opcode(OP_CHECKSIG);
1253
1254 let result = p2pk.get_public_key();
1255 assert!(result.is_some());
1256 assert_eq!(result.unwrap(), pubkey_bytes.to_vec());
1257
1258 let pubkey_bytes_uncompressed = [0x04u8; 65];
1260 let mut p2pk_uncompressed = Script::new();
1261 p2pk_uncompressed.write_bin(&pubkey_bytes_uncompressed);
1262 p2pk_uncompressed.write_opcode(OP_CHECKSIG);
1263
1264 let result = p2pk_uncompressed.get_public_key();
1265 assert!(result.is_some());
1266 assert_eq!(result.unwrap().len(), 65);
1267
1268 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1270 assert!(p2pkh.get_public_key().is_none());
1271
1272 let empty = Script::new();
1274 assert!(empty.get_public_key().is_none());
1275 }
1276
1277 #[test]
1278 fn test_get_public_key_hex() {
1279 let pubkey_bytes = [0x02u8; 33];
1280 let mut p2pk = Script::new();
1281 p2pk.write_bin(&pubkey_bytes);
1282 p2pk.write_opcode(OP_CHECKSIG);
1283
1284 let hex = p2pk.get_public_key_hex();
1285 assert!(hex.is_some());
1286 assert_eq!(hex.unwrap(), crate::primitives::to_hex(&pubkey_bytes));
1287
1288 let p2pkh = Script::from_hex("76a914000000000000000000000000000000000000000088ac").unwrap();
1290 assert!(p2pkh.get_public_key_hex().is_none());
1291 }
1292}