use super::error::{InterpreterError, InterpreterErrorCode};
use crate::opcodes::*;
use crate::Script;
#[derive(Debug, Clone)]
pub struct ParsedOpcode {
pub opcode: u8,
pub data: Vec<u8>,
}
impl ParsedOpcode {
pub fn name(&self) -> &'static str {
crate::opcodes::opcode_to_string(self.opcode)
}
pub fn is_disabled(&self) -> bool {
matches!(self.opcode, OP_2MUL | OP_2DIV)
}
pub fn always_illegal(&self) -> bool {
matches!(self.opcode, OP_VERIF | OP_VERNOTIF)
}
pub fn is_conditional(&self) -> bool {
matches!(
self.opcode,
OP_IF | OP_NOTIF | OP_ELSE | OP_ENDIF | OP_VERIF | OP_VERNOTIF
)
}
pub fn requires_tx(&self) -> bool {
matches!(
self.opcode,
OP_CHECKSIG
| OP_CHECKSIGVERIFY
| OP_CHECKMULTISIG
| OP_CHECKMULTISIGVERIFY
| OP_CHECKSEQUENCEVERIFY
)
}
pub fn enforce_minimum_data_push(&self) -> Result<(), InterpreterError> {
let data_len = self.data.len();
if data_len == 0 && self.opcode != OP_0 {
return Err(InterpreterError::new(
InterpreterErrorCode::MinimalData,
format!(
"zero length data push is encoded with opcode {} instead of OP_0",
self.name()
),
));
}
if data_len == 1
&& (1..=16).contains(&self.data[0])
&& self.opcode != OP_1 + self.data[0] - 1
{
return Err(InterpreterError::new(
InterpreterErrorCode::MinimalData,
format!(
"data push of the value {} encoded with opcode {} instead of OP_{}",
self.data[0],
self.name(),
self.data[0]
),
));
}
if data_len == 1 && self.data[0] == 0x81 && self.opcode != OP_1NEGATE {
return Err(InterpreterError::new(
InterpreterErrorCode::MinimalData,
format!(
"data push of the value -1 encoded with opcode {} instead of OP_1NEGATE",
self.name()
),
));
}
if data_len <= 75 {
if self.opcode as usize != data_len {
return Err(InterpreterError::new(
InterpreterErrorCode::MinimalData,
format!(
"data push of {} bytes encoded with opcode {} instead of OP_DATA_{}",
data_len,
self.name(),
data_len
),
));
}
} else if data_len <= 255 {
if self.opcode != OP_PUSHDATA1 {
return Err(InterpreterError::new(
InterpreterErrorCode::MinimalData,
format!(
"data push of {} bytes encoded with opcode {} instead of OP_PUSHDATA1",
data_len,
self.name()
),
));
}
} else if data_len <= 65535 && self.opcode != OP_PUSHDATA2 {
return Err(InterpreterError::new(
InterpreterErrorCode::MinimalData,
format!(
"data push of {} bytes encoded with opcode {} instead of OP_PUSHDATA2",
data_len,
self.name()
),
));
}
Ok(())
}
pub fn canonical_push(&self) -> bool {
let opcode = self.opcode;
let data = &self.data;
let data_len = data.len();
if opcode > OP_16 {
return true;
}
if opcode < OP_PUSHDATA1 && opcode > OP_0 && data_len == 1 && data[0] <= 16 {
return false;
}
if opcode == OP_PUSHDATA1 && data_len < OP_PUSHDATA1 as usize {
return false;
}
if opcode == OP_PUSHDATA2 && data_len <= 0xff {
return false;
}
if opcode == OP_PUSHDATA4 && data_len <= 0xffff {
return false;
}
true
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = vec![self.opcode];
if self.opcode == 0
|| (self.opcode >= OP_1NEGATE && self.opcode <= OP_16)
|| self.opcode > OP_PUSHDATA4
{
if self.opcode == OP_RETURN && !self.data.is_empty() {
out.extend_from_slice(&self.data);
}
return out;
}
match self.opcode {
OP_PUSHDATA1 => {
out.push(self.data.len() as u8);
out.extend_from_slice(&self.data);
}
OP_PUSHDATA2 => {
out.extend_from_slice(&(self.data.len() as u16).to_le_bytes());
out.extend_from_slice(&self.data);
}
OP_PUSHDATA4 => {
out.extend_from_slice(&(self.data.len() as u32).to_le_bytes());
out.extend_from_slice(&self.data);
}
_ => {
out.extend_from_slice(&self.data);
}
}
out
}
}
pub type ParsedScript = Vec<ParsedOpcode>;
pub fn is_push_only(script: &ParsedScript) -> bool {
script.iter().all(|op| op.opcode <= OP_16)
}
pub fn remove_opcode_by_data(script: &ParsedScript, data: &[u8]) -> ParsedScript {
script
.iter()
.filter(|pop| !pop.canonical_push() || !pop.data.windows(data.len()).any(|w| w == data))
.cloned()
.collect()
}
pub fn remove_opcode(script: &ParsedScript, opcode: u8) -> ParsedScript {
script
.iter()
.filter(|pop| pop.opcode != opcode)
.cloned()
.collect()
}
pub fn unparse(pscript: &ParsedScript) -> Script {
let mut bytes = Vec::new();
for pop in pscript {
bytes.extend_from_slice(&pop.to_bytes());
}
Script::from_bytes(&bytes)
}
pub fn parse_script(
script: &Script,
error_on_checksig: bool,
) -> Result<ParsedScript, InterpreterError> {
let scr = script.to_bytes();
let mut parsed_ops = Vec::new();
let mut conditional_depth = 0i32;
let mut i = 0;
while i < scr.len() {
let instruction = scr[i];
let mut parsed_op = ParsedOpcode {
opcode: instruction,
data: Vec::new(),
};
if error_on_checksig && parsed_op.requires_tx() {
return Err(InterpreterError::new(
InterpreterErrorCode::InvalidParams,
"tx and previous output must be supplied for checksig".to_string(),
));
}
match instruction {
OP_IF | OP_NOTIF | OP_VERIF | OP_VERNOTIF => conditional_depth += 1,
OP_ENDIF => {
if conditional_depth > 0 {
conditional_depth -= 1;
}
}
OP_RETURN if conditional_depth == 0 => {
if i + 1 < scr.len() {
parsed_op.data = scr[i + 1..].to_vec();
}
parsed_ops.push(parsed_op);
return Ok(parsed_ops);
}
_ => {}
}
match instruction {
OP_PUSHDATA1 => {
if i + 1 >= scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"script truncated".to_string(),
));
}
let data_len = scr[i + 1] as usize;
if i + 2 + data_len > scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"push data exceeds script length".to_string(),
));
}
parsed_op.data = scr[i + 2..i + 2 + data_len].to_vec();
i += 2 + data_len;
}
OP_PUSHDATA2 => {
if i + 2 >= scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"script truncated".to_string(),
));
}
let data_len = u16::from_le_bytes([scr[i + 1], scr[i + 2]]) as usize;
if i + 3 + data_len > scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"push data exceeds script length".to_string(),
));
}
parsed_op.data = scr[i + 3..i + 3 + data_len].to_vec();
i += 3 + data_len;
}
OP_PUSHDATA4 => {
if i + 4 >= scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"script truncated".to_string(),
));
}
let data_len =
u32::from_le_bytes([scr[i + 1], scr[i + 2], scr[i + 3], scr[i + 4]]) as usize;
if i + 5 + data_len > scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"push data exceeds script length".to_string(),
));
}
parsed_op.data = scr[i + 5..i + 5 + data_len].to_vec();
i += 5 + data_len;
}
op if op >= OP_DATA_1 && op <= OP_DATA_75 => {
let data_len = op as usize;
if i + 1 + data_len > scr.len() {
return Err(InterpreterError::new(
InterpreterErrorCode::MalformedPush,
"script truncated".to_string(),
));
}
parsed_op.data = scr[i + 1..i + 1 + data_len].to_vec();
i += 1 + data_len;
}
_ => {
i += 1;
}
}
parsed_ops.push(parsed_op);
}
Ok(parsed_ops)
}