script builder && basic tests

This commit is contained in:
debris 2016-09-08 16:40:07 +02:00
parent bf63fcbede
commit 03f5d3d4b0
3 changed files with 200 additions and 26 deletions

View File

@ -1,21 +1,54 @@
use script::{Opcode, Script};
use script::{Opcode, Script, Num};
#[derive(Default)]
pub struct Builder {
data: Vec<u8>,
}
impl Builder {
pub fn new() -> Self {
Builder {
data: Vec::new(),
}
pub fn push_opcode(mut self, opcode: Opcode) -> Self {
self.data.push(opcode as u8);
self
}
pub fn push_opcode(&mut self, opcode: Opcode) -> &mut Self {
pub fn push_bool(mut self, value: bool) -> Self {
let opcode = match value {
true => Opcode::OP_1,
false => Opcode::OP_0,
};
self.data.push(opcode as u8);
self
}
pub fn push_num(mut self, num: Num) -> Self {
self.push_data(&num.to_vec())
}
pub fn push_data(mut self, data: &[u8]) -> Self {
let len = data.len();
if len < Opcode::OP_PUSHDATA1 as usize {
self.data.push(len as u8);
} else if len < 0x100 {
self.data.push(Opcode::OP_PUSHDATA1 as u8);
self.data.push(len as u8);
} else if len < 0x10000 {
self.data.push(Opcode::OP_PUSHDATA2 as u8);
self.data.push(len as u8);
self.data.push((len >> 8) as u8);
} else if len < 0x1_0000_0000 {
self.data.push(Opcode::OP_PUSHDATA4 as u8);
self.data.push(len as u8);
self.data.push((len >> 8) as u8);
self.data.push((len >> 16) as u8);
self.data.push((len >> 24) as u8);
} else {
panic!("Cannot push more than 0x1_0000_0000 bytes");
}
self.data.extend_from_slice(data);
self
}
pub fn into_script(self) -> Script {
Script::new(self.data)
}

View File

@ -65,3 +65,11 @@ pub struct VerificationFlags {
/// Making v1-v16 witness program non-standard
pub verify_discourage_upgradable_witness_program: bool,
}
impl VerificationFlags {
pub fn verify_p2sh(mut self, value: bool) -> Self {
self.verify_p2sh = value;
self
}
}

View File

@ -425,22 +425,6 @@ pub fn eval_script(
Opcode::OP_RETURN => {
return Err(Error::ReturnOpcode);
},
Opcode::OP_EQUAL => {
try!(require_len(stack, 2));
let equal = stack.pop() == stack.pop();
let to_push = match equal {
true => vec![1],
false => vec![0],
};
stack.push(to_push);
},
Opcode::OP_EQUALVERIFY => {
try!(require_len(stack, 2));
let equal = stack.pop() == stack.pop();
if !equal {
return Err(Error::EqualVerify);
}
},
Opcode::OP_TOALTSTACK => {
try!(require_not_empty(stack));
altstack.push(stack.pop().unwrap());
@ -537,6 +521,45 @@ pub fn eval_script(
}
stack.push(v);
},
Opcode::OP_ROT => {
try!(require_len(stack, 3));
let len = stack.len();
stack.swap(len - 3, len - 2);
stack.swap(len - 2, len - 1);
},
Opcode::OP_SWAP => {
try!(require_len(stack, 2));
let len = stack.len();
stack.swap(len - 2, len - 1);
},
Opcode::OP_TUCK => {
try!(require_len(stack, 2));
let len = stack.len();
let v = stack[len - 1].clone();
stack.insert(len - 2, v);
},
Opcode::OP_SIZE => {
try!(require_not_empty(stack));
let n = Num::from(stack.last().unwrap().len());
stack.push(n.to_vec());
},
Opcode::OP_EQUAL => {
try!(require_len(stack, 2));
let v1 = stack.pop();
let v2 = stack.pop();
let to_push = match v1 == v2 {
true => vec![1],
false => vec![0],
};
stack.push(to_push);
},
Opcode::OP_EQUALVERIFY => {
try!(require_len(stack, 2));
let equal = stack.pop() == stack.pop();
if !equal {
return Err(Error::EqualVerify);
}
},
_ => (),
},
}
@ -547,13 +570,13 @@ pub fn eval_script(
last != &vec![0; last.len()]
};
Ok(true)
Ok(success)
}
#[cfg(test)]
mod tests {
use hex::FromHex;
use script::{Opcode, Script, VerificationFlags};
use script::{Opcode, Script, VerificationFlags, Builder, Error};
use super::{is_public_key, eval_script, NoopSignatureChecker, SignatureVersion};
#[test]
@ -570,9 +593,9 @@ mod tests {
#[test]
fn test_push_data() {
let expected = vec![vec![0x5a]];
let mut flags = VerificationFlags::default();
let flags = VerificationFlags::default()
.verify_p2sh(true);
let checker = NoopSignatureChecker;
flags.verify_p2sh = true;
let version = SignatureVersion::Base;
let direct = Script::new(vec![Opcode::OP_PUSHBYTES_1 as u8, 0x5a]);
let pushdata1 = Script::new(vec![Opcode::OP_PUSHDATA1 as u8, 0x1, 0x5a]);
@ -593,4 +616,114 @@ mod tests {
assert_eq!(expected, pushdata2_stack);
assert_eq!(expected, pushdata4_stack);
}
fn basic_test(script: &Script, expected: Result<bool, Error>, expected_stack: Vec<Vec<u8>>) {
let flags = VerificationFlags::default()
.verify_p2sh(true);
let checker = NoopSignatureChecker;
let version = SignatureVersion::Base;
let mut stack = vec![];
assert_eq!(eval_script(&mut stack, script, &flags, &checker, version), expected);
if expected.is_ok() {
assert_eq!(stack, expected_stack);
}
}
#[test]
fn test_equal() {
let script = Builder::default()
.push_data(&[0x4])
.push_data(&[0x4])
.push_opcode(Opcode::OP_EQUAL)
.into_script();
let result = Ok(true);
let stack = vec![vec![1]];
basic_test(&script, result, stack);
}
#[test]
fn test_equal_false() {
let script = Builder::default()
.push_data(&[0x4])
.push_data(&[0x3])
.push_opcode(Opcode::OP_EQUAL)
.into_script();
let result = Ok(false);
let stack = vec![vec![0]];
basic_test(&script, result, stack);
}
#[test]
fn test_equal_invalid_stack() {
let script = Builder::default()
.push_data(&[0x4])
.push_opcode(Opcode::OP_EQUAL)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test(&script, result, vec![]);
}
#[test]
fn test_equal_verify() {
let script = Builder::default()
.push_data(&[0x4])
.push_data(&[0x4])
.push_opcode(Opcode::OP_EQUALVERIFY)
.into_script();
let result = Ok(false);
let stack = vec![];
basic_test(&script, result, stack);
}
#[test]
fn test_equal_verify_failed() {
let script = Builder::default()
.push_data(&[0x4])
.push_data(&[0x3])
.push_opcode(Opcode::OP_EQUALVERIFY)
.into_script();
let result = Err(Error::EqualVerify);
basic_test(&script, result, vec![]);
}
#[test]
fn test_equal_verify_invalid_stack() {
let script = Builder::default()
.push_data(&[0x4])
.push_opcode(Opcode::OP_EQUALVERIFY)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test(&script, result, vec![]);
}
#[test]
fn test_size() {
let script = Builder::default()
.push_data(&[0x12, 0x34])
.push_opcode(Opcode::OP_SIZE)
.into_script();
let result = Ok(true);
let stack = vec![vec![0x12, 0x34], vec![0x2]];
basic_test(&script, result, stack);
}
#[test]
fn test_size_false() {
let script = Builder::default()
.push_data(&[])
.push_opcode(Opcode::OP_SIZE)
.into_script();
let result = Ok(false);
let stack = vec![vec![], vec![]];
basic_test(&script, result, stack);
}
#[test]
fn test_size_invalid_stack() {
let script = Builder::default()
.push_opcode(Opcode::OP_SIZE)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test(&script, result, vec![]);
}
}