From bf09ab27547b0633a444fcc4679e08a6047b3d25 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 10 Aug 2014 19:35:58 -0700 Subject: [PATCH] Fix script bugs (can now fully validate testnet up to multisig) --- src/blockdata/opcodes.rs | 2 +- src/blockdata/script.rs | 147 +++++++++++++++++++++++++++++++---- src/blockdata/transaction.rs | 9 +++ src/blockdata/utxoset.rs | 76 ++++++++++++++++-- 4 files changed, 209 insertions(+), 25 deletions(-) diff --git a/src/blockdata/opcodes.rs b/src/blockdata/opcodes.rs index 81c5e751..d8c9980d 100644 --- a/src/blockdata/opcodes.rs +++ b/src/blockdata/opcodes.rs @@ -592,7 +592,7 @@ pub mod all { super::PushNum(-1) // 16 opcodes } else if OP_PUSHNUM_1 as u8 <= *self as u8 && *self as u8 <= OP_PUSHNUM_16 as u8 { - super::PushNum(1 + OP_PUSHNUM_1 as int - *self as int) + super::PushNum(1 + *self as int - OP_PUSHNUM_1 as int) // 76 opcodes } else if *self as u8 <= OP_PUSHBYTES_75 as u8 { super::PushBytes(*self as uint) diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 53aa5c81..196e878b 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -25,6 +25,7 @@ //! use std::char::from_digit; +use std::default::Default; use serialize::json; use crypto::digest::Digest; @@ -119,6 +120,8 @@ impl SignatureHashType { /// Helper to encode an integer in script format fn build_scriptint(n: i64) -> Vec { + if n == 0 { return vec![] } + let neg = n < 0; let mut abs = if neg { -n } else { n } as uint; @@ -155,7 +158,7 @@ fn build_scriptint(n: i64) -> Vec { /// don't fit in 64 bits (for efficiency on modern processors) so we /// simply say, anything in excess of 32 bits is no longer a number. /// This is basically a ranged type implementation. -fn read_scriptint(v: &[u8]) -> Result { +pub fn read_scriptint(v: &[u8]) -> Result { let len = v.len(); if len == 0 { return Ok(0); } if len > 4 { return Err(NumericOverflow); } @@ -172,17 +175,21 @@ fn read_scriptint(v: &[u8]) -> Result { /// This is like "read_scriptint then map 0 to false and everything /// else as true", except that the overflow rules don't apply. #[inline] -fn read_scriptbool(v: &[u8]) -> bool { +pub fn read_scriptbool(v: &[u8]) -> bool { !v.iter().all(|&w| w == 0) } -/// Helper to read a script uint -fn read_uint<'a, I:Iterator<(uint, &'a u8)>>(mut iter: I, size: uint) +/// Read a script-encoded unsigned integer +pub fn read_uint<'a, I:Iterator<(uint, &'a u8)>>(mut iter: I, size: uint) -> Result { + let mut sh = 0; let mut ret = 0; for _ in range(0, size) { match iter.next() { - Some((_, &n)) => { ret = (ret << 8) + n as uint; } + Some((_, &n)) => { + ret += n as uint << sh; + sh += 8; + } None => { return Err(EarlyEndOfScript); } } } @@ -339,8 +346,8 @@ impl Script { for (index, byte) in iter { let executing = exec_stack.iter().all(|e| *e); -println!("{}, {} len {} stack {}", index, allops::Opcode::from_u8(*byte), stack.len(), stack); // The definitions of all these categories are in opcodes.rs +//println!("read {} as {}", allops::Opcode::from_u8(*byte), allops::Opcode::from_u8(*byte).classify()); match (executing, allops::Opcode::from_u8(*byte).classify()) { // Illegal operations mean failure regardless of execution state (_, opcodes::IllegalOp) => return Err(IllegalOpcode), @@ -476,7 +483,6 @@ println!("{}, {} len {} stack {}", index, allops::Opcode::from_u8(*byte), stac if stack.len() < 2 { return Err(PopEmptyStack); } let a = stack.pop().unwrap(); let b = stack.pop().unwrap(); -println!("comparing {} to {} , eq {}", a, b, a == b) stack.push(build_scriptint(if a == b { 1 } else { 0 })); if op == opcodes::OP_EQUALVERIFY { op_verify!(stack); } } @@ -521,7 +527,6 @@ println!("comparing {} to {} , eq {}", a, b, a == b) let pubkey = PublicKey::from_slice(stack.pop().unwrap().as_slice()); let signature = stack.pop().unwrap(); -println!("pubkey {} sig {}", pubkey, signature); if pubkey.is_err() { stack.push(build_scriptint(0)); } else if signature.len() == 0 { @@ -575,7 +580,11 @@ println!("pubkey {} sig {}", pubkey, signature); SigHashNone => { tx_copy.output = vec![]; } SigHashSingle => { if input_index < tx_copy.output.len() { - let new_outs = tx_copy.output.move_iter().take(input_index + 1).collect(); + let mut new_outs = Vec::with_capacity(input_index + 1); + for _ in range(0, input_index) { + new_outs.push(Default::default()) + } + new_outs.push(tx_copy.output.swap_remove(input_index).unwrap()); tx_copy.output = new_outs; } else { sighash_single_bug = true; @@ -647,6 +656,7 @@ impl, E> ConsensusDecodable for Script { #[cfg(test)] mod test { use std::io::IoResult; + use serialize::hex::FromHex; use super::{Script, build_scriptint, read_scriptint, read_scriptbool}; use super::{NoTransaction, PopEmptyStack, VerifyFailed}; @@ -654,7 +664,6 @@ mod test { use network::serialize::{deserialize, serialize}; use blockdata::opcodes; use blockdata::transaction::Transaction; - use util::misc::hex_bytes; use util::thinvec::ThinVec; #[test] @@ -687,7 +696,7 @@ mod test { #[test] fn script_serialize() { - let hex_script = hex_bytes("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52").unwrap(); + let hex_script = "6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52".from_hex().unwrap(); let script: IoResult