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 32 : fd_stake_history_entry_t const * entry ) {
22 32 : fd_accdb_svm_update_t update[1];
23 32 : fd_acc_t rw = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_stake_history_id, 1 );
24 :
25 32 : 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 32 : } else {
33 : /* Sanity check existing state */
34 32 : 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 32 : }
38 :
39 32 : if( FD_UNLIKELY( rw.data_len<8UL ) ) FD_LOG_ERR(( "invalid stake history sysvar" ));
40 :
41 32 : ulong len = FD_LOAD( ulong, rw.data );
42 32 : len = fd_ulong_min( len, FD_SYSVAR_STAKE_HISTORY_CAP );
43 32 : ulong min_sz = 8UL + len * sizeof(fd_stake_history_entry_t);
44 32 : 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 32 : if( FD_UNLIKELY( rw.data_len!=FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ) ) rw.data_len = FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ;
50 :
51 32 : 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 32 : ulong idx = 0UL;
55 32 : int found = 0;
56 32 : {
57 32 : ulong lo = 0UL;
58 32 : ulong hi = len;
59 228 : 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 32 : if( !found ) idx = lo;
73 32 : }
74 :
75 : /* Insert new element */
76 32 : ulong new_len = fd_ulong_if( found, len, fd_ulong_min( len+1UL, FD_SYSVAR_STAKE_HISTORY_CAP ) );
77 32 : ulong used_sz = 8UL + new_len * sizeof(fd_stake_history_entry_t);
78 :
79 32 : 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 32 : } 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 32 : ulong shift_count = fd_ulong_min( len, FD_SYSVAR_STAKE_HISTORY_CAP-1UL ) - idx;
86 32 : memmove( &entries[ idx+1UL ], &entries[ idx ], shift_count * sizeof(fd_stake_history_entry_t) );
87 32 : entries[ idx ] = *entry;
88 32 : new_len = fd_ulong_min( len+1UL, FD_SYSVAR_STAKE_HISTORY_CAP );
89 32 : }
90 : /* else: idx == cap and not found - new entry would be truncated, drop */
91 :
92 : /* Zero trailing bytes (technically a no-op) */
93 32 : FD_TEST( used_sz <= FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
94 32 : fd_memset( rw.data+used_sz, 0, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ-used_sz );
95 :
96 32 : FD_STORE( ulong, rw.data, new_len );
97 :
98 : /* Balance is updated later by fd_stake_history_ensure_rent_exempt. */
99 32 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &rw, update );
100 32 : }
101 :
102 : void
103 : fd_stake_history_ensure_rent_exempt( fd_bank_t * bank,
104 : fd_accdb_t * accdb,
105 32 : fd_capture_ctx_t * capture_ctx ) {
106 32 : fd_accdb_svm_update_t update[1];
107 32 : fd_acc_t rw = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_stake_history_id, 0 );
108 32 : if( FD_UNLIKELY( !rw.lamports ) ) return; /* already released */
109 :
110 32 : fd_sysvar_adjust_balance_for_rent( bank, &rw );
111 32 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &rw, update );
112 32 : }
113 :
114 : int
115 : fd_sysvar_stake_history_validate( uchar const * data,
116 12977 : ulong sz ) {
117 12977 : if( FD_UNLIKELY( sz < 8UL ) ) return 0;
118 12977 : ulong len = FD_LOAD( ulong, data );
119 12977 : ulong min_sz;
120 12977 : if( FD_UNLIKELY( __builtin_umull_overflow( len, 32UL, &min_sz ) ) ) return 0;
121 12972 : if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 8UL, &min_sz ) ) ) return 0;
122 12972 : if( FD_UNLIKELY( sz < min_sz ) ) return 0;
123 12962 : return 1;
124 12972 : }
125 :
126 : fd_stake_history_t *
127 : fd_sysvar_stake_history_view( fd_stake_history_t * view,
128 : uchar const * data,
129 1409 : ulong sz ) {
130 1409 : if( FD_UNLIKELY( !fd_sysvar_stake_history_validate( data, sz ) ) ) return NULL;
131 1409 : view->len = FD_LOAD( ulong, data );
132 1409 : view->entries = fd_type_pun_const( data + 8UL );
133 1409 : return view;
134 1409 : }
135 :
136 : fd_stake_history_entry_t const *
137 : fd_sysvar_stake_history_query( fd_stake_history_t const * view,
138 8736 : ulong epoch ) {
139 8736 : if( FD_UNLIKELY( !view || !view->len || epoch>view->entries[0].epoch ) ) return NULL;
140 8689 : ulong index = view->entries[0].epoch - epoch;
141 8689 : if( FD_UNLIKELY( index>=view->len ) ) return NULL;
142 7580 : return &view->entries[index];
143 8689 : }
144 :
145 : int
146 646 : fd_sysvar_stake_history_is_contiguous( fd_stake_history_t const * view ) {
147 646 : if( FD_UNLIKELY( !view || !view->len ) ) return 1;
148 642 : ulong newest = view->entries[0].epoch;
149 642 : if( FD_UNLIKELY( view->len-1UL>newest ) ) return 0;
150 130806 : for( ulong i=1UL; i<view->len; i++ ) {
151 130189 : if( FD_UNLIKELY( view->entries[i].epoch!=newest-i ) ) return 0;
152 130189 : }
153 617 : return 1;
154 642 : }
|