bitcoin_script_analyzer/script/
mod.rs1pub mod convert;
2pub mod stack;
3
4use crate::opcode::Opcode;
5use core::fmt;
6
7#[derive(Debug, Clone, Copy)]
8pub enum ScriptElem<'a> {
9 Op(Opcode),
10 Bytes(&'a [u8]),
11}
12
13impl<'a> fmt::Display for ScriptElem<'a> {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 match self {
16 Self::Op(opcode) => write!(f, "{}", opcode.name().unwrap_or("UNKNOWN")),
17 Self::Bytes(bytes) => {
18 write!(f, "<")?;
19 for byte in *bytes {
20 write!(f, "{:02x}", byte)?;
21 }
22 write!(f, ">")
23 }
24 }
25 }
26}
27
28pub type Script<'a> = Vec<ScriptElem<'a>>;
29pub type ScriptSlice<'a> = &'a [ScriptElem<'a>];
30
31pub fn parse_script(bytes: &[u8]) -> Result<Script<'_>, ParseScriptError> {
32 let mut a = Vec::new();
33
34 let mut offset = 0;
35 while offset < bytes.len() {
36 let b = bytes[offset];
37 offset += 1;
38 let opcode = Opcode { opcode: b };
39 if opcode.name().is_some() {
40 if let Some(n) = opcode.pushdata_length() {
41 let n = n;
42 let Some(push_size) = bytes.get(offset..offset + n) else {
43 return Err(ParseScriptError::UnexpectedEndPushdataLength(opcode));
44 };
45 let l = u32::from_le_bytes({
46 let mut buf = [0u8; 4];
47 buf[0..push_size.len()].copy_from_slice(push_size);
48 buf
49 }) as usize;
50 offset += n;
51 let Some(data) = bytes.get(offset..offset + l) else {
52 return Err(ParseScriptError::UnexpectedEnd(l, bytes.len() - offset));
53 };
54 offset += l;
55 a.push(ScriptElem::Bytes(data));
56 } else {
57 a.push(ScriptElem::Op(opcode));
58 }
59 } else if b <= 75 {
60 let Some(data) = bytes.get(offset..offset + b as usize) else {
61 return Err(ParseScriptError::UnexpectedEnd(b as usize, bytes.len() - offset));
62 };
63 offset += b as usize;
64 a.push(ScriptElem::Bytes(data));
65 } else {
66 return Err(ParseScriptError::Invalid(b));
67 }
68 }
69
70 Ok(a)
71}
72
73#[derive(Debug)]
74pub enum ParseScriptError {
75 Invalid(u8),
76 UnexpectedEndPushdataLength(Opcode),
77 UnexpectedEnd(usize, usize),
78}
79
80impl fmt::Display for ParseScriptError {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 Self::Invalid(opcode) => write!(f, "Invalid opcode 0x{opcode:02x}"),
84 Self::UnexpectedEndPushdataLength(opcode) => write!(
85 f,
86 "{opcode} with incomplete push length (SCRIPT_ERR_BAD_OPCODE)"
87 ),
88 Self::UnexpectedEnd(expected, actual) => write!(
89 f,
90 "Invalid length, expected {expected} but got {actual} (SCRIPT_ERR_BAD_OPCODE)"
91 ),
92 }
93 }
94}
95
96