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: 82 101 81.2 %
Date: 2026-11-11 12:40:43 Functions: 6 7 85.7 %

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

Generated by: LCOV version 1.14