LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_recent_hashes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 36 62 58.1 %
Date: 2025-10-13 04:42:14 Functions: 4 5 80.0 %

          Line data    Source code
       1             : #include "fd_sysvar_recent_hashes.h"
       2             : #include "../fd_acc_mgr.h"
       3             : #include "fd_sysvar.h"
       4             : #include "../fd_system_ids.h"
       5             : 
       6             : /* Skips fd_types encoding preflight checks and directly serializes the
       7             :    blockhash queue into a buffer representing account data for the
       8             :    recent blockhashes sysvar. */
       9             : 
      10             : static void
      11             : encode_rbh_from_blockhash_queue( fd_bank_t * bank,
      12         453 :                                  uchar       out_mem[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] ) {
      13         453 :   fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( bank );
      14             : 
      15         453 :   ulong queue_sz = fd_blockhash_deq_cnt( bhq->d.deque );
      16         453 :   ulong out_max  = fd_ulong_min( queue_sz, FD_SYSVAR_RECENT_HASHES_CAP );
      17             : 
      18         453 :   uchar * enc = out_mem;
      19         453 :   fd_memcpy( enc, &out_max, sizeof(ulong) );
      20             : 
      21         453 :   enc += sizeof(ulong);
      22             : 
      23             :   /* Iterate over blockhash queue and encode the recent blockhashes.
      24             :      We can do direct memcpying and avoid redundant checks from fd_types
      25             :      encoders since the enc buffer is already sized out to the
      26             :      worst-case bound. */
      27         453 :   ulong out_idx = 0UL;
      28         453 :   for( fd_blockhash_deq_iter_t iter = fd_blockhash_deq_iter_init_rev( bhq->d.deque );
      29       34428 :        out_idx<FD_SYSVAR_RECENT_HASHES_CAP &&
      30       34428 :           !fd_blockhash_deq_iter_done_rev( bhq->d.deque, iter );
      31       33975 :        out_idx++,   iter = fd_blockhash_deq_iter_prev( bhq->d.deque, iter ) ) {
      32       33975 :     fd_blockhash_info_t const * n = fd_blockhash_deq_iter_ele_const( bhq->d.deque, iter );
      33       33975 :     fd_memcpy( enc, n->hash.uc, 32 );
      34       33975 :     FD_STORE( ulong, enc+32, n->fee_calculator.lamports_per_signature );
      35       33975 :     enc += 40;
      36       33975 :   }
      37         453 : }
      38             : 
      39             : void
      40             : fd_sysvar_recent_hashes_init( fd_bank_t *               bank,
      41             :                               fd_funk_t *               funk,
      42             :                               fd_funk_txn_xid_t const * xid,
      43           3 :                               fd_capture_ctx_t *        capture_ctx ) {
      44           3 :   uchar enc[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] = {0};
      45           3 :   encode_rbh_from_blockhash_queue( bank, enc );
      46           3 :   fd_sysvar_account_update( bank, funk, xid, capture_ctx, &fd_sysvar_recent_block_hashes_id, enc, FD_SYSVAR_RECENT_HASHES_BINCODE_SZ );
      47           3 : }
      48             : 
      49             : // https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L113
      50             : static void
      51             : register_blockhash( fd_bank_t *       bank,
      52         450 :                     fd_hash_t const * hash ) {
      53         450 :   fd_blockhashes_t * bhq = fd_bank_block_hash_queue_modify( bank );
      54         450 :   fd_blockhash_info_t * bh = fd_blockhashes_push_new( bhq, hash );
      55         450 :   bh->fee_calculator = (fd_fee_calculator_t){
      56         450 :     .lamports_per_signature = fd_bank_lamports_per_signature_get( bank )
      57         450 :   };
      58         450 : }
      59             : 
      60             : /* This implementation is more consistent with Agave's bank implementation for updating the block hashes sysvar:
      61             :    1. Update the block hash queue with the latest poh
      62             :    2. Take the first 150 blockhashes from the queue (or fewer if there are)
      63             :    3. Manually serialize the recent blockhashes
      64             :    4. Set the sysvar account with the new data */
      65             : void
      66             : fd_sysvar_recent_hashes_update( fd_bank_t *               bank,
      67             :                                 fd_funk_t *               funk,
      68             :                                 fd_funk_txn_xid_t const * xid,
      69         450 :                                 fd_capture_ctx_t *        capture_ctx ) {
      70         450 :   register_blockhash( bank, fd_bank_poh_query( bank ) );
      71             : 
      72         450 :   uchar enc[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] = {0};
      73         450 :   encode_rbh_from_blockhash_queue( bank, enc );
      74         450 :   fd_sysvar_account_update( bank, funk, xid, capture_ctx, &fd_sysvar_recent_block_hashes_id, enc, sizeof(enc) );
      75         450 : }
      76             : 
      77             : fd_recent_block_hashes_t *
      78           0 : fd_sysvar_recent_hashes_read( fd_funk_t * funk, fd_funk_txn_xid_t const * xid, fd_spad_t * spad ) {
      79           0 :   FD_TXN_ACCOUNT_DECL( acc );
      80           0 :   int err = fd_txn_account_init_from_funk_readonly( acc, &fd_sysvar_recent_block_hashes_id, funk, xid );
      81           0 :   if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) )
      82           0 :     return NULL;
      83             : 
      84           0 :   fd_bincode_decode_ctx_t ctx = {
      85           0 :     .data    = fd_txn_account_get_data( acc ),
      86           0 :     .dataend = fd_txn_account_get_data( acc ) + fd_txn_account_get_data_len( acc ),
      87           0 :   };
      88             : 
      89             :   /* This check is needed as a quirk of the fuzzer. If a sysvar account
      90             :      exists in the accounts database, but doesn't have any lamports,
      91             :      this means that the account does not exist. This wouldn't happen
      92             :      in a real execution environment. */
      93           0 :   if( FD_UNLIKELY( fd_txn_account_get_lamports( acc )==0UL ) ) {
      94           0 :     return NULL;
      95           0 :   }
      96             : 
      97           0 :   ulong total_sz = 0;
      98           0 :   err = fd_recent_block_hashes_decode_footprint( &ctx, &total_sz );
      99           0 :   if( FD_UNLIKELY( err ) ) {
     100           0 :     return NULL;
     101           0 :   }
     102             : 
     103           0 :   uchar * mem = fd_spad_alloc( spad, fd_recent_block_hashes_align(), total_sz );
     104           0 :   if( FD_UNLIKELY( !mem ) ) {
     105           0 :     FD_LOG_CRIT(( "fd_spad_alloc failed" ));
     106           0 :   }
     107             : 
     108             :   /* This would never happen in a real cluster, this is a workaround
     109             :      for fuzz-generated cases where sysvar accounts are not funded. */
     110           0 :   if( FD_UNLIKELY( fd_txn_account_get_lamports( acc ) == 0 ) ) {
     111           0 :     return NULL;
     112           0 :   }
     113             : 
     114           0 :   return fd_recent_block_hashes_decode( mem, &ctx );
     115           0 : }

Generated by: LCOV version 1.14