Handling CHECKMULTISIG(VERIFY) ops in TapScript context; refactoring classifier

This commit is contained in:
Dr Maxim Orlovsky 2021-09-14 08:21:53 +02:00
parent b0ad6748e4
commit c75f3ef4a8
No known key found for this signature in database
GPG Key ID: FFC0250947E5C6F7
1 changed files with 71 additions and 71 deletions

View File

@ -674,77 +674,62 @@ impl All {
/// Classifies an Opcode into a broad class /// Classifies an Opcode into a broad class
#[inline] #[inline]
pub fn classify(self, ctx: ClassifyContext) -> Class { pub fn classify(self, ctx: ClassifyContext) -> Class {
// 3 opcodes use self::all::*;
match ctx { match (self, ctx) {
ClassifyContext::TapScript => // 3 opcodes // 3 opcodes illegal in all contexts
if self == all::OP_VERIF || self == all::OP_VERNOTIF || (OP_VERIF, _) | (OP_VERNOTIF, _) | (OP_INVALIDOPCODE, _) => Class::IllegalOp,
self == all::OP_INVALIDOPCODE {
Class::IllegalOp // 15 opcodes illegal in Legacy context
// 11 opcodes (OP_CAT, ctx) | (OP_SUBSTR, ctx)
} else if self == all::OP_NOP || | (OP_LEFT, ctx) | (OP_RIGHT, ctx)
(all::OP_NOP1.code <= self.code && | (OP_INVERT, ctx)
self.code <= all::OP_NOP10.code) { | (OP_AND, ctx) | (OP_OR, ctx) | (OP_XOR, ctx)
Class::NoOp | (OP_2MUL, ctx) | (OP_2DIV, ctx)
// 87 opcodes | (OP_MUL, ctx) | (OP_DIV, ctx) | (OP_MOD, ctx)
} else if self.code == 80 || self.code == 98 || | (OP_LSHIFT, ctx) | (OP_RSHIFT, ctx) if ctx == ClassifyContext::Legacy => Class::IllegalOp,
(self.code >= 126 && self.code <= 129) ||
(self.code >= 131 && self.code <= 134) || // 87 opcodes of SuccessOp class only in TapScript context
(self.code >= 137 && self.code <= 138) || (op, ClassifyContext::TapScript)
(self.code >= 141 && self.code <= 142) || if op.code == 80 || op.code == 98 ||
(self.code >= 149 && self.code <= 153) || (op.code >= 126 && op.code <= 129) ||
(self.code >= 187 && self.code <= 254) { (op.code >= 131 && op.code <= 134) ||
Class::SuccessOp (op.code >= 137 && op.code <= 138) ||
// 1 opcode (op.code >= 141 && op.code <= 142) ||
} else if self == all::OP_RETURN{ (op.code >= 149 && op.code <= 153) ||
Class::ReturnOp (op.code >= 187 && op.code <= 254) => Class::SuccessOp,
// 1 opcode
} else if self == all::OP_PUSHNUM_NEG1 { // 11 opcodes of NoOp class
Class::PushNum(-1) (OP_NOP, _) => Class::NoOp,
// 16 opcodes (op, _) if op.code >= OP_NOP1.code && op.code <= OP_NOP10.code => Class::NoOp,
} else if all::OP_PUSHNUM_1.code <= self.code &&
self.code <= all::OP_PUSHNUM_16.code { // 1 opcode for `OP_RETURN`
Class::PushNum(1 + self.code as i32 - all::OP_PUSHNUM_1.code as i32) (OP_RETURN, _) => Class::ReturnOp,
// 76 opcodes
} else if self.code <= all::OP_PUSHBYTES_75.code { // 4 opcodes operating equally to `OP_RETURN` only in Legacy context
Class::PushBytes(self.code as u32) (OP_RESERVED, ctx)
// 61 opcodes | (OP_RESERVED1, ctx) | (OP_RESERVED2, ctx)
} else { | (OP_VER, ctx) if ctx == ClassifyContext::Legacy => Class::ReturnOp,
Class::Ordinary(Ordinary::try_from_all(self).unwrap())
// 71 opcodes operating equally to `OP_RETURN` only in Legacy context
(op, ClassifyContext::Legacy) if op.code >= OP_CHECKSIGADD.code => Class::ReturnOp,
// 2 opcodes operating equally to `OP_RETURN` only in TapScript context
(OP_CHECKMULTISIG, ClassifyContext::TapScript)
| (OP_CHECKMULTISIGVERIFY, ClassifyContext::TapScript) => Class::ReturnOp,
// 1 opcode of PushNum class
(OP_PUSHNUM_NEG1, _) => Class::PushNum(-1),
// 16 opcodes of PushNum class
(op, _) if op.code >= OP_PUSHNUM_1.code && op.code <= OP_PUSHNUM_16.code => {
Class::PushNum(1 + self.code as i32 - OP_PUSHNUM_1.code as i32)
}, },
ClassifyContext::Legacy =>
if self == all::OP_VERIF || self == all::OP_VERNOTIF || // 76 opcodes of PushBytes class
self == all::OP_CAT || self == all::OP_SUBSTR || (op, _) if op.code <= OP_PUSHBYTES_75.code => Class::PushBytes(self.code as u32),
self == all::OP_LEFT || self == all::OP_RIGHT ||
self == all::OP_INVERT || self == all::OP_AND || // opcodes of Ordinary class: 61 for Legacy and 60 for TapScript context
self == all::OP_OR || self == all::OP_XOR || (_, _) => Class::Ordinary(Ordinary::with(self)),
self == all::OP_2MUL || self == all::OP_2DIV ||
self == all::OP_MUL || self == all::OP_DIV || self == all::OP_MOD ||
self == all::OP_LSHIFT || self == all::OP_RSHIFT {
Class::IllegalOp
// 11 opcodes
} else if self == all::OP_NOP ||
(all::OP_NOP1.code <= self.code &&
self.code <= all::OP_NOP10.code) {
Class::NoOp
// 75 opcodes
} else if self == all::OP_RESERVED || self == all::OP_VER || self == all::OP_RETURN ||
self == all::OP_RESERVED1 || self == all::OP_RESERVED2 ||
self.code >= all::OP_CHECKSIGADD.code {
Class::ReturnOp
// 1 opcode
} else if self == all::OP_PUSHNUM_NEG1 {
Class::PushNum(-1)
// 16 opcodes
} else if all::OP_PUSHNUM_1.code <= self.code &&
self.code <= all::OP_PUSHNUM_16.code {
Class::PushNum(1 + self.code as i32 - all::OP_PUSHNUM_1.code as i32)
// 76 opcodes
} else if self.code <= all::OP_PUSHBYTES_75.code {
Class::PushBytes(self.code as u32)
// 61 opcodes
} else {
Class::Ordinary(Ordinary::try_from_all(self).unwrap())
}
} }
} }
@ -825,6 +810,13 @@ macro_rules! ordinary_opcode {
} }
impl Ordinary { impl Ordinary {
fn with(b: All) -> Self {
match b {
$( all::$op => { Ordinary::$op } ),*
_ => unreachable!("construction of `Ordinary` type from non-ordinary opcode {}", b),
}
}
/// Try to create from an All /// Try to create from an All
pub fn try_from_all(b: All) -> Option<Self> { pub fn try_from_all(b: All) -> Option<Self> {
match b { match b {
@ -890,6 +882,14 @@ mod tests {
#[test] #[test]
fn classify_test() { fn classify_test() {
let op174 = all::OP_CHECKMULTISIG;
assert_eq!(op174.classify(ClassifyContext::Legacy), Class::Ordinary(Ordinary::OP_CHECKMULTISIG));
assert_eq!(op174.classify(ClassifyContext::TapScript), Class::ReturnOp);
let op175 = all::OP_CHECKMULTISIGVERIFY;
assert_eq!(op175.classify(ClassifyContext::Legacy), Class::Ordinary(Ordinary::OP_CHECKMULTISIGVERIFY));
assert_eq!(op175.classify(ClassifyContext::TapScript), Class::ReturnOp);
let op186 = all::OP_CHECKSIGADD; let op186 = all::OP_CHECKSIGADD;
assert_eq!(op186.classify(ClassifyContext::Legacy), Class::ReturnOp); assert_eq!(op186.classify(ClassifyContext::Legacy), Class::ReturnOp);
assert_eq!(op186.classify(ClassifyContext::TapScript), Class::Ordinary(Ordinary::OP_CHECKSIGADD)); assert_eq!(op186.classify(ClassifyContext::TapScript), Class::Ordinary(Ordinary::OP_CHECKSIGADD));