@@ -63,17 +63,21 @@ pub struct DecodedBlockData(Vec<u8>);
6363#[ derive( Debug , Clone , PartialEq ) ]
6464pub struct EncodedBlockData ( pub Vec < u8 > ) ;
6565
66+ /// Encoded genesis Block contents as cbor, used for hash validation
67+ #[ derive( Debug , Clone , PartialEq ) ]
68+ pub struct EncodedGenesisBlockContents ( pub Vec < u8 > ) ;
69+
6670/// Signatures
6771#[ derive( Debug , Clone , PartialEq ) ]
6872pub struct Signatures ( Vec < Signature > ) ;
6973
7074/// Validator's keys defined in the corresponding certificates referenced by the validator.
7175pub struct ValidatorKeys ( pub Vec < [ u8 ; SECRET_KEY_LENGTH ] > ) ;
7276
73- /// Decoder block
77+ /// Decoded block
7478pub type DecodedBlock = ( DecodedBlockHeader , DecodedBlockData , Signatures ) ;
7579
76- /// Decoder block header
80+ /// Decoded block header
7781pub type DecodedBlockHeader = (
7882 ChainId ,
7983 Height ,
@@ -86,9 +90,25 @@ pub type DecodedBlockHeader = (
8690 BlockHeaderSize ,
8791) ;
8892
93+ /// Decoded Genesis block
94+ pub type DecodedBlockGenesis = (
95+ ChainId ,
96+ Height ,
97+ BlockTimeStamp ,
98+ PreviousBlockHash ,
99+ LedgerType ,
100+ PurposeId ,
101+ Validator ,
102+ BlockHeaderSize ,
103+ EncodedGenesisBlockContents ,
104+ ) ;
105+
89106/// Encoded whole block including block header, cbor encoded block data and signatures.
90107pub type EncodedBlock = Vec < u8 > ;
91108
109+ /// Encoded genesis block, see genesis_to_prev_hash
110+ pub type EncodedGenesisBlock = Vec < u8 > ;
111+
92112/// Choice of hash function:
93113/// must be the same as the hash of the previous block.
94114pub enum HashFunction {
@@ -98,7 +118,7 @@ pub enum HashFunction {
98118 Blake2b ,
99119}
100120
101- /// Encode block
121+ /// Encode standard block
102122pub fn encode_block (
103123 block_hdr_cbor : Vec < u8 > , block_data : EncodedBlockData , validator_keys : ValidatorKeys ,
104124 hasher : HashFunction ,
@@ -134,13 +154,15 @@ pub fn encode_block(
134154 encoder. bytes ( sig) ?;
135155 }
136156
137- Ok ( [ block_hdr_cbor, encoder. writer ( ) . to_vec ( ) ] . concat ( ) )
157+ let block_data_with_sigs = encoder. writer ( ) . to_vec ( ) ;
158+ // block hdr + block data + sigs
159+ let encoded_block = [ block_hdr_cbor, block_data_with_sigs] . concat ( ) ;
160+
161+ Ok ( encoded_block)
138162}
139163
140- /// Decoded block
164+ /// Decoded standard block
141165pub fn decode_block ( encoded_block : Vec < u8 > ) -> anyhow:: Result < DecodedBlock > {
142- // Decode cbor to bytes
143-
144166 // Decoded block hdr
145167 let block_hdr: DecodedBlockHeader = decode_block_header ( encoded_block. clone ( ) ) ?;
146168
@@ -211,6 +233,7 @@ pub fn encode_block_header(
211233
212234 Ok ( encoder. writer ( ) . to_vec ( ) )
213235}
236+
214237/// Decode block header
215238pub fn decode_block_header ( block : Vec < u8 > ) -> anyhow:: Result < DecodedBlockHeader > {
216239 // Decode cbor to bytes
@@ -301,6 +324,146 @@ pub fn decode_block_header(block: Vec<u8>) -> anyhow::Result<DecodedBlockHeader>
301324 ) )
302325}
303326
327+ /// Encode genesis block
328+ pub fn encode_genesis (
329+ chain_id : ChainId , ts : BlockTimeStamp , ledger_type : LedgerType , pid : PurposeId ,
330+ validator : Validator , hasher : HashFunction ,
331+ ) -> anyhow:: Result < Vec < u8 > > {
332+ // Genesis block MUST have 0 value
333+ const BLOCK_HEIGHT : u32 = 0 ;
334+
335+ let out: Vec < u8 > = Vec :: new ( ) ;
336+ let mut encoder = minicbor:: Encoder :: new ( out) ;
337+
338+ encoder. bytes ( & chain_id. 0 . to_bytes ( ) ) ?;
339+ encoder. bytes ( & BLOCK_HEIGHT . to_be_bytes ( ) ) ?;
340+ encoder. bytes ( & ts. 0 . to_be_bytes ( ) ) ?;
341+ encoder. bytes ( ledger_type. 0 . as_bytes ( ) ) ?;
342+ encoder. bytes ( & pid. 0 . to_bytes ( ) ) ?;
343+
344+ // marks how many validators for decoding side.
345+ encoder. bytes ( & validator. 0 . len ( ) . to_be_bytes ( ) ) ?;
346+ for validator in validator. 0 . iter ( ) {
347+ encoder. bytes ( & validator. 0 ) ?;
348+ }
349+
350+ // Get hash of the genesis_to_prev_hash bytes i.e hash of itself
351+ let genesis_prev_bytes = encoder. writer ( ) . to_vec ( ) ;
352+
353+ // Size of encoded contents which is hashed
354+ encoder. bytes ( & genesis_prev_bytes. len ( ) . to_be_bytes ( ) ) ?;
355+
356+ let genesis_prev_hash = match hasher {
357+ HashFunction :: Blake3 => blake3 ( & genesis_prev_bytes) ?. to_vec ( ) ,
358+ HashFunction :: Blake2b => blake2b_512 ( & genesis_prev_bytes) ?. to_vec ( ) ,
359+ } ;
360+
361+ // prev_block_id for the Genesis block MUST be a hash of the genesis_to_prev_hash bytes
362+ // last 64 bytes (depending on given hash function) of encoding are the hash of the genesis contents
363+ encoder. bytes ( & genesis_prev_hash. as_slice ( ) ) ?;
364+
365+ Ok ( encoder. writer ( ) . to_vec ( ) )
366+ }
367+
368+ /// Decode genesis
369+ pub fn decode_genesis_block ( genesis_block : Vec < u8 > ) -> anyhow:: Result < DecodedBlockGenesis > {
370+ let binding = genesis_block. clone ( ) ;
371+ let mut cbor_decoder = minicbor:: Decoder :: new ( & binding) ;
372+
373+ // Raw chain_id
374+ let chain_id = ChainId ( Ulid :: from_bytes (
375+ cbor_decoder
376+ . bytes ( )
377+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for chain id : {e}" ) ) ) ?
378+ . try_into ( ) ?,
379+ ) ) ;
380+
381+ // Raw Block height
382+ let block_height = Height ( u32:: from_be_bytes (
383+ cbor_decoder
384+ . bytes ( )
385+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for block height : {e}" ) ) ) ?
386+ . try_into ( ) ?,
387+ ) ) ;
388+
389+ // Raw time stamp
390+ let ts = BlockTimeStamp ( i64:: from_be_bytes (
391+ cbor_decoder
392+ . bytes ( )
393+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for timestamp : {e}" ) ) ) ?
394+ . try_into ( ) ?,
395+ ) ) ;
396+
397+ // Raw ledger type
398+ let ledger_type = LedgerType ( Uuid :: from_bytes (
399+ cbor_decoder
400+ . bytes ( )
401+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for ledger type : {e}" ) ) ) ?
402+ . try_into ( ) ?,
403+ ) ) ;
404+
405+ // Raw purpose id
406+ let purpose_id = PurposeId ( Ulid :: from_bytes (
407+ cbor_decoder
408+ . bytes ( )
409+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for purpose id : {e}" ) ) ) ?
410+ . try_into ( ) ?,
411+ ) ) ;
412+
413+ // Number of validators
414+ let number_of_validators = usize:: from_be_bytes (
415+ cbor_decoder
416+ . bytes ( )
417+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for number of validators : {e}" ) ) ) ?
418+ . try_into ( ) ?,
419+ ) ;
420+
421+ // Extract validators
422+ let mut validators = Vec :: new ( ) ;
423+ for _validator in 0 ..number_of_validators {
424+ let validator_kid: [ u8 ; 16 ] = cbor_decoder
425+ . bytes ( )
426+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for validators : {e}" ) ) ) ?
427+ . try_into ( ) ?;
428+
429+ validators. push ( Kid ( validator_kid) ) ;
430+ }
431+
432+ // Size of encoded contents
433+ let encoded_content_size = usize:: from_be_bytes (
434+ cbor_decoder
435+ . bytes ( )
436+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for encoded contents size : {e}" ) ) ) ?
437+ . try_into ( ) ?,
438+ ) ;
439+
440+ // prev_block_id for the Genesis block MUST be a hash of the genesis_to_prev_hash bytes
441+ // last 64 bytes (depending on hash function) of encoding are the hash of the contents
442+ let prev_block_hash = PreviousBlockHash (
443+ cbor_decoder
444+ . bytes ( )
445+ . map_err ( |e| anyhow:: anyhow!( format!( "Invalid cbor for prev block hash : {e}" ) ) ) ?
446+ . to_vec ( ) ,
447+ ) ;
448+
449+ let genesis_block_contents: Vec < u8 > = genesis_block
450+ . into_iter ( )
451+ . take ( encoded_content_size)
452+ . collect ( ) ;
453+
454+ Ok ( (
455+ chain_id,
456+ block_height,
457+ ts,
458+ prev_block_hash,
459+ ledger_type,
460+ purpose_id,
461+ Validator ( validators) ,
462+ BlockHeaderSize ( cbor_decoder. position ( ) ) ,
463+ EncodedGenesisBlockContents ( genesis_block_contents) ,
464+ ) )
465+ }
466+
304467#[ cfg( test) ]
305468mod tests {
306469 use ed25519_dalek:: { SigningKey , SECRET_KEY_LENGTH } ;
@@ -315,6 +478,8 @@ mod tests {
315478 } ;
316479
317480 use crate :: serialize:: HashFunction :: Blake2b ;
481+
482+ use super :: { decode_genesis_block, encode_genesis} ;
318483 #[ test]
319484 fn block_header_encode_decode ( ) {
320485 let kid_a: [ u8 ; 16 ] = hex:: decode ( "00112233445566778899aabbccddeeff" )
@@ -443,4 +608,49 @@ mod tests {
443608 verifying_key. verify_strict ( & data_to_sign, & sig) . unwrap ( ) ;
444609 }
445610 }
611+
612+ #[ test]
613+ fn genesis_block_encode_decode ( ) {
614+ let kid_a: [ u8 ; 16 ] = hex:: decode ( "00112233445566778899aabbccddeeff" )
615+ . unwrap ( )
616+ . try_into ( )
617+ . unwrap ( ) ;
618+
619+ let kid_b: [ u8 ; 16 ] = hex:: decode ( "00112233445566778899aabbccddeeff" )
620+ . unwrap ( )
621+ . try_into ( )
622+ . unwrap ( ) ;
623+
624+ let chain_id = ChainId ( Ulid :: new ( ) ) ;
625+ let block_ts = BlockTimeStamp ( 0 ) ;
626+ let ledger_type = LedgerType ( Uuid :: new_v4 ( ) ) ;
627+ let purpose_id = PurposeId ( Ulid :: new ( ) ) ;
628+ let validators = Validator ( vec ! [ Kid ( kid_a) , Kid ( kid_b) ] ) ;
629+
630+ let encoded_block_genesis = encode_genesis (
631+ chain_id,
632+ block_ts,
633+ ledger_type. clone ( ) ,
634+ purpose_id. clone ( ) ,
635+ validators. clone ( ) ,
636+ Blake2b ,
637+ )
638+ . unwrap ( ) ;
639+
640+ let decoded_genesis = decode_genesis_block ( encoded_block_genesis. clone ( ) ) . unwrap ( ) ;
641+ assert_eq ! ( decoded_genesis. 0 , chain_id) ;
642+ assert_eq ! ( decoded_genesis. 1 , Height ( 0 ) ) ;
643+ assert_eq ! ( decoded_genesis. 2 , block_ts) ;
644+ assert_eq ! ( decoded_genesis. 4 , ledger_type) ;
645+ assert_eq ! ( decoded_genesis. 5 , purpose_id) ;
646+ assert_eq ! ( decoded_genesis. 6 , validators) ;
647+
648+ // prev_block_id for the Genesis block MUST be a hash of the genesis_to_prev_hash bytes
649+ let prev_block_hash = decoded_genesis. 3 . 0 ;
650+
651+ // last 64 bytes of encoding are the hash of the contents
652+ let prev_block_from_original_encoding = & encoded_block_genesis[ 110 ..] ;
653+
654+ assert_eq ! ( prev_block_hash, prev_block_from_original_encoding) ;
655+ }
446656}
0 commit comments