LCOV - code coverage report
Current view: top level - disco/shred - fd_shredder.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 188 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "fd_shredder.h"
       2             : #include "../../ballet/shred/fd_shred.h"
       3             : 
       4             : void *
       5             : fd_shredder_new( void *                mem,
       6             :                  fd_shredder_sign_fn * signer,
       7           0 :                  void *                signer_ctx ) {
       8           0 :   fd_shredder_t * shredder = (fd_shredder_t *)mem;
       9             : 
      10           0 :   if( FD_UNLIKELY( !mem ) ) {
      11           0 :     FD_LOG_WARNING(( "NULL shredder memory" ));
      12           0 :     return NULL;
      13           0 :   }
      14             : 
      15           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_shredder_align() ) ) ) {
      16           0 :     FD_LOG_WARNING(( "misaligned shredder memory" ));
      17           0 :     return NULL;
      18           0 :   }
      19             : 
      20           0 :   shredder->shred_version = 0;
      21           0 :   shredder->entry_batch   = NULL;
      22           0 :   shredder->sz            = 0UL;
      23           0 :   shredder->offset        = 0UL;
      24             : 
      25           0 :   fd_memset( &(shredder->meta), 0, sizeof(fd_entry_batch_meta_t) );
      26           0 :   shredder->slot              = ULONG_MAX;
      27           0 :   shredder->data_idx_offset   = 0UL;
      28           0 :   shredder->parity_idx_offset = 0UL;
      29             : 
      30           0 :   shredder->signer     = signer;
      31           0 :   shredder->signer_ctx = signer_ctx;
      32             : 
      33           0 :   FD_COMPILER_MFENCE();
      34           0 :   FD_VOLATILE( shredder->magic ) = FD_SHREDDER_MAGIC;
      35           0 :   FD_COMPILER_MFENCE();
      36             : 
      37           0 :   return (void *)shredder;
      38           0 : }
      39             : 
      40             : fd_shredder_t *
      41           0 : fd_shredder_join( void * mem ) {
      42           0 :   if( FD_UNLIKELY( !mem ) ) {
      43           0 :     FD_LOG_WARNING(( "NULL shredder memory" ));
      44           0 :     return NULL;
      45           0 :   }
      46             : 
      47           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_shredder_align() ) ) ) {
      48           0 :     FD_LOG_WARNING(( "misaligned shredder memory" ));
      49           0 :     return NULL;
      50           0 :   }
      51             : 
      52           0 :   fd_shredder_t * shredder = (fd_shredder_t *)mem;
      53             : 
      54           0 :   if( FD_UNLIKELY( shredder->magic!=FD_SHREDDER_MAGIC ) ) {
      55           0 :     FD_LOG_WARNING(( "bad magic" ));
      56           0 :     return NULL;
      57           0 :   }
      58             : 
      59           0 :   return shredder;
      60           0 : }
      61             : 
      62             : void *
      63           0 : fd_shredder_leave(  fd_shredder_t * shredder ) {
      64           0 :   return (void *)shredder;
      65           0 : }
      66             : 
      67             : void *
      68           0 : fd_shredder_delete( void *          mem      ) {
      69           0 :   fd_shredder_t * shredder = (fd_shredder_t *)mem;
      70             : 
      71           0 :   if( FD_UNLIKELY( shredder->magic!=FD_SHREDDER_MAGIC ) ) {
      72           0 :     FD_LOG_WARNING(( "bad magic" ));
      73           0 :     return NULL;
      74           0 :   }
      75             : 
      76           0 :   FD_COMPILER_MFENCE();
      77           0 :   FD_VOLATILE( shredder->magic ) = 0UL;
      78           0 :   FD_COMPILER_MFENCE();
      79             : 
      80           0 :   return (void *)shredder;
      81           0 : }
      82             : 
      83             : 
      84             : fd_shredder_t *
      85             : fd_shredder_skip_batch( fd_shredder_t * shredder,
      86             :                         ulong           entry_batch_sz,
      87             :                         ulong           slot,
      88           0 :                         int             block_complete ) {
      89             : 
      90           0 :   if( FD_UNLIKELY( entry_batch_sz==0UL ) ) return NULL;
      91             : 
      92           0 :   if( FD_UNLIKELY( slot != shredder->slot ) ) {
      93           0 :     shredder->data_idx_offset   = 0UL;
      94           0 :     shredder->parity_idx_offset = 0UL;
      95           0 :   }
      96             : 
      97           0 :   ulong data_shred_cnt        =  fd_shredder_count_data_shreds(   entry_batch_sz, block_complete );
      98           0 :   ulong parity_shred_cnt      =  fd_shredder_count_parity_shreds( entry_batch_sz, block_complete );
      99             : 
     100           0 :   shredder->data_idx_offset   += data_shred_cnt;
     101           0 :   shredder->parity_idx_offset += parity_shred_cnt;
     102           0 :   shredder->slot              =  slot;
     103             : 
     104           0 :   return shredder;
     105           0 : }
     106             : 
     107             : 
     108             : fd_shredder_t *
     109             : fd_shredder_init_batch( fd_shredder_t *               shredder,
     110             :                         void const    *               entry_batch,
     111             :                         ulong                         entry_batch_sz,
     112             :                         ulong                         slot,
     113           0 :                         fd_entry_batch_meta_t const * metadata ) {
     114             : 
     115           0 :   if( FD_UNLIKELY( entry_batch_sz==0UL ) ) return NULL; /* FIXME: should this warn? Silently expand it to 1 byte? */
     116             : 
     117           0 :   shredder->entry_batch = entry_batch;
     118           0 :   shredder->sz          = entry_batch_sz;
     119           0 :   shredder->offset      = 0UL;
     120             : 
     121           0 :   if( FD_UNLIKELY( slot != shredder->slot ) ) {
     122           0 :     shredder->data_idx_offset   = 0UL;
     123           0 :     shredder->parity_idx_offset = 0UL;
     124           0 :   }
     125             : 
     126           0 :   shredder->slot = slot;
     127           0 :   shredder->meta = *metadata;
     128             : 
     129           0 :   return shredder;
     130           0 : }
     131             : 
     132             : 
     133             : fd_fec_set_t *
     134             : fd_shredder_next_fec_set( fd_shredder_t * shredder,
     135             :                           fd_fec_set_t *  result,
     136           0 :                           uchar *         chained_merkle_root ) {
     137           0 :   uchar const * entry_batch = shredder->entry_batch;
     138           0 :   ulong         offset      = shredder->offset;
     139           0 :   ulong         entry_sz    = shredder->sz;
     140             : 
     141           0 :   fd_ed25519_sig_t __attribute__((aligned(32UL))) root_signature;
     142             : 
     143           0 :   if( FD_UNLIKELY( (offset==entry_sz) ) ) return NULL;
     144             : 
     145           0 :   ulong entry_bytes_remaining = entry_sz - offset;
     146             : 
     147             :   /* Set the shred type */
     148           0 :   int block_complete = !!shredder->meta.block_complete;
     149           0 :   int is_chained     = 1;
     150           0 :   int is_resigned    = block_complete & (entry_bytes_remaining<=FD_SHREDDER_RESIGNED_FEC_SET_PAYLOAD_SZ);
     151             : 
     152           0 :   uchar data_type = fd_uchar_if( is_resigned, FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED, FD_SHRED_TYPE_MERKLE_DATA_CHAINED  );
     153           0 :   uchar code_type = fd_uchar_if( is_resigned, FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED, FD_SHRED_TYPE_MERKLE_CODE_CHAINED  );
     154             : 
     155             :   /* how many total payload bytes in this FEC set? */
     156           0 :   ulong chunk_size;
     157           0 :   if( FD_LIKELY( !block_complete ) ) { /* the easy case */
     158           0 :     chunk_size = fd_ulong_min( entry_bytes_remaining, FD_SHREDDER_CHAINED_FEC_SET_PAYLOAD_SZ  );
     159           0 :   } else if( FD_UNLIKELY( is_resigned ) ) {
     160           0 :     chunk_size = fd_ulong_min( entry_bytes_remaining, FD_SHREDDER_RESIGNED_FEC_SET_PAYLOAD_SZ );
     161           0 :   } else {
     162             :     /* Save room for the resigned batch.  The difference is > 0 because
     163             :        if it weren't, we would have taken the is_resigned case. */
     164           0 :     chunk_size = fd_ulong_min( entry_bytes_remaining-FD_SHREDDER_RESIGNED_FEC_SET_PAYLOAD_SZ, FD_SHREDDER_CHAINED_FEC_SET_PAYLOAD_SZ );
     165           0 :   }
     166           0 :   ulong last_in_batch           = (chunk_size+offset==entry_sz);
     167           0 :   ulong adj_entry_sz            = offset+chunk_size;
     168             : 
     169           0 :   ulong data_shred_cnt          = FD_FEC_SHRED_CNT;
     170           0 :   ulong parity_shred_cnt        = FD_FEC_SHRED_CNT;
     171             :   /* Our notion of tree depth counts the root, while the shred version
     172             :      doesn't. */
     173           0 :   ulong tree_depth              = fd_bmtree_depth( data_shred_cnt+parity_shred_cnt )-1UL;
     174           0 :   ulong data_shred_payload_sz   = 1115UL - 20UL*tree_depth - 32UL*(ulong)is_chained - 64UL*(ulong)is_resigned;
     175           0 :   ulong parity_shred_payload_sz = data_shred_payload_sz + FD_SHRED_DATA_HEADER_SZ - FD_SHRED_SIGNATURE_SZ;
     176           0 :   ulong data_merkle_sz          = parity_shred_payload_sz + 32UL*(ulong)is_chained;
     177           0 :   ulong parity_merkle_sz        = data_merkle_sz + FD_SHRED_CODE_HEADER_SZ - FD_SHRED_SIGNATURE_SZ;
     178             : 
     179           0 :   fd_reedsol_t * reedsol = fd_reedsol_encode_init( shredder->reedsol, parity_shred_payload_sz );
     180             : 
     181             :   /* Write headers and copy the data shred payload */
     182           0 :   ulong flags_for_last = ((last_in_batch & (ulong)block_complete)<<7) | (last_in_batch<<6);
     183           0 :   for( ulong i=0UL; i<data_shred_cnt; i++ ) {
     184           0 :     fd_shred_t         * shred = result->data_shreds[ i ].s;
     185             : 
     186             :     /* Size in bytes of the payload section of this data shred,
     187             :        excluding any zero-padding */
     188           0 :     ulong shred_payload_sz = fd_ulong_min( adj_entry_sz-offset, data_shred_payload_sz );
     189             : 
     190           0 :     shred->variant            = fd_shred_variant( data_type, (uchar)tree_depth );
     191           0 :     shred->slot               = shredder->slot;
     192           0 :     shred->idx                = (uint  )(shredder->data_idx_offset + i);
     193           0 :     shred->version            = (ushort)(shredder->shred_version);
     194           0 :     shred->fec_set_idx        = (uint  )(shredder->data_idx_offset);
     195           0 :     shred->data.parent_off    = (ushort)(shredder->meta.parent_offset);
     196           0 :     shred->data.flags         = (uchar )(fd_ulong_if( i==data_shred_cnt-1UL, flags_for_last, 0UL ) | (shredder->meta.reference_tick & 0x3FUL));
     197           0 :     shred->data.size          = (ushort)(FD_SHRED_DATA_HEADER_SZ + shred_payload_sz);
     198             : 
     199           0 :     uchar * payload = fd_memcpy( result->data_shreds[ i ].b + FD_SHRED_DATA_HEADER_SZ , entry_batch+offset, shred_payload_sz );
     200           0 :     offset += shred_payload_sz;
     201             : 
     202             :     /* Write zero-padding, possibly a no-op */
     203           0 :     fd_memset( payload+shred_payload_sz, 0, data_shred_payload_sz-shred_payload_sz );
     204             : 
     205             :     /* Set the last bytes of the signature field to the Merkle tree
     206             :        prefix so we can use the faster batch sha256 API to compute the
     207             :        Merkle tree */
     208           0 :     fd_memcpy( shred->signature + 64UL - 26UL, "\x00SOLANA_MERKLE_SHREDS_LEAF", 26UL );
     209             : 
     210             :     /* Prepare to generate parity data: data shred starts right after
     211             :        signature and goes until start of Merkle proof. */
     212           0 :     fd_reedsol_encode_add_data_shred( reedsol, ((uchar*)shred) + sizeof(fd_ed25519_sig_t) );
     213             : 
     214             :     /* Set chained merkle root */
     215           0 :     uchar * merkle = ((uchar*)shred) + fd_shred_chain_off( shred->variant );
     216           0 :     memcpy( merkle, chained_merkle_root, FD_SHRED_MERKLE_ROOT_SZ );
     217           0 :   }
     218             : 
     219           0 :   for( ulong j=0UL; j<parity_shred_cnt; j++ ) {
     220           0 :     fd_shred_t        * shred = result->parity_shreds[ j ].s;
     221           0 :     shred->variant            = fd_shred_variant( code_type, (uchar)tree_depth );
     222           0 :     shred->slot               = shredder->slot;
     223           0 :     shred->idx                = (uint  )(shredder->parity_idx_offset + j);
     224           0 :     shred->version            = (ushort)(shredder->shred_version);
     225           0 :     shred->fec_set_idx        = (uint  )(shredder->data_idx_offset);
     226           0 :     shred->code.data_cnt      = (ushort)(data_shred_cnt);
     227           0 :     shred->code.code_cnt      = (ushort)(parity_shred_cnt);
     228           0 :     shred->code.idx           = (ushort)(j);
     229             : 
     230           0 :     fd_memcpy( shred->signature + 64UL - 26UL, "\x00SOLANA_MERKLE_SHREDS_LEAF", 26UL );
     231             : 
     232             :     /* Prepare to generate parity data: parity info starts right after
     233             :        signature and goes until start of Merkle proof. */
     234           0 :     fd_reedsol_encode_add_parity_shred( reedsol, result->parity_shreds[ j ].b + FD_SHRED_CODE_HEADER_SZ );
     235             : 
     236             :     /* Set chained merkle root */
     237           0 :     uchar * merkle = ((uchar*)shred) + fd_shred_chain_off( shred->variant );
     238           0 :     memcpy( merkle, chained_merkle_root, FD_SHRED_MERKLE_ROOT_SZ );
     239           0 :   }
     240             : 
     241             :   /* Generate parity data */
     242           0 :   fd_reedsol_encode_fini( reedsol );
     243             : 
     244             :   /* Generate Merkle leaves */
     245           0 :   fd_sha256_batch_t * sha256 = fd_sha256_batch_init( shredder->sha256 );
     246           0 :   fd_bmtree_node_t * leaves = shredder->bmtree_leaves;
     247             : 
     248           0 :   for( ulong i=0UL; i<data_shred_cnt; i++ )
     249           0 :     fd_sha256_batch_add( sha256, result->data_shreds[i].b+sizeof(fd_ed25519_sig_t)-26UL,   data_merkle_sz+26UL,   leaves[i].hash );
     250           0 :   for( ulong j=0UL; j<parity_shred_cnt; j++ )
     251           0 :     fd_sha256_batch_add( sha256, result->parity_shreds[j].b+sizeof(fd_ed25519_sig_t)-26UL, parity_merkle_sz+26UL, leaves[j+data_shred_cnt].hash );
     252           0 :   fd_sha256_batch_fini( sha256 );
     253             : 
     254             :   /* Generate Merkle Proofs */
     255           0 :   fd_bmtree_commit_t * bmtree = fd_bmtree_commit_init( shredder->_bmtree_footprint, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ, tree_depth+1UL );
     256           0 :   fd_bmtree_commit_append( bmtree, leaves, data_shred_cnt+parity_shred_cnt );
     257           0 :   uchar * root = fd_bmtree_commit_fini( bmtree );
     258             : 
     259             :   /* Sign Merkle Root */
     260           0 :   shredder->signer( shredder->signer_ctx, root_signature, root );
     261             : 
     262             :   /* Write signature and Merkle proof */
     263           0 :   for( ulong i=0UL; i<data_shred_cnt; i++ ) {
     264           0 :     fd_shred_t * shred = result->data_shreds[ i ].s;
     265           0 :     fd_memcpy( shred->signature, root_signature, FD_ED25519_SIG_SZ );
     266             : 
     267           0 :     uchar * merkle = result->data_shreds[ i ].b + fd_shred_merkle_off( shred );
     268           0 :     fd_bmtree_get_proof( bmtree, merkle, i );
     269             : 
     270             :     /* Agave doesn't seem to set the rentransmitter signature when the shred is first created,
     271             :        i.e. the leader sends shreds with rentransmitter signature set to 0.
     272             :        https://github.com/anza-xyz/agave/blob/v2.2.10/ledger/src/shred/merkle.rs#L1417-L1418 */
     273           0 :     if( FD_UNLIKELY( is_resigned ) ) {
     274           0 :       memset( ((uchar*)shred) + fd_shred_retransmitter_sig_off( shred ), 0, 64UL );
     275           0 :     }
     276           0 :   }
     277             : 
     278           0 :   for( ulong j=0UL; j<parity_shred_cnt; j++ ) {
     279           0 :     fd_shred_t * shred = result->parity_shreds[ j ].s;
     280           0 :     fd_memcpy( shred->signature, root_signature, FD_ED25519_SIG_SZ );
     281             : 
     282           0 :     uchar * merkle = result->parity_shreds[ j ].b + fd_shred_merkle_off( shred );
     283           0 :     fd_bmtree_get_proof( bmtree, merkle, data_shred_cnt+j );
     284             : 
     285           0 :     if( FD_UNLIKELY( is_resigned ) ) {
     286           0 :       memset( ((uchar*)shred) + fd_shred_retransmitter_sig_off( shred ), 0, 64UL );
     287           0 :     }
     288           0 :   }
     289             : 
     290           0 :   shredder->offset             = offset;
     291           0 :   shredder->data_idx_offset   += data_shred_cnt;
     292           0 :   shredder->parity_idx_offset += parity_shred_cnt;
     293             : 
     294           0 :   memcpy( chained_merkle_root, root, FD_SHRED_MERKLE_ROOT_SZ );
     295             : 
     296           0 :   return result;
     297           0 : }
     298             : 
     299           0 : fd_shredder_t * fd_shredder_fini_batch( fd_shredder_t * shredder ) {
     300           0 :   shredder->entry_batch = NULL;
     301           0 :   shredder->sz          = 0UL;
     302           0 :   shredder->offset      = 0UL;
     303             : 
     304           0 :   return shredder;
     305           0 : }

Generated by: LCOV version 1.14