bitwise ops (OP_AND, OP_OR, OP_XOR) implementation

This commit is contained in:
Svyatoslav Nikolsky 2018-03-02 12:48:27 +03:00
parent 05f885bfd9
commit aca8deeeba
4 changed files with 172 additions and 2 deletions

View File

@ -33,6 +33,7 @@ pub enum Error {
InvalidAltstackOperation,
UnbalancedConditional,
InvalidSplitRange,
InvalidBitwiseOperation,
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
NegativeLocktime,
@ -94,6 +95,7 @@ impl fmt::Display for Error {
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),
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
Error::NegativeLocktime => "Negative locktime".fmt(f),

View File

@ -73,6 +73,15 @@ pub struct VerificationFlags {
///
/// 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,
}
impl VerificationFlags {
@ -125,5 +134,20 @@ impl VerificationFlags {
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
}
}

View File

@ -598,6 +598,39 @@ pub fn eval_script(
};
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_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 |
@ -3318,4 +3351,112 @@ mod tests {
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());
}
}

View File

@ -439,8 +439,11 @@ impl Opcode {
match *self {
OP_CAT if !flags.verify_concat => true,
OP_SUBSTR if !flags.verify_split => true,
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_AND if !flags.verify_and => true,
OP_OR if !flags.verify_or => true,
OP_XOR if !flags.verify_xor => true,
OP_LEFT | OP_RIGHT | OP_INVERT | OP_2MUL | OP_2DIV |
OP_MUL | OP_DIV | OP_MOD | OP_LSHIFT |
OP_RSHIFT => true,
_ => false,
}