LCOV - code coverage report
Current view: top level - ballet/shred - fd_shred.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 78 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 2 0.0 %

          Line data    Source code
       1             : #include "fd_shred.h"
       2             : 
       3             : fd_shred_t const *
       4             : fd_shred_parse( uchar const * const buf,
       5             :                 ulong         const sz,
       6           0 :                 ulong         const max_shred_idx ) {
       7           0 :   ulong total_shred_sz = sz;
       8             :   /* Initial bounds check */
       9           0 :   if( FD_UNLIKELY( total_shred_sz<fd_ulong_min( FD_SHRED_DATA_HEADER_SZ, FD_SHRED_CODE_HEADER_SZ ) ) ) return NULL;
      10             : 
      11           0 :   fd_shred_t const * shred = (fd_shred_t *)buf;
      12             : 
      13             :   /* Validate shred type.
      14             :      Safe to access because `variant` ends at 0x41, which is <= 0x58 */
      15           0 :   uchar variant = shred->variant;
      16           0 :   uchar type = fd_shred_type( variant );
      17           0 :   if( FD_UNLIKELY( (type!=FD_SHRED_TYPE_MERKLE_DATA) &
      18           0 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE) &
      19           0 :                    (type!=FD_SHRED_TYPE_MERKLE_DATA_CHAINED) &
      20           0 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE_CHAINED) &
      21           0 :                    (type!=FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED) &
      22           0 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED) &
      23           0 :                    (variant!=0xa5 /*FD_SHRED_TYPE_LEGACY_DATA*/ ) &
      24           0 :                    (variant!=0x5a /*FD_SHRED_TYPE_LEGACY_CODE*/ ) ) )
      25           0 :     return NULL;
      26             : 
      27             :   /* There are six sections of a shred that can contribute to the size:
      28             :      header, payload, zero-padding, Merkle root of previous erasure
      29             :      batch, Merkle proof, and retransmitter signature.
      30             :      Some of these may have 0 size in certain cases.  sz is the sum of
      31             :      all 5, while for data shreds, shred->data.size == header+payload.
      32             :      We'll call the last three section the trailer. */
      33           0 :   ulong header_sz       = fd_shred_header_sz( variant ); /* between 88 and 89 bytes */
      34           0 :   ulong trailer_sz      = fd_shred_merkle_sz( shred->variant )   /* between 0 and 300 bytes */
      35           0 :                            + fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0UL ) /* 0 or 64 */
      36           0 :                            + fd_ulong_if( fd_shred_is_chained( type ),  FD_SHRED_MERKLE_ROOT_SZ, 0UL ); /* 0 or 32 */
      37           0 :   ulong zero_padding_sz;
      38           0 :   ulong payload_sz;
      39             : 
      40           0 :   if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) {
      41           0 :     if( FD_UNLIKELY( shred->data.size<header_sz ) ) return NULL;
      42           0 :     payload_sz = (ulong)shred->data.size - header_sz; /* between 0 and USHORT_MAX */
      43           0 :     if( FD_UNLIKELY( (type!=FD_SHRED_TYPE_LEGACY_DATA) & (sz<FD_SHRED_MIN_SZ) ) ) return NULL;
      44             : 
      45             :     /* legacy data shreds might be shorter than the normal
      46             :        FD_SHRED_MIN_SZ, but they don't have Merkle proofs, so everything
      47             :        after the payload is zero-padding/ignored.  On the other hand,
      48             :        Merkle data shreds might have some zero-padding, but anything
      49             :        between [FD_SHRED_MIN_SZ, sz) is extra bytes after the shred
      50             :        (which we don't care about the contents of but also tolerate).
      51             :        The Merkle proof is not in bytes [sz-merkle_proof_sz, sz) but in
      52             :        [FD_SHRED_MIN_SZ-merkle_proof_sz, FD_SHRED_MIN_SZ).  From above,
      53             :        we know sz >= FD_SHRED_MIN_SZ in this case. */
      54           0 :     uchar is_legacy_data_shred = type==FD_SHRED_TYPE_LEGACY_DATA;
      55           0 :     ulong effective_sz = fd_ulong_if( is_legacy_data_shred, sz, FD_SHRED_MIN_SZ );
      56           0 :     if( FD_UNLIKELY( effective_sz < header_sz+payload_sz+trailer_sz ) ) return NULL;
      57           0 :     zero_padding_sz = effective_sz - header_sz - payload_sz - trailer_sz;
      58           0 :   }
      59           0 :   else if( FD_LIKELY( type & FD_SHRED_TYPEMASK_CODE ) ) {
      60           0 :     zero_padding_sz = 0UL;
      61             :     /* Payload size is not specified directly, but the whole shred must
      62             :        be FD_SHRED_MAX_SZ. */
      63           0 :     if( FD_UNLIKELY( header_sz+zero_padding_sz+trailer_sz > FD_SHRED_MAX_SZ ) ) return NULL;
      64           0 :     payload_sz      = FD_SHRED_MAX_SZ - header_sz - zero_padding_sz - trailer_sz;
      65           0 :   }
      66           0 :   else return NULL;
      67             : 
      68           0 :   if( FD_UNLIKELY( sz < header_sz + payload_sz + zero_padding_sz + trailer_sz ) ) return NULL;
      69             : 
      70             :   /* At this point we know all the fields exist, but we need to sanity
      71             :      check a few fields that would make a shred illegal. */
      72           0 :   if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) {
      73           0 :     ulong parent_off = (ulong)shred->data.parent_off;
      74           0 :     ulong slot       = shred->slot;
      75           0 :     if( FD_UNLIKELY( (shred->data.flags&0xC0)==0x80                              ) ) return NULL;
      76           0 :     if( FD_UNLIKELY( parent_off>slot                                             ) ) return NULL;
      77             : 
      78             :     /* Slot 0 is its own parent (parent_off==0); every other slot has
      79             :        a parent strictly below it, so parent_off must be nonzero.  A
      80             :        parent of slot 0 is valid for any slot: whether it lies below
      81             :        the root is decided when the shred is ingested, not here.
      82             :        https://github.com/anza-xyz/agave/blob/v4.4.0-beta.0/ledger/src/shred/traits.rs#L41-L54 */
      83           0 :     if( FD_UNLIKELY( (slot!=0UL) & (parent_off==0UL)                             ) ) return NULL;
      84           0 :     if( FD_UNLIKELY( shred->idx<shred->fec_set_idx                               ) ) return NULL;
      85             : 
      86             :     /* https://github.com/anza-xyz/agave/blob/v4.0.2/ledger/src/shred/shred_data.rs#L19 */
      87           0 :     if( FD_UNLIKELY( shred->idx>=max_shred_idx                                   ) ) return NULL;
      88           0 :   } else {
      89           0 :     if( FD_UNLIKELY( shred->code.idx>=shred->code.code_cnt                       ) ) return NULL;
      90           0 :     if( FD_UNLIKELY( shred->code.idx> shred->idx                                 ) ) return NULL;
      91           0 :     if( FD_UNLIKELY( (shred->code.data_cnt==0)|(shred->code.code_cnt==0)         ) ) return NULL;
      92           0 :     if( FD_UNLIKELY( shred->code.code_cnt>256                                    ) ) return NULL;
      93           0 :     if( FD_UNLIKELY( (ulong)shred->code.data_cnt+(ulong)shred->code.code_cnt>256 ) ) return NULL; /* I don't see this check in Agave, but it seems necessary */
      94             : 
      95             :     /* https://github.com/anza-xyz/agave/blob/v4.0.2/ledger/src/shred/shred_code.rs#L40 */
      96           0 :     if( FD_UNLIKELY( shred->idx>=max_shred_idx                                                          ) ) return NULL;
      97             :     /* https://github.com/anza-xyz/agave/blob/v4.0.2/ledger/src/shred/shred_code.rs#L12-L25
      98             :        Subtractions are safe here due to the checks above. */
      99           0 :     if( FD_UNLIKELY( (ulong)shred->fec_set_idx+(ulong)shred->code.data_cnt-1UL>=max_shred_idx           ) ) return NULL;
     100           0 :     if( FD_UNLIKELY( (ulong)(shred->idx-shred->code.idx)+(ulong)shred->code.code_cnt-1UL>=max_shred_idx ) ) return NULL;
     101           0 :   }
     102             : 
     103           0 :   return shred;
     104           0 : }
     105             : 
     106             : int
     107           0 : fd_shred_merkle_root( fd_shred_t const * shred, void * bmtree_mem, fd_bmtree_node_t * root_out ) {
     108           0 :   fd_bmtree_commit_t * tree = fd_bmtree_commit_init( bmtree_mem,
     109           0 :                                                      FD_SHRED_MERKLE_NODE_SZ,
     110           0 :                                                      FD_BMTREE_LONG_PREFIX_SZ,
     111           0 :                                                      FD_SHRED_MERKLE_LAYER_CNT );
     112             : 
     113           0 :   uchar shred_type  = fd_shred_type( shred->variant );
     114           0 :   int is_data_shred = fd_shred_is_data( shred_type );
     115           0 :   ulong in_type_idx = fd_ulong_if( is_data_shred, shred->idx - shred->fec_set_idx, shred->code.idx );
     116           0 :   ulong shred_idx   = fd_ulong_if( is_data_shred, in_type_idx, in_type_idx + shred->code.data_cnt  );
     117             : 
     118           0 :   ulong tree_depth           = fd_shred_merkle_cnt( shred->variant ); /* In [0, 15] */
     119           0 :   if( FD_UNLIKELY( tree_depth!=FD_SHRED_MERKLE_LAYER_CNT-1UL ) ) return 0;
     120           0 :   ulong reedsol_protected_sz = 1115UL + FD_SHRED_DATA_HEADER_SZ - FD_SHRED_SIGNATURE_SZ - FD_SHRED_MERKLE_NODE_SZ*tree_depth
     121           0 :                                       - FD_SHRED_MERKLE_ROOT_SZ*fd_shred_is_chained ( shred_type )
     122           0 :                                       - FD_SHRED_SIGNATURE_SZ  *fd_shred_is_resigned( shred_type); /* In [743, 1139] conservatively*/
     123           0 :   ulong data_merkle_protected_sz   = reedsol_protected_sz + FD_SHRED_MERKLE_ROOT_SZ*fd_shred_is_chained ( shred_type );
     124           0 :   ulong parity_merkle_protected_sz = reedsol_protected_sz + FD_SHRED_MERKLE_ROOT_SZ*fd_shred_is_chained ( shred_type )+FD_SHRED_CODE_HEADER_SZ-FD_ED25519_SIG_SZ;
     125           0 :   ulong merkle_protected_sz  = fd_ulong_if( is_data_shred, data_merkle_protected_sz, parity_merkle_protected_sz );
     126           0 :   fd_bmtree_node_t leaf;
     127           0 :   fd_bmtree_hash_leaf( &leaf, (uchar const *)shred + sizeof(fd_ed25519_sig_t), merkle_protected_sz, FD_BMTREE_LONG_PREFIX_SZ );
     128             : 
     129           0 :   return fd_bmtree_commitp_insert_with_proof( tree, shred_idx, &leaf, (uchar const *)fd_shred_merkle_nodes( shred ), fd_shred_merkle_cnt( shred->variant ), root_out );
     130           0 : }

Generated by: LCOV version 1.14