use crate::script::op_codes::*;
use crate::util::{Error, Result};
use hex;
use std::fmt;
pub mod checker;
pub mod interpreter;
#[allow(dead_code)]
pub mod op_codes;
pub mod stack;
pub use self::checker::{Checker, TransactionChecker, TransactionlessChecker};
pub(crate) use self::interpreter::next_op;
pub use self::interpreter::{NO_FLAGS, PREGENESIS_RULES};
#[derive(Default, Clone, PartialEq, Eq, Hash)]
pub struct Script(pub Vec<u8>);
impl Script {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn append(&mut self, byte: u8) {
self.0.push(byte);
}
pub fn append_slice(&mut self, slice: &[u8]) {
self.0.extend_from_slice(slice);
}
pub fn append_data(&mut self, data: &[u8]) -> Result<()> {
let len = data.len();
if len > u32::MAX as usize {
return Err(Error::BadArgument("Data too large for push".to_string()));
}
match len {
0 => self.0.push(OP_0),
1..=75 => {
self.0.push(len as u8);
self.0.extend_from_slice(data);
}
76..=255 => {
self.0.push(OP_PUSHDATA1);
self.0.push(len as u8);
self.0.extend_from_slice(data);
}
256..=65535 => {
self.0.push(OP_PUSHDATA2);
self.0.push((len & 0xFF) as u8);
self.0.push(((len >> 8) & 0xFF) as u8);
self.0.extend_from_slice(data);
}
_ => {
self.0.push(OP_PUSHDATA4);
self.0.push((len & 0xFF) as u8);
self.0.push(((len >> 8) & 0xFF) as u8);
self.0.push(((len >> 16) & 0xFF) as u8);
self.0.push(((len >> 24) & 0xFF) as u8);
self.0.extend_from_slice(data);
}
}
Ok(())
}
pub fn append_num(&mut self, n: i32) -> Result<()> {
let _ = self.append_data(&stack::encode_num(n as i64)?);
Ok(())
}
#[must_use]
pub fn eval<T: Checker>(&self, checker: &mut T, flags: u32) -> Result<()> {
interpreter::eval(&self.0, checker, flags)
}
}
impl fmt::Debug for Script {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = &self.0;
let mut ret = String::with_capacity(script.len() * 3); ret.push('[');
let mut i = 0;
while i < script.len() {
if i != 0 {
ret.push(' ');
}
match script[i] {
OP_0 => ret.push_str("OP_0"),
OP_1NEGATE => ret.push_str("OP_1NEGATE"),
OP_1 => ret.push_str("OP_1"),
OP_2 => ret.push_str("OP_2"),
OP_3 => ret.push_str("OP_3"),
OP_4 => ret.push_str("OP_4"),
OP_5 => ret.push_str("OP_5"),
OP_6 => ret.push_str("OP_6"),
OP_7 => ret.push_str("OP_7"),
OP_8 => ret.push_str("OP_8"),
OP_9 => ret.push_str("OP_9"),
OP_10 => ret.push_str("OP_10"),
OP_11 => ret.push_str("OP_11"),
OP_12 => ret.push_str("OP_12"),
OP_13 => ret.push_str("OP_13"),
OP_14 => ret.push_str("OP_14"),
OP_15 => ret.push_str("OP_15"),
OP_16 => ret.push_str("OP_16"),
len @ 1..=75 => {
ret.push_str(&format!("OP_PUSH{} ", len));
let end = i + 1 + len as usize;
if end <= script.len() {
ret.push_str(&hex::encode(&script[i + 1..end]));
} else {
break;
}
}
OP_PUSHDATA1 => {
ret.push_str("OP_PUSHDATA1 ");
if i + 2 > script.len() {
break;
}
let len = script[i + 1] as usize;
ret.push_str(&format!("{} ", len));
let end = i + 2 + len;
if end <= script.len() {
ret.push_str(&hex::encode(&script[i + 2..end]));
} else {
break;
}
}
OP_PUSHDATA2 => {
ret.push_str("OP_PUSHDATA2 ");
if i + 3 > script.len() {
break;
}
let len = u16::from_le_bytes([script[i + 1], script[i + 2]]) as usize;
ret.push_str(&format!("{} ", len));
let end = i + 3 + len;
if end <= script.len() {
ret.push_str(&hex::encode(&script[i + 3..end]));
} else {
break;
}
}
OP_PUSHDATA4 => {
ret.push_str("OP_PUSHDATA4 ");
if i + 5 > script.len() {
break;
}
let len = u32::from_le_bytes([
script[i + 1],
script[i + 2],
script[i + 3],
script[i + 4],
]) as usize;
ret.push_str(&format!("{} ", len));
let end = i + 5 + len;
if end <= script.len() {
ret.push_str(&hex::encode(&script[i + 5..end]));
} else {
break;
}
}
OP_NOP => ret.push_str("OP_NOP"),
OP_IF => ret.push_str("OP_IF"),
OP_NOTIF => ret.push_str("OP_NOTIF"),
OP_ELSE => ret.push_str("OP_ELSE"),
OP_ENDIF => ret.push_str("OP_ENDIF"),
OP_VERIFY => ret.push_str("OP_VERIFY"),
OP_RETURN => ret.push_str("OP_RETURN"),
OP_TOALTSTACK => ret.push_str("OP_TOALTSTACK"),
OP_FROMALTSTACK => ret.push_str("OP_FROMALTSTACK"),
OP_IFDUP => ret.push_str("OP_IFDUP"),
OP_DEPTH => ret.push_str("OP_DEPTH"),
OP_DROP => ret.push_str("OP_DROP"),
OP_DUP => ret.push_str("OP_DUP"),
OP_NIP => ret.push_str("OP_NIP"),
OP_OVER => ret.push_str("OP_OVER"),
OP_PICK => ret.push_str("OP_PICK"),
OP_ROLL => ret.push_str("OP_ROLL"),
OP_ROT => ret.push_str("OP_ROT"),
OP_SWAP => ret.push_str("OP_SWAP"),
OP_TUCK => ret.push_str("OP_TUCK"),
OP_2DROP => ret.push_str("OP_2DROP"),
OP_2DUP => ret.push_str("OP_2DUP"),
OP_3DUP => ret.push_str("OP_3DUP"),
OP_2OVER => ret.push_str("OP_2OVER"),
OP_2ROT => ret.push_str("OP_2ROT"),
OP_2SWAP => ret.push_str("OP_2SWAP"),
OP_CAT => ret.push_str("OP_CAT"),
OP_SPLIT => ret.push_str("OP_SPLIT"),
OP_SIZE => ret.push_str("OP_SIZE"),
OP_AND => ret.push_str("OP_AND"),
OP_OR => ret.push_str("OP_OR"),
OP_XOR => ret.push_str("OP_XOR"),
OP_EQUAL => ret.push_str("OP_EQUAL"),
OP_EQUALVERIFY => ret.push_str("OP_EQUALVERIFY"),
OP_1ADD => ret.push_str("OP_1ADD"),
OP_1SUB => ret.push_str("OP_1SUB"),
OP_NEGATE => ret.push_str("OP_NEGATE"),
OP_ABS => ret.push_str("OP_ABS"),
OP_NOT => ret.push_str("OP_NOT"),
OP_0NOTEQUAL => ret.push_str("OP_0NOTEQUAL"),
OP_ADD => ret.push_str("OP_ADD"),
OP_SUB => ret.push_str("OP_SUB"),
OP_DIV => ret.push_str("OP_DIV"),
OP_MOD => ret.push_str("OP_MOD"),
OP_BOOLAND => ret.push_str("OP_BOOLAND"),
OP_BOOLOR => ret.push_str("OP_BOOLOR"),
OP_NUMEQUAL => ret.push_str("OP_NUMEQUAL"),
OP_NUMEQUALVERIFY => ret.push_str("OP_NUMEQUALVERIFY"),
OP_NUMNOTEQUAL => ret.push_str("OP_NUMNOTEQUAL"),
OP_LESSTHAN => ret.push_str("OP_LESSTHAN"),
OP_GREATERTHAN => ret.push_str("OP_GREATERTHAN"),
OP_LESSTHANOREQUAL => ret.push_str("OP_LESSTHANOREQUAL"),
OP_GREATERTHANOREQUAL => ret.push_str("OP_GREATERTHANOREQUAL"),
OP_MIN => ret.push_str("OP_MIN"),
OP_MAX => ret.push_str("OP_MAX"),
OP_WITHIN => ret.push_str("OP_WITHIN"),
OP_NUM2BIN => ret.push_str("OP_NUM2BIN"),
OP_BIN2NUM => ret.push_str("OP_BIN2NUM"),
OP_RIPEMD160 => ret.push_str("OP_RIPEMD160"),
OP_SHA1 => ret.push_str("OP_SHA1"),
OP_SHA256 => ret.push_str("OP_SHA256"),
OP_HASH160 => ret.push_str("OP_HASH160"),
OP_HASH256 => ret.push_str("OP_HASH256"),
OP_CODESEPARATOR => ret.push_str("OP_CODESEPARATOR"),
OP_CHECKSIG => ret.push_str("OP_CHECKSIG"),
OP_CHECKSIGVERIFY => ret.push_str("OP_CHECKSIGVERIFY"),
OP_CHECKMULTISIG => ret.push_str("OP_CHECKMULTISIG"),
OP_CHECKMULTISIGVERIFY => ret.push_str("OP_CHECKMULTISIGVERIFY"),
OP_CHECKLOCKTIMEVERIFY => ret.push_str("OP_CHECKLOCKTIMEVERIFY"),
OP_CHECKSEQUENCEVERIFY => ret.push_str("OP_CHECKSEQUENCEVERIFY"),
_ => ret.push_str(&format!("{}", script[i])),
}
i = next_op(i, script);
}
if i < script.len() {
for j in i..script.len() {
ret.push_str(&format!(" {}", script[j]));
}
}
ret.push(']');
f.write_str(&ret)
}
}
#[cfg(test)]
mod tests {
use super::op_codes::*;
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn append_data() {
let mut s = Script::new();
s.append_data(&[]).unwrap();
assert_eq!(s.0.len(), 1);
let mut s = Script::new();
s.append_data(&vec![0; 1]).unwrap();
assert_eq!(s.0[0], OP_PUSH + 1);
assert_eq!(s.0.len(), 2);
let mut s = Script::new();
s.append_data(&vec![0; 75]).unwrap();
assert_eq!(s.0[0], OP_PUSH + 75);
assert_eq!(s.0.len(), 76);
let mut s = Script::new();
s.append_data(&vec![0; 76]).unwrap();
assert_eq!(s.0[0], OP_PUSHDATA1);
assert_eq!(s.0[1], 76);
assert_eq!(s.0.len(), 78);
let mut s = Script::new();
s.append_data(&vec![0; 255]).unwrap();
assert_eq!(s.0[0], OP_PUSHDATA1);
assert_eq!(s.0[1], 255);
assert_eq!(s.0.len(), 257);
let mut s = Script::new();
s.append_data(&vec![0; 256]).unwrap();
assert_eq!(s.0[0], OP_PUSHDATA2);
assert_eq!(s.0[1], 0);
assert_eq!(s.0[2], 1);
assert_eq!(s.0.len(), 259);
let mut s = Script::new();
s.append_data(&vec![0; 65535]).unwrap();
assert_eq!(s.0[0], OP_PUSHDATA2);
assert_eq!(s.0[1], 255);
assert_eq!(s.0[2], 255);
assert_eq!(s.0.len(), 65538);
let mut s = Script::new();
s.append_data(&vec![0; 65536]).unwrap();
assert_eq!(s.0[0], OP_PUSHDATA4);
assert_eq!(s.0[1], 0);
assert_eq!(s.0[2], 0);
assert_eq!(s.0[3], 1);
assert_eq!(s.0[4], 0);
assert_eq!(s.0.len(), 65541);
}
#[test]
fn append_data_too_large() {
let mut s = Script::new();
let result = s.append_data(&vec![0u8; (u32::MAX as usize) + 1]);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Bad argument: Data too large for push"
);
}
}