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};
|
2016-09-12 06:09:32 -07:00
|
|
|
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.
|
2016-09-14 10:47:58 -07:00
|
|
|
#[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())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-14 10:47:58 -07:00
|
|
|
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
|
|
|
|
2016-09-14 14:54:52 -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)
|
|
|
|
}
|
|
|
|
}
|
2016-09-13 02:44:57 -07:00
|
|
|
|
|
|
|
/// Returns Script without OP_CODESEPARATOR opcodes
|
|
|
|
pub fn without_separators(&self) -> Script {
|
|
|
|
let mut pc = 0;
|
|
|
|
let mut result = Vec::new();
|
2016-09-14 14:54:52 -07:00
|
|
|
|
2016-09-13 02:44:57 -07:00
|
|
|
while pc < self.len() {
|
2016-09-14 14:54:52 -07:00
|
|
|
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;
|
2016-09-13 02:44:57 -07:00
|
|
|
},
|
2016-09-14 14:54:52 -07:00
|
|
|
_ => {
|
|
|
|
result.push(self[pc]);
|
|
|
|
pc += 1;
|
|
|
|
}
|
2016-09-13 02:44:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-14 14:54:52 -07:00
|
|
|
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-14 14:54:52 -07:00
|
|
|
}
|
|
|
|
|
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-14 14:54:52 -07:00
|
|
|
|
2016-09-12 01:57:12 -07:00
|
|
|
while pc < self.len() {
|
2016-09-14 14:54:52 -07:00
|
|
|
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
|
|
|
}
|
2016-09-14 14:54:52 -07:00
|
|
|
|
|
|
|
pc += instruction.step;
|
2016-09-12 01:57:12 -07:00
|
|
|
}
|
2016-09-14 14:54:52 -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());
|
|
|
|
}
|
2016-09-14 10:47:58 -07:00
|
|
|
|
|
|
|
#[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
|
|
|
}
|