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