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