Line data Source code
1 : #include <limits.h>
2 :
3 : #include "fd_stakes.h"
4 : #include "../runtime/fd_bank.h"
5 : #include "../runtime/program/vote/fd_vote_state_versioned.h"
6 : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
7 : #include "../runtime/sysvar/fd_sysvar_epoch_schedule.h"
8 : #include "../runtime/fd_runtime_stack.h"
9 : #include "../runtime/fd_system_ids.h"
10 : #include "fd_stake_delegations.h"
11 : #include "../accdb/fd_accdb_impl_v1.h"
12 : #include "../accdb/fd_accdb_sync.h"
13 : #include "../../util/bits/fd_sat.h"
14 :
15 : /**********************************************************************/
16 : /* Constants */
17 : /**********************************************************************/
18 :
19 0 : #define DEFAULT_WARMUP_COOLDOWN_RATE ( 0.25 )
20 0 : #define NEW_WARMUP_COOLDOWN_RATE ( 0.09 )
21 0 : #define DEFAULT_SLASH_PENALTY ( 12 )
22 :
23 : /**********************************************************************/
24 : /* Types */
25 : /**********************************************************************/
26 :
27 : struct effective_activating {
28 : ulong effective;
29 : ulong activating;
30 : };
31 : typedef struct effective_activating effective_activating_t;
32 :
33 : typedef fd_stake_history_entry_t fd_stake_activation_status_t;
34 :
35 : /**********************************************************************/
36 : /* Static helpers */
37 : /**********************************************************************/
38 :
39 : static inline double
40 0 : warmup_cooldown_rate( ulong current_epoch, ulong * new_rate_activation_epoch ) {
41 0 : ulong activation_epoch = new_rate_activation_epoch ? *new_rate_activation_epoch : ULONG_MAX;
42 0 : return current_epoch<activation_epoch ? DEFAULT_WARMUP_COOLDOWN_RATE : NEW_WARMUP_COOLDOWN_RATE;
43 0 : }
44 :
45 : static fd_stake_history_entry_t const *
46 : fd_stake_history_ele_binary_search_const( fd_stake_history_t const * history,
47 0 : ulong epoch ) {
48 0 : ulong start = 0UL;
49 0 : ulong end = history->fd_stake_history_len - 1;
50 :
51 0 : while ( start<=end ) {
52 0 : ulong mid = start + ( end - start ) / 2UL;
53 0 : if( history->fd_stake_history[mid].epoch==epoch ) {
54 0 : return &history->fd_stake_history[mid].entry;
55 0 : } else if( history->fd_stake_history[mid].epoch<epoch ) {
56 0 : if ( mid==0 ) return NULL;
57 0 : end = mid - 1;
58 0 : } else {
59 0 : start = mid + 1;
60 0 : }
61 0 : }
62 0 : return NULL;
63 0 : }
64 :
65 : static fd_stake_history_entry_t const *
66 : fd_stake_history_ele_query_const( fd_stake_history_t const * history,
67 0 : ulong epoch ) {
68 0 : if( 0 == history->fd_stake_history_len ) {
69 0 : return NULL;
70 0 : }
71 :
72 0 : if( epoch > history->fd_stake_history[0].epoch ) {
73 0 : return NULL;
74 0 : }
75 :
76 0 : ulong off = (history->fd_stake_history[0].epoch - epoch);
77 0 : if( off >= history->fd_stake_history_len ) {
78 0 : return fd_stake_history_ele_binary_search_const( history, epoch );
79 0 : }
80 :
81 0 : ulong e = (off + history->fd_stake_history_offset) & (history->fd_stake_history_size - 1);
82 :
83 0 : if ( history->fd_stake_history[e].epoch == epoch ) {
84 0 : return &history->fd_stake_history[e].entry;
85 0 : }
86 :
87 0 : return fd_stake_history_ele_binary_search_const( history, epoch );
88 0 : }
89 :
90 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L728
91 : static effective_activating_t
92 : stake_and_activating( fd_delegation_t const * self,
93 : ulong target_epoch,
94 : fd_stake_history_t const * history,
95 30 : ulong * new_rate_activation_epoch ) {
96 30 : ulong delegated_stake = self->stake;
97 :
98 30 : fd_stake_history_entry_t const * cluster_stake_at_activation_epoch;
99 30 : if( self->activation_epoch==ULONG_MAX ) {
100 30 : return ( effective_activating_t ){ .effective = delegated_stake, .activating = 0 };
101 30 : } else if( self->activation_epoch==self->deactivation_epoch ) {
102 0 : return ( effective_activating_t ){ .effective = 0, .activating = 0 };
103 0 : } else if( target_epoch==self->activation_epoch ) {
104 0 : return ( effective_activating_t ){ .effective = 0, .activating = delegated_stake };
105 0 : } else if( target_epoch<self->activation_epoch ) {
106 0 : return ( effective_activating_t ){ .effective = 0, .activating = 0 };
107 0 : } else if( history &&
108 0 : ( cluster_stake_at_activation_epoch = fd_stake_history_ele_query_const(
109 0 : history, self->activation_epoch ) ) ) {
110 0 : ulong prev_epoch = self->activation_epoch;
111 0 : fd_stake_history_entry_t const * prev_cluster_stake = cluster_stake_at_activation_epoch;
112 :
113 0 : ulong current_epoch;
114 0 : ulong current_effective_stake = 0;
115 0 : for( ;; ) {
116 0 : current_epoch = prev_epoch + 1;
117 0 : if( FD_LIKELY( prev_cluster_stake->activating==0 ) ) {
118 0 : break;
119 0 : }
120 :
121 0 : ulong remaining_activating_stake = delegated_stake - current_effective_stake;
122 0 : double weight = (double)remaining_activating_stake / (double)prev_cluster_stake->activating;
123 0 : double warmup_cooldown_rate_ =
124 0 : warmup_cooldown_rate( current_epoch, new_rate_activation_epoch );
125 :
126 0 : double newly_effective_cluster_stake =
127 0 : (double)prev_cluster_stake->effective * warmup_cooldown_rate_;
128 0 : ulong newly_effective_stake =
129 0 : fd_ulong_max( fd_rust_cast_double_to_ulong( weight * newly_effective_cluster_stake ), 1 );
130 :
131 0 : current_effective_stake += newly_effective_stake;
132 0 : if( FD_LIKELY( current_effective_stake>=delegated_stake ) ) {
133 0 : current_effective_stake = delegated_stake;
134 0 : break;
135 0 : }
136 :
137 0 : if( FD_LIKELY( current_epoch>=target_epoch ||
138 0 : current_epoch>=self->deactivation_epoch ) ) {
139 0 : break;
140 0 : }
141 :
142 0 : fd_stake_history_entry_t const * current_cluster_stake =
143 0 : fd_stake_history_ele_query_const( history, current_epoch );
144 0 : if( FD_LIKELY( current_cluster_stake ) ) {
145 0 : prev_epoch = current_epoch;
146 0 : prev_cluster_stake = current_cluster_stake;
147 0 : } else {
148 0 : break;
149 0 : }
150 0 : }
151 0 : return ( effective_activating_t ){ .effective = current_effective_stake,
152 0 : .activating = delegated_stake - current_effective_stake };
153 0 : } else {
154 0 : return ( effective_activating_t ){ .effective = delegated_stake, .activating = 0 };
155 0 : }
156 30 : }
157 :
158 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L641
159 : static fd_stake_activation_status_t
160 : stake_activating_and_deactivating( fd_delegation_t const * self,
161 : ulong target_epoch,
162 : fd_stake_history_t const * stake_history,
163 30 : ulong * new_rate_activation_epoch ) {
164 :
165 30 : effective_activating_t effective_activating =
166 30 : stake_and_activating( self, target_epoch, stake_history, new_rate_activation_epoch );
167 :
168 30 : ulong effective_stake = effective_activating.effective;
169 30 : ulong activating_stake = effective_activating.activating;
170 :
171 30 : fd_stake_history_entry_t const * cluster_stake_at_deactivation_epoch = NULL;
172 :
173 30 : if( target_epoch<self->deactivation_epoch ) {
174 30 : if( activating_stake==0 ) {
175 30 : return ( fd_stake_history_entry_t ){
176 30 : .effective = effective_stake, .deactivating = 0, .activating = 0 };
177 30 : } else {
178 0 : return ( fd_stake_history_entry_t ){
179 0 : .effective = effective_stake, .deactivating = 0, .activating = activating_stake };
180 0 : }
181 30 : } else if( target_epoch==self->deactivation_epoch ) {
182 0 : return ( fd_stake_history_entry_t ){
183 0 : .effective = effective_stake, .deactivating = effective_stake, .activating = 0 };
184 0 : } else if( stake_history &&
185 0 : ( cluster_stake_at_deactivation_epoch = fd_stake_history_ele_query_const( stake_history, self->deactivation_epoch ) ) ) {
186 0 : ulong prev_epoch = self->deactivation_epoch;
187 0 : fd_stake_history_entry_t const * prev_cluster_stake = cluster_stake_at_deactivation_epoch;
188 :
189 0 : ulong current_epoch;
190 0 : ulong current_effective_stake = effective_stake;
191 0 : for( ;; ) {
192 0 : current_epoch = prev_epoch + 1;
193 0 : if( prev_cluster_stake->deactivating==0 ) break;
194 :
195 0 : double weight = (double)current_effective_stake / (double)prev_cluster_stake->deactivating;
196 0 : double warmup_cooldown_rate_ =
197 0 : warmup_cooldown_rate( current_epoch, new_rate_activation_epoch );
198 :
199 0 : double newly_not_effective_cluster_stake =
200 0 : (double)prev_cluster_stake->effective * warmup_cooldown_rate_;
201 0 : ulong newly_not_effective_stake =
202 0 : fd_ulong_max( fd_rust_cast_double_to_ulong( weight * newly_not_effective_cluster_stake ), 1 );
203 :
204 0 : current_effective_stake =
205 0 : fd_ulong_sat_sub( current_effective_stake, newly_not_effective_stake );
206 0 : if( current_effective_stake==0 ) break;
207 :
208 0 : if( current_epoch>=target_epoch ) break;
209 :
210 0 : fd_stake_history_entry_t const * current_cluster_stake = NULL;
211 0 : if( ( current_cluster_stake = fd_stake_history_ele_query_const(stake_history, current_epoch ) ) ) {
212 0 : prev_epoch = current_epoch;
213 0 : prev_cluster_stake = current_cluster_stake;
214 0 : } else {
215 0 : break;
216 0 : }
217 0 : }
218 0 : return ( fd_stake_history_entry_t ){ .effective = current_effective_stake,
219 0 : .deactivating = current_effective_stake,
220 0 : .activating = 0 };
221 0 : } else {
222 0 : return ( fd_stake_history_entry_t ){ .effective = 0, .activating = 0, .deactivating = 0 };
223 0 : }
224 30 : }
225 :
226 : static void
227 : write_stake_config( fd_accdb_user_t * accdb,
228 : fd_funk_txn_xid_t const * xid,
229 0 : fd_stake_config_t const * stake_config ) {
230 0 : ulong data_sz = fd_stake_config_size( stake_config );
231 0 : fd_pubkey_t const * address = &fd_solana_stake_program_config_id;
232 :
233 0 : fd_accdb_rw_t rw[1];
234 0 : fd_accdb_open_rw( accdb, rw, xid, address, data_sz, FD_ACCDB_FLAG_CREATE );
235 :
236 : /* FIXME update capitalization? */
237 : /* FIXME set owner to Config program? */
238 : /* FIXME Agave reflink? */
239 : /* FIXME derive lamport balance from rent instead of hardcoding */
240 :
241 0 : fd_accdb_ref_lamports_set( rw, 960480UL );
242 0 : fd_accdb_ref_exec_bit_set( rw, 0 );
243 0 : fd_accdb_ref_data_sz_set( accdb, rw, data_sz, 0 );
244 0 : fd_bincode_encode_ctx_t ctx = {
245 0 : .data = fd_accdb_ref_data( rw ),
246 0 : .dataend = (uchar *)fd_accdb_ref_data( rw ) + data_sz
247 0 : };
248 0 : if( fd_stake_config_encode( stake_config, &ctx ) )
249 0 : FD_LOG_ERR( ( "fd_stake_config_encode failed" ) );
250 :
251 0 : fd_accdb_close_rw( accdb, rw );
252 0 : }
253 :
254 : /**********************************************************************/
255 : /* Public API */
256 : /**********************************************************************/
257 :
258 : int
259 : fd_stakes_new_warmup_cooldown_rate_epoch(
260 : fd_epoch_schedule_t const * epoch_schedule,
261 : fd_features_t const * features,
262 : /* out */ ulong * epoch,
263 : int * err
264 10 : ) {
265 10 : *err = 0;
266 :
267 10 : if( FD_UNLIKELY( !epoch_schedule ) ) {
268 0 : *epoch = ULONG_MAX;
269 0 : *err = FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
270 0 : return 1;
271 0 : }
272 10 : *epoch = fd_slot_to_epoch( epoch_schedule, features->reduce_stake_warmup_cooldown, NULL );
273 10 : return 1;
274 10 : }
275 :
276 : void
277 : fd_stakes_config_init( fd_accdb_user_t * accdb,
278 0 : fd_funk_txn_xid_t const * xid ) {
279 0 : fd_stake_config_t stake_config = {
280 0 : .warmup_cooldown_rate = DEFAULT_WARMUP_COOLDOWN_RATE,
281 0 : .slash_penalty = DEFAULT_SLASH_PENALTY,
282 0 : };
283 0 : write_stake_config( accdb, xid, &stake_config );
284 0 : }
285 :
286 : int
287 : fd_stakes_get_state( fd_account_meta_t const * meta,
288 301 : fd_stake_state_v2_t * out ) {
289 301 : int rc;
290 :
291 301 : fd_bincode_decode_ctx_t bincode_ctx = {
292 301 : .data = fd_account_data( meta ),
293 301 : .dataend = fd_account_data( meta ) + meta->dlen,
294 301 : };
295 :
296 301 : ulong total_sz = 0UL;
297 301 : rc = fd_stake_state_v2_decode_footprint( &bincode_ctx, &total_sz );
298 301 : if( FD_UNLIKELY( rc!=FD_BINCODE_SUCCESS ) ) {
299 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
300 0 : }
301 :
302 301 : fd_stake_state_v2_decode( out, &bincode_ctx );
303 :
304 301 : return 0;
305 301 : }
306 :
307 : fd_stake_history_entry_t
308 : fd_stakes_activating_and_deactivating( fd_stake_delegation_t const * stake_delegation,
309 : ulong target_epoch,
310 : fd_stake_history_t const * stake_history,
311 30 : ulong * new_rate_activation_epoch ) {
312 30 : fd_delegation_t delegation = {
313 30 : .voter_pubkey = stake_delegation->vote_account,
314 30 : .stake = stake_delegation->stake,
315 30 : .deactivation_epoch = stake_delegation->deactivation_epoch==USHORT_MAX ? ULONG_MAX : stake_delegation->deactivation_epoch,
316 30 : .activation_epoch = stake_delegation->activation_epoch==USHORT_MAX ? ULONG_MAX : stake_delegation->activation_epoch,
317 30 : .warmup_cooldown_rate = fd_stake_delegations_warmup_cooldown_rate_to_double( stake_delegation->warmup_cooldown_rate ),
318 30 : };
319 :
320 30 : return stake_activating_and_deactivating(
321 30 : &delegation, target_epoch, stake_history, new_rate_activation_epoch );
322 30 : }
323 :
324 : ulong
325 : fd_stake_weights_by_node( fd_vote_stakes_t * vote_stakes,
326 : ushort fork_idx,
327 300 : fd_vote_stake_weight_t * weights ) {
328 300 : ulong weights_cnt = 0;
329 300 : uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
330 300 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_fork_iter_init( vote_stakes, fork_idx, iter_mem );
331 601 : !fd_vote_stakes_fork_iter_done( vote_stakes, fork_idx, iter );
332 301 : fd_vote_stakes_fork_iter_next( vote_stakes, fork_idx, iter ) ) {
333 301 : fd_pubkey_t pubkey;
334 301 : ulong stake_t_2;
335 301 : fd_pubkey_t node_account_t_2;
336 301 : fd_vote_stakes_fork_iter_ele( vote_stakes, fork_idx, iter, &pubkey, NULL, &stake_t_2, NULL, &node_account_t_2 );
337 301 : if( FD_UNLIKELY( !stake_t_2 ) ) continue;
338 :
339 301 : fd_memcpy( weights[ weights_cnt ].vote_key.uc, &pubkey, sizeof(fd_pubkey_t) );
340 301 : fd_memcpy( weights[ weights_cnt ].id_key.uc, &node_account_t_2, sizeof(fd_pubkey_t) );
341 301 : weights[ weights_cnt ].stake = stake_t_2;
342 301 : weights_cnt++;
343 301 : }
344 300 : sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt );
345 :
346 300 : return weights_cnt;
347 300 : }
348 :
349 : ulong
350 : fd_stake_weights_by_node_next( fd_vote_stakes_t * vote_stakes,
351 : ushort fork_idx,
352 301 : fd_vote_stake_weight_t * weights ) {
353 301 : ulong weights_cnt = 0;
354 301 : uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
355 301 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_fork_iter_init( vote_stakes, fork_idx, iter_mem );
356 602 : !fd_vote_stakes_fork_iter_done( vote_stakes, fork_idx, iter );
357 301 : fd_vote_stakes_fork_iter_next( vote_stakes, fork_idx, iter ) ) {
358 301 : fd_pubkey_t pubkey;
359 301 : ulong stake_t_1;
360 301 : fd_pubkey_t node_account_t_1;
361 301 : fd_vote_stakes_fork_iter_ele( vote_stakes, fork_idx, iter, &pubkey, &stake_t_1, NULL, &node_account_t_1, NULL );
362 301 : if( FD_UNLIKELY( !stake_t_1 ) ) continue;
363 :
364 301 : fd_memcpy( weights[ weights_cnt ].vote_key.uc, &pubkey, sizeof(fd_pubkey_t) );
365 301 : fd_memcpy( weights[ weights_cnt ].id_key.uc, &node_account_t_1, sizeof(fd_pubkey_t) );
366 301 : weights[ weights_cnt ].stake = stake_t_1;
367 301 : weights_cnt++;
368 301 : }
369 301 : sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt );
370 :
371 301 : return weights_cnt;
372 301 : }
373 :
374 : static void
375 : get_vote_credits_commission( uchar const * account_data,
376 : ulong account_data_len,
377 : uchar * buf,
378 : fd_vote_rewards_t * vote_ele,
379 : fd_pubkey_t * node_account_t_1,
380 : ulong * last_vote_slot,
381 : long * last_vote_timestamp,
382 2 : fd_epoch_credits_t * epoch_credits_opt ) {
383 :
384 2 : fd_bincode_decode_ctx_t ctx = {
385 2 : .data = account_data,
386 2 : .dataend = account_data + account_data_len,
387 2 : };
388 :
389 2 : fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( buf, &ctx );
390 2 : if( FD_UNLIKELY( vsv==NULL ) ) {
391 0 : FD_LOG_CRIT(( "unable to decode vote state versioned" ));
392 0 : }
393 2 : fd_vote_epoch_credits_t * vote_epoch_credits = NULL;
394 :
395 2 : switch( vsv->discriminant ) {
396 0 : case fd_vote_state_versioned_enum_v1_14_11:
397 0 : vote_ele->commission = vsv->inner.v1_14_11.commission;
398 0 : *node_account_t_1 = vsv->inner.v1_14_11.node_pubkey;
399 0 : *last_vote_slot = vsv->inner.v1_14_11.last_timestamp.slot;
400 0 : *last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp;
401 0 : vote_epoch_credits = vsv->inner.v1_14_11.epoch_credits;
402 0 : break;
403 0 : case fd_vote_state_versioned_enum_v3:
404 0 : vote_ele->commission = vsv->inner.v3.commission;
405 0 : *node_account_t_1 = vsv->inner.v3.node_pubkey;
406 0 : *last_vote_slot = vsv->inner.v3.last_timestamp.slot;
407 0 : *last_vote_timestamp = vsv->inner.v3.last_timestamp.timestamp;
408 0 : vote_epoch_credits = vsv->inner.v3.epoch_credits;
409 0 : break;
410 2 : case fd_vote_state_versioned_enum_v4:
411 2 : vote_ele->commission = (uchar)(vsv->inner.v4.inflation_rewards_commission_bps/100);
412 2 : *node_account_t_1 = vsv->inner.v4.node_pubkey;
413 2 : *last_vote_slot = vsv->inner.v4.last_timestamp.slot;
414 2 : *last_vote_timestamp = vsv->inner.v4.last_timestamp.timestamp;
415 2 : vote_epoch_credits = vsv->inner.v4.epoch_credits;
416 2 : break;
417 0 : default:
418 0 : FD_LOG_CRIT(( "invalid vote state version %u", vsv->discriminant ));
419 2 : }
420 :
421 2 : if( !epoch_credits_opt ) return;
422 2 : epoch_credits_opt->cnt = 0UL;
423 2 : for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( vote_epoch_credits );
424 15 : !deq_fd_vote_epoch_credits_t_iter_done( vote_epoch_credits, iter );
425 13 : iter = deq_fd_vote_epoch_credits_t_iter_next( vote_epoch_credits, iter ) ) {
426 13 : fd_vote_epoch_credits_t * ele = deq_fd_vote_epoch_credits_t_iter_ele( vote_epoch_credits, iter );
427 13 : epoch_credits_opt->epoch[ epoch_credits_opt->cnt ] = (ushort)ele->epoch;
428 13 : epoch_credits_opt->credits[ epoch_credits_opt->cnt ] = ele->credits;
429 13 : epoch_credits_opt->prev_credits[ epoch_credits_opt->cnt ] = ele->prev_credits;
430 13 : epoch_credits_opt->cnt++;
431 13 : }
432 2 : }
433 :
434 : /* We need to update the amount of stake that each vote account has for
435 : the given epoch. This can only be done after the stake history
436 : sysvar has been updated. We also cache the stakes for each of the
437 : vote accounts for the previous epoch.
438 :
439 : https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L471 */
440 : void
441 : fd_refresh_vote_accounts( fd_bank_t * bank,
442 : fd_accdb_user_t * accdb,
443 : fd_funk_txn_xid_t const * xid,
444 : fd_runtime_stack_t * runtime_stack,
445 : fd_stake_delegations_t const * stake_delegations,
446 : fd_stake_history_t const * history,
447 2 : ulong * new_rate_activation_epoch ) {
448 :
449 2 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes_locking_modify( bank );
450 :
451 2 : fd_top_votes_t * top_votes = fd_bank_top_votes_modify( bank );
452 2 : fd_top_votes_init( top_votes );
453 :
454 2 : ushort parent_idx = bank->data->vote_stakes_fork_id;
455 2 : ushort child_idx = fd_vote_stakes_new_child( vote_stakes );
456 :
457 2 : bank->data->vote_stakes_fork_id = child_idx;
458 :
459 :
460 2 : uchar __attribute__((aligned(128))) vsv_buf[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ];
461 :
462 2 : fd_vote_rewards_map_t * vote_ele_map = fd_type_pun( runtime_stack->stakes.vote_map_mem );
463 2 : fd_vote_rewards_map_reset( vote_ele_map );
464 2 : ulong vote_ele_cnt = 0UL;
465 :
466 2 : ulong epoch = fd_bank_epoch_get( bank );
467 :
468 2 : ulong total_stake = 0UL;
469 2 : fd_stake_delegations_iter_t iter_[1];
470 2 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
471 4 : !fd_stake_delegations_iter_done( iter );
472 2 : fd_stake_delegations_iter_next( iter ) ) {
473 :
474 2 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
475 :
476 2 : fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating(
477 2 : stake_delegation,
478 2 : epoch,
479 2 : history,
480 2 : new_rate_activation_epoch );
481 :
482 2 : if( FD_UNLIKELY( !fd_vote_stakes_query_pubkey( vote_stakes, child_idx, &stake_delegation->vote_account ) ) ) {
483 2 : fd_accdb_ro_t vote_ro[1];
484 :
485 2 : ulong old_stake_t_1 = 0UL;
486 2 : fd_pubkey_t old_node_account_t_1 = {0};
487 2 : int exists_prev = fd_vote_stakes_query_t_1( vote_stakes, parent_idx, &stake_delegation->vote_account, &old_stake_t_1, &old_node_account_t_1 );
488 2 : int exists_curr = 1;
489 2 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, vote_ro, xid, &stake_delegation->vote_account ) ) ) {
490 0 : exists_curr = 0;
491 2 : } else if( FD_UNLIKELY( !fd_vsv_is_correct_size_and_initialized( vote_ro->meta ) ) ) {
492 0 : fd_accdb_close_ro( accdb, vote_ro );
493 0 : exists_curr = 0;
494 0 : }
495 :
496 2 : if( FD_UNLIKELY( !exists_curr ) ) {
497 :
498 : /* If the vote account does not exist going into the epoch
499 : boundary, and did not exist at the end of the last epoch
500 : boundary, then we can fully skip it. */
501 0 : if( FD_UNLIKELY( !exists_prev ) ) continue;
502 :
503 : /* If the account does not exist but did in the previous epoch,
504 : it still needs to be added to the top votes and the vote
505 : stakes data structure in case the vote account is revived
506 : again. */
507 0 : fd_top_votes_insert( top_votes,
508 0 : &stake_delegation->vote_account,
509 0 : &old_node_account_t_1,
510 0 : old_stake_t_1,
511 0 : 0UL,
512 0 : 0L );
513 0 : fd_top_votes_invalidate( top_votes, &stake_delegation->vote_account );
514 :
515 0 : fd_vote_stakes_insert_key(
516 0 : vote_stakes,
517 0 : child_idx,
518 0 : &stake_delegation->vote_account,
519 0 : &old_node_account_t_1,
520 0 : &old_node_account_t_1,
521 0 : old_stake_t_1,
522 0 : fd_bank_epoch_get( bank ),
523 0 : 0 );
524 2 : } else {
525 : /* If the account currently exists, we need to insert the entry
526 : into the vote stakes data structure. We will treat the t-2
527 : stake as 0 if the account did not exist at the end of the
528 : last epoch boundary.*/
529 2 : fd_pubkey_t curr_node_account_t_1;
530 2 : ulong last_vote_slot;
531 2 : long last_vote_timestamp;
532 2 : fd_epoch_credits_t * epoch_credits = vote_ele_cnt<runtime_stack->expected_vote_accounts ? &runtime_stack->stakes.epoch_credits[ vote_ele_cnt ] : NULL;
533 :
534 2 : get_vote_credits_commission( fd_accdb_ref_data_const( vote_ro ),
535 2 : fd_accdb_ref_data_sz( vote_ro ),
536 2 : vsv_buf,
537 2 : &runtime_stack->stakes.vote_ele[ vote_ele_cnt ],
538 2 : &curr_node_account_t_1,
539 2 : &last_vote_slot,
540 2 : &last_vote_timestamp,
541 2 : epoch_credits );
542 2 : fd_accdb_close_ro( accdb, vote_ro );
543 :
544 : /* If old_node_account_t_1 gets zero-initialized which means
545 : that it is still valid to use. */
546 2 : fd_vote_stakes_insert_key(
547 2 : vote_stakes,
548 2 : child_idx,
549 2 : &stake_delegation->vote_account,
550 2 : &curr_node_account_t_1,
551 2 : &old_node_account_t_1,
552 2 : old_stake_t_1,
553 2 : fd_bank_epoch_get( bank ),
554 2 : 1 );
555 :
556 2 : fd_top_votes_insert(
557 2 : top_votes,
558 2 : &stake_delegation->vote_account,
559 2 : &old_node_account_t_1,
560 2 : old_stake_t_1,
561 2 : last_vote_slot,
562 2 : last_vote_timestamp );
563 :
564 2 : fd_vote_rewards_t * vote_ele = &runtime_stack->stakes.vote_ele[ vote_ele_cnt ];
565 2 : vote_ele->pubkey = stake_delegation->vote_account;
566 2 : vote_ele->vote_rewards = 0UL;
567 2 : fd_vote_rewards_map_ele_insert( vote_ele_map, vote_ele, runtime_stack->stakes.vote_ele );
568 2 : vote_ele_cnt++;
569 2 : }
570 2 : }
571 :
572 2 : fd_vote_stakes_insert_update( vote_stakes,
573 2 : child_idx,
574 2 : &stake_delegation->vote_account,
575 2 : new_entry.effective );
576 :
577 2 : total_stake += new_entry.effective;
578 2 : }
579 2 : fd_bank_total_epoch_stake_set( bank, total_stake );
580 :
581 2 : fd_vote_stakes_insert_fini( vote_stakes, child_idx );
582 :
583 2 : fd_bank_vote_stakes_end_locking_modify( bank );
584 2 : }
585 :
586 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L280 */
587 : void
588 : fd_stakes_activate_epoch( fd_bank_t * bank,
589 : fd_runtime_stack_t * runtime_stack,
590 : fd_accdb_user_t * accdb,
591 : fd_funk_txn_xid_t const * xid,
592 : fd_capture_ctx_t * capture_ctx,
593 : fd_stake_delegations_t const * stake_delegations,
594 2 : ulong * new_rate_activation_epoch ) {
595 :
596 : /* First, we need to accumulate the stats for the current amount of
597 : effective, activating, and deactivating stake for the current
598 : epoch. Once this is computed, we can add update our stake history
599 : sysvar. Afterward, we can refresh the stake values for the vote
600 : accounts for the new epoch. */
601 :
602 2 : fd_stake_history_t stake_history[1];
603 2 : if( FD_UNLIKELY( !fd_sysvar_stake_history_read( accdb, xid, stake_history ) ) ) {
604 0 : FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
605 0 : }
606 :
607 2 : fd_epoch_stake_history_entry_pair_t new_elem = {
608 2 : .epoch = fd_bank_epoch_get( bank ),
609 2 : .entry = {
610 2 : .effective = 0UL,
611 2 : .activating = 0UL,
612 2 : .deactivating = 0UL
613 2 : }
614 2 : };
615 :
616 2 : fd_stake_delegations_iter_t iter_[1];
617 2 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
618 4 : !fd_stake_delegations_iter_done( iter );
619 2 : fd_stake_delegations_iter_next( iter ) ) {
620 2 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
621 :
622 2 : fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating(
623 2 : stake_delegation,
624 2 : fd_bank_epoch_get( bank ),
625 2 : stake_history,
626 2 : new_rate_activation_epoch );
627 2 : new_elem.entry.effective += new_entry.effective;
628 2 : new_elem.entry.activating += new_entry.activating;
629 2 : new_elem.entry.deactivating += new_entry.deactivating;
630 2 : }
631 2 : fd_sysvar_stake_history_update( bank, accdb, xid, capture_ctx, &new_elem );
632 :
633 2 : if( FD_UNLIKELY( !fd_sysvar_stake_history_read( accdb, xid, stake_history ) ) ) {
634 0 : FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
635 0 : }
636 :
637 : /* Now increment the epoch and recompute the stakes for the vote
638 : accounts for the new epoch value. */
639 :
640 2 : fd_bank_epoch_set( bank, fd_bank_epoch_get( bank ) + 1UL );
641 :
642 2 : fd_refresh_vote_accounts( bank,
643 2 : accdb,
644 2 : xid,
645 2 : runtime_stack,
646 2 : stake_delegations,
647 2 : stake_history,
648 2 : new_rate_activation_epoch );
649 :
650 2 : }
651 :
652 : void
653 : fd_stakes_update_stake_delegation( fd_pubkey_t const * pubkey,
654 : fd_account_meta_t const * meta,
655 0 : fd_bank_t * bank ) {
656 :
657 0 : fd_stake_delegations_t * stake_delegations = fd_bank_stake_delegations_modify( bank );
658 :
659 0 : if( meta->lamports==0UL ) {
660 0 : fd_stake_delegations_fork_remove( stake_delegations, bank->data->stake_delegations_fork_id, pubkey );
661 0 : return;
662 0 : }
663 :
664 0 : fd_stake_state_v2_t stake_state;
665 0 : int err = fd_stakes_get_state( meta, &stake_state );
666 0 : if( FD_UNLIKELY( err!=0 ) ) {
667 0 : fd_stake_delegations_fork_remove( stake_delegations, bank->data->stake_delegations_fork_id, pubkey );
668 0 : return;
669 0 : }
670 :
671 0 : if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
672 0 : fd_stake_delegations_fork_remove( stake_delegations, bank->data->stake_delegations_fork_id, pubkey );
673 0 : return;
674 0 : }
675 :
676 0 : if( FD_UNLIKELY( fd_stake_state_v2_is_uninitialized( &stake_state ) ) ) {
677 0 : fd_stake_delegations_fork_remove( stake_delegations, bank->data->stake_delegations_fork_id, pubkey );
678 0 : return;
679 0 : }
680 :
681 0 : if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
682 0 : fd_stake_delegations_fork_remove( stake_delegations, bank->data->stake_delegations_fork_id, pubkey );
683 0 : return;
684 0 : }
685 :
686 0 : fd_stake_delegations_fork_update( stake_delegations, bank->data->stake_delegations_fork_id, pubkey,
687 0 : &stake_state.inner.stake.stake.delegation.voter_pubkey,
688 0 : stake_state.inner.stake.stake.delegation.stake,
689 0 : stake_state.inner.stake.stake.delegation.activation_epoch,
690 0 : stake_state.inner.stake.stake.delegation.deactivation_epoch,
691 0 : stake_state.inner.stake.stake.credits_observed,
692 0 : stake_state.inner.stake.stake.delegation.warmup_cooldown_rate );
693 0 : }
|