bitwise ops (OP_AND, OP_OR, OP_XOR) implementation
This commit is contained in:
parent
05f885bfd9
commit
aca8deeeba
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue