Merge pull request #490 from paritytech/div_mod_ops_impl

Bitcoin Cash: OP_DIV and OP_MOD implementation
This commit is contained in:
Marek Kotewicz 2018-03-04 13:23:59 +01:00 committed by GitHub
commit be6d461bde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 556 additions and 4 deletions

View File

@ -28,6 +28,10 @@ impl Bytes {
pub fn append(&mut self, other: &mut Bytes) {
self.0.append(&mut other.0);
}
pub fn split_off(&mut self, at: usize) -> Bytes {
Bytes(self.0.split_off(at))
}
}
impl HeapSizeOf for Bytes {

View File

@ -32,6 +32,9 @@ pub enum Error {
InvalidStackOperation,
InvalidAltstackOperation,
UnbalancedConditional,
InvalidSplitRange,
InvalidBitwiseOperation,
DivisionByZero,
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
NegativeLocktime,
@ -92,6 +95,9 @@ impl fmt::Display for Error {
Error::InvalidStackOperation => "Invalid stack operation".fmt(f),
Error::InvalidAltstackOperation => "Invalid altstack operation".fmt(f),
Error::UnbalancedConditional => "Unbalanced conditional".fmt(f),
Error::InvalidSplitRange => "Invalid OP_SPLIT range".fmt(f),
Error::InvalidBitwiseOperation => "Invalid bitwise operation (check length of inputs)".fmt(f),
Error::DivisionByZero => "Invalid division operation".fmt(f),
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
Error::NegativeLocktime => "Negative locktime".fmt(f),

View File

@ -68,6 +68,26 @@ pub struct VerificationFlags {
/// Support OP_CAT opcode
pub verify_concat: bool,
/// Support OP_SPLIT opcode
///
/// This opcode replaces OP_SUBSTR => enabling both OP_SPLIT && OP_SUBSTR would be an error
pub verify_split: bool,
/// Support OP_AND opcode
pub verify_and: bool,
/// Support OP_OR opcode
pub verify_or: bool,
/// Support OP_XOR opcode
pub verify_xor: bool,
/// Support OP_DIV opcode
pub verify_div: bool,
/// Support OP_MOD opcode
pub verify_mod: bool,
}
impl VerificationFlags {
@ -115,5 +135,35 @@ impl VerificationFlags {
self.verify_concat = value;
self
}
pub fn verify_split(mut self, value: bool) -> Self {
self.verify_split = value;
self
}
pub fn verify_and(mut self, value: bool) -> Self {
self.verify_and = value;
self
}
pub fn verify_or(mut self, value: bool) -> Self {
self.verify_or = value;
self
}
pub fn verify_xor(mut self, value: bool) -> Self {
self.verify_xor = value;
self
}
pub fn verify_div(mut self, value: bool) -> Self {
self.verify_div = value;
self
}
pub fn verify_mod(mut self, value: bool) -> Self {
self.verify_mod = value;
self
}
}

View File

@ -582,6 +582,72 @@ pub fn eval_script(
}
value_to_update.append(&mut value_to_append);
},
// OP_SPLIT replaces OP_SUBSTR
Opcode::OP_SUBSTR if flags.verify_split => {
let n = Num::from_slice(&stack.pop()?, flags.verify_minimaldata, 4)?;
if n.is_negative() {
return Err(Error::InvalidStackOperation);
}
let n: usize = n.into();
let splitted_value = {
let mut value_to_split = stack.last_mut()?;
if n > value_to_split.len() {
return Err(Error::InvalidSplitRange);
}
value_to_split.split_off(n)
};
stack.push(splitted_value);
},
Opcode::OP_AND if flags.verify_and => {
let mask = stack.pop()?;
let mask_len = mask.len();
let value_to_update = stack.last_mut()?;
if mask_len != value_to_update.len() {
return Err(Error::InvalidBitwiseOperation);
}
for (byte_to_update, byte_mask) in (*value_to_update).iter_mut().zip(mask.iter()) {
*byte_to_update = *byte_to_update & byte_mask;
}
},
Opcode::OP_OR if flags.verify_or => {
let mask = stack.pop()?;
let mask_len = mask.len();
let value_to_update = stack.last_mut()?;
if mask_len != value_to_update.len() {
return Err(Error::InvalidBitwiseOperation);
}
for (byte_to_update, byte_mask) in (*value_to_update).iter_mut().zip(mask.iter()) {
*byte_to_update = *byte_to_update | byte_mask;
}
},
Opcode::OP_XOR if flags.verify_xor => {
let mask = stack.pop()?;
let mask_len = mask.len();
let value_to_update = stack.last_mut()?;
if mask_len != value_to_update.len() {
return Err(Error::InvalidBitwiseOperation);
}
for (byte_to_update, byte_mask) in (*value_to_update).iter_mut().zip(mask.iter()) {
*byte_to_update = *byte_to_update ^ byte_mask;
}
},
Opcode::OP_DIV if flags.verify_div => {
let v1 = Num::from_slice(&stack.pop()?, flags.verify_minimaldata, 4)?;
let v2 = Num::from_slice(&stack.pop()?, flags.verify_minimaldata, 4)?;
if v2.is_zero() {
return Err(Error::DivisionByZero);
}
stack.push((v1 / v2).to_bytes());
},
Opcode::OP_MOD if flags.verify_mod => {
let v1 = Num::from_slice(&stack.pop()?, flags.verify_minimaldata, 4)?;
let v2 = Num::from_slice(&stack.pop()?, flags.verify_minimaldata, 4)?;
if v2.is_zero() {
return Err(Error::DivisionByZero);
}
stack.push((v1 % v2).to_bytes());
},
Opcode::OP_CAT | Opcode::OP_SUBSTR | Opcode::OP_LEFT | Opcode::OP_RIGHT |
Opcode::OP_INVERT | Opcode::OP_AND | Opcode::OP_OR | Opcode::OP_XOR |
Opcode::OP_2MUL | Opcode::OP_2DIV | Opcode::OP_MUL | Opcode::OP_DIV |
@ -3102,7 +3168,6 @@ mod tests {
#[test]
fn op_cat_disabled_by_default() {
// maxlen_x empty OP_CAT → ok
let script = Builder::default()
.push_data(&[1; 1])
.push_data(&[1; 1])
@ -3203,4 +3268,410 @@ mod tests {
basic_test_with_flags(&script, &VerificationFlags::default().verify_concat(true), result,
vec![vec![0x11, 0x22, 0x33].into()].into());
}
#[test]
fn op_split_disabled_by_default() {
let script = Builder::default()
.push_data(&[0x11, 0x22])
.push_num(1.into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Err(Error::DisabledOpcode(Opcode::OP_SUBSTR));
basic_test_with_flags(&script, &VerificationFlags::default(), result,
vec![].into());
}
#[test]
fn op_split_empty_at_zero_succeeds() {
// OP_0 0 OP_SPLIT -> OP_0 OP_0
let script = Builder::default()
.push_opcode(Opcode::OP_0)
.push_num(0.into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Ok(false);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![vec![0; 0].into(), vec![0; 0].into()].into());
}
#[test]
fn op_split_non_empty_at_zero_succeeds() {
// x 0 OP_SPLIT -> OP_0 x
let script = Builder::default()
.push_data(&[0x00, 0x11, 0x22])
.push_num(0.into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![vec![0; 0].into(), vec![0x00, 0x11, 0x22].into()].into());
}
#[test]
fn op_split_non_empty_at_len_succeeds() {
// x len(x) OP_SPLIT -> x OP_0
let script = Builder::default()
.push_data(&[0x00, 0x11, 0x22])
.push_num(3.into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Ok(false);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![vec![0x00, 0x11, 0x22].into(), vec![0; 0].into()].into());
}
#[test]
fn op_split_non_empty_at_post_len_fails() {
// x (len(x) + 1) OP_SPLIT -> FAIL
let script = Builder::default()
.push_data(&[0x00, 0x11, 0x22])
.push_num(4.into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Err(Error::InvalidSplitRange);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![].into());
}
#[test]
fn op_split_non_empty_at_mid_succeeds() {
let script = Builder::default()
.push_data(&[0x00, 0x11, 0x22])
.push_num(2.into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![vec![0x00, 0x11].into(), vec![0x22].into()].into());
}
#[test]
fn op_split_fails_if_position_is_nan() {
let script = Builder::default()
.push_data(&[0x00, 0x11, 0x22])
.push_opcode(Opcode::OP_1NEGATE) // NaN
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![].into());
}
#[test]
fn op_split_fails_if_position_is_negative() {
let script = Builder::default()
.push_data(&[0x00, 0x11, 0x22])
.push_num((-10).into())
.push_opcode(Opcode::OP_SUBSTR)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_split(true), result,
vec![].into());
}
#[test]
fn op_and_disabled_by_default() {
let script = Builder::default()
.push_data(&[0x11])
.push_data(&[0x22])
.push_opcode(Opcode::OP_AND)
.into_script();
let result = Err(Error::DisabledOpcode(Opcode::OP_AND));
basic_test_with_flags(&script, &VerificationFlags::default(), result,
vec![].into());
}
#[test]
fn op_and_fails_with_different_len_args() {
let script = Builder::default()
.push_data(&[0x11, 0x22])
.push_data(&[0x22])
.push_opcode(Opcode::OP_AND)
.into_script();
let result = Err(Error::InvalidBitwiseOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_and(true), result,
vec![].into());
}
#[test]
fn op_and_succeeds() {
let script = Builder::default()
.push_data(&[0x34, 0x56])
.push_data(&[0x56, 0x78])
.push_opcode(Opcode::OP_AND)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_and(true), result,
vec![vec![0x14, 0x50].into()].into());
}
#[test]
fn op_or_disabled_by_default() {
let script = Builder::default()
.push_data(&[0x11])
.push_data(&[0x22])
.push_opcode(Opcode::OP_OR)
.into_script();
let result = Err(Error::DisabledOpcode(Opcode::OP_OR));
basic_test_with_flags(&script, &VerificationFlags::default(), result,
vec![].into());
}
#[test]
fn op_or_fails_with_different_len_args() {
let script = Builder::default()
.push_data(&[0x11, 0x22])
.push_data(&[0x22])
.push_opcode(Opcode::OP_OR)
.into_script();
let result = Err(Error::InvalidBitwiseOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_or(true), result,
vec![].into());
}
#[test]
fn op_or_succeeds() {
let script = Builder::default()
.push_data(&[0x34, 0x56])
.push_data(&[0x56, 0x78])
.push_opcode(Opcode::OP_OR)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_or(true), result,
vec![vec![0x76, 0x7e].into()].into());
}
#[test]
fn op_xor_disabled_by_default() {
let script = Builder::default()
.push_data(&[0x11])
.push_data(&[0x22])
.push_opcode(Opcode::OP_XOR)
.into_script();
let result = Err(Error::DisabledOpcode(Opcode::OP_XOR));
basic_test_with_flags(&script, &VerificationFlags::default(), result,
vec![].into());
}
#[test]
fn op_xor_fails_with_different_len_args() {
let script = Builder::default()
.push_data(&[0x11, 0x22])
.push_data(&[0x22])
.push_opcode(Opcode::OP_XOR)
.into_script();
let result = Err(Error::InvalidBitwiseOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_xor(true), result,
vec![].into());
}
#[test]
fn op_xor_succeeds() {
let script = Builder::default()
.push_data(&[0x34, 0x56])
.push_data(&[0x56, 0x78])
.push_opcode(Opcode::OP_XOR)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_xor(true), result,
vec![vec![0x62, 0x2e].into()].into());
}
#[test]
fn op_div_disabled_by_default() {
let script = Builder::default()
.push_num(13.into())
.push_num(5.into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Err(Error::DisabledOpcode(Opcode::OP_DIV));
basic_test_with_flags(&script, &VerificationFlags::default(), result,
vec![].into());
}
#[test]
fn op_div_num_by_nan_fails() {
// a b OP_DIV -> failure where !isnum(a) or !isnum(b). Both operands must be valid numbers
let script = Builder::default()
.push_opcode(Opcode::OP_1SUB)
.push_num(5.into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![].into());
}
#[test]
fn op_div_nan_by_num_fails() {
// a b OP_DIV -> failure where !isnum(a) or !isnum(b). Both operands must be valid numbers
let script = Builder::default()
.push_num(5.into())
.push_opcode(Opcode::OP_1SUB)
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![].into());
}
#[test]
fn op_div_num_by_zero_fails() {
// a 0 OP_DIV -> failure. Division by positive zero (all sizes), negative zero (all sizes), OP_0
let script = Builder::default()
.push_num(0.into())
.push_num(5.into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Err(Error::DivisionByZero);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![].into());
}
#[test]
fn op_div_negative_by_negative_succeeds() {
let script = Builder::default()
.push_num((-5).into())
.push_num((-13).into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![Num::from(2).to_bytes()].into());
}
#[test]
fn op_div_negative_by_positive_succeeds() {
let script = Builder::default()
.push_num(5.into())
.push_num((-13).into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![Num::from(-2).to_bytes()].into());
}
#[test]
fn op_div_positive_by_negative_succeeds() {
let script = Builder::default()
.push_num((-5).into())
.push_num(13.into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![Num::from(-2).to_bytes()].into());
}
#[test]
fn op_div_positive_by_positive_succeeds() {
let script = Builder::default()
.push_num(5.into())
.push_num(13.into())
.push_opcode(Opcode::OP_DIV)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_div(true), result,
vec![Num::from(2).to_bytes()].into());
}
#[test]
fn op_mod_disabled_by_default() {
let script = Builder::default()
.push_num(13.into())
.push_num(5.into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Err(Error::DisabledOpcode(Opcode::OP_MOD));
basic_test_with_flags(&script, &VerificationFlags::default(), result,
vec![].into());
}
#[test]
fn op_mod_num_by_nan_fails() {
// a b OP_MOD -> failure where !isnum(a) or !isnum(b). Both operands must be valid numbers
let script = Builder::default()
.push_opcode(Opcode::OP_1SUB)
.push_num(5.into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![].into());
}
#[test]
fn op_mod_nan_by_num_fails() {
// a b OP_MOD -> failure where !isnum(a) or !isnum(b). Both operands must be valid numbers
let script = Builder::default()
.push_num(5.into())
.push_opcode(Opcode::OP_1SUB)
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Err(Error::InvalidStackOperation);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![].into());
}
#[test]
fn op_mod_num_by_zero_fails() {
// a 0 OP_MOD -> failure. Division by positive zero (all sizes), negative zero (all sizes), OP_0
let script = Builder::default()
.push_num(0.into())
.push_num(5.into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Err(Error::DivisionByZero);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![].into());
}
#[test]
fn op_mod_negative_by_negative_succeeds() {
let script = Builder::default()
.push_num((-5).into())
.push_num((-13).into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![Num::from(-3).to_bytes()].into());
}
#[test]
fn op_mod_negative_by_positive_succeeds() {
let script = Builder::default()
.push_num(5.into())
.push_num((-13).into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![Num::from(-3).to_bytes()].into());
}
#[test]
fn op_mod_positive_by_negative_succeeds() {
let script = Builder::default()
.push_num((-5).into())
.push_num(13.into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![Num::from(3).to_bytes()].into());
}
#[test]
fn op_mod_positive_by_positive_succeeds() {
let script = Builder::default()
.push_num(5.into())
.push_num(13.into())
.push_opcode(Opcode::OP_MOD)
.into_script();
let result = Ok(true);
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
vec![Num::from(3).to_bytes()].into());
}
}

View File

@ -210,3 +210,19 @@ impl ops::Neg for Num {
(-self.value).into()
}
}
impl ops::Div for Num {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
(self.value / rhs.value).into()
}
}
impl ops::Rem for Num {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
(self.value % rhs.value).into()
}
}

View File

@ -438,9 +438,14 @@ impl Opcode {
use self::Opcode::*;
match *self {
OP_CAT if !flags.verify_concat => true,
OP_SUBSTR | OP_LEFT | OP_RIGHT | OP_INVERT | OP_AND | OP_OR |
OP_XOR | OP_2MUL | OP_2DIV | OP_MUL | OP_DIV | OP_MOD | OP_LSHIFT |
OP_RSHIFT => true,
OP_SUBSTR if !flags.verify_split => true,
OP_AND if !flags.verify_and => true,
OP_OR if !flags.verify_or => true,
OP_XOR if !flags.verify_xor => true,
OP_DIV if !flags.verify_div => true,
OP_MOD if !flags.verify_mod => true,
OP_LEFT | OP_RIGHT | OP_INVERT | OP_2MUL | OP_2DIV |
OP_MUL | OP_LSHIFT | OP_RSHIFT => true,
_ => false,
}
}