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

Generated by: LCOV version 1.14