pub mod convert;
pub mod stack;
use crate::opcode::Opcode;
use core::fmt;
#[derive(Debug, Clone, Copy)]
pub enum ScriptElem<'a> {
Op(Opcode),
Bytes(&'a [u8]),
}
impl<'a> fmt::Display for ScriptElem<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Op(opcode) => write!(f, "{}", opcode.name().unwrap_or("UNKNOWN")),
Self::Bytes(bytes) => {
write!(f, "<")?;
for byte in *bytes {
write!(f, "{:02x}", byte)?;
}
write!(f, ">")
}
}
}
}
pub type Script<'a> = Vec<ScriptElem<'a>>;
pub type ScriptSlice<'a> = &'a [ScriptElem<'a>];
pub fn parse_script(bytes: &[u8]) -> Result<Script<'_>, ParseScriptError> {
let mut a = Vec::new();
let mut offset = 0;
while offset < bytes.len() {
let b = bytes[offset];
offset += 1;
let opcode = Opcode { opcode: b };
if opcode.name().is_some() {
if let Some(n) = opcode.pushdata_length() {
let n = n;
let Some(push_size) = bytes.get(offset..offset + n) else {
return Err(ParseScriptError::UnexpectedEndPushdataLength(opcode));
};
let l = u32::from_le_bytes({
let mut buf = [0u8; 4];
buf[0..push_size.len()].copy_from_slice(push_size);
buf
}) as usize;
offset += n;
let Some(data) = bytes.get(offset..offset + l) else {
return Err(ParseScriptError::UnexpectedEnd(l, bytes.len() - offset));
};
offset += l;
a.push(ScriptElem::Bytes(data));
} else {
a.push(ScriptElem::Op(opcode));
}
} else if b <= 75 {
let Some(data) = bytes.get(offset..offset + b as usize) else {
return Err(ParseScriptError::UnexpectedEnd(b as usize, bytes.len() - offset));
};
offset += b as usize;
a.push(ScriptElem::Bytes(data));
} else {
return Err(ParseScriptError::Invalid(b));
}
}
Ok(a)
}
#[derive(Debug)]
pub enum ParseScriptError {
Invalid(u8),
UnexpectedEndPushdataLength(Opcode),
UnexpectedEnd(usize, usize),
}
impl fmt::Display for ParseScriptError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Invalid(opcode) => write!(f, "Invalid opcode 0x{opcode:02x}"),
Self::UnexpectedEndPushdataLength(opcode) => write!(
f,
"{opcode} with incomplete push length (SCRIPT_ERR_BAD_OPCODE)"
),
Self::UnexpectedEnd(expected, actual) => write!(
f,
"Invalid length, expected {expected} but got {actual} (SCRIPT_ERR_BAD_OPCODE)"
),
}
}
}