parity-zcash/src/script/script.rs

314 lines
7.3 KiB
Rust
Raw Normal View History

2016-08-19 05:50:22 -07:00
//! Serialized script, used inside transaction inputs and outputs.
2016-09-12 13:20:01 -07:00
use std::{fmt, ops};
use hex::{ToHex, FromHex};
use script::{Opcode, Error};
2016-08-19 05:50:22 -07:00
/// Maximum number of bytes pushable to the stack
2016-09-15 02:45:31 -07:00
pub const MAX_SCRIPT_ELEMENT_SIZE: usize = 520;
2016-08-19 05:50:22 -07:00
/// Maximum number of non-push operations per script
2016-09-15 02:45:31 -07:00
pub const MAX_OPS_PER_SCRIPT: u32 = 201;
2016-08-19 05:50:22 -07:00
/// Maximum number of public keys per multisig
2016-09-14 08:48:00 -07:00
pub const MAX_PUBKEYS_PER_MULTISIG: usize = 20;
2016-08-19 05:50:22 -07:00
/// Maximum script length in bytes
2016-08-23 06:07:14 -07:00
pub const MAX_SCRIPT_SIZE: usize = 10000;
2016-08-19 05:50:22 -07:00
/// Threshold for nLockTime: below this value it is interpreted as block number,
/// otherwise as UNIX timestamp.
2016-09-15 02:45:31 -07:00
pub const LOCKTIME_THRESHOLD: u32 = 500000000; // Tue Nov 5 00:53:20 1985 UTC
2016-08-19 05:50:22 -07:00
/// Serialized script, used inside transaction inputs and outputs.
#[derive(PartialEq)]
2016-08-19 05:50:22 -07:00
pub struct Script {
data: Vec<u8>,
}
2016-09-12 13:20:01 -07:00
impl From<&'static str> for Script {
fn from(s: &'static str) -> Self {
Script::new(s.from_hex().unwrap())
}
}
impl From<Vec<u8>> for Script {
fn from(s: Vec<u8>) -> Self {
Script::new(s)
}
}
2016-08-19 05:50:22 -07:00
impl Script {
/// Script constructor.
pub fn new(data: Vec<u8>) -> Self {
Script {
data: data,
}
}
/// Extra-fast test for pay-to-script-hash scripts.
pub fn is_pay_to_script_hash(&self) -> bool {
self.data.len() == 23 &&
self.data[0] == Opcode::OP_HASH160 as u8 &&
self.data[1] == 0x14 &&
self.data[22] == Opcode::OP_EQUAL as u8
}
/// Extra-fast test for pay-to-witness-script-hash scripts.
pub fn is_pay_to_witness_script_hash(&self) -> bool {
self.data.len() == 34 &&
self.data[0] == Opcode::OP_0 as u8 &&
self.data[1] == 0x20
}
2016-08-23 06:07:14 -07:00
2016-09-11 03:22:25 -07:00
pub fn subscript(&self, from: usize) -> Script {
Script::new(self.data[from..].to_vec())
}
2016-08-23 06:07:14 -07:00
2016-09-11 03:22:25 -07:00
pub fn find_and_delete(&self, data: &[u8]) -> Script {
let mut result = Vec::new();
let mut current = 0;
let len = data.len();
let end = self.data.len();
2016-08-23 06:07:14 -07:00
2016-09-11 03:22:25 -07:00
if len > end {
2016-09-14 03:14:17 -07:00
return Script::new(self.data.to_vec());
2016-09-11 03:22:25 -07:00
}
2016-08-23 06:07:14 -07:00
2016-09-11 03:22:25 -07:00
while current < end - len {
2016-09-14 13:58:39 -07:00
if &self.data[current..current + len] != data {
2016-09-11 03:22:25 -07:00
result.push(self.data[current]);
current += 1;
} else {
current += len;
}
2016-08-23 06:07:14 -07:00
}
2016-09-11 03:22:25 -07:00
result.extend_from_slice(&self.data[current..]);
Script::new(result)
2016-08-23 06:07:14 -07:00
}
2016-09-11 03:22:25 -07:00
pub fn get_opcode(&self, position: usize) -> Result<Opcode, Error> {
Opcode::from_u8(self.data[position]).ok_or(Error::BadOpcode)
}
2016-08-23 06:07:14 -07:00
pub fn get_instruction(&self, position: usize) -> Result<Instruction, Error> {
let opcode = try!(self.get_opcode(position));
let instruction = match opcode {
Opcode::OP_PUSHDATA1 |
Opcode::OP_PUSHDATA2 |
Opcode::OP_PUSHDATA4 => {
let len = match opcode {
Opcode::OP_PUSHDATA1 => 1,
Opcode::OP_PUSHDATA2 => 2,
_ => 4,
};
let slice = try!(self.take(position + 1, len));
let n = try!(read_usize(slice, len));
let bytes = try!(self.take_checked(position + 1 + len, n));
Instruction {
opcode: opcode,
step: len + n + 1,
data: Some(bytes),
}
},
o if o >= Opcode::OP_0 && o <= Opcode::OP_PUSHBYTES_75 => {
let bytes = try!(self.take_checked(position+ 1, opcode as usize));
Instruction {
opcode: o,
step: opcode as usize + 1,
data: Some(bytes),
}
},
_ => Instruction {
opcode: opcode,
step: 1,
data: None,
}
};
Ok(instruction)
}
2016-08-23 06:07:14 -07:00
#[inline]
2016-09-11 03:22:25 -07:00
pub fn take(&self, offset: usize, len: usize) -> Result<&[u8], Error> {
2016-08-23 06:07:14 -07:00
if offset + len > self.data.len() {
Err(Error::BadOpcode)
} else {
Ok(&self.data[offset..offset + len])
}
}
#[inline]
2016-09-11 03:22:25 -07:00
pub fn take_checked(&self, offset: usize, len: usize) -> Result<&[u8], Error> {
2016-08-23 06:07:14 -07:00
if len > MAX_SCRIPT_ELEMENT_SIZE {
Err(Error::ScriptSize)
} else {
self.take(offset, len)
}
}
/// Returns Script without OP_CODESEPARATOR opcodes
pub fn without_separators(&self) -> Script {
let mut pc = 0;
let mut result = Vec::new();
while pc < self.len() {
match self.get_instruction(pc) {
Ok(instruction) => {
if instruction.opcode != Opcode::OP_CODESEPARATOR {
result.extend_from_slice(&self[pc..pc + instruction.step]);
}
pc += instruction.step;
},
_ => {
result.push(self[pc]);
pc += 1;
}
}
}
Script::new(result)
}
2016-09-15 02:45:31 -07:00
/// Returns true if script contains only push opcodes
pub fn is_push_only(&self) -> bool {
let mut pc = 0;
while pc < self.len() {
let instruction = match self.get_instruction(pc) {
Ok(i) => i,
_ => return false,
};
if instruction.opcode > Opcode::OP_16 {
return false;
}
pc += instruction.step;
}
true
}
2016-08-19 05:50:22 -07:00
}
2016-09-12 13:20:01 -07:00
impl ops::Deref for Script {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.data
}
}
pub struct Instruction<'a> {
2016-09-15 02:45:31 -07:00
pub opcode: Opcode,
pub step: usize,
pub data: Option<&'a [u8]>,
}
2016-09-15 02:45:31 -07:00
fn read_usize(data: &[u8], size: usize) -> Result<usize, Error> {
2016-09-12 01:57:12 -07:00
if data.len() < size {
return Err(Error::BadOpcode);
}
let result = data
.iter()
.take(size)
.enumerate()
.fold(0, |acc, (i, x)| acc + ((*x as usize) << (i * 8)));
Ok(result)
}
macro_rules! try_or_fmt {
($fmt: expr, $expr: expr) => {
match $expr {
Ok(o) => o,
Err(e) => return e.fmt($fmt),
}
}
}
impl fmt::Debug for Script {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.data.to_hex())
}
}
impl fmt::Display for Script {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut pc = 0;
2016-09-12 01:57:12 -07:00
while pc < self.len() {
let instruction = try_or_fmt!(f, self.get_instruction(pc));
match instruction.data {
Some(data) => try!(writeln!(f, "{:?} 0x{}", instruction.opcode, data.to_hex())),
None => try!(writeln!(f, "{:?}", instruction.opcode)),
2016-09-12 01:57:12 -07:00
}
pc += instruction.step;
2016-09-12 01:57:12 -07:00
}
2016-09-12 01:57:12 -07:00
Ok(())
}
}
2016-08-19 05:50:22 -07:00
#[cfg(test)]
mod tests {
use hex::FromHex;
2016-09-12 01:57:12 -07:00
use script::{Builder, Opcode};
2016-08-19 05:50:22 -07:00
use super::Script;
#[test]
fn test_is_pay_to_script_hash() {
let data = "a9143b80842f4ea32806ce5e723a255ddd6490cfd28d87".from_hex().unwrap();
let data2 = "a9143b80842f4ea32806ce5e723a255ddd6490cfd28d88".from_hex().unwrap();
assert!(Script::new(data).is_pay_to_script_hash());
assert!(!Script::new(data2).is_pay_to_script_hash());
}
#[test]
fn test_is_pay_to_witness_script_hash() {
let data = "00203b80842f4ea32806ce5e723a255ddd6490cfd28dac38c58bf9254c0577330693".from_hex().unwrap();
let data2 = "01203b80842f4ea32806ce5e723a255ddd6490cfd28dac38c58bf9254c0577330693".from_hex().unwrap();
assert!(Script::new(data).is_pay_to_witness_script_hash());
assert!(!Script::new(data2).is_pay_to_witness_script_hash());
}
2016-09-12 01:57:12 -07:00
#[test]
fn test_script_debug() {
use std::fmt::Write;
let script = Builder::default()
.push_num(3.into())
.push_num(2.into())
.push_opcode(Opcode::OP_ADD)
.into_script();
let s = "0103010293";
let mut res = String::new();
write!(&mut res, "{:?}", script).unwrap();
assert_eq!(s.to_string(), res);
}
#[test]
fn test_script_display() {
let script = Builder::default()
.push_num(3.into())
.push_num(2.into())
.push_opcode(Opcode::OP_ADD)
.into_script();
let s = r#"OP_PUSHBYTES_1 0x03
OP_PUSHBYTES_1 0x02
OP_ADD
"#;
assert_eq!(script.to_string(), s.to_string());
}
#[test]
fn test_script_without_op_codeseparator() {
let script: Script = "ab00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into();
let scr_goal: Script = "00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into();
assert_eq!(script.without_separators(), scr_goal);
}
2016-08-19 05:50:22 -07:00
}