LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_stake_history.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 90 101 89.1 %
Date: 2026-07-10 05:48:38 Functions: 5 5 100.0 %

          Line data    Source code
       1             : #include "fd_sysvar_stake_history.h"
       2             : #include "fd_sysvar.h"
       3             : #include "../fd_system_ids.h"
       4             : #include "../fd_accdb_svm.h"
       5             : #include "fd_sysvar_rent.h"
       6             : 
       7             : void
       8             : fd_sysvar_stake_history_init( fd_bank_t *        bank,
       9             :                               fd_accdb_t *       accdb,
      10          12 :                               fd_capture_ctx_t * capture_ctx ) {
      11          12 :   uchar data[ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ];
      12          12 :   fd_memset( data, 0, sizeof(data) );
      13          12 :   fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_stake_history_id, data, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
      14          12 : }
      15             : 
      16             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-rc.1/runtime/src/bank.rs#L2452-L2463 */
      17             : 
      18             : void
      19             : fd_sysvar_stake_history_update( fd_bank_t *                      bank,
      20             :                                 fd_accdb_t *                     accdb,
      21             :                                 fd_capture_ctx_t *               capture_ctx,
      22         222 :                                 fd_stake_history_entry_t const * entry ) {
      23         222 :   fd_accdb_svm_update_t update[1];
      24         222 :   fd_acc_t rw = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_stake_history_id, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
      25             : 
      26         222 :   if( FD_UNLIKELY( !rw.lamports ) ) {
      27             :     /* Initialize account if it did not exist */
      28           0 :     fd_memcpy( rw.owner, fd_sysvar_owner_id.key, sizeof(fd_pubkey_t) );
      29           0 :     rw.lamports = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
      30           0 :     rw.data_len = FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ;
      31           0 :     fd_memset( rw.data, 0, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
      32             :     /* Now a valid StakeHistory sysvar with zero entries */
      33         222 :   } else {
      34             :     /* Sanity check existing state */
      35         222 :     if( FD_UNLIKELY( 0!=memcmp( rw.owner, &fd_sysvar_owner_id, sizeof(fd_pubkey_t) ) ) ) {
      36           0 :       FD_LOG_ERR(( "stake history sysvar not owned by sysvar owner" ));
      37           0 :     }
      38         222 :   }
      39             : 
      40         222 :   if( FD_UNLIKELY( rw.data_len<8UL ) ) FD_LOG_ERR(( "invalid stake history sysvar" ));
      41             : 
      42         222 :   ulong len = FD_LOAD( ulong, rw.data );
      43         222 :   len = fd_ulong_min( len, FD_SYSVAR_STAKE_HISTORY_CAP );
      44         222 :   ulong min_sz = 8UL + len * sizeof(fd_stake_history_entry_t);
      45         222 :   if( FD_UNLIKELY( rw.data_len<min_sz ) ) {
      46           0 :     FD_LOG_ERR(( "invalid stake history sysvar: data_len too small (%lu, required %lu)", rw.data_len, min_sz ));
      47           0 :   }
      48             : 
      49             :   /* https://github.com/anza-xyz/solana-sdk/blob/account%40v4.3.0/account/src/lib.rs#L618 */
      50         222 :   if( FD_UNLIKELY( rw.data_len!=FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ) ) rw.data_len = FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ;
      51             : 
      52         222 :   fd_stake_history_entry_t * entries = fd_type_pun( rw.data+8UL );
      53             : 
      54             :   /* https://github.com/solana-program/stake/blob/interface%40v4.0.0/interface/src/stake_history.rs#L83 */
      55         222 :   ulong idx   = 0UL;
      56         222 :   int   found = 0;
      57         222 :   {
      58         222 :     ulong lo = 0UL;
      59         222 :     ulong hi = len;
      60         390 :     while( lo < hi ) {
      61         174 :       ulong mid         = lo + (hi - lo) / 2UL;
      62         174 :       ulong probe_epoch = entries[mid].epoch;
      63         174 :       if( entry->epoch == probe_epoch ) {
      64           6 :         idx   = mid;
      65           6 :         found = 1;
      66           6 :         break;
      67         168 :       } else if( entry->epoch > probe_epoch ) {
      68         135 :         hi = mid;
      69         135 :       } else {
      70          33 :         lo = mid + 1UL;
      71          33 :       }
      72         174 :     }
      73         222 :     if( !found ) idx = lo;
      74         222 :   }
      75             : 
      76             :   /* Ensure account is rent exempt
      77             :      https://github.com/anza-xyz/agave/blob/v4.0.0-rc.1/runtime/src/bank.rs#L5849-L5854 */
      78         222 :   ulong rent_min = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
      79         222 :   if( FD_UNLIKELY( rent_min>rw.lamports ) ) rw.lamports = rent_min;
      80             : 
      81             :   /* Insert new element */
      82         222 :   ulong new_len = fd_ulong_if( found, len, fd_ulong_min( len+1UL, FD_SYSVAR_STAKE_HISTORY_CAP ) );
      83         222 :   ulong used_sz = 8UL + new_len * sizeof(fd_stake_history_entry_t);
      84             : 
      85         222 :   if( found ) {
      86             :     /* https://github.com/solana-program/stake/blob/interface%40v4.0.0/interface/src/stake_history.rs#L84 */
      87           6 :     entries[ idx ] = *entry;
      88         216 :   } else if( idx < FD_SYSVAR_STAKE_HISTORY_CAP ) {
      89             :     /* https://github.com/solana-program/stake/blob/interface%40v4.0.0/interface/src/stake_history.rs#L85
      90             :        https://github.com/solana-program/stake/blob/interface%40v4.0.0/interface/src/stake_history.rs#L87 */
      91         213 :     ulong shift_count = fd_ulong_min( len, FD_SYSVAR_STAKE_HISTORY_CAP-1UL ) - idx;
      92         213 :     memmove( &entries[ idx+1UL ], &entries[ idx ], shift_count * sizeof(fd_stake_history_entry_t) );
      93         213 :     entries[ idx ] = *entry;
      94         213 :     new_len = fd_ulong_min( len+1UL, FD_SYSVAR_STAKE_HISTORY_CAP );
      95         213 :   }
      96             :   /* else: idx == cap and not found - new entry would be truncated, drop */
      97             : 
      98             :   /* Zero trailing bytes (technically a no-op) */
      99         222 :   FD_TEST( used_sz <= FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
     100         222 :   fd_memset( rw.data+used_sz, 0, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ-used_sz );
     101             : 
     102         222 :   FD_STORE( ulong, rw.data, new_len );
     103         222 :   fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &rw, update );
     104         222 : }
     105             : 
     106             : int
     107             : fd_sysvar_stake_history_validate( uchar const * data,
     108        9048 :                                   ulong         sz ) {
     109        9048 :   if( FD_UNLIKELY( sz < 8UL ) ) return 0;
     110        9048 :   ulong len = FD_LOAD( ulong, data );
     111        9048 :   ulong min_sz;
     112        9048 :   if( FD_UNLIKELY( __builtin_umull_overflow( len, 32UL, &min_sz ) ) ) return 0;
     113        9048 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 8UL, &min_sz ) ) ) return 0;
     114        9048 :   if( FD_UNLIKELY( sz < min_sz ) ) return 0;
     115        9048 :   return 1;
     116        9048 : }
     117             : 
     118             : fd_stake_history_t *
     119             : fd_sysvar_stake_history_view( fd_stake_history_t * view,
     120             :                               uchar const *        data,
     121        1302 :                               ulong                sz ) {
     122        1302 :   if( FD_UNLIKELY( !fd_sysvar_stake_history_validate( data, sz ) ) ) return NULL;
     123        1302 :   view->len     = FD_LOAD( ulong, data );
     124        1302 :   view->entries = fd_type_pun_const( data + 8UL );
     125        1302 :   return view;
     126        1302 : }
     127             : 
     128             : fd_stake_history_entry_t const *
     129             : fd_sysvar_stake_history_query( fd_stake_history_t const * view,
     130         684 :                                ulong                      epoch ) {
     131         684 :   if( FD_UNLIKELY( !view || !view->len ) ) return NULL;
     132         489 :   if( epoch > view->entries[0].epoch ) return NULL;
     133             : 
     134         486 :   ulong off = view->entries[0].epoch - epoch;
     135         486 :   if( off < view->len && view->entries[off].epoch == epoch ) {
     136          24 :     return &view->entries[off];
     137          24 :   }
     138             : 
     139         462 :   ulong lo = 0UL;
     140         462 :   ulong hi = view->len - 1UL;
     141        1587 :   while( lo <= hi ) {
     142        1134 :     ulong mid = lo + ( hi - lo ) / 2UL;
     143        1134 :     if( view->entries[mid].epoch == epoch ) {
     144           9 :       return &view->entries[mid];
     145        1125 :     } else if( view->entries[mid].epoch > epoch ) {
     146        1125 :       lo = mid + 1UL;
     147        1125 :     } else {
     148           0 :       if( mid == 0UL ) return NULL;
     149           0 :       hi = mid - 1UL;
     150           0 :     }
     151        1134 :   }
     152         453 :   return NULL;
     153         462 : }

Generated by: LCOV version 1.14