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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
Unknown,
|
||||||
|
ReturnOpcode,
|
||||||
|
|
||||||
// Max sizes.
|
// Max sizes.
|
||||||
ScriptSize,
|
ScriptSize,
|
||||||
PushSize,
|
PushSize,
|
||||||
|
NumberOverflow,
|
||||||
|
NumberNotMinimallyEncoded,
|
||||||
|
|
||||||
|
// Failed verify operations
|
||||||
|
Verify,
|
||||||
|
EqualVerify,
|
||||||
|
|
||||||
// Logical/Format/Canonical errors.
|
// Logical/Format/Canonical errors.
|
||||||
BadOpcode,
|
BadOpcode,
|
||||||
DisabledOpcode(Opcode),
|
DisabledOpcode(Opcode),
|
||||||
|
InvalidStackOperation,
|
||||||
|
|
||||||
|
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
|
||||||
|
NegativeLocktime,
|
||||||
|
UnsatisfiedLocktime,
|
||||||
|
|
||||||
// BIP62
|
// BIP62
|
||||||
SignatureHashtype,
|
SignatureHashtype,
|
||||||
|
@ -17,18 +31,34 @@ pub enum Error {
|
||||||
Minimaldata,
|
Minimaldata,
|
||||||
SignatureHighS,
|
SignatureHighS,
|
||||||
PubkeyType,
|
PubkeyType,
|
||||||
|
|
||||||
|
// Softfork safeness
|
||||||
|
DiscourageUpgradableNops,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
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.
|
// Max sizes.
|
||||||
Error::ScriptSize => "Script is too long".fmt(f),
|
Error::ScriptSize => "Script is too long".fmt(f),
|
||||||
Error::PushSize => "Pushing too many bytes".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.
|
// Logical/Format/Canonical errors.
|
||||||
Error::BadOpcode => "Bad Opcode".fmt(f),
|
Error::BadOpcode => "Bad Opcode".fmt(f),
|
||||||
Error::DisabledOpcode(ref opcode) => writeln!(f, "Disabled Opcode: {:?}", opcode),
|
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
|
// BIP62
|
||||||
Error::SignatureHashtype => "Invalid Signature Hashtype".fmt(f),
|
Error::SignatureHashtype => "Invalid Signature Hashtype".fmt(f),
|
||||||
|
@ -36,6 +66,9 @@ impl fmt::Display for Error {
|
||||||
Error::Minimaldata => "Check minimaldata failed".fmt(f),
|
Error::Minimaldata => "Check minimaldata failed".fmt(f),
|
||||||
Error::SignatureHighS => "Invalid High S in Signature".fmt(f),
|
Error::SignatureHighS => "Invalid High S in Signature".fmt(f),
|
||||||
Error::PubkeyType => "Invalid Pubkey".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>>,
|
stack: &mut Vec<Vec<u8>>,
|
||||||
script: &Script,
|
script: &Script,
|
||||||
flags: &VerificationFlags,
|
flags: &VerificationFlags,
|
||||||
_checker: &SignatureChecker,
|
checker: &SignatureChecker,
|
||||||
_version: SignatureVersion
|
_version: SignatureVersion
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
if script.len() > script::MAX_SCRIPT_SIZE {
|
if script.len() > script::MAX_SCRIPT_SIZE {
|
||||||
|
@ -285,11 +285,87 @@ pub fn eval_script(
|
||||||
}
|
}
|
||||||
stack.push(bytes.to_vec());
|
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)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod builder;
|
||||||
mod error;
|
mod error;
|
||||||
mod flags;
|
mod flags;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
|
@ -7,6 +8,7 @@ mod script;
|
||||||
mod sign;
|
mod sign;
|
||||||
mod standard;
|
mod standard;
|
||||||
|
|
||||||
|
pub use self::builder::Builder;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::flags::VerificationFlags;
|
pub use self::flags::VerificationFlags;
|
||||||
pub use self::interpreter::eval_script;
|
pub use self::interpreter::eval_script;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! Script numeric.
|
//! Script numeric.
|
||||||
|
use script::Error;
|
||||||
|
|
||||||
/// Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers.
|
/// 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],
|
/// 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 {
|
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> {
|
pub fn to_vec(&self) -> Vec<u8> {
|
||||||
if self.value == 0 {
|
if self.value == 0 {
|
||||||
return vec![];
|
return vec![];
|
||||||
|
@ -67,4 +105,8 @@ impl Num {
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_negative(&self) -> bool {
|
||||||
|
self.value < 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue