Line data Source code
1 : #include "fd_stakes.h"
2 : #include "../runtime/fd_bank.h"
3 : #include "../runtime/fd_system_ids.h"
4 : #include "../runtime/program/fd_stake_program.h"
5 : #include "../runtime/program/fd_vote_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 : /* We need to update the amount of stake that each vote account has for
29 : the given epoch. This can only be done after the stake history
30 : sysvar has been updated. We also cache the stakes for each of the
31 : vote accounts for the previous epoch.
32 :
33 : https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L471 */
34 : void
35 : fd_refresh_vote_accounts( fd_bank_t * bank,
36 : fd_stake_delegations_t const * stake_delegations,
37 : fd_stake_history_t const * history,
38 0 : ulong * new_rate_activation_epoch ) {
39 :
40 0 : ulong epoch = fd_bank_epoch_get( bank );
41 :
42 0 : ulong total_stake = 0UL;
43 :
44 0 : fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
45 0 : if( FD_UNLIKELY( !vote_states ) ) {
46 0 : FD_LOG_CRIT(( "vote_states is NULL" ));
47 0 : }
48 :
49 : /* Reset the vote stakes so we can re-compute them based on the most
50 : current stake delegation values. */
51 0 : fd_vote_states_reset_stakes( vote_states );
52 :
53 0 : fd_stake_delegations_iter_t iter_[1];
54 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
55 0 : !fd_stake_delegations_iter_done( iter );
56 0 : fd_stake_delegations_iter_next( iter ) ) {
57 0 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
58 :
59 0 : fd_delegation_t delegation = {
60 0 : .voter_pubkey = stake_delegation->vote_account,
61 0 : .stake = stake_delegation->stake,
62 0 : .deactivation_epoch = stake_delegation->deactivation_epoch,
63 0 : .activation_epoch = stake_delegation->activation_epoch,
64 0 : .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
65 0 : };
66 :
67 0 : fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
68 0 : &delegation,
69 0 : epoch,
70 0 : history,
71 0 : new_rate_activation_epoch );
72 :
73 0 : fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, &stake_delegation->vote_account );
74 0 : if( FD_LIKELY( vote_state ) ) {
75 0 : total_stake += new_entry.effective;
76 0 : vote_state->stake += new_entry.effective;
77 0 : }
78 0 : }
79 :
80 0 : fd_bank_total_epoch_stake_set( bank, total_stake );
81 :
82 : /* This corresponding logic does not exist in the Agave client. The
83 : stakes from epoch T-2 are cached in the vote states struct in order
84 : to make clock calulations more efficient. This is purely an
85 : optimization. */
86 :
87 0 : fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_prev_locking_query( bank );
88 :
89 0 : if( FD_LIKELY( fd_bank_slot_get( bank )!=0UL ) ) {
90 0 : fd_vote_states_iter_t vs_iter_[1];
91 0 : for( fd_vote_states_iter_t * vs_iter = fd_vote_states_iter_init( vs_iter_, vote_states );
92 0 : !fd_vote_states_iter_done( vs_iter );
93 0 : fd_vote_states_iter_next( vs_iter ) ) {
94 0 : fd_vote_state_ele_t * vote_state = fd_vote_states_iter_ele( vs_iter );
95 0 : fd_vote_state_ele_t * vote_state_prev = fd_vote_states_query( vote_states_prev, &vote_state->vote_account );
96 0 : vote_state->stake_t_2 = !!vote_state_prev ? vote_state_prev->stake : 0UL;
97 0 : }
98 0 : }
99 0 : fd_bank_vote_states_prev_prev_end_locking_query( bank );
100 0 : fd_bank_vote_states_end_locking_modify( bank );
101 0 : }
102 :
103 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L280 */
104 : void
105 : fd_stakes_activate_epoch( fd_bank_t * bank,
106 : fd_accdb_user_t * accdb,
107 : fd_funk_txn_xid_t const * xid,
108 : fd_capture_ctx_t * capture_ctx,
109 : fd_stake_delegations_t const * stake_delegations,
110 0 : ulong * new_rate_activation_epoch ) {
111 :
112 : /* First, we need to accumulate the stats for the current amount of
113 : effective, activating, and deactivating stake for the current
114 : epoch. Once this is computed, we can add update our stake history
115 : sysvar. Afterward, we can refresh the stake values for the vote
116 : accounts for the new epoch. */
117 :
118 0 : fd_stake_history_t stake_history[1];
119 0 : if( FD_UNLIKELY( !fd_sysvar_stake_history_read( accdb->funk, xid, stake_history ) ) ) {
120 0 : FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
121 0 : }
122 :
123 0 : fd_epoch_stake_history_entry_pair_t new_elem = {
124 0 : .epoch = fd_bank_epoch_get( bank ),
125 0 : .entry = {
126 0 : .effective = 0UL,
127 0 : .activating = 0UL,
128 0 : .deactivating = 0UL
129 0 : }
130 0 : };
131 :
132 0 : fd_stake_delegations_iter_t iter_[1];
133 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
134 0 : !fd_stake_delegations_iter_done( iter );
135 0 : fd_stake_delegations_iter_next( iter ) ) {
136 0 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
137 :
138 0 : fd_delegation_t delegation = {
139 0 : .voter_pubkey = stake_delegation->vote_account,
140 0 : .stake = stake_delegation->stake,
141 0 : .activation_epoch = stake_delegation->activation_epoch,
142 0 : .deactivation_epoch = stake_delegation->deactivation_epoch,
143 0 : .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
144 0 : };
145 :
146 0 : fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
147 0 : &delegation,
148 0 : fd_bank_epoch_get( bank ),
149 0 : stake_history,
150 0 : new_rate_activation_epoch );
151 0 : new_elem.entry.effective += new_entry.effective;
152 0 : new_elem.entry.activating += new_entry.activating;
153 0 : new_elem.entry.deactivating += new_entry.deactivating;
154 0 : }
155 :
156 0 : fd_sysvar_stake_history_update( bank, accdb, xid, capture_ctx, &new_elem );
157 :
158 0 : if( FD_UNLIKELY( !fd_sysvar_stake_history_read( accdb->funk, xid, stake_history ) ) ) {
159 0 : FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
160 0 : }
161 :
162 : /* Now increment the epoch and recompute the stakes for the vote
163 : accounts for the new epoch value. */
164 :
165 0 : fd_bank_epoch_set( bank, fd_bank_epoch_get( bank ) + 1UL );
166 :
167 0 : fd_refresh_vote_accounts( bank,
168 0 : stake_delegations,
169 0 : stake_history,
170 0 : new_rate_activation_epoch );
171 :
172 0 : }
173 :
174 : int
175 : write_stake_state( fd_txn_account_t * stake_acc_rec,
176 0 : fd_stake_state_v2_t * stake_state ) {
177 :
178 0 : ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state);
179 :
180 0 : fd_bincode_encode_ctx_t ctx = {
181 0 : .data = fd_txn_account_get_data_mut( stake_acc_rec ),
182 0 : .dataend = fd_txn_account_get_data_mut( stake_acc_rec ) + encoded_stake_state_size,
183 0 : };
184 0 : if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) {
185 0 : FD_LOG_ERR(( "fd_stake_state_encode failed" ));
186 0 : }
187 :
188 0 : return 0;
189 0 : }
190 :
191 : void
192 : fd_stakes_update_stake_delegation( fd_txn_account_t * stake_account,
193 0 : fd_bank_t * bank ) {
194 :
195 0 : if( !stake_account->is_mutable ) return;
196 :
197 0 : fd_stake_delegations_t * stake_delegations_delta = fd_bank_stake_delegations_delta_locking_modify( bank );
198 0 : if( FD_UNLIKELY( !stake_delegations_delta ) ) {
199 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation delta" ));
200 0 : }
201 :
202 0 : if( fd_txn_account_get_lamports( stake_account )==0UL ) {
203 0 : fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
204 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
205 0 : return;
206 0 : }
207 :
208 0 : fd_stake_state_v2_t stake_state;
209 0 : int err = fd_stake_get_state( stake_account, &stake_state );
210 0 : if( FD_UNLIKELY( err!=0 ) ) {
211 0 : fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
212 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
213 0 : return;
214 0 : }
215 :
216 0 : if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
217 0 : fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
218 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
219 0 : return;
220 0 : }
221 :
222 0 : if( FD_UNLIKELY( fd_stake_state_v2_is_uninitialized( &stake_state ) ) ) {
223 0 : fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
224 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
225 0 : return;
226 0 : }
227 :
228 0 : if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
229 0 : fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
230 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
231 0 : return;
232 0 : }
233 :
234 0 : fd_stake_delegations_update( stake_delegations_delta,
235 0 : stake_account->pubkey,
236 0 : &stake_state.inner.stake.stake.delegation.voter_pubkey,
237 0 : stake_state.inner.stake.stake.delegation.stake,
238 0 : stake_state.inner.stake.stake.delegation.activation_epoch,
239 0 : stake_state.inner.stake.stake.delegation.deactivation_epoch,
240 0 : stake_state.inner.stake.stake.credits_observed,
241 0 : stake_state.inner.stake.stake.delegation.warmup_cooldown_rate );
242 :
243 0 : fd_bank_stake_delegations_delta_end_locking_modify( bank );
244 0 : }
245 :
246 : void
247 : fd_stakes_update_vote_state( fd_txn_account_t * vote_account,
248 0 : fd_bank_t * bank ) {
249 :
250 0 : if( !vote_account->is_mutable ) return;
251 :
252 0 : fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
253 :
254 0 : if( fd_txn_account_get_lamports( vote_account )==0UL ) {
255 0 : fd_vote_states_remove( vote_states, vote_account->pubkey );
256 0 : fd_bank_vote_states_end_locking_modify( bank );
257 0 : return;
258 0 : }
259 :
260 0 : if( !fd_vote_state_versions_is_correct_and_initialized( vote_account ) ) {
261 0 : fd_vote_states_remove( vote_states, vote_account->pubkey );
262 0 : fd_bank_vote_states_end_locking_modify( bank );
263 0 : return;
264 0 : }
265 :
266 0 : fd_vote_states_update_from_account( vote_states,
267 0 : vote_account->pubkey,
268 0 : fd_txn_account_get_data( vote_account ),
269 0 : fd_txn_account_get_data_len( vote_account ) );
270 0 : fd_bank_vote_states_end_locking_modify( bank );
271 0 : }
|