OP_DIV and OP_MOD implementations

This commit is contained in:
Svyatoslav Nikolsky 2018-03-02 14:09:31 +03:00
parent aca8deeeba
commit 06a4ef8dbc
5 changed files with 252 additions and 2 deletions

View File

@ -34,6 +34,7 @@ pub enum Error {
UnbalancedConditional,
InvalidSplitRange,
InvalidBitwiseOperation,
DivisionByZero,
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
NegativeLocktime,
@ -96,6 +97,7 @@ impl fmt::Display for Error {
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

@ -82,6 +82,12 @@ pub struct VerificationFlags {
/// 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 {
@ -149,5 +155,15 @@ impl VerificationFlags {
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

@ -631,6 +631,23 @@ pub fn eval_script(
*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 |
@ -3459,4 +3476,202 @@ mod tests {
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

@ -442,9 +442,10 @@ impl Opcode {
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_DIV | OP_MOD | OP_LSHIFT |
OP_RSHIFT => true,
OP_MUL | OP_LSHIFT | OP_RSHIFT => true,
_ => false,
}
}