stack structure

This commit is contained in:
debris 2016-09-17 15:46:53 +02:00
parent a53fd1bd5d
commit 7a13143824
3 changed files with 456 additions and 200 deletions

View File

@ -4,7 +4,7 @@ use transaction::SEQUENCE_LOCKTIME_DISABLE_FLAG;
use crypto::{sha1, sha256, dhash160, dhash256, ripemd160};
use script::{
script, Script, Num, VerificationFlags, Opcode, Error,
Sighash, SignatureChecker, SignatureVersion
Sighash, SignatureChecker, SignatureVersion, Stack
};
/// Helper function.
@ -228,22 +228,6 @@ fn cast_to_bool(data: &[u8]) -> bool {
}
}
#[inline]
fn require_not_empty(stack: &Vec<Vec<u8>>) -> Result<(), Error> {
match stack.is_empty() {
true => Err(Error::InvalidStackOperation),
false => Ok(()),
}
}
#[inline]
fn require_len(stack: &Vec<Vec<u8>>, len: usize) -> Result<(), Error> {
match stack.len() < len {
true => Err(Error::InvalidStackOperation),
false => Ok(()),
}
}
pub fn verify_script(
script_sig: &Script,
script_pubkey: &Script,
@ -254,8 +238,8 @@ pub fn verify_script(
return Err(Error::SignaturePushOnly);
}
let mut stack = Vec::new();
let mut stack_copy = Vec::new();
let mut stack = Stack::new();
let mut stack_copy = Stack::new();
try!(eval_script(&mut stack, script_sig, flags, checker, SignatureVersion::Base));
@ -281,7 +265,7 @@ pub fn verify_script(
// an empty stack and the EvalScript above would return false.
assert!(!stack.is_empty());
let pubkey2: Script = stack.pop().unwrap().into();
let pubkey2: Script = try!(stack.pop()).into();
let res = try!(eval_script(&mut stack, &pubkey2, flags, checker, SignatureVersion::Base));
if !res {
@ -306,7 +290,7 @@ pub fn verify_script(
}
pub fn eval_script(
stack: &mut Vec<Vec<u8>>,
stack: &mut Stack<Vec<u8>>,
script: &Script,
flags: &VerificationFlags,
checker: &SignatureChecker,
@ -320,7 +304,7 @@ pub fn eval_script(
let mut op_count = 0;
let mut begincode = 0;
let mut exec_stack = Vec::<bool>::new();
let mut altstack = Vec::<Vec<u8>>::new();
let mut altstack = Stack::<Vec<u8>>::new();
while pc < script.len() {
let executing = exec_stack.iter().all(|x| *x);
@ -471,8 +455,6 @@ pub fn eval_script(
}
}
try!(require_not_empty(stack));
// Note that elsewhere numeric opcodes are limited to
// operands in the range -2**31+1 to 2**31-1, however it is
// legal for opcodes to produce results exceeding that
@ -487,7 +469,7 @@ pub fn eval_script(
// Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums, which are good until 2**39-1, well
// beyond the 2**32-1 limit of the nLockTime field itself.
let lock_time = try!(Num::from_slice(stack.last().unwrap(), flags.verify_minimaldata, 5));
let lock_time = try!(Num::from_slice(try!(stack.last()), flags.verify_minimaldata, 5));
// In the rare event that the argument may be < 0 due to
// some arithmetic being done first, you can always use
@ -507,9 +489,7 @@ pub fn eval_script(
}
}
try!(require_not_empty(stack));
let sequence = try!(Num::from_slice(stack.last().unwrap(), flags.verify_minimaldata, 5));
let sequence = try!(Num::from_slice(try!(stack.last()), flags.verify_minimaldata, 5));
if sequence.is_negative() {
return Err(Error::NegativeLocktime);
@ -536,8 +516,7 @@ pub fn eval_script(
Opcode::OP_IF | Opcode::OP_NOTIF => {
let mut exec_value = false;
if executing {
try!(require_not_empty(stack).map_err(|_| Error::UnbalancedConditional));
exec_value = cast_to_bool(&stack.pop().unwrap());
exec_value = cast_to_bool(&try!(stack.pop().map_err(|_| Error::UnbalancedConditional)));
if opcode == Opcode::OP_NOTIF {
exec_value = !exec_value;
}
@ -558,9 +537,7 @@ pub fn eval_script(
exec_stack.pop();
},
Opcode::OP_VERIFY => {
try!(require_not_empty(stack));
// should we return an error without popping the value?
let exec_value = cast_to_bool(&stack.pop().unwrap());
let exec_value = cast_to_bool(&try!(stack.pop()));
if !exec_value {
return Err(Error::Verify);
}
@ -569,63 +546,32 @@ pub fn eval_script(
return Err(Error::ReturnOpcode);
},
Opcode::OP_TOALTSTACK => {
try!(require_not_empty(stack));
altstack.push(stack.pop().unwrap());
altstack.push(try!(stack.pop()));
},
Opcode::OP_FROMALTSTACK => {
try!(require_not_empty(&altstack).map_err(|_| Error::InvalidAltstackOperation));
stack.push(altstack.pop().unwrap());
stack.push(try!(altstack.pop().map_err(|_| Error::InvalidAltstackOperation)));
},
Opcode::OP_2DROP => {
try!(require_len(stack, 2));
stack.pop();
stack.pop();
try!(stack.drop(2));
},
Opcode::OP_2DUP => {
try!(require_len(stack, 2));
let v1 = stack[stack.len() - 2].clone();
let v2 = stack[stack.len() - 1].clone();
stack.push(v1);
stack.push(v2);
try!(stack.dup(2));
},
Opcode::OP_3DUP => {
try!(require_len(stack, 3));
let v1 = stack[stack.len() - 3].clone();
let v2 = stack[stack.len() - 2].clone();
let v3 = stack[stack.len() - 1].clone();
stack.push(v1);
stack.push(v2);
stack.push(v3);
try!(stack.dup(3));
},
Opcode::OP_2OVER => {
try!(require_len(stack, 4));
let v1 = stack[stack.len() - 4].clone();
let v2 = stack[stack.len() - 3].clone();
stack.push(v1);
stack.push(v2);
try!(stack.over(2));
},
Opcode::OP_2ROT => {
try!(require_len(stack, 6));
let v1 = stack[stack.len() - 6].clone();
let v2 = stack[stack.len() - 5].clone();
let len = stack.len();
stack.remove(len - 6);
// -5 -just removed element
stack.remove(len - 6);
stack.push(v1);
stack.push(v2);
try!(stack.rot(2));
},
Opcode::OP_2SWAP => {
try!(require_len(stack, 4));
let len = stack.len();
stack.swap(len - 4, len - 2);
stack.swap(len - 3, len - 1);
try!(stack.swap(2));
},
Opcode::OP_IFDUP => {
try!(require_not_empty(stack));
if cast_to_bool(stack.last().unwrap()) {
let last = stack.last().unwrap().clone();
stack.push(last);
if cast_to_bool(try!(stack.last())) {
try!(stack.dup(1));
}
},
Opcode::OP_DEPTH => {
@ -633,63 +579,46 @@ pub fn eval_script(
stack.push(depth.to_vec());
},
Opcode::OP_DROP => {
try!(require_not_empty(stack));
stack.pop();
try!(stack.pop());
},
Opcode::OP_DUP => {
try!(require_not_empty(stack));
let v1 = stack[stack.len() - 1].clone();
stack.push(v1);
try!(stack.dup(1));
},
Opcode::OP_NIP => {
try!(require_len(stack, 2));
let len = stack.len();
stack.swap_remove(len - 2);
try!(stack.nip());
},
Opcode::OP_OVER => {
try!(require_len(stack, 2));
let v = stack[stack.len() - 2].clone();
stack.push(v);
try!(stack.over(1));
},
Opcode::OP_PICK | Opcode::OP_ROLL => {
try!(require_len(stack, 2));
let n: i64 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4)).into();
let n: i64 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4)).into();
if n < 0 || n >= stack.len() as i64 {
return Err(Error::InvalidStackOperation);
}
let v = stack[n as usize + 1].clone();
if opcode == Opcode::OP_ROLL {
stack.remove(n as usize + 1);
}
let v = match opcode {
Opcode::OP_PICK => try!(stack.top(n as usize)).clone(),
_ => try!(stack.remove(n as usize)),
};
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);
try!(stack.rot(1));
},
Opcode::OP_SWAP => {
try!(require_len(stack, 2));
let len = stack.len();
stack.swap(len - 2, len - 1);
try!(stack.swap(1));
},
Opcode::OP_TUCK => {
try!(require_len(stack, 2));
let len = stack.len();
let v = stack[len - 1].clone();
stack.insert(len - 2, v);
try!(stack.tuck());
},
Opcode::OP_SIZE => {
try!(require_not_empty(stack));
let n = Num::from(stack.last().unwrap().len());
let n = Num::from(try!(stack.last()).len());
stack.push(n.to_vec());
},
Opcode::OP_EQUAL => {
try!(require_len(stack, 2));
let v1 = stack.pop();
let v2 = stack.pop();
let v1 = try!(stack.pop());
let v2 = try!(stack.pop());
let to_push = match v1 == v2 {
true => vec![1],
false => vec![0],
@ -697,137 +626,116 @@ pub fn eval_script(
stack.push(to_push);
},
Opcode::OP_EQUALVERIFY => {
try!(require_len(stack, 2));
let equal = stack.pop() == stack.pop();
let equal = try!(stack.pop()) == try!(stack.pop());
if !equal {
return Err(Error::EqualVerify);
}
},
Opcode::OP_1ADD => {
try!(require_not_empty(stack));
let n = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4)) + 1.into();
let n = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4)) + 1.into();
stack.push(n.to_vec());
},
Opcode::OP_1SUB => {
try!(require_not_empty(stack));
let n = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4)) - 1.into();
let n = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4)) - 1.into();
stack.push(n.to_vec());
},
Opcode::OP_NEGATE => {
try!(require_not_empty(stack));
let n = -try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let n = -try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
stack.push(n.to_vec());
},
Opcode::OP_ABS => {
try!(require_not_empty(stack));
let n = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4)).abs();
let n = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4)).abs();
stack.push(n.to_vec());
},
Opcode::OP_NOT => {
try!(require_not_empty(stack));
let n = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4)).is_zero();
let n = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4)).is_zero();
let n = Num::from(n);
stack.push(n.to_vec());
},
Opcode::OP_0NOTEQUAL => {
try!(require_not_empty(stack));
let n = !try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4)).is_zero();
let n = !try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4)).is_zero();
let n = Num::from(n);
stack.push(n.to_vec());
},
Opcode::OP_ADD => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
stack.push((v1 + v2).to_vec());
},
Opcode::OP_SUB => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
stack.push((v2 - v1).to_vec());
},
Opcode::OP_BOOLAND => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(!v1.is_zero() && !v2.is_zero());
stack.push(v.to_vec());
},
Opcode::OP_BOOLOR => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(!v1.is_zero() || !v2.is_zero());
stack.push(v.to_vec());
},
Opcode::OP_NUMEQUAL => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(v1 == v2);
stack.push(v.to_vec());
},
Opcode::OP_NUMEQUALVERIFY => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
if v1 != v2 {
return Err(Error::NumEqualVerify);
}
},
Opcode::OP_NUMNOTEQUAL => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(v1 != v2);
stack.push(v.to_vec());
},
Opcode::OP_LESSTHAN => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(v1 > v2);
stack.push(v.to_vec());
},
Opcode::OP_GREATERTHAN => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(v1 < v2);
stack.push(v.to_vec());
},
Opcode::OP_LESSTHANOREQUAL => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(v1 >= v2);
stack.push(v.to_vec());
},
Opcode::OP_GREATERTHANOREQUAL => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v = Num::from(v1 <= v2);
stack.push(v.to_vec());
},
Opcode::OP_MIN => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
stack.push(cmp::min(v1, v2).to_vec());
},
Opcode::OP_MAX => {
try!(require_len(stack, 2));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
stack.push(cmp::max(v1, v2).to_vec());
},
Opcode::OP_WITHIN => {
try!(require_len(stack, 3));
let v1 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v3 = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let v1 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v2 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let v3 = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
let to_push = match v2 <= v3 && v3 <= v1 {
true => vec![1],
false => vec![0],
@ -835,37 +743,31 @@ pub fn eval_script(
stack.push(to_push);
},
Opcode::OP_RIPEMD160 => {
try!(require_not_empty(stack));
let v = ripemd160(&stack.pop().unwrap());
let v = ripemd160(&try!(stack.pop()));
stack.push(v.to_vec());
},
Opcode::OP_SHA1 => {
try!(require_not_empty(stack));
let v = sha1(&stack.pop().unwrap());
let v = sha1(&try!(stack.pop()));
stack.push(v.to_vec());
},
Opcode::OP_SHA256 => {
try!(require_not_empty(stack));
let v = sha256(&stack.pop().unwrap());
let v = sha256(&try!(stack.pop()));
stack.push(v.to_vec());
},
Opcode::OP_HASH160 => {
try!(require_not_empty(stack));
let v = dhash160(&stack.pop().unwrap());
let v = dhash160(&try!(stack.pop()));
stack.push(v.to_vec());
},
Opcode::OP_HASH256 => {
try!(require_not_empty(stack));
let v = dhash256(&stack.pop().unwrap());
let v = dhash256(&try!(stack.pop()));
stack.push(v.to_vec());
},
Opcode::OP_CODESEPARATOR => {
begincode = pc;
},
Opcode::OP_CHECKSIG | Opcode::OP_CHECKSIGVERIFY => {
try!(require_len(stack, 2));
let pubkey = stack.pop().unwrap();
let signature = stack.pop().unwrap();
let pubkey = try!(stack.pop());
let signature = try!(stack.pop());
let mut subscript = script.subscript(begincode);
if version == SignatureVersion::Base {
subscript = script.find_and_delete(&signature);
@ -890,25 +792,21 @@ pub fn eval_script(
}
},
Opcode::OP_CHECKMULTISIG | Opcode::OP_CHECKMULTISIGVERIFY => {
try!(require_not_empty(stack));
let keys_count = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let keys_count = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
if keys_count < 0.into() || keys_count > script::MAX_PUBKEYS_PER_MULTISIG.into() {
return Err(Error::PubkeyCount);
}
let keys_count: usize = keys_count.into();
try!(require_len(stack, keys_count));
let keys: Vec<_> = (0..keys_count).into_iter().map(|_| stack.pop().unwrap()).rev().collect();
let keys: Vec<_> = try!((0..keys_count).into_iter().map(|_| stack.pop()).rev().collect());
try!(require_not_empty(stack));
let sigs_count = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
let sigs_count = try!(Num::from_slice(&try!(stack.pop()), flags.verify_minimaldata, 4));
if sigs_count < 0.into() || sigs_count > keys_count.into() {
return Err(Error::SigCount);
}
let sigs_count: usize = sigs_count.into();
try!(require_len(stack, sigs_count));
let sigs: Vec<_> = (0..sigs_count).into_iter().map(|_| stack.pop().unwrap()).rev().collect();
let sigs: Vec<_> = try!((0..sigs_count).into_iter().map(|_| stack.pop()).rev().collect());
let mut subscript = script.subscript(begincode);
@ -938,8 +836,7 @@ pub fn eval_script(
success = sigs.len() - s <= keys.len() - k;
}
try!(require_not_empty(stack));
if !stack.pop().unwrap().is_empty() && flags.verify_nulldummy {
if !try!(stack.pop()).is_empty() && flags.verify_nulldummy {
return Err(Error::SignatureNullDummy);
}
@ -983,7 +880,7 @@ pub fn eval_script(
}
let success = !stack.is_empty() && {
let last = stack.last().unwrap();
let last = try!(stack.last());
cast_to_bool(last)
};
@ -996,7 +893,7 @@ mod tests {
use transaction::Transaction;
use script::{
Opcode, Script, VerificationFlags, Builder, Error, Num, TransactionInputSigner,
NoopSignatureChecker, SignatureVersion, TransactionSignatureChecker
NoopSignatureChecker, SignatureVersion, TransactionSignatureChecker, Stack
};
use super::{eval_script, verify_script, is_public_key};
@ -1013,7 +910,7 @@ mod tests {
// https://github.com/bitcoin/bitcoin/blob/d612837814020ae832499d18e6ee5eb919a87907/src/test/script_tests.cpp#L900
#[test]
fn test_push_data() {
let expected = vec![vec![0x5a]];
let expected = vec![vec![0x5a]].into();
let flags = VerificationFlags::default()
.verify_p2sh(true);
let checker = NoopSignatureChecker;
@ -1023,19 +920,19 @@ mod tests {
let pushdata2 = Script::new(vec![Opcode::OP_PUSHDATA2 as u8, 0x1, 0, 0x5a]);
let pushdata4 = Script::new(vec![Opcode::OP_PUSHDATA4 as u8, 0x1, 0, 0, 0, 0x5a]);
let mut direct_stack = vec![];
let mut pushdata1_stack= vec![];
let mut pushdata2_stack= vec![];
let mut pushdata4_stack= vec![];
let mut direct_stack = Stack::new();
let mut pushdata1_stack = Stack::new();
let mut pushdata2_stack = Stack::new();
let mut pushdata4_stack = Stack::new();
assert!(eval_script(&mut direct_stack, &direct, &flags, &checker, version).unwrap());
assert!(eval_script(&mut pushdata1_stack, &pushdata1, &flags, &checker, version).unwrap());
assert!(eval_script(&mut pushdata2_stack, &pushdata2, &flags, &checker, version).unwrap());
assert!(eval_script(&mut pushdata4_stack, &pushdata4, &flags, &checker, version).unwrap());
assert_eq!(expected, direct_stack);
assert_eq!(expected, pushdata1_stack);
assert_eq!(expected, pushdata2_stack);
assert_eq!(expected, pushdata4_stack);
assert_eq!(direct_stack, expected);
assert_eq!(pushdata1_stack, expected);
assert_eq!(pushdata2_stack, expected);
assert_eq!(pushdata4_stack, expected);
}
fn basic_test(script: &Script, expected: Result<bool, Error>, expected_stack: Vec<Vec<u8>>) {
@ -1043,10 +940,10 @@ mod tests {
.verify_p2sh(true);
let checker = NoopSignatureChecker;
let version = SignatureVersion::Base;
let mut stack = vec![];
let mut stack = Stack::new();
assert_eq!(eval_script(&mut stack, script, &flags, &checker, version), expected);
if expected.is_ok() {
assert_eq!(stack, expected_stack);
assert_eq!(stack, expected_stack.into());
}
}

View File

@ -6,6 +6,7 @@ mod num;
mod opcode;
mod script;
mod sign;
mod stack;
mod verify;
pub use self::builder::Builder;
@ -19,5 +20,6 @@ pub use self::sign::{
TransactionInputSigner, UnsignedTransactionInput,
Sighash, SighashBase, SignatureVersion
};
pub use self::stack::Stack;
pub use self::verify::{SignatureChecker, NoopSignatureChecker, TransactionSignatureChecker};

357
src/script/stack.rs Normal file
View File

@ -0,0 +1,357 @@
use std::ops;
use script::Error;
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Stack<T> {
data: Vec<T>,
}
impl<T> From<Vec<T>> for Stack<T> {
fn from(v: Vec<T>) -> Self {
Stack {
data: v
}
}
}
impl<T> ops::Deref for Stack<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T> ops::DerefMut for Stack<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T> Stack<T> {
#[inline]
pub fn new() -> Self {
Stack {
data: Vec::new()
}
}
#[inline]
pub fn require(&self, len: usize) -> Result<(), Error> {
if self.data.len() < len {
return Err(Error::InvalidStackOperation);
}
Ok(())
}
#[inline]
pub fn last(&self) -> Result<&T, Error> {
self.data.last().ok_or(Error::InvalidStackOperation)
}
#[inline]
pub fn pop(&mut self) -> Result<T, Error> {
self.data.pop().ok_or(Error::InvalidStackOperation)
}
#[inline]
pub fn push(&mut self, value: T) {
self.data.push(value)
}
#[inline]
pub fn top(&self, i: usize) -> Result<&T, Error> {
let pos = i + 1;
try!(self.require(pos));
Ok(&self.data[self.data.len() - pos])
}
#[inline]
pub fn remove(&mut self, i: usize) -> Result<T, Error> {
let pos = i + 1;
try!(self.require(pos));
let to_remove = self.data.len() - pos;
Ok(self.data.remove(to_remove))
}
pub fn drop(&mut self, i: usize) -> Result<(), Error> {
try!(self.require(i));
let mut j = i;
while j > 0 {
self.data.pop();
j -= 1;
}
Ok(())
}
pub fn dup(&mut self, i: usize) -> Result<(), Error> where T: Clone {
try!(self.require(i));
let mut j = i;
while j > 0 {
let v = self.data[self.data.len() - j].clone();
self.data.push(v);
j -= 1;
}
Ok(())
}
pub fn over(&mut self, i: usize) -> Result<(), Error> where T: Clone {
let mut j = i * 2;
try!(self.require(j));
let to_clone = j;
while j > i {
let v = self.data[self.data.len() - to_clone].clone();
self.data.push(v);
j -= 1;
}
Ok(())
}
pub fn rot(&mut self, i: usize) -> Result<(), Error> {
let mut j = i * 3;
try!(self.require(j));
let to_remove = self.data.len() - j;
let limit = j - i;
while j > limit {
let v = self.data.remove(to_remove);
self.data.push(v);
j -= 1;
}
Ok(())
}
pub fn swap(&mut self, i: usize) -> Result<(), Error> {
let mut j = i * 2;
let mut k = i;
try!(self.require(j));
let len = self.data.len();
while k > 0 {
self.data.swap(len - j, len - k);
j -= 1;
k -= 1;
}
Ok(())
}
pub fn nip(&mut self) -> Result<(), Error> {
try!(self.require(2));
let len = self.data.len();
self.data.swap_remove(len - 2);
Ok(())
}
pub fn tuck(&mut self) -> Result<(), Error> where T: Clone {
try!(self.require(2));
let len = self.data.len();
let v = self.data[len - 1].clone();
self.data.insert(len - 2, v);
Ok(())
}
}
#[cfg(test)]
mod tests {
use script::Error;
use super::Stack;
#[test]
fn test_stack_require() {
let stack: Stack<u8> = vec![].into();
assert_eq!(stack.require(0), Ok(()));
assert_eq!(stack.require(1), Err(Error::InvalidStackOperation));
let stack: Stack<u8> = vec![0].into();
assert_eq!(stack.require(0), Ok(()));
assert_eq!(stack.require(1), Ok(()));
assert_eq!(stack.require(2), Err(Error::InvalidStackOperation));
let stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.require(0), Ok(()));
assert_eq!(stack.require(1), Ok(()));
assert_eq!(stack.require(2), Ok(()));
assert_eq!(stack.require(3), Err(Error::InvalidStackOperation));
}
#[test]
fn test_stack_last() {
let stack: Stack<u8> = vec![].into();
assert_eq!(stack.last(), Err(Error::InvalidStackOperation));
let stack: Stack<u8> = vec![0].into();
assert_eq!(stack.last(), Ok(&0));
let stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.last(), Ok(&5));
}
#[test]
fn test_stack_pop() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.pop(), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![].into());
let mut stack: Stack<u8> = vec![0].into();
assert_eq!(stack.pop(), Ok(0));
assert_eq!(stack, vec![].into());
let mut stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.pop(), Ok(5));
assert_eq!(stack.pop(), Ok(0));
assert_eq!(stack, vec![].into());
}
#[test]
fn test_stack_push() {
let mut stack: Stack<u8> = vec![].into();
stack.push(0);
assert_eq!(stack, vec![0].into());
stack.push(5);
assert_eq!(stack, vec![0, 5].into());
}
#[test]
fn test_stack_top() {
let stack: Stack<u8> = vec![].into();
assert_eq!(stack.top(0), Err(Error::InvalidStackOperation));
let stack: Stack<u8> = vec![0].into();
assert_eq!(stack.top(0), Ok(&0));
assert_eq!(stack.top(1), Err(Error::InvalidStackOperation));
let stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.top(0), Ok(&5));
assert_eq!(stack.top(1), Ok(&0));
assert_eq!(stack.top(2), Err(Error::InvalidStackOperation));
}
#[test]
fn test_stack_remove() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.remove(0), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![].into());
let mut stack: Stack<u8> = vec![0].into();
assert_eq!(stack.remove(0), Ok(0));
assert_eq!(stack.remove(0), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![].into());
let mut stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.remove(1), Ok(0));
assert_eq!(stack, vec![5].into());
assert_eq!(stack.remove(0), Ok(5));
assert_eq!(stack, vec![].into());
}
#[test]
fn test_stack_drop() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.drop(0), Ok(()));
let mut stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.drop(0), Ok(()));
assert_eq!(stack, vec![0, 5].into());
assert_eq!(stack.drop(3), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![0, 5].into());
assert_eq!(stack.drop(1), Ok(()));
assert_eq!(stack, vec![0].into());
let mut stack: Stack<u8> = vec![3, 5, 0].into();
assert_eq!(stack.drop(3), Ok(()));
assert_eq!(stack, vec![].into());
}
#[test]
fn test_stack_dup() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.dup(0), Ok(()));
assert_eq!(stack.dup(1), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![].into());
let mut stack: Stack<u8> = vec![0].into();
assert_eq!(stack.dup(0), Ok(()));
assert_eq!(stack.dup(2), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![0].into());
assert_eq!(stack.dup(1), Ok(()));
assert_eq!(stack, vec![0, 0].into());
assert_eq!(stack.dup(2), Ok(()));
assert_eq!(stack, vec![0, 0, 0, 0].into());
let mut stack: Stack<u8> = vec![0, 1].into();
assert_eq!(stack.dup(1), Ok(()));
assert_eq!(stack.dup(2), Ok(()));
assert_eq!(stack, vec![0, 1, 1, 1, 1].into());
}
#[test]
fn test_stack_over() {
let mut stack: Stack<u8> = vec![0].into();
assert_eq!(stack.over(0), Ok(()));
assert_eq!(stack.over(1), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.over(2), Err(Error::InvalidStackOperation));
assert_eq!(stack.over(1), Ok(()));
assert_eq!(stack, vec![0, 5, 0].into());
assert_eq!(stack.over(1), Ok(()));
assert_eq!(stack, vec![0, 5, 0, 5].into());
let mut stack: Stack<u8> = vec![1, 2, 3, 4].into();
assert_eq!(stack.over(2), Ok(()));
assert_eq!(stack, vec![1, 2, 3, 4, 1, 2].into());
}
#[test]
fn test_stack_rot() {
let mut stack: Stack<u8> = vec![0, 5].into();
assert_eq!(stack.rot(0), Ok(()));
assert_eq!(stack.rot(1), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![0, 5].into());
let mut stack: Stack<u8> = vec![0, 1, 2].into();
assert_eq!(stack.rot(2), Err(Error::InvalidStackOperation));
assert_eq!(stack.rot(1), Ok(()));
assert_eq!(stack, vec![1, 2, 0].into());
let mut stack: Stack<u8> = vec![0, 1, 2, 3].into();
assert_eq!(stack.rot(1), Ok(()));
assert_eq!(stack, vec![0, 2, 3, 1].into());
let mut stack: Stack<u8> = vec![0, 1, 2, 3, 4, 5].into();
assert_eq!(stack.rot(3), Err(Error::InvalidStackOperation));
assert_eq!(stack.rot(2), Ok(()));
assert_eq!(stack, vec![2, 3, 4, 5, 0, 1].into());
}
#[test]
fn test_stack_swap() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.swap(0), Ok(()));
assert_eq!(stack, vec![].into());
assert_eq!(stack.swap(1), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0, 1, 2, 3].into();
assert_eq!(stack.swap(0), Ok(()));
assert_eq!(stack, vec![0, 1, 2, 3].into());
assert_eq!(stack.swap(1), Ok(()));
assert_eq!(stack, vec![0, 1, 3, 2].into());
assert_eq!(stack.swap(2), Ok(()));
assert_eq!(stack, vec![3, 2, 0, 1].into());
assert_eq!(stack.swap(3), Err(Error::InvalidStackOperation));
assert_eq!(stack, vec![3, 2, 0, 1].into());
}
#[test]
fn test_stack_nip() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.nip(), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0].into();
assert_eq!(stack.nip(), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0, 1].into();
assert_eq!(stack.nip(), Ok(()));
assert_eq!(stack, vec![1].into());
assert_eq!(stack.nip(), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0, 1, 2, 3].into();
assert_eq!(stack.nip(), Ok(()));
assert_eq!(stack, vec![0, 1, 3].into());
assert_eq!(stack.nip(), Ok(()));
assert_eq!(stack, vec![0, 3].into());
}
#[test]
fn test_stack_tuck() {
let mut stack: Stack<u8> = vec![].into();
assert_eq!(stack.tuck(), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0].into();
assert_eq!(stack.tuck(), Err(Error::InvalidStackOperation));
let mut stack: Stack<u8> = vec![0, 1].into();
assert_eq!(stack.tuck(), Ok(()));
assert_eq!(stack, vec![1, 0, 1].into());
assert_eq!(stack.tuck(), Ok(()));
assert_eq!(stack, vec![1, 1, 0, 1].into());
let mut stack: Stack<u8> = vec![0, 1, 2, 3].into();
assert_eq!(stack.tuck(), Ok(()));
assert_eq!(stack, vec![0, 1, 3, 2, 3].into());
assert_eq!(stack.tuck(), Ok(()));
assert_eq!(stack, vec![0, 1, 3, 3, 2, 3].into());
}
}