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

Generated by: LCOV version 1.14