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 : #include "vote/fd_authorized_voters.h"
10 : #include "vote/fd_vote_common.h"
11 : #include "vote/fd_vote_lockout.h"
12 : #include "vote/fd_vote_state_versioned.h"
13 : #include "vote/fd_vote_state_v3.h"
14 : #include "vote/fd_vote_state_v4.h"
15 :
16 : #include <limits.h>
17 : #include <math.h>
18 : #include <stdio.h>
19 : #include <string.h>
20 :
21 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
22 0 : #define INITIAL_LOCKOUT 2UL
23 :
24 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L51
25 : #define VOTE_CREDITS_MAXIMUM_PER_SLOT_OLD 8
26 :
27 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
28 : #define SLOT_DEFAULT 0UL
29 :
30 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
31 : #define SLOT_MAX ULONG_MAX
32 :
33 : #define ACCOUNTS_MAX 4 /* Vote instructions take in at most 4 accounts */
34 :
35 : #define DEFAULT_COMPUTE_UNITS 2100UL
36 :
37 : /**********************************************************************/
38 : /* VoteStateHandler */
39 : /**********************************************************************/
40 :
41 : /* This is a temporary method in Agave (until the vote state v4 feature
42 : is cleaned up) to check the vote state and, in some cases, check
43 : if the vote account is uninitialized or not. Initializes a v3 or v4
44 : vote account depending on the target version.
45 : https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L45-L77 */
46 : static int
47 : get_vote_state_handler_checked( fd_borrowed_account_t const * vote_account,
48 : int target_version,
49 : uchar check_initialized,
50 : uchar * vote_state_mem,
51 : uchar * authorized_voters_mem,
52 0 : uchar * landed_votes_mem ) {
53 0 : int rc;
54 0 : switch( target_version ) {
55 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L50-L62 */
56 0 : case VOTE_STATE_TARGET_VERSION_V3: {
57 0 : rc = fd_vote_state_v3_deserialize( vote_account, vote_state_mem, authorized_voters_mem, landed_votes_mem );
58 0 : if( FD_UNLIKELY( rc ) ) return rc;
59 :
60 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
61 0 : if( FD_UNLIKELY( check_initialized && fd_vsv_is_uninitialized( versioned ) ) ) {
62 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
63 0 : }
64 :
65 0 : return FD_EXECUTOR_INSTR_SUCCESS;
66 0 : }
67 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L63-L75 */
68 0 : case VOTE_STATE_TARGET_VERSION_V4: {
69 0 : rc = fd_vsv_deserialize( vote_account, vote_state_mem );
70 0 : if( FD_UNLIKELY( rc ) ) return rc;
71 :
72 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
73 0 : if( FD_UNLIKELY( fd_vsv_is_uninitialized( versioned ) ) ) {
74 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
75 0 : }
76 :
77 0 : rc = fd_vsv_try_convert_to_v4( versioned, vote_account->pubkey, landed_votes_mem );
78 0 : if( FD_UNLIKELY( rc ) ) return rc;
79 :
80 0 : return FD_EXECUTOR_INSTR_SUCCESS;
81 0 : }
82 0 : default:
83 0 : FD_LOG_CRIT(( "unsupported version: %d", target_version ));
84 0 : }
85 0 : }
86 :
87 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L888-L902 */
88 : static int
89 : check_vote_account_length( fd_borrowed_account_t const * vote_account,
90 0 : int target_version ) {
91 0 : ulong length = fd_borrowed_account_get_data_len( vote_account );
92 0 : ulong expected;
93 0 : switch( target_version ) {
94 0 : case VOTE_STATE_TARGET_VERSION_V3:
95 0 : expected = FD_VOTE_STATE_V3_SZ;
96 0 : break;
97 0 : case VOTE_STATE_TARGET_VERSION_V4:
98 0 : expected = FD_VOTE_STATE_V4_SZ;
99 0 : break;
100 0 : default:
101 0 : FD_LOG_CRIT(( "unsupported version: %d", target_version ));
102 0 : }
103 0 : if( FD_UNLIKELY( length!=expected ) ) {
104 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
105 0 : }
106 0 : return FD_EXECUTOR_INSTR_SUCCESS;
107 0 : }
108 :
109 : /* The versioned parameter must point to a buffer that is aligned to
110 : FD_VOTE_STATE_VERSIONED_ALIGN and is at least
111 : FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes in size.
112 : https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L855-L870 */
113 : static int
114 : init_vote_account_state( fd_exec_instr_ctx_t * ctx,
115 : fd_borrowed_account_t * vote_account,
116 : fd_vote_state_versioned_t * versioned,
117 : int target_version,
118 : fd_vote_init_t * vote_init,
119 0 : fd_sol_sysvar_clock_t const * clock ) {
120 : /* Reset the object */
121 0 : fd_vote_state_versioned_new( versioned );
122 :
123 0 : switch( target_version ) {
124 0 : case VOTE_STATE_TARGET_VERSION_V3:
125 0 : fd_vote_program_v3_create_new(
126 0 : vote_init,
127 0 : clock,
128 0 : ctx->runtime->vote_program.init_account.authorized_voters_mem,
129 0 : versioned
130 0 : );
131 0 : return fd_vote_state_v3_set_vote_account_state(
132 0 : ctx,
133 0 : vote_account,
134 0 : versioned,
135 0 : ctx->runtime->vote_program.init_account.vote_lockout_mem
136 0 : );
137 0 : case VOTE_STATE_TARGET_VERSION_V4:
138 0 : fd_vote_state_v4_create_new(
139 0 : vote_account->pubkey,
140 0 : vote_init,
141 0 : clock,
142 0 : ctx->runtime->vote_program.init_account.authorized_voters_mem,
143 0 : versioned
144 0 : );
145 0 : return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned );
146 0 : default:
147 0 : FD_LOG_CRIT(( "unsupported version: %d", target_version ));
148 0 : }
149 0 : }
150 :
151 : /**********************************************************************/
152 : /* mod vote_state */
153 : /**********************************************************************/
154 :
155 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L82-L324 */
156 : static int
157 : check_and_filter_proposed_vote_state( fd_exec_instr_ctx_t * ctx,
158 : fd_vote_state_versioned_t * versioned,
159 : fd_vote_lockout_t * proposed_lockouts,
160 : uchar * proposed_has_root,
161 : ulong * proposed_root,
162 : fd_hash_t const * proposed_hash,
163 0 : fd_slot_hash_t const * slot_hashes /* deque */ ) {
164 0 : fd_landed_vote_t const * votes = fd_vsv_get_votes( versioned );
165 0 : ulong const * root_slot = fd_vsv_get_root_slot( versioned );
166 0 : uchar has_root_slot = !!(root_slot!=NULL);
167 :
168 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L208
169 0 : if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) {
170 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
171 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
172 0 : }
173 0 : fd_landed_vote_t const * last_vote = NULL;
174 0 : if( !deq_fd_landed_vote_t_empty( votes ) ) {
175 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L212
176 0 : last_vote = deq_fd_landed_vote_t_peek_tail_const( votes );
177 0 : }
178 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L218
179 0 : if( FD_LIKELY( last_vote ) ) {
180 0 : if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot <=
181 0 : last_vote->lockout.slot ) ) {
182 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
183 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
184 0 : }
185 0 : }
186 :
187 : /* must be nonempty, checked above */
188 0 : ulong last_vote_state_update_slot = deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot;
189 :
190 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L224
191 0 : if( FD_UNLIKELY( deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
192 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
193 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
194 0 : }
195 :
196 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L227
197 0 : ulong earliest_slot_hash_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
198 :
199 : /* Check if the proposed vote is too old to be in the SlotHash history */
200 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L230
201 0 : if( FD_UNLIKELY( last_vote_state_update_slot < earliest_slot_hash_in_history ) ) {
202 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
203 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
204 0 : }
205 :
206 : /* Check if the proposed root is too old */
207 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L237
208 0 : if( *proposed_has_root ) {
209 0 : ulong const proposed_root_ = *proposed_root;
210 : /* If the new proposed root `R` is less than the earliest slot hash in the history
211 : such that we cannot verify whether the slot was actually was on this fork, set
212 : the root to the latest vote in the current vote that's less than R. */
213 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L242
214 0 : if( proposed_root_ < earliest_slot_hash_in_history ) {
215 0 : *proposed_has_root = has_root_slot;
216 0 : if( has_root_slot ) {
217 0 : *proposed_root = *root_slot;
218 0 : }
219 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init_rev( votes );
220 0 : !deq_fd_landed_vote_t_iter_done_rev( votes, iter );
221 0 : iter = deq_fd_landed_vote_t_iter_prev( votes, iter ) ) {
222 : /* Ensure we're iterating from biggest to smallest vote in the
223 : current vote state */
224 0 : fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
225 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L248
226 0 : if( vote->lockout.slot <= proposed_root_ ) {
227 0 : *proposed_has_root = 1;
228 0 : *proposed_root = vote->lockout.slot;
229 0 : break;
230 0 : }
231 0 : }
232 0 : }
233 0 : }
234 :
235 : /* Index into the new proposed vote state's slots, starting with the root if it exists then
236 : we use this mutable root to fold checking the root slot into the below loop for performance */
237 0 : int has_root_to_check = *proposed_has_root;
238 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L259
239 0 : ulong root_to_check = *proposed_root;
240 0 : ulong proposed_lockouts_index = 0UL;
241 0 : ulong lockouts_len = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
242 :
243 : /* Index into the slot_hashes, starting at the oldest known slot hash */
244 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L264
245 0 : ulong slot_hashes_index = deq_fd_slot_hash_t_cnt( slot_hashes );
246 :
247 : /* proposed_lockouts_indexes_to_filter's size is bounded by the length
248 : of the proposed lockouts provided in the instruction data, which is
249 : capped at roughly 10KB / sizeof(ulong). */
250 0 : ulong proposed_lockouts_indexes_to_filter[ FD_INSTR_DATA_MAX/sizeof(ulong) ];
251 0 : ulong filter_index = 0UL;
252 :
253 : /* Note:
254 :
255 : 1) `vote_state_update.lockouts` is sorted from oldest/smallest vote to newest/largest
256 : vote, due to the way votes are applied to the vote state (newest votes
257 : pushed to the back).
258 :
259 : 2) Conversely, `slot_hashes` is sorted from newest/largest vote to
260 : the oldest/smallest vote.
261 :
262 : Unlike for vote updates, vote state updates here can't only check votes older than the last vote
263 : because have to ensure that every slot is actually part of the history, not just the most
264 : recent ones */
265 :
266 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L279
267 0 : while( proposed_lockouts_index < lockouts_len && slot_hashes_index > 0 ) {
268 0 : ulong proposed_vote_slot =
269 0 : fd_ulong_if( has_root_to_check,
270 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L281
271 0 : root_to_check,
272 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L283
273 0 : deq_fd_vote_lockout_t_peek_index_const( proposed_lockouts,
274 0 : proposed_lockouts_index )
275 0 : ->slot );
276 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L285
277 0 : if( !has_root_to_check && proposed_lockouts_index > 0UL &&
278 0 : proposed_vote_slot <=
279 0 : deq_fd_vote_lockout_t_peek_index_const(
280 0 : proposed_lockouts,
281 0 : fd_ulong_checked_sub_expect(
282 0 : proposed_lockouts_index,
283 0 : 1,
284 0 : "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`" ) )
285 0 : ->slot ) {
286 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L293
287 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
288 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
289 0 : }
290 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L295
291 0 : ulong ancestor_slot =
292 0 : deq_fd_slot_hash_t_peek_index_const(
293 0 : slot_hashes,
294 0 : fd_ulong_checked_sub_expect(
295 0 : slot_hashes_index,
296 0 : 1UL,
297 0 : "`slot_hashes_index` is positive when computing `ancestor_slot`" ) )
298 0 : ->slot;
299 : /* Find if this slot in the proposed vote state exists in the SlotHashes history
300 : to confirm if it was a valid ancestor on this fork */
301 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L303
302 0 : if( proposed_vote_slot < ancestor_slot ) {
303 0 : if( slot_hashes_index == deq_fd_slot_hash_t_cnt( slot_hashes ) ) {
304 : /* The vote slot does not exist in the SlotHashes history because it's too old,
305 : i.e. older than the oldest slot in the history. */
306 0 : if( proposed_vote_slot >= earliest_slot_hash_in_history ) {
307 0 : ctx->txn_out->err.custom_err = 0;
308 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
309 0 : }
310 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L310
311 0 : if( !fd_vote_contains_slot( votes, proposed_vote_slot ) && !has_root_to_check ) {
312 : /* If the vote slot is both:
313 : 1) Too old
314 : 2) Doesn't already exist in vote state
315 : Then filter it out */
316 0 : proposed_lockouts_indexes_to_filter[filter_index++] = proposed_lockouts_index; }
317 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L318
318 0 : if( has_root_to_check ) {
319 0 : ulong new_proposed_root = root_to_check;
320 : /* 1. Because `root_to_check.is_some()`, then we know that
321 : we haven't checked the root yet in this loop, so
322 : `proposed_vote_slot` == `new_proposed_root` == `vote_state_update.root` */
323 0 : FD_TEST( new_proposed_root == proposed_vote_slot );
324 : /* 2. We know from the assert earlier in the function that
325 : `proposed_vote_slot < earliest_slot_hash_in_history`,
326 : so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history` */
327 0 : if( new_proposed_root >= earliest_slot_hash_in_history ) {
328 0 : ctx->txn_out->err.custom_err = 0;
329 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
330 0 : }
331 :
332 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L329
333 0 : has_root_to_check = 0;
334 0 : root_to_check = ULONG_MAX;
335 0 : } else {
336 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L331
337 0 : proposed_lockouts_index = fd_ulong_checked_add_expect(
338 0 : proposed_lockouts_index,
339 0 : 1,
340 0 : "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when "
341 0 : "`proposed_vote_slot` is too old to be in SlotHashes history" );
342 0 : }
343 0 : continue;
344 0 : } else {
345 : /* If the vote slot is new enough to be in the slot history,
346 : but is not part of the slot history, then it must belong to another fork,
347 : which means this vote state update is invalid. */
348 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L340
349 0 : if( has_root_to_check ) {
350 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ON_DIFFERENT_FORK;
351 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
352 0 : } else {
353 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
354 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
355 0 : }
356 0 : }
357 0 : } else if( proposed_vote_slot > ancestor_slot ) {
358 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L347
359 :
360 : /* Decrement `slot_hashes_index` to find newer slots in the SlotHashes history */
361 0 : slot_hashes_index = fd_ulong_checked_sub_expect(
362 0 : slot_hashes_index,
363 0 : 1,
364 0 : "`slot_hashes_index` is positive when finding newer slots in SlotHashes history" );
365 0 : continue;
366 0 : } else {
367 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L354
368 :
369 : /* Once the slot in `vote_state_update.lockouts` is found, bump to the next slot
370 : in `vote_state_update.lockouts` and continue. If we were checking the root,
371 : start checking the vote state instead. */
372 0 : if( has_root_to_check ) {
373 0 : has_root_to_check = 0;
374 0 : root_to_check = ULONG_MAX;
375 0 : } else {
376 0 : proposed_lockouts_index = fd_ulong_checked_add_expect(
377 0 : proposed_lockouts_index,
378 0 : 1,
379 0 : "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` "
380 0 : "when match is found in SlotHashes history" );
381 0 : slot_hashes_index = fd_ulong_checked_sub_expect(
382 0 : slot_hashes_index,
383 0 : 1,
384 0 : "`slot_hashes_index` is positive when match is found in SlotHashes history" );
385 0 : }
386 0 : }
387 0 : }
388 :
389 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L372
390 0 : if( proposed_lockouts_index != deq_fd_vote_lockout_t_cnt( proposed_lockouts ) ) {
391 : /* The last vote slot in the update did not exist in SlotHashes */
392 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
393 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
394 0 : }
395 :
396 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L401
397 0 : if( memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, slot_hashes_index )->hash,
398 0 : proposed_hash,
399 0 : sizeof( fd_hash_t ) ) != 0 ) {
400 : /* This means the newest vote in the slot has a match that
401 : doesn't match the expected hash for that slot on this fork */
402 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
403 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
404 0 : }
405 :
406 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L418
407 : /* Filter out the irrelevant votes */
408 0 : proposed_lockouts_index = 0UL;
409 0 : ulong filter_votes_index = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
410 :
411 : /* We need to iterate backwards here because proposed_lockouts_indexes_to_filter[ i ] is a
412 : strictly increasing value. Forward iterating can lead to the proposed lockout indicies to get
413 : shifted leading to popping the wrong proposed lockouts or out of bounds accessing. We need
414 : to be sure of handling underflow in this case. */
415 :
416 0 : for( ulong i=filter_index; i>0UL && filter_votes_index>0UL; i-- ) {
417 0 : proposed_lockouts_index = i - 1UL;
418 0 : if( FD_UNLIKELY( proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ]>=filter_votes_index ) ) {
419 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
420 0 : }
421 :
422 0 : deq_fd_vote_lockout_t_pop_idx_tail( proposed_lockouts, proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ] );
423 0 : filter_votes_index--;
424 0 : }
425 :
426 0 : return FD_EXECUTOR_INSTR_SUCCESS;
427 0 : }
428 :
429 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L440
430 : static int
431 : check_slots_are_valid( fd_exec_instr_ctx_t * ctx,
432 : fd_vote_state_versioned_t * versioned,
433 : ulong const * vote_slots,
434 : fd_hash_t const * vote_hash,
435 0 : fd_slot_hash_t const * slot_hashes /* deque */ ) {
436 0 : ulong i = 0;
437 0 : ulong j = deq_fd_slot_hash_t_cnt( slot_hashes );
438 0 : ulong vote_slots_len = deq_ulong_cnt( vote_slots );
439 :
440 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L462
441 0 : while( i < vote_slots_len && j > 0 ) {
442 0 : ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( versioned );
443 0 : if( FD_UNLIKELY( last_voted_slot_ &&
444 0 : *deq_ulong_peek_index_const( vote_slots, i ) <= *last_voted_slot_ ) ) {
445 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L469
446 0 : i = fd_ulong_checked_add_expect(
447 0 : i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots" );
448 0 : continue;
449 0 : }
450 :
451 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L476
452 0 : if( FD_UNLIKELY(
453 0 : *deq_ulong_peek_index_const( vote_slots, i ) !=
454 0 : deq_fd_slot_hash_t_peek_index_const( slot_hashes,
455 0 : fd_ulong_checked_sub_expect( j, 1, "`j` is positive" ) )
456 0 : ->slot ) ) {
457 0 : j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when finding newer slots" );
458 0 : continue;
459 0 : }
460 :
461 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L486
462 0 : i = fd_ulong_checked_add_expect(
463 0 : i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found" );
464 0 : j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when hash is found" );
465 0 : }
466 :
467 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L494
468 0 : if( FD_UNLIKELY( j == deq_fd_slot_hash_t_cnt( slot_hashes ) ) ) {
469 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
470 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
471 0 : }
472 0 : if( FD_UNLIKELY( i != vote_slots_len ) ) {
473 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
474 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
475 0 : }
476 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L514
477 0 : if( FD_UNLIKELY( 0 != memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, j )->hash,
478 0 : vote_hash,
479 0 : 32UL ) ) ) {
480 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
481 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
482 0 : }
483 0 : return 0;
484 0 : }
485 :
486 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L565
487 : static int
488 : process_new_vote_state( fd_exec_instr_ctx_t * ctx,
489 : fd_vote_state_versioned_t * versioned,
490 : fd_landed_vote_t * new_state,
491 : int has_new_root,
492 : ulong new_root,
493 : int has_timestamp,
494 : long timestamp,
495 : ulong epoch,
496 0 : ulong current_slot ) {
497 0 : int rc;
498 0 : fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( versioned );
499 0 : ulong const * root_slot = fd_vsv_get_root_slot( versioned );
500 0 : uchar has_root_slot = !!(root_slot!=NULL);
501 :
502 0 : FD_TEST( !deq_fd_landed_vote_t_empty( new_state ) );
503 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L575
504 0 : if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( new_state ) > MAX_LOCKOUT_HISTORY ) ) {
505 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_MANY_VOTES;
506 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
507 0 : };
508 :
509 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L579
510 0 : if( FD_UNLIKELY( has_new_root && has_root_slot ) ) {
511 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L581
512 0 : if( FD_UNLIKELY( new_root<*root_slot ) ) {
513 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
514 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
515 0 : }
516 0 : } else if( FD_UNLIKELY( !has_new_root && has_root_slot ) ) {
517 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L586
518 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
519 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
520 0 : } else {
521 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L588
522 : /* no-op */
523 0 : }
524 :
525 0 : fd_landed_vote_t * previous_vote = NULL;
526 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
527 0 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
528 0 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
529 0 : fd_landed_vote_t * vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
530 0 : if( FD_LIKELY( vote->lockout.confirmation_count == 0 ) ) {
531 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ZERO_CONFIRMATIONS;
532 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
533 0 : } else if( FD_UNLIKELY( vote->lockout.confirmation_count > MAX_LOCKOUT_HISTORY ) ) {
534 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_TOO_LARGE;
535 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
536 0 : } else if( FD_LIKELY( has_new_root ) ) {
537 0 : if( FD_UNLIKELY( vote->lockout.slot <= new_root && new_root != SLOT_DEFAULT ) ) {
538 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOT_SMALLER_THAN_ROOT;
539 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
540 0 : }
541 0 : }
542 :
543 0 : if( FD_LIKELY( previous_vote ) ) {
544 0 : if( FD_UNLIKELY( previous_vote->lockout.slot >= vote->lockout.slot ) ) {
545 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
546 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
547 0 : } else if( FD_UNLIKELY( previous_vote->lockout.confirmation_count <=
548 0 : vote->lockout.confirmation_count ) ) {
549 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATIONS_NOT_ORDERED;
550 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
551 0 : } else if( FD_UNLIKELY( vote->lockout.slot >
552 0 : fd_vote_lockout_last_locked_out_slot( &previous_vote->lockout ) ) ) {
553 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_NEW_VOTE_STATE_LOCKOUT_MISMATCH;
554 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
555 0 : }
556 0 : }
557 0 : previous_vote = vote;
558 0 : }
559 :
560 0 : ulong current_vote_state_index = 0;
561 0 : ulong new_vote_state_index = 0;
562 :
563 : /* Accumulate credits earned by newly rooted slots. The behavior changes with
564 : timely_vote_credits: prior to this feature, there was a bug that counted a new root slot as 1
565 : credit even if it had never been voted on. timely_vote_credits fixes this bug by only awarding
566 : credits for slots actually voted on and finalized. */
567 :
568 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L635
569 :
570 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L641
571 0 : ulong earned_credits = 0;
572 :
573 0 : if( FD_LIKELY( has_new_root ) ) {
574 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
575 0 : !deq_fd_landed_vote_t_iter_done( votes, iter );
576 0 : iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
577 0 : fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
578 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L647
579 0 : if( FD_UNLIKELY( current_vote->lockout.slot <= new_root ) ) {
580 : // this is safe because we're inside if has_new_root
581 0 : earned_credits = fd_ulong_checked_add_expect(
582 0 : fd_vote_credits_for_vote_at_index(
583 0 : votes,
584 0 : current_vote_state_index
585 0 : ),
586 0 : earned_credits,
587 0 : "`earned_credits` does not overflow" );
588 0 : current_vote_state_index = fd_ulong_checked_add_expect(
589 0 : current_vote_state_index,
590 0 : 1,
591 0 : "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
592 0 : "when processing new root" );
593 0 : continue;
594 0 : }
595 0 : break;
596 0 : }
597 0 : }
598 :
599 : // For any slots newly added to the new vote state, the vote latency of that slot is not provided by the
600 : // vote instruction contents, but instead is computed from the actual latency of the vote
601 : // instruction. This prevents other validators from manipulating their own vote latencies within their vote states
602 : // and forcing the rest of the cluster to accept these possibly fraudulent latency values. If the
603 : // timly_vote_credits feature is not enabled then vote latency is set to 0 for new votes.
604 : //
605 : // For any slot that is in both the new state and the current state, the vote latency of the new state is taken
606 : // from the current state.
607 : //
608 : // Thus vote latencies are set here for any newly vote-on slots when a vote instruction is received.
609 : // They are copied into the new vote state after every vote for already voted-on slots.
610 : // And when voted-on slots are rooted, the vote latencies stored in the vote state of all the rooted slots is used
611 : // to compute credits earned.
612 : // All validators compute the same vote latencies because all process the same vote instruction at the
613 : // same slot, and the only time vote latencies are ever computed is at the time that their slot is first voted on;
614 : // after that, the latencies are retained unaltered until the slot is rooted.
615 :
616 : // All the votes in our current vote state that are missing from the new vote state
617 : // must have been expired by later votes. Check that the lockouts match this assumption.
618 :
619 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L686
620 0 : while( current_vote_state_index < deq_fd_landed_vote_t_cnt( votes ) &&
621 0 : new_vote_state_index < deq_fd_landed_vote_t_cnt( new_state ) ) {
622 0 : fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index );
623 :
624 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L690
625 0 : fd_landed_vote_t * new_vote =
626 0 : deq_fd_landed_vote_t_peek_index( new_state, new_vote_state_index );
627 :
628 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L696
629 0 : if( FD_LIKELY( current_vote->lockout.slot < new_vote->lockout.slot ) ) {
630 : /* The agave implementation of calculating the last locked out
631 : slot does not calculate a min between the current vote's
632 : confirmation count and max lockout history. The reason we do
633 : this is to make sure that the fuzzers continue working:
634 : the max lockout history can not be > MAX_LOCKOUT_HISTORY. */
635 0 : ulong confirmation_count = fd_ulong_min( current_vote->lockout.confirmation_count, MAX_LOCKOUT_HISTORY );
636 0 : ulong last_locked_out_slot = fd_ulong_sat_add( current_vote->lockout.slot,
637 0 : (ulong)pow( INITIAL_LOCKOUT, (double)confirmation_count ) );
638 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L697
639 0 : if( last_locked_out_slot >= new_vote->lockout.slot ) {
640 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L698
641 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_LOCKOUT_CONFLICT;
642 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
643 0 : }
644 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L700
645 0 : current_vote_state_index =
646 0 : fd_ulong_checked_add_expect( current_vote_state_index,
647 0 : 1,
648 0 : "`current_vote_state_index` is bounded by "
649 0 : "`MAX_LOCKOUT_HISTORY` when slot is less than proposed" );
650 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L704
651 0 : } else if( FD_UNLIKELY( current_vote->lockout.slot == new_vote->lockout.slot ) ) {
652 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L707
653 0 : if( new_vote->lockout.confirmation_count < current_vote->lockout.confirmation_count ) {
654 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_ROLL_BACK;
655 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
656 0 : }
657 :
658 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L712
659 0 : new_vote->latency = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index )->latency;
660 :
661 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L714
662 0 : current_vote_state_index =
663 0 : fd_ulong_checked_add_expect( current_vote_state_index,
664 0 : 1,
665 0 : "`current_vote_state_index` is bounded by "
666 0 : "`MAX_LOCKOUT_HISTORY` when slot is equal to proposed" );
667 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L717
668 0 : new_vote_state_index =
669 0 : fd_ulong_checked_add_expect( new_vote_state_index,
670 0 : 1,
671 0 : "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
672 0 : "when slot is equal to proposed" );
673 0 : } else {
674 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L722
675 0 : new_vote_state_index =
676 0 : fd_ulong_checked_add_expect( new_vote_state_index,
677 0 : 1,
678 0 : "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
679 0 : "when slot is greater than proposed" );
680 0 : }
681 0 : }
682 :
683 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L737
684 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
685 0 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
686 0 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
687 0 : fd_landed_vote_t * new_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
688 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L738
689 0 : if( FD_UNLIKELY( new_vote->latency == 0 ) ) {
690 : // this is unlikely because as validators upgrade, it should converge to the new vote state
691 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L739
692 0 : new_vote->latency = fd_vote_compute_vote_latency( new_vote->lockout.slot, current_slot );
693 0 : }
694 0 : }
695 :
696 : // doesn't matter what the value of slot if `is_some = 0` i.e. `Option::None`
697 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L744
698 0 : int both_none = !has_root_slot && !has_new_root;
699 0 : if( ( !both_none && ( has_root_slot!=has_new_root ||
700 0 : *root_slot!=new_root ) ) ) {
701 0 : fd_vsv_increment_credits( versioned, epoch, earned_credits );
702 0 : }
703 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750
704 0 : if( FD_LIKELY( has_timestamp ) ) {
705 : /* new_state asserted nonempty at function beginning */
706 0 : if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( new_state ) ) ) {
707 0 : FD_LOG_CRIT(( "invariant violation: landed votes is empty" ));
708 0 : }
709 0 : ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot;
710 0 : rc = fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp );
711 0 : if( FD_UNLIKELY( rc ) ) { return rc; }
712 0 : fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp );
713 0 : }
714 :
715 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L754
716 0 : fd_vsv_set_root_slot( versioned, has_new_root ? &new_root : NULL );
717 :
718 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L755
719 0 : deq_fd_landed_vote_t_remove_all( votes );
720 0 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
721 0 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
722 0 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
723 0 : fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
724 0 : deq_fd_landed_vote_t_push_tail_wrap( votes, *landed_vote );
725 0 : }
726 :
727 0 : return FD_EXECUTOR_INSTR_SUCCESS;
728 0 : }
729 :
730 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L716-L759 */
731 : static int
732 : authorize( fd_exec_instr_ctx_t * ctx,
733 : fd_borrowed_account_t * vote_account,
734 : int target_version,
735 : fd_pubkey_t const * authorized,
736 : fd_vote_authorize_t vote_authorize,
737 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
738 0 : fd_sol_sysvar_clock_t const * clock ) {
739 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L724-L727 */
740 0 : int rc = get_vote_state_handler_checked(
741 0 : vote_account,
742 0 : target_version,
743 0 : 0,
744 0 : ctx->runtime->vote_program.authorize.vote_state_mem,
745 0 : ctx->runtime->vote_program.authorize.authorized_voters_mem,
746 0 : ctx->runtime->vote_program.authorize.landed_votes_mem
747 0 : );
748 0 : if( FD_UNLIKELY( rc ) ) return rc;
749 :
750 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L729-L756 */
751 0 : fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.authorize.vote_state_mem;
752 0 : switch( vote_authorize.discriminant ) {
753 :
754 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L730-L750 */
755 0 : case fd_vote_authorize_enum_voter: {
756 :
757 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L731-L732 */
758 0 : int authorized_withdrawer_signer = !fd_vote_verify_authorized_signer(
759 0 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
760 0 : signers
761 0 : );
762 :
763 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L737-L740 */
764 0 : ulong target_epoch;
765 0 : rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch );
766 0 : if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
767 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
768 0 : }
769 :
770 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L866
771 0 : rc = fd_vsv_set_new_authorized_voter(
772 0 : ctx,
773 0 : vote_state_versioned,
774 0 : authorized,
775 0 : clock->epoch,
776 0 : target_epoch,
777 0 : authorized_withdrawer_signer,
778 0 : signers
779 0 : );
780 0 : if( FD_UNLIKELY( rc ) ) return rc;
781 0 : break;
782 0 : }
783 :
784 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L751-L756 */
785 0 : case fd_vote_authorize_enum_withdrawer: {
786 0 : rc = fd_vote_verify_authorized_signer(
787 0 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
788 0 : signers
789 0 : );
790 0 : if( FD_UNLIKELY( rc ) ) return rc;
791 0 : fd_vsv_set_authorized_withdrawer( vote_state_versioned, authorized );
792 0 : break;
793 0 : }
794 0 : default:
795 0 : FD_LOG_CRIT(( "unsupported vote_authorize discriminant: %u", vote_authorize.discriminant ));
796 0 : }
797 :
798 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L758 */
799 0 : return fd_vsv_set_vote_account_state(
800 0 : ctx,
801 0 : vote_account,
802 0 : vote_state_versioned,
803 0 : ctx->runtime->vote_program.authorize.vote_lockout_mem
804 0 : );
805 0 : }
806 :
807 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L761-L785 */
808 : static int
809 : update_validator_identity( fd_exec_instr_ctx_t * ctx,
810 : int target_version,
811 : fd_borrowed_account_t * vote_account,
812 : fd_pubkey_t const * node_pubkey,
813 0 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
814 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L768-L771 */
815 0 : int rc = get_vote_state_handler_checked(
816 0 : vote_account,
817 0 : target_version,
818 0 : 0,
819 0 : ctx->runtime->vote_program.update_validator_identity.vote_state_mem,
820 0 : ctx->runtime->vote_program.update_validator_identity.authorized_voters_mem,
821 0 : ctx->runtime->vote_program.update_validator_identity.landed_votes_mem
822 0 : );
823 0 : if( FD_UNLIKELY( rc ) ) return rc;
824 :
825 0 : fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_validator_identity.vote_state_mem;
826 :
827 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L774 */
828 0 : rc = fd_vote_verify_authorized_signer(
829 0 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
830 0 : signers
831 0 : );
832 0 : if( FD_UNLIKELY( rc ) ) return rc;
833 :
834 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L777 */
835 0 : rc = fd_vote_verify_authorized_signer( node_pubkey, signers );
836 0 : if( FD_UNLIKELY( rc ) ) return rc;
837 :
838 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L779 */
839 0 : fd_vsv_set_node_pubkey( vote_state_versioned, node_pubkey );
840 :
841 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L782 */
842 0 : fd_vsv_set_block_revenue_collector( vote_state_versioned, node_pubkey );
843 :
844 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L784 */
845 0 : return fd_vsv_set_vote_account_state(
846 0 : ctx,
847 0 : vote_account,
848 0 : vote_state_versioned,
849 0 : ctx->runtime->vote_program.update_validator_identity.vote_lockout_mem
850 0 : );
851 0 : }
852 :
853 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L971
854 : static int
855 0 : is_commission_update_allowed( ulong slot, fd_epoch_schedule_t const * epoch_schedule ) {
856 0 : if( FD_LIKELY( epoch_schedule->slots_per_epoch > 0UL ) ) {
857 0 : ulong relative_slot = fd_ulong_sat_sub( slot, epoch_schedule->first_normal_slot );
858 : // TODO underflow and overflow edge cases in addition to div by 0
859 0 : relative_slot %= epoch_schedule->slots_per_epoch;
860 0 : return fd_ulong_sat_mul( relative_slot, 2 ) <= epoch_schedule->slots_per_epoch;
861 0 : } else {
862 0 : return 1;
863 0 : }
864 0 : }
865 :
866 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L787-L818 */
867 : static int
868 : update_commission( fd_exec_instr_ctx_t * ctx,
869 : int target_version,
870 : fd_borrowed_account_t * vote_account,
871 : uchar commission,
872 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
873 : fd_epoch_schedule_t const * epoch_schedule,
874 0 : fd_sol_sysvar_clock_t const * clock ) {
875 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L796-L799 */
876 0 : int rc = 0;
877 0 : int get_vsv_rc = get_vote_state_handler_checked(
878 0 : vote_account,
879 0 : target_version,
880 0 : false,
881 0 : ctx->runtime->vote_program.update_commission.vote_state_mem,
882 0 : ctx->runtime->vote_program.update_commission.authorized_voters_mem,
883 0 : ctx->runtime->vote_program.update_commission.landed_votes_mem
884 0 : );
885 :
886 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L800-L804 */
887 0 : fd_vote_state_versioned_t * vote_state_versioned = NULL;
888 0 : int enforce_commission_update_rule = 1;
889 0 : if( FD_LIKELY( get_vsv_rc==FD_EXECUTOR_INSTR_SUCCESS ) ) {
890 0 : vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem;
891 0 : enforce_commission_update_rule = (commission>fd_vsv_get_commission( vote_state_versioned ));
892 0 : }
893 :
894 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L806-L808 */
895 0 : if( FD_UNLIKELY( enforce_commission_update_rule && !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) {
896 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE;
897 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
898 0 : }
899 :
900 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L810 */
901 0 : if( FD_UNLIKELY( get_vsv_rc ) ) return get_vsv_rc;
902 :
903 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L813 */
904 0 : rc = fd_vote_verify_authorized_signer(
905 0 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
906 0 : signers
907 0 : );
908 0 : if( FD_UNLIKELY( rc ) ) return rc;
909 :
910 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L815 */
911 0 : fd_vsv_set_commission( vote_state_versioned, commission );
912 :
913 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L817 */
914 0 : return fd_vsv_set_vote_account_state(
915 0 : ctx,
916 0 : vote_account,
917 0 : vote_state_versioned,
918 0 : ctx->runtime->vote_program.update_commission.vote_lockout_mem
919 0 : );
920 0 : }
921 :
922 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L848C8-L903 */
923 : static int
924 : withdraw( fd_exec_instr_ctx_t * ctx,
925 : fd_borrowed_account_t * vote_account,
926 : int target_version,
927 : ulong lamports,
928 : ushort to_account_index,
929 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
930 : fd_rent_t const * rent_sysvar,
931 0 : fd_sol_sysvar_clock_t const * clock ) {
932 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L860-L863 */
933 0 : int rc = get_vote_state_handler_checked(
934 0 : vote_account,
935 0 : target_version,
936 0 : 0,
937 0 : ctx->runtime->vote_program.withdraw.vote_state_mem,
938 0 : ctx->runtime->vote_program.withdraw.authorized_voters_mem,
939 0 : ctx->runtime->vote_program.withdraw.landed_votes_mem
940 0 : );
941 0 : if( FD_UNLIKELY( rc ) ) return rc;
942 :
943 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.withdraw.vote_state_mem;
944 :
945 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L865 */
946 0 : rc = fd_vote_verify_authorized_signer(
947 0 : fd_vsv_get_authorized_withdrawer( versioned ),
948 0 : signers
949 0 : );
950 0 : if( FD_UNLIKELY( rc ) ) return rc;
951 :
952 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L867-L870 */
953 0 : ulong vote_account_lamports = fd_borrowed_account_get_lamports( vote_account );
954 0 : if( FD_UNLIKELY( lamports>vote_account_lamports ) ) {
955 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
956 0 : }
957 0 : ulong remaining_balance = vote_account_lamports-lamports;
958 :
959 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L872-L896 */
960 0 : if( FD_UNLIKELY( remaining_balance==0UL ) ) {
961 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L873-L883 */
962 0 : fd_vote_epoch_credits_t const * epoch_credits = fd_vsv_get_epoch_credits( versioned );
963 0 : ulong last_epoch_with_credits;
964 0 : int reject_active_vote_account_close = 0;
965 0 : if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) {
966 0 : ulong current_epoch = clock->epoch;
967 0 : last_epoch_with_credits = deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->epoch;
968 0 : reject_active_vote_account_close = fd_ulong_sat_sub( current_epoch, last_epoch_with_credits )<2UL;
969 0 : }
970 :
971 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L885-L890 */
972 0 : if( FD_UNLIKELY( reject_active_vote_account_close ) ) {
973 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ACTIVE_VOTE_ACCOUNT_CLOSE;
974 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
975 0 : } else {
976 0 : rc = fd_vsv_deinitialize_vote_account_state(
977 0 : ctx,
978 0 : vote_account,
979 0 : target_version,
980 0 : ctx->runtime->vote_program.withdraw.vote_lockout_mem
981 0 : );
982 0 : if( FD_UNLIKELY( rc ) ) return rc;
983 0 : }
984 0 : } else {
985 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L892-L895 */
986 0 : ulong min_rent_exempt_balance = fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) );
987 0 : if( FD_UNLIKELY( remaining_balance<min_rent_exempt_balance ) ) {
988 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
989 0 : }
990 0 : }
991 :
992 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L898 */
993 0 : rc = fd_borrowed_account_checked_sub_lamports( vote_account, lamports );
994 0 : if( FD_UNLIKELY( rc ) ) return rc;
995 :
996 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L899 */
997 0 : fd_borrowed_account_drop( vote_account );
998 :
999 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L900 */
1000 0 : fd_guarded_borrowed_account_t to = {0};
1001 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_account_index, &to );
1002 :
1003 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L901 */
1004 0 : rc = fd_borrowed_account_checked_add_lamports( &to, lamports );
1005 0 : if( FD_UNLIKELY( rc ) ) return rc;
1006 :
1007 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1008 0 : }
1009 :
1010 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L638-L651 */
1011 : static int
1012 : process_vote_unfiltered( fd_exec_instr_ctx_t * ctx,
1013 : fd_vote_state_versioned_t * versioned,
1014 : ulong * vote_slots,
1015 : fd_vote_t const * vote,
1016 : fd_slot_hash_t const * slot_hashes, /* deque */
1017 : ulong epoch,
1018 0 : ulong current_slot ) {
1019 0 : int rc;
1020 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L770
1021 0 : rc = check_slots_are_valid( ctx, versioned, vote_slots, &vote->hash, slot_hashes );
1022 0 : if( FD_UNLIKELY( rc ) ) return rc;
1023 0 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote_slots );
1024 0 : !deq_ulong_iter_done( vote_slots, iter );
1025 0 : iter = deq_ulong_iter_next( vote_slots, iter ) ) {
1026 0 : ulong * ele = deq_ulong_iter_ele( vote_slots, iter );
1027 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L772
1028 0 : fd_vsv_process_next_vote_slot( versioned, *ele, epoch, current_slot );
1029 0 : }
1030 0 : return 0;
1031 0 : }
1032 :
1033 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L783
1034 : static int
1035 : process_vote( fd_exec_instr_ctx_t * ctx,
1036 : fd_vote_state_versioned_t * versioned,
1037 : fd_vote_t const * vote,
1038 : fd_slot_hash_t const * slot_hashes, /* deque */
1039 : ulong epoch,
1040 0 : ulong current_slot ) {
1041 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L792
1042 0 : if( FD_UNLIKELY( deq_ulong_empty( vote->slots ) ) ) {
1043 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1044 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1045 0 : }
1046 :
1047 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L795
1048 0 : ulong earliest_slot_in_history = 0;
1049 0 : if( FD_UNLIKELY( !deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
1050 0 : earliest_slot_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
1051 0 : }
1052 :
1053 : /* We know that the size of the vote_slots is bounded by the number of
1054 : slots that can fit inside of an instruction. A very loose bound is
1055 : assuming that the entire transaction is just filled with a vote
1056 : slot deque (1232 bytes per transaction/8 bytes per slot) == 154
1057 : slots. The footprint of a deque is as follows:
1058 : fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(DEQUE_T) ) + sizeof(DEQUE_T)*max, alignof(DEQUE_(private_t)) );
1059 : So, the footprint in our case is:
1060 : fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(ulong) ) + sizeof(ulong)*154, alignof(DEQUE_(private_t)) );
1061 : Which is equal to
1062 : fd_ulong_align_up( 32UL + 154 * 8UL, 8UL ) = 1264UL; */
1063 0 : #define VOTE_SLOTS_MAX (FD_TXN_MTU/sizeof(ulong))
1064 0 : #define VOTE_SLOTS_DEQUE_FOOTPRINT (1264UL )
1065 0 : #define VOTE_SLOTS_DEQUE_ALIGN (8UL)
1066 0 : FD_TEST( deq_ulong_footprint( VOTE_SLOTS_MAX ) == VOTE_SLOTS_DEQUE_FOOTPRINT );
1067 0 : FD_TEST( deq_ulong_align() == 8UL );
1068 0 : FD_TEST( deq_ulong_cnt( vote->slots ) <= VOTE_SLOTS_MAX );
1069 0 : uchar * vote_slots_mem[ VOTE_SLOTS_DEQUE_FOOTPRINT ] __attribute__((aligned(VOTE_SLOTS_DEQUE_ALIGN)));
1070 :
1071 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L796
1072 0 : ulong * vote_slots = deq_ulong_join( deq_ulong_new( vote_slots_mem, deq_ulong_cnt( vote->slots ) ) );
1073 0 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
1074 0 : !deq_ulong_iter_done( vote->slots, iter );
1075 0 : iter = deq_ulong_iter_next( vote->slots, iter ) ) {
1076 0 : ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
1077 0 : if( FD_UNLIKELY( *ele >= earliest_slot_in_history ) ) {
1078 0 : vote_slots = deq_ulong_push_tail_wrap( vote_slots, *ele );
1079 0 : }
1080 0 : }
1081 :
1082 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L802
1083 0 : if( FD_UNLIKELY( deq_ulong_cnt( vote_slots ) == 0 ) ) {
1084 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTES_TOO_OLD_ALL_FILTERED;
1085 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1086 0 : }
1087 :
1088 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L805
1089 0 : return process_vote_unfiltered(
1090 0 : ctx,
1091 0 : versioned,
1092 0 : vote_slots,
1093 0 : vote,
1094 0 : slot_hashes,
1095 0 : epoch,
1096 0 : current_slot
1097 0 : );
1098 0 : }
1099 :
1100 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L905-L926 */
1101 : static int
1102 : initialize_account( fd_exec_instr_ctx_t * ctx,
1103 : fd_borrowed_account_t * vote_account,
1104 : int target_version,
1105 : fd_vote_init_t * vote_init,
1106 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1107 0 : fd_sol_sysvar_clock_t const * clock ) {
1108 0 : int rc;
1109 :
1110 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L915 */
1111 0 : rc = check_vote_account_length( vote_account, target_version );
1112 0 : if( FD_UNLIKELY( rc ) ) return rc;
1113 :
1114 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L916 */
1115 0 : rc = fd_vsv_get_state( vote_account->meta, ctx->runtime->vote_program.init_account.vote_state_mem );
1116 0 : if( FD_UNLIKELY( rc ) ) return rc;
1117 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.init_account.vote_state_mem;
1118 :
1119 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L918-L920 */
1120 0 : if( FD_UNLIKELY( !fd_vsv_is_uninitialized( versioned ) ) ) {
1121 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1122 0 : }
1123 :
1124 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L923 */
1125 0 : rc = fd_vote_verify_authorized_signer( &vote_init->node_pubkey, signers );
1126 0 : if( FD_UNLIKELY( rc ) ) {
1127 0 : return rc;
1128 0 : }
1129 :
1130 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L925 */
1131 0 : return init_vote_account_state( ctx, vote_account, versioned, target_version, vote_init, clock );
1132 0 : }
1133 :
1134 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L928-L953 */
1135 : static int
1136 : process_vote_with_account( fd_exec_instr_ctx_t * ctx,
1137 : fd_borrowed_account_t * vote_account,
1138 : int target_version,
1139 : fd_slot_hash_t const * slot_hashes, /* deque */
1140 : fd_sol_sysvar_clock_t const * clock,
1141 : fd_vote_t * vote,
1142 0 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
1143 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L936-L939 */
1144 0 : int rc = get_vote_state_handler_checked(
1145 0 : vote_account,
1146 0 : target_version,
1147 0 : 1,
1148 0 : ctx->runtime->vote_program.process_vote.vote_state_mem,
1149 0 : ctx->runtime->vote_program.process_vote.authorized_voters_mem,
1150 0 : ctx->runtime->vote_program.process_vote.landed_votes_mem
1151 0 : );
1152 0 : if( FD_UNLIKELY( rc ) ) return rc;
1153 :
1154 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.process_vote.vote_state_mem;
1155 :
1156 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L941 */
1157 0 : fd_pubkey_t * authorized_voter = NULL;
1158 0 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1159 0 : if( FD_UNLIKELY( rc ) ) return rc;
1160 :
1161 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L942 */
1162 0 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers );
1163 0 : if( FD_UNLIKELY( rc ) ) return rc;
1164 :
1165 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L944 */
1166 0 : rc = process_vote( ctx, versioned, vote, slot_hashes, clock->epoch, clock->slot );
1167 0 : if( FD_UNLIKELY( rc ) ) return rc;
1168 :
1169 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L945-L951 */
1170 0 : if( FD_LIKELY( vote->has_timestamp ) ) {
1171 : /* Calling max() on an empty iterator returns None */
1172 0 : if( FD_UNLIKELY( deq_ulong_cnt( vote->slots )==0 ) ) {
1173 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1174 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1175 0 : }
1176 :
1177 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L948 */
1178 0 : ulong max = 0UL;
1179 0 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
1180 0 : !deq_ulong_iter_done( vote->slots, iter );
1181 0 : iter = deq_ulong_iter_next( vote->slots, iter ) ) {
1182 0 : ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
1183 0 : max = fd_ulong_max( max, *ele );
1184 0 : }
1185 :
1186 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L950 */
1187 0 : rc = fd_vsv_process_timestamp( ctx, versioned, max, vote->timestamp );
1188 0 : if( FD_UNLIKELY( rc ) ) return rc;
1189 0 : }
1190 :
1191 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L952 */
1192 0 : return fd_vsv_set_vote_account_state(
1193 0 : ctx,
1194 0 : vote_account,
1195 0 : versioned,
1196 0 : ctx->runtime->vote_program.process_vote.vote_lockout_mem
1197 0 : );
1198 0 : }
1199 :
1200 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1156
1201 : static int
1202 : do_process_vote_state_update( fd_exec_instr_ctx_t * ctx,
1203 : fd_vote_state_versioned_t * versioned,
1204 : fd_slot_hash_t const * slot_hashes, /* deque */
1205 : ulong epoch,
1206 : ulong slot,
1207 0 : fd_vote_state_update_t * vote_state_update ) {
1208 0 : int rc;
1209 :
1210 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1164
1211 0 : rc = check_and_filter_proposed_vote_state(
1212 0 : ctx,
1213 0 : versioned,
1214 0 : vote_state_update->lockouts,
1215 0 : &vote_state_update->has_root,
1216 0 : &vote_state_update->root,
1217 0 : &vote_state_update->hash,
1218 0 : slot_hashes
1219 0 : );
1220 0 : if( FD_UNLIKELY( rc ) ) return rc;
1221 :
1222 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1177
1223 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 ) ) );
1224 0 : for( deq_fd_vote_lockout_t_iter_t iter =
1225 0 : deq_fd_vote_lockout_t_iter_init( vote_state_update->lockouts );
1226 0 : !deq_fd_vote_lockout_t_iter_done( vote_state_update->lockouts, iter );
1227 0 : iter = deq_fd_vote_lockout_t_iter_next( vote_state_update->lockouts, iter ) ) {
1228 0 : fd_vote_lockout_t * lockout =
1229 0 : deq_fd_vote_lockout_t_iter_ele( vote_state_update->lockouts, iter );
1230 0 : deq_fd_landed_vote_t_push_tail_wrap( landed_votes,
1231 0 : ( fd_landed_vote_t ){ .latency = 0, .lockout = *lockout } );
1232 0 : }
1233 :
1234 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1171
1235 0 : return process_new_vote_state(
1236 0 : ctx,
1237 0 : versioned,
1238 0 : landed_votes,
1239 0 : vote_state_update->has_root,
1240 0 : vote_state_update->root,
1241 0 : vote_state_update->has_timestamp,
1242 0 : vote_state_update->timestamp,
1243 0 : epoch,
1244 0 : slot
1245 0 : );
1246 0 : }
1247 :
1248 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L955-L979 */
1249 : static int
1250 : process_vote_state_update( fd_exec_instr_ctx_t * ctx,
1251 : fd_borrowed_account_t * vote_account,
1252 : int target_version,
1253 : fd_slot_hash_t const * slot_hashes,
1254 : fd_sol_sysvar_clock_t const * clock,
1255 : fd_vote_state_update_t * vote_state_update,
1256 0 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
1257 :
1258 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L963-L966 */
1259 0 : int rc = get_vote_state_handler_checked(
1260 0 : vote_account,
1261 0 : target_version,
1262 0 : 1,
1263 0 : ctx->runtime->vote_program.process_vote.vote_state_mem,
1264 0 : ctx->runtime->vote_program.process_vote.authorized_voters_mem,
1265 0 : ctx->runtime->vote_program.process_vote.landed_votes_mem
1266 0 : );
1267 0 : if( FD_UNLIKELY( rc ) ) return rc;
1268 :
1269 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.process_vote.vote_state_mem;
1270 :
1271 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L968 */
1272 0 : fd_pubkey_t * authorized_voter = NULL;
1273 0 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1274 0 : if( FD_UNLIKELY( rc ) ) return rc;
1275 :
1276 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L969 */
1277 0 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers );
1278 0 : if( FD_UNLIKELY( rc ) ) return rc;
1279 :
1280 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L971-L977 */
1281 0 : rc = do_process_vote_state_update(
1282 0 : ctx,
1283 0 : versioned,
1284 0 : slot_hashes,
1285 0 : clock->epoch,
1286 0 : clock->slot,
1287 0 : vote_state_update
1288 0 : );
1289 0 : if( FD_UNLIKELY( rc ) ) {
1290 0 : return rc;
1291 0 : }
1292 :
1293 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L978 */
1294 0 : return fd_vsv_set_vote_account_state(
1295 0 : ctx,
1296 0 : vote_account,
1297 0 : versioned,
1298 0 : ctx->runtime->vote_program.process_vote.vote_lockout_mem
1299 0 : );
1300 0 : }
1301 :
1302 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1035-L1061 */
1303 : static int
1304 : do_process_tower_sync( fd_exec_instr_ctx_t * ctx,
1305 : fd_vote_state_versioned_t * versioned,
1306 : fd_slot_hash_t const * slot_hashes, /* deque */
1307 : ulong epoch,
1308 : ulong slot,
1309 0 : fd_tower_sync_t * tower_sync ) {
1310 :
1311 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1042-L1048 */
1312 0 : int rc = check_and_filter_proposed_vote_state(
1313 0 : ctx,
1314 0 : versioned,
1315 0 : tower_sync->lockouts,
1316 0 : &tower_sync->has_root,
1317 0 : &tower_sync->root,
1318 0 : &tower_sync->hash,
1319 0 : slot_hashes
1320 0 : );
1321 0 : if( FD_UNLIKELY( rc ) ) return rc;
1322 :
1323 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1049-L1060 */
1324 0 : return process_new_vote_state(
1325 0 : ctx,
1326 0 : versioned,
1327 0 : fd_vote_lockout_landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ),
1328 0 : tower_sync->has_root,
1329 0 : tower_sync->root,
1330 0 : tower_sync->has_timestamp,
1331 0 : tower_sync->timestamp,
1332 0 : epoch,
1333 0 : slot
1334 0 : );
1335 0 : }
1336 :
1337 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1009-L1033 */
1338 : static int
1339 : process_tower_sync( fd_exec_instr_ctx_t * ctx,
1340 : fd_borrowed_account_t * vote_account,
1341 : int target_version,
1342 : fd_slot_hash_t const * slot_hashes, /* deque */
1343 : fd_sol_sysvar_clock_t const * clock,
1344 : fd_tower_sync_t * tower_sync,
1345 0 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
1346 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1017-L1020 */
1347 0 : int rc = get_vote_state_handler_checked(
1348 0 : vote_account,
1349 0 : target_version,
1350 0 : 1,
1351 0 : ctx->runtime->vote_program.tower_sync.vote_state_mem,
1352 0 : ctx->runtime->vote_program.tower_sync.authorized_voters_mem,
1353 0 : ctx->runtime->vote_program.tower_sync.vote_state_landed_votes_mem
1354 0 : );
1355 0 : if( FD_UNLIKELY( rc ) ) return rc;
1356 :
1357 0 : fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.tower_sync.vote_state_mem;
1358 :
1359 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1022 */
1360 0 : fd_pubkey_t * authorized_voter = NULL;
1361 0 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1362 0 : if( FD_UNLIKELY( rc ) ) return rc;
1363 :
1364 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1023 */
1365 0 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers );
1366 0 : if( FD_UNLIKELY( rc ) ) return rc;
1367 :
1368 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1025-L1031 */
1369 0 : rc = do_process_tower_sync(
1370 0 : ctx,
1371 0 : versioned,
1372 0 : slot_hashes,
1373 0 : clock->epoch,
1374 0 : clock->slot,
1375 0 : tower_sync
1376 0 : );
1377 0 : if( FD_UNLIKELY( rc ) ) return rc;
1378 :
1379 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1032 */
1380 0 : return fd_vsv_set_vote_account_state(
1381 0 : ctx,
1382 0 : vote_account,
1383 0 : versioned,
1384 0 : ctx->runtime->vote_program.tower_sync.vote_lockout_mem
1385 0 : );
1386 0 : }
1387 :
1388 : /**********************************************************************/
1389 : /* FD-only encoders / decoders (doesn't map directly to Labs impl) */
1390 : /**********************************************************************/
1391 :
1392 : int
1393 : fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update,
1394 : fd_vote_state_update_t * vote_update,
1395 0 : fd_exec_instr_ctx_t const * ctx ) {
1396 : // Taken from:
1397 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L954
1398 0 : if( compact_update->root != ULONG_MAX ) {
1399 0 : vote_update->has_root = 1;
1400 0 : vote_update->root = compact_update->root;
1401 0 : } else {
1402 0 : vote_update->has_root = 0;
1403 0 : vote_update->root = ULONG_MAX;
1404 0 : }
1405 :
1406 0 : ulong lockouts_len = compact_update->lockouts_len;
1407 0 : ulong lockouts_max = fd_ulong_max( lockouts_len, MAX_LOCKOUT_HISTORY );
1408 :
1409 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 ) );
1410 0 : ulong slot = fd_ulong_if( vote_update->has_root, vote_update->root, 0 );
1411 :
1412 0 : for( ulong i=0; i < lockouts_len; ++i ) {
1413 0 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( vote_update->lockouts );
1414 0 : fd_vote_lockout_new( elem );
1415 :
1416 0 : fd_lockout_offset_t * lock_offset = &compact_update->lockouts[i];
1417 :
1418 0 : ulong next_slot;
1419 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow( slot, lock_offset->offset, &next_slot ) ) )
1420 0 : return 0;
1421 :
1422 0 : elem->slot = slot = next_slot;
1423 0 : elem->confirmation_count = (uint)lock_offset->confirmation_count;
1424 0 : }
1425 :
1426 0 : vote_update->hash = compact_update->hash;
1427 0 : vote_update->has_timestamp = compact_update->has_timestamp;
1428 0 : vote_update->timestamp = compact_update->timestamp;
1429 :
1430 0 : return 1;
1431 0 : }
1432 :
1433 : /**********************************************************************/
1434 : /* mod vote_processor */
1435 : /**********************************************************************/
1436 :
1437 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21-L51 */
1438 : static int
1439 : process_authorize_with_seed_instruction( /* invoke_context */
1440 : fd_exec_instr_ctx_t * ctx,
1441 : int target_version,
1442 : fd_borrowed_account_t * vote_account,
1443 : fd_pubkey_t const * new_authority,
1444 : fd_vote_authorize_t authorization_type,
1445 : fd_pubkey_t const * current_authority_derived_key_owner,
1446 : uchar const * current_authority_derived_key_seed,
1447 0 : ulong current_authority_derived_key_seed_len ) {
1448 0 : int rc = 0;
1449 :
1450 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31 */
1451 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
1452 0 : if( FD_UNLIKELY( rc ) ) return rc;
1453 :
1454 0 : fd_sol_sysvar_clock_t clock_;
1455 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1456 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1457 :
1458 0 : fd_pubkey_t * expected_authority_keys[FD_TXN_SIG_MAX] = { 0 };
1459 0 : fd_pubkey_t single_signer = { 0 };
1460 :
1461 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L30-L42 */
1462 0 : if( fd_instr_acc_is_signer_idx( ctx->instr, 2, &rc ) ) {
1463 :
1464 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L31 */
1465 0 : fd_pubkey_t const * base_pubkey = NULL;
1466 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &base_pubkey );
1467 0 : if( FD_UNLIKELY( rc ) ) return rc;
1468 :
1469 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L34-L40 */
1470 0 : expected_authority_keys[0] = &single_signer;
1471 0 : rc = fd_pubkey_create_with_seed( ctx,
1472 0 : base_pubkey->uc,
1473 0 : (char const *)current_authority_derived_key_seed,
1474 0 : current_authority_derived_key_seed_len,
1475 0 : current_authority_derived_key_owner->uc,
1476 0 : /* insert */ expected_authority_keys[0]->uc );
1477 0 : if( FD_UNLIKELY( rc ) ) return rc;
1478 0 : }
1479 :
1480 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L43-L50 */
1481 0 : return authorize(
1482 0 : ctx,
1483 0 : vote_account,
1484 0 : target_version,
1485 0 : new_authority,
1486 0 : authorization_type,
1487 0 : (fd_pubkey_t const **)expected_authority_keys,
1488 0 : clock
1489 0 : );
1490 0 : }
1491 :
1492 : /**********************************************************************/
1493 : /* Entry point for the Vote Program */
1494 : /**********************************************************************/
1495 :
1496 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
1497 : int
1498 0 : fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) {
1499 : /* FD-specific init */
1500 0 : int rc = FD_EXECUTOR_INSTR_SUCCESS;
1501 :
1502 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
1503 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
1504 :
1505 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L64-L67 */
1506 0 : fd_guarded_borrowed_account_t me = {0};
1507 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me );
1508 0 : if( FD_UNLIKELY( !fd_pubkey_eq( fd_borrowed_account_get_owner( &me ), &fd_solana_vote_program_id ) ) ) {
1509 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
1510 0 : }
1511 :
1512 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L69-L74 */
1513 0 : int target_version = FD_FEATURE_ACTIVE_BANK( ctx->bank, vote_state_v4 ) ? VOTE_STATE_TARGET_VERSION_V4 : VOTE_STATE_TARGET_VERSION_V3;
1514 :
1515 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L69
1516 0 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { 0 };
1517 0 : fd_exec_instr_ctx_get_signers( ctx, signers );
1518 :
1519 0 : uchar __attribute__((aligned(alignof(fd_vote_instruction_t)))) vote_instruction_mem[ FD_VOTE_INSTRUCTION_FOOTPRINT ];
1520 0 : fd_vote_instruction_t * instruction = fd_bincode_decode_static_limited_deserialize(
1521 0 : vote_instruction,
1522 0 : vote_instruction_mem,
1523 0 : ctx->instr->data,
1524 0 : ctx->instr->data_sz,
1525 0 : FD_TXN_MTU,
1526 0 : NULL );
1527 0 : if( FD_UNLIKELY( !instruction ) ) {
1528 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1529 0 : }
1530 :
1531 : /* PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR LABS IMPL:
1532 : */
1533 0 : switch( instruction->discriminant ) {
1534 :
1535 : /* InitializeAccount
1536 : *
1537 : * Instruction:
1538 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L32
1539 : *
1540 : * Processor:
1541 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L71
1542 : */
1543 0 : case fd_vote_instruction_enum_initialize_account: {
1544 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L72
1545 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
1546 0 : if( FD_UNLIKELY( rc ) ) return rc;
1547 0 : fd_rent_t rent_;
1548 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
1549 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1550 :
1551 0 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &me ) <
1552 0 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &me ) ) ) )
1553 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1554 :
1555 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L76
1556 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
1557 0 : if( FD_UNLIKELY( rc ) ) return rc;
1558 0 : fd_sol_sysvar_clock_t clock_;
1559 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1560 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1561 :
1562 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L78
1563 0 : rc = initialize_account( ctx, &me, target_version, &instruction->inner.initialize_account, signers, clock );
1564 :
1565 0 : break;
1566 0 : }
1567 :
1568 : /* Authorize
1569 : *
1570 : * Instruction:
1571 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L40
1572 : *
1573 : * Processor:
1574 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L86
1575 : *
1576 : * Notes:
1577 : * - Up to two signers: the vote authority and the authorized withdrawer.
1578 : */
1579 0 : case fd_vote_instruction_enum_authorize: {
1580 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L87
1581 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
1582 0 : if( FD_UNLIKELY( rc ) ) return rc;
1583 0 : fd_sol_sysvar_clock_t clock_;
1584 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1585 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1586 :
1587 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L89
1588 0 : fd_pubkey_t const * voter_pubkey = &instruction->inner.authorize.pubkey;
1589 0 : fd_vote_authorize_t vote_authorize = instruction->inner.authorize.vote_authorize;
1590 :
1591 0 : rc = authorize( ctx, &me, target_version, voter_pubkey, vote_authorize, signers, clock );
1592 :
1593 0 : break;
1594 0 : }
1595 :
1596 : /* AuthorizeWithSeed
1597 : *
1598 : * Instruction:
1599 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L117
1600 : *
1601 : * Processor:
1602 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L98
1603 : */
1604 0 : case fd_vote_instruction_enum_authorize_with_seed: {
1605 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L99
1606 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 3 ) ) {
1607 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1608 0 : break;
1609 0 : }
1610 :
1611 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L100
1612 0 : fd_vote_authorize_with_seed_args_t * args = &instruction->inner.authorize_with_seed;
1613 :
1614 0 : rc = process_authorize_with_seed_instruction(
1615 0 : ctx,
1616 0 : target_version,
1617 0 : &me,
1618 0 : &args->new_authority,
1619 0 : args->authorization_type,
1620 0 : &args->current_authority_derived_key_owner,
1621 0 : args->current_authority_derived_key_seed,
1622 0 : args->current_authority_derived_key_seed_len
1623 0 : );
1624 :
1625 0 : break;
1626 0 : }
1627 :
1628 : /* AuthorizeCheckedWithSeed
1629 : *
1630 : * Instruction:
1631 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L131
1632 : *
1633 : * Processor:
1634 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L111
1635 : */
1636 0 : case fd_vote_instruction_enum_authorize_checked_with_seed: {
1637 0 : fd_vote_authorize_checked_with_seed_args_t const * args =
1638 0 : &instruction->inner.authorize_checked_with_seed;
1639 :
1640 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L112
1641 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
1642 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1643 0 : break;
1644 0 : }
1645 :
1646 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L99-L100
1647 0 : fd_pubkey_t const * new_authority = NULL;
1648 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &new_authority );
1649 0 : if( FD_UNLIKELY( rc ) ) return rc;
1650 :
1651 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L116
1652 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
1653 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1654 0 : if( FD_UNLIKELY( !!rc ) ) break;
1655 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L117
1656 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1657 0 : break;
1658 0 : }
1659 :
1660 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L119
1661 0 : rc = process_authorize_with_seed_instruction(
1662 0 : ctx,
1663 0 : target_version,
1664 0 : &me,
1665 0 : new_authority,
1666 0 : args->authorization_type,
1667 0 : &args->current_authority_derived_key_owner,
1668 0 : args->current_authority_derived_key_seed,
1669 0 : args->current_authority_derived_key_seed_len );
1670 :
1671 0 : break;
1672 0 : }
1673 :
1674 : /* UpdateValidatorIdentity
1675 : *
1676 : * Instruction:
1677 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L65
1678 : *
1679 : * Processor:
1680 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L130
1681 : */
1682 0 : case fd_vote_instruction_enum_update_validator_identity: {
1683 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L131
1684 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
1685 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1686 0 : break;
1687 0 : }
1688 :
1689 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L118-L120
1690 0 : fd_pubkey_t const * node_pubkey = NULL;
1691 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1UL, &node_pubkey );
1692 0 : if( FD_UNLIKELY( rc ) ) return rc;
1693 :
1694 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L135
1695 0 : rc = update_validator_identity( ctx, target_version, &me, node_pubkey, signers );
1696 :
1697 0 : break;
1698 0 : }
1699 :
1700 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L142
1701 0 : case fd_vote_instruction_enum_update_commission: {
1702 :
1703 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L149
1704 0 : fd_epoch_schedule_t epoch_schedule_;
1705 0 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, &epoch_schedule_ );
1706 0 : if( FD_UNLIKELY( !epoch_schedule ) ) {
1707 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1708 0 : }
1709 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L150
1710 :
1711 0 : fd_sol_sysvar_clock_t clock_;
1712 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1713 0 : if( FD_UNLIKELY( !clock ) ) {
1714 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1715 0 : }
1716 :
1717 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L145
1718 0 : rc = update_commission(
1719 0 : ctx,
1720 0 : target_version,
1721 0 : &me,
1722 0 : instruction->inner.update_commission,
1723 0 : signers,
1724 0 : epoch_schedule,
1725 0 : clock
1726 0 : );
1727 :
1728 0 : break;
1729 0 : }
1730 :
1731 : /* Vote
1732 : *
1733 : * Instruction:
1734 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L49
1735 : */
1736 0 : case fd_vote_instruction_enum_vote:;
1737 : /* clang-format off */
1738 0 : __attribute__((fallthrough));
1739 : /* clang-format on */
1740 :
1741 : /* VoteSwitch
1742 : *
1743 : * Instruction:
1744 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L81
1745 : *
1746 : * Processor:
1747 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L154
1748 : */
1749 0 : case fd_vote_instruction_enum_vote_switch: {
1750 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
1751 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1752 0 : }
1753 :
1754 0 : fd_vote_t * vote;
1755 0 : if( instruction->discriminant == fd_vote_instruction_enum_vote ) {
1756 0 : vote = &instruction->inner.vote;
1757 0 : } else if( instruction->discriminant == fd_vote_instruction_enum_vote_switch ) {
1758 0 : vote = &instruction->inner.vote_switch.vote;
1759 0 : } else {
1760 0 : FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant ));
1761 0 : }
1762 :
1763 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L155
1764 0 : int err;
1765 0 : err = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_slot_hashes_id );
1766 0 : if( FD_UNLIKELY( err ) ) return err;
1767 :
1768 0 : if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
1769 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1770 0 : }
1771 :
1772 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L157
1773 0 : err = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
1774 0 : if( FD_UNLIKELY( err ) ) return err;
1775 0 : fd_sol_sysvar_clock_t clock_;
1776 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1777 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1778 :
1779 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
1780 0 : rc = process_vote_with_account(
1781 0 : ctx,
1782 0 : &me,
1783 0 : target_version,
1784 0 : slot_hashes,
1785 0 : clock,
1786 0 : vote,
1787 0 : signers
1788 0 : );
1789 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
1790 :
1791 0 : break;
1792 0 : }
1793 :
1794 : /* UpdateVoteState
1795 : *
1796 : * Instruction:
1797 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L100
1798 : */
1799 0 : case fd_vote_instruction_enum_update_vote_state:;
1800 : /* clang-format off */
1801 0 : __attribute__((fallthrough));
1802 : /* clang-format on */
1803 :
1804 : /* UpdateVoteStateSwitch
1805 : *
1806 : * Instruction:
1807 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L107
1808 : *
1809 : * Processor:
1810 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L169
1811 : */
1812 0 : case fd_vote_instruction_enum_update_vote_state_switch: {
1813 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
1814 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1815 0 : }
1816 :
1817 0 : fd_vote_state_update_t * vote_state_update;
1818 0 : switch( instruction->discriminant ) {
1819 0 : case fd_vote_instruction_enum_update_vote_state:
1820 0 : vote_state_update = &instruction->inner.update_vote_state;
1821 0 : break;
1822 0 : case fd_vote_instruction_enum_update_vote_state_switch:
1823 0 : vote_state_update = &instruction->inner.update_vote_state_switch.vote_state_update;
1824 0 : break;
1825 0 : default:
1826 0 : FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant ));
1827 0 : }
1828 :
1829 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L171
1830 0 : if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
1831 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1832 0 : }
1833 :
1834 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L172
1835 0 : fd_sol_sysvar_clock_t clock_;
1836 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1837 0 : if( FD_UNLIKELY( !clock ) )
1838 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1839 :
1840 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L173
1841 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
1842 0 : rc = process_vote_state_update(
1843 0 : ctx,
1844 0 : &me,
1845 0 : target_version,
1846 0 : slot_hashes,
1847 0 : clock,
1848 0 : vote_state_update,
1849 0 : signers
1850 0 : );
1851 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
1852 :
1853 0 : break;
1854 0 : }
1855 :
1856 : /* CompactUpdateVoteState
1857 : *
1858 : * Instruction:
1859 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L139
1860 : *
1861 : * Notes:
1862 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
1863 : * - Feature gated, but live on mainnet.
1864 : */
1865 0 : case fd_vote_instruction_enum_compact_update_vote_state:;
1866 0 : __attribute__((fallthrough));
1867 :
1868 : /* CompactUpdateVoteStateSwitch
1869 : *
1870 : * Instruction:
1871 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L146
1872 : *
1873 : * Processor:
1874 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L183
1875 : *
1876 : * Notes:
1877 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
1878 : * - Feature gated, but live on mainnet.
1879 : */
1880 0 : case fd_vote_instruction_enum_compact_update_vote_state_switch: {
1881 : /* https://github.com/anza-xyz/agave/blob/dc4b9dcbbf859ff48f40d00db824bde063fdafcc/programs/vote/src/vote_processor.rs#L183-L191 */
1882 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
1883 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1884 0 : }
1885 :
1886 0 : fd_compact_vote_state_update_t * vote_state_update = NULL;
1887 0 : if( instruction->discriminant == fd_vote_instruction_enum_compact_update_vote_state ) {
1888 0 : vote_state_update = &instruction->inner.compact_update_vote_state;
1889 0 : } else if( instruction->discriminant ==
1890 0 : fd_vote_instruction_enum_compact_update_vote_state_switch ) {
1891 0 : vote_state_update =
1892 0 : &instruction->inner.compact_update_vote_state_switch.compact_vote_state_update;
1893 0 : }
1894 :
1895 0 : fd_vote_state_update_t vote_update;
1896 0 : fd_vote_state_update_new( &vote_update );
1897 0 : if( FD_UNLIKELY( !fd_vote_decode_compact_update( vote_state_update, &vote_update, ctx ) ) )
1898 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1899 :
1900 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L185
1901 0 : if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
1902 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1903 0 : }
1904 :
1905 0 : fd_sol_sysvar_clock_t clock_;
1906 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1907 0 : if( FD_UNLIKELY( !clock ) )
1908 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1909 :
1910 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L187
1911 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
1912 0 : rc = process_vote_state_update(
1913 0 : ctx,
1914 0 : &me,
1915 0 : target_version,
1916 0 : slot_hashes,
1917 0 : clock,
1918 0 : &vote_update,
1919 0 : signers
1920 0 : );
1921 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
1922 :
1923 0 : break;
1924 0 : }
1925 :
1926 : /* TowerSync(Switch)
1927 : *
1928 : * Instruction:
1929 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L151-L157
1930 : *
1931 : * Processor:
1932 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L196-L215
1933 : */
1934 :
1935 0 : case fd_vote_instruction_enum_tower_sync:
1936 0 : case fd_vote_instruction_enum_tower_sync_switch: {
1937 0 : fd_tower_sync_t * tower_sync = (instruction->discriminant == fd_vote_instruction_enum_tower_sync)
1938 0 : ? &instruction->inner.tower_sync
1939 0 : : &instruction->inner.tower_sync_switch.tower_sync;
1940 :
1941 0 : if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
1942 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1943 0 : }
1944 :
1945 0 : fd_sol_sysvar_clock_t clock_;
1946 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1947 0 : if( FD_UNLIKELY( !clock ) ) {
1948 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1949 0 : }
1950 :
1951 0 : fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
1952 0 : FD_TEST( slot_hashes );
1953 0 : rc = process_tower_sync(
1954 0 : ctx,
1955 0 : &me,
1956 0 : target_version,
1957 0 : slot_hashes,
1958 0 : clock,
1959 0 : tower_sync,
1960 0 : signers
1961 0 : );
1962 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
1963 :
1964 0 : break;
1965 0 : }
1966 :
1967 : /* Withdraw
1968 : *
1969 : * Instruction:
1970 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L57
1971 : *
1972 : * Processor:
1973 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L216
1974 : */
1975 0 : case fd_vote_instruction_enum_withdraw: {
1976 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
1977 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1978 0 : break;
1979 0 : }
1980 0 : fd_rent_t rent_;
1981 0 : fd_rent_t const * rent_sysvar = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
1982 0 : if( FD_UNLIKELY( !rent_sysvar ) )
1983 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1984 0 : fd_sol_sysvar_clock_t clock_;
1985 0 : fd_sol_sysvar_clock_t const * clock_sysvar = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1986 0 : if( FD_UNLIKELY( !clock_sysvar ) )
1987 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1988 :
1989 0 : rc = withdraw(
1990 0 : ctx,
1991 0 : &me,
1992 0 : target_version,
1993 0 : instruction->inner.withdraw,
1994 0 : 1UL,
1995 0 : signers,
1996 0 : rent_sysvar,
1997 0 : clock_sysvar
1998 0 : );
1999 :
2000 0 : break;
2001 0 : }
2002 :
2003 : /* AuthorizeChecked
2004 : *
2005 : * Instruction:
2006 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L93
2007 : *
2008 : * Processor:
2009 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L234
2010 : *
2011 : * Notes:
2012 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2013 : * - Feature gated, but live on mainnet.
2014 : */
2015 0 : case fd_vote_instruction_enum_authorize_checked: {
2016 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
2017 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2018 0 : break;
2019 0 : }
2020 :
2021 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L243-L245
2022 0 : fd_pubkey_t const * voter_pubkey = NULL;
2023 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &voter_pubkey );
2024 0 : if( FD_UNLIKELY( rc ) ) return rc;
2025 :
2026 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L239
2027 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
2028 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
2029 0 : if( FD_UNLIKELY( !!rc ) ) break;
2030 :
2031 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2032 0 : break;
2033 0 : }
2034 :
2035 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L242
2036 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2037 0 : if( FD_UNLIKELY( rc ) ) return rc;
2038 0 : fd_sol_sysvar_clock_t clock_;
2039 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2040 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2041 :
2042 0 : rc = authorize(
2043 0 : ctx,
2044 0 : &me,
2045 0 : target_version,
2046 0 : voter_pubkey,
2047 0 : instruction->inner.authorize_checked,
2048 0 : signers,
2049 0 : clock
2050 0 : );
2051 0 : break;
2052 0 : }
2053 :
2054 0 : default:
2055 0 : FD_LOG_CRIT(( "unsupported vote instruction: %u", instruction->discriminant ));
2056 0 : }
2057 :
2058 0 : return rc;
2059 0 : }
2060 :
2061 : /**********************************************************************/
2062 : /* Public API */
2063 : /**********************************************************************/
2064 :
2065 : /* TODO: Old code, remove whenever stake program gets cleaned up */
2066 : void
2067 : fd_vote_convert_to_current( fd_vote_state_versioned_t * self,
2068 : uchar * authorized_voters_mem,
2069 0 : uchar * landed_votes_mem ) {
2070 0 : fd_vsv_try_convert_to_v3( self, authorized_voters_mem, landed_votes_mem );
2071 0 : }
2072 :
2073 : fd_vote_state_versioned_t *
2074 : fd_vote_get_state( fd_account_meta_t const * self,
2075 0 : uchar * mem ) {
2076 0 : int err = fd_vsv_get_state( self, mem );
2077 0 : return err ? NULL : (fd_vote_state_versioned_t *)mem;
2078 0 : }
|