1use super::op::opcode_to_name;
7use crate::primitives::to_hex;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ScriptChunk {
13 pub op: u8,
15 pub data: Option<Vec<u8>>,
17}
18
19impl ScriptChunk {
20 pub fn new_opcode(op: u8) -> Self {
22 Self { op, data: None }
23 }
24
25 pub fn new_push(data: Vec<u8>) -> Self {
34 let len = data.len();
35 let op = if len == 0 {
36 super::op::OP_0
37 } else if len < super::op::OP_PUSHDATA1 as usize {
38 len as u8
39 } else if len < 256 {
40 super::op::OP_PUSHDATA1
41 } else if len < 65536 {
42 super::op::OP_PUSHDATA2
43 } else {
44 super::op::OP_PUSHDATA4
45 };
46
47 Self {
48 op,
49 data: if data.is_empty() { None } else { Some(data) },
50 }
51 }
52
53 pub fn new(op: u8, data: Option<Vec<u8>>) -> Self {
55 Self { op, data }
56 }
57
58 pub fn is_push_data(&self) -> bool {
60 self.op <= super::op::OP_16
61 }
62
63 pub fn to_asm(&self) -> String {
65 match &self.data {
66 Some(data) => to_hex(data),
67 None => {
68 if self.op == super::op::OP_0 {
69 "0".to_string()
70 } else if self.op == super::op::OP_1NEGATE {
71 "-1".to_string()
72 } else {
73 opcode_to_name(self.op)
74 .map(|s| s.to_string())
75 .unwrap_or_else(|| format!("OP_UNKNOWN{}", self.op))
76 }
77 }
78 }
79 }
80}
81
82impl Default for ScriptChunk {
83 fn default() -> Self {
84 Self::new_opcode(super::op::OP_0)
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::script::op::*;
92
93 #[test]
94 fn test_new_opcode() {
95 let chunk = ScriptChunk::new_opcode(OP_DUP);
96 assert_eq!(chunk.op, OP_DUP);
97 assert!(chunk.data.is_none());
98 }
99
100 #[test]
101 fn test_new_push_empty() {
102 let chunk = ScriptChunk::new_push(vec![]);
103 assert_eq!(chunk.op, OP_0);
104 assert!(chunk.data.is_none());
105 }
106
107 #[test]
108 fn test_new_push_small() {
109 let data = vec![0x01, 0x02, 0x03];
110 let chunk = ScriptChunk::new_push(data.clone());
111 assert_eq!(chunk.op, 3); assert_eq!(chunk.data, Some(data));
113 }
114
115 #[test]
116 fn test_new_push_medium() {
117 let data = vec![0u8; 100];
118 let chunk = ScriptChunk::new_push(data.clone());
119 assert_eq!(chunk.op, OP_PUSHDATA1);
120 assert_eq!(chunk.data, Some(data));
121 }
122
123 #[test]
124 fn test_new_push_large() {
125 let data = vec![0u8; 300];
126 let chunk = ScriptChunk::new_push(data.clone());
127 assert_eq!(chunk.op, super::super::op::OP_PUSHDATA2);
128 assert_eq!(chunk.data, Some(data));
129 }
130
131 #[test]
132 fn test_is_push_data() {
133 assert!(ScriptChunk::new_opcode(OP_0).is_push_data());
134 assert!(ScriptChunk::new_opcode(OP_1).is_push_data());
135 assert!(ScriptChunk::new_opcode(OP_16).is_push_data());
136 assert!(!ScriptChunk::new_opcode(OP_DUP).is_push_data());
137 assert!(!ScriptChunk::new_opcode(OP_CHECKSIG).is_push_data());
138 }
139
140 #[test]
141 fn test_to_asm_opcode() {
142 assert_eq!(ScriptChunk::new_opcode(OP_DUP).to_asm(), "OP_DUP");
143 assert_eq!(ScriptChunk::new_opcode(OP_HASH160).to_asm(), "OP_HASH160");
144 assert_eq!(ScriptChunk::new_opcode(OP_CHECKSIG).to_asm(), "OP_CHECKSIG");
145 }
146
147 #[test]
148 fn test_to_asm_special() {
149 assert_eq!(ScriptChunk::new_opcode(OP_0).to_asm(), "0");
150 assert_eq!(ScriptChunk::new_opcode(OP_1NEGATE).to_asm(), "-1");
151 }
152
153 #[test]
154 fn test_to_asm_data() {
155 let chunk = ScriptChunk::new(0x03, Some(vec![0xab, 0xcd, 0xef]));
156 assert_eq!(chunk.to_asm(), "abcdef");
157 }
158}