@@ -52,36 +52,60 @@ impl PlutusScripts {
5252#[ wasm_bindgen]
5353#[ derive( Clone , Debug , Eq , Ord , PartialEq , PartialOrd ) ]
5454pub struct ConstrPlutusData {
55- tag : Int ,
55+ alternative : BigNum ,
5656 data : PlutusList ,
5757}
5858
5959to_from_bytes ! ( ConstrPlutusData ) ;
6060
6161#[ wasm_bindgen]
6262impl ConstrPlutusData {
63- pub fn tag ( & self ) -> Int {
64- self . tag . clone ( )
63+ pub fn alternative ( & self ) -> BigNum {
64+ self . alternative . clone ( )
6565 }
6666
6767 pub fn data ( & self ) -> PlutusList {
6868 self . data . clone ( )
6969 }
7070
71- pub fn new ( tag : Int , data : & PlutusList ) -> Self {
71+ pub fn new ( alternative : & BigNum , data : & PlutusList ) -> Self {
7272 Self {
73- tag ,
73+ alternative : alternative . clone ( ) ,
7474 data : data. clone ( ) ,
7575 }
7676 }
7777}
7878
7979impl ConstrPlutusData {
80- fn is_tag_compact ( tag : i128 ) -> bool {
81- ( tag >= 121 && tag <= 127 ) || ( tag >= 1280 && tag <= 1400 )
80+ // see: https://github.com/input-output-hk/plutus/blob/1f31e640e8a258185db01fa899da63f9018c0e85/plutus-core/plutus-core/src/PlutusCore/Data.hs#L61
81+ // We don't directly serialize the alternative in the tag, instead the scheme is:
82+ // - Alternatives 0-6 -> tags 121-127, followed by the arguments in a list
83+ // - Alternatives 7-127 -> tags 1280-1400, followed by the arguments in a list
84+ // - Any alternatives, including those that don't fit in the above -> tag 102 followed by a list containing
85+ // an unsigned integer for the actual alternative, and then the arguments in a (nested!) list.
86+ const GENERAL_FORM_TAG : u64 = 102 ;
87+
88+ // None -> needs general tag serialization, not compact
89+ fn alternative_to_compact_cbor_tag ( alt : u64 ) -> Option < u64 > {
90+ if alt <= 6 {
91+ Some ( 121 + alt)
92+ } else if alt >= 7 && alt <= 127 {
93+ Some ( 1280 - 7 + alt)
94+ } else {
95+ None
96+ }
8297 }
8398
84- const GENERAL_FORM_TAG : u64 = 102 ;
99+ // None -> General tag(=102) OR Invalid CBOR tag for this scheme
100+ fn compact_cbor_tag_to_alternative ( cbor_tag : u64 ) -> Option < u64 > {
101+ if cbor_tag >= 121 && cbor_tag <= 127 {
102+ Some ( cbor_tag - 121 )
103+ } else if cbor_tag >= 1280 && cbor_tag <= 1400 {
104+ Some ( cbor_tag - 1280 + 7 )
105+ } else {
106+ None
107+ }
108+ }
85109}
86110
87111const COST_MODEL_OP_COUNT : usize = 166 ;
@@ -606,15 +630,15 @@ impl Deserialize for PlutusScripts {
606630// TODO: write tests for this hand-coded implementation?
607631impl cbor_event:: se:: Serialize for ConstrPlutusData {
608632 fn serialize < ' se , W : Write > ( & self , serializer : & ' se mut Serializer < W > ) -> cbor_event:: Result < & ' se mut Serializer < W > > {
609- if Self :: is_tag_compact ( self . tag . 0 ) {
633+ if let Some ( compact_tag ) = Self :: alternative_to_compact_cbor_tag ( from_bignum ( & self . alternative ) ) {
610634 // compact form
611- serializer. write_tag ( self . tag . 0 as u64 ) ?;
635+ serializer. write_tag ( compact_tag as u64 ) ?;
612636 self . data . serialize ( serializer)
613637 } else {
614638 // general form
615639 serializer. write_tag ( Self :: GENERAL_FORM_TAG ) ?;
616640 serializer. write_array ( cbor_event:: Len :: Len ( 2 ) ) ?;
617- self . tag . serialize ( serializer) ?;
641+ self . alternative . serialize ( serializer) ?;
618642 self . data . serialize ( serializer)
619643 }
620644 }
@@ -623,13 +647,13 @@ impl cbor_event::se::Serialize for ConstrPlutusData {
623647impl Deserialize for ConstrPlutusData {
624648 fn deserialize < R : BufRead + Seek > ( raw : & mut Deserializer < R > ) -> Result < Self , DeserializeError > {
625649 ( || -> Result < _ , DeserializeError > {
626- let ( tag , data) = match raw. tag ( ) ? {
650+ let ( alternative , data) = match raw. tag ( ) ? {
627651 // general form
628652 Self :: GENERAL_FORM_TAG => {
629653 let len = raw. array ( ) ?;
630654 let mut read_len = CBORReadLen :: new ( len) ;
631655 read_len. read_elems ( 2 ) ?;
632- let tag = Int :: deserialize ( raw) ?;
656+ let alternative = BigNum :: deserialize ( raw) ?;
633657 let data = ( || -> Result < _ , DeserializeError > {
634658 Ok ( PlutusList :: deserialize ( raw) ?)
635659 } ) ( ) . map_err ( |e| e. annotate ( "datas" ) ) ?;
@@ -640,17 +664,22 @@ impl Deserialize for ConstrPlutusData {
640664 _ => return Err ( DeserializeFailure :: EndingBreakMissing . into ( ) ) ,
641665 } ,
642666 }
643- ( tag , data)
667+ ( alternative , data)
644668 } ,
645669 // concise form
646- tag if Self :: is_tag_compact ( tag. into ( ) ) => ( Int :: new ( & to_bignum ( tag) ) , PlutusList :: deserialize ( raw) ?) ,
647- invalid_tag => return Err ( DeserializeFailure :: TagMismatch {
648- found : invalid_tag,
649- expected : Self :: GENERAL_FORM_TAG ,
650- } . into ( ) ) ,
670+ tag => {
671+ if let Some ( alternative) = Self :: compact_cbor_tag_to_alternative ( tag) {
672+ ( to_bignum ( alternative) , PlutusList :: deserialize ( raw) ?)
673+ } else {
674+ return Err ( DeserializeFailure :: TagMismatch {
675+ found : tag,
676+ expected : Self :: GENERAL_FORM_TAG ,
677+ } . into ( ) ) ;
678+ }
679+ } ,
651680 } ;
652681 Ok ( ConstrPlutusData {
653- tag ,
682+ alternative ,
654683 data,
655684 } )
656685 } ) ( ) . map_err ( |e| e. annotate ( "ConstrPlutusData" ) )
@@ -1131,3 +1160,24 @@ impl Deserialize for Strings {
11311160 Ok ( Self ( arr) )
11321161 }
11331162}
1163+
1164+ #[ cfg( test) ]
1165+ mod tests {
1166+ use super :: * ;
1167+
1168+ #[ test]
1169+ pub fn plutus_constr_data ( ) {
1170+ let constr_0 = PlutusData :: new_constr_plutus_data (
1171+ & ConstrPlutusData :: new ( & to_bignum ( 0 ) , & PlutusList :: new ( ) )
1172+ ) ;
1173+ let constr_0_hash = hex:: encode ( hash_plutus_data ( & constr_0) . to_bytes ( ) ) ;
1174+ assert_eq ! ( constr_0_hash, "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec" ) ;
1175+ let constr_0_roundtrip = PlutusData :: from_bytes ( constr_0. to_bytes ( ) ) . unwrap ( ) ;
1176+ assert_eq ! ( constr_0, constr_0_roundtrip) ;
1177+ let constr_1854 = PlutusData :: new_constr_plutus_data (
1178+ & ConstrPlutusData :: new ( & to_bignum ( 1854 ) , & PlutusList :: new ( ) )
1179+ ) ;
1180+ let constr_1854_roundtrip = PlutusData :: from_bytes ( constr_1854. to_bytes ( ) ) . unwrap ( ) ;
1181+ assert_eq ! ( constr_1854, constr_1854_roundtrip) ;
1182+ }
1183+ }
0 commit comments