LCOV - code coverage report
Current view: top level - flamenco/alpenglow - fd_block_marker_serde.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 358 358 100.0 %
Date: 2026-09-17 04:28:31 Functions: 17 17 100.0 %

          Line data    Source code
       1             : #include "fd_block_marker_serde.h"
       2             : 
       3             : #include "../../ballet/txn/fd_compact_u16.h"
       4             : 
       5       20943 : #define FAIL( cond, err ) do { if( FD_UNLIKELY( cond ) ) return FD_BLOCK_MARKER_DE_ERR_##err; } while( 0 )
       6             : 
       7             : /* BlockComponent:       https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L486-L488
       8             :    VersionedBlockMarker: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L435-L438
       9             :    LengthPrefixed:       https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L172-L175 */
      10             : 
      11             : struct marker_serde {
      12             :   ulong         entry_cnt; /* BlockComponent::BlockMarker            (u64, always 0) */
      13             :   ushort        version;   /* VersionedBlockMarker::V1               (u16 tag)       */
      14             :   uchar         tag;       /* BlockMarkerV1                          (u8 tag)        */
      15             :   ushort        length;    /* LengthPrefixed<VersionedBlock*>::len   (u16)           */
      16             :   uchar const * payload;   /* LengthPrefixed<VersionedBlock*>::inner (length bytes)  */
      17             : };
      18             : typedef struct marker_serde marker_serde_t;
      19             : 
      20             : /* VersionedBlockHeader: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L364-L367
      21             :    BlockHeaderV1:        https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L251-L254 */
      22             : 
      23             : struct header_serde {
      24             :   uchar         version;         /* VersionedBlockHeader::V1       (u8 tag, #[wincode(tag = 1)]) */
      25             :   ulong         parent_slot;     /* BlockHeaderV1::parent_slot     (Slot)                        */
      26             :   uchar const * parent_block_id; /* BlockHeaderV1::parent_block_id (Hash)                        */
      27             : };
      28             : typedef struct header_serde header_serde_t;
      29             : 
      30             : /* VersionedUpdateParent: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L371-L374
      31             :    UpdateParentV1:        https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L257-L260 */
      32             : 
      33             : struct update_parent_serde {
      34             :   uchar         version;             /* VersionedUpdateParent::V1           (u8 tag, #[wincode(tag = 1)]) */
      35             :   ulong         new_parent_slot;     /* UpdateParentV1::new_parent_slot     (Slot)                        */
      36             :   uchar const * new_parent_block_id; /* UpdateParentV1::new_parent_block_id (Hash)                        */
      37             : };
      38             : typedef struct update_parent_serde update_parent_serde_t;
      39             : 
      40             : /* solana_signer_store base2 bitmap: one byte version tag, u16 bit count,
      41             :    then the bits packed LSB first, bit i naming rank i.  Only base2 goes
      42             :    in a footer; base3 pairs are a votor wire shape (ag_bls_serde.h). */
      43             : 
      44             : struct bitmap_serde {
      45             :   uchar         version;    /* solana_signer_store::Version  (u8 tag), base2 */
      46             :   ushort        bit_cnt;    /* solana_signer_store::num_bits (u16)           */
      47             :   uchar const * payload;    /* solana_signer_store::data_bytes               */
      48             :   ulong         payload_sz; /* bit_cnt rounded up to a byte                  */
      49             : };
      50             : typedef struct bitmap_serde bitmap_serde_t;
      51             : 
      52             : /* VotesAggregate: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L324-L328
      53             :    Not the wire framing in ag_cert_serde.h: the signature is compressed
      54             :    and the bitmap sits under a u16 byte count rather than a u64. */
      55             : 
      56             : struct votes_aggregate_serde {
      57             :   uchar const * signature; /* VotesAggregate::signature  (BLSSignatureCompressed)         */
      58             :   ushort        bitmap_sz; /* VotesAggregate::bitmap len (FixIntLen<u16>)                 */
      59             :   uchar const * bitmap;    /* VotesAggregate::bitmap     (WincodeVec<u8, FixIntLen<u16>>) */
      60             : };
      61             : typedef struct votes_aggregate_serde votes_aggregate_serde_t;
      62             : 
      63             : /* BlockFinalizationCert: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L299-L304 */
      64             : 
      65             : struct final_cert_serde {
      66             :   ulong         slot;                /* BlockFinalizationCert::slot            (Slot)                             */
      67             :   uchar const * block_id;            /* BlockFinalizationCert::block_id        (Hash)                             */
      68             :   uchar const * final_aggregate;     /* BlockFinalizationCert::final_aggregate (VotesAggregate)                   */
      69             :   uchar         has_notar_aggregate; /* BlockFinalizationCert::notar_aggregate (Option<VotesAggregate> tag)       */
      70             :   uchar const * notar_aggregate;     /* BlockFinalizationCert::notar_aggregate (VotesAggregate, NULL when absent) */
      71             : };
      72             : typedef struct final_cert_serde final_cert_serde_t;
      73             : 
      74             : /* SkipRewardCertificate: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/votor-messages/src/reward_certificate.rs#L34-L42
      75             :    The bitmap byte count is a ShortU16, not the aggregate's u16. */
      76             : 
      77             : struct skip_reward_cert_serde {
      78             :   ulong         slot;      /* SkipRewardCertificate::slot       (Slot)                     */
      79             :   uchar const * signature; /* SkipRewardCertificate::signature  (BLSSignatureCompressed)   */
      80             :   ushort        bitmap_sz; /* SkipRewardCertificate::bitmap len (ShortU16)                 */
      81             :   uchar const * bitmap;    /* SkipRewardCertificate::bitmap     (WincodeVec<u8, ShortU16>) */
      82             : };
      83             : typedef struct skip_reward_cert_serde skip_reward_cert_serde_t;
      84             : 
      85             : /* NotarRewardCertificate: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/votor-messages/src/reward_certificate.rs#L75-L85 */
      86             : 
      87             : struct notar_reward_cert_serde {
      88             :   ulong         slot;      /* NotarRewardCertificate::slot       (Slot)                     */
      89             :   uchar const * block_id;  /* NotarRewardCertificate::block_id   (Hash)                     */
      90             :   uchar const * signature; /* NotarRewardCertificate::signature  (BLSSignatureCompressed)   */
      91             :   ushort        bitmap_sz; /* NotarRewardCertificate::bitmap len (ShortU16)                 */
      92             :   uchar const * bitmap;    /* NotarRewardCertificate::bitmap     (WincodeVec<u8, ShortU16>) */
      93             : };
      94             : typedef struct notar_reward_cert_serde notar_reward_cert_serde_t;
      95             : 
      96             : /* VersionedBlockFooter: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L357-L360
      97             :    BlockFooterV1:        https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L240-L248
      98             :    Each Option is a one byte tag immediately followed by its own body
      99             :    when present, so a cert's length shifts the next tag. */
     100             : 
     101             : struct footer_serde {
     102             :   uchar         version;                   /* VersionedBlockFooter::V1                 (u8 tag, #[wincode(tag = 1)])        */
     103             :   uchar const * bank_hash;                 /* BlockFooterV1::bank_hash                 (Hash)                               */
     104             :   ulong         block_producer_time_nanos; /* BlockFooterV1::block_producer_time_nanos (u64)                                */
     105             :   uchar         user_agent_len;            /* BlockFooterV1::block_user_agent len      (FixIntLen<u8>)                      */
     106             :   uchar const * user_agent;                /* BlockFooterV1::block_user_agent          (WincodeVec<u8, FixIntLen<u8>>)      */
     107             :   uchar         has_block_final_cert;      /* BlockFooterV1::block_final_cert          (Option<BlockFinalizationCert> tag)  */
     108             :   uchar const * block_final_cert;          /* BlockFooterV1::block_final_cert          (BlockFinalizationCert)              */
     109             :   uchar         has_skip_reward_cert;      /* BlockFooterV1::skip_reward_cert          (Option<SkipRewardCertificate> tag)  */
     110             :   uchar const * skip_reward_cert;          /* BlockFooterV1::skip_reward_cert          (SkipRewardCertificate)              */
     111             :   uchar         has_notar_reward_cert;     /* BlockFooterV1::notar_reward_cert         (Option<NotarRewardCertificate> tag) */
     112             :   uchar const * notar_reward_cert;         /* BlockFooterV1::notar_reward_cert         (NotarRewardCertificate)             */
     113             : };
     114             : typedef struct footer_serde footer_serde_t;
     115             : 
     116             : static ulong
     117             : header_ser( fd_block_header_t const * header,
     118          30 :             uchar *                   buf ) {
     119          30 :   header_serde_t hdr;
     120             : 
     121          30 :   hdr.version         = (uchar)1;
     122          30 :   hdr.parent_slot     = header->parent_slot;
     123          30 :   hdr.parent_block_id = header->parent_block_id.uc;
     124             : 
     125          30 :   ulong off = 0UL;
     126          30 :   buf[ off ] = hdr.version;                                  off += sizeof(uchar);
     127          30 :   FD_STORE( ulong, buf+off, hdr.parent_slot );               off += sizeof(ulong);
     128          30 :   memcpy( buf+off, hdr.parent_block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
     129             : 
     130          30 :   return off;
     131          30 : }
     132             : 
     133             : /* bitmap_ser writes the base2 bitmap naming the ranks in signer_set,
     134             :    exactly nbits wide: a footer cert re-emits the width it carries, so
     135             :    one that came off the wire goes back out byte for byte.  Returns the
     136             :    bytes written, or 0UL if nbits passes AG_VAT_MAX (which sizes
     137             :    FD_BLOCK_FOOTER_SER_MAX) or a rank sits at or past nbits. */
     138             : 
     139             : static ulong
     140             : bitmap_ser( ulong                nbits,
     141             :             fd_bls_set_t const * signer_set,
     142         120 :             uchar *              buf ) {
     143         120 :   ulong bits = fd_ulong_min( FD_BLS_SET_MAX, fd_bls_set_last( signer_set )+1UL ); /* one past the highest rank, 0 when empty */
     144         120 :   if( FD_UNLIKELY( nbits>AG_VAT_MAX || bits>nbits ) ) return 0UL;
     145             : 
     146         114 :   bitmap_serde_t bm;
     147             : 
     148         114 :   bm.version    = (uchar)0; /* base2 */
     149         114 :   bm.bit_cnt    = (ushort)nbits;
     150         114 :   bm.payload    = NULL; /* written straight into buf below */
     151         114 :   bm.payload_sz = FD_BLOCK_BITMAP_SER_SZ( nbits )-FD_BLOCK_BITMAP_SER_HDR_SZ;
     152             : 
     153         114 :   ulong off = 0UL;
     154         114 :   buf[ off ] = bm.version;                 off += sizeof(uchar);
     155         114 :   FD_STORE( ushort, buf+off, bm.bit_cnt ); off += sizeof(ushort);
     156         114 :   fd_memset( buf+off, 0, bm.payload_sz );
     157       46374 :   for( ulong i=0UL; i<bits; i++ ) {
     158       46260 :     if( fd_bls_set_test( signer_set, i ) ) buf[ off+(i>>3) ] |= (uchar)( 1U << (i&7U) );
     159       46260 :   }
     160         114 :                                            off += bm.payload_sz;
     161         114 :   return off;
     162         120 : }
     163             : 
     164             : static ulong
     165             : votes_aggregate_ser( fd_block_footer_cert_t const * cert,
     166          63 :                      uchar *                        buf ) {
     167          63 :   votes_aggregate_serde_t aggregate;
     168             : 
     169          63 :   aggregate.signature = cert->sig; /* already compressed */
     170          63 :   aggregate.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
     171          63 :   aggregate.bitmap    = NULL; /* written straight into buf by bitmap_ser below */
     172             : 
     173          63 :   ulong off = 0UL;
     174          63 :   ulong sz;
     175          63 :   memcpy( buf+off, aggregate.signature, FD_BLS_SIG_COMPRESSED_SZ );                     off += FD_BLS_SIG_COMPRESSED_SZ;
     176          63 :   FD_STORE( ushort, buf+off, aggregate.bitmap_sz );                                     off += sizeof(ushort);
     177          63 :   if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
     178          60 :                                                                                         off += sz;
     179          60 :   return off;
     180          63 : }
     181             : 
     182             : static ulong
     183             : final_cert_ser( fd_block_footer_t const * footer,
     184          45 :                 uchar *                   buf ) {
     185          45 :   int is_fast = footer->has_fast_final_cert;
     186             :   /* one block_id goes on the wire, so the two certs of a slow
     187             :      finalization must name one slot */
     188          45 :   if( FD_UNLIKELY( !is_fast && footer->final_cert.slot!=footer->notar_cert.slot ) ) return 0UL;
     189             : 
     190          42 :   final_cert_serde_t cert;
     191             : 
     192          42 :   cert.slot                = is_fast ? footer->fast_final_cert.slot        : footer->final_cert.slot;
     193          42 :   cert.block_id            = is_fast ? footer->fast_final_cert.block_id.uc : footer->notar_cert.block_id.uc;
     194          42 :   cert.final_aggregate     = NULL; /* written straight into buf by votes_aggregate_ser below */
     195          42 :   cert.has_notar_aggregate = (uchar)!is_fast;
     196          42 :   cert.notar_aggregate     = NULL;
     197             : 
     198          42 :   ulong off = 0UL;
     199          42 :   ulong sz;
     200          42 :   FD_STORE( ulong, buf+off, cert.slot );                                                                          off += sizeof(ulong);
     201          42 :   memcpy( buf+off, cert.block_id, sizeof(fd_hash_t) );                                                            off += sizeof(fd_hash_t);
     202          42 :   if( FD_UNLIKELY( !(sz=votes_aggregate_ser( is_fast ? &footer->fast_final_cert : &footer->final_cert, buf+off )) ) ) return 0UL;
     203          39 :                                                                                                                   off += sz;
     204          39 :   buf[ off ] = cert.has_notar_aggregate;                                                                          off += sizeof(uchar);
     205          39 :   if( cert.has_notar_aggregate ) {
     206          21 :     if( FD_UNLIKELY( !(sz=votes_aggregate_ser( &footer->notar_cert, buf+off )) ) ) return 0UL;
     207          21 :                                                                                                                   off += sz;
     208          21 :   }
     209          39 :   return off;
     210          39 : }
     211             : 
     212             : static ulong
     213             : skip_reward_cert_ser( fd_block_footer_cert_t const * cert,
     214          30 :                       uchar *                        buf ) {
     215          30 :   skip_reward_cert_serde_t reward;
     216             : 
     217          30 :   reward.slot      = cert->slot;
     218          30 :   reward.signature = cert->sig; /* already compressed */
     219          30 :   reward.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
     220          30 :   reward.bitmap    = NULL; /* written straight into buf by bitmap_ser below */
     221             : 
     222          30 :   ulong off = 0UL;
     223          30 :   ulong sz;
     224          30 :   FD_STORE( ulong, buf+off, reward.slot );                                              off += sizeof(ulong);
     225          30 :   memcpy( buf+off, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );                        off += FD_BLS_SIG_COMPRESSED_SZ;
     226          30 :                                                                                         off += fd_cu16_enc( reward.bitmap_sz, buf+off );
     227          30 :   if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
     228          27 :                                                                                         off += sz;
     229          27 :   return off;
     230          30 : }
     231             : 
     232             : static ulong
     233             : notar_reward_cert_ser( fd_block_footer_cert_t const * cert,
     234          27 :                        uchar *                        buf ) {
     235          27 :   notar_reward_cert_serde_t reward;
     236             : 
     237          27 :   reward.slot      = cert->slot;
     238          27 :   reward.block_id  = cert->block_id.uc;
     239          27 :   reward.signature = cert->sig; /* already compressed */
     240          27 :   reward.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
     241          27 :   reward.bitmap    = NULL; /* written straight into buf by bitmap_ser below */
     242             : 
     243          27 :   ulong off = 0UL;
     244          27 :   ulong sz;
     245          27 :   FD_STORE( ulong, buf+off, reward.slot );                                              off += sizeof(ulong);
     246          27 :   memcpy( buf+off, reward.block_id, sizeof(fd_hash_t) );                                off += sizeof(fd_hash_t);
     247          27 :   memcpy( buf+off, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );                        off += FD_BLS_SIG_COMPRESSED_SZ;
     248          27 :                                                                                         off += fd_cu16_enc( reward.bitmap_sz, buf+off );
     249          27 :   if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
     250          27 :                                                                                         off += sz;
     251          27 :   return off;
     252          27 : }
     253             : 
     254             : static ulong
     255             : footer_ser( fd_block_footer_t const * footer,
     256          93 :             uchar *                   buf ) {
     257          93 :   if( FD_UNLIKELY( footer->has_fast_final_cert && footer->has_final_cert ) ) return 0UL; /* one finalization shape or the other */
     258          90 :   if( FD_UNLIKELY( footer->user_agent_len>FD_BLOCK_FOOTER_USER_AGENT_MAX ) ) return 0UL;
     259             : 
     260          87 :   footer_serde_t ftr;
     261             : 
     262          87 :   ftr.version                   = (uchar)1;
     263          87 :   ftr.bank_hash                 = footer->bank_hash.uc;
     264          87 :   ftr.block_producer_time_nanos = footer->block_producer_time_nanos;
     265          87 :   ftr.user_agent_len            = (uchar)footer->user_agent_len;
     266          87 :   ftr.user_agent                = footer->user_agent;
     267          87 :   ftr.has_block_final_cert      = (uchar)( footer->has_fast_final_cert || footer->has_final_cert );
     268          87 :   ftr.block_final_cert          = NULL; /* the three cert bodies are written straight into buf below */
     269          87 :   ftr.has_skip_reward_cert      = (uchar)!!footer->has_skip_reward_cert;
     270          87 :   ftr.skip_reward_cert          = NULL;
     271          87 :   ftr.has_notar_reward_cert     = (uchar)!!footer->has_notar_reward_cert;
     272          87 :   ftr.notar_reward_cert         = NULL;
     273             : 
     274          87 :   ulong off = 0UL;
     275          87 :   ulong sz;
     276          87 :   buf[ off ] = ftr.version;                                                                          off += sizeof(uchar);
     277          87 :   memcpy( buf+off, ftr.bank_hash, sizeof(fd_hash_t) );                                               off += sizeof(fd_hash_t);
     278          87 :   FD_STORE( ulong, buf+off, ftr.block_producer_time_nanos );                                         off += sizeof(ulong);
     279          87 :   buf[ off ] = ftr.user_agent_len;                                                                   off += sizeof(uchar);
     280          87 :   memcpy( buf+off, ftr.user_agent, ftr.user_agent_len );                                             off += ftr.user_agent_len;
     281          87 :   buf[ off ] = ftr.has_block_final_cert;                                                             off += sizeof(uchar);
     282          87 :   if( ftr.has_block_final_cert ) {
     283          45 :     if( FD_UNLIKELY( !(sz=final_cert_ser( footer, buf+off )) ) ) return 0UL;
     284          39 :                                                                                                      off += sz;
     285          39 :   }
     286          81 :   buf[ off ] = ftr.has_skip_reward_cert;                                                             off += sizeof(uchar);
     287          81 :   if( ftr.has_skip_reward_cert ) {
     288          30 :     if( FD_UNLIKELY( !(sz=skip_reward_cert_ser( &footer->skip_reward_cert, buf+off )) ) ) return 0UL;
     289          27 :                                                                                                      off += sz;
     290          27 :   }
     291          78 :   buf[ off ] = ftr.has_notar_reward_cert;                                                            off += sizeof(uchar);
     292          78 :   if( ftr.has_notar_reward_cert ) {
     293          27 :     if( FD_UNLIKELY( !(sz=notar_reward_cert_ser( &footer->notar_reward_cert, buf+off )) ) ) return 0UL;
     294          27 :                                                                                                      off += sz;
     295          27 :   }
     296             : 
     297          78 :   return off;
     298          78 : }
     299             : 
     300             : ulong
     301             : fd_block_marker_ser( fd_block_marker_t const * self,
     302         129 :                      uchar                     buf[ static FD_BLOCK_MARKER_SER_MAX ] ) {
     303         129 :   uchar * payload = buf+FD_BLOCK_MARKER_PREAMBLE_SZ;
     304         129 :   ulong   payload_sz;
     305         129 :   switch( self->kind ) {
     306          93 :   case FD_BLOCK_MARKER_KIND_FOOTER: payload_sz = footer_ser( &self->footer, payload ); break;
     307          30 :   case FD_BLOCK_MARKER_KIND_HEADER: payload_sz = header_ser( &self->header, payload ); break;
     308           6 :   default:                          return 0UL; /* nothing we produce is an UpdateParent or GenesisCertificate */
     309         129 :   }
     310         123 :   if( FD_UNLIKELY( !payload_sz ) ) return 0UL;
     311             : 
     312         108 :   marker_serde_t marker;
     313             : 
     314         108 :   marker.entry_cnt = 0UL;
     315         108 :   marker.version   = (ushort)1;
     316         108 :   marker.tag       = (uchar)self->kind;
     317         108 :   marker.length    = (ushort)payload_sz;
     318         108 :   marker.payload   = payload; /* already in place */
     319             : 
     320         108 :   ulong off = 0UL;
     321         108 :   FD_STORE( ulong,  buf+off, marker.entry_cnt ); off += sizeof(ulong);
     322         108 :   FD_STORE( ushort, buf+off, marker.version );   off += sizeof(ushort);
     323         108 :   buf[ off ] = marker.tag;                       off += sizeof(uchar);
     324         108 :   FD_STORE( ushort, buf+off, marker.length );    off += sizeof(ushort);
     325         108 :                                                  off += payload_sz;
     326             : 
     327         108 :   return off;
     328         123 : }
     329             : 
     330             : static int
     331             : header_de( fd_block_header_t * header,
     332             :            uchar const *       buf,
     333             :            ulong               buf_sz,
     334         117 :            ulong *             sz ) {
     335         117 :   FAIL( buf_sz<FD_BLOCK_HEADER_SER_SZ, SZ );
     336             : 
     337         117 :   header_serde_t hdr; ulong off = 0UL;
     338         117 :   hdr.version         = buf[ off ];                off += sizeof(uchar);
     339         117 :   FAIL( hdr.version!=1, INVAL ); /* VersionedBlockHeader has only V1 */
     340         114 :   hdr.parent_slot     = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
     341         114 :   hdr.parent_block_id = buf+off;                   off += sizeof(fd_hash_t);
     342             : 
     343         114 :   header->parent_slot = hdr.parent_slot;
     344         114 :   memcpy( header->parent_block_id.uc, hdr.parent_block_id, sizeof(fd_hash_t) );
     345             : 
     346         114 :   *sz = off;
     347         114 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     348         117 : }
     349             : 
     350             : static int
     351             : update_parent_de( fd_update_parent_t * update_parent,
     352             :                   uchar const *        buf,
     353             :                   ulong                buf_sz,
     354          12 :                   ulong *              sz ) {
     355          12 :   FAIL( buf_sz<FD_UPDATE_PARENT_SER_SZ, SZ );
     356             : 
     357          12 :   update_parent_serde_t upd; ulong off = 0UL;
     358          12 :   upd.version             = buf[ off ];                off += sizeof(uchar);
     359          12 :   FAIL( upd.version!=1, INVAL ); /* VersionedUpdateParent has only V1 */
     360          12 :   upd.new_parent_slot     = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
     361          12 :   upd.new_parent_block_id = buf+off;                   off += sizeof(fd_hash_t);
     362             : 
     363          12 :   update_parent->new_parent_slot = upd.new_parent_slot;
     364          12 :   memcpy( update_parent->new_parent_block_id.uc, upd.new_parent_block_id, sizeof(fd_hash_t) );
     365             : 
     366          12 :   *sz = off;
     367          12 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     368          12 : }
     369             : 
     370             : /* bitmap_de decodes the base2 bitmap at b into nbits and signer_set,
     371             :    checking the version, the width against FD_BLS_SET_MAX and the
     372             :    payload length against the width.  The width is kept verbatim, so a
     373             :    cert re-encodes at the width it came with. */
     374             : 
     375             : static int
     376             : bitmap_de( ushort *       nbits,
     377             :            fd_bls_set_t * signer_set,
     378             :            uchar const *  b,
     379         153 :            ulong          b_sz ) {
     380         153 :   FAIL( b_sz<FD_BLOCK_BITMAP_SER_HDR_SZ, SZ );
     381             : 
     382         150 :   bitmap_serde_t bm; ulong off = 0UL;
     383         150 :   bm.version    = b[ off ];                 off += sizeof(uchar);
     384         150 :   FAIL( bm.version!=0, INVAL ); /* base2 only */
     385         147 :   bm.bit_cnt    = FD_LOAD( ushort, b+off ); off += sizeof(ushort);
     386         147 :   FAIL( (ulong)bm.bit_cnt>FD_BLS_SET_MAX, SZ );
     387         141 :   bm.payload    = b+off;
     388         141 :   bm.payload_sz = b_sz-off;
     389         141 :   FAIL( bm.payload_sz!=FD_BLOCK_BITMAP_SER_SZ( bm.bit_cnt )-FD_BLOCK_BITMAP_SER_HDR_SZ, INVAL );
     390             : 
     391         138 :   fd_bls_set_null( signer_set );
     392       72582 :   for( ulong i=0UL; i<(ulong)bm.bit_cnt; i++ ) {
     393       72444 :     if( (bm.payload[ i>>3 ] >> (i&7U)) & 1U ) fd_bls_set_insert( signer_set, i );
     394       72444 :   }
     395         138 :   *nbits = bm.bit_cnt;
     396         138 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     397         141 : }
     398             : 
     399             : static int
     400             : votes_aggregate_de( fd_block_footer_cert_t * cert,
     401             :                     uchar const *            buf,
     402             :                     ulong                    buf_sz,
     403          75 :                     ulong *                  sz ) {
     404          75 :   FAIL( buf_sz<FD_BLOCK_VOTES_AGGREGATE_SER_HDR_SZ, SZ );
     405             : 
     406          75 :   votes_aggregate_serde_t aggregate; ulong off = 0UL;
     407          75 :   aggregate.signature = buf+off;                    off += FD_BLS_SIG_COMPRESSED_SZ;
     408          75 :   aggregate.bitmap_sz = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
     409          75 :   FAIL( (ulong)aggregate.bitmap_sz>buf_sz-off, SZ );
     410          72 :   aggregate.bitmap    = buf+off;                    off += aggregate.bitmap_sz;
     411             : 
     412          72 :   int err = bitmap_de( &cert->nbits, cert->signer_set, aggregate.bitmap, aggregate.bitmap_sz );
     413          72 :   if( FD_UNLIKELY( err ) ) return err;
     414          69 :   memcpy( cert->sig, aggregate.signature, FD_BLS_SIG_COMPRESSED_SZ ); /* stays compressed for votor */
     415             : 
     416          69 :   *sz = off;
     417          69 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     418          72 : }
     419             : 
     420             : static int
     421             : final_cert_de( fd_block_footer_t * footer,
     422             :                uchar const *       buf,
     423             :                ulong               buf_sz,
     424          51 :                ulong *             sz ) {
     425          51 :   FAIL( buf_sz<FD_BLOCK_FINAL_CERT_SER_HDR_SZ, SZ );
     426             : 
     427          51 :   final_cert_serde_t cert; ulong off = 0UL;
     428          51 :   cert.slot            = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
     429          51 :   cert.block_id        = buf+off;                   off += sizeof(fd_hash_t);
     430          51 :   cert.final_aggregate = buf+off;
     431             : 
     432             :   /* zeroed so final_cert.block_id stays zero on the slow path */
     433          51 :   fd_block_footer_cert_t final_agg[1]; fd_memset( final_agg, 0, sizeof(final_agg) );
     434          51 :   ulong agg_sz;
     435          51 :   int   err = votes_aggregate_de( final_agg, cert.final_aggregate, buf_sz-off, &agg_sz );
     436          51 :   if( FD_UNLIKELY( err ) ) return err;
     437          48 :   off += agg_sz;
     438             : 
     439          48 :   FAIL( buf_sz-off<sizeof(uchar), SZ );
     440          48 :   cert.has_notar_aggregate = buf[ off ];            off += sizeof(uchar);
     441          48 :   FAIL( cert.has_notar_aggregate>1, INVAL );
     442          45 :   cert.notar_aggregate     = cert.has_notar_aggregate ? buf+off : NULL;
     443             : 
     444          45 :   if( !cert.notar_aggregate ) {
     445          21 :     footer->has_fast_final_cert  = 1;
     446          21 :     footer->fast_final_cert      = *final_agg;
     447          21 :     footer->fast_final_cert.slot = cert.slot;
     448          21 :     memcpy( footer->fast_final_cert.block_id.uc, cert.block_id, sizeof(fd_hash_t) );
     449          24 :   } else {
     450          24 :     err = votes_aggregate_de( &footer->notar_cert, cert.notar_aggregate, buf_sz-off, &agg_sz );
     451          24 :     if( FD_UNLIKELY( err ) ) return err;
     452          21 :     off += agg_sz;
     453          21 :     footer->has_final_cert  = 1;
     454          21 :     footer->final_cert      = *final_agg;
     455          21 :     footer->final_cert.slot = cert.slot;
     456          21 :     footer->notar_cert.slot = cert.slot;
     457          21 :     memcpy( footer->notar_cert.block_id.uc, cert.block_id, sizeof(fd_hash_t) );
     458          21 :   }
     459             : 
     460          42 :   *sz = off;
     461          42 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     462          45 : }
     463             : 
     464             : static int
     465             : skip_reward_cert_de( fd_block_footer_cert_t * cert,
     466             :                      uchar const *            buf,
     467             :                      ulong                    buf_sz,
     468          54 :                      ulong *                  sz ) {
     469          54 :   FAIL( buf_sz<FD_BLOCK_SKIP_REWARD_CERT_SER_HDR_SZ, SZ );
     470             : 
     471          54 :   skip_reward_cert_serde_t reward; ulong off = 0UL;
     472          54 :   reward.slot      = FD_LOAD( ulong, buf+off );             off += sizeof(ulong);
     473          54 :   reward.signature = buf+off;                               off += FD_BLS_SIG_COMPRESSED_SZ;
     474          54 :   FAIL( buf_sz==off, SZ ); /* no byte for the ShortU16 */
     475          51 :   ulong cu16_sz = fd_cu16_dec_sz( buf+off, buf_sz-off );
     476          51 :   FAIL( !cu16_sz, INVAL ); /* ShortU16 malformed (or a multi byte one cut short, which fd_cu16_dec_sz cannot tell apart) */
     477          48 :   reward.bitmap_sz = fd_cu16_dec_fixed( buf+off, cu16_sz ); off += cu16_sz;
     478          48 :   FAIL( (ulong)reward.bitmap_sz>buf_sz-off, SZ );
     479          48 :   reward.bitmap    = buf+off;                               off += reward.bitmap_sz;
     480             : 
     481          48 :   int err = bitmap_de( &cert->nbits, cert->signer_set, reward.bitmap, reward.bitmap_sz );
     482          48 :   if( FD_UNLIKELY( err ) ) return err;
     483          36 :   cert->slot = reward.slot; /* block_id stays zero */
     484          36 :   memcpy( cert->sig, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );
     485             : 
     486          36 :   *sz = off;
     487          36 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     488          48 : }
     489             : 
     490             : static int
     491             : notar_reward_cert_de( fd_block_footer_cert_t * cert,
     492             :                       uchar const *            buf,
     493             :                       ulong                    buf_sz,
     494          33 :                       ulong *                  sz ) {
     495          33 :   FAIL( buf_sz<FD_BLOCK_NOTAR_REWARD_CERT_SER_HDR_SZ, SZ );
     496             : 
     497          33 :   notar_reward_cert_serde_t reward; ulong off = 0UL;
     498          33 :   reward.slot      = FD_LOAD( ulong, buf+off );             off += sizeof(ulong);
     499          33 :   reward.block_id  = buf+off;                               off += sizeof(fd_hash_t);
     500          33 :   reward.signature = buf+off;                               off += FD_BLS_SIG_COMPRESSED_SZ;
     501          33 :   FAIL( buf_sz==off, SZ ); /* no byte for the ShortU16 */
     502          33 :   ulong cu16_sz = fd_cu16_dec_sz( buf+off, buf_sz-off );
     503          33 :   FAIL( !cu16_sz, INVAL ); /* ShortU16 malformed (or a multi byte one cut short, which fd_cu16_dec_sz cannot tell apart) */
     504          33 :   reward.bitmap_sz = fd_cu16_dec_fixed( buf+off, cu16_sz ); off += cu16_sz;
     505          33 :   FAIL( (ulong)reward.bitmap_sz>buf_sz-off, SZ );
     506          33 :   reward.bitmap    = buf+off;                               off += reward.bitmap_sz;
     507             : 
     508          33 :   int err = bitmap_de( &cert->nbits, cert->signer_set, reward.bitmap, reward.bitmap_sz );
     509          33 :   if( FD_UNLIKELY( err ) ) return err;
     510          33 :   cert->slot = reward.slot;
     511          33 :   memcpy( cert->block_id.uc, reward.block_id, sizeof(fd_hash_t) );
     512          33 :   memcpy( cert->sig, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );
     513             : 
     514          33 :   *sz = off;
     515          33 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     516          33 : }
     517             : 
     518             : static int
     519             : footer_de( fd_block_footer_t * footer,
     520             :            uchar const *       buf,
     521             :            ulong               buf_sz,
     522         126 :            ulong *             sz ) {
     523         126 :   FAIL( buf_sz<FD_BLOCK_FOOTER_SER_HDR_SZ, SZ );
     524             : 
     525         126 :   footer_serde_t ftr; ulong off = 0UL;
     526         126 :   ftr.version                   = buf[ off ];                off += sizeof(uchar);
     527         126 :   FAIL( ftr.version!=1, INVAL ); /* VersionedBlockFooter has only V1 */
     528         126 :   ftr.bank_hash                 = buf+off;                   off += sizeof(fd_hash_t);
     529         126 :   ftr.block_producer_time_nanos = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
     530         126 :   ftr.user_agent_len            = buf[ off ];                off += sizeof(uchar);
     531         126 :   FAIL( buf_sz-off<(ulong)ftr.user_agent_len, SZ );
     532         126 :   ftr.user_agent                = buf+off;                   off += ftr.user_agent_len;
     533             : 
     534         126 :   memcpy( footer->bank_hash.uc, ftr.bank_hash, sizeof(fd_hash_t) );
     535         126 :   footer->block_producer_time_nanos = ftr.block_producer_time_nanos;
     536         126 :   footer->user_agent_len            = (ulong)ftr.user_agent_len;
     537         126 :   memcpy( footer->user_agent, ftr.user_agent, ftr.user_agent_len );
     538             : 
     539         126 :   int   err;
     540         126 :   ulong part_sz;
     541             : 
     542         126 :   FAIL( buf_sz-off<sizeof(uchar), SZ );
     543         123 :   ftr.has_block_final_cert  = buf[ off ];                    off += sizeof(uchar);
     544         123 :   FAIL( ftr.has_block_final_cert>1, INVAL );
     545         120 :   ftr.block_final_cert      = ftr.has_block_final_cert ? buf+off : NULL;
     546         120 :   if( ftr.block_final_cert ) {
     547          51 :     err = final_cert_de( footer, ftr.block_final_cert, buf_sz-off, &part_sz ); /* sets has_fast_final_cert or has_final_cert itself */
     548          51 :     if( FD_UNLIKELY( err ) ) return err;
     549          42 :     off += part_sz;
     550          42 :   }
     551             : 
     552         111 :   FAIL( buf_sz-off<sizeof(uchar), SZ );
     553         111 :   ftr.has_skip_reward_cert  = buf[ off ];                    off += sizeof(uchar);
     554         111 :   FAIL( ftr.has_skip_reward_cert>1, INVAL );
     555         111 :   ftr.skip_reward_cert      = ftr.has_skip_reward_cert ? buf+off : NULL;
     556         111 :   if( ftr.skip_reward_cert ) {
     557          54 :     err = skip_reward_cert_de( &footer->skip_reward_cert, ftr.skip_reward_cert, buf_sz-off, &part_sz );
     558          54 :     if( FD_UNLIKELY( err ) ) return err;
     559          36 :     off += part_sz;
     560          36 :     footer->has_skip_reward_cert = 1;
     561          36 :   }
     562             : 
     563          93 :   FAIL( buf_sz-off<sizeof(uchar), SZ );
     564          93 :   ftr.has_notar_reward_cert = buf[ off ];                    off += sizeof(uchar);
     565          93 :   FAIL( ftr.has_notar_reward_cert>1, INVAL );
     566          93 :   ftr.notar_reward_cert     = ftr.has_notar_reward_cert ? buf+off : NULL;
     567          93 :   if( ftr.notar_reward_cert ) {
     568          33 :     err = notar_reward_cert_de( &footer->notar_reward_cert, ftr.notar_reward_cert, buf_sz-off, &part_sz );
     569          33 :     if( FD_UNLIKELY( err ) ) return err;
     570          33 :     off += part_sz;
     571          33 :     footer->has_notar_reward_cert = 1;
     572          33 :   }
     573             : 
     574          93 :   *sz = off;
     575          93 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     576          93 : }
     577             : 
     578             : int
     579             : fd_block_marker_de( fd_block_marker_t * self,
     580             :                     uchar const *       buf,
     581        4641 :                     ulong               buf_sz ) {
     582        4641 :   FAIL( buf_sz<FD_BLOCK_MARKER_PREAMBLE_SZ, SZ );
     583             : 
     584        4524 :   marker_serde_t marker; ulong off = 0UL;
     585        4524 :   marker.entry_cnt = FD_LOAD( ulong,  buf+off ); off += sizeof(ulong);
     586        4524 :   marker.version   = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
     587        4524 :   marker.tag       = buf[ off ];                 off += sizeof(uchar);
     588        4524 :   marker.length    = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
     589        4524 :   FAIL( marker.entry_cnt!=0UL, INVAL ); /* an entry batch, not a marker */
     590        4521 :   FAIL( marker.version!=1,     INVAL ); /* VersionedBlockMarker has only V1 */
     591        4518 :   FAIL( (ulong)marker.length>buf_sz-off, SZ );
     592         261 :   marker.payload   = buf+off;
     593             : 
     594         261 :   fd_memset( self, 0, sizeof(fd_block_marker_t) );
     595         261 :   self->kind = (uint)marker.tag;
     596             : 
     597         261 :   int   err;
     598         261 :   ulong payload_sz;
     599         261 :   switch( self->kind ) {
     600         126 :   case FD_BLOCK_MARKER_KIND_FOOTER:        err = footer_de       ( &self->footer,        marker.payload, marker.length, &payload_sz ); break;
     601         117 :   case FD_BLOCK_MARKER_KIND_HEADER:        err = header_de       ( &self->header,        marker.payload, marker.length, &payload_sz ); break;
     602          12 :   case FD_BLOCK_MARKER_KIND_UPDATE_PARENT: err = update_parent_de( &self->update_parent, marker.payload, marker.length, &payload_sz ); break;
     603           3 :   case FD_BLOCK_MARKER_KIND_GENESIS_CERT:  return FD_BLOCK_MARKER_DE_ERR_UNSUPPORTED;
     604           3 :   default:                                 return FD_BLOCK_MARKER_DE_ERR_INVAL;
     605         261 :   }
     606         255 :   if( FD_UNLIKELY( err ) ) return err;
     607         219 :   FAIL( payload_sz!=(ulong)marker.length, SZ ); /* LengthPrefixed::len is exact */
     608             : 
     609         210 :   return FD_BLOCK_MARKER_DE_SUCCESS;
     610         219 : }

Generated by: LCOV version 1.14