script interpreter in progress
This commit is contained in:
parent
c3856e1590
commit
e47865e293
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue