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 : }
|