script interpreter in progress

This commit is contained in:
debris 2016-09-08 12:20:18 +02:00
parent c3856e1590
commit e47865e293
5 changed files with 177 additions and 2 deletions

22
src/script/builder.rs Normal file
View File

@ -0,0 +1,22 @@
use script::{Opcode, Script};
pub struct Builder {
data: Vec<u8>,
}
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)
}
}

View File

@ -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),
}
}
}

View File

@ -266,7 +266,7 @@ pub fn eval_script(
stack: &mut Vec<Vec<u8>>,
script: &Script,
flags: &VerificationFlags,
_checker: &SignatureChecker,
checker: &SignatureChecker,
_version: SignatureVersion
) -> Result<bool, Error> {
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)
}

View File

@ -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;

View File

@ -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<i64> for Num {
}
impl Num {
pub fn from_slice(data: &[u8], require_minimal: bool, max_size: usize) -> Result<Self, Error> {
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<u8> {
if self.value == 0 {
return vec![];
@ -67,4 +105,8 @@ impl Num {
result
}
pub fn is_negative(&self) -> bool {
self.value < 0
}
}