From e47865e2938e08a4db04330680fc948b571b35b2 Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 8 Sep 2016 12:20:18 +0200 Subject: [PATCH] script interpreter in progress --- src/script/builder.rs | 22 +++++++++++ src/script/error.rs | 33 ++++++++++++++++ src/script/interpreter.rs | 80 ++++++++++++++++++++++++++++++++++++++- src/script/mod.rs | 2 + src/script/num.rs | 42 ++++++++++++++++++++ 5 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 src/script/builder.rs diff --git a/src/script/builder.rs b/src/script/builder.rs new file mode 100644 index 00000000..58cb1265 --- /dev/null +++ b/src/script/builder.rs @@ -0,0 +1,22 @@ +use script::{Opcode, Script}; + +pub struct Builder { + data: Vec, +} + +impl Builder { + pub fn new() -> Self { + Builder { + data: Vec::new(), + } + } + + pub fn push_opcode(&mut self, opcode: Opcode) -> &mut Self { + self.data.push(opcode as u8); + self + } + + pub fn into_script(self) -> Script { + Script::new(self.data) + } +} diff --git a/src/script/error.rs b/src/script/error.rs index 2d8cc7ac..4be52459 100644 --- a/src/script/error.rs +++ b/src/script/error.rs @@ -3,13 +3,27 @@ use script::Opcode; #[derive(Debug, PartialEq)] pub enum Error { + Unknown, + ReturnOpcode, + // Max sizes. ScriptSize, PushSize, + NumberOverflow, + NumberNotMinimallyEncoded, + + // Failed verify operations + Verify, + EqualVerify, // Logical/Format/Canonical errors. BadOpcode, DisabledOpcode(Opcode), + InvalidStackOperation, + + // CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY + NegativeLocktime, + UnsatisfiedLocktime, // BIP62 SignatureHashtype, @@ -17,18 +31,34 @@ pub enum Error { Minimaldata, SignatureHighS, PubkeyType, + + // Softfork safeness + DiscourageUpgradableNops, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { + Error::Unknown => "Unknown error".fmt(f), + Error::ReturnOpcode => "Used return opcode".fmt(f), + + // Failed verify operations + Error::Verify => "Failed verify operation".fmt(f), + Error::EqualVerify => "Failed equal verify operation".fmt(f), + // Max sizes. Error::ScriptSize => "Script is too long".fmt(f), Error::PushSize => "Pushing too many bytes".fmt(f), + Error::NumberOverflow => "Number overflow".fmt(f), + Error::NumberNotMinimallyEncoded => "Number not minimally encoded".fmt(f), // Logical/Format/Canonical errors. Error::BadOpcode => "Bad Opcode".fmt(f), Error::DisabledOpcode(ref opcode) => writeln!(f, "Disabled Opcode: {:?}", opcode), + Error::InvalidStackOperation => "Invalid stack operation".fmt(f), + + Error::NegativeLocktime => "Negative locktime".fmt(f), + Error::UnsatisfiedLocktime => "UnsatisfiedLocktime".fmt(f), // BIP62 Error::SignatureHashtype => "Invalid Signature Hashtype".fmt(f), @@ -36,6 +66,9 @@ impl fmt::Display for Error { Error::Minimaldata => "Check minimaldata failed".fmt(f), Error::SignatureHighS => "Invalid High S in Signature".fmt(f), Error::PubkeyType => "Invalid Pubkey".fmt(f), + + // Softfork safeness + Error::DiscourageUpgradableNops => "Discourage Upgradable Nops".fmt(f), } } } diff --git a/src/script/interpreter.rs b/src/script/interpreter.rs index a6b238c8..a96ebacb 100644 --- a/src/script/interpreter.rs +++ b/src/script/interpreter.rs @@ -266,7 +266,7 @@ pub fn eval_script( stack: &mut Vec>, script: &Script, flags: &VerificationFlags, - _checker: &SignatureChecker, + checker: &SignatureChecker, _version: SignatureVersion ) -> Result { if script.len() > script::MAX_SCRIPT_SIZE { @@ -285,11 +285,87 @@ pub fn eval_script( } stack.push(bytes.to_vec()); }, - Instruction::Normal(_opcode) => { + Instruction::Normal(opcode) => match opcode { + Opcode::OP_NOP => break, + Opcode::OP_CHECKLOCKTIMEVERIFY => { + if !flags.verify_clocktimeverify { + if flags.verify_discourage_upgradable_nops { + return Err(Error::DiscourageUpgradableNops); + } + } + + if stack.is_empty() { + return Err(Error::InvalidStackOperation); + } + + // 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 + // range. This limitation is implemented by CScriptNum's + // default 4-byte limit. + // + // If we kept to that limit we'd have a year 2038 problem, + // even though the nLockTime field in transactions + // themselves is uint32 which only becomes meaningless + // after the year 2106. + // + // 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)); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if lock_time.is_negative() { + return Err(Error::NegativeLocktime); + } + + if !checker.check_lock_time(lock_time) { + return Err(Error::UnsatisfiedLocktime); + } + }, + Opcode::OP_NOP1 | Opcode::OP_NOP4 | Opcode::OP_NOP5 | Opcode::OP_NOP6 | + Opcode::OP_NOP7 | Opcode::OP_NOP8 | Opcode::OP_NOP9 | Opcode::OP_NOP10 => { + if flags.verify_discourage_upgradable_nops { + return Err(Error::DiscourageUpgradableNops); + } + }, + Opcode::OP_RETURN => { + return Err(Error::ReturnOpcode); + }, + Opcode::OP_EQUAL => { + if stack.len() < 2 { + return Err(Error::InvalidStackOperation); + } + + let equal = stack.pop() == stack.pop(); + let to_push = match equal { + true => vec![1], + false => vec![0], + }; + stack.push(to_push); + }, + Opcode::OP_EQUALVERIFY => { + if stack.len() < 2 { + return Err(Error::InvalidStackOperation); + } + + let equal = stack.pop() == stack.pop(); + if !equal { + return Err(Error::EqualVerify); + } + }, + _ => (), }, } } + let success = !stack.is_empty() && { + let last = stack.last().unwrap(); + last != &vec![0; last.len()] + }; + Ok(true) } diff --git a/src/script/mod.rs b/src/script/mod.rs index 65f63650..cceda7f2 100644 --- a/src/script/mod.rs +++ b/src/script/mod.rs @@ -1,3 +1,4 @@ +mod builder; mod error; mod flags; mod interpreter; @@ -7,6 +8,7 @@ mod script; mod sign; mod standard; +pub use self::builder::Builder; pub use self::error::Error; pub use self::flags::VerificationFlags; pub use self::interpreter::eval_script; diff --git a/src/script/num.rs b/src/script/num.rs index 7826101a..df777f06 100644 --- a/src/script/num.rs +++ b/src/script/num.rs @@ -1,4 +1,5 @@ //! Script numeric. +use script::Error; /// Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers. /// The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1], @@ -28,6 +29,43 @@ impl From for Num { } impl Num { + pub fn from_slice(data: &[u8], require_minimal: bool, max_size: usize) -> Result { + if data.len() > max_size { + return Err(Error::NumberOverflow); + } + + if data.is_empty() { + return Ok(0u8.into()); + } + + if require_minimal { + // Check that the number is encoded with the minimum possible + // number of bytes. + // + // If the most-significant-byte - excluding the sign bit - is zero + // then we're not minimal. Note how this test also rejects the + // negative-zero encoding, 0x80. + if (data.last().unwrap() & 0x7f) == 0 { + if data.len() <= 1 || (data[data.len() - 2] & 0x80) == 0 { + return Err(Error::NumberNotMinimallyEncoded) + } + } + } + + let mut result = 0i64; + for i in 0..data.len() { + result |= (data[i] as i64) << (8 * i); + } + + // If the input vector's most significant byte is 0x80, remove it from + // the result's msb and return a negative. + if data.last().unwrap() & 0x80 != 0 { + Ok((-(result & !(0x80i64 << (8 * (data.len() - 1))))).into()) + } else { + Ok(result.into()) + } + } + pub fn to_vec(&self) -> Vec { if self.value == 0 { return vec![]; @@ -67,4 +105,8 @@ impl Num { result } + + pub fn is_negative(&self) -> bool { + self.value < 0 + } }