Line data Source code
1 : #include "fd_stakes.h"
2 : #include "../runtime/fd_acc_mgr.h"
3 : #include "../runtime/fd_system_ids.h"
4 : #include "../runtime/context/fd_exec_slot_ctx.h"
5 : #include "../runtime/program/fd_stake_program.h"
6 : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
7 : #include "fd_stake_delegations.h"
8 :
9 : ulong
10 : fd_stake_weights_by_node( fd_vote_states_t const * vote_states,
11 0 : fd_vote_stake_weight_t * weights ) {
12 :
13 0 : ulong weights_cnt = 0;
14 0 : fd_vote_states_iter_t iter_[1];
15 0 : for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( iter_, vote_states ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) {
16 0 : fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
17 0 : if( FD_UNLIKELY( !vote_state->stake ) ) continue;
18 :
19 0 : fd_memcpy( weights[ weights_cnt ].vote_key.uc, &vote_state->vote_account, sizeof(fd_pubkey_t) );
20 0 : fd_memcpy( weights[ weights_cnt ].id_key.uc, &vote_state->node_account, sizeof(fd_pubkey_t) );
21 0 : weights[ weights_cnt ].stake = vote_state->stake;
22 0 : weights_cnt++;
23 0 : }
24 0 : sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt );
25 0 : return weights_cnt;
26 0 : }
27 :
28 : static void
29 : compute_stake_delegations( fd_bank_t * bank,
30 : fd_stake_delegations_t const * stake_delegations,
31 : ulong const epoch,
32 : fd_stake_history_t const * history,
33 0 : ulong * new_rate_activation_epoch ) {
34 :
35 0 : ulong total_stake = 0UL;
36 :
37 0 : fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
38 0 : if( FD_UNLIKELY( !vote_states ) ) {
39 0 : FD_LOG_CRIT(( "vote_states is NULL" ));
40 0 : }
41 :
42 : /* Reset the vote stakes so we can re-compute them based on the most
43 : current stake delegation values. */
44 0 : fd_vote_states_reset_stakes( vote_states );
45 :
46 0 : fd_stake_delegations_iter_t iter_[1];
47 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
48 0 : !fd_stake_delegations_iter_done( iter );
49 0 : fd_stake_delegations_iter_next( iter ) ) {
50 0 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
51 :
52 : // Skip any delegations that are not in the delegation pool
53 :
54 0 : fd_delegation_t delegation = {
55 0 : .voter_pubkey = stake_delegation->vote_account,
56 0 : .stake = stake_delegation->stake,
57 0 : .deactivation_epoch = stake_delegation->deactivation_epoch,
58 0 : .activation_epoch = stake_delegation->activation_epoch,
59 0 : .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
60 0 : };
61 :
62 0 : fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
63 0 : &delegation,
64 0 : epoch,
65 0 : history,
66 0 : new_rate_activation_epoch );
67 :
68 0 : fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, &stake_delegation->vote_account );
69 0 : if( FD_LIKELY( vote_state ) ) {
70 0 : total_stake += new_entry.effective;
71 0 : vote_state->stake += new_entry.effective;
72 0 : }
73 0 : }
74 :
75 0 : fd_bank_total_epoch_stake_set( bank, total_stake );
76 :
77 0 : fd_bank_vote_states_end_locking_modify( bank );
78 0 : }
79 :
80 : /* Refresh vote accounts.
81 :
82 : This updates the epoch bank stakes vote_accounts cache - that is, the total amount
83 : of delegated stake each vote account has, using the current delegation values from inside each
84 : stake account. Contrary to the Agave equivalent, it also merges the stakes cache vote accounts with the
85 : new vote account keys from this epoch.
86 :
87 : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L562 */
88 : void
89 : fd_refresh_vote_accounts( fd_exec_slot_ctx_t * slot_ctx,
90 : fd_stake_delegations_t const * stake_delegations,
91 : fd_stake_history_t const * history,
92 0 : ulong * new_rate_activation_epoch ) {
93 :
94 0 : compute_stake_delegations(
95 0 : slot_ctx->bank,
96 0 : stake_delegations,
97 0 : fd_bank_epoch_get( slot_ctx->bank ),
98 0 : history,
99 0 : new_rate_activation_epoch );
100 0 : }
101 :
102 : static void
103 : accumulate_stake_cache_delegations( fd_stake_delegations_t const * stake_delegations,
104 : fd_stake_history_t const * history,
105 : ulong * new_rate_activation_epoch,
106 : fd_stake_history_entry_t * accumulator,
107 0 : ulong epoch ) {
108 :
109 0 : ulong effective = 0UL;
110 0 : ulong activating = 0UL;
111 0 : ulong deactivating = 0UL;
112 :
113 0 : fd_stake_delegations_iter_t iter_[1];
114 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
115 0 : !fd_stake_delegations_iter_done( iter );
116 0 : fd_stake_delegations_iter_next( iter ) ) {
117 0 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
118 :
119 0 : fd_delegation_t delegation = {
120 0 : .voter_pubkey = stake_delegation->vote_account,
121 0 : .stake = stake_delegation->stake,
122 0 : .activation_epoch = stake_delegation->activation_epoch,
123 0 : .deactivation_epoch = stake_delegation->deactivation_epoch,
124 0 : .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
125 0 : };
126 :
127 0 : fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
128 0 : &delegation,
129 0 : epoch,
130 0 : history,
131 0 : new_rate_activation_epoch );
132 0 : effective += new_entry.effective;
133 0 : activating += new_entry.activating;
134 0 : deactivating += new_entry.deactivating;
135 0 : }
136 :
137 0 : accumulator->effective += effective;
138 0 : accumulator->activating += activating;
139 0 : accumulator->deactivating += deactivating;
140 :
141 0 : }
142 :
143 : /* Accumulates information about epoch stakes into `temp_info`, which is a temporary cache
144 : used to save intermediate state about stake and vote accounts to avoid them from having to
145 : be recomputed on every access, especially at the epoch boundary. Also collects stats in `accumulator` */
146 : void
147 : fd_accumulate_stake_infos( ulong epoch,
148 : fd_stake_delegations_t const * stake_delegations,
149 : fd_stake_history_t const * history,
150 : ulong * new_rate_activation_epoch,
151 0 : fd_stake_history_entry_t * accumulator ) {
152 :
153 0 : accumulate_stake_cache_delegations(
154 0 : stake_delegations,
155 0 : history,
156 0 : new_rate_activation_epoch,
157 0 : accumulator,
158 0 : epoch );
159 :
160 0 : }
161 :
162 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L169 */
163 : void
164 : fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx,
165 : fd_stake_delegations_t const * stake_delegations,
166 : ulong * new_rate_activation_epoch,
167 0 : fd_spad_t * runtime_spad ) {
168 :
169 : /* Current stake delegations: list of all current delegations in stake_delegations
170 : https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L180 */
171 : /* Add a new entry to the Stake History sysvar for the previous epoch
172 : https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L181-L192 */
173 :
174 0 : fd_stake_history_t const * history = fd_sysvar_stake_history_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
175 0 : if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
176 :
177 0 : fd_stake_history_entry_t accumulator = {
178 0 : .effective = 0UL,
179 0 : .activating = 0UL,
180 0 : .deactivating = 0UL
181 0 : };
182 :
183 : /* Accumulate stats for stake accounts */
184 0 : fd_accumulate_stake_infos(
185 0 : fd_bank_epoch_get( slot_ctx->bank ),
186 0 : stake_delegations,
187 0 : history,
188 0 : new_rate_activation_epoch,
189 0 : &accumulator );
190 :
191 : /* https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L359 */
192 0 : fd_epoch_stake_history_entry_pair_t new_elem = {
193 0 : .epoch = fd_bank_epoch_get( slot_ctx->bank ),
194 0 : .entry = {
195 0 : .effective = accumulator.effective,
196 0 : .activating = accumulator.activating,
197 0 : .deactivating = accumulator.deactivating
198 0 : }
199 0 : };
200 :
201 0 : fd_sysvar_stake_history_update( slot_ctx, &new_elem, runtime_spad );
202 :
203 0 : }
204 :
205 : int
206 : write_stake_state( fd_txn_account_t * stake_acc_rec,
207 0 : fd_stake_state_v2_t * stake_state ) {
208 :
209 0 : ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state);
210 :
211 0 : fd_bincode_encode_ctx_t ctx = {
212 0 : .data = fd_txn_account_get_data_mut( stake_acc_rec ),
213 0 : .dataend = fd_txn_account_get_data_mut( stake_acc_rec ) + encoded_stake_state_size,
214 0 : };
215 0 : if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) {
216 0 : FD_LOG_ERR(( "fd_stake_state_encode failed" ));
217 0 : }
218 :
219 0 : return 0;
220 0 : }
221 :
222 : /* Removes stake delegation from stakes */
223 : static void
224 : fd_stakes_remove_stake_delegation( fd_txn_account_t * stake_account,
225 0 : fd_bank_t * bank ) {
226 :
227 0 : fd_stake_delegations_t * stake_delegations_delta = fd_bank_stake_delegations_delta_locking_modify( bank );
228 0 : if( FD_UNLIKELY( !stake_delegations_delta ) ) {
229 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation delta" ));
230 0 : }
231 :
232 0 : fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
233 :
234 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
235 0 : }
236 :
237 : /* Updates stake delegation in epoch stakes */
238 : static void
239 : fd_stakes_upsert_stake_delegation( fd_txn_account_t * stake_account,
240 0 : fd_bank_t * bank ) {
241 :
242 0 : fd_stake_delegations_t * stake_delegations_delta = fd_bank_stake_delegations_delta_locking_modify( bank );
243 0 : if( FD_UNLIKELY( !stake_delegations_delta ) ) {
244 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation delta" ));
245 0 : }
246 :
247 0 : fd_stake_state_v2_t stake_state;
248 0 : int err = fd_stake_get_state( stake_account, &stake_state );
249 0 : if( FD_UNLIKELY( err != 0 ) ) {
250 0 : FD_LOG_WARNING(( "Failed to get stake state" ));
251 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
252 0 : return;
253 0 : }
254 :
255 0 : if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
256 0 : FD_LOG_WARNING(( "Not a valid stake" ));
257 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
258 0 : return;
259 0 : }
260 :
261 0 : if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
262 0 : FD_LOG_WARNING(( "Stake is empty" ));
263 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
264 0 : return;
265 0 : }
266 :
267 0 : fd_stake_delegations_update(
268 0 : stake_delegations_delta,
269 0 : stake_account->pubkey,
270 0 : &stake_state.inner.stake.stake.delegation.voter_pubkey,
271 0 : stake_state.inner.stake.stake.delegation.stake,
272 0 : stake_state.inner.stake.stake.delegation.activation_epoch,
273 0 : stake_state.inner.stake.stake.delegation.deactivation_epoch,
274 0 : stake_state.inner.stake.stake.credits_observed,
275 0 : stake_state.inner.stake.stake.delegation.warmup_cooldown_rate );
276 :
277 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
278 0 : }
279 :
280 : void
281 : fd_update_stake_delegation( fd_txn_account_t * stake_account,
282 0 : fd_bank_t * bank ) {
283 :
284 0 : int is_empty = fd_txn_account_get_lamports( stake_account )==0UL;
285 0 : int is_uninit = 1;
286 0 : if( fd_txn_account_get_data_len( stake_account )>=4UL ) {
287 0 : uint prefix = FD_LOAD( uint, fd_txn_account_get_data( stake_account ) );
288 0 : is_uninit = ( prefix==fd_stake_state_v2_enum_uninitialized );
289 0 : }
290 :
291 0 : if( is_empty || is_uninit ) {
292 0 : fd_stakes_remove_stake_delegation( stake_account, bank );
293 0 : } else {
294 0 : fd_stakes_upsert_stake_delegation( stake_account, bank );
295 0 : }
296 0 : }
|