LCOV - code coverage report
Current view: top level - ballet/shred - fd_shred.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 52 73 71.2 %
Date: 2025-01-08 12:08:44 Functions: 1 2 50.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       10089 :                 ulong         const sz ) {
       6       10089 :   ulong total_shred_sz = sz;
       7             :   /* Initial bounds check */
       8       10089 :   if( FD_UNLIKELY( total_shred_sz<fd_ulong_min( FD_SHRED_DATA_HEADER_SZ, FD_SHRED_CODE_HEADER_SZ ) ) ) return NULL;
       9             : 
      10       10089 :   fd_shred_t const * shred = (fd_shred_t *)buf;
      11             : 
      12             :   /* Validate shred type.
      13             :      Safe to access because `variant` ends at 0x41, which is <= 0x58 */
      14       10089 :   uchar variant = shred->variant;
      15       10089 :   uchar type = fd_shred_type( variant );
      16       10089 :   if( FD_UNLIKELY( (type!=FD_SHRED_TYPE_MERKLE_DATA) &
      17       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE) &
      18       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_DATA_CHAINED) &
      19       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE_CHAINED) &
      20       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED) &
      21       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED) &
      22       10089 :                    (variant!=0xa5 /*FD_SHRED_TYPE_LEGACY_DATA*/ ) &
      23       10089 :                    (variant!=0x5a /*FD_SHRED_TYPE_LEGACY_CODE*/ ) ) )
      24         474 :     return NULL;
      25             : 
      26             :   /* There are six sections of a shred that can contribute to the size:
      27             :      header, payload, zero-padding, Merkle root of previous erasure
      28             :      batch, Merkle proof, and retransmitter signature.
      29             :      Some of these may have 0 size in certain cases.  sz is the sum of
      30             :      all 5, while for data shreds, shred->data.size == header+payload.
      31             :      We'll call the last three section the trailer. */
      32        9615 :   ulong header_sz       = fd_shred_header_sz( variant ); /* between 88 and 89 bytes */
      33        9615 :   ulong trailer_sz      = fd_shred_merkle_sz( shred->variant )   /* between 0 and 300 bytes */
      34        9615 :                            + fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0UL ) /* 0 or 64 */
      35        9615 :                            + fd_ulong_if( fd_shred_is_chained( type ),  FD_SHRED_MERKLE_ROOT_SZ, 0UL ); /* 0 or 32 */
      36        9615 :   ulong zero_padding_sz;
      37        9615 :   ulong payload_sz;
      38             : 
      39        9615 :   if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) {
      40        4377 :     if( FD_UNLIKELY( shred->data.size<header_sz ) ) return NULL;
      41        4377 :     payload_sz = (ulong)shred->data.size - header_sz; /* between 0 and USHORT_MAX */
      42        4377 :     if( FD_UNLIKELY( (type!=FD_SHRED_TYPE_LEGACY_DATA) & (sz<FD_SHRED_MIN_SZ) ) ) return NULL;
      43             : 
      44             :     /* legacy data shreds might be shorter than the normal
      45             :        FD_SHRED_MIN_SZ, but they don't have Merkle proofs, so everything
      46             :        after the payload is zero-padding/ignored.  On the other hand,
      47             :        Merkle data shreds might have some zero-padding, but anything
      48             :        between [FD_SHRED_MIN_SZ, sz) is extra bytes after the shred
      49             :        (which we don't care about the contents of but also tolerate).
      50             :        The Merkle proof is not in bytes [sz-merkle_proof_sz, sz) but in
      51             :        [FD_SHRED_MIN_SZ-merkle_proof_sz, FD_SHRED_MIN_SZ).  From above,
      52             :        we know sz >= FD_SHRED_MIN_SZ in this case. */
      53        4377 :     uchar is_legacy_data_shred = type==FD_SHRED_TYPE_LEGACY_DATA;
      54        4377 :     ulong effective_sz = fd_ulong_if( is_legacy_data_shred, sz, FD_SHRED_MIN_SZ );
      55        4377 :     if( FD_UNLIKELY( effective_sz < header_sz+payload_sz+trailer_sz ) ) return NULL;
      56        4377 :     zero_padding_sz = effective_sz - header_sz - payload_sz - trailer_sz;
      57        4377 :   }
      58        5238 :   else if( FD_LIKELY( type & FD_SHRED_TYPEMASK_CODE ) ) {
      59        5238 :     zero_padding_sz = 0UL;
      60             :     /* Payload size is not specified directly, but the whole shred must
      61             :        be FD_SHRED_MAX_SZ. */
      62        5238 :     if( FD_UNLIKELY( header_sz+zero_padding_sz+trailer_sz > FD_SHRED_MAX_SZ ) ) return NULL;
      63        5238 :     payload_sz      = FD_SHRED_MAX_SZ - header_sz - zero_padding_sz - trailer_sz;
      64        5238 :   }
      65           0 :   else return NULL;
      66             : 
      67        9615 :   if( FD_UNLIKELY( sz < header_sz + payload_sz + zero_padding_sz + trailer_sz ) ) return NULL;
      68             : 
      69             :   /* At this point we know all the fields exist, but we need to sanity
      70             :      check a few fields that would make a shred illegal. */
      71        9615 :   if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) {
      72        4377 :     ulong parent_off = (ulong)shred->data.parent_off;
      73        4377 :     ulong slot       = shred->slot;
      74        4377 :     if( FD_UNLIKELY( (shred->data.flags&0xC0)==0x80                              ) ) return NULL;
      75        4374 :     if( FD_UNLIKELY( parent_off>slot                                             ) ) return NULL;
      76             :     /* The property we want to enforce is
      77             :            slot==0 <=> parent_off==0 <=> slot==parent_off,
      78             :        where <=> means if and only if.  It's a strange expression
      79             :        though, because any two of the statements automatically imply the
      80             :        other one, so it's logically equivalent to:
      81             :             (slot==0 or parent_off==0)   <=> slot==parent_off
      82             :        We want the complement though, so that we can return NULL, and
      83             :        the complement of iff is xor. */
      84        4368 :     if( FD_UNLIKELY( ((parent_off==0) | (slot==0UL)) ^ (slot==parent_off)        ) ) return NULL;
      85        4362 :     if( FD_UNLIKELY( shred->idx<shred->fec_set_idx                               ) ) return NULL;
      86        5238 :   } else {
      87        5238 :     if( FD_UNLIKELY( shred->code.idx>=shred->code.code_cnt                       ) ) return NULL;
      88        5232 :     if( FD_UNLIKELY( shred->code.idx> shred->idx                                 ) ) return NULL;
      89        5229 :     if( FD_UNLIKELY( (shred->code.data_cnt==0)|(shred->code.code_cnt==0)         ) ) return NULL;
      90        5226 :     if( FD_UNLIKELY( shred->code.code_cnt>256                                    ) ) return NULL;
      91        5226 :     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 */
      92        5226 :   }
      93             : 
      94        9576 :   return shred;
      95        9615 : }
      96             : 
      97             : FD_FN_PURE int
      98           0 : fd_shred_merkle_root( fd_shred_t const * shred, void * bmtree_mem, fd_bmtree_node_t * root_out ) {
      99           0 :   fd_bmtree_commit_t * tree = fd_bmtree_commit_init( bmtree_mem,
     100           0 :                                                      FD_SHRED_MERKLE_NODE_SZ,
     101           0 :                                                      FD_BMTREE_LONG_PREFIX_SZ,
     102           0 :                                                      FD_SHRED_MERKLE_LAYER_CNT );
     103             : 
     104           0 :   uchar shred_type  = fd_shred_type( shred->variant );
     105           0 :   int is_data_shred = fd_shred_is_data( shred_type );
     106           0 :   ulong in_type_idx = fd_ulong_if( is_data_shred, shred->idx - shred->fec_set_idx, shred->code.idx );
     107           0 :   ulong shred_idx   = fd_ulong_if( is_data_shred, in_type_idx, in_type_idx + shred->code.data_cnt  );
     108             : 
     109           0 :   ulong tree_depth           = fd_shred_merkle_cnt( shred->variant ); /* In [0, 15] */
     110           0 :   ulong reedsol_protected_sz = 1115UL + FD_SHRED_DATA_HEADER_SZ - FD_SHRED_SIGNATURE_SZ - FD_SHRED_MERKLE_NODE_SZ*tree_depth
     111           0 :                                       - FD_SHRED_MERKLE_ROOT_SZ*fd_shred_is_chained ( shred_type )
     112           0 :                                       - FD_SHRED_SIGNATURE_SZ  *fd_shred_is_resigned( shred_type); /* In [743, 1139] conservatively*/
     113           0 :   ulong data_merkle_protected_sz   = reedsol_protected_sz + FD_SHRED_MERKLE_ROOT_SZ*fd_shred_is_chained ( shred_type );
     114           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;
     115           0 :   ulong merkle_protected_sz  = fd_ulong_if( is_data_shred, data_merkle_protected_sz, parity_merkle_protected_sz );
     116           0 :   fd_bmtree_node_t leaf;
     117           0 :   fd_bmtree_hash_leaf( &leaf, (uchar const *)shred + sizeof(fd_ed25519_sig_t), merkle_protected_sz, FD_BMTREE_LONG_PREFIX_SZ );
     118             : 
     119           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 );
     120           0 : }

Generated by: LCOV version 1.14