LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_slot_history.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 111 125 88.8 %
Date: 2026-11-13 04:25:15 Functions: 5 5 100.0 %

          Line data    Source code
       1             : #include "fd_sysvar_slot_history.h"
       2             : #include "fd_sysvar.h"
       3             : #include "../fd_system_ids.h"
       4             : #include "../fd_accdb_svm.h"
       5             : 
       6   182866272 : #define FD_SLOT_HISTORY_BITS_PER_BLOCK (8UL * sizeof(ulong))
       7             : 
       8          21 : #define FD_SLOT_HISTORY_BLOCKS_LEN (FD_SLOT_HISTORY_MAX_ENTRIES / FD_SLOT_HISTORY_BITS_PER_BLOCK)
       9             : 
      10             : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L16 */
      11             : 
      12             : void
      13             : fd_sysvar_slot_history_init( fd_bank_t *        bank,
      14             :                              fd_accdb_t *       accdb,
      15           9 :                              fd_capture_ctx_t * capture_ctx ) {
      16           9 :   uchar data[ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ];
      17           9 :   uchar * p = data;
      18             : 
      19             :   /* has_bits */
      20           9 :   *p = 1;
      21           9 :   p++;
      22             : 
      23             :   /* bits_bitvec_len */
      24           9 :   FD_STORE( ulong, p, FD_SLOT_HISTORY_BLOCKS_LEN );
      25           9 :   p += sizeof(ulong);
      26             : 
      27             :   /* content */
      28           9 :   fd_memset( p, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
      29           9 :   p += FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong);
      30             : 
      31             :   /* bits_len */
      32           9 :   FD_STORE( ulong, p, FD_SLOT_HISTORY_MAX_ENTRIES );
      33           9 :   p += sizeof(ulong);
      34             : 
      35             :   /* next_slot */
      36           9 :   FD_STORE( ulong, p, bank->f.slot + 1UL );
      37           9 :   p += sizeof(ulong);
      38             : 
      39           9 :   FD_STATIC_ASSERT( FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ == 1 + sizeof(ulong) + FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) + sizeof(ulong) + sizeof(ulong), "bin code size mismatch" );
      40             : 
      41           9 :   fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_slot_history_id, data, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ );
      42           9 : }
      43             : 
      44             : void
      45             : fd_sysvar_slot_history_update( fd_bank_t *        bank,
      46             :                                fd_accdb_t *       accdb,
      47         432 :                                fd_capture_ctx_t * capture_ctx ) {
      48         432 :   fd_accdb_svm_update_t update[1];
      49         432 :   fd_acc_t acc = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_slot_history_id, 1 );
      50         432 :   if( FD_UNLIKELY( !acc.lamports ) ) {
      51             :     /* SlotHistory::default() contains slot zero, with next_slot=1. */
      52           9 :     acc.data_len = FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ;
      53           9 :     acc.executable = 0;
      54           9 :     fd_memcpy( acc.owner, fd_sysvar_owner_id.uc, sizeof(fd_pubkey_t) );
      55           9 :     fd_memset( acc.data, 0, acc.data_len );
      56           9 :     acc.data[0] = 1;
      57           9 :     FD_STORE( ulong, acc.data+1UL, FD_SLOT_HISTORY_BLOCKS_LEN );
      58           9 :     FD_STORE( ulong, acc.data+9UL, 1UL );
      59           9 :     FD_STORE( ulong, acc.data+9UL+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), FD_SLOT_HISTORY_MAX_ENTRIES );
      60           9 :     FD_STORE( ulong, acc.data+17UL+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), 1UL );
      61           9 :   }
      62         432 :   FD_TEST( !memcmp( acc.owner, fd_sysvar_owner_id.uc, sizeof(fd_pubkey_t) ) ); /* Slot history account must be owned by sysvar owner */
      63         432 :   if( FD_UNLIKELY( !fd_sysvar_slot_history_validate( acc.data, acc.data_len ) ) ) {
      64           0 :     FD_LOG_HEXDUMP_ERR(( "invalid slot history sysvar", acc.data, acc.data_len ));
      65           0 :   }
      66             : 
      67         432 :   if( FD_UNLIKELY( acc.data[ 0 ]!=1 ) ) {
      68             :     /* initialize if !has_bits */
      69           3 :     if( FD_UNLIKELY( acc.data_len<FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ) ) FD_LOG_HEXDUMP_ERR(( "invalid slot history sysvar (data_sz too small)", acc.data, acc.data_len ));
      70           3 :     acc.data[ 0 ] = 1;
      71           3 :     FD_STORE( ulong, acc.data+1, FD_SLOT_HISTORY_BLOCKS_LEN );
      72           3 :     fd_memset( acc.data+9, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
      73           3 :     FD_STORE( ulong, acc.data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), FD_SLOT_HISTORY_MAX_ENTRIES );
      74           3 :     FD_STORE( ulong, acc.data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong)+8UL, 0UL );
      75           3 :   }
      76             : 
      77         432 :   ulong bits_bitvec_len = FD_LOAD( ulong, acc.data+1UL );
      78         432 :   if( FD_UNLIKELY( !bits_bitvec_len ) ) {
      79           3 :     fd_sysvar_adjust_balance_for_rent( bank, &acc );
      80           3 :     fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &acc, update );
      81           3 :     return;
      82           3 :   }
      83         429 :   ulong min_sz;
      84         429 :   if( FD_UNLIKELY( __builtin_umull_overflow( bits_bitvec_len, sizeof(ulong), &min_sz ) ) ) {
      85           0 :     FD_LOG_ERR(( "invalid slot history sysvar: bits_bitvec_len overflow (%lu)", bits_bitvec_len ));
      86           0 :   }
      87         429 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) {
      88           0 :     FD_LOG_ERR(( "invalid slot history sysvar: min_sz overflow" ));
      89           0 :   }
      90         429 :   if( FD_UNLIKELY( acc.data_len < min_sz ) ) {
      91           0 :     FD_LOG_ERR(( "invalid slot history sysvar: data_sz too small (%lu, required %lu)", acc.data_len, min_sz ));
      92           0 :   }
      93         429 :   uchar * bits      = acc.data + 9UL;
      94         429 :   uchar * footer    = acc.data + 9UL + bits_bitvec_len * sizeof(ulong);
      95         429 :   ulong   next_slot = FD_LOAD( ulong, footer+8UL );
      96             : 
      97             :   /* https://github.com/anza-xyz/solana-sdk/blob/slot-history%40v2.2.1/slot-history/src/lib.rs#L62-L74 */
      98         429 :   ulong cur_slot = bank->f.slot;
      99         429 :   if( FD_UNLIKELY( cur_slot>next_slot && cur_slot-next_slot>=FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
     100           6 :     fd_memset( bits, 0, bits_bitvec_len * sizeof(ulong) );
     101         423 :   } else {
     102        5937 :     for( ulong i=next_slot; i<cur_slot; i++ ) {
     103        5514 :       ulong   block_idx = (i / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
     104        5514 :       uchar * word      = &bits[ block_idx*sizeof(ulong) ];
     105        5514 :       FD_STORE( ulong, word, FD_LOAD( ulong, word ) & (~(1UL << (i % FD_SLOT_HISTORY_BITS_PER_BLOCK))) );
     106        5514 :     }
     107         423 :   }
     108             : 
     109             :   /* new slot */
     110         429 :   ulong   block_idx = (cur_slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
     111         429 :   uchar * word      = &bits[ block_idx*sizeof(ulong) ];
     112         429 :   FD_STORE( ulong, word, FD_LOAD( ulong, word ) | (1UL << (cur_slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) );
     113             : 
     114         429 :   FD_STORE( ulong, footer+8UL, cur_slot+1UL );
     115             : 
     116         429 :   fd_sysvar_adjust_balance_for_rent( bank, &acc );
     117         429 :   fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &acc, update );
     118         429 : }
     119             : 
     120             : int
     121             : fd_sysvar_slot_history_validate( uchar const * data,
     122       92228 :                                  ulong         sz ) {
     123       92228 :   if( FD_UNLIKELY( sz < 17UL ) ) return 0;
     124       92168 :   uchar has_bits = data[0];
     125       92168 :   if( FD_UNLIKELY( has_bits>1 ) ) return 0;
     126       92165 :   if( !has_bits ) return 1;
     127       92162 :   if( FD_UNLIKELY( sz < 25UL ) ) return 0;
     128       92132 :   ulong blocks_len = FD_LOAD( ulong, data+1 );
     129       92132 :   ulong min_sz;
     130       92132 :   if( FD_UNLIKELY( __builtin_umull_overflow( blocks_len, sizeof(ulong), &min_sz ) ) ) return 0;
     131       92129 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) return 0;
     132       92126 :   if( FD_UNLIKELY( sz < min_sz ) ) return 0;
     133       92123 :   return 1;
     134       92126 : }
     135             : 
     136             : fd_slot_history_view_t *
     137             : fd_sysvar_slot_history_view( fd_slot_history_view_t * view,
     138             :                              uchar const *            data,
     139       91691 :                              ulong                    sz ) {
     140       91691 :   if( FD_UNLIKELY( !fd_sysvar_slot_history_validate( data, sz ) ) ) return NULL;
     141       91685 :   if( FD_UNLIKELY( !data[0] ) ) {
     142           0 :     view->bits       = NULL;
     143           0 :     view->blocks_len = 0UL;
     144           0 :     view->bits_len   = FD_LOAD( ulong, data+1    );
     145           0 :     view->next_slot  = FD_LOAD( ulong, data+1+8UL );
     146           0 :     return view;
     147           0 :   }
     148       91685 :   ulong blocks_len = FD_LOAD( ulong, data+1 );
     149       91685 :   uchar const * footer = data + 9UL + blocks_len * sizeof(ulong);
     150       91685 :   view->bits       = data + 9UL;
     151       91685 :   view->blocks_len = blocks_len;
     152       91685 :   view->bits_len   = FD_LOAD( ulong, footer    );
     153       91685 :   view->next_slot  = FD_LOAD( ulong, footer+8UL );
     154       91685 :   return view;
     155       91685 : }
     156             : 
     157             : int
     158             : fd_sysvar_slot_history_find_slot( fd_slot_history_view_t const * view,
     159    91430202 :                                   ulong                          slot ) {
     160    91430202 :   if( FD_UNLIKELY( !view->blocks_len ) ) return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
     161    91430199 :   if( slot > view->next_slot - 1UL ) {
     162          30 :     return FD_SLOT_HISTORY_SLOT_FUTURE;
     163          30 :   }
     164    91430169 :   if( slot < fd_ulong_sat_sub( view->next_slot, FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
     165          15 :     return FD_SLOT_HISTORY_SLOT_TOO_OLD;
     166          15 :   }
     167    91430154 :   ulong block_idx = (slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % view->blocks_len;
     168    91430154 :   ulong word      = FD_LOAD( ulong, view->bits + block_idx*sizeof(ulong) );
     169    91430154 :   if( word & (1UL << (slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) ) {
     170       16326 :     return FD_SLOT_HISTORY_SLOT_FOUND;
     171       16326 :   }
     172    91413828 :   return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
     173    91430154 : }

Generated by: LCOV version 1.14