Line data Source code
1 : #include "fd_vote_program.h"
2 : #include "../fd_runtime.h"
3 : #include "../fd_borrowed_account.h"
4 : #include "../fd_executor.h"
5 : #include "../fd_pubkey_utils.h"
6 : #include "../sysvar/fd_sysvar_rent.h"
7 : #include "../sysvar/fd_sysvar.h"
8 : #include "../fd_system_ids.h"
9 :
10 : #include <limits.h>
11 : #include <math.h>
12 : #include <stdio.h>
13 : #include <string.h>
14 :
15 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L35
16 0 : #define MAX_LOCKOUT_HISTORY 31UL
17 :
18 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
19 0 : #define INITIAL_LOCKOUT 2UL
20 :
21 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
22 : #define MAX_EPOCH_CREDITS_HISTORY 64UL
23 :
24 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42
25 0 : #define DEFAULT_PRIOR_VOTERS_OFFSET 114
26 :
27 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L45
28 0 : #define VOTE_CREDITS_GRACE_SLOTS 2
29 :
30 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L48
31 0 : #define VOTE_CREDITS_MAXIMUM_PER_SLOT 16
32 :
33 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L51
34 : #define VOTE_CREDITS_MAXIMUM_PER_SLOT_OLD 8
35 :
36 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
37 : #define SLOT_DEFAULT 0UL
38 :
39 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
40 : #define SLOT_MAX ULONG_MAX
41 :
42 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886
43 0 : #define VERSION_OFFSET (4UL)
44 :
45 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887
46 : #define DEFAULT_PRIOR_VOTERS_END (118)
47 :
48 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6
49 0 : #define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL)
50 :
51 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60
52 : #define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL)
53 :
54 : #define ACCOUNTS_MAX 4 /* Vote instructions take in at most 4 accounts */
55 :
56 : #define DEFAULT_COMPUTE_UNITS 2100UL
57 :
58 : /**********************************************************************/
59 : /* size_of */
60 : /**********************************************************************/
61 :
62 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L82
63 : static inline ulong
64 0 : size_of_versioned( int is_current ) {
65 0 : return fd_ulong_if( is_current, FD_VOTE_STATE_V3_SZ, FD_VOTE_STATE_V2_SZ );
66 0 : }
67 :
68 : /**********************************************************************/
69 : /* impl Lockout */
70 : /**********************************************************************/
71 :
72 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104
73 : static inline ulong
74 0 : lockout( fd_vote_lockout_t * self ) {
75 : /* Confirmation count can never be greater than MAX_LOCKOUT_HISTORY, preventing overflow.
76 : Although Agave does not consider overflow, we do for fuzzing conformance. */
77 0 : ulong confirmation_count = fd_ulong_min( self->confirmation_count, MAX_LOCKOUT_HISTORY );
78 0 : return 1UL<<confirmation_count;
79 0 : }
80 :
81 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L110
82 : static inline ulong
83 0 : last_locked_out_slot( fd_vote_lockout_t * self ) {
84 0 : return fd_ulong_sat_add( self->slot, lockout( self ) );
85 0 : }
86 :
87 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114
88 : static inline ulong
89 0 : is_locked_out_at_slot( fd_vote_lockout_t * self, ulong slot ) {
90 0 : return last_locked_out_slot( self ) >= slot;
91 0 : }
92 :
93 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122
94 : static void
95 0 : increase_confirmation_count( fd_vote_lockout_t * self, uint by ) {
96 0 : self->confirmation_count = fd_uint_sat_add( self->confirmation_count, by );
97 0 : }
98 :
99 : /**********************************************************************/
100 : /* impl From<VoteState> for VoteState1_14_11 */
101 : /**********************************************************************/
102 :
103 : /* from_vote_state_1_14_11 converts a "current" vote state object into
104 : the older "v1.14.11" version. This destroys the "current" object in
105 : the process. */
106 :
107 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67
108 : static void
109 : from_vote_state_1_14_11( fd_vote_state_t * vote_state,
110 : fd_vote_state_1_14_11_t * vote_state_1_14_11, /* out */
111 0 : uchar * vote_lockout_mem ) {
112 0 : vote_state_1_14_11->node_pubkey = vote_state->node_pubkey; /* copy */
113 0 : vote_state_1_14_11->authorized_withdrawer = vote_state->authorized_withdrawer; /* copy */
114 0 : vote_state_1_14_11->commission = vote_state->commission; /* copy */
115 :
116 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L72
117 0 : if( vote_state->votes ) {
118 0 : vote_state_1_14_11->votes = deq_fd_vote_lockout_t_join(
119 0 : deq_fd_vote_lockout_t_new( vote_lockout_mem, deq_fd_landed_vote_t_cnt( vote_state->votes ) ) );
120 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes );
121 0 : !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter );
122 0 : iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) {
123 0 : fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter );
124 0 : deq_fd_vote_lockout_t_push_tail_wrap( vote_state_1_14_11->votes, landed_vote->lockout );
125 0 : }
126 0 : }
127 :
128 0 : vote_state_1_14_11->has_root_slot = vote_state->has_root_slot; /* copy */
129 0 : vote_state_1_14_11->root_slot = vote_state->root_slot; /* copy */
130 0 : vote_state_1_14_11->authorized_voters = vote_state->authorized_voters; /* move */
131 0 : vote_state_1_14_11->prior_voters = vote_state->prior_voters; /* deep copy */
132 0 : vote_state_1_14_11->epoch_credits = vote_state->epoch_credits; /* move */
133 0 : vote_state_1_14_11->last_timestamp = vote_state->last_timestamp; /* deep copy */
134 :
135 : /* Clear moved objects */
136 0 : vote_state->authorized_voters.treap = NULL;
137 0 : vote_state->authorized_voters.pool = NULL;
138 0 : vote_state->epoch_credits = NULL;
139 :
140 0 : }
141 :
142 : /**********************************************************************/
143 : /* impl VoteAccount */
144 : /**********************************************************************/
145 :
146 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */
147 : static int
148 : get_state( fd_txn_account_t const * self,
149 0 : uchar * res ) {
150 :
151 0 : fd_bincode_decode_ctx_t decode = {
152 0 : .data = fd_txn_account_get_data( self ),
153 0 : .dataend = fd_txn_account_get_data( self ) + fd_txn_account_get_data_len( self ),
154 0 : };
155 :
156 0 : ulong total_sz = 0UL;
157 0 : int err = fd_vote_state_versioned_decode_footprint( &decode, &total_sz );
158 0 : if( FD_UNLIKELY( err ) ) {
159 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
160 0 : }
161 :
162 0 : FD_TEST( total_sz<=FD_VOTE_STATE_VERSIONED_FOOTPRINT );
163 :
164 0 : fd_vote_state_versioned_decode( res, &decode );
165 :
166 0 : return FD_EXECUTOR_INSTR_SUCCESS;
167 :
168 0 : }
169 :
170 : static int
171 : set_state( fd_borrowed_account_t * self,
172 0 : fd_vote_state_versioned_t * state ) {
173 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */
174 0 : uchar * data = NULL;
175 0 : ulong dlen = 0UL;
176 0 : int err = fd_borrowed_account_get_data_mut( self, &data, &dlen );
177 0 : if( FD_UNLIKELY( err ) ) {
178 0 : return err;
179 0 : }
180 :
181 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L978
182 0 : ulong serialized_size = fd_vote_state_versioned_size( state );
183 0 : if( FD_UNLIKELY( serialized_size > dlen ) )
184 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
185 :
186 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L983
187 0 : fd_bincode_encode_ctx_t encode =
188 0 : { .data = data,
189 0 : .dataend = data + dlen };
190 0 : do {
191 0 : int err = fd_vote_state_versioned_encode( state, &encode );
192 0 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_vote_state_versioned_encode failed (%d)", err ));
193 0 : } while(0);
194 :
195 0 : return FD_EXECUTOR_INSTR_SUCCESS;
196 0 : }
197 :
198 : /**********************************************************************/
199 : /* impl AuthorizedVoters */
200 : /**********************************************************************/
201 :
202 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17
203 : static fd_vote_authorized_voters_t *
204 : authorized_voters_new( ulong epoch,
205 : fd_pubkey_t const * pubkey,
206 0 : uchar * mem ) {
207 :
208 0 : FD_SCRATCH_ALLOC_INIT( l, mem );
209 0 : fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(), sizeof(fd_vote_authorized_voters_t) );
210 0 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
211 0 : void * treap_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
212 :
213 0 : authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
214 0 : authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
215 0 : if( 0 == fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) {
216 0 : FD_LOG_ERR(( "Authorized_voter pool is empty" ));
217 0 : }
218 0 : fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool );
219 0 : ele->epoch = epoch;
220 0 : ele->pubkey = *pubkey;
221 0 : ele->prio = (ulong)&ele->pubkey;
222 0 : fd_vote_authorized_voters_treap_ele_insert( authorized_voters->treap, ele, authorized_voters->pool );
223 0 : return authorized_voters;
224 0 : }
225 :
226 : // Helper to create an empty AuthorizedVoters structure (for default/uninitialized states)
227 : static fd_vote_authorized_voters_t *
228 0 : authorized_voters_new_empty( uchar * mem ) {
229 0 : FD_SCRATCH_ALLOC_INIT( l, mem );
230 0 : fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(), sizeof(fd_vote_authorized_voters_t) );
231 0 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
232 0 : void * treap_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
233 :
234 0 : authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
235 0 : authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
236 0 : return authorized_voters;
237 0 : }
238 :
239 : static inline int
240 0 : authorized_voters_is_empty( fd_vote_authorized_voters_t * self ) {
241 0 : return fd_vote_authorized_voters_treap_ele_cnt( self->treap ) == 0;
242 0 : }
243 :
244 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80
245 : static inline int
246 0 : authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ) {
247 0 : return !!fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool );
248 0 : }
249 :
250 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72
251 : static inline fd_vote_authorized_voter_t *
252 0 : authorized_voters_last( fd_vote_authorized_voters_t * self ) {
253 0 : fd_vote_authorized_voters_treap_rev_iter_t iter =
254 0 : fd_vote_authorized_voters_treap_rev_iter_init( self->treap, self->pool );
255 0 : return fd_vote_authorized_voters_treap_rev_iter_ele( iter, self->pool );
256 0 : }
257 :
258 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43
259 : static void
260 : authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self,
261 0 : ulong current_epoch ) {
262 :
263 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L46
264 0 : ulong expired_keys[ FD_VOTE_AUTHORIZED_VOTERS_MIN ];
265 0 : ulong key_cnt = 0;
266 0 : for( fd_vote_authorized_voters_treap_fwd_iter_t iter =
267 0 : fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool );
268 0 : !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
269 0 : iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) {
270 0 : fd_vote_authorized_voter_t * ele =
271 0 : fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool );
272 0 : if( ele->epoch < current_epoch ) expired_keys[key_cnt++] = ele->epoch;
273 0 : }
274 :
275 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L52
276 0 : for( ulong i = 0; i < key_cnt; i++ ) {
277 0 : fd_vote_authorized_voter_t * ele =
278 0 : fd_vote_authorized_voters_treap_ele_query( self->treap, expired_keys[i], self->pool );
279 0 : fd_vote_authorized_voters_treap_ele_remove( self->treap, ele, self->pool );
280 0 : fd_vote_authorized_voters_pool_ele_release( self->pool, ele );
281 0 : }
282 :
283 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L60
284 0 : FD_TEST( !authorized_voters_is_empty( self ) );
285 :
286 0 : }
287 :
288 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91
289 : static fd_vote_authorized_voter_t *
290 : authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self,
291 : ulong epoch,
292 0 : int * existed ) {
293 0 : *existed = 0;
294 0 : ulong latest_epoch = 0;
295 0 : fd_vote_authorized_voter_t * res =
296 0 : fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool );
297 : // "predecessor" would be more big-O optimal here, but mirroring labs logic
298 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L93
299 0 : if( FD_UNLIKELY( !res ) ) {
300 0 : for( fd_vote_authorized_voters_treap_fwd_iter_t iter =
301 0 : fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool );
302 0 : !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
303 0 : iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) {
304 0 : fd_vote_authorized_voter_t * ele =
305 0 : fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool );
306 0 : if( ele->epoch < epoch && ( latest_epoch == 0 || ele->epoch > latest_epoch ) ) {
307 0 : latest_epoch = ele->epoch;
308 0 : res = ele;
309 0 : }
310 0 : }
311 0 : *existed = 0;
312 0 : return res;
313 0 : } else {
314 0 : *existed = 1;
315 0 : return res;
316 0 : }
317 0 : return res;
318 0 : }
319 :
320 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28
321 : static fd_vote_authorized_voter_t *
322 : authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self,
323 0 : ulong epoch ) {
324 0 : int existed = 0;
325 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L29
326 0 : fd_vote_authorized_voter_t * res =
327 0 : authorized_voters_get_or_calculate_authorized_voter_for_epoch( self, epoch, &existed );
328 0 : if( !res ) return NULL;
329 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L32
330 0 : if( !existed ) {
331 : /* insert cannot fail because !existed */
332 0 : if( 0 == fd_vote_authorized_voters_pool_free( self->pool) ) {
333 0 : FD_LOG_ERR(( "Authorized_voter pool is empty" ));
334 0 : }
335 0 : fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool );
336 0 : ele->epoch = epoch;
337 0 : ele->pubkey = res->pubkey;
338 0 : ele->prio = (ulong)&res->pubkey;
339 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L33
340 0 : fd_vote_authorized_voters_treap_ele_insert( self->treap, ele, self->pool );
341 0 : }
342 0 : return res;
343 0 : }
344 :
345 : /**********************************************************************/
346 : /* impl VoteStateVersions */
347 : /**********************************************************************/
348 :
349 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L66
350 : static fd_landed_vote_t *
351 : landed_votes_from_lockouts( fd_vote_lockout_t * lockouts,
352 0 : uchar * mem ) {
353 0 : if( !lockouts ) return NULL;
354 :
355 : /* Allocate MAX_LOCKOUT_HISTORY (sane case) by default. In case the
356 : vote account is corrupt, allocate as many entries are needed. */
357 :
358 0 : ulong cnt = deq_fd_vote_lockout_t_cnt( lockouts );
359 0 : cnt = fd_ulong_max( cnt, MAX_LOCKOUT_HISTORY );
360 :
361 0 : fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( mem, cnt ) );
362 0 : if( FD_UNLIKELY( !landed_votes ) ) {
363 0 : FD_LOG_CRIT(( "failed to join landed votes" ));
364 0 : }
365 :
366 0 : for( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( lockouts );
367 0 : !deq_fd_vote_lockout_t_iter_done( lockouts, iter );
368 0 : iter = deq_fd_vote_lockout_t_iter_next( lockouts, iter ) ) {
369 0 : fd_vote_lockout_t const * ele = deq_fd_vote_lockout_t_iter_ele_const( lockouts, iter );
370 :
371 0 : fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( landed_votes );
372 0 : fd_landed_vote_new( elem );
373 :
374 0 : elem->latency = 0;
375 0 : elem->lockout.slot = ele->slot;
376 0 : elem->lockout.confirmation_count = ele->confirmation_count;
377 0 : }
378 :
379 0 : return landed_votes;
380 0 : }
381 :
382 :
383 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L70
384 : static inline int
385 0 : is_uninitialized( fd_vote_state_versioned_t * self ) {
386 0 : switch( self->discriminant ) {
387 0 : case fd_vote_state_versioned_enum_v0_23_5:;
388 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L73
389 0 : fd_pubkey_t pubkey_default = { 0 };
390 0 : return 0 ==
391 0 : memcmp( &self->inner.v0_23_5.authorized_voter, &pubkey_default, sizeof( fd_pubkey_t ) );
392 0 : case fd_vote_state_versioned_enum_v1_14_11:;
393 0 : return authorized_voters_is_empty( &self->inner.v1_14_11.authorized_voters );
394 0 : case fd_vote_state_versioned_enum_current:
395 0 : return authorized_voters_is_empty( &self->inner.current.authorized_voters );
396 0 : default:
397 0 : FD_LOG_ERR(( "missing handler or invalid vote state version: %u", self->discriminant ));
398 0 : }
399 0 : }
400 :
401 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L73
402 : static void
403 : convert_to_current( fd_vote_state_versioned_t * self,
404 : uchar * authorized_voters_mem,
405 0 : uchar * landed_votes_mem ) {
406 0 : switch( self->discriminant ) {
407 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L19
408 0 : case fd_vote_state_versioned_enum_v0_23_5: {
409 0 : fd_vote_state_0_23_5_t * state = &self->inner.v0_23_5;
410 : // Check if uninitialized (authorized_voter is all zeros)
411 0 : int is_uninitialized = 1;
412 0 : for( ulong i = 0; i < sizeof(fd_pubkey_t); i++ ) {
413 0 : if( state->authorized_voter.uc[i] != 0 ) {
414 0 : is_uninitialized = 0;
415 0 : break;
416 0 : }
417 0 : }
418 :
419 0 : fd_vote_authorized_voters_t * authorized_voters;
420 0 : if( is_uninitialized ) {
421 : // Create empty AuthorizedVoters (default), initialized but with no entries
422 0 : authorized_voters = authorized_voters_new_empty( authorized_voters_mem );
423 0 : } else {
424 0 : authorized_voters = authorized_voters_new(
425 0 : state->authorized_voter_epoch, &state->authorized_voter, authorized_voters_mem );
426 0 : }
427 :
428 : /* Temporary to hold current */
429 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L23
430 0 : fd_vote_state_t current = {
431 0 : .node_pubkey = state->node_pubkey, /* copy */
432 0 : .authorized_withdrawer = state->authorized_withdrawer, /* copy */
433 0 : .commission = state->commission, /* copy */
434 0 : .votes = landed_votes_from_lockouts( state->votes, landed_votes_mem ),
435 0 : .has_root_slot = state->has_root_slot, /* copy */
436 0 : .root_slot = state->root_slot, /* copy */
437 0 : .authorized_voters = *authorized_voters,
438 0 : .prior_voters = (fd_vote_prior_voters_t) {
439 0 : .idx = 31UL,
440 0 : .is_empty = 1,
441 0 : },
442 0 : .epoch_credits = state->epoch_credits, /* move */
443 0 : .last_timestamp = state->last_timestamp, /* deep copy */
444 0 : };
445 :
446 : /* Move objects */
447 0 : state->epoch_credits = NULL;
448 :
449 : /* Emplace new vote state into target */
450 0 : self->discriminant = fd_vote_state_versioned_enum_current;
451 0 : self->inner.current = current;
452 :
453 0 : break;
454 0 : }
455 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L44
456 0 : case fd_vote_state_versioned_enum_v1_14_11: {
457 0 : fd_vote_state_1_14_11_t * state = &self->inner.v1_14_11;
458 :
459 : /* Temporary to hold current */
460 0 : fd_vote_state_t current = {
461 0 : .node_pubkey = state->node_pubkey, /* copy */
462 0 : .authorized_withdrawer = state->authorized_withdrawer, /* copy */
463 0 : .commission = state->commission, /* copy */
464 0 : .votes = landed_votes_from_lockouts( state->votes, landed_votes_mem ),
465 0 : .has_root_slot = state->has_root_slot, /* copy */
466 0 : .root_slot = state->root_slot, /* copy */
467 0 : .authorized_voters = state->authorized_voters, /* move */
468 0 : .prior_voters = state->prior_voters, /* deep copy */
469 0 : .epoch_credits = state->epoch_credits, /* move */
470 0 : .last_timestamp = state->last_timestamp /* deep copy */
471 0 : };
472 :
473 : /* Move objects */
474 0 : state->authorized_voters.treap = NULL;
475 0 : state->authorized_voters.pool = NULL;
476 0 : state->epoch_credits = NULL;
477 :
478 : /* Emplace new vote state into target */
479 0 : self->discriminant = fd_vote_state_versioned_enum_current;
480 0 : self->inner.current = current;
481 :
482 0 : break;
483 0 : }
484 0 : case fd_vote_state_versioned_enum_current:
485 0 : break;
486 0 : default:
487 0 : FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant ));
488 0 : }
489 0 : }
490 :
491 : /**********************************************************************/
492 : /* impl VoteState */
493 : /**********************************************************************/
494 :
495 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L428
496 : static void
497 : vote_state_new( fd_vote_init_t * vote_init,
498 : fd_sol_sysvar_clock_t const * clock,
499 : uchar * authorized_voters_mem,
500 0 : fd_vote_state_t * vote_state /* out */ ) {
501 0 : vote_state->node_pubkey = vote_init->node_pubkey;
502 0 : vote_state->authorized_voters = *authorized_voters_new( clock->epoch, &vote_init->authorized_voter, authorized_voters_mem );
503 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L431
504 0 : vote_state->authorized_withdrawer = vote_init->authorized_withdrawer;
505 0 : vote_state->commission = vote_init->commission;
506 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L434
507 0 : vote_state->prior_voters.idx = 31;
508 0 : vote_state->prior_voters.is_empty = 1;
509 0 : }
510 :
511 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985
512 : static inline int
513 : verify_authorized_signer( fd_pubkey_t const * authorized,
514 0 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
515 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L989
516 0 : return fd_signers_contains( signers, authorized ) ?
517 0 : FD_EXECUTOR_INSTR_SUCCESS :
518 0 : FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
519 0 : }
520 :
521 : // lambda function: https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L873
522 : static inline int
523 : verify( fd_pubkey_t * epoch_authorized_voter,
524 : int authorized_withdrawer_signer,
525 0 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
526 0 : if( authorized_withdrawer_signer )
527 0 : return 0;
528 0 : else
529 0 : return verify_authorized_signer( epoch_authorized_voter, signers );
530 0 : }
531 :
532 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L845
533 : static void
534 0 : pop_expired_votes( fd_vote_state_t * self, ulong next_vote_slot ) {
535 0 : while( !deq_fd_landed_vote_t_empty( self->votes ) ) {
536 0 : fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( self->votes );
537 0 : if( !( is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) {
538 0 : deq_fd_landed_vote_t_pop_tail( self->votes );
539 0 : } else {
540 0 : break;
541 0 : }
542 0 : }
543 0 : }
544 :
545 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855
546 : static void
547 0 : double_lockouts( fd_vote_state_t * self ) {
548 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856
549 0 : ulong stack_depth = deq_fd_landed_vote_t_cnt( self->votes );
550 0 : ulong i = 0;
551 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857
552 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( self->votes );
553 0 : !deq_fd_landed_vote_t_iter_done( self->votes, iter );
554 0 : iter = deq_fd_landed_vote_t_iter_next( self->votes, iter ) ) {
555 0 : fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( self->votes, iter );
556 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860
557 0 : if( stack_depth >
558 0 : fd_ulong_checked_add_expect(
559 0 : i,
560 0 : (ulong)v->lockout.confirmation_count,
561 0 : "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) )
562 0 : {
563 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864
564 0 : increase_confirmation_count( &v->lockout, 1 );
565 0 : }
566 0 : i++;
567 0 : }
568 0 : }
569 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L668
570 : static inline uchar
571 0 : compute_vote_latency( ulong voted_for_slot, ulong current_slot ) {
572 0 : return (uchar)fd_ulong_min( fd_ulong_sat_sub( current_slot, voted_for_slot ), UCHAR_MAX );
573 0 : }
574 :
575 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L673
576 : static ulong
577 0 : credits_for_vote_at_index( fd_vote_state_t * self, ulong index ) {
578 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L679
579 0 : fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_peek_index( self->votes, index );
580 0 : ulong latency = landed_vote == NULL ? 0 : landed_vote->latency;
581 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L683
582 0 : ulong max_credits = VOTE_CREDITS_MAXIMUM_PER_SLOT;
583 :
584 : // If latency is 0, this means that the Lockout was created and stored from a software version
585 : // that did not store vote latencies; in this case, 1 credit is awarded
586 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L691
587 0 : if( FD_UNLIKELY( latency == 0 ) ) {
588 0 : return 1;
589 0 : }
590 :
591 0 : ulong diff = 0;
592 0 : int cf = fd_ulong_checked_sub( latency, VOTE_CREDITS_GRACE_SLOTS, &diff );
593 0 : if( cf != 0 || diff == 0 ) {
594 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L697
595 0 : return max_credits;
596 0 : }
597 :
598 0 : ulong credits = 0;
599 0 : cf = fd_ulong_checked_sub( max_credits, diff, &credits );
600 0 : if( cf != 0 || credits == 0 ) {
601 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L705
602 0 : return 1;
603 0 : }
604 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L707
605 0 : return credits;
606 0 : }
607 :
608 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L282-L309 */
609 : static void
610 0 : increment_credits( fd_vote_state_t * self, ulong epoch, ulong credits ) {
611 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */
612 0 : if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( self->epoch_credits ) ) ) {
613 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */
614 0 : deq_fd_vote_epoch_credits_t_push_tail_wrap(
615 0 : self->epoch_credits,
616 0 : ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } );
617 0 : } else if( FD_LIKELY( epoch !=
618 0 : deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->epoch ) ) {
619 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */
620 0 : fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits );
621 :
622 0 : ulong credits = last->credits;
623 0 : ulong prev_credits = last->prev_credits;
624 :
625 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */
626 0 : if( FD_LIKELY( credits!=prev_credits ) ) {
627 0 : if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) {
628 : /* Although Agave performs a `.remove(0)` AFTER the call to
629 : `.push()`, there is an edge case where the epoch credits is
630 : full, making the call to `_push_tail()` unsafe. Since Agave's
631 : structures are dynamically allocated, it is safe for them to
632 : simply call `.push()` and then popping afterwards. We have to
633 : reverse the order of operations to maintain correct behavior
634 : and avoid overflowing the deque.
635 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */
636 0 : deq_fd_vote_epoch_credits_t_pop_head( self->epoch_credits );
637 0 : }
638 :
639 : /* This will not fail because we already popped if we're at
640 : capacity, since the epoch_credits deque is allocated with a
641 : minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */
642 0 : deq_fd_vote_epoch_credits_t_push_tail(
643 0 : self->epoch_credits,
644 0 : ( fd_vote_epoch_credits_t ){
645 0 : .epoch = epoch, .credits = credits, .prev_credits = credits } );
646 0 : } else {
647 : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */
648 0 : deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->epoch = epoch;
649 :
650 : /* Here we can perform the same deque size check and pop if
651 : we're beyond the maximum epoch credits len. */
652 0 : if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) {
653 0 : deq_fd_vote_epoch_credits_t_pop_head( self->epoch_credits );
654 0 : }
655 0 : }
656 0 : }
657 :
658 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663
659 0 : deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->credits = fd_ulong_sat_add(
660 0 : deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->credits, credits );
661 0 : }
662 :
663 : static inline ulong *
664 : last_voted_slot( fd_vote_state_t * self );
665 :
666 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L595
667 : static void
668 : process_next_vote_slot( fd_vote_state_t * self,
669 : ulong next_vote_slot,
670 : ulong epoch,
671 0 : ulong current_slot ) {
672 0 : ulong * last_voted_slot_ = last_voted_slot( self );
673 0 : if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return;
674 :
675 0 : pop_expired_votes( self, next_vote_slot );
676 :
677 0 : fd_landed_vote_t landed_vote = { .latency = compute_vote_latency( next_vote_slot, current_slot ),
678 0 : ( fd_vote_lockout_t ){ .slot = next_vote_slot } };
679 :
680 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623
681 0 : if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( self->votes ) == MAX_LOCKOUT_HISTORY ) ) {
682 0 : ulong credits = credits_for_vote_at_index( self, 0 );
683 0 : fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( self->votes );
684 0 : self->has_root_slot = 1;
685 0 : self->root_slot = landed_vote.lockout.slot;
686 :
687 0 : increment_credits( self, epoch, credits );
688 0 : }
689 :
690 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634
691 0 : deq_fd_landed_vote_t_push_tail_wrap( self->votes, landed_vote );
692 0 : double_lockouts( self );
693 0 : }
694 :
695 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828
696 : static int
697 : get_and_update_authorized_voter( fd_vote_state_t * self,
698 : ulong current_epoch,
699 0 : fd_pubkey_t ** pubkey /* out */ ) {
700 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L832
701 0 : fd_vote_authorized_voter_t * authorized_voter =
702 0 : authorized_voters_get_and_cache_authorized_voter_for_epoch( &self->authorized_voters,
703 0 : current_epoch );
704 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L835
705 0 : if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
706 0 : *pubkey = &authorized_voter->pubkey;
707 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L837
708 0 : authorized_voters_purge_authorized_voters( &self->authorized_voters, current_epoch );
709 0 : return FD_EXECUTOR_INSTR_SUCCESS;
710 0 : }
711 :
712 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L768
713 : static int
714 : set_new_authorized_voter( fd_vote_state_t * self,
715 : fd_pubkey_t const * authorized_pubkey,
716 : ulong current_epoch,
717 : ulong target_epoch,
718 : /* "verify" closure */ int authorized_withdrawer_signer,
719 : /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
720 0 : fd_exec_instr_ctx_t const * ctx ) {
721 0 : int rc;
722 0 : fd_pubkey_t * epoch_authorized_voter = NULL;
723 :
724 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L778
725 0 : rc = get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter );
726 0 : if( FD_UNLIKELY( rc ) ) return rc;
727 :
728 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L779
729 0 : rc = verify( epoch_authorized_voter, authorized_withdrawer_signer, signers );
730 0 : if( FD_UNLIKELY( rc ) ) return rc;
731 :
732 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L786
733 0 : if( FD_UNLIKELY( authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) {
734 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE;
735 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
736 0 : }
737 :
738 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L791
739 0 : fd_vote_authorized_voter_t * latest_authorized =
740 0 : authorized_voters_last( &self->authorized_voters );
741 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L794
742 0 : if( FD_UNLIKELY( ( !latest_authorized ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
743 0 : ulong latest_epoch = latest_authorized->epoch;
744 0 : fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey;
745 :
746 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799
747 0 : if( 0 != memcmp( latest_authorized_pubkey, authorized_pubkey, sizeof( fd_pubkey_t ) ) ) {
748 0 : fd_vote_prior_voters_t * prior_voters = &self->prior_voters;
749 :
750 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801
751 0 : ulong epoch_of_last_authorized_switch = 0UL;
752 0 : if( (!prior_voters->is_empty) & (prior_voters->idx < 32) ) {
753 0 : epoch_of_last_authorized_switch = prior_voters->buf[prior_voters->idx].epoch_end;
754 0 : }
755 :
756 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L810
757 0 : if( target_epoch <= latest_epoch )
758 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
759 :
760 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L815
761 0 : prior_voters->idx += 1UL;
762 0 : prior_voters->idx %= 32UL;
763 0 : prior_voters->buf[prior_voters->idx] =
764 0 : ( fd_vote_prior_voter_t ){ .pubkey = *latest_authorized_pubkey,
765 0 : .epoch_start = epoch_of_last_authorized_switch,
766 0 : .epoch_end = target_epoch };
767 0 : prior_voters->is_empty = 0;
768 0 : }
769 :
770 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822
771 0 : if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) {
772 0 : FD_LOG_ERR(( "Authorized_voter pool is empty" ));
773 0 : }
774 :
775 0 : fd_vote_authorized_voter_t * ele =
776 0 : fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool );
777 0 : ele->epoch = target_epoch;
778 0 : ele->pubkey = *authorized_pubkey;
779 0 : ele->prio = (ulong)&ele->pubkey;
780 0 : fd_vote_authorized_voters_treap_ele_insert(
781 0 : self->authorized_voters.treap, ele, self->authorized_voters.pool );
782 :
783 0 : return 0;
784 0 : }
785 :
786 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L869
787 : static int
788 : process_timestamp( fd_vote_state_t * self,
789 : ulong slot,
790 : long timestamp,
791 0 : fd_exec_instr_ctx_t const * ctx ) {
792 0 : if( FD_UNLIKELY(
793 0 : ( slot < self->last_timestamp.slot || timestamp < self->last_timestamp.timestamp ) ||
794 0 : ( slot == self->last_timestamp.slot &&
795 0 : ( slot != self->last_timestamp.slot || timestamp != self->last_timestamp.timestamp ) &&
796 0 : self->last_timestamp.slot != 0 ) ) ) {
797 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD;
798 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
799 0 : }
800 0 : self->last_timestamp.slot = slot;
801 0 : self->last_timestamp.timestamp = timestamp;
802 :
803 0 : return 0;
804 0 : }
805 :
806 : /**********************************************************************/
807 : /* mod vote_state */
808 : /**********************************************************************/
809 :
810 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L166
811 : __attribute__((warn_unused_result)) static int
812 : set_vote_account_state( fd_borrowed_account_t * vote_account,
813 : fd_vote_state_t * vote_state,
814 : fd_exec_instr_ctx_t const * ctx /* feature_set */,
815 0 : uchar * vote_lockout_mem ) {
816 : /* This is a horrible conditional expression in Agave.
817 : The terms were broken up into their own variables. */
818 :
819 0 : ulong vsz = size_of_versioned( 1 );
820 :
821 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L175
822 0 : fd_rent_t const rent = fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache );
823 0 : int resize_needed = fd_borrowed_account_get_data_len( vote_account ) < vsz;
824 0 : int resize_rent_exempt = fd_rent_exempt_minimum_balance( &rent, vsz ) <= fd_borrowed_account_get_lamports( vote_account );
825 :
826 : /* The resize operation itself is part of the horrible conditional,
827 : but behind a short-circuit operator. */
828 0 : int resize_failed = 0;
829 0 : if( resize_needed && resize_rent_exempt ) {
830 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L179
831 0 : resize_failed =
832 0 : fd_borrowed_account_set_data_length( vote_account, vsz ) != FD_EXECUTOR_INSTR_SUCCESS;
833 0 : }
834 :
835 0 : if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) {
836 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L184
837 0 : fd_vote_state_versioned_t v1_14_11;
838 0 : fd_vote_state_versioned_new_disc( &v1_14_11, fd_vote_state_versioned_enum_v1_14_11 );
839 0 : from_vote_state_1_14_11( vote_state, &v1_14_11.inner.v1_14_11, vote_lockout_mem );
840 0 : return set_state( vote_account, &v1_14_11 );
841 0 : }
842 :
843 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L189
844 : // TODO: This is stupid... optimize this...
845 0 : fd_vote_state_versioned_t new_current = { .discriminant = fd_vote_state_versioned_enum_current,
846 0 : .inner = { .current = *vote_state } };
847 0 : return set_state( vote_account, &new_current );
848 0 : }
849 :
850 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L727
851 : static inline fd_vote_lockout_t *
852 0 : last_lockout( fd_vote_state_t * self ) {
853 0 : if( deq_fd_landed_vote_t_empty( self->votes ) ) return NULL;
854 0 : fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( self->votes );
855 0 : return &last_vote->lockout;
856 0 : }
857 :
858 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L731
859 : static inline ulong *
860 0 : last_voted_slot( fd_vote_state_t * self ) {
861 0 : fd_vote_lockout_t * last_lockout_ = last_lockout( self );
862 0 : if( FD_UNLIKELY( !last_lockout_ ) ) return NULL;
863 0 : return &last_lockout_->slot;
864 0 : }
865 :
866 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L573
867 : static uchar
868 0 : contains_slot( fd_vote_state_t * vote_state, ulong slot ) {
869 : /* Logic is copied from slice::binary_search_by() in Rust. While not fully optimized,
870 : it aims to achieve fuzzing conformance for both sorted and unsorted inputs. */
871 0 : ulong size = deq_fd_landed_vote_t_cnt( vote_state->votes );
872 0 : if( FD_UNLIKELY( size==0UL ) ) return 0;
873 :
874 0 : ulong base = 0UL;
875 0 : while( size>1UL ) {
876 0 : ulong half = size / 2UL;
877 0 : ulong mid = base + half;
878 0 : ulong mid_slot = deq_fd_landed_vote_t_peek_index_const( vote_state->votes, mid )->lockout.slot;
879 0 : base = (slot<mid_slot) ? base : mid;
880 0 : size -= half;
881 0 : }
882 :
883 0 : return deq_fd_landed_vote_t_peek_index_const( vote_state->votes, base )->lockout.slot==slot;
884 0 : }
885 :
886 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L201
887 : static int
888 : check_and_filter_proposed_vote_state( fd_vote_state_t * vote_state,
889 : fd_vote_lockout_t * proposed_lockouts,
890 : uchar * proposed_has_root,
891 : ulong * proposed_root,
892 : fd_hash_t const * proposed_hash,
893 : fd_slot_hash_t const * slot_hashes, /* deque */
894 0 : fd_exec_instr_ctx_t const * ctx ) {
895 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L208
896 0 : if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) {
897 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
898 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
899 0 : }
900 0 : fd_landed_vote_t const * last_vote = NULL;
901 0 : if( !deq_fd_landed_vote_t_empty( vote_state->votes ) ) {
902 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L212
903 0 : last_vote = deq_fd_landed_vote_t_peek_tail( vote_state->votes );
904 0 : }
905 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L218
906 0 : if( FD_LIKELY( last_vote ) ) {
907 0 : if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot <=
908 0 : last_vote->lockout.slot ) ) {
909 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
910 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
911 0 : }
912 0 : }
913 :
914 : /* must be nonempty, checked above */
915 0 : ulong last_vote_state_update_slot = deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot;
916 :
917 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L224
918 0 : if( FD_UNLIKELY( deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
919 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
920 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
921 0 : }
922 :
923 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L227
924 0 : ulong earliest_slot_hash_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
925 :
926 : /* Check if the proposed vote is too old to be in the SlotHash history */
927 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L230
928 0 : if( FD_UNLIKELY( last_vote_state_update_slot < earliest_slot_hash_in_history ) ) {
929 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
930 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
931 0 : }
932 :
933 : /* Check if the proposed root is too old */
934 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L237
935 0 : if( *proposed_has_root ) {
936 0 : ulong const proposed_root_ = *proposed_root;
937 : /* If the new proposed root `R` is less than the earliest slot hash in the history
938 : such that we cannot verify whether the slot was actually was on this fork, set
939 : the root to the latest vote in the current vote that's less than R. */
940 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L242
941 0 : if( proposed_root_ < earliest_slot_hash_in_history ) {
942 0 : *proposed_has_root = vote_state->has_root_slot;
943 0 : *proposed_root = vote_state->root_slot;
944 0 : for( deq_fd_landed_vote_t_iter_t iter =
945 0 : deq_fd_landed_vote_t_iter_init_rev( vote_state->votes );
946 0 : !deq_fd_landed_vote_t_iter_done_rev( vote_state->votes, iter );
947 0 : iter = deq_fd_landed_vote_t_iter_prev( vote_state->votes, iter ) ) {
948 : /* Ensure we're iterating from biggest to smallest vote in the
949 : current vote state */
950 0 : fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter );
951 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L248
952 0 : if( vote->lockout.slot <= proposed_root_ ) {
953 0 : *proposed_has_root = 1;
954 0 : *proposed_root = vote->lockout.slot;
955 0 : break;
956 0 : }
957 :
958 0 : }
959 0 : }
960 0 : }
961 :
962 : /* Index into the new proposed vote state's slots, starting with the root if it exists then
963 : we use this mutable root to fold checking the root slot into the below loop for performance */
964 0 : int has_root_to_check = *proposed_has_root;
965 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L259
966 0 : ulong root_to_check = *proposed_root;
967 0 : ulong proposed_lockouts_index = 0UL;
968 0 : ulong lockouts_len = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
969 :
970 : /* Index into the slot_hashes, starting at the oldest known slot hash */
971 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L264
972 0 : ulong slot_hashes_index = deq_fd_slot_hash_t_cnt( slot_hashes );
973 :
974 : /* proposed_lockouts_indexes_to_filter's size is bounded by the length
975 : of the proposed lockouts provided in the instruction data, which is
976 : capped at roughly 10KB / sizeof(ulong). */
977 0 : ulong proposed_lockouts_indexes_to_filter[ FD_INSTR_DATA_MAX/sizeof(ulong) ];
978 0 : ulong filter_index = 0UL;
979 :
980 : /* Note:
981 :
982 : 1) `vote_state_update.lockouts` is sorted from oldest/smallest vote to newest/largest
983 : vote, due to the way votes are applied to the vote state (newest votes
984 : pushed to the back).
985 :
986 : 2) Conversely, `slot_hashes` is sorted from newest/largest vote to
987 : the oldest/smallest vote.
988 :
989 : Unlike for vote updates, vote state updates here can't only check votes older than the last vote
990 : because have to ensure that every slot is actually part of the history, not just the most
991 : recent ones */
992 :
993 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L279
994 0 : while( proposed_lockouts_index < lockouts_len && slot_hashes_index > 0 ) {
995 0 : ulong proposed_vote_slot =
996 0 : fd_ulong_if( has_root_to_check,
997 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L281
998 0 : root_to_check,
999 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L283
1000 0 : deq_fd_vote_lockout_t_peek_index_const( proposed_lockouts,
1001 0 : proposed_lockouts_index )
1002 0 : ->slot );
1003 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L285
1004 0 : if( !has_root_to_check && proposed_lockouts_index > 0UL &&
1005 0 : proposed_vote_slot <=
1006 0 : deq_fd_vote_lockout_t_peek_index_const(
1007 0 : proposed_lockouts,
1008 0 : fd_ulong_checked_sub_expect(
1009 0 : proposed_lockouts_index,
1010 0 : 1,
1011 0 : "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`" ) )
1012 0 : ->slot ) {
1013 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L293
1014 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
1015 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1016 0 : }
1017 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L295
1018 0 : ulong ancestor_slot =
1019 0 : deq_fd_slot_hash_t_peek_index_const(
1020 0 : slot_hashes,
1021 0 : fd_ulong_checked_sub_expect(
1022 0 : slot_hashes_index,
1023 0 : 1UL,
1024 0 : "`slot_hashes_index` is positive when computing `ancestor_slot`" ) )
1025 0 : ->slot;
1026 : /* Find if this slot in the proposed vote state exists in the SlotHashes history
1027 : to confirm if it was a valid ancestor on this fork */
1028 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L303
1029 0 : if( proposed_vote_slot < ancestor_slot ) {
1030 0 : if( slot_hashes_index == deq_fd_slot_hash_t_cnt( slot_hashes ) ) {
1031 : /* The vote slot does not exist in the SlotHashes history because it's too old,
1032 : i.e. older than the oldest slot in the history. */
1033 0 : if( proposed_vote_slot >= earliest_slot_hash_in_history ) {
1034 0 : ctx->txn_out->err.custom_err = 0;
1035 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1036 0 : }
1037 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L310
1038 0 : if( !contains_slot( vote_state, proposed_vote_slot ) && !has_root_to_check ) {
1039 : /* If the vote slot is both:
1040 : 1) Too old
1041 : 2) Doesn't already exist in vote state
1042 : Then filter it out */
1043 0 : proposed_lockouts_indexes_to_filter[filter_index++] = proposed_lockouts_index; }
1044 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L318
1045 0 : if( has_root_to_check ) {
1046 0 : ulong new_proposed_root = root_to_check;
1047 : /* 1. Because `root_to_check.is_some()`, then we know that
1048 : we haven't checked the root yet in this loop, so
1049 : `proposed_vote_slot` == `new_proposed_root` == `vote_state_update.root` */
1050 0 : FD_TEST( new_proposed_root == proposed_vote_slot );
1051 : /* 2. We know from the assert earlier in the function that
1052 : `proposed_vote_slot < earliest_slot_hash_in_history`,
1053 : so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history` */
1054 0 : if( new_proposed_root >= earliest_slot_hash_in_history ) {
1055 0 : ctx->txn_out->err.custom_err = 0;
1056 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1057 0 : }
1058 :
1059 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L329
1060 0 : has_root_to_check = 0;
1061 0 : root_to_check = ULONG_MAX;
1062 0 : } else {
1063 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L331
1064 0 : proposed_lockouts_index = fd_ulong_checked_add_expect(
1065 0 : proposed_lockouts_index,
1066 0 : 1,
1067 0 : "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when "
1068 0 : "`proposed_vote_slot` is too old to be in SlotHashes history" );
1069 0 : }
1070 0 : continue;
1071 0 : } else {
1072 : /* If the vote slot is new enough to be in the slot history,
1073 : but is not part of the slot history, then it must belong to another fork,
1074 : which means this vote state update is invalid. */
1075 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L340
1076 0 : if( has_root_to_check ) {
1077 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ON_DIFFERENT_FORK;
1078 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1079 0 : } else {
1080 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
1081 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1082 0 : }
1083 0 : }
1084 0 : } else if( proposed_vote_slot > ancestor_slot ) {
1085 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L347
1086 :
1087 : /* Decrement `slot_hashes_index` to find newer slots in the SlotHashes history */
1088 0 : slot_hashes_index = fd_ulong_checked_sub_expect(
1089 0 : slot_hashes_index,
1090 0 : 1,
1091 0 : "`slot_hashes_index` is positive when finding newer slots in SlotHashes history" );
1092 0 : continue;
1093 0 : } else {
1094 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L354
1095 :
1096 : /* Once the slot in `vote_state_update.lockouts` is found, bump to the next slot
1097 : in `vote_state_update.lockouts` and continue. If we were checking the root,
1098 : start checking the vote state instead. */
1099 0 : if( has_root_to_check ) {
1100 0 : has_root_to_check = 0;
1101 0 : root_to_check = ULONG_MAX;
1102 0 : } else {
1103 0 : proposed_lockouts_index = fd_ulong_checked_add_expect(
1104 0 : proposed_lockouts_index,
1105 0 : 1,
1106 0 : "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` "
1107 0 : "when match is found in SlotHashes history" );
1108 0 : slot_hashes_index = fd_ulong_checked_sub_expect(
1109 0 : slot_hashes_index,
1110 0 : 1,
1111 0 : "`slot_hashes_index` is positive when match is found in SlotHashes history" );
1112 0 : }
1113 0 : }
1114 0 : }
1115 :
1116 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L372
1117 0 : if( proposed_lockouts_index != deq_fd_vote_lockout_t_cnt( proposed_lockouts ) ) {
1118 : /* The last vote slot in the update did not exist in SlotHashes */
1119 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
1120 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1121 0 : }
1122 :
1123 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L401
1124 0 : if( memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, slot_hashes_index )->hash,
1125 0 : proposed_hash,
1126 0 : sizeof( fd_hash_t ) ) != 0 ) {
1127 : /* This means the newest vote in the slot has a match that
1128 : doesn't match the expected hash for that slot on this fork */
1129 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
1130 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1131 0 : }
1132 :
1133 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L418
1134 : /* Filter out the irrelevant votes */
1135 0 : proposed_lockouts_index = 0UL;
1136 0 : ulong filter_votes_index = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
1137 :
1138 : /* We need to iterate backwards here because proposed_lockouts_indexes_to_filter[ i ] is a
1139 : strictly increasing value. Forward iterating can lead to the proposed lockout indicies to get
1140 : shifted leading to popping the wrong proposed lockouts or out of bounds accessing. We need
1141 : to be sure of handling underflow in this case. */
1142 :
1143 0 : for( ulong i=filter_index; i>0UL && filter_votes_index>0UL; i-- ) {
1144 0 : proposed_lockouts_index = i - 1UL;
1145 0 : if( FD_UNLIKELY( proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ]>=filter_votes_index ) ) {
1146 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
1147 0 : }
1148 :
1149 0 : deq_fd_vote_lockout_t_pop_idx_tail( proposed_lockouts, proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ] );
1150 0 : filter_votes_index--;
1151 0 : }
1152 :
1153 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1154 0 : }
1155 :
1156 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L440
1157 : static int
1158 : check_slots_are_valid( fd_vote_state_t * vote_state,
1159 : ulong const * vote_slots,
1160 : fd_hash_t const * vote_hash,
1161 : fd_slot_hash_t const * slot_hashes, /* deque */
1162 0 : fd_exec_instr_ctx_t const * ctx ) {
1163 0 : ulong i = 0;
1164 0 : ulong j = deq_fd_slot_hash_t_cnt( slot_hashes );
1165 0 : ulong vote_slots_len = deq_ulong_cnt( vote_slots );
1166 :
1167 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L462
1168 0 : while( i < vote_slots_len && j > 0 ) {
1169 0 : ulong * last_voted_slot_ = last_voted_slot( vote_state );
1170 0 : if( FD_UNLIKELY( last_voted_slot_ &&
1171 0 : *deq_ulong_peek_index_const( vote_slots, i ) <= *last_voted_slot_ ) ) {
1172 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L469
1173 0 : i = fd_ulong_checked_add_expect(
1174 0 : i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots" );
1175 0 : continue;
1176 0 : }
1177 :
1178 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L476
1179 0 : if( FD_UNLIKELY(
1180 0 : *deq_ulong_peek_index_const( vote_slots, i ) !=
1181 0 : deq_fd_slot_hash_t_peek_index_const( slot_hashes,
1182 0 : fd_ulong_checked_sub_expect( j, 1, "`j` is positive" ) )
1183 0 : ->slot ) ) {
1184 0 : j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when finding newer slots" );
1185 0 : continue;
1186 0 : }
1187 :
1188 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L486
1189 0 : i = fd_ulong_checked_add_expect(
1190 0 : i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found" );
1191 0 : j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when hash is found" );
1192 0 : }
1193 :
1194 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L494
1195 0 : if( FD_UNLIKELY( j == deq_fd_slot_hash_t_cnt( slot_hashes ) ) ) {
1196 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
1197 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1198 0 : }
1199 0 : if( FD_UNLIKELY( i != vote_slots_len ) ) {
1200 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
1201 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1202 0 : }
1203 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L514
1204 0 : if( FD_UNLIKELY( 0 != memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, j )->hash,
1205 0 : vote_hash,
1206 0 : 32UL ) ) ) {
1207 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
1208 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1209 0 : }
1210 0 : return 0;
1211 0 : }
1212 :
1213 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L565
1214 : static int
1215 : process_new_vote_state( fd_vote_state_t * vote_state,
1216 : fd_landed_vote_t * new_state,
1217 : int has_new_root,
1218 : ulong new_root,
1219 : int has_timestamp,
1220 : long timestamp,
1221 : ulong epoch,
1222 : ulong current_slot,
1223 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1224 0 : int rc;
1225 :
1226 0 : FD_TEST( !deq_fd_landed_vote_t_empty( new_state ) );
1227 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L575
1228 0 : if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( new_state ) > MAX_LOCKOUT_HISTORY ) ) {
1229 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_MANY_VOTES;
1230 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1231 0 : };
1232 :
1233 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L579
1234 0 : if( FD_UNLIKELY( has_new_root && vote_state->has_root_slot ) ) {
1235 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L581
1236 0 : if( FD_UNLIKELY( new_root < vote_state->root_slot ) ) {
1237 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
1238 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1239 0 : }
1240 0 : } else if( FD_UNLIKELY( !has_new_root && vote_state->has_root_slot ) ) {
1241 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L586
1242 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
1243 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1244 0 : } else {
1245 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L588
1246 : /* no-op */
1247 0 : }
1248 :
1249 0 : fd_landed_vote_t * previous_vote = NULL;
1250 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
1251 0 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
1252 0 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
1253 0 : fd_landed_vote_t * vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
1254 0 : if( FD_LIKELY( vote->lockout.confirmation_count == 0 ) ) {
1255 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ZERO_CONFIRMATIONS;
1256 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1257 0 : } else if( FD_UNLIKELY( vote->lockout.confirmation_count > MAX_LOCKOUT_HISTORY ) ) {
1258 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_TOO_LARGE;
1259 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1260 0 : } else if( FD_LIKELY( has_new_root ) ) {
1261 0 : if( FD_UNLIKELY( vote->lockout.slot <= new_root && new_root != SLOT_DEFAULT ) ) {
1262 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOT_SMALLER_THAN_ROOT;
1263 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1264 0 : }
1265 0 : }
1266 :
1267 0 : if( FD_LIKELY( previous_vote ) ) {
1268 0 : if( FD_UNLIKELY( previous_vote->lockout.slot >= vote->lockout.slot ) ) {
1269 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
1270 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1271 0 : } else if( FD_UNLIKELY( previous_vote->lockout.confirmation_count <=
1272 0 : vote->lockout.confirmation_count ) ) {
1273 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATIONS_NOT_ORDERED;
1274 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1275 0 : } else if( FD_UNLIKELY( vote->lockout.slot >
1276 0 : last_locked_out_slot( &previous_vote->lockout ) ) ) {
1277 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_NEW_VOTE_STATE_LOCKOUT_MISMATCH;
1278 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1279 0 : }
1280 0 : }
1281 0 : previous_vote = vote;
1282 0 : }
1283 :
1284 0 : ulong current_vote_state_index = 0;
1285 0 : ulong new_vote_state_index = 0;
1286 :
1287 : /* Accumulate credits earned by newly rooted slots. The behavior changes with
1288 : timely_vote_credits: prior to this feature, there was a bug that counted a new root slot as 1
1289 : credit even if it had never been voted on. timely_vote_credits fixes this bug by only awarding
1290 : credits for slots actually voted on and finalized. */
1291 :
1292 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L635
1293 :
1294 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L641
1295 0 : ulong earned_credits = 0;
1296 :
1297 0 : if( FD_LIKELY( has_new_root ) ) {
1298 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes );
1299 0 : !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter );
1300 0 : iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) {
1301 0 : fd_landed_vote_t * current_vote = deq_fd_landed_vote_t_iter_ele( vote_state->votes, iter );
1302 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L647
1303 0 : if( FD_UNLIKELY( current_vote->lockout.slot <= new_root ) ) {
1304 : // this is safe because we're inside if has_new_root
1305 0 : earned_credits = fd_ulong_checked_add_expect(
1306 0 : credits_for_vote_at_index( vote_state,
1307 0 : current_vote_state_index ),
1308 0 : earned_credits,
1309 0 : "`earned_credits` does not overflow" );
1310 0 : current_vote_state_index = fd_ulong_checked_add_expect(
1311 0 : current_vote_state_index,
1312 0 : 1,
1313 0 : "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
1314 0 : "when processing new root" );
1315 0 : continue;
1316 0 : }
1317 0 : break;
1318 0 : }
1319 0 : }
1320 :
1321 : // For any slots newly added to the new vote state, the vote latency of that slot is not provided by the
1322 : // vote instruction contents, but instead is computed from the actual latency of the vote
1323 : // instruction. This prevents other validators from manipulating their own vote latencies within their vote states
1324 : // and forcing the rest of the cluster to accept these possibly fraudulent latency values. If the
1325 : // timly_vote_credits feature is not enabled then vote latency is set to 0 for new votes.
1326 : //
1327 : // For any slot that is in both the new state and the current state, the vote latency of the new state is taken
1328 : // from the current state.
1329 : //
1330 : // Thus vote latencies are set here for any newly vote-on slots when a vote instruction is received.
1331 : // They are copied into the new vote state after every vote for already voted-on slots.
1332 : // And when voted-on slots are rooted, the vote latencies stored in the vote state of all the rooted slots is used
1333 : // to compute credits earned.
1334 : // All validators compute the same vote latencies because all process the same vote instruction at the
1335 : // same slot, and the only time vote latencies are ever computed is at the time that their slot is first voted on;
1336 : // after that, the latencies are retained unaltered until the slot is rooted.
1337 :
1338 : // All the votes in our current vote state that are missing from the new vote state
1339 : // must have been expired by later votes. Check that the lockouts match this assumption.
1340 :
1341 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L686
1342 0 : while( current_vote_state_index < deq_fd_landed_vote_t_cnt( vote_state->votes ) &&
1343 0 : new_vote_state_index < deq_fd_landed_vote_t_cnt( new_state ) ) {
1344 0 : fd_landed_vote_t * current_vote =
1345 0 : deq_fd_landed_vote_t_peek_index( vote_state->votes, current_vote_state_index );
1346 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L690
1347 0 : fd_landed_vote_t * new_vote =
1348 0 : deq_fd_landed_vote_t_peek_index( new_state, new_vote_state_index );
1349 :
1350 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L696
1351 0 : if( FD_LIKELY( current_vote->lockout.slot < new_vote->lockout.slot ) ) {
1352 : /* The agave implementation of calculating the last locked out
1353 : slot does not calculate a min between the current vote's
1354 : confirmation count and max lockout history. The reason we do
1355 : this is to make sure that the fuzzers continue working:
1356 : the max lockout history can not be > MAX_LOCKOUT_HISTORY. */
1357 0 : ulong confirmation_count = fd_ulong_min( current_vote->lockout.confirmation_count, MAX_LOCKOUT_HISTORY );
1358 0 : ulong last_locked_out_slot = fd_ulong_sat_add( current_vote->lockout.slot,
1359 0 : (ulong)pow( INITIAL_LOCKOUT, (double)confirmation_count ) );
1360 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L697
1361 0 : if( last_locked_out_slot >= new_vote->lockout.slot ) {
1362 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L698
1363 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_LOCKOUT_CONFLICT;
1364 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1365 0 : }
1366 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L700
1367 0 : current_vote_state_index =
1368 0 : fd_ulong_checked_add_expect( current_vote_state_index,
1369 0 : 1,
1370 0 : "`current_vote_state_index` is bounded by "
1371 0 : "`MAX_LOCKOUT_HISTORY` when slot is less than proposed" );
1372 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L704
1373 0 : } else if( FD_UNLIKELY( current_vote->lockout.slot == new_vote->lockout.slot ) ) {
1374 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L707
1375 0 : if( new_vote->lockout.confirmation_count < current_vote->lockout.confirmation_count ) {
1376 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_ROLL_BACK;
1377 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1378 0 : }
1379 :
1380 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L712
1381 0 : new_vote->latency =
1382 0 : deq_fd_landed_vote_t_peek_index( vote_state->votes, current_vote_state_index )->latency;
1383 :
1384 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L714
1385 0 : current_vote_state_index =
1386 0 : fd_ulong_checked_add_expect( current_vote_state_index,
1387 0 : 1,
1388 0 : "`current_vote_state_index` is bounded by "
1389 0 : "`MAX_LOCKOUT_HISTORY` when slot is equal to proposed" );
1390 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L717
1391 0 : new_vote_state_index =
1392 0 : fd_ulong_checked_add_expect( new_vote_state_index,
1393 0 : 1,
1394 0 : "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
1395 0 : "when slot is equal to proposed" );
1396 0 : } else {
1397 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L722
1398 0 : new_vote_state_index =
1399 0 : fd_ulong_checked_add_expect( new_vote_state_index,
1400 0 : 1,
1401 0 : "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
1402 0 : "when slot is greater than proposed" );
1403 0 : }
1404 0 : }
1405 :
1406 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L737
1407 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
1408 0 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
1409 0 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
1410 0 : fd_landed_vote_t * new_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
1411 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L738
1412 0 : if( FD_UNLIKELY( new_vote->latency == 0 ) ) {
1413 : // this is unlikely because as validators upgrade, it should converge to the new vote state
1414 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L739
1415 0 : new_vote->latency = compute_vote_latency( new_vote->lockout.slot, current_slot );
1416 0 : }
1417 0 : }
1418 :
1419 : // doesn't matter what the value of slot if `is_some = 0` i.e. `Option::None`
1420 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L744
1421 0 : int both_none = !vote_state->has_root_slot && !has_new_root;
1422 0 : if( ( !both_none && ( vote_state->has_root_slot != has_new_root ||
1423 0 : vote_state->root_slot != new_root ) ) ) {
1424 0 : increment_credits( vote_state, epoch, earned_credits );
1425 0 : }
1426 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750
1427 0 : if( FD_LIKELY( has_timestamp ) ) {
1428 : /* new_state asserted nonempty at function beginning */
1429 0 : if( deq_fd_landed_vote_t_empty( new_state ) ) {
1430 0 : FD_LOG_ERR(( "solana panic" ));
1431 : // TODO: solana panics ... unclear what to return
1432 0 : ctx->txn_out->err.custom_err = 0;
1433 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1434 0 : }
1435 0 : ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot;
1436 0 : rc = process_timestamp( vote_state, last_slot, timestamp, ctx );
1437 0 : if( FD_UNLIKELY( rc ) ) { return rc; }
1438 0 : vote_state->last_timestamp.timestamp = timestamp;
1439 0 : }
1440 :
1441 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L754
1442 0 : vote_state->has_root_slot = (uchar)has_new_root;
1443 0 : vote_state->root_slot = new_root;
1444 :
1445 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L755
1446 0 : deq_fd_landed_vote_t_remove_all( vote_state->votes );
1447 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
1448 0 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
1449 0 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
1450 0 : fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
1451 0 : deq_fd_landed_vote_t_push_tail_wrap( vote_state->votes, *landed_vote );
1452 0 : }
1453 :
1454 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1455 0 : }
1456 :
1457 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L849
1458 : static int
1459 : authorize( fd_borrowed_account_t * vote_account,
1460 : fd_pubkey_t const * authorized,
1461 : fd_vote_authorize_t vote_authorize,
1462 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1463 : fd_sol_sysvar_clock_t const * clock,
1464 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1465 0 : int rc = 0;
1466 :
1467 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L857
1468 :
1469 0 : rc = get_state( vote_account->acct, ctx->runtime->vote_program.authorize.vote_state_mem );
1470 0 : if( FD_UNLIKELY( rc ) ) return rc;
1471 0 : fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.authorize.vote_state_mem;
1472 :
1473 0 : convert_to_current( vote_state_versioned,
1474 0 : ctx->runtime->vote_program.authorize.authorized_voters_mem,
1475 0 : ctx->runtime->vote_program.authorize.landed_votes_mem );
1476 0 : fd_vote_state_t * vote_state = &vote_state_versioned->inner.current;
1477 :
1478 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L861
1479 0 : switch( vote_authorize.discriminant ) {
1480 :
1481 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L862
1482 0 : case fd_vote_authorize_enum_voter:;
1483 :
1484 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L863
1485 0 : int authorized_withdrawer_signer =
1486 0 : FD_EXECUTOR_INSTR_SUCCESS ==
1487 0 : verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
1488 :
1489 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L869-L872
1490 0 : ulong target_epoch;
1491 0 : rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch );
1492 0 : if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1493 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1494 0 : }
1495 :
1496 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L866
1497 0 : rc = set_new_authorized_voter( vote_state,
1498 0 : authorized,
1499 0 : clock->epoch,
1500 0 : target_epoch,
1501 0 : authorized_withdrawer_signer,
1502 0 : signers,
1503 0 : ctx );
1504 0 : if( FD_UNLIKELY( rc ) ) return rc;
1505 0 : break;
1506 :
1507 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L883
1508 0 : case fd_vote_authorize_enum_withdrawer:
1509 0 : rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
1510 0 : if( FD_UNLIKELY( rc ) ) return rc;
1511 0 : vote_state->authorized_withdrawer = *authorized;
1512 0 : break;
1513 :
1514 : // failing exhaustive check is fatal
1515 0 : default:
1516 0 : __builtin_unreachable();
1517 0 : }
1518 :
1519 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L890
1520 0 : return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.authorize.vote_lockout_mem );
1521 0 : }
1522 :
1523 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L894
1524 : static int
1525 : update_validator_identity( fd_borrowed_account_t * vote_account,
1526 : fd_pubkey_t const * node_pubkey,
1527 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1528 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1529 0 : int rc = 0;
1530 :
1531 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L900
1532 0 : rc = get_state( vote_account->acct, ctx->runtime->vote_program.update_validator_identity.vote_state_mem );
1533 0 : if( FD_UNLIKELY( rc ) ) return rc;
1534 0 : fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_validator_identity.vote_state_mem;
1535 0 : convert_to_current( vote_state_versioned,
1536 0 : ctx->runtime->vote_program.update_validator_identity.authorized_voters_mem,
1537 0 : ctx->runtime->vote_program.update_validator_identity.landed_votes_mem );
1538 0 : fd_vote_state_t * vote_state = &vote_state_versioned->inner.current;
1539 :
1540 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L905
1541 0 : rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
1542 0 : if( FD_UNLIKELY( rc ) ) return rc;
1543 :
1544 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L908
1545 0 : rc = verify_authorized_signer( node_pubkey, signers );
1546 0 : if( FD_UNLIKELY( rc ) ) return rc;
1547 :
1548 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L910
1549 0 : vote_state->node_pubkey = *node_pubkey;
1550 :
1551 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L912
1552 0 : return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.update_validator_identity.vote_lockout_mem );
1553 0 : }
1554 :
1555 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L971
1556 : static int
1557 0 : is_commission_update_allowed( ulong slot, fd_epoch_schedule_t const * epoch_schedule ) {
1558 0 : if( FD_LIKELY( epoch_schedule->slots_per_epoch > 0UL ) ) {
1559 0 : ulong relative_slot = fd_ulong_sat_sub( slot, epoch_schedule->first_normal_slot );
1560 : // TODO underflow and overflow edge cases in addition to div by 0
1561 0 : relative_slot %= epoch_schedule->slots_per_epoch;
1562 0 : return fd_ulong_sat_mul( relative_slot, 2 ) <= epoch_schedule->slots_per_epoch;
1563 0 : } else {
1564 0 : return 1;
1565 0 : }
1566 0 : }
1567 :
1568 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L916
1569 : static int
1570 : update_commission( fd_borrowed_account_t * vote_account,
1571 : uchar commission,
1572 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1573 : fd_epoch_schedule_t const * epoch_schedule,
1574 : fd_sol_sysvar_clock_t const * clock,
1575 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1576 0 : int rc = 0;
1577 :
1578 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L925
1579 0 : fd_vote_state_versioned_t * vote_state_versioned = NULL;
1580 0 : fd_vote_state_t * vote_state = NULL;
1581 :
1582 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L927
1583 0 : int enforce_commission_update_rule = 1;
1584 0 : rc = get_state( vote_account->acct, ctx->runtime->vote_program.update_commission.vote_state_mem );
1585 0 : if( FD_LIKELY( rc==FD_EXECUTOR_INSTR_SUCCESS ) ) {
1586 0 : vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem;
1587 0 : convert_to_current( vote_state_versioned,
1588 0 : ctx->runtime->vote_program.update_commission.authorized_voters_mem,
1589 0 : ctx->runtime->vote_program.update_commission.landed_votes_mem );
1590 0 : vote_state = &vote_state_versioned->inner.current;
1591 0 : enforce_commission_update_rule = commission > vote_state->commission;
1592 0 : }
1593 :
1594 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L940
1595 0 : if( FD_LIKELY( enforce_commission_update_rule ) ) {
1596 0 : if( FD_UNLIKELY( !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) {
1597 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE;
1598 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1599 0 : }
1600 0 : }
1601 :
1602 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L949
1603 0 : if( !vote_state ) {
1604 0 : rc = get_state( vote_account->acct, ctx->runtime->vote_program.update_commission.vote_state_mem );
1605 0 : if( FD_UNLIKELY( rc ) ) return rc;
1606 0 : vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem;
1607 0 : convert_to_current( vote_state_versioned,
1608 0 : ctx->runtime->vote_program.update_commission.authorized_voters_mem,
1609 0 : ctx->runtime->vote_program.update_commission.landed_votes_mem );
1610 0 : vote_state = &vote_state_versioned->inner.current;
1611 0 : }
1612 :
1613 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L957
1614 0 : rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
1615 0 : if( FD_UNLIKELY( rc ) ) return rc;
1616 :
1617 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L959
1618 0 : vote_state->commission = commission;
1619 :
1620 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L961
1621 0 : return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.update_commission.vote_lockout_mem );
1622 0 : }
1623 :
1624 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L997
1625 : static int
1626 : withdraw( fd_exec_instr_ctx_t const * ctx,
1627 : fd_borrowed_account_t * vote_account,
1628 : ulong lamports,
1629 : ushort to_account_index,
1630 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1631 : fd_rent_t const * rent_sysvar,
1632 0 : fd_sol_sysvar_clock_t const * clock ) {
1633 0 : int rc = 0;
1634 :
1635 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1010
1636 0 : rc = get_state( vote_account->acct, ctx->runtime->vote_program.withdraw.vote_state_mem );
1637 0 : if( FD_UNLIKELY( rc ) ) return rc;
1638 0 : fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.withdraw.vote_state_mem;
1639 :
1640 0 : convert_to_current( vote_state_versioned,
1641 0 : ctx->runtime->vote_program.withdraw.authorized_voters_mem,
1642 0 : ctx->runtime->vote_program.withdraw.landed_votes_mem );
1643 0 : fd_vote_state_t * vote_state = &vote_state_versioned->inner.current;
1644 :
1645 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1014
1646 0 : rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
1647 0 : if( FD_UNLIKELY( rc ) ) return rc;
1648 :
1649 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1016
1650 0 : if( FD_UNLIKELY( lamports > fd_borrowed_account_get_lamports( vote_account ) ) )
1651 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1652 0 : ulong remaining_balance = fd_borrowed_account_get_lamports( vote_account ) - lamports;
1653 :
1654 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1021
1655 0 : if( FD_UNLIKELY( remaining_balance == 0 ) ) {
1656 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1014
1657 0 : int reject_active_vote_account_close = 0;
1658 :
1659 0 : ulong last_epoch_with_credits;
1660 0 : if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( vote_state->epoch_credits ) ) ) {
1661 0 : last_epoch_with_credits =
1662 0 : deq_fd_vote_epoch_credits_t_peek_tail_const( vote_state->epoch_credits )->epoch;
1663 0 : ulong current_epoch = clock->epoch;
1664 0 : reject_active_vote_account_close =
1665 0 : fd_ulong_sat_sub( current_epoch, last_epoch_with_credits ) < 2;
1666 0 : }
1667 :
1668 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1034
1669 0 : if( FD_UNLIKELY( reject_active_vote_account_close ) ) {
1670 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1036
1671 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ACTIVE_VOTE_ACCOUNT_CLOSE;
1672 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1673 0 : } else {
1674 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1040
1675 0 : fd_vote_state_versioned_t vote_state_versions;
1676 0 : fd_vote_state_versioned_new_disc( &vote_state_versions,
1677 0 : fd_vote_state_versioned_enum_current );
1678 0 : vote_state_versions.inner.current.prior_voters.idx = 31;
1679 0 : vote_state_versions.inner.current.prior_voters.is_empty = 1;
1680 0 : fd_vote_state_t * default_vote_state = &vote_state_versions.inner.current;
1681 0 : rc = 0;
1682 0 : rc = set_vote_account_state( vote_account, default_vote_state, ctx, ctx->runtime->vote_program.withdraw.vote_lockout_mem );
1683 0 : if( FD_UNLIKELY( rc != 0 ) ) return rc;
1684 0 : }
1685 0 : } else {
1686 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1043
1687 0 : ulong min_rent_exempt_balance =
1688 0 : fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) );
1689 0 : if( remaining_balance < min_rent_exempt_balance ) {
1690 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1691 0 : }
1692 0 : }
1693 :
1694 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1049
1695 0 : rc = fd_borrowed_account_checked_sub_lamports( vote_account, lamports );
1696 0 : if( FD_UNLIKELY( rc ) ) return rc;
1697 :
1698 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_state/mod.rs#L1019 */
1699 0 : fd_borrowed_account_drop( vote_account );
1700 :
1701 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_state/mod.rs#L1020-L1021 */
1702 0 : fd_guarded_borrowed_account_t to = {0};
1703 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_account_index, &to );
1704 :
1705 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1053
1706 0 : rc = fd_borrowed_account_checked_add_lamports( &to, lamports );
1707 0 : if( FD_UNLIKELY( rc ) ) return rc;
1708 :
1709 0 : return 0;
1710 0 : }
1711 :
1712 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L760
1713 : static int
1714 : process_vote_unfiltered( fd_vote_state_t * vote_state,
1715 : ulong * vote_slots,
1716 : fd_vote_t const * vote,
1717 : fd_slot_hash_t const * slot_hashes, /* deque */
1718 : ulong epoch,
1719 : ulong current_slot,
1720 0 : fd_exec_instr_ctx_t const * ctx ) {
1721 0 : int rc;
1722 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L770
1723 0 : rc = check_slots_are_valid( vote_state, vote_slots, &vote->hash, slot_hashes, ctx );
1724 0 : if( FD_UNLIKELY( rc ) ) return rc;
1725 0 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote_slots );
1726 0 : !deq_ulong_iter_done( vote_slots, iter );
1727 0 : iter = deq_ulong_iter_next( vote_slots, iter ) ) {
1728 0 : ulong * ele = deq_ulong_iter_ele( vote_slots, iter );
1729 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L772
1730 0 : process_next_vote_slot( vote_state, *ele, epoch, current_slot );
1731 0 : }
1732 0 : return 0;
1733 0 : }
1734 :
1735 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L783
1736 : static int
1737 : process_vote( fd_vote_state_t * vote_state,
1738 : fd_vote_t const * vote,
1739 : fd_slot_hash_t const * slot_hashes, /* deque */
1740 : ulong epoch,
1741 : ulong current_slot,
1742 0 : fd_exec_instr_ctx_t const * ctx ) {
1743 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L792
1744 0 : if( FD_UNLIKELY( deq_ulong_empty( vote->slots ) ) ) {
1745 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1746 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1747 0 : }
1748 :
1749 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L795
1750 0 : ulong earliest_slot_in_history = 0;
1751 0 : if( FD_UNLIKELY( !deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
1752 0 : earliest_slot_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
1753 0 : }
1754 :
1755 : /* We know that the size of the vote_slots is bounded by the number of
1756 : slots that can fit inside of an instruction. A very loose bound is
1757 : assuming that the entire transaction is just filled with a vote
1758 : slot deque (1232 bytes per transaction/8 bytes per slot) == 154
1759 : slots. The footprint of a deque is as follows:
1760 : fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(DEQUE_T) ) + sizeof(DEQUE_T)*max, alignof(DEQUE_(private_t)) );
1761 : So, the footprint in our case is:
1762 : fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(ulong) ) + sizeof(ulong)*154, alignof(DEQUE_(private_t)) );
1763 : Which is equal to
1764 : fd_ulong_align_up( 32UL + 154 * 8UL, 8UL ) = 1264UL; */
1765 0 : #define VOTE_SLOTS_MAX (FD_TXN_MTU/sizeof(ulong))
1766 0 : #define VOTE_SLOTS_DEQUE_FOOTPRINT (1264UL )
1767 0 : #define VOTE_SLOTS_DEQUE_ALIGN (8UL)
1768 0 : FD_TEST( deq_ulong_footprint( VOTE_SLOTS_MAX ) == VOTE_SLOTS_DEQUE_FOOTPRINT );
1769 0 : FD_TEST( deq_ulong_align() == 8UL );
1770 0 : FD_TEST( deq_ulong_cnt( vote->slots ) <= VOTE_SLOTS_MAX );
1771 0 : uchar * vote_slots_mem[ VOTE_SLOTS_DEQUE_FOOTPRINT ] __attribute__((aligned(VOTE_SLOTS_DEQUE_ALIGN)));
1772 :
1773 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L796
1774 0 : ulong * vote_slots = deq_ulong_join( deq_ulong_new( vote_slots_mem, deq_ulong_cnt( vote->slots ) ) );
1775 0 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
1776 0 : !deq_ulong_iter_done( vote->slots, iter );
1777 0 : iter = deq_ulong_iter_next( vote->slots, iter ) ) {
1778 0 : ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
1779 0 : if( FD_UNLIKELY( *ele >= earliest_slot_in_history ) ) {
1780 0 : vote_slots = deq_ulong_push_tail_wrap( vote_slots, *ele );
1781 0 : }
1782 0 : }
1783 :
1784 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L802
1785 0 : if( FD_UNLIKELY( deq_ulong_cnt( vote_slots ) == 0 ) ) {
1786 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTES_TOO_OLD_ALL_FILTERED;
1787 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1788 0 : }
1789 :
1790 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L805
1791 0 : return process_vote_unfiltered(
1792 0 : vote_state, vote_slots, vote, slot_hashes, epoch, current_slot, ctx );
1793 0 : }
1794 :
1795 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1060
1796 : static int
1797 : initialize_account( fd_borrowed_account_t * vote_account,
1798 : fd_vote_init_t * vote_init,
1799 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1800 : fd_sol_sysvar_clock_t const * clock,
1801 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1802 0 : int rc;
1803 :
1804 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1067
1805 0 : ulong data_len = fd_borrowed_account_get_data_len( vote_account );
1806 0 : if( FD_UNLIKELY( data_len != size_of_versioned( 1 ) ) ) {
1807 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1808 0 : }
1809 :
1810 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074
1811 0 : rc = get_state( vote_account->acct, ctx->runtime->vote_program.init_account.vote_state_mem );
1812 0 : if( FD_UNLIKELY( rc ) ) return rc;
1813 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.init_account.vote_state_mem;
1814 :
1815 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1076
1816 0 : if( FD_UNLIKELY( !is_uninitialized( versioned ) ) ) {
1817 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1818 0 : }
1819 :
1820 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1081
1821 0 : rc = verify_authorized_signer( &vote_init->node_pubkey, signers );
1822 0 : if( FD_UNLIKELY( rc ) ) {
1823 0 : return rc;
1824 0 : }
1825 :
1826 : /*
1827 : * N.B. Technically we should destroy() to release memory before
1828 : * newing, otherwise the pointers are wiped and memory is leaked.
1829 : * We are probably fine for now since we are bump allocating
1830 : * everything and the enclosing frame will free everything when
1831 : * popped.
1832 : */
1833 : // reset the object
1834 0 : fd_vote_state_versioned_new( versioned );
1835 :
1836 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1083
1837 0 : vote_state_new( vote_init,
1838 0 : clock,
1839 0 : ctx->runtime->vote_program.init_account.authorized_voters_mem,
1840 0 : &versioned->inner.current );
1841 0 : return set_vote_account_state( vote_account, &versioned->inner.current, ctx, ctx->runtime->vote_program.init_account.vote_lockout_mem );
1842 0 : }
1843 :
1844 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1086
1845 : static int
1846 : verify_and_get_vote_state( fd_borrowed_account_t * vote_account,
1847 : fd_sol_sysvar_clock_t const * clock,
1848 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX],
1849 : fd_vote_state_t * vote_state /* out */,
1850 : uchar * vote_state_mem,
1851 : uchar * authorized_voters_mem,
1852 0 : uchar * landed_votes_mem ) {
1853 0 : int rc = 0;
1854 :
1855 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1091
1856 0 : rc = get_state( vote_account->acct, vote_state_mem );
1857 0 : if( FD_UNLIKELY( rc ) ) return rc;
1858 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
1859 :
1860 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1093
1861 0 : if( FD_UNLIKELY( is_uninitialized( versioned ) ) )
1862 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
1863 :
1864 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1097
1865 0 : convert_to_current( versioned, authorized_voters_mem, landed_votes_mem );
1866 0 : *vote_state = versioned->inner.current;
1867 :
1868 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1098
1869 0 : fd_pubkey_t * authorized_voter = NULL;
1870 0 : rc = get_and_update_authorized_voter( vote_state, clock->epoch, &authorized_voter );
1871 0 : if( FD_UNLIKELY( rc ) ) return rc;
1872 :
1873 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1099
1874 0 : rc = verify_authorized_signer( authorized_voter, signers );
1875 0 : if( FD_UNLIKELY( rc ) ) return rc;
1876 :
1877 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1878 0 : }
1879 :
1880 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1104
1881 : static int
1882 : process_vote_with_account( fd_borrowed_account_t * vote_account,
1883 : fd_slot_hash_t const * slot_hashes, /* deque */
1884 : fd_sol_sysvar_clock_t const * clock,
1885 : fd_vote_t * vote,
1886 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1887 0 : fd_exec_instr_ctx_t const * ctx ) {
1888 :
1889 0 : int rc;
1890 0 : fd_vote_state_t vote_state;
1891 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1112
1892 0 : rc = verify_and_get_vote_state( vote_account,
1893 0 : clock,
1894 0 : signers,
1895 0 : &vote_state,
1896 0 : ctx->runtime->vote_program.process_vote.vote_state_mem,
1897 0 : ctx->runtime->vote_program.process_vote.authorized_voters_mem,
1898 0 : ctx->runtime->vote_program.process_vote.landed_votes_mem );
1899 0 : if( FD_UNLIKELY( rc ) ) return rc;
1900 :
1901 :
1902 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1117
1903 0 : rc = process_vote( &vote_state, vote, slot_hashes, clock->epoch, clock->slot, ctx );
1904 0 : if( FD_UNLIKELY( rc ) ) return rc;
1905 :
1906 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1126
1907 0 : if( FD_LIKELY( vote->has_timestamp ) ) {
1908 0 : if( FD_UNLIKELY( deq_ulong_cnt( vote->slots ) == 0 ) ) {
1909 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1910 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1911 0 : }
1912 :
1913 0 : ulong max = deq_ulong_peek_head( vote->slots ) ? *deq_ulong_peek_head( vote->slots ) : 0UL;
1914 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1127
1915 0 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
1916 0 : !deq_ulong_iter_done( vote->slots, iter );
1917 0 : iter = deq_ulong_iter_next( vote->slots, iter ) ) {
1918 0 : ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
1919 0 : max = fd_ulong_max( max, *ele );
1920 0 : }
1921 0 : if( FD_UNLIKELY( !max ) ) {
1922 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1923 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1924 0 : }
1925 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1131
1926 0 : rc = process_timestamp( &vote_state, max, vote->timestamp, ctx );
1927 0 : if( FD_UNLIKELY( rc ) ) return rc;
1928 0 : }
1929 :
1930 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1133
1931 0 : return set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem );
1932 0 : }
1933 :
1934 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1156
1935 : static int
1936 : do_process_vote_state_update( fd_vote_state_t * vote_state,
1937 : fd_slot_hash_t const * slot_hashes, /* deque */
1938 : ulong epoch,
1939 : ulong slot,
1940 : fd_vote_state_update_t * vote_state_update,
1941 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1942 0 : int rc;
1943 :
1944 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1164
1945 0 : rc = check_and_filter_proposed_vote_state(
1946 0 : vote_state,
1947 0 : vote_state_update->lockouts, &vote_state_update->has_root, &vote_state_update->root, &vote_state_update->hash,
1948 0 : slot_hashes, ctx );
1949 0 : if( FD_UNLIKELY( rc ) ) return rc;
1950 :
1951 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1177
1952 0 : fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( ctx->runtime->vote_program.process_vote.vs_update_landed_votes_mem, deq_fd_vote_lockout_t_cnt( vote_state_update->lockouts ) ) );
1953 0 : for( deq_fd_vote_lockout_t_iter_t iter =
1954 0 : deq_fd_vote_lockout_t_iter_init( vote_state_update->lockouts );
1955 0 : !deq_fd_vote_lockout_t_iter_done( vote_state_update->lockouts, iter );
1956 0 : iter = deq_fd_vote_lockout_t_iter_next( vote_state_update->lockouts, iter ) ) {
1957 0 : fd_vote_lockout_t * lockout =
1958 0 : deq_fd_vote_lockout_t_iter_ele( vote_state_update->lockouts, iter );
1959 0 : deq_fd_landed_vote_t_push_tail_wrap( landed_votes,
1960 0 : ( fd_landed_vote_t ){ .latency = 0, .lockout = *lockout } );
1961 0 : }
1962 :
1963 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1171
1964 0 : return process_new_vote_state( vote_state,
1965 0 : landed_votes,
1966 0 : vote_state_update->has_root,
1967 0 : vote_state_update->root,
1968 0 : vote_state_update->has_timestamp,
1969 0 : vote_state_update->timestamp,
1970 0 : epoch,
1971 0 : slot,
1972 0 : ctx );
1973 0 : }
1974 :
1975 : static int
1976 : process_vote_state_update( fd_borrowed_account_t * vote_account,
1977 : fd_slot_hash_t const * slot_hashes,
1978 : fd_sol_sysvar_clock_t const * clock,
1979 : fd_vote_state_update_t * vote_state_update,
1980 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1981 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
1982 0 : fd_vote_state_t vote_state;
1983 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1144
1984 0 : int rc = verify_and_get_vote_state( vote_account,
1985 0 : clock,
1986 0 : signers,
1987 0 : &vote_state,
1988 0 : ctx->runtime->vote_program.process_vote.vote_state_mem,
1989 0 : ctx->runtime->vote_program.process_vote.authorized_voters_mem,
1990 0 : ctx->runtime->vote_program.process_vote.landed_votes_mem );
1991 0 : if( FD_UNLIKELY( rc ) ) return rc;
1992 :
1993 :
1994 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1145
1995 0 : rc = do_process_vote_state_update(
1996 0 : &vote_state, slot_hashes, clock->epoch, clock->slot, vote_state_update, ctx );
1997 0 : if( FD_UNLIKELY( rc ) ) {
1998 0 : return rc;
1999 0 : }
2000 :
2001 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1153
2002 0 : rc = set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem );
2003 :
2004 0 : return rc;
2005 0 : }
2006 :
2007 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1206
2008 : static int
2009 : do_process_tower_sync( fd_vote_state_t * vote_state,
2010 : fd_slot_hash_t const * slot_hashes, /* deque */
2011 : ulong epoch,
2012 : ulong slot,
2013 : fd_tower_sync_t * tower_sync,
2014 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
2015 :
2016 0 : do {
2017 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1214
2018 0 : int err = check_and_filter_proposed_vote_state(
2019 0 : vote_state,
2020 0 : tower_sync->lockouts, &tower_sync->has_root, &tower_sync->root, &tower_sync->hash,
2021 0 : slot_hashes, ctx );
2022 0 : if( FD_UNLIKELY( err ) ) return err;
2023 0 : } while(0);
2024 :
2025 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1221
2026 0 : return process_new_vote_state(
2027 0 : vote_state,
2028 0 : landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ),
2029 0 : tower_sync->has_root,
2030 0 : tower_sync->root,
2031 0 : tower_sync->has_timestamp,
2032 0 : tower_sync->timestamp,
2033 0 : epoch,
2034 0 : slot,
2035 0 : ctx );
2036 0 : }
2037 :
2038 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1186
2039 : static int
2040 : process_tower_sync( fd_borrowed_account_t * vote_account,
2041 : fd_slot_hash_t const * slot_hashes, /* deque */
2042 : fd_sol_sysvar_clock_t const * clock,
2043 : fd_tower_sync_t * tower_sync,
2044 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
2045 0 : fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
2046 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1194
2047 0 : fd_vote_state_t vote_state;
2048 0 : do {
2049 0 : int err = verify_and_get_vote_state( vote_account,
2050 0 : clock,
2051 0 : signers,
2052 0 : &vote_state,
2053 0 : ctx->runtime->vote_program.tower_sync.vote_state_mem,
2054 0 : ctx->runtime->vote_program.tower_sync.authorized_voters_mem,
2055 0 : ctx->runtime->vote_program.tower_sync.vote_state_landed_votes_mem );
2056 0 : if( FD_UNLIKELY( err ) ) return err;
2057 0 : } while(0);
2058 :
2059 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1195
2060 0 : do {
2061 0 : int err = do_process_tower_sync( &vote_state, slot_hashes, clock->epoch, clock->slot, tower_sync, ctx );
2062 0 : if( FD_UNLIKELY( err ) ) return err;
2063 0 : } while(0);
2064 :
2065 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1203
2066 0 : return set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem );
2067 0 : }
2068 :
2069 : /**********************************************************************/
2070 : /* FD-only encoders / decoders (doesn't map directly to Labs impl) */
2071 : /**********************************************************************/
2072 :
2073 : int
2074 : fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update,
2075 : fd_vote_state_update_t * vote_update,
2076 0 : fd_exec_instr_ctx_t const * ctx ) {
2077 : // Taken from:
2078 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L954
2079 0 : if( compact_update->root != ULONG_MAX ) {
2080 0 : vote_update->has_root = 1;
2081 0 : vote_update->root = compact_update->root;
2082 0 : } else {
2083 0 : vote_update->has_root = 0;
2084 0 : vote_update->root = ULONG_MAX;
2085 0 : }
2086 :
2087 0 : ulong lockouts_len = compact_update->lockouts_len;
2088 0 : ulong lockouts_max = fd_ulong_max( lockouts_len, MAX_LOCKOUT_HISTORY );
2089 :
2090 0 : vote_update->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( ctx->runtime->vote_program.process_vote.compact_vs_lockout_mem, lockouts_max ) );
2091 0 : ulong slot = fd_ulong_if( vote_update->has_root, vote_update->root, 0 );
2092 :
2093 0 : for( ulong i=0; i < lockouts_len; ++i ) {
2094 0 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( vote_update->lockouts );
2095 0 : fd_vote_lockout_new( elem );
2096 :
2097 0 : fd_lockout_offset_t * lock_offset = &compact_update->lockouts[i];
2098 :
2099 0 : ulong next_slot;
2100 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow( slot, lock_offset->offset, &next_slot ) ) )
2101 0 : return 0;
2102 :
2103 0 : elem->slot = slot = next_slot;
2104 0 : elem->confirmation_count = (uint)lock_offset->confirmation_count;
2105 0 : }
2106 :
2107 0 : vote_update->hash = compact_update->hash;
2108 0 : vote_update->has_timestamp = compact_update->has_timestamp;
2109 0 : vote_update->timestamp = compact_update->timestamp;
2110 :
2111 0 : return 1;
2112 0 : }
2113 :
2114 : /**********************************************************************/
2115 : /* mod vote_processor */
2116 : /**********************************************************************/
2117 :
2118 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21
2119 : static int
2120 : process_authorize_with_seed_instruction(
2121 : /* invoke_context */
2122 : fd_exec_instr_ctx_t const * ctx,
2123 : /* transaction_context */
2124 : fd_borrowed_account_t * vote_account,
2125 : fd_pubkey_t const * new_authority,
2126 : fd_vote_authorize_t authorization_type,
2127 : fd_pubkey_t const * current_authority_derived_key_owner,
2128 : uchar const * current_authority_derived_key_seed,
2129 0 : ulong current_authority_derived_key_seed_len ) {
2130 0 : int rc = 0;
2131 :
2132 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31
2133 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2134 0 : if( FD_UNLIKELY( rc ) ) return rc;
2135 :
2136 0 : fd_sol_sysvar_clock_t clock_;
2137 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2138 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2139 :
2140 0 : fd_pubkey_t * expected_authority_keys[FD_TXN_SIG_MAX] = { 0 };
2141 0 : fd_pubkey_t single_signer = { 0 };
2142 :
2143 0 : if( ctx->instr->acct_cnt < 3 )
2144 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2145 :
2146 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L33
2147 0 : if( fd_instr_acc_is_signer_idx( ctx->instr, 2, &rc ) ) {
2148 :
2149 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L34
2150 0 : fd_pubkey_t const * base_pubkey = NULL;
2151 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &base_pubkey );
2152 0 : if( FD_UNLIKELY( rc ) ) return rc;
2153 :
2154 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L37
2155 0 : expected_authority_keys[0] = &single_signer;
2156 0 : rc = fd_pubkey_create_with_seed( ctx,
2157 0 : base_pubkey->uc,
2158 0 : (char const *)current_authority_derived_key_seed,
2159 0 : current_authority_derived_key_seed_len,
2160 0 : current_authority_derived_key_owner->uc,
2161 0 : /* insert */ expected_authority_keys[0]->uc );
2162 0 : if( FD_UNLIKELY( rc ) ) return rc;
2163 0 : }
2164 :
2165 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L43
2166 0 : return authorize( vote_account,
2167 0 : new_authority,
2168 0 : authorization_type,
2169 0 : (fd_pubkey_t const **)expected_authority_keys,
2170 0 : clock,
2171 0 : ctx );
2172 0 : }
2173 :
2174 : /**********************************************************************/
2175 : /* Entry point for the Vote Program */
2176 : /**********************************************************************/
2177 :
2178 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
2179 : int
2180 0 : fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) {
2181 : /* FD-specific init */
2182 0 : int rc = FD_EXECUTOR_INSTR_SUCCESS;
2183 :
2184 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
2185 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
2186 :
2187 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L64
2188 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) ) {
2189 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2190 0 : }
2191 :
2192 0 : fd_guarded_borrowed_account_t me = {0};
2193 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me );
2194 :
2195 0 : switch( rc ) {
2196 0 : case FD_ACC_MGR_SUCCESS:
2197 0 : break;
2198 0 : case FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT:
2199 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L637
2200 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2201 0 : default:
2202 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L639
2203 0 : return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED;
2204 0 : }
2205 :
2206 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L65
2207 0 : if( FD_UNLIKELY( 0 != memcmp( fd_borrowed_account_get_owner( &me ),
2208 0 : fd_solana_vote_program_id.key,
2209 0 : sizeof( fd_pubkey_t ) ) ) ) {
2210 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L66
2211 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
2212 0 : }
2213 :
2214 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L69
2215 0 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { 0 };
2216 0 : fd_exec_instr_ctx_get_signers( ctx, signers );
2217 :
2218 0 : uchar __attribute__((aligned(alignof(fd_vote_instruction_t)))) vote_instruction_mem[ FD_VOTE_INSTRUCTION_FOOTPRINT ];
2219 0 : fd_vote_instruction_t * instruction = fd_bincode_decode_static_limited_deserialize(
2220 0 : vote_instruction,
2221 0 : vote_instruction_mem,
2222 0 : ctx->instr->data,
2223 0 : ctx->instr->data_sz,
2224 0 : FD_TXN_MTU,
2225 0 : NULL );
2226 0 : if( FD_UNLIKELY( !instruction ) ) {
2227 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2228 0 : }
2229 :
2230 : /* PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR LABS IMPL:
2231 : */
2232 0 : switch( instruction->discriminant ) {
2233 :
2234 : /* InitializeAccount
2235 : *
2236 : * Instruction:
2237 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L32
2238 : *
2239 : * Processor:
2240 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L71
2241 : */
2242 0 : case fd_vote_instruction_enum_initialize_account: {
2243 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L72
2244 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
2245 0 : if( FD_UNLIKELY( rc ) ) return rc;
2246 0 : fd_rent_t rent_;
2247 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2248 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2249 :
2250 0 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &me ) <
2251 0 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &me ) ) ) )
2252 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
2253 :
2254 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L76
2255 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2256 0 : if( FD_UNLIKELY( rc ) ) return rc;
2257 0 : fd_sol_sysvar_clock_t clock_;
2258 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2259 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2260 :
2261 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L78
2262 0 : rc = initialize_account( &me,
2263 0 : &instruction->inner.initialize_account,
2264 0 : signers,
2265 0 : clock,
2266 0 : ctx );
2267 :
2268 0 : break;
2269 0 : }
2270 :
2271 : /* Authorize
2272 : *
2273 : * Instruction:
2274 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L40
2275 : *
2276 : * Processor:
2277 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L86
2278 : *
2279 : * Notes:
2280 : * - Up to two signers: the vote authority and the authorized withdrawer.
2281 : */
2282 0 : case fd_vote_instruction_enum_authorize: {
2283 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L87
2284 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2285 0 : if( FD_UNLIKELY( rc ) ) return rc;
2286 0 : fd_sol_sysvar_clock_t clock_;
2287 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2288 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2289 :
2290 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L89
2291 0 : fd_pubkey_t const * voter_pubkey = &instruction->inner.authorize.pubkey;
2292 0 : fd_vote_authorize_t vote_authorize = instruction->inner.authorize.vote_authorize;
2293 :
2294 0 : rc = authorize( &me, voter_pubkey, vote_authorize, signers, clock, ctx );
2295 :
2296 0 : break;
2297 0 : }
2298 :
2299 : /* AuthorizeWithSeed
2300 : *
2301 : * Instruction:
2302 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L117
2303 : *
2304 : * Processor:
2305 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L98
2306 : */
2307 0 : case fd_vote_instruction_enum_authorize_with_seed: {
2308 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L99
2309 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 3 ) ) {
2310 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2311 0 : break;
2312 0 : }
2313 :
2314 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L100
2315 0 : fd_vote_authorize_with_seed_args_t * args = &instruction->inner.authorize_with_seed;
2316 :
2317 0 : rc = process_authorize_with_seed_instruction( ctx,
2318 0 : &me,
2319 0 : &args->new_authority,
2320 0 : args->authorization_type,
2321 0 : &args->current_authority_derived_key_owner,
2322 0 : args->current_authority_derived_key_seed,
2323 0 : args->current_authority_derived_key_seed_len );
2324 :
2325 0 : break;
2326 0 : }
2327 :
2328 : /* AuthorizeCheckedWithSeed
2329 : *
2330 : * Instruction:
2331 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L131
2332 : *
2333 : * Processor:
2334 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L111
2335 : */
2336 0 : case fd_vote_instruction_enum_authorize_checked_with_seed: {
2337 0 : fd_vote_authorize_checked_with_seed_args_t const * args =
2338 0 : &instruction->inner.authorize_checked_with_seed;
2339 :
2340 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L112
2341 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
2342 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2343 0 : break;
2344 0 : }
2345 :
2346 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L99-L100
2347 0 : fd_pubkey_t const * new_authority = NULL;
2348 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &new_authority );
2349 0 : if( FD_UNLIKELY( rc ) ) return rc;
2350 :
2351 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L116
2352 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
2353 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
2354 0 : if( FD_UNLIKELY( !!rc ) ) break;
2355 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L117
2356 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2357 0 : break;
2358 0 : }
2359 :
2360 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L119
2361 0 : rc = process_authorize_with_seed_instruction( ctx,
2362 0 : &me,
2363 0 : new_authority,
2364 0 : args->authorization_type,
2365 0 : &args->current_authority_derived_key_owner,
2366 0 : args->current_authority_derived_key_seed,
2367 0 : args->current_authority_derived_key_seed_len );
2368 :
2369 0 : break;
2370 0 : }
2371 :
2372 : /* UpdateValidatorIdentity
2373 : *
2374 : * Instruction:
2375 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L65
2376 : *
2377 : * Processor:
2378 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L130
2379 : */
2380 0 : case fd_vote_instruction_enum_update_validator_identity: {
2381 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L131
2382 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
2383 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2384 0 : break;
2385 0 : }
2386 :
2387 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L118-L120
2388 0 : fd_pubkey_t const * node_pubkey = NULL;
2389 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1UL, &node_pubkey );
2390 0 : if( FD_UNLIKELY( rc ) ) return rc;
2391 :
2392 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L135
2393 0 : rc = update_validator_identity( &me, node_pubkey, signers, ctx );
2394 :
2395 0 : break;
2396 0 : }
2397 :
2398 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L142
2399 0 : case fd_vote_instruction_enum_update_commission: {
2400 :
2401 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L149
2402 0 : fd_epoch_schedule_t epoch_schedule_;
2403 0 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, &epoch_schedule_ );
2404 0 : if( FD_UNLIKELY( !epoch_schedule ) ) {
2405 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2406 0 : }
2407 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L150
2408 :
2409 0 : fd_sol_sysvar_clock_t clock_;
2410 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2411 0 : if( FD_UNLIKELY( !clock ) ) {
2412 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2413 0 : }
2414 :
2415 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L145
2416 0 : rc = update_commission( &me,
2417 0 : instruction->inner.update_commission,
2418 0 : signers,
2419 0 : epoch_schedule,
2420 0 : clock,
2421 0 : ctx );
2422 :
2423 0 : break;
2424 0 : }
2425 :
2426 : /* Vote
2427 : *
2428 : * Instruction:
2429 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L49
2430 : */
2431 0 : case fd_vote_instruction_enum_vote:;
2432 : /* clang-format off */
2433 0 : __attribute__((fallthrough));
2434 : /* clang-format on */
2435 :
2436 : /* VoteSwitch
2437 : *
2438 : * Instruction:
2439 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L81
2440 : *
2441 : * Processor:
2442 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L154
2443 : */
2444 0 : case fd_vote_instruction_enum_vote_switch: {
2445 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
2446 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2447 0 : }
2448 :
2449 0 : fd_vote_t * vote;
2450 0 : if( instruction->discriminant == fd_vote_instruction_enum_vote ) {
2451 0 : vote = &instruction->inner.vote;
2452 0 : } else if( instruction->discriminant == fd_vote_instruction_enum_vote_switch ) {
2453 0 : vote = &instruction->inner.vote_switch.vote;
2454 0 : } else {
2455 0 : __builtin_unreachable();
2456 0 : }
2457 :
2458 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L155
2459 0 : int err;
2460 0 : err = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_slot_hashes_id );
2461 0 : if( FD_UNLIKELY( err ) ) return err;
2462 :
2463 0 : if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2464 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2465 0 : }
2466 :
2467 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L157
2468 0 : err = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2469 0 : if( FD_UNLIKELY( err ) ) return err;
2470 0 : fd_sol_sysvar_clock_t clock_;
2471 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2472 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2473 :
2474 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
2475 0 : rc = process_vote_with_account( &me, slot_hashes, clock, vote, signers, ctx );
2476 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
2477 :
2478 0 : break;
2479 0 : }
2480 :
2481 : /* UpdateVoteState
2482 : *
2483 : * Instruction:
2484 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L100
2485 : */
2486 0 : case fd_vote_instruction_enum_update_vote_state:;
2487 : /* clang-format off */
2488 0 : __attribute__((fallthrough));
2489 : /* clang-format on */
2490 :
2491 : /* UpdateVoteStateSwitch
2492 : *
2493 : * Instruction:
2494 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L107
2495 : *
2496 : * Processor:
2497 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L169
2498 : */
2499 0 : case fd_vote_instruction_enum_update_vote_state_switch: {
2500 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
2501 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2502 0 : }
2503 :
2504 0 : fd_vote_state_update_t * vote_state_update;
2505 0 : switch( instruction->discriminant ) {
2506 0 : case fd_vote_instruction_enum_update_vote_state:
2507 0 : vote_state_update = &instruction->inner.update_vote_state;
2508 0 : break;
2509 0 : case fd_vote_instruction_enum_update_vote_state_switch:
2510 0 : vote_state_update = &instruction->inner.update_vote_state_switch.vote_state_update;
2511 0 : break;
2512 0 : default:
2513 0 : __builtin_unreachable();
2514 0 : }
2515 :
2516 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L171
2517 0 : if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2518 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2519 0 : }
2520 :
2521 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L172
2522 0 : fd_sol_sysvar_clock_t clock_;
2523 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2524 0 : if( FD_UNLIKELY( !clock ) )
2525 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2526 :
2527 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L173
2528 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
2529 0 : rc = process_vote_state_update( &me, slot_hashes, clock, vote_state_update, signers, ctx );
2530 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
2531 :
2532 0 : break;
2533 0 : }
2534 :
2535 : /* CompactUpdateVoteState
2536 : *
2537 : * Instruction:
2538 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L139
2539 : *
2540 : * Notes:
2541 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2542 : * - Feature gated, but live on mainnet.
2543 : */
2544 0 : case fd_vote_instruction_enum_compact_update_vote_state:;
2545 0 : __attribute__((fallthrough));
2546 :
2547 : /* CompactUpdateVoteStateSwitch
2548 : *
2549 : * Instruction:
2550 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L146
2551 : *
2552 : * Processor:
2553 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L183
2554 : *
2555 : * Notes:
2556 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2557 : * - Feature gated, but live on mainnet.
2558 : */
2559 0 : case fd_vote_instruction_enum_compact_update_vote_state_switch: {
2560 : /* https://github.com/anza-xyz/agave/blob/dc4b9dcbbf859ff48f40d00db824bde063fdafcc/programs/vote/src/vote_processor.rs#L183-L191 */
2561 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
2562 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2563 0 : }
2564 :
2565 0 : fd_compact_vote_state_update_t * vote_state_update = NULL;
2566 0 : if( instruction->discriminant == fd_vote_instruction_enum_compact_update_vote_state ) {
2567 0 : vote_state_update = &instruction->inner.compact_update_vote_state;
2568 0 : } else if( instruction->discriminant ==
2569 0 : fd_vote_instruction_enum_compact_update_vote_state_switch ) {
2570 0 : vote_state_update =
2571 0 : &instruction->inner.compact_update_vote_state_switch.compact_vote_state_update;
2572 0 : }
2573 :
2574 0 : fd_vote_state_update_t vote_update;
2575 0 : fd_vote_state_update_new( &vote_update );
2576 0 : if( FD_UNLIKELY( !fd_vote_decode_compact_update( vote_state_update, &vote_update, ctx ) ) )
2577 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2578 :
2579 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L185
2580 0 : if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2581 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2582 0 : }
2583 :
2584 0 : fd_sol_sysvar_clock_t clock_;
2585 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2586 0 : if( FD_UNLIKELY( !clock ) )
2587 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2588 :
2589 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L187
2590 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
2591 0 : rc = process_vote_state_update( &me, slot_hashes, clock, &vote_update, signers, ctx );
2592 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
2593 :
2594 0 : break;
2595 0 : }
2596 :
2597 : /* TowerSync(Switch)
2598 : *
2599 : * Instruction:
2600 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L151-L157
2601 : *
2602 : * Processor:
2603 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L196-L215
2604 : */
2605 :
2606 0 : case fd_vote_instruction_enum_tower_sync:
2607 0 : case fd_vote_instruction_enum_tower_sync_switch: {
2608 0 : fd_tower_sync_t * tower_sync = (instruction->discriminant == fd_vote_instruction_enum_tower_sync)
2609 0 : ? &instruction->inner.tower_sync
2610 0 : : &instruction->inner.tower_sync_switch.tower_sync;
2611 :
2612 0 : if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2613 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2614 0 : }
2615 :
2616 0 : fd_sol_sysvar_clock_t clock_;
2617 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2618 0 : if( FD_UNLIKELY( !clock ) ) {
2619 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2620 0 : }
2621 :
2622 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
2623 0 : FD_TEST( slot_hashes );
2624 0 : rc = process_tower_sync( &me, slot_hashes, clock, tower_sync, signers, ctx );
2625 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
2626 :
2627 0 : break;
2628 0 : }
2629 :
2630 : /* Withdraw
2631 : *
2632 : * Instruction:
2633 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L57
2634 : *
2635 : * Processor:
2636 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L216
2637 : */
2638 0 : case fd_vote_instruction_enum_withdraw: {
2639 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
2640 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2641 0 : break;
2642 0 : }
2643 0 : fd_rent_t rent_;
2644 0 : fd_rent_t const * rent_sysvar = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2645 0 : if( FD_UNLIKELY( !rent_sysvar ) )
2646 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2647 0 : fd_sol_sysvar_clock_t clock_;
2648 0 : fd_sol_sysvar_clock_t const * clock_sysvar = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2649 0 : if( FD_UNLIKELY( !clock_sysvar ) )
2650 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2651 :
2652 0 : rc = withdraw( ctx,
2653 0 : &me,
2654 0 : instruction->inner.withdraw,
2655 0 : 1UL,
2656 0 : signers,
2657 0 : rent_sysvar,
2658 0 : clock_sysvar );
2659 :
2660 0 : break;
2661 0 : }
2662 :
2663 : /* AuthorizeChecked
2664 : *
2665 : * Instruction:
2666 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L93
2667 : *
2668 : * Processor:
2669 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L234
2670 : *
2671 : * Notes:
2672 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2673 : * - Feature gated, but live on mainnet.
2674 : */
2675 0 : case fd_vote_instruction_enum_authorize_checked: {
2676 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
2677 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2678 0 : break;
2679 0 : }
2680 :
2681 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L243-L245
2682 0 : fd_pubkey_t const * voter_pubkey = NULL;
2683 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &voter_pubkey );
2684 0 : if( FD_UNLIKELY( rc ) ) return rc;
2685 :
2686 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L239
2687 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
2688 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
2689 0 : if( FD_UNLIKELY( !!rc ) ) break;
2690 :
2691 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2692 0 : break;
2693 0 : }
2694 :
2695 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L242
2696 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2697 0 : if( FD_UNLIKELY( rc ) ) return rc;
2698 0 : fd_sol_sysvar_clock_t clock_;
2699 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2700 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2701 :
2702 0 : rc = authorize( &me,
2703 0 : voter_pubkey,
2704 0 : instruction->inner.authorize_checked,
2705 0 : signers,
2706 0 : clock,
2707 0 : ctx );
2708 0 : break;
2709 0 : }
2710 :
2711 0 : default:
2712 0 : FD_LOG_ERR(( "unsupported vote instruction: %u", instruction->discriminant ));
2713 0 : }
2714 :
2715 0 : return rc;
2716 0 : }
2717 :
2718 : /**********************************************************************/
2719 : /* Public API */
2720 : /**********************************************************************/
2721 :
2722 : uint
2723 0 : fd_vote_state_versions_is_correct_and_initialized( fd_txn_account_t * vote_account ) {
2724 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L885
2725 0 : uint data_len_check = fd_txn_account_get_data_len( vote_account )==FD_VOTE_STATE_V3_SZ;
2726 0 : uchar test_data[DEFAULT_PRIOR_VOTERS_OFFSET] = {0};
2727 0 : uint data_check = memcmp((
2728 0 : fd_txn_account_get_data( vote_account )+VERSION_OFFSET), test_data, DEFAULT_PRIOR_VOTERS_OFFSET)!=0;
2729 0 : if (data_check && data_len_check) {
2730 0 : return 1;
2731 0 : }
2732 :
2733 : // VoteState1_14_11::is_correct_size_and_initialized
2734 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L58
2735 0 : data_len_check = fd_txn_account_get_data_len( vote_account )==FD_VOTE_STATE_V2_SZ;
2736 0 : uchar test_data_1_14_11[DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11] = {0};
2737 0 : data_check = memcmp( (
2738 0 : fd_txn_account_get_data( vote_account )+VERSION_OFFSET), test_data_1_14_11, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11)!=0;
2739 0 : return data_check && data_len_check;
2740 0 : }
2741 :
2742 : fd_vote_state_versioned_t *
2743 : fd_vote_get_state( fd_txn_account_t const * self,
2744 0 : uchar * mem /* out */ ) {
2745 :
2746 0 : int err = get_state( self, mem );
2747 0 : return err ? NULL : (fd_vote_state_versioned_t *)mem;
2748 0 : }
2749 :
2750 : void
2751 : fd_vote_convert_to_current( fd_vote_state_versioned_t * self,
2752 : uchar * authorized_voters_mem,
2753 0 : uchar * landed_votes_mem ) {
2754 0 : convert_to_current( self, authorized_voters_mem, landed_votes_mem );
2755 0 : }
2756 :
2757 : void
2758 : fd_vote_store_account( fd_txn_account_t * vote_account,
2759 0 : fd_bank_t * bank ) {
2760 :
2761 0 : fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
2762 :
2763 0 : if( fd_txn_account_get_lamports( vote_account )==0UL ) {
2764 0 : fd_vote_states_remove( vote_states, vote_account->pubkey );
2765 0 : fd_bank_vote_states_end_locking_modify( bank );
2766 0 : return;
2767 0 : }
2768 :
2769 0 : if( !fd_vote_state_versions_is_correct_and_initialized( vote_account ) ) {
2770 0 : fd_vote_states_remove( vote_states, vote_account->pubkey );
2771 0 : fd_bank_vote_states_end_locking_modify( bank );
2772 0 : return;
2773 0 : }
2774 :
2775 0 : fd_vote_states_update_from_account( vote_states,
2776 0 : vote_account->pubkey,
2777 0 : fd_txn_account_get_data( vote_account ),
2778 0 : fd_txn_account_get_data_len( vote_account ) );
2779 0 : fd_bank_vote_states_end_locking_modify( bank );
2780 0 : }
|