11//! CBOR encoding and decoding implementation.
22//! <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl>
33
4+ use coset:: CborSerializable ;
45use minicbor:: {
56 data:: { IanaTag , Tag } ,
67 Decode , Decoder , Encode , Encoder ,
78} ;
89
9- use crate :: { Choice , GeneralizedTx , Proof , PropId , TxBody , Uuid , Vote , VoterData } ;
10+ use crate :: {
11+ Choice , EventKey , EventMap , GeneralizedTx , Proof , PropId , TxBody , Uuid , Vote , VoterData ,
12+ } ;
1013
1114/// UUID CBOR tag <https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml/>.
1215const CBOR_UUID_TAG : u64 = 37 ;
@@ -15,10 +18,10 @@ const CBOR_UUID_TAG: u64 = 37;
1518const VOTE_LEN : u64 = 3 ;
1619
1720/// `TxBody` array struct length
18- const TX_BODY_LEN : u64 = 3 ;
21+ const TX_BODY_LEN : u64 = 4 ;
1922
2023/// `GeneralizedTx` array struct length
21- const GENERALIZED_TX_LEN : u64 = 1 ;
24+ const GENERALIZED_TX_LEN : u64 = 2 ;
2225
2326impl Decode < ' _ , ( ) > for GeneralizedTx {
2427 fn decode ( d : & mut Decoder < ' _ > , ( ) : & mut ( ) ) -> Result < Self , minicbor:: decode:: Error > {
@@ -29,7 +32,19 @@ impl Decode<'_, ()> for GeneralizedTx {
2932 } ;
3033
3134 let tx_body = TxBody :: decode ( d, & mut ( ) ) ?;
32- Ok ( Self { tx_body } )
35+
36+ let signature = {
37+ let sign_bytes = read_cbor_bytes ( d)
38+ . map_err ( |_| minicbor:: decode:: Error :: message ( "missing `signature` field" ) ) ?;
39+ let mut sign = coset:: CoseSign :: from_slice ( & sign_bytes) . map_err ( |_| {
40+ minicbor:: decode:: Error :: message ( "`signature` must be COSE_Sign encoded object" )
41+ } ) ?;
42+ // We don't need to hold the original encoded data of the COSE protected header
43+ sign. protected . original_data = None ;
44+ sign
45+ } ;
46+
47+ Ok ( Self { tx_body, signature } )
3348 }
3449}
3550
@@ -39,6 +54,16 @@ impl Encode<()> for GeneralizedTx {
3954 ) -> Result < ( ) , minicbor:: encode:: Error < W :: Error > > {
4055 e. array ( GENERALIZED_TX_LEN ) ?;
4156 self . tx_body . encode ( e, & mut ( ) ) ?;
57+
58+ let sign_bytes = self
59+ . signature
60+ . clone ( )
61+ . to_vec ( )
62+ . map_err ( minicbor:: encode:: Error :: message) ?;
63+ e. writer_mut ( )
64+ . write_all ( & sign_bytes)
65+ . map_err ( minicbor:: encode:: Error :: write) ?;
66+
4267 Ok ( ( ) )
4368 }
4469}
@@ -52,10 +77,12 @@ impl Decode<'_, ()> for TxBody {
5277 } ;
5378
5479 let vote_type = Uuid :: decode ( d, & mut ( ) ) ?;
80+ let event = EventMap :: decode ( d, & mut ( ) ) ?;
5581 let votes = Vec :: < Vote > :: decode ( d, & mut ( ) ) ?;
5682 let voter_data = VoterData :: decode ( d, & mut ( ) ) ?;
5783 Ok ( Self {
5884 vote_type,
85+ event,
5986 votes,
6087 voter_data,
6188 } )
@@ -68,12 +95,81 @@ impl Encode<()> for TxBody {
6895 ) -> Result < ( ) , minicbor:: encode:: Error < W :: Error > > {
6996 e. array ( TX_BODY_LEN ) ?;
7097 self . vote_type . encode ( e, & mut ( ) ) ?;
98+ self . event . encode ( e, & mut ( ) ) ?;
7199 self . votes . encode ( e, & mut ( ) ) ?;
72100 self . voter_data . encode ( e, & mut ( ) ) ?;
73101 Ok ( ( ) )
74102 }
75103}
76104
105+ impl Decode < ' _ , ( ) > for EventMap {
106+ fn decode ( d : & mut Decoder < ' _ > , ( ) : & mut ( ) ) -> Result < Self , minicbor:: decode:: Error > {
107+ let Some ( len) = d. map ( ) ? else {
108+ return Err ( minicbor:: decode:: Error :: message (
109+ "must be a defined sized map" ,
110+ ) ) ;
111+ } ;
112+
113+ let map = ( 0 ..len)
114+ . map ( |_| {
115+ let key = EventKey :: decode ( d, & mut ( ) ) ?;
116+
117+ let value = read_cbor_bytes ( d) . map_err ( |_| {
118+ minicbor:: decode:: Error :: message ( "missing event map `value` field" )
119+ } ) ?;
120+ Ok ( ( key, value) )
121+ } )
122+ . collect :: < Result < _ , _ > > ( ) ?;
123+
124+ Ok ( EventMap ( map) )
125+ }
126+ }
127+
128+ impl Encode < ( ) > for EventMap {
129+ fn encode < W : minicbor:: encode:: Write > (
130+ & self , e : & mut Encoder < W > , ( ) : & mut ( ) ,
131+ ) -> Result < ( ) , minicbor:: encode:: Error < W :: Error > > {
132+ e. map ( self . 0 . len ( ) as u64 ) ?;
133+
134+ for ( key, value) in & self . 0 {
135+ key. encode ( e, & mut ( ) ) ?;
136+
137+ e. writer_mut ( )
138+ . write_all ( value)
139+ . map_err ( minicbor:: encode:: Error :: write) ?;
140+ }
141+
142+ Ok ( ( ) )
143+ }
144+ }
145+
146+ impl Decode < ' _ , ( ) > for EventKey {
147+ fn decode ( d : & mut Decoder < ' _ > , ( ) : & mut ( ) ) -> Result < Self , minicbor:: decode:: Error > {
148+ let pos = d. position ( ) ;
149+ // try to decode as int
150+ if let Ok ( i) = d. int ( ) {
151+ Ok ( EventKey :: Int ( i) )
152+ } else {
153+ // try to decode as text
154+ d. set_position ( pos) ;
155+ let str = d. str ( ) ?;
156+ Ok ( EventKey :: Text ( str. to_string ( ) ) )
157+ }
158+ }
159+ }
160+
161+ impl Encode < ( ) > for EventKey {
162+ fn encode < W : minicbor:: encode:: Write > (
163+ & self , e : & mut Encoder < W > , ( ) : & mut ( ) ,
164+ ) -> Result < ( ) , minicbor:: encode:: Error < W :: Error > > {
165+ match self {
166+ EventKey :: Int ( i) => e. int ( * i) ?,
167+ EventKey :: Text ( s) => e. str ( s) ?,
168+ } ;
169+ Ok ( ( ) )
170+ }
171+ }
172+
77173impl Decode < ' _ , ( ) > for VoterData {
78174 fn decode ( d : & mut Decoder < ' _ > , ( ) : & mut ( ) ) -> Result < Self , minicbor:: decode:: Error > {
79175 let tag = d. tag ( ) ?;
@@ -238,9 +334,23 @@ impl Encode<()> for PropId {
238334 }
239335}
240336
337+ /// Reads CBOR bytes from the decoder and returns them as bytes.
338+ fn read_cbor_bytes ( d : & mut Decoder < ' _ > ) -> Result < Vec < u8 > , minicbor:: decode:: Error > {
339+ let start = d. position ( ) ;
340+ d. skip ( ) ?;
341+ let end = d. position ( ) ;
342+ let bytes = d
343+ . input ( )
344+ . get ( start..end)
345+ . ok_or ( minicbor:: decode:: Error :: end_of_input ( ) ) ?
346+ . to_vec ( ) ;
347+ Ok ( bytes)
348+ }
349+
241350#[ cfg( test) ]
242351mod tests {
243352 use proptest:: { prelude:: any_with, sample:: size_range} ;
353+ use proptest_derive:: Arbitrary ;
244354 use test_strategy:: proptest;
245355
246356 use super :: * ;
@@ -249,6 +359,13 @@ mod tests {
249359 type PropChoice = Vec < u8 > ;
250360 type PropVote = ( Vec < PropChoice > , Vec < u8 > , Vec < u8 > ) ;
251361
362+ #[ derive( Debug , Arbitrary ) ]
363+ enum PropEventKey {
364+ Text ( String ) ,
365+ U64 ( u64 ) ,
366+ I64 ( i64 ) ,
367+ }
368+
252369 #[ proptest]
253370 fn generalized_tx_from_bytes_to_bytes_test (
254371 vote_type : Vec < u8 > ,
@@ -262,25 +379,39 @@ mod tests {
262379 ) ,
263380 ) ) ) ]
264381 votes : Vec < PropVote > ,
382+ event : Vec < ( PropEventKey , u64 ) > ,
265383 voter_data : Vec < u8 > ,
266384 ) {
267- let generalized_tx = GeneralizedTx {
268- tx_body : TxBody {
269- vote_type : Uuid ( vote_type) ,
270- votes : votes
271- . into_iter ( )
272- . map ( |( choices, proof, prop_id) | {
273- Vote {
274- choices : choices. into_iter ( ) . map ( Choice ) . collect ( ) ,
275- proof : Proof ( proof) ,
276- prop_id : PropId ( prop_id) ,
277- }
278- } )
279- . collect ( ) ,
280- voter_data : VoterData ( voter_data) ,
281- } ,
385+ let event = event
386+ . into_iter ( )
387+ . map ( |( key, val) | {
388+ let key = match key {
389+ PropEventKey :: Text ( key) => EventKey :: Text ( key) ,
390+ PropEventKey :: U64 ( val) => EventKey :: Int ( val. into ( ) ) ,
391+ PropEventKey :: I64 ( val) => EventKey :: Int ( val. into ( ) ) ,
392+ } ;
393+ let value = val. to_bytes ( ) . unwrap ( ) ;
394+ ( key, value)
395+ } )
396+ . collect ( ) ;
397+ let tx_body = TxBody {
398+ vote_type : Uuid ( vote_type) ,
399+ event : EventMap ( event) ,
400+ votes : votes
401+ . into_iter ( )
402+ . map ( |( choices, proof, prop_id) | {
403+ Vote {
404+ choices : choices. into_iter ( ) . map ( Choice ) . collect ( ) ,
405+ proof : Proof ( proof) ,
406+ prop_id : PropId ( prop_id) ,
407+ }
408+ } )
409+ . collect ( ) ,
410+ voter_data : VoterData ( voter_data) ,
282411 } ;
283412
413+ let generalized_tx = GeneralizedTx :: new ( tx_body) ;
414+
284415 let bytes = generalized_tx. to_bytes ( ) . unwrap ( ) ;
285416 let decoded = GeneralizedTx :: from_bytes ( & bytes) . unwrap ( ) ;
286417 assert_eq ! ( generalized_tx, decoded) ;
0 commit comments