Merge pull request #490 from paritytech/div_mod_ops_impl
Bitcoin Cash: OP_DIV and OP_MOD implementation
This commit is contained in:
commit
be6d461bde
|
@ -28,6 +28,10 @@ impl Bytes {
|
||||||
pub fn append(&mut self, other: &mut Bytes) {
|
pub fn append(&mut self, other: &mut Bytes) {
|
||||||
self.0.append(&mut other.0);
|
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 {
|
impl HeapSizeOf for Bytes {
|
||||||
|
|
|
@ -32,6 +32,9 @@ pub enum Error {
|
||||||
InvalidStackOperation,
|
InvalidStackOperation,
|
||||||
InvalidAltstackOperation,
|
InvalidAltstackOperation,
|
||||||
UnbalancedConditional,
|
UnbalancedConditional,
|
||||||
|
InvalidSplitRange,
|
||||||
|
InvalidBitwiseOperation,
|
||||||
|
DivisionByZero,
|
||||||
|
|
||||||
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
|
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
|
||||||
NegativeLocktime,
|
NegativeLocktime,
|
||||||
|
@ -92,6 +95,9 @@ impl fmt::Display for Error {
|
||||||
Error::InvalidStackOperation => "Invalid stack operation".fmt(f),
|
Error::InvalidStackOperation => "Invalid stack operation".fmt(f),
|
||||||
Error::InvalidAltstackOperation => "Invalid altstack operation".fmt(f),
|
Error::InvalidAltstackOperation => "Invalid altstack operation".fmt(f),
|
||||||
Error::UnbalancedConditional => "Unbalanced conditional".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
|
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
|
||||||
Error::NegativeLocktime => "Negative locktime".fmt(f),
|
Error::NegativeLocktime => "Negative locktime".fmt(f),
|
||||||
|
|
|
@ -68,6 +68,26 @@ pub struct VerificationFlags {
|
||||||
|
|
||||||
/// Support OP_CAT opcode
|
/// Support OP_CAT opcode
|
||||||
pub verify_concat: bool,
|
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 {
|
impl VerificationFlags {
|
||||||
|
@ -115,5 +135,35 @@ impl VerificationFlags {
|
||||||
self.verify_concat = value;
|
self.verify_concat = value;
|
||||||
self
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -582,6 +582,72 @@ pub fn eval_script(
|
||||||
}
|
}
|
||||||
value_to_update.append(&mut value_to_append);
|
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_CAT | Opcode::OP_SUBSTR | Opcode::OP_LEFT | Opcode::OP_RIGHT |
|
||||||
Opcode::OP_INVERT | Opcode::OP_AND | Opcode::OP_OR | Opcode::OP_XOR |
|
Opcode::OP_INVERT | Opcode::OP_AND | Opcode::OP_OR | Opcode::OP_XOR |
|
||||||
Opcode::OP_2MUL | Opcode::OP_2DIV | Opcode::OP_MUL | Opcode::OP_DIV |
|
Opcode::OP_2MUL | Opcode::OP_2DIV | Opcode::OP_MUL | Opcode::OP_DIV |
|
||||||
|
@ -3102,7 +3168,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn op_cat_disabled_by_default() {
|
fn op_cat_disabled_by_default() {
|
||||||
// maxlen_x empty OP_CAT → ok
|
|
||||||
let script = Builder::default()
|
let script = Builder::default()
|
||||||
.push_data(&[1; 1])
|
.push_data(&[1; 1])
|
||||||
.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,
|
basic_test_with_flags(&script, &VerificationFlags::default().verify_concat(true), result,
|
||||||
vec![vec![0x11, 0x22, 0x33].into()].into());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,3 +210,19 @@ impl ops::Neg for Num {
|
||||||
(-self.value).into()
|
(-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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -438,9 +438,14 @@ impl Opcode {
|
||||||
use self::Opcode::*;
|
use self::Opcode::*;
|
||||||
match *self {
|
match *self {
|
||||||
OP_CAT if !flags.verify_concat => true,
|
OP_CAT if !flags.verify_concat => true,
|
||||||
OP_SUBSTR | OP_LEFT | OP_RIGHT | OP_INVERT | OP_AND | OP_OR |
|
OP_SUBSTR if !flags.verify_split => true,
|
||||||
OP_XOR | OP_2MUL | OP_2DIV | OP_MUL | OP_DIV | OP_MOD | OP_LSHIFT |
|
OP_AND if !flags.verify_and => true,
|
||||||
OP_RSHIFT => 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,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue