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