interpreter in progress

This commit is contained in:
debris 2016-09-14 17:48:00 +02:00
parent 8bb9897d72
commit cbd3a130a4
5 changed files with 124 additions and 35 deletions

View File

@ -9,8 +9,11 @@ pub enum Error {
// Max sizes.
ScriptSize,
PushSize,
StackSize,
NumberOverflow,
NumberNotMinimallyEncoded,
SigCount,
PubkeyCount,
// Failed verify operations
Verify,
@ -34,6 +37,7 @@ pub enum Error {
SignatureDer,
Minimaldata,
SignatureHighS,
SignatureNullDummy,
PubkeyType,
// Softfork safeness
@ -51,10 +55,13 @@ impl fmt::Display for Error {
Error::EqualVerify => "Failed equal verify operation".fmt(f),
Error::CheckSigVerify => "Failed signature check".fmt(f),
Error::NumEqualVerify => "Failed num equal verify operation".fmt(f),
Error::SigCount => "Maximum number of signature exceeded".fmt(f),
Error::PubkeyCount => "Maximum number of pubkeys per multisig exceeded".fmt(f),
// Max sizes.
Error::ScriptSize => "Script is too long".fmt(f),
Error::PushSize => "Pushing too many bytes".fmt(f),
Error::StackSize => "Stack is too big".fmt(f),
Error::NumberOverflow => "Number overflow".fmt(f),
Error::NumberNotMinimallyEncoded => "Number not minimally encoded".fmt(f),
@ -74,6 +81,7 @@ impl fmt::Display for Error {
Error::SignatureDer => "Invalid Signature".fmt(f),
Error::Minimaldata => "Check minimaldata failed".fmt(f),
Error::SignatureHighS => "Invalid High S in Signature".fmt(f),
Error::SignatureNullDummy => "Multisig extra stack element is not empty".fmt(f),
Error::PubkeyType => "Invalid Pubkey".fmt(f),
// Softfork safeness

View File

@ -1,6 +1,6 @@
use std::cmp;
use keys::{Signature, Public};
use transaction::{SEQUENCE_LOCKTIME_DISABLE_FLAG};
use transaction::SEQUENCE_LOCKTIME_DISABLE_FLAG;
use crypto::{sha1, sha256, dhash160, dhash256, ripemd160};
use script::{
script, Script, Num, VerificationFlags, Opcode, Error, read_usize,
@ -10,7 +10,7 @@ use script::{
/// Helper function.
fn check_signature(
checker: &SignatureChecker,
script_sig: Vec<u8>,
mut script_sig: Vec<u8>,
public: Vec<u8>,
script_code: &Script,
version: SignatureVersion
@ -24,8 +24,8 @@ fn check_signature(
return false;
}
let hash_type = *script_sig.last().unwrap() as u32;
let signature = script_sig[..script_sig.len() - 1].into();
let hash_type = script_sig.pop().unwrap() as u32;
let signature = script_sig.into();
checker.check_signature(&signature, &public, script_code, hash_type, version)
}
@ -261,8 +261,20 @@ pub fn eval_script(
let mut altstack = Vec::<Vec<u8>>::new();
while pc < script.len() {
let fexec = exec_stack.iter().find(|&x| !x).is_some();
let executing = exec_stack.iter().all(|x| *x);
let opcode = try!(script.get_opcode(pc));
// TODO: verify push opcodes minimal data and size
// even if they are not executed
// TODO: verify maximum number of opcodes per script
if !(executing || (Opcode::OP_IF <= opcode && opcode <= Opcode::OP_ENDIF)) {
// TODO: instead of moving pc counter, move to next opcode
pc += 1;
continue;
}
match opcode {
Opcode::OP_PUSHDATA1 |
Opcode::OP_PUSHDATA2 |
@ -365,6 +377,7 @@ pub fn eval_script(
stack.push(bytes.to_vec());
pc += opcode as usize;
},
Opcode::OP_1NEGATE |
Opcode::OP_1 |
Opcode::OP_2 |
Opcode::OP_3 |
@ -448,15 +461,21 @@ pub fn eval_script(
}
}
},
Opcode::OP_NOP1 | Opcode::OP_NOP4 | Opcode::OP_NOP5 | Opcode::OP_NOP6 |
Opcode::OP_NOP7 | Opcode::OP_NOP8 | Opcode::OP_NOP9 | Opcode::OP_NOP10 => {
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_IF | Opcode::OP_NOTIF => {
let mut exec_value = false;
if fexec {
if executing {
try!(require_not_empty(stack).map_err(|_| Error::UnbalancedConditional));
exec_value = cast_to_bool(&stack.pop().unwrap());
if opcode == Opcode::OP_NOTIF {
@ -810,11 +829,92 @@ pub fn eval_script(
_ => {},
}
},
Opcode::OP_CHECKMULTISIG | Opcode::OP_CHECKMULTISIGVERIFY => {
try!(require_not_empty(stack));
let keys_count = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
if keys_count < 0.into() || keys_count > script::MAX_PUBKEYS_PER_MULTISIG.into() {
return Err(Error::PubkeyCount);
}
try!(require_len(stack, usize::from(keys_count) + 1));
let sigs_count = try!(Num::from_slice(&stack.pop().unwrap(), flags.verify_minimaldata, 4));
if sigs_count < 0.into() || sigs_count > keys_count {
return Err(Error::SigCount);
}
let sigs_count: usize = sigs_count.into();
let keys_count: usize = keys_count.into();
try!(require_len(stack, keys_count + sigs_count));
let mut subscript = script.subscript(begincode);
let keys: Vec<_> = (0..keys_count).into_iter().map(|_| stack.pop().unwrap()).rev().collect();
let sigs: Vec<_> = (0..sigs_count).into_iter().map(|_| stack.pop().unwrap()).rev().collect();
if version == SignatureVersion::Base {
for signature in &sigs {
subscript = subscript.find_and_delete(signature);
}
}
let mut success = true;
let mut k = 0;
let mut s = 0;
while s < sigs.len() && success {
// TODO: remove redundant copying
let key = keys[k].clone();
let sig = sigs[s].clone();
try!(check_signature_encoding(&sig, flags));
try!(check_pubkey_encoding(&key, flags));
let ok = check_signature(checker, sig, key, &subscript, version);
if ok {
s += 1;
}
k += 1;
success = sigs.len() - s <= keys.len() - k;
}
try!(require_not_empty(stack));
if !stack.pop().unwrap().is_empty() && flags.verify_nulldummy {
return Err(Error::SignatureNullDummy);
}
match opcode {
Opcode::OP_CHECKMULTISIG => {
let to_push = match success {
true => vec![1],
false => vec![0],
};
stack.push(to_push);
},
Opcode::OP_CHECKMULTISIGVERIFY if !success => {
return Err(Error::CheckSigVerify);
},
_ => {},
}
},
Opcode::OP_RESERVED |
Opcode::OP_VER |
Opcode::OP_RESERVED1 |
Opcode::OP_RESERVED2 => {},
Opcode::OP_VERIF |
Opcode::OP_VERNOTIF => {},
}
if stack.len() + altstack.len() > 1000 {
return Err(Error::StackSize);
}
pc += 1;
}
if !exec_stack.is_empty() {
return Err(Error::UnbalancedConditional);
}
let success = !stack.is_empty() && {
let last = stack.last().unwrap();
cast_to_bool(last)

View File

@ -63,6 +63,12 @@ impl From<Num> for i64 {
}
}
impl From<Num> for usize {
fn from(n: Num) -> Self {
n.value as usize
}
}
impl Num {
pub fn from_slice(data: &[u8], require_minimal: bool, max_size: usize) -> Result<Self, Error> {
if data.len() > max_size {

View File

@ -212,14 +212,6 @@ pub enum Opcode {
OP_NOP8 = 0xb7,
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
// template matching params
OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb,
OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe,
OP_INVALIDOPCODE = 0xff,
}
impl fmt::Display for Opcode {
@ -437,15 +429,6 @@ impl Opcode {
0xb7 => Some(OP_NOP8),
0xb8 => Some(OP_NOP9),
0xb9 => Some(OP_NOP10),
// template matching params
0xfa => Some(OP_SMALLINTEGER),
0xfb => Some(OP_PUBKEYS),
0xfd => Some(OP_PUBKEYHASH),
0xfe => Some(OP_PUBKEY),
0xff => Some(OP_INVALIDOPCODE),
_ => None,
}
}
@ -681,13 +664,5 @@ mod tests {
assert_eq!(Opcode::OP_NOP8, Opcode::from_u8(Opcode::OP_NOP8 as u8).unwrap());
assert_eq!(Opcode::OP_NOP9, Opcode::from_u8(Opcode::OP_NOP9 as u8).unwrap());
assert_eq!(Opcode::OP_NOP10, Opcode::from_u8(Opcode::OP_NOP10 as u8).unwrap());
// template matching params
assert_eq!(Opcode::OP_SMALLINTEGER, Opcode::from_u8(Opcode::OP_SMALLINTEGER as u8).unwrap());
assert_eq!(Opcode::OP_PUBKEYS, Opcode::from_u8(Opcode::OP_PUBKEYS as u8).unwrap());
assert_eq!(Opcode::OP_PUBKEYHASH, Opcode::from_u8(Opcode::OP_PUBKEYHASH as u8).unwrap());
assert_eq!(Opcode::OP_PUBKEY, Opcode::from_u8(Opcode::OP_PUBKEY as u8).unwrap());
assert_eq!(Opcode::OP_INVALIDOPCODE, Opcode::from_u8(Opcode::OP_INVALIDOPCODE as u8).unwrap());
}
}

View File

@ -11,7 +11,7 @@ const MAX_SCRIPT_ELEMENT_SIZE: usize = 520;
const _MAX_OPS_PER_SCRIPT: u32 = 201;
/// Maximum number of public keys per multisig
const _MAX_PUBKEYS_PER_MULTISIG: u32 = 20;
pub const MAX_PUBKEYS_PER_MULTISIG: usize = 20;
/// Maximum script length in bytes
pub const MAX_SCRIPT_SIZE: usize = 10000;