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 12 : fd_capture_ctx_t * capture_ctx ) {
10 12 : uchar data[ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ];
11 12 : fd_memset( data, 0, sizeof(data) );
12 12 : fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_stake_history_id, data, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
13 12 : }
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 309 : fd_stake_history_entry_t const * entry ) {
22 309 : fd_accdb_svm_update_t update[1];
23 309 : fd_acc_t rw = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_stake_history_id, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
24 :
25 309 : if( FD_UNLIKELY( !rw.lamports ) ) {
26 : /* Initialize account if it did not exist */
27 3 : fd_memcpy( rw.owner, fd_sysvar_owner_id.key, sizeof(fd_pubkey_t) );
28 3 : rw.lamports = FD_SYSVAR_RENT_UNADJUSTED_INITIAL_BALANCE;
29 3 : rw.data_len = FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ;
30 3 : fd_memset( rw.data, 0, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
31 : /* Now a valid StakeHistory sysvar with zero entries */
32 306 : } else {
33 : /* Sanity check existing state */
34 306 : 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 306 : }
38 :
39 309 : if( FD_UNLIKELY( rw.data_len<8UL ) ) FD_LOG_ERR(( "invalid stake history sysvar" ));
40 :
41 309 : ulong len = FD_LOAD( ulong, rw.data );
42 309 : len = fd_ulong_min( len, FD_SYSVAR_STAKE_HISTORY_CAP );
43 309 : ulong min_sz = 8UL + len * sizeof(fd_stake_history_entry_t);
44 309 : 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 309 : if( FD_UNLIKELY( rw.data_len!=FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ) ) rw.data_len = FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ;
50 :
51 309 : 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 309 : ulong idx = 0UL;
55 309 : int found = 0;
56 309 : {
57 309 : ulong lo = 0UL;
58 309 : ulong hi = len;
59 462 : while( lo < hi ) {
60 159 : ulong mid = lo + (hi - lo) / 2UL;
61 159 : ulong probe_epoch = entries[mid].epoch;
62 159 : if( entry->epoch == probe_epoch ) {
63 6 : idx = mid;
64 6 : found = 1;
65 6 : break;
66 153 : } else if( entry->epoch > probe_epoch ) {
67 120 : hi = mid;
68 120 : } else {
69 33 : lo = mid + 1UL;
70 33 : }
71 159 : }
72 309 : if( !found ) idx = lo;
73 309 : }
74 :
75 : /* Insert new element */
76 309 : ulong new_len = fd_ulong_if( found, len, fd_ulong_min( len+1UL, FD_SYSVAR_STAKE_HISTORY_CAP ) );
77 309 : ulong used_sz = 8UL + new_len * sizeof(fd_stake_history_entry_t);
78 :
79 309 : if( found ) {
80 : /* https://github.com/solana-program/stake/blob/interface%40v4.0.0/interface/src/stake_history.rs#L84 */
81 6 : entries[ idx ] = *entry;
82 303 : } 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 300 : ulong shift_count = fd_ulong_min( len, FD_SYSVAR_STAKE_HISTORY_CAP-1UL ) - idx;
86 300 : memmove( &entries[ idx+1UL ], &entries[ idx ], shift_count * sizeof(fd_stake_history_entry_t) );
87 300 : entries[ idx ] = *entry;
88 300 : new_len = fd_ulong_min( len+1UL, FD_SYSVAR_STAKE_HISTORY_CAP );
89 300 : }
90 : /* else: idx == cap and not found - new entry would be truncated, drop */
91 :
92 : /* Zero trailing bytes (technically a no-op) */
93 309 : FD_TEST( used_sz <= FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ );
94 309 : fd_memset( rw.data+used_sz, 0, FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ-used_sz );
95 :
96 309 : FD_STORE( ulong, rw.data, new_len );
97 :
98 : /* Balance is updated later by fd_stake_history_ensure_rent_exempt. */
99 309 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &rw, update );
100 309 : }
101 :
102 : void
103 : fd_stake_history_ensure_rent_exempt( fd_bank_t * bank,
104 : fd_accdb_t * accdb,
105 279 : fd_capture_ctx_t * capture_ctx ) {
106 279 : fd_accdb_svm_update_t update[1];
107 279 : fd_acc_t rw = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_stake_history_id, 0 );
108 279 : if( FD_UNLIKELY( !rw.lamports ) ) return; /* already released */
109 :
110 276 : fd_sysvar_adjust_balance_for_rent( bank, &rw );
111 276 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &rw, update );
112 276 : }
113 :
114 : int
115 : fd_sysvar_stake_history_validate( uchar const * data,
116 10023 : ulong sz ) {
117 10023 : if( FD_UNLIKELY( sz < 8UL ) ) return 0;
118 10023 : ulong len = FD_LOAD( ulong, data );
119 10023 : ulong min_sz;
120 10023 : if( FD_UNLIKELY( __builtin_umull_overflow( len, 32UL, &min_sz ) ) ) return 0;
121 10023 : if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 8UL, &min_sz ) ) ) return 0;
122 10023 : if( FD_UNLIKELY( sz < min_sz ) ) return 0;
123 10023 : return 1;
124 10023 : }
125 :
126 : fd_stake_history_t *
127 : fd_sysvar_stake_history_view( fd_stake_history_t * view,
128 : uchar const * data,
129 1638 : ulong sz ) {
130 1638 : if( FD_UNLIKELY( !fd_sysvar_stake_history_validate( data, sz ) ) ) return NULL;
131 1638 : view->len = FD_LOAD( ulong, data );
132 1638 : view->entries = fd_type_pun_const( data + 8UL );
133 1638 : return view;
134 1638 : }
135 :
136 : fd_stake_history_entry_t const *
137 : fd_sysvar_stake_history_query( fd_stake_history_t const * view,
138 513 : ulong epoch ) {
139 513 : if( FD_UNLIKELY( !view || !view->len || epoch>view->entries[0].epoch ) ) return NULL;
140 303 : ulong index = view->entries[0].epoch - epoch;
141 303 : if( FD_UNLIKELY( index>=view->len ) ) return NULL;
142 42 : return &view->entries[index];
143 303 : }
144 :
145 : int
146 804 : fd_sysvar_stake_history_is_contiguous( fd_stake_history_t const * view ) {
147 804 : if( FD_UNLIKELY( !view || !view->len ) ) return 1;
148 624 : ulong newest = view->entries[0].epoch;
149 624 : if( FD_UNLIKELY( view->len-1UL>newest ) ) return 0;
150 1470 : for( ulong i=1UL; i<view->len; i++ ) {
151 846 : if( FD_UNLIKELY( view->entries[i].epoch!=newest-i ) ) return 0;
152 846 : }
153 624 : return 1;
154 624 : }
|