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: 93 118 78.8 %
Date: 2026-05-15 07:18:56 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         714 : #define FD_SLOT_HISTORY_BITS_PER_BLOCK (8UL * sizeof(ulong))
       7             : 
       8          18 : #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_user_t *         accdb,
      15             :                              fd_funk_txn_xid_t const * xid,
      16           9 :                              fd_capture_ctx_t *        capture_ctx ) {
      17           9 :   uchar data[ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ];
      18           9 :   uchar * p = data;
      19             : 
      20             :   /* has_bits */
      21           9 :   *p = 1;
      22           9 :   p++;
      23             : 
      24             :   /* bits_bitvec_len */
      25           9 :   FD_STORE( ulong, p, FD_SLOT_HISTORY_BLOCKS_LEN );
      26           9 :   p += sizeof(ulong);
      27             : 
      28             :   /* content */
      29           9 :   fd_memset( p, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
      30           9 :   p += FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong);
      31             : 
      32             :   /* bits_len */
      33           9 :   FD_STORE( ulong, p, FD_SLOT_HISTORY_MAX_ENTRIES );
      34           9 :   p += sizeof(ulong);
      35             : 
      36             :   /* next_slot */
      37           9 :   FD_STORE( ulong, p, bank->f.slot + 1UL );
      38           9 :   p += sizeof(ulong);
      39             : 
      40           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" );
      41             : 
      42           9 :   fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_slot_history_id, data, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ );
      43           9 : }
      44             : 
      45             : void
      46             : fd_sysvar_slot_history_update( fd_bank_t *               bank,
      47             :                                fd_accdb_user_t *         accdb,
      48             :                                fd_funk_txn_xid_t const * xid,
      49          96 :                                fd_capture_ctx_t *        capture_ctx ) {
      50             : 
      51          96 :   ulong cur_slot = bank->f.slot;
      52             : 
      53          96 :   fd_accdb_rw_t rw[1];
      54          96 :   fd_accdb_svm_update_t update[1];
      55          96 :   if( FD_UNLIKELY( !fd_accdb_svm_open_rw( accdb, bank, xid, rw, update, &fd_sysvar_slot_history_id, 0UL, 0 ) ) ) {
      56           0 :     FD_LOG_ERR(( "state is missing slot history sysvar" ));
      57           0 :   }
      58          96 :   if( FD_UNLIKELY( 0!=memcmp( fd_accdb_ref_owner( rw->ro ), &fd_sysvar_owner_id, sizeof(fd_pubkey_t) ) ) ) {
      59           0 :     FD_LOG_ERR(( "slot history sysvar not owned by sysvar owner" ));
      60           0 :   }
      61          96 :   uchar * data    = fd_accdb_ref_data   ( rw );
      62          96 :   ulong   data_sz = fd_accdb_ref_data_sz( rw->ro );
      63          96 :   if( FD_UNLIKELY( data[0]!=1 ) ) {
      64             :     /* initialize if !has_bits */
      65           0 :     if( FD_UNLIKELY( data_sz < FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ) ) {
      66           0 :       FD_LOG_HEXDUMP_ERR(( "invalid slot history sysvar (data_sz too small)", data, data_sz ));
      67           0 :     }
      68           0 :     data[0] = 1;
      69           0 :     FD_STORE( ulong, data+1, FD_SLOT_HISTORY_BLOCKS_LEN );
      70           0 :     fd_memset( data+9, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
      71           0 :     FD_STORE( ulong, data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), FD_SLOT_HISTORY_MAX_ENTRIES );
      72           0 :     FD_STORE( ulong, data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong)+8UL, 0UL );
      73           0 :   }
      74          96 :   ulong bits_bitvec_len = FD_LOAD( ulong, data+1 );
      75          96 :   if( FD_UNLIKELY( !bits_bitvec_len ) ) {
      76           3 :     fd_accdb_svm_close_rw( accdb, bank, capture_ctx, rw, update );
      77           3 :     return;
      78           3 :   }
      79          93 :   ulong min_sz;
      80          93 :   if( FD_UNLIKELY( __builtin_umull_overflow( bits_bitvec_len, sizeof(ulong), &min_sz ) ) ) {
      81           0 :     FD_LOG_ERR(( "invalid slot history sysvar: bits_bitvec_len overflow (%lu)", bits_bitvec_len ));
      82           0 :   }
      83          93 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) {
      84           0 :     FD_LOG_ERR(( "invalid slot history sysvar: min_sz overflow" ));
      85           0 :   }
      86          93 :   if( FD_UNLIKELY( data_sz < min_sz ) ) {
      87           0 :     FD_LOG_ERR(( "invalid slot history sysvar: data_sz too small (%lu, required %lu)", data_sz, min_sz ));
      88           0 :   }
      89          93 :   uchar * bits      = data + 9UL;
      90          93 :   uchar * footer    = data + 9UL + bits_bitvec_len * sizeof(ulong);
      91          93 :   ulong   next_slot = FD_LOAD( ulong, footer+8UL );
      92             : 
      93             :   /* https://github.com/anza-xyz/solana-sdk/blob/slot-history%40v2.2.1/slot-history/src/lib.rs#L62-L74 */
      94          93 :   if( FD_UNLIKELY( cur_slot > next_slot && cur_slot - next_slot >= FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
      95           3 :     fd_memset( bits, 0, bits_bitvec_len * sizeof(ulong) );
      96          90 :   } else {
      97         603 :     for( ulong i=next_slot; i<cur_slot; i++ ) {
      98         513 :       ulong   block_idx = (i / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
      99         513 :       uchar * word      = &bits[ block_idx*sizeof(ulong) ];
     100         513 :       FD_STORE( ulong, word, FD_LOAD( ulong, word ) & (~(1UL << (i % FD_SLOT_HISTORY_BITS_PER_BLOCK))) );
     101         513 :     }
     102          90 :   }
     103             : 
     104             :   /* new slot */
     105          93 :   ulong   block_idx = (cur_slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
     106          93 :   uchar * word      = &bits[ block_idx*sizeof(ulong) ];
     107          93 :   FD_STORE( ulong, word, FD_LOAD( ulong, word ) | (1UL << (cur_slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) );
     108             : 
     109          93 :   FD_STORE( ulong, footer+8UL, cur_slot+1UL );
     110             : 
     111          93 :   fd_accdb_svm_close_rw( accdb, bank, capture_ctx, rw, update );
     112          93 : }
     113             : 
     114             : int
     115             : fd_sysvar_slot_history_validate( uchar const * data,
     116        6993 :                                  ulong         sz ) {
     117        6993 :   if( FD_UNLIKELY( sz < 17UL ) ) return 0;
     118        6984 :   uchar has_bits = data[0];
     119        6984 :   if( FD_UNLIKELY( has_bits>1 ) ) return 0;
     120        6981 :   if( !has_bits ) return 1;
     121        6981 :   if( FD_UNLIKELY( sz < 25UL ) ) return 0;
     122        6972 :   ulong blocks_len = FD_LOAD( ulong, data+1 );
     123        6972 :   ulong min_sz;
     124        6972 :   if( FD_UNLIKELY( __builtin_umull_overflow( blocks_len, sizeof(ulong), &min_sz ) ) ) return 0;
     125        6969 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) return 0;
     126        6966 :   if( FD_UNLIKELY( sz < min_sz ) ) return 0;
     127        6963 :   return 1;
     128        6966 : }
     129             : 
     130             : fd_slot_history_view_t *
     131             : fd_sysvar_slot_history_view( fd_slot_history_view_t * view,
     132             :                              uchar const *            data,
     133          33 :                              ulong                    sz ) {
     134          33 :   if( FD_UNLIKELY( !fd_sysvar_slot_history_validate( data, sz ) ) ) return NULL;
     135          27 :   if( FD_UNLIKELY( !data[0] ) ) {
     136           0 :     view->bits       = NULL;
     137           0 :     view->blocks_len = 0UL;
     138           0 :     view->bits_len   = FD_LOAD( ulong, data+1    );
     139           0 :     view->next_slot  = FD_LOAD( ulong, data+1+8UL );
     140           0 :     return view;
     141           0 :   }
     142          27 :   ulong blocks_len = FD_LOAD( ulong, data+1 );
     143          27 :   uchar const * footer = data + 9UL + blocks_len * sizeof(ulong);
     144          27 :   view->bits       = data + 9UL;
     145          27 :   view->blocks_len = blocks_len;
     146          27 :   view->bits_len   = FD_LOAD( ulong, footer    );
     147          27 :   view->next_slot  = FD_LOAD( ulong, footer+8UL );
     148          27 :   return view;
     149          27 : }
     150             : 
     151             : int
     152             : fd_sysvar_slot_history_find_slot( fd_slot_history_view_t const * view,
     153          81 :                                   ulong                          slot ) {
     154          81 :   if( FD_UNLIKELY( !view->blocks_len ) ) return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
     155          78 :   if( slot > view->next_slot - 1UL ) {
     156          21 :     return FD_SLOT_HISTORY_SLOT_FUTURE;
     157          21 :   }
     158          57 :   if( slot < fd_ulong_sat_sub( view->next_slot, FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
     159          12 :     return FD_SLOT_HISTORY_SLOT_TOO_OLD;
     160          12 :   }
     161          45 :   ulong block_idx = (slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % view->blocks_len;
     162          45 :   ulong word      = FD_LOAD( ulong, view->bits + block_idx*sizeof(ulong) );
     163          45 :   if( word & (1UL << (slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) ) {
     164          21 :     return FD_SLOT_HISTORY_SLOT_FOUND;
     165          21 :   }
     166          24 :   return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
     167          45 : }

Generated by: LCOV version 1.14