Line data Source code
1 : #include "fd_vote_state_versioned.h"
2 : #include "fd_vote_codec_tmpl.h"
3 : #include "fd_vote_utils.h"
4 : #include "fd_vote_state_v3.h"
5 : #include "fd_vote_state_v4.h"
6 : #include "fd_authorized_voters.h"
7 : #include "../fd_vote_program.h"
8 : #include "../../fd_runtime.h"
9 : #include "../../fd_system_ids.h"
10 :
11 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42 */
12 : #define DEFAULT_PRIOR_VOTERS_OFFSET 114
13 :
14 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886 */
15 : #define VERSION_OFFSET (4UL)
16 :
17 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887 */
18 : #define DEFAULT_PRIOR_VOTERS_END (118)
19 :
20 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6 */
21 : #define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL)
22 :
23 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60 */
24 : #define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL)
25 :
26 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L780-L785 */
27 : static inline fd_vote_lockout_t *
28 35787 : last_lockout( fd_vote_state_versioned_t * self ) {
29 35787 : fd_landed_vote_t * votes = NULL;
30 35787 : switch( self->kind ) {
31 0 : case fd_vote_state_versioned_enum_v3:
32 0 : votes = self->v3.votes;
33 0 : break;
34 35787 : case fd_vote_state_versioned_enum_v4:
35 35787 : votes = self->v4.votes;
36 35787 : break;
37 0 : default:
38 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
39 35787 : }
40 :
41 35787 : if( deq_fd_landed_vote_t_empty( votes ) ) return NULL;
42 33939 : fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( votes );
43 33939 : return &last_vote->lockout;
44 35787 : }
45 :
46 : /**********************************************************************/
47 : /* Getters */
48 : /**********************************************************************/
49 :
50 : int
51 : fd_vsv_get_state( fd_acc_t const * acc,
52 4336 : fd_vote_state_versioned_t * versioned ) {
53 4336 : if( FD_UNLIKELY( !fd_vote_state_versioned_deserialize( versioned, acc->data, acc->data_len ) ) ) {
54 528 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
55 528 : }
56 :
57 3808 : return FD_EXECUTOR_INSTR_SUCCESS;
58 4336 : }
59 :
60 : int
61 : fd_vsv_deserialize( fd_acc_t const * acc,
62 4169 : fd_vote_state_versioned_t * versioned ) {
63 4169 : int rc = fd_vsv_get_state( acc, versioned );
64 4169 : if( FD_UNLIKELY( rc ) ) return rc;
65 :
66 3641 : if( FD_UNLIKELY( versioned->kind==fd_vote_state_versioned_enum_uninitialized ) ) {
67 658 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
68 658 : }
69 :
70 2983 : return FD_EXECUTOR_INSTR_SUCCESS;
71 3641 : }
72 :
73 : fd_pubkey_t const *
74 1222 : fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) {
75 1222 : switch( self->kind ) {
76 0 : case fd_vote_state_versioned_enum_v1_14_11:
77 0 : return &self->v1_14_11.authorized_withdrawer;
78 0 : case fd_vote_state_versioned_enum_v3:
79 0 : return &self->v3.authorized_withdrawer;
80 1222 : case fd_vote_state_versioned_enum_v4:
81 1222 : return &self->v4.authorized_withdrawer;
82 0 : default:
83 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
84 1222 : }
85 1222 : }
86 :
87 : uchar
88 0 : fd_vsv_get_commission( fd_vote_state_versioned_t * self ) {
89 0 : switch( self->kind ) {
90 0 : case fd_vote_state_versioned_enum_v3:
91 0 : return self->v3.commission;
92 0 : case fd_vote_state_versioned_enum_v4:
93 0 : return (uchar)fd_ushort_min( (ushort)UCHAR_MAX, (ushort)( self->v4.inflation_rewards_commission_bps/100 ) );
94 0 : default:
95 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
96 0 : }
97 0 : }
98 :
99 : fd_vote_epoch_credits_t const *
100 9 : fd_vsv_get_epoch_credits( fd_vote_state_versioned_t * self ) {
101 9 : return fd_vsv_get_epoch_credits_mutable( self );
102 9 : }
103 :
104 : fd_landed_vote_t const *
105 681 : fd_vsv_get_votes( fd_vote_state_versioned_t * self ) {
106 681 : return fd_vsv_get_votes_mutable( self );
107 681 : }
108 :
109 : ulong const *
110 35787 : fd_vsv_get_last_voted_slot( fd_vote_state_versioned_t * self ) {
111 35787 : fd_vote_lockout_t * last_lockout_ = last_lockout( self );
112 35787 : if( FD_UNLIKELY( !last_lockout_ ) ) return NULL;
113 33939 : return &last_lockout_->slot;
114 35787 : }
115 :
116 : ulong const *
117 1323 : fd_vsv_get_root_slot( fd_vote_state_versioned_t * self ) {
118 1323 : switch( self->kind ) {
119 0 : case fd_vote_state_versioned_enum_v3:
120 0 : if( !self->v3.has_root_slot ) return NULL;
121 0 : return &self->v3.root_slot;
122 1323 : case fd_vote_state_versioned_enum_v4:
123 1323 : if( !self->v4.has_root_slot ) return NULL;
124 940 : return &self->v4.root_slot;
125 0 : default:
126 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
127 1323 : }
128 1323 : }
129 :
130 : fd_vote_block_timestamp_t const *
131 535 : fd_vsv_get_last_timestamp( fd_vote_state_versioned_t * self ) {
132 535 : switch( self->kind ) {
133 0 : case fd_vote_state_versioned_enum_v3:
134 0 : return &self->v3.last_timestamp;
135 535 : case fd_vote_state_versioned_enum_v4:
136 535 : return &self->v4.last_timestamp;
137 0 : default:
138 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
139 535 : }
140 535 : }
141 :
142 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L938 */
143 : int
144 238 : fd_vsv_has_bls_pubkey( fd_vote_state_versioned_t * self ) {
145 238 : switch( self->kind ) {
146 0 : case fd_vote_state_versioned_enum_uninitialized:
147 0 : return 0;
148 0 : case fd_vote_state_versioned_enum_v1_14_11:
149 0 : return 0;
150 0 : case fd_vote_state_versioned_enum_v3:
151 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L483 */
152 0 : return 0;
153 238 : case fd_vote_state_versioned_enum_v4:
154 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L676 */
155 238 : return !!self->v4.has_bls_pubkey_compressed;
156 0 : default:
157 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
158 238 : }
159 238 : }
160 :
161 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L823-L828 */
162 : ulong
163 180 : fd_vsv_get_pending_delegator_rewards( fd_vote_state_versioned_t * self ) {
164 180 : switch( self->kind ) {
165 0 : case fd_vote_state_versioned_enum_v3:
166 0 : return 0UL;
167 180 : case fd_vote_state_versioned_enum_v4:
168 180 : return self->v4.pending_delegator_rewards;
169 0 : default:
170 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
171 180 : }
172 180 : }
173 :
174 : /**********************************************************************/
175 : /* Mutable getters */
176 : /**********************************************************************/
177 :
178 : fd_vote_epoch_credits_t *
179 7612 : fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ) {
180 7612 : switch( self->kind ) {
181 0 : case fd_vote_state_versioned_enum_v3:
182 0 : return self->v3.epoch_credits;
183 7612 : case fd_vote_state_versioned_enum_v4:
184 7612 : return self->v4.epoch_credits;
185 0 : default:
186 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
187 7612 : }
188 7612 : }
189 :
190 : fd_landed_vote_t *
191 49502 : fd_vsv_get_votes_mutable( fd_vote_state_versioned_t * self ) {
192 49502 : switch( self->kind ) {
193 0 : case fd_vote_state_versioned_enum_v3:
194 0 : return self->v3.votes;
195 49502 : case fd_vote_state_versioned_enum_v4:
196 49502 : return self->v4.votes;
197 0 : default:
198 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
199 49502 : }
200 49502 : }
201 :
202 : /**********************************************************************/
203 : /* Setters */
204 : /**********************************************************************/
205 :
206 : int
207 : fd_vsv_set_state( fd_borrowed_account_t * self,
208 1386 : fd_vote_state_versioned_t * state ) {
209 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */
210 1386 : uchar * data = NULL;
211 1386 : ulong dlen = 0UL;
212 1386 : int err = fd_borrowed_account_get_data_mut( self, &data, &dlen );
213 1386 : if( FD_UNLIKELY( err ) ) {
214 0 : return err;
215 0 : }
216 :
217 : /* Note that although the serialization method already performs bounds
218 : checks, the account data buffer should remain unmodified if the
219 : serialization would fail.
220 : https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L978 */
221 1386 : ulong serialized_size = fd_vote_state_versioned_serialized_size( state );
222 1386 : if( FD_UNLIKELY( serialized_size>dlen ) ) {
223 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
224 0 : }
225 :
226 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L983 */
227 1386 : if( FD_UNLIKELY( fd_vote_state_versioned_serialize( state, data, dlen ) ) ) {
228 0 : FD_LOG_CRIT(( "invariant violation: fd_vote_state_versioned_serialize failed" ));
229 0 : }
230 :
231 1386 : return FD_EXECUTOR_INSTR_SUCCESS;
232 1386 : }
233 :
234 : int
235 : fd_vsv_set_vote_account_state( fd_exec_instr_ctx_t const * ctx,
236 : fd_borrowed_account_t * vote_account,
237 1231 : fd_vote_state_versioned_t * versioned ) {
238 1231 : switch( versioned->kind ) {
239 0 : case fd_vote_state_versioned_enum_v3:
240 0 : return fd_vote_state_v3_set_vote_account_state( ctx, vote_account, versioned );
241 1231 : case fd_vote_state_versioned_enum_v4:
242 1231 : return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned );
243 0 : default:
244 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", versioned->kind ));
245 1231 : }
246 1231 : }
247 :
248 : void
249 : fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self,
250 130 : fd_pubkey_t const * authorized_withdrawer ) {
251 130 : switch( self->kind ) {
252 0 : case fd_vote_state_versioned_enum_v3: {
253 0 : self->v3.authorized_withdrawer = *authorized_withdrawer;
254 0 : break;
255 0 : }
256 130 : case fd_vote_state_versioned_enum_v4: {
257 130 : self->v4.authorized_withdrawer = *authorized_withdrawer;
258 130 : break;
259 0 : }
260 0 : default:
261 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
262 130 : }
263 130 : }
264 :
265 : int
266 : fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx,
267 : fd_vote_state_versioned_t * self,
268 : fd_pubkey_t const * authorized_pubkey,
269 : ulong current_epoch,
270 : ulong target_epoch,
271 : uchar const * bls_pubkey,
272 : int authorized_withdrawer_signer,
273 : fd_pubkey_t const * signers[ FD_INSTR_SIGNERS_MAX ],
274 216 : ulong signers_cnt ) {
275 216 : switch( self->kind ) {
276 0 : case fd_vote_state_versioned_enum_v3:
277 0 : return fd_vote_state_v3_set_new_authorized_voter(
278 0 : ctx,
279 0 : &self->v3,
280 0 : authorized_pubkey,
281 0 : current_epoch,
282 0 : target_epoch,
283 0 : bls_pubkey,
284 0 : authorized_withdrawer_signer,
285 0 : signers,
286 0 : signers_cnt
287 0 : );
288 216 : case fd_vote_state_versioned_enum_v4:
289 216 : return fd_vote_state_v4_set_new_authorized_voter(
290 216 : ctx,
291 216 : &self->v4,
292 216 : authorized_pubkey,
293 216 : current_epoch,
294 216 : target_epoch,
295 216 : bls_pubkey,
296 216 : authorized_withdrawer_signer,
297 216 : signers,
298 216 : signers_cnt
299 216 : );
300 0 : default:
301 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
302 216 : }
303 216 : }
304 :
305 : void
306 : fd_vsv_set_node_pubkey( fd_vote_state_versioned_t * self,
307 175 : fd_pubkey_t const * node_pubkey ) {
308 175 : switch( self->kind ) {
309 0 : case fd_vote_state_versioned_enum_v3:
310 0 : self->v3.node_pubkey = *node_pubkey;
311 0 : break;
312 175 : case fd_vote_state_versioned_enum_v4:
313 175 : self->v4.node_pubkey = *node_pubkey;
314 175 : break;
315 0 : default:
316 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
317 175 : }
318 175 : }
319 :
320 : void
321 : fd_vsv_set_block_revenue_collector( fd_vote_state_versioned_t * self,
322 175 : fd_pubkey_t const * block_revenue_collector ) {
323 175 : switch( self->kind ) {
324 175 : case fd_vote_state_versioned_enum_v4:
325 175 : self->v4.block_revenue_collector = *block_revenue_collector;
326 175 : break;
327 0 : case fd_vote_state_versioned_enum_v3:
328 : /* No-op for v3 */
329 0 : break;
330 0 : default:
331 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
332 175 : }
333 175 : }
334 :
335 : void
336 : fd_vsv_set_inflation_rewards_collector( fd_vote_state_versioned_t * self,
337 0 : fd_pubkey_t const * inflation_rewards_collector ) {
338 0 : switch( self->kind ) {
339 0 : case fd_vote_state_versioned_enum_v4:
340 0 : self->v4.inflation_rewards_collector = *inflation_rewards_collector;
341 0 : break;
342 0 : case fd_vote_state_versioned_enum_v3:
343 : /* No-op for v3 */
344 0 : break;
345 0 : default:
346 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
347 0 : }
348 0 : }
349 :
350 : void
351 : fd_vsv_set_commission( fd_vote_state_versioned_t * self,
352 179 : uchar commission ) {
353 179 : switch( self->kind ) {
354 0 : case fd_vote_state_versioned_enum_v3:
355 0 : self->v3.commission = commission;
356 0 : break;
357 179 : case fd_vote_state_versioned_enum_v4:
358 179 : self->v4.inflation_rewards_commission_bps = (ushort)( commission*100 );
359 179 : break;
360 0 : default:
361 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
362 179 : }
363 179 : }
364 :
365 : void
366 : fd_vsv_set_inflation_rewards_commission_bps( fd_vote_state_versioned_t * self,
367 0 : ushort commission_bps ) {
368 0 : switch( self->kind ) {
369 0 : case fd_vote_state_versioned_enum_v4:
370 0 : self->v4.inflation_rewards_commission_bps = commission_bps;
371 0 : break;
372 0 : case fd_vote_state_versioned_enum_v3:
373 : /* No-op for v3 */
374 0 : break;
375 0 : default:
376 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
377 0 : }
378 0 : }
379 :
380 : void
381 : fd_vsv_set_block_revenue_commission_bps( fd_vote_state_versioned_t * self,
382 0 : ushort commission_bps ) {
383 0 : switch( self->kind ) {
384 0 : case fd_vote_state_versioned_enum_v4:
385 0 : self->v4.block_revenue_commission_bps = commission_bps;
386 0 : break;
387 0 : case fd_vote_state_versioned_enum_v3:
388 : /* No-op for v3 */
389 0 : break;
390 0 : default:
391 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
392 0 : }
393 0 : }
394 :
395 : void
396 7680 : fd_vsv_set_root_slot( fd_vote_state_versioned_t * self, ulong * root_slot ) {
397 7680 : switch( self->kind ) {
398 0 : case fd_vote_state_versioned_enum_v3:
399 0 : self->v3.has_root_slot = (root_slot!=NULL);
400 0 : if( FD_LIKELY( root_slot ) ) {
401 0 : self->v3.root_slot = *root_slot;
402 0 : }
403 0 : break;
404 7680 : case fd_vote_state_versioned_enum_v4:
405 7680 : self->v4.has_root_slot = (root_slot!=NULL);
406 7680 : if( FD_LIKELY( root_slot ) ) {
407 7592 : self->v4.root_slot = *root_slot;
408 7592 : }
409 7680 : break;
410 0 : default:
411 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
412 7680 : }
413 7680 : }
414 :
415 : void
416 : fd_vsv_set_last_timestamp( fd_vote_state_versioned_t * self,
417 387 : fd_vote_block_timestamp_t const * last_timestamp ) {
418 387 : switch( self->kind ) {
419 0 : case fd_vote_state_versioned_enum_v3:
420 0 : self->v3.last_timestamp = *last_timestamp;
421 0 : break;
422 387 : case fd_vote_state_versioned_enum_v4:
423 387 : self->v4.last_timestamp = *last_timestamp;
424 387 : break;
425 0 : default:
426 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
427 387 : }
428 387 : }
429 :
430 : /**********************************************************************/
431 : /* General functions */
432 : /**********************************************************************/
433 :
434 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855
435 : static void
436 16060 : double_lockouts( fd_vote_state_versioned_t * self ) {
437 16060 : fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
438 :
439 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856
440 16060 : ulong stack_depth = deq_fd_landed_vote_t_cnt( votes );
441 16060 : ulong i = 0;
442 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857
443 16060 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
444 377205 : !deq_fd_landed_vote_t_iter_done( votes, iter );
445 361145 : iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
446 361145 : fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( votes, iter );
447 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860
448 361145 : if( stack_depth >
449 361145 : fd_ulong_checked_add_expect(
450 361145 : i,
451 361145 : (ulong)v->lockout.confirmation_count,
452 361145 : "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) )
453 361145 : {
454 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864
455 361145 : fd_vote_lockout_increase_confirmation_count( &v->lockout, 1 );
456 361145 : }
457 361145 : i++;
458 361145 : }
459 16060 : }
460 :
461 : void
462 : fd_vsv_increment_credits( fd_vote_state_versioned_t * self,
463 : ulong epoch,
464 7603 : ulong credits ) {
465 7603 : fd_vote_epoch_credits_t * epoch_credits = fd_vsv_get_epoch_credits_mutable( self );
466 :
467 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */
468 7603 : if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) {
469 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */
470 2 : deq_fd_vote_epoch_credits_t_push_tail_wrap(
471 2 : epoch_credits,
472 2 : ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } );
473 7601 : } else if( FD_LIKELY( epoch !=
474 7601 : deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch ) ) {
475 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */
476 299 : fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits );
477 :
478 299 : ulong credits = last->credits;
479 299 : ulong prev_credits = last->prev_credits;
480 :
481 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */
482 299 : if( FD_LIKELY( credits!=prev_credits ) ) {
483 296 : if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) {
484 : /* Although Agave performs a `.remove(0)` AFTER the call to
485 : `.push()`, there is an edge case where the epoch credits is
486 : full, making the call to `_push_tail()` unsafe. Since Agave's
487 : structures are dynamically allocated, it is safe for them to
488 : simply call `.push()` and then popping afterwards. We have to
489 : reverse the order of operations to maintain correct behavior
490 : and avoid overflowing the deque.
491 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */
492 3 : deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
493 3 : }
494 :
495 : /* This will not fail because we already popped if we're at
496 : capacity, since the epoch_credits deque is allocated with a
497 : minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */
498 296 : deq_fd_vote_epoch_credits_t_push_tail(
499 296 : epoch_credits,
500 296 : ( fd_vote_epoch_credits_t ){
501 296 : .epoch = epoch, .credits = credits, .prev_credits = credits } );
502 296 : } else {
503 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */
504 3 : deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch = epoch;
505 :
506 : /* Here we can perform the same deque size check and pop if
507 : we're beyond the maximum epoch credits len. */
508 3 : if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) {
509 0 : deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
510 0 : }
511 3 : }
512 299 : }
513 :
514 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663
515 7603 : deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits = fd_ulong_sat_add(
516 7603 : deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits, credits );
517 7603 : }
518 :
519 : int
520 : fd_vsv_process_timestamp( fd_exec_instr_ctx_t * ctx,
521 : fd_vote_state_versioned_t * self,
522 : ulong slot,
523 535 : long timestamp ) {
524 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L160 */
525 535 : fd_vote_block_timestamp_t const * last_timestamp = fd_vsv_get_last_timestamp( self );
526 535 : if( FD_UNLIKELY(
527 535 : ( slot<last_timestamp->slot || timestamp<last_timestamp->timestamp ) ||
528 535 : ( slot==last_timestamp->slot &&
529 535 : ( slot!=last_timestamp->slot || timestamp!=last_timestamp->timestamp ) &&
530 535 : last_timestamp->slot!=0UL ) ) ) {
531 148 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD;
532 148 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
533 148 : }
534 :
535 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L168 */
536 387 : fd_vote_block_timestamp_t new_timestamp = {
537 387 : .slot = slot,
538 387 : .timestamp = timestamp,
539 387 : };
540 387 : fd_vsv_set_last_timestamp( self, &new_timestamp );
541 387 : return FD_EXECUTOR_INSTR_SUCCESS;
542 535 : }
543 :
544 : void
545 16060 : fd_vsv_pop_expired_votes( fd_vote_state_versioned_t * self, ulong next_vote_slot ) {
546 16060 : fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
547 :
548 20930 : while( !deq_fd_landed_vote_t_empty( votes ) ) {
549 20617 : fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( votes );
550 20617 : if( !( fd_vote_lockout_is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) {
551 4870 : deq_fd_landed_vote_t_pop_tail( votes );
552 15747 : } else {
553 15747 : break;
554 15747 : }
555 20617 : }
556 16060 : }
557 :
558 : void
559 : fd_vsv_process_next_vote_slot( fd_vote_state_versioned_t * self,
560 : ulong next_vote_slot,
561 : ulong epoch,
562 16060 : ulong current_slot ) {
563 16060 : ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( self );
564 16060 : if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return;
565 :
566 16060 : fd_vsv_pop_expired_votes( self, next_vote_slot );
567 :
568 16060 : fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
569 :
570 16060 : fd_landed_vote_t landed_vote = {
571 16060 : .latency = fd_vote_compute_vote_latency( next_vote_slot, current_slot ),
572 16060 : .lockout = ( fd_vote_lockout_t ){ .slot = next_vote_slot }
573 16060 : };
574 :
575 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623
576 16060 : if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( votes ) == MAX_LOCKOUT_HISTORY ) ) {
577 7252 : ulong credits = fd_vote_credits_for_vote_at_index( votes, 0 );
578 7252 : fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( votes );
579 7252 : fd_vsv_set_root_slot( self, &landed_vote.lockout.slot );
580 :
581 7252 : fd_vsv_increment_credits( self, epoch, credits );
582 7252 : }
583 :
584 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634
585 16060 : deq_fd_landed_vote_t_push_tail_wrap( votes, landed_vote );
586 16060 : double_lockouts( self );
587 16060 : }
588 :
589 : int
590 0 : fd_vsv_try_convert_to_v3( fd_vote_state_versioned_t * self ) {
591 0 : switch( self->kind ) {
592 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L47-L73 */
593 0 : case fd_vote_state_versioned_enum_uninitialized: {
594 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
595 0 : }
596 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L75-L91 */
597 0 : case fd_vote_state_versioned_enum_v1_14_11: {
598 0 : fd_vote_state_1_14_11_t * state = &self->v1_14_11;
599 :
600 : /* Temporary to hold v3. Note that since v1_14_11 and v3/v4 use
601 : the same underlying types for votes, we can just directly set
602 : it and avoid an intermediate call to
603 : fd_vote_lockout_landed_votes_from_lockouts. */
604 0 : fd_vote_state_v3_t v3 = {
605 0 : .node_pubkey = state->node_pubkey,
606 0 : .authorized_withdrawer = state->authorized_withdrawer,
607 0 : .commission = state->commission,
608 0 : .votes = state->votes,
609 0 : .has_root_slot = state->has_root_slot,
610 0 : .root_slot = state->root_slot,
611 0 : .authorized_voters = state->authorized_voters,
612 0 : .prior_voters = state->prior_voters,
613 0 : .epoch_credits = state->epoch_credits,
614 0 : .last_timestamp = state->last_timestamp
615 0 : };
616 :
617 : /* Emplace new vote state into target */
618 0 : self->kind = fd_vote_state_versioned_enum_v3;
619 0 : self->v3 = v3;
620 :
621 0 : return FD_EXECUTOR_INSTR_SUCCESS;
622 0 : }
623 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L93 */
624 0 : case fd_vote_state_versioned_enum_v3:
625 0 : return FD_EXECUTOR_INSTR_SUCCESS;
626 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L96 */
627 0 : case fd_vote_state_versioned_enum_v4:
628 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
629 0 : default:
630 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
631 0 : }
632 0 : }
633 :
634 : int
635 : fd_vsv_try_convert_to_v4( fd_vote_state_versioned_t * self,
636 2919 : fd_pubkey_t const * vote_pubkey ) {
637 2919 : switch( self->kind ) {
638 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L971-L974 */
639 0 : case fd_vote_state_versioned_enum_uninitialized: {
640 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
641 0 : }
642 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L975-L989 */
643 102 : case fd_vote_state_versioned_enum_v1_14_11: {
644 102 : fd_vote_state_1_14_11_t * state = &self->v1_14_11;
645 :
646 : /* Temporary to hold v4. Note that since v1_14_11 and v3/v4 use
647 : the same underlying types for votes, we can just directly set
648 : it and avoid an intermediate call to
649 : fd_vote_lockout_landed_votes_from_lockouts. */
650 102 : fd_vote_state_v4_t v4 = {
651 102 : .node_pubkey = state->node_pubkey,
652 102 : .authorized_withdrawer = state->authorized_withdrawer,
653 102 : .inflation_rewards_collector = *vote_pubkey,
654 102 : .block_revenue_collector = state->node_pubkey,
655 102 : .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
656 102 : .block_revenue_commission_bps = FD_VOTE_DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
657 102 : .pending_delegator_rewards = 0,
658 102 : .has_bls_pubkey_compressed = 0,
659 102 : .votes = state->votes,
660 102 : .has_root_slot = state->has_root_slot,
661 102 : .root_slot = state->root_slot,
662 102 : .authorized_voters = state->authorized_voters,
663 102 : .epoch_credits = state->epoch_credits,
664 102 : .last_timestamp = state->last_timestamp
665 102 : };
666 :
667 : /* Emplace new vote state into target */
668 102 : self->kind = fd_vote_state_versioned_enum_v4;
669 102 : self->v4 = v4;
670 :
671 102 : return FD_EXECUTOR_INSTR_SUCCESS;
672 0 : }
673 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L990-L1004 */
674 1244 : case fd_vote_state_versioned_enum_v3: {
675 1244 : fd_vote_state_v3_t * state = &self->v3;
676 1244 : fd_vote_state_v4_t v4 = {
677 1244 : .node_pubkey = state->node_pubkey,
678 1244 : .authorized_withdrawer = state->authorized_withdrawer,
679 1244 : .inflation_rewards_collector = *vote_pubkey,
680 1244 : .block_revenue_collector = state->node_pubkey,
681 1244 : .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
682 1244 : .block_revenue_commission_bps = FD_VOTE_DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
683 1244 : .pending_delegator_rewards = 0,
684 1244 : .has_bls_pubkey_compressed = 0,
685 1244 : .votes = state->votes,
686 1244 : .has_root_slot = state->has_root_slot,
687 1244 : .root_slot = state->root_slot,
688 1244 : .authorized_voters = state->authorized_voters,
689 1244 : .epoch_credits = state->epoch_credits,
690 1244 : .last_timestamp = state->last_timestamp
691 1244 : };
692 :
693 : /* Emplace new vote state into target */
694 1244 : self->kind = fd_vote_state_versioned_enum_v4;
695 1244 : self->v4 = v4;
696 :
697 1244 : return FD_EXECUTOR_INSTR_SUCCESS;
698 0 : }
699 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L1005 */
700 1573 : case fd_vote_state_versioned_enum_v4:
701 1573 : return FD_EXECUTOR_INSTR_SUCCESS;
702 0 : default:
703 0 : FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
704 2919 : }
705 2919 : }
706 :
707 : int
708 : fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t * ctx,
709 : fd_borrowed_account_t * vote_account,
710 9 : int target_version ) {
711 : /* V4 clears the entire account data on deinitialize.
712 : https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/handler.rs#L366-L377 */
713 9 : (void)ctx;
714 9 : (void)target_version;
715 9 : uchar * data;
716 9 : ulong dlen;
717 9 : int rc = fd_borrowed_account_get_data_mut( vote_account, &data, &dlen );
718 9 : if( FD_UNLIKELY( rc ) ) return rc;
719 8 : fd_memset( data, 0, dlen );
720 8 : return FD_EXECUTOR_INSTR_SUCCESS;
721 9 : }
722 :
723 : int
724 3155 : fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) {
725 3155 : switch( self->kind ) {
726 167 : case fd_vote_state_versioned_enum_uninitialized:
727 167 : return 1;
728 145 : case fd_vote_state_versioned_enum_v1_14_11:
729 145 : return fd_authorized_voters_is_empty( &self->v1_14_11.authorized_voters );
730 1270 : case fd_vote_state_versioned_enum_v3:
731 1270 : return fd_authorized_voters_is_empty( &self->v3.authorized_voters );
732 1573 : case fd_vote_state_versioned_enum_v4:
733 1573 : return 0; // v4 vote states are always initialized
734 0 : default:
735 0 : FD_LOG_CRIT(( "unsupported vote state versioned kind: %u", self->kind ));
736 3155 : }
737 3155 : }
738 :
739 : int
740 : fd_vsv_is_correct_size_and_initialized( uchar const * data,
741 1648 : ulong data_len ) {
742 : /* VoteStateV4::is_correct_size_and_initialized
743 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L207-L210 */
744 1648 : if( FD_LIKELY( data_len==FD_VOTE_STATE_V4_SZ && FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v4 ) ) {
745 1641 : return 1;
746 1641 : }
747 :
748 : /* VoteStateV3::is_correct_size_and_initialized
749 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L509-L514 */
750 7 : if( FD_LIKELY( data_len==FD_VOTE_STATE_V3_SZ &&
751 8 : !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) {
752 8 : return 1;
753 8 : }
754 :
755 : /* VoteState1_14_11::is_correct_size_and_initialized
756 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_1_14_11.rs#L63-L69 */
757 >1844*10^16 : if( FD_LIKELY( data_len==FD_VOTE_STATE_V2_SZ &&
758 >1844*10^16 : !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) {
759 0 : return 1;
760 0 : }
761 :
762 >1844*10^16 : return 0;
763 >1844*10^16 : }
764 :
765 : int
766 : fd_vsv_is_correct_size_owner_and_init( uchar const * owner,
767 : uchar const * data,
768 1661 : ulong data_len ) {
769 1661 : if( FD_UNLIKELY( memcmp( owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
770 12 : return 0;
771 12 : }
772 :
773 1649 : return fd_vsv_is_correct_size_and_initialized( data, data_len );
774 1661 : }
|