Add CHECKSIGADD and update classify API
This commit is contained in:
parent
bd5d875e8a
commit
c252b36786
|
@ -417,8 +417,8 @@ pub mod all {
|
||||||
/// Does nothing
|
/// Does nothing
|
||||||
pub const OP_NOP10: All = All {code: 0xb9};
|
pub const OP_NOP10: All = All {code: 0xb9};
|
||||||
// Every other opcode acts as OP_RETURN
|
// Every other opcode acts as OP_RETURN
|
||||||
/// Synonym for OP_RETURN
|
/// OP_CHECKSIGADD post tapscript
|
||||||
pub const OP_RETURN_186: All = All {code: 0xba};
|
pub const OP_CHECKSIGADD: All = All {code: 0xba};
|
||||||
/// Synonym for OP_RETURN
|
/// Synonym for OP_RETURN
|
||||||
pub const OP_RETURN_187: All = All {code: 0xbb};
|
pub const OP_RETURN_187: All = All {code: 0xbb};
|
||||||
/// Synonym for OP_RETURN
|
/// Synonym for OP_RETURN
|
||||||
|
@ -556,7 +556,7 @@ pub mod all {
|
||||||
/// Synonym for OP_RETURN
|
/// Synonym for OP_RETURN
|
||||||
pub const OP_RETURN_254: All = All {code: 0xfe};
|
pub const OP_RETURN_254: All = All {code: 0xfe};
|
||||||
/// Synonym for OP_RETURN
|
/// Synonym for OP_RETURN
|
||||||
pub const OP_RETURN_255: All = All {code: 0xff};
|
pub const OP_INVALIDOPCODE: All = All {code: 0xff};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for All {
|
impl fmt::Debug for All {
|
||||||
|
@ -652,16 +652,66 @@ impl fmt::Debug for All {
|
||||||
all::OP_CLTV => write!(f, "CLTV"),
|
all::OP_CLTV => write!(f, "CLTV"),
|
||||||
all::OP_CSV => write!(f, "CSV"),
|
all::OP_CSV => write!(f, "CSV"),
|
||||||
All {code: x} if x >= all::OP_NOP1.code && x <= all::OP_NOP10.code => write!(f, "NOP{}", x - all::OP_NOP1.code + 1),
|
All {code: x} if x >= all::OP_NOP1.code && x <= all::OP_NOP10.code => write!(f, "NOP{}", x - all::OP_NOP1.code + 1),
|
||||||
|
all::OP_INVALIDOPCODE => write!(f, "INVALIDOPCODE"),
|
||||||
|
all::OP_CHECKSIGADD => write!(f, "CHECKSIGADD"),
|
||||||
All {code: x} => write!(f, "RETURN_{}", x),
|
All {code: x} => write!(f, "RETURN_{}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Classification context for the opcode. Some opcodes like `OP_RESERVED`
|
||||||
|
/// abort the script in [`ClassifyContext::Legacy`] context, but will act as OP_SUCCESS in tapscript
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum ClassifyContext {
|
||||||
|
/// Opcode used in tapscript context
|
||||||
|
TapScript,
|
||||||
|
/// Opcode used in legacy context
|
||||||
|
Legacy,
|
||||||
|
}
|
||||||
|
|
||||||
impl All {
|
impl All {
|
||||||
/// Classifies an Opcode into a broad class
|
/// Classifies an Opcode into a broad class
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn classify(self) -> Class {
|
pub fn classify(self, ctx: ClassifyContext) -> Class {
|
||||||
// 17 opcodes
|
// 3 opcodes
|
||||||
|
match ctx {
|
||||||
|
ClassifyContext::TapScript => // 3 opcodes
|
||||||
|
if self == all::OP_VERIF || self == all::OP_VERNOTIF ||
|
||||||
|
self == all::OP_INVALIDOPCODE {
|
||||||
|
Class::IllegalOp
|
||||||
|
// 11 opcodes
|
||||||
|
} else if self == all::OP_NOP ||
|
||||||
|
(all::OP_NOP1.code <= self.code &&
|
||||||
|
self.code <= all::OP_NOP10.code) {
|
||||||
|
Class::NoOp
|
||||||
|
// 87 opcodes
|
||||||
|
} else if self.code == 80 || self.code == 98 ||
|
||||||
|
(self.code >= 126 && self.code <= 129) ||
|
||||||
|
(self.code >= 131 && self.code <= 134) ||
|
||||||
|
(self.code >= 137 && self.code <= 138) ||
|
||||||
|
(self.code >= 141 && self.code <= 142) ||
|
||||||
|
(self.code >= 149 && self.code <= 153) ||
|
||||||
|
(self.code >= 187 && self.code <= 254) {
|
||||||
|
Class::SuccessOp
|
||||||
|
// 1 opcode
|
||||||
|
} else if self == all::OP_RETURN{
|
||||||
|
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())
|
||||||
|
},
|
||||||
|
ClassifyContext::Legacy =>
|
||||||
if self == all::OP_VERIF || self == all::OP_VERNOTIF ||
|
if self == all::OP_VERIF || self == all::OP_VERNOTIF ||
|
||||||
self == all::OP_CAT || self == all::OP_SUBSTR ||
|
self == all::OP_CAT || self == all::OP_SUBSTR ||
|
||||||
self == all::OP_LEFT || self == all::OP_RIGHT ||
|
self == all::OP_LEFT || self == all::OP_RIGHT ||
|
||||||
|
@ -679,7 +729,7 @@ impl All {
|
||||||
// 75 opcodes
|
// 75 opcodes
|
||||||
} else if self == all::OP_RESERVED || self == all::OP_VER || self == all::OP_RETURN ||
|
} else if self == all::OP_RESERVED || self == all::OP_VER || self == all::OP_RETURN ||
|
||||||
self == all::OP_RESERVED1 || self == all::OP_RESERVED2 ||
|
self == all::OP_RESERVED1 || self == all::OP_RESERVED2 ||
|
||||||
self.code >= all::OP_RETURN_186.code {
|
self.code >= all::OP_CHECKSIGADD.code {
|
||||||
Class::ReturnOp
|
Class::ReturnOp
|
||||||
// 1 opcode
|
// 1 opcode
|
||||||
} else if self == all::OP_PUSHNUM_NEG1 {
|
} else if self == all::OP_PUSHNUM_NEG1 {
|
||||||
|
@ -691,11 +741,12 @@ impl All {
|
||||||
// 76 opcodes
|
// 76 opcodes
|
||||||
} else if self.code <= all::OP_PUSHBYTES_75.code {
|
} else if self.code <= all::OP_PUSHBYTES_75.code {
|
||||||
Class::PushBytes(self.code as u32)
|
Class::PushBytes(self.code as u32)
|
||||||
// 60 opcodes
|
// 61 opcodes
|
||||||
} else {
|
} else {
|
||||||
Class::Ordinary(Ordinary::try_from_all(self).unwrap())
|
Class::Ordinary(Ordinary::try_from_all(self).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Encode as a byte
|
/// Encode as a byte
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -742,6 +793,8 @@ pub enum Class {
|
||||||
PushBytes(u32),
|
PushBytes(u32),
|
||||||
/// Fails the script if executed
|
/// Fails the script if executed
|
||||||
ReturnOp,
|
ReturnOp,
|
||||||
|
/// Succeeds the script even if not executed
|
||||||
|
SuccessOp,
|
||||||
/// Fails the script even if not executed
|
/// Fails the script even if not executed
|
||||||
IllegalOp,
|
IllegalOp,
|
||||||
/// Does nothing
|
/// Does nothing
|
||||||
|
@ -805,7 +858,8 @@ ordinary_opcode! {
|
||||||
// crypto
|
// crypto
|
||||||
OP_RIPEMD160, OP_SHA1, OP_SHA256, OP_HASH160, OP_HASH256,
|
OP_RIPEMD160, OP_SHA1, OP_SHA256, OP_HASH160, OP_HASH256,
|
||||||
OP_CODESEPARATOR, OP_CHECKSIG, OP_CHECKSIGVERIFY,
|
OP_CODESEPARATOR, OP_CHECKSIG, OP_CHECKSIGVERIFY,
|
||||||
OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
|
OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY,
|
||||||
|
OP_CHECKSIGADD
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ordinary {
|
impl Ordinary {
|
||||||
|
@ -1023,7 +1077,7 @@ mod tests {
|
||||||
roundtrip!(unique, OP_NOP8);
|
roundtrip!(unique, OP_NOP8);
|
||||||
roundtrip!(unique, OP_NOP9);
|
roundtrip!(unique, OP_NOP9);
|
||||||
roundtrip!(unique, OP_NOP10);
|
roundtrip!(unique, OP_NOP10);
|
||||||
roundtrip!(unique, OP_RETURN_186);
|
roundtrip!(unique, OP_CHECKSIGADD);
|
||||||
roundtrip!(unique, OP_RETURN_187);
|
roundtrip!(unique, OP_RETURN_187);
|
||||||
roundtrip!(unique, OP_RETURN_188);
|
roundtrip!(unique, OP_RETURN_188);
|
||||||
roundtrip!(unique, OP_RETURN_189);
|
roundtrip!(unique, OP_RETURN_189);
|
||||||
|
@ -1092,7 +1146,7 @@ mod tests {
|
||||||
roundtrip!(unique, OP_RETURN_252);
|
roundtrip!(unique, OP_RETURN_252);
|
||||||
roundtrip!(unique, OP_RETURN_253);
|
roundtrip!(unique, OP_RETURN_253);
|
||||||
roundtrip!(unique, OP_RETURN_254);
|
roundtrip!(unique, OP_RETURN_254);
|
||||||
roundtrip!(unique, OP_RETURN_255);
|
roundtrip!(unique, OP_INVALIDOPCODE);
|
||||||
assert_eq!(unique.len(), 256);
|
assert_eq!(unique.len(), 256);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,8 +407,9 @@ impl Script {
|
||||||
|
|
||||||
/// Whether a script can be proven to have no satisfying input
|
/// Whether a script can be proven to have no satisfying input
|
||||||
pub fn is_provably_unspendable(&self) -> bool {
|
pub fn is_provably_unspendable(&self) -> bool {
|
||||||
!self.0.is_empty() && (opcodes::All::from(self.0[0]).classify() == opcodes::Class::ReturnOp ||
|
!self.0.is_empty() &&
|
||||||
opcodes::All::from(self.0[0]).classify() == opcodes::Class::IllegalOp)
|
(opcodes::All::from(self.0[0]).classify(opcodes::ClassifyContext::Legacy) == opcodes::Class::ReturnOp ||
|
||||||
|
opcodes::All::from(self.0[0]).classify(opcodes::ClassifyContext::Legacy) == opcodes::Class::IllegalOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the minimum value an output with this script should have in order to be
|
/// Gets the minimum value an output with this script should have in order to be
|
||||||
|
@ -479,7 +480,7 @@ impl Script {
|
||||||
let opcode = opcodes::All::from(script[index]);
|
let opcode = opcodes::All::from(script[index]);
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
let data_len = if let opcodes::Class::PushBytes(n) = opcode.classify() {
|
let data_len = if let opcodes::Class::PushBytes(n) = opcode.classify(opcodes::ClassifyContext::Legacy) {
|
||||||
n as usize
|
n as usize
|
||||||
} else {
|
} else {
|
||||||
match opcode {
|
match opcode {
|
||||||
|
@ -589,7 +590,9 @@ impl<'a> Iterator for Instructions<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match opcodes::All::from(self.data[0]).classify() {
|
// classify parameter does not really matter here since we are only using
|
||||||
|
// it for pushes and nums
|
||||||
|
match opcodes::All::from(self.data[0]).classify(opcodes::ClassifyContext::Legacy) {
|
||||||
opcodes::Class::PushBytes(n) => {
|
opcodes::Class::PushBytes(n) => {
|
||||||
let n = n as usize;
|
let n = n as usize;
|
||||||
if self.data.len() < n + 1 {
|
if self.data.len() < n + 1 {
|
||||||
|
|
|
@ -132,7 +132,7 @@ impl Template {
|
||||||
pub fn first_push_as_number(&self) -> Option<usize> {
|
pub fn first_push_as_number(&self) -> Option<usize> {
|
||||||
if !self.0.is_empty() {
|
if !self.0.is_empty() {
|
||||||
if let TemplateElement::Op(op) = self.0[0] {
|
if let TemplateElement::Op(op) = self.0[0] {
|
||||||
if let opcodes::Class::PushNum(n) = op.classify() {
|
if let opcodes::Class::PushNum(n) = op.classify(opcodes::ClassifyContext::Legacy) {
|
||||||
if n >= 0 {
|
if n >= 0 {
|
||||||
return Some(n as usize);
|
return Some(n as usize);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ pub fn untemplate(script: &script::Script) -> Result<(Template, Vec<PublicKey>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
script::Instruction::Op(op) => {
|
script::Instruction::Op(op) => {
|
||||||
match op.classify() {
|
match op.classify(opcodes::ClassifyContext::Legacy) {
|
||||||
// CHECKSIG should only come after a list of keys
|
// CHECKSIG should only come after a list of keys
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKSIG) |
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKSIG) |
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKSIGVERIFY) => {
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKSIGVERIFY) => {
|
||||||
|
|
|
@ -223,7 +223,7 @@ pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
|
||||||
top = top.wrapping_sub(needle.len());
|
top = top.wrapping_sub(needle.len());
|
||||||
if overflow { break; }
|
if overflow { break; }
|
||||||
} else {
|
} else {
|
||||||
i += match opcodes::All::from((*haystack)[i]).classify() {
|
i += match opcodes::All::from((*haystack)[i]).classify(opcodes::ClassifyContext::Legacy) {
|
||||||
opcodes::Class::PushBytes(n) => n as usize + 1,
|
opcodes::Class::PushBytes(n) => n as usize + 1,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
||||||
|
|
Loading…
Reference in New Issue