@@ -417,8 +417,8 @@ pub mod all {
417
417
/// Does nothing
418
418
pub const OP_NOP10 : All = All { code : 0xb9 } ;
419
419
// Every other opcode acts as OP_RETURN
420
- /// Synonym for OP_RETURN
421
- pub const OP_RETURN_186 : All = All { code : 0xba } ;
420
+ /// OP_CHECKSIGADD post tapscript
421
+ pub const OP_CHECKSIGADD : All = All { code : 0xba } ;
422
422
/// Synonym for OP_RETURN
423
423
pub const OP_RETURN_187 : All = All { code : 0xbb } ;
424
424
/// Synonym for OP_RETURN
@@ -556,7 +556,7 @@ pub mod all {
556
556
/// Synonym for OP_RETURN
557
557
pub const OP_RETURN_254 : All = All { code : 0xfe } ;
558
558
/// Synonym for OP_RETURN
559
- pub const OP_RETURN_255 : All = All { code : 0xff } ;
559
+ pub const OP_INVALIDOPCODE : All = All { code : 0xff } ;
560
560
}
561
561
562
562
impl fmt:: Debug for All {
@@ -652,48 +652,84 @@ impl fmt::Debug for All {
652
652
all:: OP_CLTV => write ! ( f, "CLTV" ) ,
653
653
all:: OP_CSV => write ! ( f, "CSV" ) ,
654
654
All { code : x} if x >= all:: OP_NOP1 . code && x <= all:: OP_NOP10 . code => write ! ( f, "NOP{}" , x - all:: OP_NOP1 . code + 1 ) ,
655
+ all:: OP_INVALIDOPCODE => write ! ( f, "INVALIDOPCODE" ) ,
656
+ all:: OP_CHECKSIGADD => write ! ( f, "CHECKSIGADD" ) ,
655
657
All { code : x} => write ! ( f, "RETURN_{}" , x) ,
656
658
}
657
659
}
658
660
}
659
661
662
+
663
+ /// Classification context for the opcode. Some opcodes like `OP_RESERVED`
664
+ /// abort the script in [`ClassifyContext::Legacy`] context, but will act as OP_SUCCESS in tapscript
665
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
666
+ pub enum ClassifyContext {
667
+ /// Opcode used in tapscript context
668
+ TapScript ,
669
+ /// Opcode used in legacy context
670
+ Legacy ,
671
+ }
672
+
660
673
impl All {
661
674
/// Classifies an Opcode into a broad class
662
675
#[ inline]
663
- pub fn classify ( self ) -> Class {
664
- // 17 opcodes
665
- if self == all:: OP_VERIF || self == all:: OP_VERNOTIF ||
666
- self == all:: OP_CAT || self == all:: OP_SUBSTR ||
667
- self == all:: OP_LEFT || self == all:: OP_RIGHT ||
668
- self == all:: OP_INVERT || self == all:: OP_AND ||
669
- self == all:: OP_OR || self == all:: OP_XOR ||
670
- self == all:: OP_2MUL || self == all:: OP_2DIV ||
671
- self == all:: OP_MUL || self == all:: OP_DIV || self == all:: OP_MOD ||
672
- self == all:: OP_LSHIFT || self == all:: OP_RSHIFT {
673
- Class :: IllegalOp
674
- // 11 opcodes
675
- } else if self == all:: OP_NOP ||
676
- ( all:: OP_NOP1 . code <= self . code &&
677
- self . code <= all:: OP_NOP10 . code ) {
678
- Class :: NoOp
679
- // 75 opcodes
680
- } else if self == all:: OP_RESERVED || self == all:: OP_VER || self == all:: OP_RETURN ||
681
- self == all:: OP_RESERVED1 || self == all:: OP_RESERVED2 ||
682
- self . code >= all:: OP_RETURN_186 . code {
683
- Class :: ReturnOp
684
- // 1 opcode
685
- } else if self == all:: OP_PUSHNUM_NEG1 {
686
- Class :: PushNum ( -1 )
687
- // 16 opcodes
688
- } else if all:: OP_PUSHNUM_1 . code <= self . code &&
689
- self . code <= all:: OP_PUSHNUM_16 . code {
690
- Class :: PushNum ( 1 + self . code as i32 - all:: OP_PUSHNUM_1 . code as i32 )
691
- // 76 opcodes
692
- } else if self . code <= all:: OP_PUSHBYTES_75 . code {
693
- Class :: PushBytes ( self . code as u32 )
694
- // 60 opcodes
695
- } else {
696
- Class :: Ordinary ( Ordinary :: try_from_all ( self ) . unwrap ( ) )
676
+ pub fn classify ( self , ctx : ClassifyContext ) -> Class {
677
+ use self :: all:: * ;
678
+ match ( self , ctx) {
679
+ // 3 opcodes illegal in all contexts
680
+ ( OP_VERIF , _) | ( OP_VERNOTIF , _) | ( OP_INVALIDOPCODE , _) => Class :: IllegalOp ,
681
+
682
+ // 15 opcodes illegal in Legacy context
683
+ ( OP_CAT , ctx) | ( OP_SUBSTR , ctx)
684
+ | ( OP_LEFT , ctx) | ( OP_RIGHT , ctx)
685
+ | ( OP_INVERT , ctx)
686
+ | ( OP_AND , ctx) | ( OP_OR , ctx) | ( OP_XOR , ctx)
687
+ | ( OP_2MUL , ctx) | ( OP_2DIV , ctx)
688
+ | ( OP_MUL , ctx) | ( OP_DIV , ctx) | ( OP_MOD , ctx)
689
+ | ( OP_LSHIFT , ctx) | ( OP_RSHIFT , ctx) if ctx == ClassifyContext :: Legacy => Class :: IllegalOp ,
690
+
691
+ // 87 opcodes of SuccessOp class only in TapScript context
692
+ ( op, ClassifyContext :: TapScript )
693
+ if op. code == 80 || op. code == 98 ||
694
+ ( op. code >= 126 && op. code <= 129 ) ||
695
+ ( op. code >= 131 && op. code <= 134 ) ||
696
+ ( op. code >= 137 && op. code <= 138 ) ||
697
+ ( op. code >= 141 && op. code <= 142 ) ||
698
+ ( op. code >= 149 && op. code <= 153 ) ||
699
+ ( op. code >= 187 && op. code <= 254 ) => Class :: SuccessOp ,
700
+
701
+ // 11 opcodes of NoOp class
702
+ ( OP_NOP , _) => Class :: NoOp ,
703
+ ( op, _) if op. code >= OP_NOP1 . code && op. code <= OP_NOP10 . code => Class :: NoOp ,
704
+
705
+ // 1 opcode for `OP_RETURN`
706
+ ( OP_RETURN , _) => Class :: ReturnOp ,
707
+
708
+ // 4 opcodes operating equally to `OP_RETURN` only in Legacy context
709
+ ( OP_RESERVED , ctx)
710
+ | ( OP_RESERVED1 , ctx) | ( OP_RESERVED2 , ctx)
711
+ | ( OP_VER , ctx) if ctx == ClassifyContext :: Legacy => Class :: ReturnOp ,
712
+
713
+ // 71 opcodes operating equally to `OP_RETURN` only in Legacy context
714
+ ( op, ClassifyContext :: Legacy ) if op. code >= OP_CHECKSIGADD . code => Class :: ReturnOp ,
715
+
716
+ // 2 opcodes operating equally to `OP_RETURN` only in TapScript context
717
+ ( OP_CHECKMULTISIG , ClassifyContext :: TapScript )
718
+ | ( OP_CHECKMULTISIGVERIFY , ClassifyContext :: TapScript ) => Class :: ReturnOp ,
719
+
720
+ // 1 opcode of PushNum class
721
+ ( OP_PUSHNUM_NEG1 , _) => Class :: PushNum ( -1 ) ,
722
+
723
+ // 16 opcodes of PushNum class
724
+ ( op, _) if op. code >= OP_PUSHNUM_1 . code && op. code <= OP_PUSHNUM_16 . code => {
725
+ Class :: PushNum ( 1 + self . code as i32 - OP_PUSHNUM_1 . code as i32 )
726
+ } ,
727
+
728
+ // 76 opcodes of PushBytes class
729
+ ( op, _) if op. code <= OP_PUSHBYTES_75 . code => Class :: PushBytes ( self . code as u32 ) ,
730
+
731
+ // opcodes of Ordinary class: 61 for Legacy and 60 for TapScript context
732
+ ( _, _) => Class :: Ordinary ( Ordinary :: with ( self ) ) ,
697
733
}
698
734
}
699
735
@@ -743,6 +779,8 @@ pub enum Class {
743
779
PushBytes ( u32 ) ,
744
780
/// Fails the script if executed
745
781
ReturnOp ,
782
+ /// Succeeds the script even if not executed
783
+ SuccessOp ,
746
784
/// Fails the script even if not executed
747
785
IllegalOp ,
748
786
/// Does nothing
@@ -774,6 +812,13 @@ macro_rules! ordinary_opcode {
774
812
}
775
813
776
814
impl Ordinary {
815
+ fn with( b: All ) -> Self {
816
+ match b {
817
+ $( all:: $op => { Ordinary :: $op } ) ,*
818
+ _ => unreachable!( "construction of `Ordinary` type from non-ordinary opcode {}" , b) ,
819
+ }
820
+ }
821
+
777
822
/// Try to create from an All
778
823
pub fn try_from_all( b: All ) -> Option <Self > {
779
824
match b {
@@ -807,7 +852,8 @@ ordinary_opcode! {
807
852
// crypto
808
853
OP_RIPEMD160 , OP_SHA1 , OP_SHA256 , OP_HASH160 , OP_HASH256 ,
809
854
OP_CODESEPARATOR , OP_CHECKSIG , OP_CHECKSIGVERIFY ,
810
- OP_CHECKMULTISIG , OP_CHECKMULTISIGVERIFY
855
+ OP_CHECKMULTISIG , OP_CHECKMULTISIGVERIFY ,
856
+ OP_CHECKSIGADD
811
857
}
812
858
813
859
impl Ordinary {
@@ -836,6 +882,25 @@ mod tests {
836
882
}
837
883
}
838
884
885
+ #[ test]
886
+ fn classify_test ( ) {
887
+ let op174 = all:: OP_CHECKMULTISIG ;
888
+ assert_eq ! ( op174. classify( ClassifyContext :: Legacy ) , Class :: Ordinary ( Ordinary :: OP_CHECKMULTISIG ) ) ;
889
+ assert_eq ! ( op174. classify( ClassifyContext :: TapScript ) , Class :: ReturnOp ) ;
890
+
891
+ let op175 = all:: OP_CHECKMULTISIGVERIFY ;
892
+ assert_eq ! ( op175. classify( ClassifyContext :: Legacy ) , Class :: Ordinary ( Ordinary :: OP_CHECKMULTISIGVERIFY ) ) ;
893
+ assert_eq ! ( op175. classify( ClassifyContext :: TapScript ) , Class :: ReturnOp ) ;
894
+
895
+ let op186 = all:: OP_CHECKSIGADD ;
896
+ assert_eq ! ( op186. classify( ClassifyContext :: Legacy ) , Class :: ReturnOp ) ;
897
+ assert_eq ! ( op186. classify( ClassifyContext :: TapScript ) , Class :: Ordinary ( Ordinary :: OP_CHECKSIGADD ) ) ;
898
+
899
+ let op187 = all:: OP_RETURN_187 ;
900
+ assert_eq ! ( op187. classify( ClassifyContext :: Legacy ) , Class :: ReturnOp ) ;
901
+ assert_eq ! ( op187. classify( ClassifyContext :: TapScript ) , Class :: SuccessOp ) ;
902
+ }
903
+
839
904
#[ test]
840
905
fn str_roundtrip ( ) {
841
906
let mut unique = HashSet :: new ( ) ;
@@ -1025,7 +1090,7 @@ mod tests {
1025
1090
roundtrip ! ( unique, OP_NOP8 ) ;
1026
1091
roundtrip ! ( unique, OP_NOP9 ) ;
1027
1092
roundtrip ! ( unique, OP_NOP10 ) ;
1028
- roundtrip ! ( unique, OP_RETURN_186 ) ;
1093
+ roundtrip ! ( unique, OP_CHECKSIGADD ) ;
1029
1094
roundtrip ! ( unique, OP_RETURN_187 ) ;
1030
1095
roundtrip ! ( unique, OP_RETURN_188 ) ;
1031
1096
roundtrip ! ( unique, OP_RETURN_189 ) ;
@@ -1094,7 +1159,7 @@ mod tests {
1094
1159
roundtrip ! ( unique, OP_RETURN_252 ) ;
1095
1160
roundtrip ! ( unique, OP_RETURN_253 ) ;
1096
1161
roundtrip ! ( unique, OP_RETURN_254 ) ;
1097
- roundtrip ! ( unique, OP_RETURN_255 ) ;
1162
+ roundtrip ! ( unique, OP_INVALIDOPCODE ) ;
1098
1163
assert_eq ! ( unique. len( ) , 256 ) ;
1099
1164
}
1100
1165
}
0 commit comments