script builder && basic tests
This commit is contained in:
parent
bf63fcbede
commit
03f5d3d4b0
|
@ -1,21 +1,54 @@
|
||||||
use script::{Opcode, Script};
|
use script::{Opcode, Script, Num};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
pub fn new() -> Self {
|
pub fn push_opcode(mut self, opcode: Opcode) -> Self {
|
||||||
Builder {
|
self.data.push(opcode as u8);
|
||||||
data: Vec::new(),
|
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.data.push(opcode as u8);
|
||||||
self
|
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 {
|
pub fn into_script(self) -> Script {
|
||||||
Script::new(self.data)
|
Script::new(self.data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,3 +65,11 @@ pub struct VerificationFlags {
|
||||||
/// Making v1-v16 witness program non-standard
|
/// Making v1-v16 witness program non-standard
|
||||||
pub verify_discourage_upgradable_witness_program: bool,
|
pub verify_discourage_upgradable_witness_program: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VerificationFlags {
|
||||||
|
pub fn verify_p2sh(mut self, value: bool) -> Self {
|
||||||
|
self.verify_p2sh = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -425,22 +425,6 @@ pub fn eval_script(
|
||||||
Opcode::OP_RETURN => {
|
Opcode::OP_RETURN => {
|
||||||
return Err(Error::ReturnOpcode);
|
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 => {
|
Opcode::OP_TOALTSTACK => {
|
||||||
try!(require_not_empty(stack));
|
try!(require_not_empty(stack));
|
||||||
altstack.push(stack.pop().unwrap());
|
altstack.push(stack.pop().unwrap());
|
||||||
|
@ -537,6 +521,45 @@ pub fn eval_script(
|
||||||
}
|
}
|
||||||
stack.push(v);
|
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()]
|
last != &vec![0; last.len()]
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(true)
|
Ok(success)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
use script::{Opcode, Script, VerificationFlags};
|
use script::{Opcode, Script, VerificationFlags, Builder, Error};
|
||||||
use super::{is_public_key, eval_script, NoopSignatureChecker, SignatureVersion};
|
use super::{is_public_key, eval_script, NoopSignatureChecker, SignatureVersion};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -570,9 +593,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_push_data() {
|
fn test_push_data() {
|
||||||
let expected = vec![vec![0x5a]];
|
let expected = vec![vec![0x5a]];
|
||||||
let mut flags = VerificationFlags::default();
|
let flags = VerificationFlags::default()
|
||||||
|
.verify_p2sh(true);
|
||||||
let checker = NoopSignatureChecker;
|
let checker = NoopSignatureChecker;
|
||||||
flags.verify_p2sh = true;
|
|
||||||
let version = SignatureVersion::Base;
|
let version = SignatureVersion::Base;
|
||||||
let direct = Script::new(vec![Opcode::OP_PUSHBYTES_1 as u8, 0x5a]);
|
let direct = Script::new(vec![Opcode::OP_PUSHBYTES_1 as u8, 0x5a]);
|
||||||
let pushdata1 = Script::new(vec![Opcode::OP_PUSHDATA1 as u8, 0x1, 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, pushdata2_stack);
|
||||||
assert_eq!(expected, pushdata4_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![]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue