1use sha2::{Digest, Sha256};
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23#[repr(u8)]
24#[allow(non_camel_case_types)]
25pub enum Opcode {
26 OP_0 = 0x00,
28 OP_1 = 0x51,
30 OP_2 = 0x52,
32 OP_3 = 0x53,
34 OP_16 = 0x60,
36 OP_RETURN = 0x6a,
38 OP_DUP = 0x76,
40 OP_EQUAL = 0x87,
42 OP_EQUALVERIFY = 0x88,
44 OP_VERIFY = 0x69,
46 OP_HASH160 = 0xa9,
48 OP_CHECKSIG = 0xac,
50 OP_CHECKSIGADD = 0xba,
52 OP_NUMEQUAL = 0x9c,
54 OP_NUMEQUALVERIFY = 0x9d,
56 OP_CHECKLOCKTIMEVERIFY = 0xb1,
58 OP_CHECKSEQUENCEVERIFY = 0xb2,
60 OP_SUCCESS = 0x50,
62 OP_NOP = 0x61,
64 OP_DROP = 0x75,
66 OP_SWAP = 0x7c,
68 OP_IF = 0x63,
70 OP_ELSE = 0x67,
72 OP_ENDIF = 0x68,
74}
75
76impl From<Opcode> for u8 {
77 fn from(op: Opcode) -> u8 {
78 op as u8
79 }
80}
81
82#[derive(Clone, Debug)]
86pub struct Script {
87 bytes: Vec<u8>,
89}
90
91impl Script {
92 pub fn new() -> Self {
94 Self { bytes: Vec::new() }
95 }
96
97 pub fn push_opcode(mut self, opcode: Opcode) -> Self {
99 self.bytes.push(opcode as u8);
100 self
101 }
102
103 pub fn push_data(mut self, data: &[u8]) -> Self {
110 let len = data.len();
111 if len == 0 {
112 self.bytes.push(0x00); } else if len <= 75 {
114 self.bytes.push(len as u8); self.bytes.extend_from_slice(data);
116 } else if len <= 255 {
117 self.bytes.push(0x4c); self.bytes.push(len as u8);
119 self.bytes.extend_from_slice(data);
120 } else if len <= 65535 {
121 self.bytes.push(0x4d); self.bytes.extend_from_slice(&(len as u16).to_le_bytes());
123 self.bytes.extend_from_slice(data);
124 }
125 self
126 }
127
128 pub fn push_key(self, x_only_pubkey: &[u8; 32]) -> Self {
130 self.push_data(x_only_pubkey)
131 }
132
133 pub fn push_byte(mut self, byte: u8) -> Self {
135 self.bytes.push(byte);
136 self
137 }
138
139 pub fn push_int(mut self, value: i64) -> Self {
141 if value == 0 {
142 self.bytes.push(0x00); } else if value == -1 {
144 self.bytes.push(0x4f); } else if (1..=16).contains(&value) {
146 self.bytes.push(0x50 + value as u8); } else {
148 let mut v = value.unsigned_abs();
150 let negative = value < 0;
151 let mut encoded = Vec::new();
152 while v > 0 {
153 encoded.push((v & 0xFF) as u8);
154 v >>= 8;
155 }
156 if encoded.last().is_some_and(|b| b & 0x80 != 0) {
158 encoded.push(if negative { 0x80 } else { 0x00 });
159 } else if negative {
160 if let Some(last) = encoded.last_mut() {
161 *last |= 0x80;
162 }
163 }
164 self = self.push_data(&encoded);
165 }
166 self
167 }
168
169 pub fn to_bytes(&self) -> &[u8] {
171 &self.bytes
172 }
173
174 pub fn into_bytes(self) -> Vec<u8> {
176 self.bytes
177 }
178
179 pub fn len(&self) -> usize {
181 self.bytes.len()
182 }
183
184 pub fn is_empty(&self) -> bool {
186 self.bytes.is_empty()
187 }
188
189 pub fn script_hash(&self) -> [u8; 32] {
191 let result = Sha256::digest(&self.bytes);
192 let mut hash = [0u8; 32];
193 hash.copy_from_slice(&result);
194 hash
195 }
196}
197
198impl Default for Script {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204pub fn checksig_script(x_only_pubkey: &[u8; 32]) -> Script {
210 Script::new()
211 .push_key(x_only_pubkey)
212 .push_opcode(Opcode::OP_CHECKSIG)
213}
214
215pub fn multisig_script(keys: &[[u8; 32]], threshold: u32) -> Script {
222 let mut script = Script::new();
223
224 for (i, key) in keys.iter().enumerate() {
225 script = script.push_key(key);
226 if i == 0 {
227 script = script.push_opcode(Opcode::OP_CHECKSIG);
228 } else {
229 script = script.push_opcode(Opcode::OP_CHECKSIGADD);
230 }
231 }
232
233 script = script.push_int(threshold as i64);
234 script.push_opcode(Opcode::OP_NUMEQUALVERIFY)
235}
236
237pub fn timelocked_script(x_only_pubkey: &[u8; 32], locktime: u32) -> Script {
241 Script::new()
242 .push_int(locktime as i64)
243 .push_opcode(Opcode::OP_CHECKLOCKTIMEVERIFY)
244 .push_opcode(Opcode::OP_DROP)
245 .push_key(x_only_pubkey)
246 .push_opcode(Opcode::OP_CHECKSIG)
247}
248
249pub fn is_annex(data: &[u8]) -> bool {
255 data.first() == Some(&0x50)
256}
257
258pub fn create_annex(data: &[u8]) -> Vec<u8> {
262 let mut annex = Vec::with_capacity(1 + data.len());
263 annex.push(0x50);
264 annex.extend_from_slice(data);
265 annex
266}
267
268#[derive(Clone, Copy, Debug, PartialEq, Eq)]
272#[repr(u8)]
273pub enum SighashType {
274 Default = 0x00,
276 All = 0x01,
278 None = 0x02,
280 Single = 0x03,
282 AllAnyoneCanPay = 0x81,
284 NoneAnyoneCanPay = 0x82,
286 SingleAnyoneCanPay = 0x83,
288}
289
290impl SighashType {
291 pub fn from_byte(byte: u8) -> Option<Self> {
293 match byte {
294 0x00 => Some(SighashType::Default),
295 0x01 => Some(SighashType::All),
296 0x02 => Some(SighashType::None),
297 0x03 => Some(SighashType::Single),
298 0x81 => Some(SighashType::AllAnyoneCanPay),
299 0x82 => Some(SighashType::NoneAnyoneCanPay),
300 0x83 => Some(SighashType::SingleAnyoneCanPay),
301 _ => None,
302 }
303 }
304
305 #[must_use]
307 pub fn to_byte(self) -> u8 {
308 self as u8
309 }
310}
311
312#[cfg(test)]
315#[allow(clippy::unwrap_used, clippy::expect_used)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_script_builder_empty() {
321 let script = Script::new();
322 assert!(script.is_empty());
323 assert_eq!(script.len(), 0);
324 }
325
326 #[test]
327 fn test_script_builder_opcode() {
328 let script = Script::new().push_opcode(Opcode::OP_CHECKSIG);
329 assert_eq!(script.to_bytes(), &[0xac]);
330 }
331
332 #[test]
333 fn test_script_builder_key_checksig() {
334 let key = [0xAA; 32];
335 let script = checksig_script(&key);
336 let bytes = script.to_bytes();
337 assert_eq!(bytes[0], 32); assert_eq!(&bytes[1..33], &key);
339 assert_eq!(bytes[33], 0xac); assert_eq!(bytes.len(), 34);
341 }
342
343 #[test]
344 fn test_script_builder_push_data_small() {
345 let data = vec![0x01, 0x02, 0x03];
346 let script = Script::new().push_data(&data);
347 assert_eq!(script.to_bytes()[0], 3); assert_eq!(&script.to_bytes()[1..], &data);
349 }
350
351 #[test]
352 fn test_script_builder_push_data_76() {
353 let data = vec![0xFF; 76];
354 let script = Script::new().push_data(&data);
355 assert_eq!(script.to_bytes()[0], 0x4c); assert_eq!(script.to_bytes()[1], 76); assert_eq!(&script.to_bytes()[2..], &data[..]);
358 }
359
360 #[test]
361 fn test_script_builder_push_int_small() {
362 let s0 = Script::new().push_int(0);
363 assert_eq!(s0.to_bytes(), &[0x00]); let s1 = Script::new().push_int(1);
366 assert_eq!(s1.to_bytes(), &[0x51]); let s16 = Script::new().push_int(16);
369 assert_eq!(s16.to_bytes(), &[0x60]); }
371
372 #[test]
373 fn test_script_builder_push_int_negative() {
374 let s = Script::new().push_int(-1);
375 assert_eq!(s.to_bytes(), &[0x4f]); }
377
378 #[test]
379 fn test_multisig_script_2_of_3() {
380 let k1 = [0x01; 32];
381 let k2 = [0x02; 32];
382 let k3 = [0x03; 32];
383 let script = multisig_script(&[k1, k2, k3], 2);
384 let bytes = script.to_bytes();
385
386 assert!(!bytes.is_empty());
388 assert_eq!(bytes[0], 32); let last = bytes[bytes.len() - 1];
392 assert_eq!(last, 0x9d); }
394
395 #[test]
396 fn test_timelocked_script() {
397 let key = [0xAA; 32];
398 let script = timelocked_script(&key, 500000);
399 let bytes = script.to_bytes();
400 assert!(!bytes.is_empty());
401 assert!(bytes.contains(&0xb1));
403 assert!(bytes.contains(&0x75));
405 assert!(bytes.contains(&0xac));
407 }
408
409 #[test]
410 fn test_annex_identification() {
411 assert!(is_annex(&[0x50, 0x01, 0x02]));
412 assert!(!is_annex(&[0x51, 0x01]));
413 assert!(!is_annex(&[]));
414 }
415
416 #[test]
417 fn test_create_annex() {
418 let annex = create_annex(&[0x01, 0x02]);
419 assert_eq!(annex, vec![0x50, 0x01, 0x02]);
420 assert!(is_annex(&annex));
421 }
422
423 #[test]
424 fn test_sighash_type_parsing() {
425 assert_eq!(SighashType::from_byte(0x00), Some(SighashType::Default));
426 assert_eq!(SighashType::from_byte(0x01), Some(SighashType::All));
427 assert_eq!(
428 SighashType::from_byte(0x81),
429 Some(SighashType::AllAnyoneCanPay)
430 );
431 assert_eq!(SighashType::from_byte(0xFF), None);
432 }
433
434 #[test]
435 fn test_script_hash() {
436 let s1 = Script::new().push_opcode(Opcode::OP_CHECKSIG);
437 let s2 = Script::new().push_opcode(Opcode::OP_CHECKSIG);
438 assert_eq!(s1.script_hash(), s2.script_hash());
439
440 let s3 = Script::new().push_opcode(Opcode::OP_RETURN);
441 assert_ne!(s1.script_hash(), s3.script_hash());
442 }
443
444 #[test]
445 fn test_script_into_bytes() {
446 let script = Script::new()
447 .push_opcode(Opcode::OP_1)
448 .push_opcode(Opcode::OP_CHECKSIG);
449 let bytes = script.into_bytes();
450 assert_eq!(bytes, vec![0x51, 0xac]);
451 }
452
453 #[test]
454 fn test_checksig_script_template() {
455 let key = [0xBB; 32];
456 let script = checksig_script(&key);
457 assert_eq!(script.len(), 34);
459 }
460}