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_cache.h"
8 : #include "../sysvar/fd_sysvar.h"
9 : #include "../fd_system_ids.h"
10 : #include "vote/fd_authorized_voters.h"
11 : #include "vote/fd_vote_utils.h"
12 : #include "vote/fd_vote_codec_tmpl.h"
13 : #include "vote/fd_vote_codec.h"
14 : #include "vote/fd_vote_state_versioned.h"
15 : #include "vote/fd_vote_state_v4.h"
16 : #include "../../../ballet/bls/fd_bls12_381.h"
17 :
18 : #include <limits.h>
19 : #include <math.h>
20 : #include <stdio.h>
21 : #include <string.h>
22 :
23 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
24 416 : #define INITIAL_LOCKOUT 2UL
25 :
26 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L51
27 : #define VOTE_CREDITS_MAXIMUM_PER_SLOT_OLD 8
28 :
29 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
30 : #define SLOT_DEFAULT 0UL
31 :
32 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
33 : #define SLOT_MAX ULONG_MAX
34 :
35 : #define ACCOUNTS_MAX 4 /* Vote instructions take in at most 4 accounts */
36 :
37 : #define DEFAULT_COMPUTE_UNITS 2100UL
38 : #define COMPUTE_UNITS_POP 34500UL
39 :
40 : /**********************************************************************/
41 : /* VoteStateHandler */
42 : /**********************************************************************/
43 :
44 : /* Check the vote state, deserialize it as V4 and convert older versions
45 : to V4 in-place.
46 :
47 : If the vote account is a V5 account, it is deserialized as a V5
48 : account. Non-V5 accounts are not automatically upgraded, because V5
49 : accounts require the location to be set.
50 :
51 : https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/mod.rs#L45-L77 */
52 : static int
53 : get_vote_state_handler_checked( fd_borrowed_account_t const * vote_account,
54 : int target_version,
55 : uchar check_initialized,
56 3849 : fd_vote_state_versioned_t * vote_state_versioned ) {
57 3849 : (void)target_version;
58 3849 : (void)check_initialized; /* V4 vote states are always initialized */
59 :
60 3849 : int rc = fd_vsv_deserialize( vote_account->acc, vote_state_versioned );
61 3849 : if( FD_UNLIKELY( rc ) ) return rc;
62 :
63 2663 : if( FD_UNLIKELY( fd_vsv_is_uninitialized( vote_state_versioned ) ) ) {
64 69 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
65 69 : }
66 :
67 2600 : if( vote_state_versioned->kind!=fd_vote_state_versioned_enum_v5 ) {
68 2600 : rc = fd_vsv_try_convert_to_v4( vote_state_versioned, (fd_pubkey_t*)vote_account->acc->pubkey );
69 2600 : if( FD_UNLIKELY( rc ) ) return rc;
70 2600 : }
71 :
72 2594 : return FD_EXECUTOR_INSTR_SUCCESS;
73 2594 : }
74 :
75 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/handler.rs#L379-L393 */
76 : static int
77 : check_vote_account_length( fd_borrowed_account_t const * vote_account,
78 185 : int target_version ) {
79 185 : (void)target_version;
80 185 : ulong length = fd_borrowed_account_get_data_len( vote_account );
81 185 : if( FD_UNLIKELY( length!=FD_VOTE_STATE_V4_SZ ) ) {
82 18 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
83 18 : }
84 167 : return FD_EXECUTOR_INSTR_SUCCESS;
85 185 : }
86 :
87 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/handler.rs#L327-L342 */
88 : static int
89 : init_vote_account_state( fd_exec_instr_ctx_t * ctx,
90 : fd_borrowed_account_t * vote_account,
91 : fd_vote_state_versioned_t * versioned,
92 : int target_version,
93 : fd_vote_init_t * vote_init,
94 165 : fd_sol_sysvar_clock_t const * clock ) {
95 165 : (void)target_version;
96 165 : fd_vote_state_v4_create_new_with_defaults( (fd_pubkey_t*)vote_account->acc->pubkey, vote_init, clock, versioned );
97 165 : return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned );
98 165 : }
99 :
100 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/handler.rs#L344-L362 */
101 : static int
102 : init_vote_account_state_v2( fd_exec_instr_ctx_t * ctx,
103 : fd_borrowed_account_t * vote_account,
104 : fd_vote_state_versioned_t * versioned,
105 : int target_version,
106 : fd_vote_init_v2_t * vote_init_v2,
107 0 : fd_sol_sysvar_clock_t const * clock ) {
108 0 : (void)target_version;
109 0 : fd_vote_state_v4_create_new( vote_init_v2, clock, versioned );
110 0 : return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned );
111 0 : }
112 :
113 : /**********************************************************************/
114 : /* mod vote_state */
115 : /**********************************************************************/
116 :
117 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L82-L324 */
118 : static int
119 : check_and_filter_proposed_vote_state( fd_exec_instr_ctx_t * ctx,
120 : fd_vote_state_versioned_t * versioned,
121 : fd_vote_lockout_t * proposed_lockouts,
122 : uchar * proposed_has_root,
123 : ulong * proposed_root,
124 : fd_hash_t const * proposed_hash,
125 377 : fd_slot_hashes_t const * slot_hashes ) {
126 377 : fd_landed_vote_t const * votes = fd_vsv_get_votes( versioned );
127 377 : ulong const * root_slot = fd_vsv_get_root_slot( versioned );
128 377 : uchar has_root_slot = !!(root_slot!=NULL);
129 :
130 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L208
131 377 : if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) {
132 6 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
133 6 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
134 6 : }
135 371 : fd_landed_vote_t const * last_vote = NULL;
136 371 : if( !deq_fd_landed_vote_t_empty( votes ) ) {
137 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L212
138 346 : last_vote = deq_fd_landed_vote_t_peek_tail_const( votes );
139 346 : }
140 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L218
141 371 : if( FD_LIKELY( last_vote ) ) {
142 345 : if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot <=
143 345 : last_vote->lockout.slot ) ) {
144 2 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
145 2 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
146 2 : }
147 345 : }
148 :
149 : /* must be nonempty, checked above */
150 369 : ulong last_vote_state_update_slot = deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot;
151 :
152 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L224
153 369 : if( FD_UNLIKELY( !slot_hashes->cnt ) ) {
154 7 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
155 7 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
156 7 : }
157 :
158 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L227
159 362 : ulong earliest_slot_hash_in_history = slot_hashes->elems[ slot_hashes->cnt-1UL ].slot;
160 :
161 : /* Check if the proposed vote is too old to be in the SlotHash history */
162 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L230
163 362 : if( FD_UNLIKELY( last_vote_state_update_slot < earliest_slot_hash_in_history ) ) {
164 6 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
165 6 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
166 6 : }
167 :
168 : /* Check if the proposed root is too old */
169 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L237
170 356 : if( *proposed_has_root ) {
171 121 : ulong const proposed_root_ = *proposed_root;
172 : /* If the new proposed root `R` is less than the earliest slot hash in the history
173 : such that we cannot verify whether the slot was actually was on this fork, set
174 : the root to the latest vote in the current vote that's less than R. */
175 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L242
176 121 : if( proposed_root_ < earliest_slot_hash_in_history ) {
177 108 : *proposed_has_root = has_root_slot;
178 108 : if( has_root_slot ) {
179 57 : *proposed_root = *root_slot;
180 57 : }
181 108 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init_rev( votes );
182 714 : !deq_fd_landed_vote_t_iter_done_rev( votes, iter );
183 684 : iter = deq_fd_landed_vote_t_iter_prev( votes, iter ) ) {
184 : /* Ensure we're iterating from biggest to smallest vote in the
185 : current vote state */
186 684 : fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
187 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L248
188 684 : if( vote->lockout.slot <= proposed_root_ ) {
189 78 : *proposed_has_root = 1;
190 78 : *proposed_root = vote->lockout.slot;
191 78 : break;
192 78 : }
193 684 : }
194 108 : }
195 121 : }
196 :
197 : /* Index into the new proposed vote state's slots, starting with the root if it exists then
198 : we use this mutable root to fold checking the root slot into the below loop for performance */
199 356 : int has_root_to_check = *proposed_has_root;
200 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L259
201 356 : ulong root_to_check = *proposed_root;
202 356 : ulong proposed_lockouts_index = 0UL;
203 356 : ulong lockouts_len = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
204 :
205 : /* Index into the slot_hashes, starting at the oldest known slot hash */
206 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L264
207 356 : ulong slot_hashes_index = slot_hashes->cnt;
208 :
209 : /* proposed_lockouts_indexes_to_filter's size is bounded by the length
210 : of the proposed lockouts provided in the instruction data. */
211 356 : ulong proposed_lockouts_indexes_to_filter[ FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN ];
212 356 : ulong filter_index = 0UL;
213 :
214 : /* Note:
215 :
216 : 1) `vote_state_update.lockouts` is sorted from oldest/smallest vote to newest/largest
217 : vote, due to the way votes are applied to the vote state (newest votes
218 : pushed to the back).
219 :
220 : 2) Conversely, `slot_hashes` is sorted from newest/largest vote to
221 : the oldest/smallest vote.
222 :
223 : Unlike for vote updates, vote state updates here can't only check votes older than the last vote
224 : because have to ensure that every slot is actually part of the history, not just the most
225 : recent ones */
226 :
227 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L279
228 40850 : while( proposed_lockouts_index < lockouts_len && slot_hashes_index > 0 ) {
229 40500 : ulong proposed_vote_slot =
230 40500 : fd_ulong_if( has_root_to_check,
231 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L281
232 40500 : root_to_check,
233 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L283
234 40500 : deq_fd_vote_lockout_t_peek_index_const( proposed_lockouts,
235 40500 : proposed_lockouts_index )
236 40500 : ->slot );
237 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L285
238 40500 : if( !has_root_to_check && proposed_lockouts_index > 0UL &&
239 40500 : proposed_vote_slot <=
240 34967 : deq_fd_vote_lockout_t_peek_index_const(
241 34967 : proposed_lockouts,
242 34967 : fd_ulong_checked_sub_expect(
243 34967 : proposed_lockouts_index,
244 34967 : 1,
245 34967 : "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`" ) )
246 34967 : ->slot ) {
247 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L293
248 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
249 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
250 0 : }
251 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L295
252 40500 : ulong ancestor_slot =
253 40500 : slot_hashes->elems[
254 40500 : fd_ulong_checked_sub_expect(
255 40500 : slot_hashes_index,
256 40500 : 1UL,
257 40500 : "`slot_hashes_index` is positive when computing `ancestor_slot`" ) ]
258 40500 : .slot;
259 : /* Find if this slot in the proposed vote state exists in the SlotHashes history
260 : to confirm if it was a valid ancestor on this fork */
261 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L303
262 40500 : if( proposed_vote_slot < ancestor_slot ) {
263 15693 : if( slot_hashes_index == slot_hashes->cnt ) {
264 : /* The vote slot does not exist in the SlotHashes history because it's too old,
265 : i.e. older than the oldest slot in the history. */
266 15687 : if( proposed_vote_slot >= earliest_slot_hash_in_history ) {
267 0 : ctx->txn_out->err.custom_err = 0;
268 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
269 0 : }
270 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L310
271 15687 : if( !fd_vote_contains_slot( votes, proposed_vote_slot ) && !has_root_to_check ) {
272 : /* If the vote slot is both:
273 : 1) Too old
274 : 2) Doesn't already exist in vote state
275 : Then filter it out */
276 15495 : proposed_lockouts_indexes_to_filter[filter_index++] = proposed_lockouts_index; }
277 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L318
278 15687 : if( has_root_to_check ) {
279 85 : ulong new_proposed_root = root_to_check;
280 : /* 1. Because `root_to_check.is_some()`, then we know that
281 : we haven't checked the root yet in this loop, so
282 : `proposed_vote_slot` == `new_proposed_root` == `vote_state_update.root` */
283 85 : FD_TEST( new_proposed_root == proposed_vote_slot );
284 : /* 2. We know from the assert earlier in the function that
285 : `proposed_vote_slot < earliest_slot_hash_in_history`,
286 : so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history` */
287 85 : if( new_proposed_root >= earliest_slot_hash_in_history ) {
288 0 : ctx->txn_out->err.custom_err = 0;
289 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
290 0 : }
291 :
292 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L329
293 85 : has_root_to_check = 0;
294 85 : root_to_check = ULONG_MAX;
295 15602 : } else {
296 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L331
297 15602 : proposed_lockouts_index = fd_ulong_checked_add_expect(
298 15602 : proposed_lockouts_index,
299 15602 : 1,
300 15602 : "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when "
301 15602 : "`proposed_vote_slot` is too old to be in SlotHashes history" );
302 15602 : }
303 15687 : continue;
304 15687 : } else {
305 : /* If the vote slot is new enough to be in the slot history,
306 : but is not part of the slot history, then it must belong to another fork,
307 : which means this vote state update is invalid. */
308 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L340
309 6 : if( has_root_to_check ) {
310 3 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ON_DIFFERENT_FORK;
311 3 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
312 3 : } else {
313 3 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
314 3 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
315 3 : }
316 6 : }
317 24807 : } else if( proposed_vote_slot > ancestor_slot ) {
318 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L347
319 :
320 : /* Decrement `slot_hashes_index` to find newer slots in the SlotHashes history */
321 24177 : slot_hashes_index = fd_ulong_checked_sub_expect(
322 24177 : slot_hashes_index,
323 24177 : 1,
324 24177 : "`slot_hashes_index` is positive when finding newer slots in SlotHashes history" );
325 24177 : continue;
326 24177 : } else {
327 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L354
328 :
329 : /* Once the slot in `vote_state_update.lockouts` is found, bump to the next slot
330 : in `vote_state_update.lockouts` and continue. If we were checking the root,
331 : start checking the vote state instead. */
332 630 : if( has_root_to_check ) {
333 12 : has_root_to_check = 0;
334 12 : root_to_check = ULONG_MAX;
335 618 : } else {
336 618 : proposed_lockouts_index = fd_ulong_checked_add_expect(
337 618 : proposed_lockouts_index,
338 618 : 1,
339 618 : "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` "
340 618 : "when match is found in SlotHashes history" );
341 618 : slot_hashes_index = fd_ulong_checked_sub_expect(
342 618 : slot_hashes_index,
343 618 : 1,
344 618 : "`slot_hashes_index` is positive when match is found in SlotHashes history" );
345 618 : }
346 630 : }
347 40500 : }
348 :
349 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L372
350 350 : if( proposed_lockouts_index != deq_fd_vote_lockout_t_cnt( proposed_lockouts ) ) {
351 : /* The last vote slot in the update did not exist in SlotHashes */
352 6 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
353 6 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
354 6 : }
355 :
356 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L401
357 344 : if( memcmp( &slot_hashes->elems[ slot_hashes_index ].hash,
358 344 : proposed_hash,
359 344 : sizeof( fd_hash_t ) ) != 0 ) {
360 : /* This means the newest vote in the slot has a match that
361 : doesn't match the expected hash for that slot on this fork */
362 2 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
363 2 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
364 2 : }
365 :
366 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L418
367 : /* Filter out the irrelevant votes */
368 342 : proposed_lockouts_index = 0UL;
369 342 : ulong filter_votes_index = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
370 :
371 : /* We need to iterate backwards here because proposed_lockouts_indexes_to_filter[ i ] is a
372 : strictly increasing value. Forward iterating can lead to the proposed lockout indices to get
373 : shifted leading to popping the wrong proposed lockouts or out of bounds accessing. We need
374 : to be sure of handling underflow in this case. */
375 :
376 15856 : for( ulong i=filter_index; i>0UL && filter_votes_index>0UL; i-- ) {
377 15514 : proposed_lockouts_index = i - 1UL;
378 15514 : if( FD_UNLIKELY( proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ]>=filter_votes_index ) ) {
379 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
380 0 : }
381 :
382 15514 : deq_fd_vote_lockout_t_pop_idx_tail( proposed_lockouts, proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ] );
383 15514 : filter_votes_index--;
384 15514 : }
385 :
386 342 : return FD_EXECUTOR_INSTR_SUCCESS;
387 342 : }
388 :
389 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L440
390 : static int
391 : check_slots_are_valid( fd_exec_instr_ctx_t * ctx,
392 : fd_vote_state_versioned_t * versioned,
393 : ulong const * vote_slots,
394 : fd_hash_t const * vote_hash,
395 335 : fd_slot_hashes_t const * slot_hashes ) {
396 335 : ulong i = 0;
397 335 : ulong j = slot_hashes->cnt;
398 335 : ulong vote_slots_len = deq_ulong_cnt( vote_slots );
399 :
400 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L462
401 18888 : while( i < vote_slots_len && j > 0 ) {
402 18553 : ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( versioned );
403 18553 : if( FD_UNLIKELY( last_voted_slot_ &&
404 18553 : *deq_ulong_peek_index_const( vote_slots, i ) <= *last_voted_slot_ ) ) {
405 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L469
406 52 : i = fd_ulong_checked_add_expect(
407 52 : i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots" );
408 52 : continue;
409 52 : }
410 :
411 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L476
412 18501 : if( FD_UNLIKELY(
413 18501 : *deq_ulong_peek_index_const( vote_slots, i ) !=
414 18501 : slot_hashes->elems[
415 18501 : fd_ulong_checked_sub_expect( j, 1, "`j` is positive" ) ]
416 18501 : .slot ) ) {
417 2525 : j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when finding newer slots" );
418 2525 : continue;
419 2525 : }
420 :
421 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L486
422 15976 : i = fd_ulong_checked_add_expect(
423 15976 : i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found" );
424 15976 : j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when hash is found" );
425 15976 : }
426 :
427 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L494
428 335 : if( FD_UNLIKELY( j == slot_hashes->cnt ) ) {
429 4 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
430 4 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
431 4 : }
432 331 : if( FD_UNLIKELY( i != vote_slots_len ) ) {
433 18 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
434 18 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
435 18 : }
436 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L514
437 313 : if( FD_UNLIKELY( 0 != memcmp( &slot_hashes->elems[ j ].hash,
438 313 : vote_hash,
439 313 : 32UL ) ) ) {
440 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
441 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
442 0 : }
443 313 : return 0;
444 313 : }
445 :
446 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L565
447 : static int
448 : process_new_vote_state( fd_exec_instr_ctx_t * ctx,
449 : fd_vote_state_versioned_t * versioned,
450 : fd_landed_vote_t * new_state,
451 : int has_new_root,
452 : ulong new_root,
453 : int has_timestamp,
454 : long timestamp,
455 : ulong epoch,
456 342 : ulong current_slot ) {
457 342 : int rc;
458 342 : fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( versioned );
459 342 : ulong const * root_slot = fd_vsv_get_root_slot( versioned );
460 342 : uchar has_root_slot = !!(root_slot!=NULL);
461 :
462 342 : FD_TEST( !deq_fd_landed_vote_t_empty( new_state ) );
463 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L575
464 342 : if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( new_state ) > MAX_LOCKOUT_HISTORY ) ) {
465 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_MANY_VOTES;
466 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
467 342 : };
468 :
469 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L579
470 342 : if( FD_UNLIKELY( has_new_root && has_root_slot ) ) {
471 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L581
472 61 : if( FD_UNLIKELY( new_root<*root_slot ) ) {
473 31 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
474 31 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
475 31 : }
476 281 : } else if( FD_UNLIKELY( !has_new_root && has_root_slot ) ) {
477 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L586
478 101 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
479 101 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
480 180 : } else {
481 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L588
482 : /* no-op */
483 180 : }
484 :
485 210 : fd_landed_vote_t * previous_vote = NULL;
486 210 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
487 830 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
488 632 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
489 632 : fd_landed_vote_t * vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
490 632 : if( FD_LIKELY( vote->lockout.confirmation_count == 0 ) ) {
491 7 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ZERO_CONFIRMATIONS;
492 7 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
493 625 : } else if( FD_UNLIKELY( vote->lockout.confirmation_count > MAX_LOCKOUT_HISTORY ) ) {
494 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_TOO_LARGE;
495 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
496 625 : } else if( FD_LIKELY( has_new_root ) ) {
497 480 : if( FD_UNLIKELY( vote->lockout.slot <= new_root && new_root != SLOT_DEFAULT ) ) {
498 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOT_SMALLER_THAN_ROOT;
499 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
500 0 : }
501 480 : }
502 :
503 625 : if( FD_LIKELY( previous_vote ) ) {
504 422 : if( FD_UNLIKELY( previous_vote->lockout.slot >= vote->lockout.slot ) ) {
505 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
506 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
507 422 : } else if( FD_UNLIKELY( previous_vote->lockout.confirmation_count <=
508 422 : vote->lockout.confirmation_count ) ) {
509 5 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATIONS_NOT_ORDERED;
510 5 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
511 417 : } else if( FD_UNLIKELY( vote->lockout.slot >
512 417 : fd_vote_lockout_last_locked_out_slot( &previous_vote->lockout ) ) ) {
513 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_NEW_VOTE_STATE_LOCKOUT_MISMATCH;
514 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
515 0 : }
516 422 : }
517 620 : previous_vote = vote;
518 620 : }
519 :
520 198 : ulong current_vote_state_index = 0;
521 198 : ulong new_vote_state_index = 0;
522 :
523 : /* Accumulate credits earned by newly rooted slots. The behavior changes with
524 : timely_vote_credits: prior to this feature, there was a bug that counted a new root slot as 1
525 : credit even if it had never been voted on. timely_vote_credits fixes this bug by only awarding
526 : credits for slots actually voted on and finalized. */
527 :
528 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L635
529 :
530 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L641
531 198 : ulong earned_credits = 0;
532 :
533 198 : if( FD_LIKELY( has_new_root ) ) {
534 63 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
535 158 : !deq_fd_landed_vote_t_iter_done( votes, iter );
536 157 : iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
537 157 : fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
538 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L647
539 157 : if( FD_UNLIKELY( current_vote->lockout.slot <= new_root ) ) {
540 : // this is safe because we're inside if has_new_root
541 95 : earned_credits = fd_ulong_checked_add_expect(
542 95 : fd_vote_credits_for_vote_at_index(
543 95 : votes,
544 95 : current_vote_state_index
545 95 : ),
546 95 : earned_credits,
547 95 : "`earned_credits` does not overflow" );
548 95 : current_vote_state_index = fd_ulong_checked_add_expect(
549 95 : current_vote_state_index,
550 95 : 1,
551 95 : "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
552 95 : "when processing new root" );
553 95 : continue;
554 95 : }
555 62 : break;
556 157 : }
557 63 : }
558 :
559 : // For any slots newly added to the new vote state, the vote latency of that slot is not provided by the
560 : // vote instruction contents, but instead is computed from the actual latency of the vote
561 : // instruction. This prevents other validators from manipulating their own vote latencies within their vote states
562 : // and forcing the rest of the cluster to accept these possibly fraudulent latency values. If the
563 : // timly_vote_credits feature is not enabled then vote latency is set to 0 for new votes.
564 : //
565 : // For any slot that is in both the new state and the current state, the vote latency of the new state is taken
566 : // from the current state.
567 : //
568 : // Thus vote latencies are set here for any newly vote-on slots when a vote instruction is received.
569 : // They are copied into the new vote state after every vote for already voted-on slots.
570 : // And when voted-on slots are rooted, the vote latencies stored in the vote state of all the rooted slots is used
571 : // to compute credits earned.
572 : // All validators compute the same vote latencies because all process the same vote instruction at the
573 : // same slot, and the only time vote latencies are ever computed is at the time that their slot is first voted on;
574 : // after that, the latencies are retained unaltered until the slot is rooted.
575 :
576 : // All the votes in our current vote state that are missing from the new vote state
577 : // must have been expired by later votes. Check that the lockouts match this assumption.
578 :
579 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L686
580 1100 : while( current_vote_state_index < deq_fd_landed_vote_t_cnt( votes ) &&
581 1100 : new_vote_state_index < deq_fd_landed_vote_t_cnt( new_state ) ) {
582 919 : fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index );
583 :
584 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L690
585 919 : fd_landed_vote_t * new_vote =
586 919 : deq_fd_landed_vote_t_peek_index( new_state, new_vote_state_index );
587 :
588 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L696
589 919 : if( FD_LIKELY( current_vote->lockout.slot < new_vote->lockout.slot ) ) {
590 : /* The agave implementation of calculating the last locked out
591 : slot does not calculate a min between the current vote's
592 : confirmation count and max lockout history. The reason we do
593 : this is to make sure that the fuzzers continue working:
594 : the max lockout history can not be > MAX_LOCKOUT_HISTORY. */
595 416 : ulong confirmation_count = fd_ulong_min( current_vote->lockout.confirmation_count, MAX_LOCKOUT_HISTORY );
596 416 : ulong last_locked_out_slot = fd_ulong_sat_add( current_vote->lockout.slot,
597 416 : (ulong)pow( INITIAL_LOCKOUT, (double)confirmation_count ) );
598 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L697
599 416 : if( last_locked_out_slot >= new_vote->lockout.slot ) {
600 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L698
601 17 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_LOCKOUT_CONFLICT;
602 17 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
603 17 : }
604 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L700
605 399 : current_vote_state_index =
606 399 : fd_ulong_checked_add_expect( current_vote_state_index,
607 399 : 1,
608 399 : "`current_vote_state_index` is bounded by "
609 399 : "`MAX_LOCKOUT_HISTORY` when slot is less than proposed" );
610 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L704
611 503 : } else if( FD_UNLIKELY( current_vote->lockout.slot == new_vote->lockout.slot ) ) {
612 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L707
613 340 : if( new_vote->lockout.confirmation_count < current_vote->lockout.confirmation_count ) {
614 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_ROLL_BACK;
615 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
616 0 : }
617 :
618 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L712
619 340 : new_vote->latency = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index )->latency;
620 :
621 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L714
622 340 : current_vote_state_index =
623 340 : fd_ulong_checked_add_expect( current_vote_state_index,
624 340 : 1,
625 340 : "`current_vote_state_index` is bounded by "
626 340 : "`MAX_LOCKOUT_HISTORY` when slot is equal to proposed" );
627 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L717
628 340 : new_vote_state_index =
629 340 : fd_ulong_checked_add_expect( new_vote_state_index,
630 340 : 1,
631 340 : "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
632 340 : "when slot is equal to proposed" );
633 340 : } else {
634 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L722
635 163 : new_vote_state_index =
636 163 : fd_ulong_checked_add_expect( new_vote_state_index,
637 163 : 1,
638 163 : "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
639 163 : "when slot is greater than proposed" );
640 163 : }
641 919 : }
642 :
643 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L737
644 181 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
645 725 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
646 544 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
647 544 : fd_landed_vote_t * new_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
648 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L738
649 544 : if( FD_UNLIKELY( new_vote->latency == 0 ) ) {
650 : // this is unlikely because as validators upgrade, it should converge to the new vote state
651 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L739
652 487 : new_vote->latency = fd_vote_compute_vote_latency( new_vote->lockout.slot, current_slot );
653 487 : }
654 544 : }
655 :
656 : // doesn't matter what the value of slot if `is_some = 0` i.e. `Option::None`
657 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L744
658 181 : int both_none = !has_root_slot && !has_new_root;
659 181 : if( ( !both_none && ( has_root_slot!=has_new_root ||
660 56 : *root_slot!=new_root ) ) ) {
661 52 : fd_vsv_increment_credits( versioned, epoch, earned_credits );
662 52 : }
663 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750
664 181 : if( FD_LIKELY( has_timestamp ) ) {
665 : /* new_state asserted nonempty at function beginning */
666 90 : if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( new_state ) ) ) {
667 0 : FD_LOG_CRIT(( "invariant violation: landed votes is empty" ));
668 0 : }
669 90 : ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot;
670 90 : rc = fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp );
671 90 : if( FD_UNLIKELY( rc ) ) { return rc; }
672 90 : }
673 :
674 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L754
675 127 : fd_vsv_set_root_slot( versioned, has_new_root ? &new_root : NULL );
676 :
677 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L755
678 127 : deq_fd_landed_vote_t_remove_all( votes );
679 127 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
680 616 : !deq_fd_landed_vote_t_iter_done( new_state, iter );
681 489 : iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
682 489 : fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
683 489 : deq_fd_landed_vote_t_push_tail_wrap( votes, *landed_vote );
684 489 : }
685 :
686 127 : return FD_EXECUTOR_INSTR_SUCCESS;
687 181 : }
688 :
689 : __attribute__((weak)) int
690 : fd_bls12_381_proof_of_possession_verify( uchar const msg[],
691 : ulong msg_sz,
692 : uchar const proof[ static FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ ],
693 0 : uchar const public_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
694 0 : (void)msg; (void)msg_sz; (void)proof; (void)public_key;
695 0 : FD_LOG_ERR(( "This build does not include BLST, which is required to run a validator.\n"
696 0 : "Rebuild with a MACHINE config that enables config/extra/with-blst.mk" ));
697 0 : }
698 :
699 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1059-L1108 */
700 : #define FD_VOTE_BLS_MSG_SZ ( 9 + 32 + 48 )
701 : static int
702 : fd_vote_verify_bls_proof_of_possession( fd_exec_instr_ctx_t * ctx,
703 : uchar const vote_account_pubkey[ static 32 ],
704 : uchar const bls_pubkey[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
705 187 : uchar const bls_proof[ static FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ ] ) {
706 : /* Consume CUs for proof of possession */
707 187 : FD_EXEC_CU_UPDATE( ctx, COMPUTE_UNITS_POP );
708 :
709 : /* Build message */
710 182 : uchar msg[ FD_VOTE_BLS_MSG_SZ ];
711 182 : memcpy( msg, "ALPENGLOW", 9 );
712 182 : memcpy( msg+9, vote_account_pubkey, 32 );
713 182 : memcpy( msg+9+32, bls_pubkey, 48 );
714 :
715 : /* Verify */
716 182 : if( FD_UNLIKELY( fd_bls12_381_proof_of_possession_verify(
717 182 : msg,
718 182 : FD_VOTE_BLS_MSG_SZ,
719 182 : bls_proof,
720 182 : bls_pubkey
721 182 : )!=FD_BLS_SUCCESS ) ) {
722 182 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
723 182 : }
724 0 : return 0;
725 182 : }
726 : #undef FD_VOTE_BLS_MSG_SZ
727 :
728 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L716-L759 */
729 : static int
730 : authorize( fd_exec_instr_ctx_t * ctx,
731 : fd_borrowed_account_t * vote_account,
732 : int target_version,
733 : fd_pubkey_t const * authorized,
734 : fd_vote_authorize_t const * vote_authorize,
735 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
736 : ulong signers_cnt,
737 : fd_sol_sysvar_clock_t const * clock,
738 866 : int is_vote_authorize_with_bls_enabled ) {
739 866 : fd_vote_state_versioned_t * vote_state_versioned = &ctx->runtime->vote_program.authorize.vote_state;
740 :
741 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L724-L727 */
742 866 : int rc = get_vote_state_handler_checked( vote_account, target_version, 0, vote_state_versioned );
743 866 : if( FD_UNLIKELY( rc ) ) return rc;
744 :
745 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L729-L756 */
746 666 : switch( vote_authorize->discriminant ) {
747 :
748 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L730-L750 */
749 238 : case fd_vote_authorize_enum_voter: {
750 :
751 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L741-L743 */
752 238 : if( FD_UNLIKELY( is_vote_authorize_with_bls_enabled && fd_vsv_has_bls_pubkey( vote_state_versioned ) ) ) {
753 22 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
754 22 : }
755 :
756 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L731-L732 */
757 216 : int authorized_withdrawer_signer = !fd_vote_verify_authorized_signer(
758 216 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
759 216 : signers,
760 216 : signers_cnt
761 216 : );
762 :
763 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L737-L740 */
764 216 : ulong target_epoch;
765 216 : rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch );
766 216 : 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/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L747 */
771 216 : rc = fd_vsv_set_new_authorized_voter(
772 216 : ctx,
773 216 : vote_state_versioned,
774 216 : authorized,
775 216 : clock->epoch,
776 216 : target_epoch,
777 216 : NULL, /* no BLS pubkey */
778 216 : authorized_withdrawer_signer,
779 216 : signers,
780 216 : signers_cnt
781 216 : );
782 216 : if( FD_UNLIKELY( rc ) ) return rc;
783 101 : break;
784 216 : }
785 :
786 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L751-L756 */
787 242 : case fd_vote_authorize_enum_withdrawer: {
788 242 : rc = fd_vote_verify_authorized_signer(
789 242 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
790 242 : signers,
791 242 : signers_cnt
792 242 : );
793 242 : if( FD_UNLIKELY( rc ) ) return rc;
794 130 : fd_vsv_set_authorized_withdrawer( vote_state_versioned, authorized );
795 130 : break;
796 242 : }
797 :
798 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L770 */
799 187 : case fd_vote_authorize_enum_voter_with_bls: {
800 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L771-L773 */
801 187 : if( FD_UNLIKELY( !is_vote_authorize_with_bls_enabled ) ) {
802 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
803 0 : }
804 :
805 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L774-L775 */
806 187 : int authorized_withdrawer_signer = !fd_vote_verify_authorized_signer(
807 187 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
808 187 : signers,
809 187 : signers_cnt
810 187 : );
811 :
812 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L777-L782 */
813 187 : rc = fd_vote_verify_bls_proof_of_possession(
814 187 : ctx,
815 187 : vote_account->acc->pubkey,
816 187 : vote_authorize->voter_with_bls.bls_pubkey,
817 187 : vote_authorize->voter_with_bls.bls_proof_of_possession
818 187 : );
819 187 : if( FD_UNLIKELY( rc ) ) {
820 187 : return rc;
821 187 : }
822 :
823 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L787-L790 */
824 0 : ulong target_epoch;
825 0 : rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch );
826 0 : if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
827 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
828 0 : }
829 :
830 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L784 */
831 0 : rc = fd_vsv_set_new_authorized_voter(
832 0 : ctx,
833 0 : vote_state_versioned,
834 0 : authorized,
835 0 : clock->epoch,
836 0 : target_epoch,
837 0 : vote_authorize->voter_with_bls.bls_pubkey,
838 0 : authorized_withdrawer_signer,
839 0 : signers,
840 0 : signers_cnt
841 0 : );
842 0 : if( FD_UNLIKELY( rc ) ) return rc;
843 :
844 0 : break;
845 0 : }
846 :
847 0 : default:
848 0 : FD_LOG_CRIT(( "unsupported vote_authorize discriminant: %u", vote_authorize->discriminant ));
849 666 : }
850 :
851 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L758 */
852 231 : return fd_vsv_set_vote_account_state( ctx, vote_account, vote_state_versioned );
853 666 : }
854 :
855 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L808-L835 */
856 : static int
857 : update_validator_identity( fd_exec_instr_ctx_t * ctx,
858 : int target_version,
859 : fd_borrowed_account_t * vote_account,
860 : fd_pubkey_t const * node_pubkey,
861 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
862 : ulong signers_cnt,
863 402 : int custom_commission_collector_enabled ) {
864 402 : fd_vote_state_versioned_t * vote_state_versioned = &ctx->runtime->vote_program.update_validator_identity.vote_state;
865 :
866 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L768-L771 */
867 402 : int rc = get_vote_state_handler_checked( vote_account, target_version, 0, vote_state_versioned );
868 402 : if( FD_UNLIKELY( rc ) ) return rc;
869 :
870 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L774 */
871 191 : rc = fd_vote_verify_authorized_signer(
872 191 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
873 191 : signers,
874 191 : signers_cnt
875 191 : );
876 191 : if( FD_UNLIKELY( rc ) ) return rc;
877 :
878 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L777 */
879 176 : rc = fd_vote_verify_authorized_signer( node_pubkey, signers, signers_cnt );
880 176 : if( FD_UNLIKELY( rc ) ) return rc;
881 :
882 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L826 */
883 175 : fd_vsv_set_node_pubkey( vote_state_versioned, node_pubkey );
884 :
885 : /* Before SIMD-0232, block_revenue_collector is always synced with node_pubkey.
886 : After SIMD-0232, the collector can be set independently.
887 : https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L828-L832 */
888 175 : if( !custom_commission_collector_enabled ) {
889 175 : fd_vsv_set_block_revenue_collector( vote_state_versioned, node_pubkey );
890 175 : }
891 :
892 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L834 */
893 175 : return fd_vsv_set_vote_account_state( ctx, vote_account, vote_state_versioned );
894 176 : }
895 :
896 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L971
897 : static int
898 0 : is_commission_update_allowed( ulong slot, fd_epoch_schedule_t const * epoch_schedule ) {
899 0 : if( FD_LIKELY( epoch_schedule->slots_per_epoch > 0UL ) ) {
900 0 : ulong relative_slot = fd_ulong_sat_sub( slot, epoch_schedule->first_normal_slot );
901 0 : relative_slot %= epoch_schedule->slots_per_epoch;
902 0 : return fd_ulong_sat_mul( relative_slot, 2 ) <= epoch_schedule->slots_per_epoch;
903 0 : } else {
904 0 : return 1;
905 0 : }
906 0 : }
907 :
908 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L838-L869 */
909 : static int
910 : update_commission( fd_exec_instr_ctx_t * ctx,
911 : int target_version,
912 : fd_borrowed_account_t * vote_account,
913 : uchar commission,
914 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
915 : ulong signers_cnt,
916 : fd_epoch_schedule_t const * epoch_schedule,
917 : fd_sol_sysvar_clock_t const * clock,
918 494 : int disable_commission_update_rule ) {
919 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L796-L799 */
920 494 : int rc = 0;
921 494 : int get_vsv_rc = get_vote_state_handler_checked(
922 494 : vote_account,
923 494 : target_version,
924 494 : false,
925 494 : &ctx->runtime->vote_program.update_commission.vote_state
926 494 : );
927 :
928 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L851-L855 */
929 494 : fd_vote_state_versioned_t * vote_state_versioned = NULL;
930 494 : int enforce_commission_update_rule = !disable_commission_update_rule;
931 494 : if( FD_LIKELY( get_vsv_rc==FD_EXECUTOR_INSTR_SUCCESS ) ) {
932 190 : vote_state_versioned = &ctx->runtime->vote_program.update_commission.vote_state;
933 190 : enforce_commission_update_rule = (!disable_commission_update_rule) && (commission>fd_vsv_get_commission( vote_state_versioned ));
934 190 : }
935 :
936 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L806-L808 */
937 494 : if( FD_UNLIKELY( enforce_commission_update_rule && !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) {
938 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE;
939 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
940 0 : }
941 :
942 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L810 */
943 494 : if( FD_UNLIKELY( get_vsv_rc ) ) return get_vsv_rc;
944 :
945 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L813 */
946 190 : rc = fd_vote_verify_authorized_signer(
947 190 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
948 190 : signers,
949 190 : signers_cnt
950 190 : );
951 190 : if( FD_UNLIKELY( rc ) ) return rc;
952 :
953 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L815 */
954 179 : fd_vsv_set_commission( vote_state_versioned, commission, clock->epoch );
955 :
956 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L817 */
957 179 : return fd_vsv_set_vote_account_state( ctx, vote_account, vote_state_versioned );
958 190 : }
959 :
960 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.6/programs/vote/src/vote_state/mod.rs#L871-L906 */
961 : static int
962 : update_commission_bps( fd_exec_instr_ctx_t * ctx,
963 : fd_borrowed_account_t * vote_account,
964 : int target_version,
965 : fd_update_commission_bps_args_t const * args,
966 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
967 : ulong signers_cnt,
968 0 : int block_revenue_sharing ) {
969 0 : fd_vote_state_versioned_t * vote_state_versioned = &ctx->runtime->vote_program.update_commission_bps.vote_state;
970 :
971 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.6/programs/vote/src/vote_state/mod.rs#L882-L884 */
972 0 : if( FD_UNLIKELY( args->kind.discriminant==fd_commission_kind_enum_block_revenue && !block_revenue_sharing ) ) {
973 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
974 0 : }
975 :
976 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.6/programs/vote/src/vote_state/mod.rs#L886-L889 */
977 0 : int rc = get_vote_state_handler_checked( vote_account, target_version, false, vote_state_versioned );
978 0 : if( FD_UNLIKELY( rc ) ) return rc;
979 :
980 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.6/programs/vote/src/vote_state/mod.rs#L894 */
981 0 : rc = fd_vote_verify_authorized_signer(
982 0 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
983 0 : signers,
984 0 : signers_cnt
985 0 : );
986 0 : if( FD_UNLIKELY( rc ) ) return rc;
987 :
988 0 : ulong current_epoch = 0UL;
989 0 : if( vote_state_versioned->kind==fd_vote_state_versioned_enum_v5 ) {
990 0 : fd_sol_sysvar_clock_t clock_;
991 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
992 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
993 0 : current_epoch = clock->epoch;
994 0 : }
995 :
996 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.6/programs/vote/src/vote_state/mod.rs#L896-L903 */
997 0 : switch( args->kind.discriminant ) {
998 0 : case fd_commission_kind_enum_inflation_rewards: {
999 0 : fd_vsv_set_inflation_rewards_commission_bps( vote_state_versioned, args->commission_bps, current_epoch );
1000 0 : break;
1001 0 : }
1002 0 : case fd_commission_kind_enum_block_revenue: {
1003 0 : fd_vsv_set_block_revenue_commission_bps( vote_state_versioned, args->commission_bps, current_epoch );
1004 0 : break;
1005 0 : }
1006 0 : default: {
1007 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1008 0 : }
1009 0 : }
1010 :
1011 0 : return fd_vsv_set_vote_account_state( ctx, vote_account, vote_state_versioned );
1012 0 : }
1013 :
1014 : /* Updates the vote account's commission collector (SIMD-0232).
1015 : collector_account==NULL means the collector is the vote account
1016 : itself, which is exempt from the collector constraints.
1017 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_state/mod.rs#L907-L935 */
1018 : static int
1019 : update_commission_collector( fd_exec_instr_ctx_t * ctx,
1020 : fd_borrowed_account_t * vote_account,
1021 : int target_version,
1022 : fd_borrowed_account_t const * collector_account,
1023 : fd_commission_kind_t const * kind,
1024 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1025 : ulong signers_cnt,
1026 0 : fd_rent_t const * rent ) {
1027 0 : fd_vote_state_versioned_t * vote_state_versioned = &ctx->runtime->vote_program.update_commission_collector.vote_state;
1028 :
1029 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_state/mod.rs#L916 */
1030 0 : int rc = get_vote_state_handler_checked( vote_account, target_version, 0, vote_state_versioned );
1031 0 : if( FD_UNLIKELY( rc ) ) return rc;
1032 :
1033 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_state/mod.rs#L918-L919 */
1034 0 : rc = fd_vote_verify_authorized_signer(
1035 0 : fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
1036 0 : signers,
1037 0 : signers_cnt
1038 0 : );
1039 0 : if( FD_UNLIKELY( rc ) ) return rc;
1040 :
1041 : /* An external collector must be system program owned, rent-exempt,
1042 : and not reserved (checked via the writable flag).
1043 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_state/mod.rs#L866-L905 */
1044 0 : fd_pubkey_t const * new_collector_key;
1045 0 : if( !collector_account ) {
1046 0 : new_collector_key = (fd_pubkey_t const *)vote_account->acc->pubkey;
1047 0 : } else {
1048 0 : if( FD_UNLIKELY( !fd_pubkey_eq( fd_borrowed_account_get_owner( collector_account ), &fd_solana_system_program_id ) ) ) {
1049 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
1050 0 : }
1051 :
1052 0 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( collector_account ) <
1053 0 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( collector_account ) ) ) ) {
1054 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1055 0 : }
1056 :
1057 0 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( collector_account ) ) ) {
1058 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1059 0 : }
1060 :
1061 0 : new_collector_key = (fd_pubkey_t const *)collector_account->acc->pubkey;
1062 0 : }
1063 :
1064 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_state/mod.rs#L923-L930 */
1065 0 : switch( kind->discriminant ) {
1066 0 : case fd_commission_kind_enum_inflation_rewards: {
1067 0 : fd_vsv_set_inflation_rewards_collector( vote_state_versioned, new_collector_key );
1068 0 : break;
1069 0 : }
1070 0 : case fd_commission_kind_enum_block_revenue: {
1071 0 : fd_vsv_set_block_revenue_collector( vote_state_versioned, new_collector_key );
1072 0 : break;
1073 0 : }
1074 0 : default: {
1075 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1076 0 : }
1077 0 : }
1078 :
1079 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_state/mod.rs#L934 */
1080 0 : return fd_vsv_set_vote_account_state( ctx, vote_account, vote_state_versioned );
1081 0 : }
1082 :
1083 : /* SIMD-0674 UpdateLocation: sets the location of a V5 vote account,
1084 : converting a V4 account to V5. */
1085 :
1086 : /* */
1087 : static int
1088 : update_location( fd_exec_instr_ctx_t * ctx,
1089 : fd_borrowed_account_t * vote_account,
1090 : int target_version,
1091 : fd_validator_location_t const * location,
1092 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1093 : ulong signers_cnt,
1094 0 : fd_sol_sysvar_clock_t const * clock ) {
1095 0 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.update_location.vote_state;
1096 :
1097 0 : int rc = get_vote_state_handler_checked( vote_account, target_version, 0, versioned );
1098 0 : if( FD_UNLIKELY( rc ) ) return rc;
1099 :
1100 : /* The current epoch's authorized voter must sign */
1101 0 : fd_pubkey_t * authorized_voter = NULL;
1102 0 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1103 0 : if( FD_UNLIKELY( rc ) ) return rc;
1104 :
1105 0 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers, signers_cnt );
1106 0 : if( FD_UNLIKELY( rc ) ) return rc;
1107 :
1108 : /* The validator location must be within the allowed bounds */
1109 0 : if( FD_UNLIKELY( !fd_validator_location_is_valid( location ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1110 :
1111 : /* Convert the vote state account to a V5 vote account if it isn't already */
1112 0 : if( versioned->kind!=fd_vote_state_versioned_enum_v5 ) {
1113 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( vote_account )!=FD_VOTE_STATE_V4_SZ ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1114 0 : rc = fd_vsv_try_convert_to_v5( versioned, clock->epoch );
1115 0 : if( FD_UNLIKELY( rc ) ) return rc;
1116 0 : rc = fd_borrowed_account_set_data_length( vote_account, FD_VOTE_STATE_V5_SZ );
1117 0 : if( FD_UNLIKELY( rc ) ) return rc;
1118 0 : }
1119 :
1120 : /* Now, we can set the location */
1121 0 : versioned->v5.location = *location;
1122 :
1123 0 : return fd_vsv_set_vote_account_state( ctx, vote_account, versioned );
1124 0 : }
1125 :
1126 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L848C8-L903 */
1127 : static int
1128 : withdraw( fd_exec_instr_ctx_t * ctx,
1129 : fd_borrowed_account_t * vote_account,
1130 : int target_version,
1131 : ulong lamports,
1132 : ushort to_account_index,
1133 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1134 : ulong signers_cnt,
1135 : fd_rent_t const * rent_sysvar,
1136 : fd_sol_sysvar_clock_t const * clock,
1137 364 : int * deinitialized ) {
1138 364 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.withdraw.vote_state;
1139 364 : *deinitialized = 0;
1140 :
1141 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L860-L863 */
1142 364 : int rc = get_vote_state_handler_checked( vote_account, target_version, 0, versioned );
1143 364 : if( FD_UNLIKELY( rc ) ) return rc;
1144 :
1145 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L865 */
1146 194 : rc = fd_vote_verify_authorized_signer(
1147 194 : fd_vsv_get_authorized_withdrawer( versioned ),
1148 194 : signers,
1149 194 : signers_cnt
1150 194 : );
1151 194 : if( FD_UNLIKELY( rc ) ) return rc;
1152 :
1153 : /* Always zero until SIMD-0123 is activated.
1154 : https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1136 */
1155 178 : ulong pending_delegator_rewards = fd_vsv_get_pending_delegator_rewards( versioned );
1156 :
1157 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L867-L870 */
1158 178 : ulong vote_account_lamports = fd_borrowed_account_get_lamports( vote_account );
1159 178 : if( FD_UNLIKELY( lamports>vote_account_lamports ) ) {
1160 9 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1161 9 : }
1162 169 : ulong remaining_balance = vote_account_lamports-lamports;
1163 :
1164 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L872-L896 */
1165 169 : if( FD_UNLIKELY( remaining_balance==0UL ) ) {
1166 : /* SIMD-0123: vote account cannot be closed if pending_delegator_rewards > 0.
1167 : https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1139-L1143 */
1168 17 : if( FD_UNLIKELY( pending_delegator_rewards > 0 ) ) {
1169 8 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1170 8 : }
1171 :
1172 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L873-L883 */
1173 9 : fd_vote_epoch_credits_t const * last_epoch_credits = fd_vsv_get_last_epoch_credits( versioned );
1174 9 : ulong last_epoch_with_credits;
1175 9 : int reject_active_vote_account_close = 0;
1176 9 : if( FD_LIKELY( last_epoch_credits ) ) {
1177 6 : ulong current_epoch = clock->epoch;
1178 6 : last_epoch_with_credits = last_epoch_credits->epoch;
1179 6 : reject_active_vote_account_close = fd_ulong_sat_sub( current_epoch, last_epoch_with_credits )<2UL;
1180 6 : }
1181 :
1182 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L885-L890 */
1183 9 : if( FD_UNLIKELY( reject_active_vote_account_close ) ) {
1184 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_ACTIVE_VOTE_ACCOUNT_CLOSE;
1185 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1186 9 : } else {
1187 9 : rc = fd_vsv_deinitialize_vote_account_state(
1188 9 : ctx,
1189 9 : vote_account,
1190 9 : target_version
1191 9 : );
1192 9 : if( FD_UNLIKELY( rc ) ) return rc;
1193 8 : *deinitialized = 1;
1194 8 : }
1195 152 : } else {
1196 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L892-L895 */
1197 152 : ulong min_rent_exempt_balance = fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) );
1198 :
1199 : /* SIMD-0123: withdrawable balance when pending_delegator_rewards > 0
1200 : is lamports - pending_delegator_rewards - rent_exempt_minimum.
1201 : https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1164-L1169 */
1202 152 : ulong min_balance;
1203 152 : if( FD_UNLIKELY( fd_ulong_checked_add( min_rent_exempt_balance, pending_delegator_rewards, &min_balance ) ) ) {
1204 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
1205 0 : }
1206 :
1207 152 : if( FD_UNLIKELY( remaining_balance<min_balance ) ) {
1208 69 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1209 69 : }
1210 152 : }
1211 :
1212 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L898 */
1213 91 : rc = fd_borrowed_account_checked_sub_lamports( vote_account, lamports );
1214 91 : if( FD_UNLIKELY( rc ) ) return rc;
1215 :
1216 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L899 */
1217 91 : fd_borrowed_account_drop( vote_account );
1218 :
1219 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L900 */
1220 91 : fd_guarded_borrowed_account_t to = {0};
1221 91 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_account_index, &to );
1222 :
1223 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L901 */
1224 91 : rc = fd_borrowed_account_checked_add_lamports( &to, lamports );
1225 91 : if( FD_UNLIKELY( rc ) ) return rc;
1226 :
1227 90 : return FD_EXECUTOR_INSTR_SUCCESS;
1228 91 : }
1229 :
1230 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L638-L651 */
1231 : static int
1232 : process_vote_unfiltered( fd_exec_instr_ctx_t * ctx,
1233 : fd_vote_state_versioned_t * versioned,
1234 : ulong * vote_slots,
1235 : fd_legacy_vote_t const * vote,
1236 : fd_slot_hashes_t const * slot_hashes,
1237 : ulong epoch,
1238 335 : ulong current_slot ) {
1239 335 : int rc;
1240 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L770
1241 335 : rc = check_slots_are_valid( ctx, versioned, vote_slots, &vote->hash, slot_hashes );
1242 335 : if( FD_UNLIKELY( rc ) ) return rc;
1243 313 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote_slots );
1244 16365 : !deq_ulong_iter_done( vote_slots, iter );
1245 16052 : iter = deq_ulong_iter_next( vote_slots, iter ) ) {
1246 16052 : ulong * ele = deq_ulong_iter_ele( vote_slots, iter );
1247 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L772
1248 16052 : fd_vsv_process_next_vote_slot( versioned, *ele, epoch, current_slot );
1249 16052 : }
1250 313 : return 0;
1251 335 : }
1252 :
1253 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L783
1254 : static int
1255 : process_vote( fd_exec_instr_ctx_t * ctx,
1256 : fd_vote_state_versioned_t * versioned,
1257 : fd_legacy_vote_t const * vote,
1258 : fd_slot_hashes_t const * slot_hashes,
1259 : ulong epoch,
1260 340 : ulong current_slot ) {
1261 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L792
1262 340 : if( FD_UNLIKELY( deq_ulong_empty( vote->slots ) ) ) {
1263 1 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1264 1 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1265 1 : }
1266 :
1267 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L795
1268 339 : ulong earliest_slot_in_history = 0;
1269 339 : if( FD_UNLIKELY( !!slot_hashes->cnt ) ) {
1270 337 : earliest_slot_in_history = slot_hashes->elems[ slot_hashes->cnt-1UL ].slot;
1271 337 : }
1272 :
1273 : /* We know that the size of the vote_slots is bounded by the number of
1274 : slots that can fit inside of an instruction. Because the
1275 : instructions are deserialized with
1276 : limited_deserialize(data, PACKET_DATA_SIZE), we can use
1277 : FD_TXN_MTU_V0 as a bound on the size of the instruction. If this
1278 : instruction is filled only with a vote slot deque, we get
1279 : (1232/8) == 154 slots.
1280 : The footprint of a deque is as follows:
1281 : fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(DEQUE_T) ) + sizeof(DEQUE_T)*max, alignof(DEQUE_(private_t)) );
1282 : So, the footprint in our case is:
1283 : fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(ulong) ) + sizeof(ulong)*154, alignof(DEQUE_(private_t)) );
1284 : Which is equal to
1285 : fd_ulong_align_up( 32UL + 154 * 8UL, 8UL ) = 1264UL; */
1286 339 : #define VOTE_SLOTS_MAX (FD_TXN_MTU_V0/sizeof(ulong))
1287 339 : #define VOTE_SLOTS_DEQUE_FOOTPRINT (1264UL )
1288 339 : #define VOTE_SLOTS_DEQUE_ALIGN (8UL)
1289 339 : FD_TEST( deq_ulong_footprint( VOTE_SLOTS_MAX ) == VOTE_SLOTS_DEQUE_FOOTPRINT );
1290 339 : FD_TEST( deq_ulong_align() == 8UL );
1291 339 : FD_TEST( deq_ulong_cnt( vote->slots ) <= VOTE_SLOTS_MAX );
1292 339 : uchar * vote_slots_mem[ VOTE_SLOTS_DEQUE_FOOTPRINT ] __attribute__((aligned(VOTE_SLOTS_DEQUE_ALIGN)));
1293 :
1294 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L796
1295 339 : ulong * vote_slots = deq_ulong_join( deq_ulong_new( vote_slots_mem, deq_ulong_cnt( vote->slots ) ) );
1296 339 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
1297 24176 : !deq_ulong_iter_done( vote->slots, iter );
1298 23837 : iter = deq_ulong_iter_next( vote->slots, iter ) ) {
1299 23837 : ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
1300 23837 : if( FD_UNLIKELY( *ele >= earliest_slot_in_history ) ) {
1301 16306 : vote_slots = deq_ulong_push_tail_wrap( vote_slots, *ele );
1302 16306 : }
1303 23837 : }
1304 :
1305 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L802
1306 339 : if( FD_UNLIKELY( deq_ulong_cnt( vote_slots ) == 0 ) ) {
1307 4 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTES_TOO_OLD_ALL_FILTERED;
1308 4 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1309 4 : }
1310 :
1311 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L805
1312 335 : return process_vote_unfiltered(
1313 335 : ctx,
1314 335 : versioned,
1315 335 : vote_slots,
1316 335 : vote,
1317 335 : slot_hashes,
1318 335 : epoch,
1319 335 : current_slot
1320 335 : );
1321 339 : }
1322 :
1323 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L905-L926 */
1324 : static int
1325 : initialize_account( fd_exec_instr_ctx_t * ctx,
1326 : fd_borrowed_account_t * vote_account,
1327 : int target_version,
1328 : fd_vote_init_t * vote_init,
1329 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1330 : ulong signers_cnt,
1331 185 : fd_sol_sysvar_clock_t const * clock ) {
1332 185 : int rc;
1333 185 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.init_account.vote_state;
1334 :
1335 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L915 */
1336 185 : rc = check_vote_account_length( vote_account, target_version );
1337 185 : if( FD_UNLIKELY( rc ) ) return rc;
1338 :
1339 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L916 */
1340 167 : rc = fd_vsv_get_state( vote_account->acc, versioned );
1341 167 : if( FD_UNLIKELY( rc ) ) return rc;
1342 :
1343 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L918-L920 */
1344 167 : if( FD_UNLIKELY( !fd_vsv_is_uninitialized( versioned ) ) ) {
1345 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1346 0 : }
1347 :
1348 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L923 */
1349 167 : rc = fd_vote_verify_authorized_signer( &vote_init->node_pubkey, signers, signers_cnt );
1350 167 : if( FD_UNLIKELY( rc ) ) {
1351 2 : return rc;
1352 2 : }
1353 :
1354 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L925 */
1355 165 : return init_vote_account_state( ctx, vote_account, versioned, target_version, vote_init, clock );
1356 167 : }
1357 :
1358 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1182-L1216
1359 : Note: this is very similar to initialize_account() but also does verify_bls_proof_of_possession() */
1360 : static int
1361 : initialize_account_v2( fd_exec_instr_ctx_t * ctx,
1362 : fd_borrowed_account_t * vote_account,
1363 : int target_version,
1364 : fd_vote_init_v2_t * vote_init_v2,
1365 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1366 : ulong signers_cnt,
1367 0 : fd_sol_sysvar_clock_t const * clock ) {
1368 0 : int rc;
1369 0 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.init_account.vote_state;
1370 :
1371 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1198 */
1372 0 : rc = check_vote_account_length( vote_account, target_version );
1373 0 : if( FD_UNLIKELY( rc ) ) return rc;
1374 :
1375 0 : rc = fd_vsv_get_state( vote_account->acc, versioned );
1376 0 : if( FD_UNLIKELY( rc ) ) return rc;
1377 :
1378 0 : if( FD_UNLIKELY( !fd_vsv_is_uninitialized( versioned ) ) ) {
1379 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1380 0 : }
1381 :
1382 0 : rc = fd_vote_verify_authorized_signer( &vote_init_v2->node_pubkey, signers, signers_cnt );
1383 0 : if( FD_UNLIKELY( rc ) ) {
1384 0 : return rc;
1385 0 : }
1386 :
1387 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1208-L1213 */
1388 0 : rc = fd_vote_verify_bls_proof_of_possession(
1389 0 : ctx,
1390 0 : vote_account->acc->pubkey,
1391 0 : vote_init_v2->authorized_voter_bls_pubkey,
1392 0 : vote_init_v2->authorized_voter_bls_proof_of_possession
1393 0 : );
1394 0 : if( FD_UNLIKELY( rc ) ) {
1395 0 : return rc;
1396 0 : }
1397 :
1398 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/mod.rs#L1215 */
1399 0 : return init_vote_account_state_v2( ctx, vote_account, versioned, target_version, vote_init_v2, clock );
1400 0 : }
1401 :
1402 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L928-L953 */
1403 : static int
1404 : process_vote_with_account( fd_exec_instr_ctx_t * ctx,
1405 : fd_borrowed_account_t * vote_account,
1406 : int target_version,
1407 : fd_slot_hashes_t const * slot_hashes,
1408 : fd_sol_sysvar_clock_t const * clock,
1409 : fd_legacy_vote_t * vote,
1410 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1411 471 : ulong signers_cnt ) {
1412 471 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.process_vote.vote_state;
1413 :
1414 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L936-L939 */
1415 471 : int rc = get_vote_state_handler_checked( vote_account, target_version, 1, versioned );
1416 471 : if( FD_UNLIKELY( rc ) ) return rc;
1417 :
1418 : /* V5 has no tower votes */
1419 355 : if( FD_UNLIKELY( !fd_vsv_has_tower_votes( versioned ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1420 :
1421 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L941 */
1422 355 : fd_pubkey_t * authorized_voter = NULL;
1423 355 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1424 355 : if( FD_UNLIKELY( rc ) ) return rc;
1425 :
1426 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L942 */
1427 355 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers, signers_cnt );
1428 355 : if( FD_UNLIKELY( rc ) ) return rc;
1429 :
1430 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L944 */
1431 340 : rc = process_vote( ctx, versioned, vote, slot_hashes, clock->epoch, clock->slot );
1432 340 : if( FD_UNLIKELY( rc ) ) return rc;
1433 :
1434 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L945-L951 */
1435 313 : if( FD_LIKELY( vote->has_timestamp ) ) {
1436 : /* Calling max() on an empty iterator returns None */
1437 144 : if( FD_UNLIKELY( deq_ulong_cnt( vote->slots )==0 ) ) {
1438 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
1439 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1440 0 : }
1441 :
1442 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L948 */
1443 144 : ulong max = 0UL;
1444 144 : for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
1445 10577 : !deq_ulong_iter_done( vote->slots, iter );
1446 10433 : iter = deq_ulong_iter_next( vote->slots, iter ) ) {
1447 10433 : ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
1448 10433 : max = fd_ulong_max( max, *ele );
1449 10433 : }
1450 :
1451 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L950 */
1452 144 : rc = fd_vsv_process_timestamp( ctx, versioned, max, vote->timestamp );
1453 144 : if( FD_UNLIKELY( rc ) ) return rc;
1454 144 : }
1455 :
1456 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L952 */
1457 219 : return fd_vsv_set_vote_account_state( ctx, vote_account, versioned );
1458 313 : }
1459 :
1460 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1156
1461 : static int
1462 : do_process_vote_state_update( fd_exec_instr_ctx_t * ctx,
1463 : fd_vote_state_versioned_t * versioned,
1464 : fd_slot_hashes_t const * slot_hashes,
1465 : ulong epoch,
1466 : ulong slot,
1467 43 : fd_vote_state_update_t * vote_state_update ) {
1468 43 : int rc;
1469 :
1470 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1164
1471 43 : rc = check_and_filter_proposed_vote_state(
1472 43 : ctx,
1473 43 : versioned,
1474 43 : vote_state_update->lockouts,
1475 43 : &vote_state_update->has_root,
1476 43 : &vote_state_update->root,
1477 43 : &vote_state_update->hash,
1478 43 : slot_hashes
1479 43 : );
1480 43 : if( FD_UNLIKELY( rc ) ) return rc;
1481 :
1482 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1177
1483 20 : 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 ) ) );
1484 20 : for( deq_fd_vote_lockout_t_iter_t iter =
1485 20 : deq_fd_vote_lockout_t_iter_init( vote_state_update->lockouts );
1486 438 : !deq_fd_vote_lockout_t_iter_done( vote_state_update->lockouts, iter );
1487 418 : iter = deq_fd_vote_lockout_t_iter_next( vote_state_update->lockouts, iter ) ) {
1488 418 : fd_vote_lockout_t * lockout =
1489 418 : deq_fd_vote_lockout_t_iter_ele( vote_state_update->lockouts, iter );
1490 418 : deq_fd_landed_vote_t_push_tail_wrap( landed_votes,
1491 418 : ( fd_landed_vote_t ){ .latency = 0, .lockout = *lockout } );
1492 418 : }
1493 :
1494 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1171
1495 20 : return process_new_vote_state(
1496 20 : ctx,
1497 20 : versioned,
1498 20 : landed_votes,
1499 20 : vote_state_update->has_root,
1500 20 : vote_state_update->root,
1501 20 : vote_state_update->has_timestamp,
1502 20 : vote_state_update->timestamp,
1503 20 : epoch,
1504 20 : slot
1505 20 : );
1506 43 : }
1507 :
1508 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L955-L979 */
1509 : static int
1510 : process_vote_state_update( fd_exec_instr_ctx_t * ctx,
1511 : fd_borrowed_account_t * vote_account,
1512 : int target_version,
1513 : fd_slot_hashes_t const * slot_hashes,
1514 : fd_sol_sysvar_clock_t const * clock,
1515 : fd_vote_state_update_t * vote_state_update,
1516 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1517 894 : ulong signers_cnt ) {
1518 894 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.process_vote.vote_state;
1519 :
1520 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L963-L966 */
1521 894 : int rc = get_vote_state_handler_checked( vote_account, target_version, 1, versioned );
1522 894 : if( FD_UNLIKELY( rc ) ) return rc;
1523 :
1524 : /* V5 has no tower votes */
1525 667 : if( FD_UNLIKELY( !fd_vsv_has_tower_votes( versioned ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1526 :
1527 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L968 */
1528 667 : fd_pubkey_t * authorized_voter = NULL;
1529 667 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1530 667 : if( FD_UNLIKELY( rc ) ) return rc;
1531 :
1532 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L969 */
1533 666 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers, signers_cnt );
1534 666 : if( FD_UNLIKELY( rc ) ) return rc;
1535 :
1536 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L971-L977 */
1537 43 : rc = do_process_vote_state_update(
1538 43 : ctx,
1539 43 : versioned,
1540 43 : slot_hashes,
1541 43 : clock->epoch,
1542 43 : clock->slot,
1543 43 : vote_state_update
1544 43 : );
1545 43 : if( FD_UNLIKELY( rc ) ) {
1546 28 : return rc;
1547 28 : }
1548 :
1549 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L978 */
1550 15 : return fd_vsv_set_vote_account_state( ctx, vote_account, versioned );
1551 43 : }
1552 :
1553 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1035-L1061 */
1554 : static int
1555 : do_process_tower_sync( fd_exec_instr_ctx_t * ctx,
1556 : fd_vote_state_versioned_t * versioned,
1557 : fd_slot_hashes_t const * slot_hashes,
1558 : ulong epoch,
1559 : ulong slot,
1560 334 : fd_tower_sync_t * tower_sync ) {
1561 :
1562 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1042-L1048 */
1563 334 : int rc = check_and_filter_proposed_vote_state(
1564 334 : ctx,
1565 334 : versioned,
1566 334 : tower_sync->lockouts,
1567 334 : &tower_sync->has_root,
1568 334 : &tower_sync->root,
1569 334 : &tower_sync->hash,
1570 334 : slot_hashes
1571 334 : );
1572 334 : if( FD_UNLIKELY( rc ) ) return rc;
1573 :
1574 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1049-L1060 */
1575 322 : return process_new_vote_state(
1576 322 : ctx,
1577 322 : versioned,
1578 322 : fd_vote_lockout_landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ),
1579 322 : tower_sync->has_root,
1580 322 : tower_sync->root,
1581 322 : tower_sync->has_timestamp,
1582 322 : tower_sync->timestamp,
1583 322 : epoch,
1584 322 : slot
1585 322 : );
1586 334 : }
1587 :
1588 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1009-L1033 */
1589 : static int
1590 : process_tower_sync( fd_exec_instr_ctx_t * ctx,
1591 : fd_borrowed_account_t * vote_account,
1592 : int target_version,
1593 : fd_slot_hashes_t const * slot_hashes,
1594 : fd_sol_sysvar_clock_t const * clock,
1595 : fd_tower_sync_t * tower_sync,
1596 : fd_pubkey_t const * signers[static FD_INSTR_SIGNERS_MAX],
1597 367 : ulong signers_cnt ) {
1598 367 : fd_vote_state_versioned_t * versioned = &ctx->runtime->vote_program.tower_sync.vote_state;
1599 :
1600 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1017-L1020 */
1601 367 : int rc = get_vote_state_handler_checked( vote_account, target_version, 1, versioned );
1602 367 : if( FD_UNLIKELY( rc ) ) return rc;
1603 :
1604 : /* V5 has no tower votes */
1605 340 : if( FD_UNLIKELY( !fd_vsv_has_tower_votes( versioned ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1606 :
1607 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1022 */
1608 340 : fd_pubkey_t * authorized_voter = NULL;
1609 340 : rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
1610 340 : if( FD_UNLIKELY( rc ) ) return rc;
1611 :
1612 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1023 */
1613 340 : rc = fd_vote_verify_authorized_signer( authorized_voter, signers, signers_cnt );
1614 340 : if( FD_UNLIKELY( rc ) ) return rc;
1615 :
1616 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1025-L1031 */
1617 334 : rc = do_process_tower_sync(
1618 334 : ctx,
1619 334 : versioned,
1620 334 : slot_hashes,
1621 334 : clock->epoch,
1622 334 : clock->slot,
1623 334 : tower_sync
1624 334 : );
1625 334 : if( FD_UNLIKELY( rc ) ) return rc;
1626 :
1627 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1032 */
1628 112 : return fd_vsv_set_vote_account_state( ctx, vote_account, versioned );
1629 334 : }
1630 :
1631 : /**********************************************************************/
1632 : /* FD-only encoders / decoders (doesn't map directly to Labs impl) */
1633 : /**********************************************************************/
1634 :
1635 : int
1636 : fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update,
1637 : fd_vote_state_update_t * vote_update,
1638 106 : fd_exec_instr_ctx_t const * ctx ) {
1639 : // Taken from:
1640 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L954
1641 106 : if( compact_update->root != ULONG_MAX ) {
1642 83 : vote_update->has_root = 1;
1643 83 : vote_update->root = compact_update->root;
1644 83 : } else {
1645 23 : vote_update->has_root = 0;
1646 23 : vote_update->root = ULONG_MAX;
1647 23 : }
1648 :
1649 106 : ulong lockouts_len = compact_update->lockouts_len;
1650 106 : ulong lockouts_max = fd_ulong_max( lockouts_len, MAX_LOCKOUT_HISTORY );
1651 :
1652 106 : 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 ) );
1653 106 : ulong slot = fd_ulong_if( vote_update->has_root, vote_update->root, 0 );
1654 :
1655 2127 : for( ulong i=0; i < lockouts_len; ++i ) {
1656 2021 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( vote_update->lockouts );
1657 2021 : fd_lockout_offset_t * lock_offset = &compact_update->lockouts[i];
1658 :
1659 2021 : ulong next_slot;
1660 2021 : if( FD_UNLIKELY( __builtin_uaddl_overflow( slot, lock_offset->offset, &next_slot ) ) ) {
1661 0 : return 0;
1662 0 : }
1663 :
1664 2021 : elem->slot = slot = next_slot;
1665 2021 : elem->confirmation_count = (uint)lock_offset->confirmation_count;
1666 2021 : }
1667 :
1668 106 : vote_update->hash = compact_update->hash;
1669 106 : vote_update->has_timestamp = compact_update->has_timestamp;
1670 106 : vote_update->timestamp = compact_update->timestamp;
1671 :
1672 106 : return 1;
1673 106 : }
1674 :
1675 : /**********************************************************************/
1676 : /* mod vote_processor */
1677 : /**********************************************************************/
1678 :
1679 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.3/programs/vote/src/vote_processor.rs#L78-L81 */
1680 : static inline int
1681 1402 : should_reject_legacy_vote_instructions( fd_exec_instr_ctx_t const * ctx ) {
1682 1402 : return FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ||
1683 1402 : ctx->bank->f.alpenglow_migration_slot!=ULONG_MAX;
1684 1402 : }
1685 :
1686 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21-L51 */
1687 : static int
1688 : process_authorize_with_seed_instruction( /* invoke_context */
1689 : fd_exec_instr_ctx_t * ctx,
1690 : int target_version,
1691 : fd_borrowed_account_t * vote_account,
1692 : fd_pubkey_t const * new_authority,
1693 : fd_vote_authorize_t const * authorization_type,
1694 : fd_pubkey_t const * current_authority_derived_key_owner,
1695 : uchar const * current_authority_derived_key_seed,
1696 : ulong current_authority_derived_key_seed_len,
1697 358 : int is_vote_authorize_with_bls_enabled ) {
1698 358 : int rc = 0;
1699 :
1700 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31 */
1701 358 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
1702 358 : if( FD_UNLIKELY( rc ) ) return rc;
1703 :
1704 352 : fd_sol_sysvar_clock_t clock_;
1705 352 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1706 352 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1707 :
1708 352 : fd_pubkey_t * expected_authority_keys[FD_INSTR_SIGNERS_MAX] = { 0 };
1709 352 : ulong expected_authority_keys_cnt = 0UL;
1710 352 : fd_pubkey_t single_signer = { 0 };
1711 :
1712 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L30-L42 */
1713 352 : if( fd_instr_acc_is_signer_idx( ctx->instr, 2, &rc ) ) {
1714 :
1715 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L31 */
1716 337 : fd_pubkey_t const * base_pubkey = NULL;
1717 337 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &base_pubkey );
1718 337 : if( FD_UNLIKELY( rc ) ) return rc;
1719 :
1720 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L34-L40 */
1721 337 : expected_authority_keys[0] = &single_signer;
1722 337 : rc = fd_pubkey_create_with_seed( ctx,
1723 337 : base_pubkey->uc,
1724 337 : (char const *)current_authority_derived_key_seed,
1725 337 : current_authority_derived_key_seed_len,
1726 337 : current_authority_derived_key_owner->uc,
1727 337 : /* insert */ expected_authority_keys[0]->uc );
1728 337 : if( FD_UNLIKELY( rc ) ) return rc;
1729 333 : expected_authority_keys_cnt = 1UL;
1730 333 : }
1731 :
1732 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L43-L50 */
1733 348 : return authorize(
1734 348 : ctx,
1735 348 : vote_account,
1736 348 : target_version,
1737 348 : new_authority,
1738 348 : authorization_type,
1739 348 : (fd_pubkey_t const **)expected_authority_keys,
1740 348 : expected_authority_keys_cnt,
1741 348 : clock,
1742 348 : is_vote_authorize_with_bls_enabled
1743 348 : );
1744 352 : }
1745 :
1746 : /**********************************************************************/
1747 : /* Entry point for the Vote Program */
1748 : /**********************************************************************/
1749 :
1750 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
1751 : int
1752 9016 : fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) {
1753 : /* FD-specific init */
1754 9016 : int rc = FD_EXECUTOR_INSTR_SUCCESS;
1755 :
1756 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
1757 9016 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
1758 :
1759 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L64-L67 */
1760 7800 : fd_guarded_borrowed_account_t me = {0};
1761 7800 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me );
1762 7677 : if( FD_UNLIKELY( !fd_pubkey_eq( fd_borrowed_account_get_owner( &me ), &fd_solana_vote_program_id ) ) ) {
1763 2491 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
1764 2491 : }
1765 :
1766 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_processor.rs#L118-L119 */
1767 5186 : int target_version = VOTE_STATE_TARGET_VERSION_V4;
1768 :
1769 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L69
1770 5186 : fd_pubkey_t const * signers[FD_INSTR_SIGNERS_MAX] = { 0 };
1771 5186 : ulong signers_cnt = 0UL;
1772 5186 : fd_exec_instr_ctx_get_signers( ctx, signers, &signers_cnt );
1773 :
1774 5186 : int bls_pubkey_management_in_vote_account = FD_FEATURE_ACTIVE_BANK( ctx->bank, bls_pubkey_management_in_vote_account );
1775 5186 : int delay_commission_updates = FD_FEATURE_ACTIVE_BANK( ctx->bank, delay_commission_updates );
1776 5186 : int commission_rate_in_basis_points = FD_FEATURE_ACTIVE_BANK( ctx->bank, commission_rate_in_basis_points );
1777 5186 : int custom_commission_collector = FD_FEATURE_ACTIVE_BANK( ctx->bank, custom_commission_collector );
1778 5186 : int block_revenue_sharing = FD_FEATURE_ACTIVE_BANK( ctx->bank, block_revenue_sharing );
1779 5186 : int validator_location_registration = FD_FEATURE_ACTIVE_BANK( ctx->bank, validator_location_registration );
1780 5186 : int vote_account_initialize_v2 = 0;
1781 :
1782 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_processor.rs#L72-L76 */
1783 5186 : int is_vote_authorize_with_bls_enabled = bls_pubkey_management_in_vote_account;
1784 :
1785 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_processor.rs#L63-L70 */
1786 5186 : int is_init_account_v2_enabled =
1787 5186 : bls_pubkey_management_in_vote_account &&
1788 5222 : commission_rate_in_basis_points &&
1789 5222 : custom_commission_collector &&
1790 5186 : block_revenue_sharing &&
1791 5186 : vote_account_initialize_v2;
1792 :
1793 5186 : fd_vote_instruction_t instruction[1];
1794 5186 : if( FD_UNLIKELY( !fd_vote_instruction_deserialize( instruction, ctx->instr->data, ctx->instr->data_sz ) ) ) {
1795 742 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1796 742 : }
1797 :
1798 : /* PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR LABS IMPL:
1799 : */
1800 4444 : switch( instruction->discriminant ) {
1801 :
1802 : /* InitializeAccount
1803 : *
1804 : * Instruction:
1805 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L32
1806 : *
1807 : * Processor:
1808 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L71
1809 : */
1810 190 : case fd_vote_instruction_enum_initialize_account: {
1811 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L72
1812 190 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
1813 190 : if( FD_UNLIKELY( rc ) ) return rc;
1814 186 : fd_rent_t rent_;
1815 186 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
1816 186 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1817 :
1818 186 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &me ) <
1819 186 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &me ) ) ) )
1820 1 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1821 :
1822 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L76
1823 185 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
1824 185 : if( FD_UNLIKELY( rc ) ) return rc;
1825 185 : fd_sol_sysvar_clock_t clock_;
1826 185 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1827 185 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1828 :
1829 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L78
1830 185 : rc = initialize_account( ctx, &me, target_version, &instruction->initialize_account, signers, signers_cnt, clock );
1831 :
1832 185 : if( FD_LIKELY( !rc ) ) {
1833 165 : ctx->txn_out->accounts.new_vote[ ctx->instr->accounts[0].index_in_transaction ] = 1;
1834 165 : ctx->txn_out->accounts.rm_vote[ ctx->instr->accounts[0].index_in_transaction ] = 0;
1835 165 : }
1836 :
1837 185 : break;
1838 185 : }
1839 :
1840 : /* Authorize
1841 : *
1842 : * Instruction:
1843 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L40
1844 : *
1845 : * Processor:
1846 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L86
1847 : *
1848 : * Notes:
1849 : * - Up to two signers: the vote authority and the authorized withdrawer.
1850 : */
1851 308 : case fd_vote_instruction_enum_authorize: {
1852 308 : fd_pubkey_t const * voter_pubkey = &instruction->authorize.pubkey;
1853 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L87
1854 308 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
1855 308 : if( FD_UNLIKELY( rc ) ) return rc;
1856 305 : fd_sol_sysvar_clock_t clock_;
1857 305 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1858 305 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1859 :
1860 305 : rc = authorize( ctx, &me, target_version, voter_pubkey, &instruction->authorize.vote_authorize, signers, signers_cnt, clock, is_vote_authorize_with_bls_enabled );
1861 :
1862 305 : break;
1863 305 : }
1864 :
1865 : /* AuthorizeWithSeed
1866 : *
1867 : * Instruction:
1868 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L117
1869 : *
1870 : * Processor:
1871 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L98
1872 : */
1873 200 : case fd_vote_instruction_enum_authorize_with_seed: {
1874 200 : fd_vote_authorize_with_seed_args_t * args = &instruction->authorize_with_seed;
1875 :
1876 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L99
1877 200 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 3 ) ) {
1878 1 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1879 1 : break;
1880 1 : }
1881 :
1882 199 : rc = process_authorize_with_seed_instruction(
1883 199 : ctx,
1884 199 : target_version,
1885 199 : &me,
1886 199 : &args->new_authority,
1887 199 : &args->authorization_type,
1888 199 : &args->current_authority_derived_key_owner,
1889 199 : args->current_authority_derived_key_seed,
1890 199 : args->current_authority_derived_key_seed_len,
1891 199 : is_vote_authorize_with_bls_enabled
1892 199 : );
1893 :
1894 199 : break;
1895 200 : }
1896 :
1897 : /* AuthorizeCheckedWithSeed
1898 : *
1899 : * Instruction:
1900 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L131
1901 : *
1902 : * Processor:
1903 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L111
1904 : */
1905 174 : case fd_vote_instruction_enum_authorize_checked_with_seed: {
1906 174 : fd_vote_authorize_checked_with_seed_args_t const * args = &instruction->authorize_checked_with_seed;
1907 :
1908 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L112
1909 174 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
1910 3 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1911 3 : break;
1912 3 : }
1913 :
1914 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L99-L100
1915 171 : fd_pubkey_t const * new_authority = NULL;
1916 171 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &new_authority );
1917 171 : if( FD_UNLIKELY( rc ) ) return rc;
1918 :
1919 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L116
1920 171 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
1921 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1922 12 : if( FD_UNLIKELY( !!rc ) ) break;
1923 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L117
1924 12 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1925 12 : break;
1926 12 : }
1927 :
1928 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L119
1929 159 : rc = process_authorize_with_seed_instruction(
1930 159 : ctx,
1931 159 : target_version,
1932 159 : &me,
1933 159 : new_authority,
1934 159 : &args->authorization_type,
1935 159 : &args->current_authority_derived_key_owner,
1936 159 : args->current_authority_derived_key_seed,
1937 159 : args->current_authority_derived_key_seed_len,
1938 159 : is_vote_authorize_with_bls_enabled );
1939 :
1940 159 : break;
1941 171 : }
1942 :
1943 : /* UpdateValidatorIdentity
1944 : *
1945 : * Instruction:
1946 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L65
1947 : *
1948 : * Processor:
1949 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L130
1950 : */
1951 402 : case fd_vote_instruction_enum_update_validator_identity: {
1952 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L131
1953 402 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
1954 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1955 0 : break;
1956 0 : }
1957 :
1958 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L118-L120
1959 402 : fd_pubkey_t const * node_pubkey = NULL;
1960 402 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1UL, &node_pubkey );
1961 402 : if( FD_UNLIKELY( rc ) ) return rc;
1962 :
1963 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L135
1964 402 : rc = update_validator_identity( ctx, target_version, &me, node_pubkey, signers, signers_cnt, custom_commission_collector );
1965 :
1966 402 : break;
1967 402 : }
1968 :
1969 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L142
1970 494 : case fd_vote_instruction_enum_update_commission: {
1971 :
1972 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L149
1973 494 : fd_epoch_schedule_t epoch_schedule_;
1974 494 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, &epoch_schedule_ );
1975 494 : if( FD_UNLIKELY( !epoch_schedule ) ) {
1976 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1977 0 : }
1978 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L150
1979 :
1980 494 : fd_sol_sysvar_clock_t clock_;
1981 494 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1982 494 : if( FD_UNLIKELY( !clock ) ) {
1983 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1984 0 : }
1985 :
1986 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L145
1987 494 : rc = update_commission(
1988 494 : ctx,
1989 494 : target_version,
1990 494 : &me,
1991 494 : instruction->update_commission,
1992 494 : signers,
1993 494 : signers_cnt,
1994 494 : epoch_schedule,
1995 494 : clock,
1996 494 : delay_commission_updates
1997 494 : );
1998 :
1999 494 : break;
2000 494 : }
2001 :
2002 : /* Vote
2003 : *
2004 : * Instruction:
2005 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L49
2006 : */
2007 264 : case fd_vote_instruction_enum_vote:;
2008 : /* clang-format off */
2009 264 : __attribute__((fallthrough));
2010 : /* clang-format on */
2011 :
2012 : /* VoteSwitch
2013 : *
2014 : * Instruction:
2015 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L81
2016 : *
2017 : * Processor:
2018 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L154
2019 : */
2020 498 : case fd_vote_instruction_enum_vote_switch: {
2021 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.3/programs/vote/src/vote_processor.rs#L222-L224 */
2022 498 : if( should_reject_legacy_vote_instructions( ctx ) ) {
2023 2 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2024 2 : }
2025 :
2026 496 : fd_legacy_vote_t * vote;
2027 496 : if( instruction->discriminant == fd_vote_instruction_enum_vote ) {
2028 262 : vote = &instruction->vote;
2029 262 : } else if( instruction->discriminant == fd_vote_instruction_enum_vote_switch ) {
2030 234 : vote = &instruction->vote_switch.vote;
2031 234 : } else {
2032 0 : FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant ));
2033 0 : }
2034 :
2035 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L155
2036 496 : int err;
2037 496 : err = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_slot_hashes_id );
2038 496 : if( FD_UNLIKELY( err ) ) return err;
2039 :
2040 473 : if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2041 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2042 0 : }
2043 :
2044 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L157
2045 473 : err = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2046 473 : if( FD_UNLIKELY( err ) ) return err;
2047 471 : fd_sol_sysvar_clock_t clock_;
2048 471 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2049 471 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2050 :
2051 471 : fd_slot_hashes_t slot_hashes_[1];
2052 471 : fd_slot_hashes_t const * slot_hashes = fd_sysvar_cache_slot_hashes_view( ctx->sysvar_cache, slot_hashes_ );
2053 471 : if( FD_UNLIKELY( !slot_hashes ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2054 471 : rc = process_vote_with_account(
2055 471 : ctx,
2056 471 : &me,
2057 471 : target_version,
2058 471 : slot_hashes,
2059 471 : clock,
2060 471 : vote,
2061 471 : signers,
2062 471 : signers_cnt
2063 471 : );
2064 :
2065 471 : break;
2066 471 : }
2067 :
2068 : /* UpdateVoteState
2069 : *
2070 : * Instruction:
2071 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L100
2072 : */
2073 406 : case fd_vote_instruction_enum_update_vote_state:;
2074 : /* clang-format off */
2075 406 : __attribute__((fallthrough));
2076 : /* clang-format on */
2077 :
2078 : /* UpdateVoteStateSwitch
2079 : *
2080 : * Instruction:
2081 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L107
2082 : *
2083 : * Processor:
2084 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L169
2085 : */
2086 796 : case fd_vote_instruction_enum_update_vote_state_switch: {
2087 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.3/programs/vote/src/vote_processor.rs#L243-L245 */
2088 796 : if( should_reject_legacy_vote_instructions( ctx ) ) {
2089 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2090 0 : }
2091 :
2092 796 : fd_vote_state_update_t * vote_state_update;
2093 796 : switch( instruction->discriminant ) {
2094 406 : case fd_vote_instruction_enum_update_vote_state:
2095 406 : vote_state_update = &instruction->update_vote_state;
2096 406 : break;
2097 390 : case fd_vote_instruction_enum_update_vote_state_switch:
2098 390 : vote_state_update = &instruction->update_vote_state_switch.vote_state_update;
2099 390 : break;
2100 0 : default:
2101 0 : FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant ));
2102 796 : }
2103 :
2104 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L171
2105 796 : if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2106 8 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2107 8 : }
2108 :
2109 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L172
2110 788 : fd_sol_sysvar_clock_t clock_;
2111 788 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2112 788 : if( FD_UNLIKELY( !clock ) )
2113 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2114 :
2115 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L173
2116 788 : fd_slot_hashes_t slot_hashes_[1];
2117 788 : fd_slot_hashes_t const * slot_hashes = fd_sysvar_cache_slot_hashes_view( ctx->sysvar_cache, slot_hashes_ );
2118 788 : if( FD_UNLIKELY( !slot_hashes ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2119 788 : rc = process_vote_state_update(
2120 788 : ctx,
2121 788 : &me,
2122 788 : target_version,
2123 788 : slot_hashes,
2124 788 : clock,
2125 788 : vote_state_update,
2126 788 : signers,
2127 788 : signers_cnt
2128 788 : );
2129 :
2130 788 : break;
2131 788 : }
2132 :
2133 : /* CompactUpdateVoteState
2134 : *
2135 : * Instruction:
2136 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L139
2137 : *
2138 : * Notes:
2139 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2140 : * - Feature gated, but live on mainnet.
2141 : */
2142 52 : case fd_vote_instruction_enum_compact_update_vote_state:;
2143 52 : __attribute__((fallthrough));
2144 :
2145 : /* CompactUpdateVoteStateSwitch
2146 : *
2147 : * Instruction:
2148 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L146
2149 : *
2150 : * Processor:
2151 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L183
2152 : *
2153 : * Notes:
2154 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2155 : * - Feature gated, but live on mainnet.
2156 : */
2157 108 : case fd_vote_instruction_enum_compact_update_vote_state_switch: {
2158 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.3/programs/vote/src/vote_processor.rs#L260-L262 */
2159 108 : if( should_reject_legacy_vote_instructions( ctx ) ) {
2160 2 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2161 2 : }
2162 :
2163 106 : fd_compact_vote_state_update_t * vote_state_update = NULL;
2164 106 : if( instruction->discriminant == fd_vote_instruction_enum_compact_update_vote_state ) {
2165 51 : vote_state_update = &instruction->compact_update_vote_state;
2166 55 : } else if( instruction->discriminant ==
2167 55 : fd_vote_instruction_enum_compact_update_vote_state_switch ) {
2168 55 : vote_state_update =
2169 55 : &instruction->compact_update_vote_state_switch.compact_vote_state_update;
2170 55 : }
2171 :
2172 106 : fd_vote_state_update_t vote_update = {0};
2173 106 : if( FD_UNLIKELY( !fd_vote_decode_compact_update( vote_state_update, &vote_update, ctx ) ) )
2174 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2175 :
2176 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L185
2177 106 : if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2178 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2179 0 : }
2180 :
2181 106 : fd_sol_sysvar_clock_t clock_;
2182 106 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2183 106 : if( FD_UNLIKELY( !clock ) )
2184 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2185 :
2186 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L187
2187 106 : fd_slot_hashes_t slot_hashes_[1];
2188 106 : fd_slot_hashes_t const * slot_hashes = fd_sysvar_cache_slot_hashes_view( ctx->sysvar_cache, slot_hashes_ );
2189 106 : if( FD_UNLIKELY( !slot_hashes ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2190 106 : rc = process_vote_state_update(
2191 106 : ctx,
2192 106 : &me,
2193 106 : target_version,
2194 106 : slot_hashes,
2195 106 : clock,
2196 106 : &vote_update,
2197 106 : signers,
2198 106 : signers_cnt
2199 106 : );
2200 :
2201 106 : break;
2202 106 : }
2203 :
2204 : /* TowerSync(Switch)
2205 : *
2206 : * Instruction:
2207 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L151-L157
2208 : *
2209 : * Processor:
2210 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L196-L215
2211 : */
2212 :
2213 479 : case fd_vote_instruction_enum_tower_sync:
2214 668 : case fd_vote_instruction_enum_tower_sync_switch: {
2215 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.3/programs/vote/src/vote_processor.rs#L277-L279 */
2216 668 : if( FD_UNLIKELY( ctx->bank->f.alpenglow_migration_slot!=ULONG_MAX ) ) {
2217 301 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2218 301 : }
2219 :
2220 367 : fd_tower_sync_t * tower_sync = (instruction->discriminant == fd_vote_instruction_enum_tower_sync)
2221 367 : ? &instruction->tower_sync
2222 367 : : &instruction->tower_sync_switch.tower_sync;
2223 :
2224 367 : if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
2225 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2226 0 : }
2227 :
2228 367 : fd_sol_sysvar_clock_t clock_;
2229 367 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2230 367 : if( FD_UNLIKELY( !clock ) ) {
2231 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2232 0 : }
2233 :
2234 367 : fd_slot_hashes_t slot_hashes_[1];
2235 367 : fd_slot_hashes_t const * slot_hashes = fd_sysvar_cache_slot_hashes_view( ctx->sysvar_cache, slot_hashes_ );
2236 367 : if( FD_UNLIKELY( !slot_hashes ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2237 367 : rc = process_tower_sync(
2238 367 : ctx,
2239 367 : &me,
2240 367 : target_version,
2241 367 : slot_hashes,
2242 367 : clock,
2243 367 : tower_sync,
2244 367 : signers,
2245 367 : signers_cnt
2246 367 : );
2247 :
2248 367 : break;
2249 367 : }
2250 :
2251 : /* Withdraw
2252 : *
2253 : * Instruction:
2254 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L57
2255 : *
2256 : * Processor:
2257 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L216
2258 : */
2259 365 : case fd_vote_instruction_enum_withdraw: {
2260 365 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
2261 1 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2262 1 : break;
2263 1 : }
2264 364 : fd_rent_t rent_;
2265 364 : fd_rent_t const * rent_sysvar = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2266 364 : if( FD_UNLIKELY( !rent_sysvar ) )
2267 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2268 364 : fd_sol_sysvar_clock_t clock_;
2269 364 : fd_sol_sysvar_clock_t const * clock_sysvar = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2270 364 : if( FD_UNLIKELY( !clock_sysvar ) )
2271 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2272 :
2273 364 : int deinitialized = 0;
2274 364 : rc = withdraw(
2275 364 : ctx,
2276 364 : &me,
2277 364 : target_version,
2278 364 : instruction->withdraw,
2279 364 : 1UL,
2280 364 : signers,
2281 364 : signers_cnt,
2282 364 : rent_sysvar,
2283 364 : clock_sysvar,
2284 364 : &deinitialized
2285 364 : );
2286 :
2287 364 : if( FD_LIKELY( !rc && deinitialized ) ) {
2288 7 : ctx->txn_out->accounts.new_vote[ ctx->instr->accounts[0].index_in_transaction ] = 0;
2289 7 : ctx->txn_out->accounts.rm_vote[ ctx->instr->accounts[0].index_in_transaction ] = 1;
2290 7 : }
2291 :
2292 :
2293 364 : break;
2294 364 : }
2295 :
2296 : /* AuthorizeChecked
2297 : *
2298 : * Instruction:
2299 : * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L93
2300 : *
2301 : * Processor:
2302 : * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L234
2303 : *
2304 : * Notes:
2305 : * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
2306 : * - Feature gated, but live on mainnet.
2307 : */
2308 294 : case fd_vote_instruction_enum_authorize_checked: {
2309 294 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
2310 5 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2311 5 : break;
2312 5 : }
2313 :
2314 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L243-L245
2315 289 : fd_pubkey_t const * voter_pubkey = NULL;
2316 289 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &voter_pubkey );
2317 289 : if( FD_UNLIKELY( rc ) ) return rc;
2318 :
2319 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L239
2320 289 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
2321 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
2322 57 : if( FD_UNLIKELY( !!rc ) ) break;
2323 :
2324 57 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2325 57 : break;
2326 57 : }
2327 :
2328 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L242
2329 232 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2330 232 : if( FD_UNLIKELY( rc ) ) return rc;
2331 214 : fd_sol_sysvar_clock_t clock_;
2332 214 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2333 214 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2334 :
2335 214 : rc = authorize(
2336 214 : ctx,
2337 214 : &me,
2338 214 : target_version,
2339 214 : voter_pubkey,
2340 214 : &instruction->authorize_checked,
2341 214 : signers,
2342 214 : signers_cnt,
2343 214 : clock,
2344 214 : is_vote_authorize_with_bls_enabled
2345 214 : );
2346 214 : break;
2347 214 : }
2348 :
2349 : /* InitializeAccountV2
2350 : *
2351 : * Instruction:
2352 : * https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.0.0/vote-interface/src/instruction.rs#L195-L200
2353 : *
2354 : * Processor:
2355 : * https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_processor.rs#L307-L324
2356 : *
2357 : * Notes:
2358 : * - The code is identical to InitializeAccount, except calling the actual initialize_account_v2
2359 : * - Feature gated.
2360 : */
2361 1 : case fd_vote_instruction_enum_initialize_account_v2: {
2362 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_processor.rs#L308-L310 */
2363 1 : if( FD_UNLIKELY( !is_init_account_v2_enabled ) ) {
2364 1 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2365 1 : }
2366 :
2367 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
2368 0 : if( FD_UNLIKELY( rc ) ) return rc;
2369 0 : fd_rent_t rent_;
2370 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2371 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2372 :
2373 0 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &me ) <
2374 0 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &me ) ) ) )
2375 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
2376 :
2377 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2378 0 : if( FD_UNLIKELY( rc ) ) return rc;
2379 0 : fd_sol_sysvar_clock_t clock_;
2380 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2381 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2382 :
2383 0 : rc = initialize_account_v2( ctx, &me, target_version, &instruction->initialize_account_v2, signers, signers_cnt, clock );
2384 :
2385 0 : if( FD_LIKELY( !rc ) ) {
2386 0 : ctx->txn_out->accounts.new_vote[ ctx->instr->accounts[0].index_in_transaction ] = 1;
2387 0 : ctx->txn_out->accounts.rm_vote[ ctx->instr->accounts[0].index_in_transaction ] = 0;
2388 0 : }
2389 :
2390 0 : break;
2391 0 : }
2392 :
2393 : /* UpdateCommissionBps (commission_rate_in_basis_points feature)
2394 : *
2395 : * Instruction:
2396 : * https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.0.0/vote-interface/src/instruction.rs#L212-L221
2397 : *
2398 : * Processor:
2399 : * https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_processor.rs#L325-L347
2400 : *
2401 : */
2402 0 : case fd_vote_instruction_enum_update_commission_bps: {
2403 0 : if( FD_UNLIKELY( !commission_rate_in_basis_points
2404 0 : || !delay_commission_updates ) ) {
2405 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2406 0 : }
2407 :
2408 0 : rc = update_commission_bps( ctx, &me, target_version, &instruction->update_commission_bps, signers, signers_cnt, block_revenue_sharing );
2409 :
2410 0 : break;
2411 0 : }
2412 :
2413 : /* UpdateCommissionCollector (SIMD-0232)
2414 : *
2415 : * Instruction:
2416 : * https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.0.0/vote-interface/src/instruction.rs#L202-L210
2417 : *
2418 : * Processor:
2419 : * https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_processor.rs#L383-L408
2420 : *
2421 : * Notes:
2422 : * - Feature gated on custom_commission_collector
2423 : */
2424 0 : case fd_vote_instruction_enum_update_commission_collector: {
2425 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_processor.rs#L384-L390 */
2426 0 : if( FD_UNLIKELY( !custom_commission_collector ) ) {
2427 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2428 0 : }
2429 :
2430 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_processor.rs#L392 */
2431 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 3 ) ) {
2432 0 : rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2433 0 : break;
2434 0 : }
2435 :
2436 : /* If instruction account 1 is the vote account itself, no account
2437 : is borrowed.
2438 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_processor.rs#L83-L98 */
2439 0 : fd_pubkey_t const * collector_key = NULL;
2440 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1, &collector_key );
2441 0 : if( FD_UNLIKELY( rc ) ) return rc;
2442 :
2443 0 : fd_guarded_borrowed_account_t collector = {0};
2444 0 : fd_borrowed_account_t const * collector_account = NULL;
2445 0 : if( !fd_pubkey_eq( collector_key, (fd_pubkey_t const *)me.acc->pubkey ) ) {
2446 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 1, &collector );
2447 0 : collector_account = &collector;
2448 0 : }
2449 :
2450 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.2/programs/vote/src/vote_processor.rs#L395-L398 */
2451 0 : fd_rent_t rent_;
2452 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2453 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2454 :
2455 0 : rc = update_commission_collector(
2456 0 : ctx,
2457 0 : &me,
2458 0 : target_version,
2459 0 : collector_account,
2460 0 : &instruction->update_commission_collector,
2461 0 : signers,
2462 0 : signers_cnt,
2463 0 : rent
2464 0 : );
2465 :
2466 0 : break;
2467 0 : }
2468 :
2469 : /* DepositDelegatorRewards (SIMD-0123)
2470 : *
2471 : * Instruction:
2472 : * https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.0.0/vote-interface/src/instruction.rs#L223-L228
2473 : *
2474 : * Processor:
2475 : * https://github.com/anza-xyz/agave/blob/b76028e061ed3bc794d1d802429b3315f02e5a51/programs/vote/src/vote_processor.rs#L391-L393
2476 : *
2477 : * Notes:
2478 : * - Disabled, always fails with InvalidInstructionData
2479 : */
2480 0 : case fd_vote_instruction_enum_deposit_delegator_rewards: {
2481 0 : rc = FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2482 0 : break;
2483 0 : }
2484 :
2485 : /* UpdateLocation (SIMD-0674 validator_location_registration feature)
2486 : *
2487 : * Notes:
2488 : * - Feature gated on validator_location_registration
2489 : * - Converts the vote account to a Vote State V5 account if it
2490 : * wasn't already.
2491 : */
2492 0 : case fd_vote_instruction_enum_update_location: {
2493 0 : if( FD_UNLIKELY( !validator_location_registration ) ) {
2494 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2495 0 : }
2496 :
2497 0 : fd_sol_sysvar_clock_t clock_;
2498 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2499 0 : if( FD_UNLIKELY( !clock ) ) {
2500 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2501 0 : }
2502 :
2503 0 : rc = update_location( ctx, &me, target_version, &instruction->update_location, signers, signers_cnt, clock );
2504 0 : break;
2505 0 : }
2506 :
2507 0 : default:
2508 0 : FD_LOG_CRIT(( "unsupported vote instruction: %u", instruction->discriminant ));
2509 4444 : }
2510 :
2511 4130 : return rc;
2512 4444 : }
|