Line data Source code
1 : #include <limits.h>
2 :
3 : #include "../../../util/bits/fd_sat.h"
4 : #include "../../../util/bits/fd_uwide.h"
5 : #include "../../accdb/fd_accdb_sync.h"
6 : #include "../../log_collector/fd_log_collector.h"
7 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
8 : #include "../sysvar/fd_sysvar_rent.h"
9 : #include "../sysvar/fd_sysvar.h"
10 : #include "../fd_borrowed_account.h"
11 : #include "../fd_executor.h"
12 : #include "../fd_pubkey_utils.h"
13 : #include "../fd_system_ids.h"
14 : #include "../fd_runtime.h"
15 : #include "fd_stake_program.h"
16 : #include "fd_vote_program.h"
17 :
18 : /* A note on fd_borrowed_account_acquire_write:
19 :
20 : The stake program uses this function to prevent aliasing of accounts.
21 : (When the same account is passed via multiple instruction account
22 : indexes.) Internally, it acquires a transaction-wide mutex on the
23 : account. If called twice on the same account while the mutex is
24 : still locked, it returns an "AccountBorrowFailed" error.
25 :
26 : There is no exact equivalent to this in Agave/Rust.
27 :
28 : let handle = instruction_context.try_borrow_instruction_account(...)
29 :
30 : The above creates the lock on the account. However, that lock is
31 : **implicitly** released when 'handle' goes out of scope. Firedancer
32 : releases the handle **explicitly**. */
33 :
34 : /* The worst case stake instruction footprint can happen with one of the
35 : dynamically sized instruction types.
36 :
37 : Both these types have a dynamic size based on the size of the seed.
38 : authorize_with_seed
39 : authorize_checked_with_seed
40 :
41 : A loose, but reaosonable bound is that the seed takes up the entire
42 : FD_TXN_MTU to express.
43 :
44 : So, the worst case footprint is the sum of the size of the
45 : instruction and the transaction MTU. */
46 :
47 : #define FD_STAKE_INSTR_FOOTPRINT (FD_TXN_MTU + sizeof(fd_stake_instruction_t))
48 :
49 : /**********************************************************************/
50 : /* Errors */
51 : /**********************************************************************/
52 :
53 : // DO NOT REORDER: https://github.com/bincode-org/bincode/blob/trunk/docs/spec.md#enums
54 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L28
55 : #define FD_STAKE_ERR_NO_CREDITS_TO_REDEEM ( 0 )
56 0 : #define FD_STAKE_ERR_LOCKUP_IN_FORCE ( 1 )
57 0 : #define FD_STAKE_ERR_ALREADY_DEACTIVATED ( 2 )
58 0 : #define FD_STAKE_ERR_TOO_SOON_TO_REDELEGATE ( 3 )
59 0 : #define FD_STAKE_ERR_INSUFFICIENT_STAKE ( 4 )
60 0 : #define FD_STAKE_ERR_MERGE_TRANSIENT_STAKE ( 5 )
61 0 : #define FD_STAKE_ERR_MERGE_MISMATCH ( 6 )
62 0 : #define FD_STAKE_ERR_CUSTODIAN_MISSING ( 7 )
63 0 : #define FD_STAKE_ERR_CUSTODIAN_SIGNATURE_MISSING ( 8 )
64 0 : #define FD_STAKE_ERR_INSUFFICIENT_REFERENCE_VOTES ( 9 )
65 0 : #define FD_STAKE_ERR_VOTE_ADDRESS_MISMATCH ( 10 )
66 0 : #define FD_STAKE_ERR_MINIMUM_DELIQUENT_EPOCHS_FOR_DEACTIVATION_NOT_MET ( 11 )
67 0 : #define FD_STAKE_ERR_INSUFFICIENT_DELEGATION ( 12 )
68 : #define FD_STAKE_ERR_REDELEGATE_TRANSIENT_OR_INACTIVE_STAKE ( 13 )
69 : #define FD_STAKE_ERR_REDELEGATE_TO_SAME_VOTE_ACCOUNT ( 14 )
70 : #define FD_STAKE_ERR_REDELEGATED_STAKE_MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED ( 15 )
71 0 : #define FD_STAKE_ERR_EPOCH_REWARDS_ACTIVE ( 16 )
72 :
73 : /**********************************************************************/
74 : /* Constants */
75 : /**********************************************************************/
76 :
77 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/lib.rs#L31
78 0 : #define MINIMUM_DELEGATION_SOL ( 1 )
79 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/mod.rs#L18
80 0 : #define MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION ( 5 )
81 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L26-L28
82 0 : #define DEFAULT_WARMUP_COOLDOWN_RATE ( 0.25 )
83 0 : #define NEW_WARMUP_COOLDOWN_RATE ( 0.09 )
84 0 : #define DEFAULT_SLASH_PENALTY ( 12 )
85 :
86 : #define STAKE_AUTHORIZE_STAKER \
87 0 : ( ( fd_stake_authorize_t ){ .discriminant = fd_stake_authorize_enum_staker } )
88 : #define STAKE_AUTHORIZE_WITHDRAWER \
89 0 : ( ( fd_stake_authorize_t ){ .discriminant = fd_stake_authorize_enum_withdrawer } )
90 :
91 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L51
92 : #define DEFAULT_COMPUTE_UNITS 750UL
93 :
94 : /**********************************************************************/
95 : /* MergeKind */
96 : /**********************************************************************/
97 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1074-L1079
98 : struct merge_kind_inactive {
99 : fd_stake_meta_t meta;
100 : ulong active_stake;
101 : fd_stake_flags_t stake_flags;
102 : };
103 : typedef struct merge_kind_inactive merge_kind_inactive_t;
104 :
105 : struct merge_kind_activation_epoch {
106 : fd_stake_meta_t meta;
107 : fd_stake_t stake;
108 : fd_stake_flags_t stake_flags;
109 : };
110 : typedef struct merge_kind_activation_epoch merge_kind_activation_epoch_t;
111 :
112 : struct merge_kind_fully_active {
113 : fd_stake_meta_t meta;
114 : fd_stake_t stake;
115 : };
116 : typedef struct merge_kind_fully_active merge_kind_fully_active_t;
117 :
118 : union merge_kind_inner {
119 : merge_kind_inactive_t inactive;
120 : merge_kind_activation_epoch_t activation_epoch;
121 : merge_kind_fully_active_t fully_active;
122 : };
123 : typedef union merge_kind_inner merge_kind_inner_t;
124 :
125 : struct merge_kind {
126 : uint discriminant;
127 : merge_kind_inner_t inner;
128 : };
129 : typedef struct merge_kind merge_kind_t;
130 :
131 : enum { merge_kind_inactive = 0, merge_kind_activation_epoch = 1, merge_kind_fully_active = 2 };
132 :
133 : typedef fd_stake_history_entry_t stake_activation_status_t;
134 :
135 : struct effective_activating {
136 : ulong effective;
137 : ulong activating;
138 : };
139 : typedef struct effective_activating effective_activating_t;
140 :
141 : /**********************************************************************/
142 : /* Bincode */
143 : /**********************************************************************/
144 :
145 : static int
146 : get_state( fd_account_meta_t const * meta,
147 0 : fd_stake_state_v2_t * out ) {
148 0 : int rc;
149 :
150 0 : fd_bincode_decode_ctx_t bincode_ctx = {
151 0 : .data = fd_account_data( meta ),
152 0 : .dataend = fd_account_data( meta ) + meta->dlen,
153 0 : };
154 :
155 0 : ulong total_sz = 0UL;
156 0 : rc = fd_stake_state_v2_decode_footprint( &bincode_ctx, &total_sz );
157 0 : if( FD_UNLIKELY( rc!=FD_BINCODE_SUCCESS ) ) {
158 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
159 0 : }
160 :
161 0 : fd_stake_state_v2_decode( out, &bincode_ctx );
162 :
163 0 : return 0;
164 0 : }
165 :
166 : static int
167 : set_state( fd_borrowed_account_t * borrowed_acct,
168 0 : fd_stake_state_v2_t const * state ) {
169 :
170 0 : uchar * data = NULL;
171 0 : ulong dlen = 0UL;
172 :
173 0 : int err = fd_borrowed_account_get_data_mut( borrowed_acct, &data, &dlen );
174 0 : if( FD_UNLIKELY( err ) ) return err;
175 :
176 0 : ulong serialized_size = fd_stake_state_v2_size( state );
177 0 : if( FD_UNLIKELY( serialized_size>dlen ) )
178 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
179 :
180 0 : fd_bincode_encode_ctx_t encode = {
181 0 : .data = data,
182 0 : .dataend = data + serialized_size,
183 0 : };
184 0 : do {
185 0 : int err = fd_stake_state_v2_encode( state, &encode );
186 0 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_stake_state_v2_encode failed" ));
187 0 : } while(0);
188 :
189 0 : return 0;
190 0 : }
191 :
192 : /**********************************************************************/
193 : /* mod stake */
194 : /**********************************************************************/
195 :
196 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/lib.rs#L29
197 : static inline ulong
198 0 : get_minimum_delegation( fd_bank_t * bank ) {
199 0 : return fd_ulong_if( FD_FEATURE_ACTIVE_BANK( bank, stake_raise_minimum_delegation_to_1_sol ),
200 0 : MINIMUM_DELEGATION_SOL * LAMPORTS_PER_SOL,
201 0 : 1 );
202 0 : }
203 :
204 : /**********************************************************************/
205 : /* mod stake/state */
206 : /**********************************************************************/
207 :
208 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L30
209 : static inline double
210 0 : warmup_cooldown_rate( ulong current_epoch, ulong * new_rate_activation_epoch ) {
211 0 : return fd_double_if( current_epoch <
212 0 : ( new_rate_activation_epoch ? *new_rate_activation_epoch : ULONG_MAX ),
213 0 : DEFAULT_WARMUP_COOLDOWN_RATE,
214 0 : NEW_WARMUP_COOLDOWN_RATE );
215 0 : }
216 :
217 : /**********************************************************************/
218 : /* validated */
219 : /**********************************************************************/
220 :
221 : struct validated_delegated_info {
222 : ulong stake_amount;
223 : };
224 : typedef struct validated_delegated_info validated_delegated_info_t;
225 :
226 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L963
227 : static int
228 : validate_delegated_amount( fd_bank_t * bank,
229 : fd_borrowed_account_t * account,
230 : fd_stake_meta_t const * meta,
231 : validated_delegated_info_t * out,
232 0 : uint * custom_err ) {
233 0 : ulong stake_amount = fd_ulong_sat_sub( fd_borrowed_account_get_lamports( account ), meta->rent_exempt_reserve );
234 :
235 0 : if( FD_UNLIKELY( stake_amount<get_minimum_delegation( bank ) ) ) {
236 0 : *custom_err = FD_STAKE_ERR_INSUFFICIENT_DELEGATION;
237 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
238 0 : }
239 0 : out->stake_amount = stake_amount;
240 0 : return 0;
241 0 : }
242 :
243 : struct validated_split_info {
244 : ulong source_remaining_balance;
245 : ulong destination_rent_exempt_reserve;
246 : };
247 : typedef struct validated_split_info validated_split_info_t;
248 :
249 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L992
250 : static int
251 : validate_split_amount( fd_exec_instr_ctx_t const * invoke_context,
252 : uchar source_account_index,
253 : uchar destination_account_index,
254 : ulong lamports,
255 : fd_stake_meta_t const * source_meta,
256 : ulong additional_required_lamports,
257 : int source_is_active,
258 0 : validated_split_info_t * out ) {
259 0 : ulong source_lamports = 0;
260 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L1003-L1004 */
261 0 : fd_guarded_borrowed_account_t source_account = {0};
262 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( invoke_context, source_account_index, &source_account );
263 :
264 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1005
265 0 : source_lamports = fd_borrowed_account_get_lamports( &source_account );
266 :
267 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L1006 */
268 0 : fd_borrowed_account_drop( &source_account );
269 :
270 0 : ulong destination_lamports = 0;
271 0 : ulong destination_data_len = 0;
272 :
273 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L1007-L1008 */
274 0 : fd_guarded_borrowed_account_t destination_account = {0};
275 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( invoke_context, destination_account_index, &destination_account );
276 :
277 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1009-1010
278 0 : destination_lamports = fd_borrowed_account_get_lamports( &destination_account );
279 0 : destination_data_len = fd_borrowed_account_get_data_len( &destination_account );
280 :
281 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L1011 */
282 0 : fd_borrowed_account_drop( &destination_account );
283 :
284 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1013-L1021
285 0 : if( FD_UNLIKELY( lamports==0 ) ) return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
286 0 : if( FD_UNLIKELY( lamports>source_lamports ) ) return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
287 :
288 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1027-L1040
289 0 : ulong source_minimum_balance =
290 0 : fd_ulong_sat_add( source_meta->rent_exempt_reserve, additional_required_lamports );
291 0 : ulong source_remaining_balance = fd_ulong_sat_sub( source_lamports, lamports );
292 :
293 0 : if( FD_LIKELY( source_remaining_balance==0 ) ) {
294 0 : } else if( source_remaining_balance<source_minimum_balance ) {
295 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
296 0 : } else {
297 0 : };
298 :
299 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1042
300 0 : fd_rent_t rent_;
301 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( invoke_context->sysvar_cache, &rent_ );
302 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
303 :
304 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1043
305 0 : ulong destination_rent_exempt_reserve =
306 0 : fd_rent_exempt_minimum_balance( rent, destination_data_len );
307 :
308 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1048
309 0 : if( FD_UNLIKELY(
310 0 : source_is_active && source_remaining_balance!=0 &&
311 0 : destination_lamports<destination_rent_exempt_reserve ) ) {
312 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
313 0 : }
314 :
315 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1059-L1066
316 0 : ulong destination_minimum_balance =
317 0 : fd_ulong_sat_add( destination_rent_exempt_reserve, additional_required_lamports );
318 0 : ulong destination_balance_deficit =
319 0 : fd_ulong_sat_sub( destination_minimum_balance, destination_lamports );
320 0 : if( FD_UNLIKELY( lamports<destination_balance_deficit ) ) {
321 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
322 0 : }
323 :
324 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1067-L1071
325 0 : out->source_remaining_balance = source_remaining_balance;
326 0 : out->destination_rent_exempt_reserve = destination_rent_exempt_reserve;
327 0 : return 0;
328 0 : }
329 :
330 : /**********************************************************************/
331 : /* impl Lockup */
332 : /**********************************************************************/
333 :
334 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L270
335 : static inline int
336 : lockup_is_in_force( fd_stake_lockup_t const * self,
337 : fd_sol_sysvar_clock_t const * clock,
338 0 : fd_pubkey_t const * custodian ) {
339 : // FIXME FD_LIKELY
340 0 : if( custodian && !memcmp( custodian, &self->custodian, sizeof( fd_pubkey_t ) ) ) {
341 0 : return 0;
342 0 : }
343 0 : return self->unix_timestamp>clock->unix_timestamp || self->epoch>clock->epoch;
344 0 : }
345 :
346 : /**********************************************************************/
347 : /* impl Authorized */
348 : /**********************************************************************/
349 :
350 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L359
351 : static inline int
352 : authorized_check( fd_stake_authorized_t const * self,
353 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
354 : ulong signers_cnt,
355 0 : fd_stake_authorize_t stake_authorize ) {
356 : /* clang-format off */
357 0 : switch( stake_authorize.discriminant ) {
358 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L365
359 0 : case fd_stake_authorize_enum_staker:
360 0 : if( FD_LIKELY( fd_signers_contains( signers, signers_cnt, &self->staker ) ) ) {
361 0 : return 0;
362 0 : }
363 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
364 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L366
365 0 : case fd_stake_authorize_enum_withdrawer:
366 0 : if( FD_LIKELY( fd_signers_contains( signers, signers_cnt, &self->withdrawer ) ) ) {
367 0 : return 0;
368 0 : }
369 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
370 0 : default:
371 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
372 0 : }
373 : /* clang-format on */
374 0 : }
375 :
376 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L371
377 : static int
378 : authorized_authorize( fd_stake_authorized_t * self,
379 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
380 : ulong signers_cnt,
381 : fd_pubkey_t const * new_authorized,
382 : fd_stake_authorize_t const * stake_authorize,
383 : fd_stake_lockup_custodian_args_t const * lockup_custodian_args,
384 0 : /* out */ uint * custom_err ) {
385 0 : int rc;
386 0 : switch( stake_authorize->discriminant ) {
387 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L379
388 0 : case fd_stake_authorize_enum_staker:
389 0 : if( FD_UNLIKELY( !fd_signers_contains( signers, signers_cnt, &self->staker ) &&
390 0 : !fd_signers_contains( signers, signers_cnt, &self->withdrawer ) ) ) {
391 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
392 0 : }
393 0 : self->staker = *new_authorized;
394 0 : break;
395 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L386
396 0 : case fd_stake_authorize_enum_withdrawer:
397 0 : if( FD_LIKELY( lockup_custodian_args ) ) {
398 0 : fd_stake_lockup_t const * lockup = &lockup_custodian_args->lockup;
399 0 : fd_sol_sysvar_clock_t const * clock = &lockup_custodian_args->clock;
400 0 : fd_pubkey_t const * custodian = lockup_custodian_args->custodian;
401 :
402 : // FIXME FD_LIKELY
403 0 : if( lockup_is_in_force( lockup, clock, NULL ) ) {
404 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L389-L402
405 0 : if( !custodian ) { // FIXME FD_LIKELY
406 0 : *custom_err = FD_STAKE_ERR_CUSTODIAN_MISSING;
407 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
408 0 : } else {
409 0 : if( FD_UNLIKELY( !fd_signers_contains( signers, signers_cnt, custodian ) ) ) {
410 0 : *custom_err = FD_STAKE_ERR_CUSTODIAN_SIGNATURE_MISSING;
411 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
412 0 : }
413 :
414 0 : if( FD_UNLIKELY( lockup_is_in_force( lockup, clock, custodian ) ) ) {
415 0 : *custom_err = FD_STAKE_ERR_LOCKUP_IN_FORCE;
416 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
417 0 : }
418 0 : }
419 0 : }
420 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L405
421 0 : rc = authorized_check( self, signers, signers_cnt, *stake_authorize );
422 0 : if( FD_UNLIKELY( rc ) ) return rc;
423 0 : self->withdrawer = *new_authorized;
424 0 : }
425 0 : }
426 0 : return 0;
427 0 : }
428 :
429 : /**********************************************************************/
430 : /* impl Meta */
431 : /**********************************************************************/
432 :
433 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L482
434 : static inline int
435 : set_lockup_meta( fd_stake_meta_t * self,
436 : fd_lockup_args_t const * lockup,
437 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
438 : ulong signers_cnt,
439 0 : fd_sol_sysvar_clock_t const * clock ) {
440 : // FIXME FD_LIKELY
441 0 : if( lockup_is_in_force( &self->lockup, clock, NULL ) ) {
442 0 : if( !fd_signers_contains( signers, signers_cnt, &self->lockup.custodian ) ) {
443 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
444 0 : }
445 0 : } else if( !fd_signers_contains( signers, signers_cnt, &self->authorized.withdrawer ) ) {
446 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
447 0 : }
448 :
449 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L498-L506
450 0 : if( lockup->unix_timestamp ) self->lockup.unix_timestamp = *lockup->unix_timestamp;
451 0 : if( lockup->epoch ) self->lockup.epoch = *lockup->epoch;
452 0 : if( lockup->custodian ) self->lockup.custodian = *lockup->custodian;
453 0 : return 0;
454 0 : }
455 :
456 : /**********************************************************************/
457 : /* impl Delegation */
458 : /**********************************************************************/
459 :
460 : typedef fd_stake_history_entry_t fd_stake_activation_status_t;
461 :
462 : fd_stake_history_entry_t const *
463 : fd_stake_history_ele_binary_search_const( fd_stake_history_t const * history,
464 0 : ulong epoch ) {
465 0 : ulong start = 0UL;
466 0 : ulong end = history->fd_stake_history_len - 1;
467 :
468 0 : while ( start<=end ) {
469 0 : ulong mid = start + ( end - start ) / 2UL;
470 0 : if( history->fd_stake_history[mid].epoch==epoch ) {
471 0 : return &history->fd_stake_history[mid].entry;
472 0 : } else if( history->fd_stake_history[mid].epoch<epoch ) {
473 0 : if ( mid==0 ) return NULL;
474 0 : end = mid - 1;
475 0 : } else {
476 0 : start = mid + 1;
477 0 : }
478 0 : }
479 0 : return NULL;
480 0 : }
481 :
482 : fd_stake_history_entry_t const *
483 : fd_stake_history_ele_query_const( fd_stake_history_t const * history,
484 0 : ulong epoch ) {
485 0 : if( 0 == history->fd_stake_history_len ) {
486 0 : return NULL;
487 0 : }
488 :
489 0 : if( epoch > history->fd_stake_history[0].epoch ) {
490 0 : return NULL;
491 0 : }
492 :
493 0 : ulong off = (history->fd_stake_history[0].epoch - epoch);
494 0 : if( off >= history->fd_stake_history_len ) {
495 0 : return fd_stake_history_ele_binary_search_const( history, epoch );
496 0 : }
497 :
498 0 : ulong e = (off + history->fd_stake_history_offset) & (history->fd_stake_history_size - 1);
499 :
500 0 : if ( history->fd_stake_history[e].epoch == epoch ) {
501 0 : return &history->fd_stake_history[e].entry;
502 0 : }
503 :
504 : // if the epoch did not match, we do a binary search
505 0 : return fd_stake_history_ele_binary_search_const( history, epoch );
506 0 : }
507 :
508 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L728
509 : static effective_activating_t
510 : stake_and_activating( fd_delegation_t const * self,
511 : ulong target_epoch,
512 : fd_stake_history_t const * history,
513 0 : ulong * new_rate_activation_epoch ) {
514 0 : ulong delegated_stake = self->stake;
515 :
516 0 : fd_stake_history_entry_t const * cluster_stake_at_activation_epoch;
517 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L736
518 0 : if( self->activation_epoch==ULONG_MAX ) {
519 0 : return ( effective_activating_t ){ .effective = delegated_stake, .activating = 0 };
520 0 : } else if( self->activation_epoch==self->deactivation_epoch ) {
521 0 : return ( effective_activating_t ){ .effective = 0, .activating = 0 };
522 0 : } else if( target_epoch==self->activation_epoch ) {
523 0 : return ( effective_activating_t ){ .effective = 0, .activating = delegated_stake };
524 0 : } else if( target_epoch<self->activation_epoch ) {
525 0 : return ( effective_activating_t ){ .effective = 0, .activating = 0 };
526 0 : } else if( history &&
527 0 : ( cluster_stake_at_activation_epoch = fd_stake_history_ele_query_const(
528 0 : history, self->activation_epoch ) ) ) {
529 0 : ulong prev_epoch = self->activation_epoch;
530 0 : fd_stake_history_entry_t const * prev_cluster_stake = cluster_stake_at_activation_epoch;
531 :
532 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L763
533 0 : ulong current_epoch;
534 0 : ulong current_effective_stake = 0;
535 0 : for( ;; ) {
536 0 : current_epoch = prev_epoch + 1;
537 0 : if( FD_LIKELY( prev_cluster_stake->activating==0 ) ) { // FIXME always optimize loop break?
538 0 : break;
539 0 : }
540 :
541 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L775-L780
542 0 : ulong remaining_activating_stake = delegated_stake - current_effective_stake;
543 0 : double weight = (double)remaining_activating_stake / (double)prev_cluster_stake->activating;
544 0 : double warmup_cooldown_rate_ =
545 0 : warmup_cooldown_rate( current_epoch, new_rate_activation_epoch );
546 :
547 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L782-L786
548 0 : double newly_effective_cluster_stake =
549 0 : (double)prev_cluster_stake->effective * warmup_cooldown_rate_;
550 0 : ulong newly_effective_stake =
551 0 : fd_ulong_max( fd_rust_cast_double_to_ulong( weight * newly_effective_cluster_stake ), 1 );
552 :
553 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L787-L792
554 0 : current_effective_stake += newly_effective_stake;
555 0 : if( FD_LIKELY( current_effective_stake>=delegated_stake ) ) {
556 0 : current_effective_stake = delegated_stake;
557 0 : break;
558 0 : }
559 :
560 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L793
561 0 : if( FD_LIKELY( current_epoch>=target_epoch ||
562 0 : current_epoch>=self->deactivation_epoch ) ) { // FIXME always optimize loop break
563 0 : break;
564 0 : }
565 :
566 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L796-L801
567 0 : fd_stake_history_entry_t const * current_cluster_stake =
568 0 : fd_stake_history_ele_query_const( history, current_epoch );
569 0 : if( FD_UNLIKELY( NULL != current_cluster_stake ) ) {
570 0 : prev_epoch = current_epoch;
571 0 : prev_cluster_stake = current_cluster_stake;
572 0 : } else {
573 : // FIXME always optimize loop break
574 0 : break;
575 0 : }
576 0 : }
577 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L804-L807
578 0 : return ( effective_activating_t ){ .effective = current_effective_stake,
579 0 : .activating = delegated_stake - current_effective_stake };
580 0 : } else {
581 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L810
582 0 : return ( effective_activating_t ){ .effective = delegated_stake, .activating = 0 };
583 0 : }
584 0 : }
585 :
586 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L641
587 : static fd_stake_activation_status_t
588 : stake_activating_and_deactivating( fd_delegation_t const * self,
589 : ulong target_epoch,
590 : fd_stake_history_t const * stake_history,
591 0 : ulong * new_rate_activation_epoch ) {
592 :
593 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L648
594 0 : effective_activating_t effective_activating =
595 0 : stake_and_activating( self, target_epoch, stake_history, new_rate_activation_epoch );
596 :
597 0 : ulong effective_stake = effective_activating.effective;
598 0 : ulong activating_stake = effective_activating.activating;
599 :
600 0 : fd_stake_history_entry_t const * cluster_stake_at_deactivation_epoch = NULL;
601 :
602 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/stake/state.rs#L652
603 0 : if( target_epoch<self->deactivation_epoch ) {
604 : // if is bootstrap
605 0 : if( activating_stake==0 ) {
606 0 : return ( fd_stake_history_entry_t ){
607 0 : .effective = effective_stake, .deactivating = 0, .activating = 0 };
608 0 : } else {
609 0 : return ( fd_stake_history_entry_t ){
610 0 : .effective = effective_stake, .deactivating = 0, .activating = activating_stake };
611 0 : }
612 0 : } else if( target_epoch==self->deactivation_epoch ) {
613 : // https://github.com/anza-xyz/agave/blob/be16321eb0db3e12a57a32f59febbf54b92ebb7c/sdk/program/src/stake/state.rs#L662
614 0 : return ( fd_stake_history_entry_t ){
615 0 : .effective = effective_stake, .deactivating = effective_stake, .activating = 0 };
616 0 : } else if( stake_history &&
617 0 : ( cluster_stake_at_deactivation_epoch = fd_stake_history_ele_query_const( stake_history, self->deactivation_epoch ) ) ) {
618 : // https://github.com/anza-xyz/agave/blob/be16321eb0db3e12a57a32f59febbf54b92ebb7c/sdk/program/src/stake/state.rs#L665
619 0 : ulong prev_epoch = self->deactivation_epoch;
620 0 : fd_stake_history_entry_t const * prev_cluster_stake = cluster_stake_at_deactivation_epoch;
621 :
622 0 : ulong current_epoch;
623 0 : ulong current_effective_stake = effective_stake;
624 0 : for( ;; ) {
625 0 : current_epoch = prev_epoch + 1;
626 0 : if( prev_cluster_stake->deactivating==0 ) break;
627 :
628 0 : double weight = (double)current_effective_stake / (double)prev_cluster_stake->deactivating;
629 0 : double warmup_cooldown_rate_ =
630 0 : warmup_cooldown_rate( current_epoch, new_rate_activation_epoch );
631 :
632 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L697-L700
633 0 : double newly_not_effective_cluster_stake =
634 0 : (double)prev_cluster_stake->effective * warmup_cooldown_rate_;
635 0 : ulong newly_not_effective_stake =
636 0 : fd_ulong_max( fd_rust_cast_double_to_ulong( weight * newly_not_effective_cluster_stake ), 1 );
637 :
638 0 : current_effective_stake =
639 0 : fd_ulong_sat_sub( current_effective_stake, newly_not_effective_stake );
640 0 : if( current_effective_stake==0 ) break;
641 :
642 0 : if( current_epoch>=target_epoch ) break;
643 :
644 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L711-L713
645 0 : fd_stake_history_entry_t const * current_cluster_stake = NULL;
646 0 : if( ( current_cluster_stake = fd_stake_history_ele_query_const(stake_history, current_epoch ) ) ) {
647 0 : prev_epoch = current_epoch;
648 0 : prev_cluster_stake = current_cluster_stake;
649 0 : } else {
650 0 : break;
651 0 : }
652 0 : }
653 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L720
654 0 : return ( fd_stake_history_entry_t ){ .effective = current_effective_stake,
655 0 : .deactivating = current_effective_stake,
656 0 : .activating = 0 };
657 0 : } else {
658 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L723C16-L723C17
659 0 : return ( fd_stake_history_entry_t ){ .effective = 0, .activating = 0, .deactivating = 0 };
660 0 : }
661 0 : }
662 :
663 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L630
664 : static inline ulong
665 : delegation_stake( fd_delegation_t const * self,
666 : ulong epoch,
667 : fd_stake_history_t const * history,
668 0 : ulong * new_rate_activation_epoch ) {
669 0 : return stake_activating_and_deactivating( self, epoch, history, new_rate_activation_epoch )
670 0 : .effective;
671 0 : }
672 :
673 : /**********************************************************************/
674 : /* mod tools */
675 : /**********************************************************************/
676 :
677 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/tools.rs#L44
678 : static inline int
679 : acceptable_reference_epoch_credits( fd_vote_epoch_credits_t * epoch_credits,
680 0 : ulong current_epoch ) {
681 0 : ulong len = deq_fd_vote_epoch_credits_t_cnt( epoch_credits );
682 0 : ulong epoch_index[1] = { ULONG_MAX };
683 : // FIXME FD_LIKELY
684 0 : if( !__builtin_usubl_overflow( len, MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION, epoch_index ) ) {
685 0 : ulong epoch = current_epoch;
686 0 : for( ulong i = len - 1; i>=*epoch_index; i-- ) {
687 0 : ulong vote_epoch = deq_fd_vote_epoch_credits_t_peek_index( epoch_credits, i )->epoch;
688 0 : if( vote_epoch!=epoch ) { return 0; }
689 0 : epoch = fd_ulong_sat_sub( epoch, 1 );
690 0 : if( i==0 ) break;
691 0 : }
692 0 : return 1;
693 0 : } else {
694 0 : return 0;
695 0 : };
696 0 : }
697 :
698 : /* https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/tools.rs#L67-L83 */
699 : static inline int
700 0 : eligible_for_deactivate_delinquent( fd_vote_epoch_credits_t * epoch_credits, ulong current_epoch ) {
701 0 : if( FD_LIKELY( deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) {
702 0 : return 1;
703 0 : }
704 :
705 0 : fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits );
706 0 : if( FD_LIKELY( !last ) ) {
707 0 : return 1;
708 0 : } else {
709 0 : ulong epoch = last->epoch;
710 0 : ulong minimum_epoch = ULONG_MAX;
711 0 : int res = fd_ulong_checked_sub( current_epoch, MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION, &minimum_epoch );
712 0 : if( FD_LIKELY( res==0 ) ) {
713 0 : return epoch<=minimum_epoch;
714 0 : } else {
715 0 : return 0;
716 0 : }
717 0 : }
718 0 : }
719 :
720 : /**********************************************************************/
721 : /* impl StakeFlags */
722 : /**********************************************************************/
723 :
724 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/stake_flags.rs#L72
725 : #define STAKE_FLAGS_MUST_FULLY_ACTIVATE_BEFORE_DEACTIVATION_IS_PERMITTED \
726 : ( ( fd_stake_flags_t ){ .bits = 1 } )
727 :
728 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/stake_flags.rs#L75
729 0 : #define STAKE_FLAGS_EMPTY ( ( fd_stake_flags_t ){ .bits = 0 } )
730 :
731 : /**********************************************************************/
732 : /* impl Stake */
733 : /**********************************************************************/
734 :
735 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L915
736 : static int
737 : stake_split( fd_stake_t * self,
738 : ulong remaining_stake_delta,
739 : ulong split_stake_amount,
740 : uint * custom_err,
741 0 : fd_stake_t * out ) {
742 0 : if( FD_UNLIKELY( remaining_stake_delta>self->delegation.stake ) ) {
743 0 : *custom_err = FD_STAKE_ERR_INSUFFICIENT_STAKE;
744 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
745 0 : }
746 0 : self->delegation.stake -= remaining_stake_delta;
747 0 : fd_stake_t new;
748 0 : new = *self;
749 0 : new.delegation.stake = split_stake_amount;
750 0 : *out = new;
751 0 : return 0;
752 0 : }
753 :
754 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L934
755 : static int
756 0 : stake_deactivate( fd_stake_t * stake, ulong epoch, uint * custom_err ) {
757 0 : if( FD_UNLIKELY( stake->delegation.deactivation_epoch!=ULONG_MAX ) ) {
758 0 : *custom_err = FD_STAKE_ERR_ALREADY_DEACTIVATED;
759 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
760 0 : } else {
761 0 : stake->delegation.deactivation_epoch = epoch;
762 0 : return 0;
763 0 : }
764 0 : }
765 :
766 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L62
767 : int
768 : fd_new_warmup_cooldown_rate_epoch(
769 : fd_epoch_schedule_t const * epoch_schedule,
770 : fd_features_t const * features,
771 : /* out */ ulong * epoch,
772 : int * err
773 135 : ) {
774 135 : *err = 0;
775 :
776 135 : if( FD_UNLIKELY( !epoch_schedule ) ) {
777 0 : *epoch = ULONG_MAX;
778 0 : *err = FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
779 0 : return 1;
780 0 : }
781 135 : *epoch = fd_slot_to_epoch( epoch_schedule, features->reduce_stake_warmup_cooldown, NULL );
782 135 : return 1;
783 135 : }
784 :
785 : /**********************************************************************/
786 : /* util */
787 : /**********************************************************************/
788 :
789 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L205
790 : FD_FN_CONST static inline ulong
791 0 : stake_state_v2_size_of( void ) {
792 0 : return 200;
793 0 : }
794 :
795 : /**********************************************************************/
796 : /* impl MergeKind */
797 : /**********************************************************************/
798 :
799 : static fd_stake_meta_t const *
800 0 : meta( merge_kind_t const * self ) {
801 0 : switch( self->discriminant ) {
802 0 : case merge_kind_inactive:
803 0 : return &self->inner.inactive.meta;
804 0 : case merge_kind_activation_epoch:
805 0 : return &self->inner.activation_epoch.meta;
806 0 : case merge_kind_fully_active:
807 0 : return &self->inner.fully_active.meta;
808 0 : default:
809 0 : FD_LOG_ERR( ( "invalid merge_kind_t discriminant" ) );
810 0 : }
811 0 : }
812 :
813 : static fd_stake_t const *
814 0 : active_stake( merge_kind_t const * self ) {
815 0 : switch( self->discriminant ) {
816 0 : case merge_kind_inactive:
817 0 : return NULL;
818 0 : case merge_kind_activation_epoch:
819 0 : return &self->inner.activation_epoch.stake;
820 0 : case merge_kind_fully_active:
821 0 : return &self->inner.fully_active.stake;
822 0 : default:
823 0 : FD_LOG_ERR( ( "invalid merge_kind_t discriminant" ) );
824 0 : }
825 0 : }
826 :
827 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1097
828 : static int
829 : get_if_mergeable( fd_exec_instr_ctx_t * invoke_context, // not const to log
830 : fd_stake_state_v2_t const * stake_state,
831 : ulong stake_lamports,
832 : fd_sol_sysvar_clock_t const * clock,
833 : fd_stake_history_t const * stake_history,
834 : merge_kind_t * out,
835 0 : uint * custom_err ) {
836 : // stake_history must be non-NULL
837 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1104
838 0 : switch( stake_state->discriminant ) {
839 0 : case fd_stake_state_v2_enum_stake: {
840 0 : fd_stake_meta_t const * meta = &stake_state->inner.stake.meta;
841 0 : fd_stake_t const * stake = &stake_state->inner.stake.stake;
842 0 : fd_stake_flags_t const * stake_flags = &stake_state->inner.stake.stake_flags;
843 :
844 0 : fd_epoch_schedule_t epoch_schedule_[1];
845 0 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( invoke_context->sysvar_cache, epoch_schedule_ );
846 0 : ulong new_rate_activation_epoch = ULONG_MAX;
847 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1111
848 0 : int err;
849 0 : int is_some = fd_new_warmup_cooldown_rate_epoch(
850 0 : epoch_schedule,
851 0 : fd_bank_features_query( invoke_context->bank ),
852 0 : &new_rate_activation_epoch,
853 0 : &err );
854 0 : if( FD_UNLIKELY( err ) ) return err;
855 :
856 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1108
857 0 : fd_stake_history_entry_t status =
858 0 : stake_activating_and_deactivating( &stake->delegation,
859 0 : clock->epoch,
860 0 : stake_history,
861 0 : fd_ptr_if( is_some, &new_rate_activation_epoch, NULL ) );
862 :
863 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1115
864 0 : if( status.effective==0 && status.activating==0 && status.deactivating==0 ) {
865 :
866 0 : *out = ( merge_kind_t ){ .discriminant = merge_kind_inactive,
867 0 : .inner = { .inactive = { .meta = *meta,
868 0 : .active_stake = stake_lamports,
869 0 : .stake_flags = *stake_flags } } };
870 0 : return 0;
871 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1116
872 0 : } else if( status.effective==0 ) {
873 0 : *out = ( merge_kind_t ){ .discriminant = merge_kind_activation_epoch,
874 0 : .inner = { .activation_epoch = { .meta = *meta,
875 0 : .stake = *stake,
876 0 : .stake_flags = *stake_flags } } };
877 0 : return 0;
878 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1117
879 0 : } else if( status.activating==0 && status.deactivating==0 ) {
880 0 : *out = ( merge_kind_t ){ .discriminant = merge_kind_fully_active,
881 0 : .inner = { .fully_active = { .meta = *meta,
882 0 : .stake = *stake } } };
883 0 : return 0;
884 0 : } else {
885 0 : fd_log_collector_msg_literal( invoke_context, "stake account with transient stake cannot be merged" );
886 0 : *custom_err = FD_STAKE_ERR_MERGE_TRANSIENT_STAKE;
887 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
888 0 : }
889 0 : break;
890 0 : }
891 0 : case fd_stake_state_v2_enum_initialized: {
892 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1126
893 0 : *out = ( merge_kind_t ){ .discriminant = merge_kind_inactive,
894 0 : .inner = { .inactive = { .meta = stake_state->inner.initialized.meta,
895 0 : .active_stake = stake_lamports,
896 0 : .stake_flags = STAKE_FLAGS_EMPTY} } };
897 0 : break;
898 0 : }
899 0 : default:
900 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1128
901 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
902 0 : }
903 0 : return 0;
904 0 : }
905 :
906 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1132
907 : static int
908 : metas_can_merge( fd_exec_instr_ctx_t * invoke_context, // not const to log
909 : fd_stake_meta_t const * stake,
910 : fd_stake_meta_t const * source,
911 : fd_sol_sysvar_clock_t const * clock,
912 0 : uint * custom_err ) {
913 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1139
914 0 : int can_merge_lockups =
915 0 : ( !memcmp( &stake->lockup, &source->lockup, sizeof( fd_stake_lockup_t ) ) ) ||
916 0 : ( !lockup_is_in_force( &stake->lockup, clock, NULL ) &&
917 0 : !lockup_is_in_force( &source->lockup, clock, NULL ) );
918 :
919 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1146
920 0 : if( FD_LIKELY( !memcmp( &stake->authorized, &source->authorized, sizeof( fd_stake_authorized_t ) ) && can_merge_lockups ) ) {
921 0 : return 0;
922 0 : } else {
923 0 : fd_log_collector_msg_literal( invoke_context, "Unable to merge due to metadata mismatch" );
924 0 : *custom_err = FD_STAKE_ERR_MERGE_MISMATCH;
925 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
926 0 : }
927 0 : }
928 :
929 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1154
930 : static int
931 : active_delegations_can_merge( fd_exec_instr_ctx_t * invoke_context, // not const to log
932 : fd_delegation_t const * stake,
933 : fd_delegation_t const * source,
934 0 : uint * custom_err ) {
935 0 : if( memcmp( &stake->voter_pubkey, &source->voter_pubkey, sizeof(fd_pubkey_t) ) ) {
936 0 : fd_log_collector_msg_literal( invoke_context, "Unable to merge due to voter mismatch" );
937 0 : *custom_err = FD_STAKE_ERR_MERGE_MISMATCH;
938 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
939 0 : } else if( FD_LIKELY( stake->deactivation_epoch==ULONG_MAX && source->deactivation_epoch==ULONG_MAX ) ) {
940 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1162
941 0 : return 0;
942 0 : } else {
943 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1167
944 0 : fd_log_collector_msg_literal( invoke_context, "Unable to merge due to stake deactivation" );
945 0 : *custom_err = FD_STAKE_ERR_MERGE_MISMATCH;
946 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
947 0 : }
948 0 : }
949 :
950 : static int
951 : stake_weighted_credits_observed( fd_stake_t const * stake,
952 : ulong absorbed_lamports,
953 : ulong absorbed_credits_observed,
954 0 : ulong * out ) {
955 : /* https://github.com/anza-xyz/agave/blob/dc74c22960b4f2adfc672f6dc3bfaa74ec1d5d48/programs/stake/src/stake_state.rs#L1194 */
956 0 : if( FD_LIKELY( stake->credits_observed==absorbed_credits_observed ) ) {
957 0 : *out = stake->credits_observed;
958 0 : return 1;
959 0 : } else {
960 : /* https://github.com/anza-xyz/agave/blob/dc74c22960b4f2adfc672f6dc3bfaa74ec1d5d48/programs/stake/src/stake_state.rs#L1197 */
961 : /* let total_stake = u128::from(stake.delegation.stake.checked_add(absorbed_lamports)?); */
962 0 : ulong total_stake;
963 : /* If there is an overflow on the ulong addition then exit */
964 0 : if( FD_UNLIKELY( fd_ulong_checked_add( stake->delegation.stake, absorbed_lamports, &total_stake ) ) ) {
965 0 : return 0;
966 0 : }
967 :
968 : /* https://github.com/anza-xyz/agave/blob/9489096dc5b7f0a61a981f3d0fd393d264896c2a/programs/stake/src/stake_state.rs#L1198 */
969 : /* The multiplication of two 64 bit integers will never overflow the 128 bits */
970 0 : ulong stake_weighted_credits_h;
971 0 : ulong stake_weighted_credits_l;
972 : /* let stake_weighted_credits = */
973 : /* u128::from(stake.credits_observed).checked_mul(u128::from(stake.delegation.stake))?; */
974 0 : fd_uwide_mul( &stake_weighted_credits_h, &stake_weighted_credits_l,
975 0 : stake->credits_observed, stake->delegation.stake );
976 :
977 : /* https://github.com/anza-xyz/agave/blob/9489096dc5b7f0a61a981f3d0fd393d264896c2a/programs/stake/src/stake_state.rs#L1200 */
978 : /* The multiplication of two 64 bit integers will never overflow the 128 bits */
979 0 : ulong absorbed_weighted_credits_h;
980 0 : ulong absorbed_weighted_credits_l;
981 : /* let absorbed_weighted_credits = */
982 : /* u128::from(absorbed_credits_observed).checked_mul(u128::from(absorbed_lamports))?; */
983 0 : fd_uwide_mul( &absorbed_weighted_credits_h, &absorbed_weighted_credits_l,
984 0 : absorbed_credits_observed, absorbed_lamports );
985 :
986 : /* https://github.com/anza-xyz/agave/blob/9489096dc5b7f0a61a981f3d0fd393d264896c2a/programs/stake/src/stake_state.rs#L1204 */
987 : /* let total_weighted_credits = stake_weighted_credits */
988 : /* .checked_add(absorbed_weighted_credits)? */
989 : /* .checked_add(total_stake)? */
990 : /* .checked_sub(1)?; */
991 0 : ulong total_weighted_credits_partial_one_h;
992 0 : ulong total_weighted_credits_partial_one_l;
993 0 : ulong carry_out = fd_uwide_add( &total_weighted_credits_partial_one_h, &total_weighted_credits_partial_one_l,
994 0 : stake_weighted_credits_h, stake_weighted_credits_l,
995 0 : absorbed_weighted_credits_h, absorbed_weighted_credits_l, 0UL );
996 : /* return on overflow */
997 0 : if( FD_UNLIKELY( carry_out ) ) {
998 0 : return 0;
999 0 : }
1000 :
1001 0 : ulong total_weighted_credits_partial_two_h;
1002 0 : ulong total_weighted_credits_partial_two_l;
1003 0 : carry_out = fd_uwide_add( &total_weighted_credits_partial_two_h, &total_weighted_credits_partial_two_l,
1004 0 : total_weighted_credits_partial_one_h, total_weighted_credits_partial_one_l,
1005 0 : 0UL, total_stake, 0UL );
1006 : /* return on overflow */
1007 0 : if( FD_UNLIKELY( carry_out ) ) {
1008 0 : return 0;
1009 0 : }
1010 :
1011 : /* The only way we can underflow the subtraction of 1 is if the value of total_weighted_credits_partial_two is zero */
1012 0 : if( FD_UNLIKELY( total_weighted_credits_partial_two_h==0 && total_weighted_credits_partial_two_l==0 ) ) {
1013 0 : return 0;
1014 0 : }
1015 0 : ulong total_weighted_credits_h;
1016 0 : ulong total_weighted_credits_l;
1017 0 : fd_uwide_dec( &total_weighted_credits_h, &total_weighted_credits_l,
1018 0 : total_weighted_credits_partial_two_h, total_weighted_credits_partial_two_l, 1UL );
1019 :
1020 : /* https://github.com/anza-xyz/agave/blob/8a1b2dc3fa4b85e26fbce0db06a462d4853b0652/programs/stake/src/stake_state.rs#L1208 */
1021 : /* u64::try_from(total_weighted_credits.checked_div(total_stake)?).ok() */
1022 0 : ulong res_h;
1023 0 : ulong res_l;
1024 0 : if( FD_UNLIKELY( fd_uwide_div( &res_h, &res_l, total_weighted_credits_h, total_weighted_credits_l, total_stake ) ) ) {
1025 0 : return 0;
1026 0 : }
1027 0 : *out = res_l;
1028 0 : return 1;
1029 0 : }
1030 0 : }
1031 :
1032 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1239
1033 : static int
1034 : merge_delegation_stake_and_credits_observed( FD_FN_UNUSED fd_exec_instr_ctx_t const * invoke_context,
1035 : fd_stake_t * stake,
1036 : ulong absorbed_lamports,
1037 0 : ulong absorbed_credits_observed ) {
1038 0 : int rc;
1039 0 : int is_some = stake_weighted_credits_observed(
1040 0 : stake, absorbed_lamports, absorbed_credits_observed, &stake->credits_observed );
1041 0 : if( FD_UNLIKELY( !is_some ) ) return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
1042 0 : rc = fd_ulong_checked_add( stake->delegation.stake, absorbed_lamports, &stake->delegation.stake );
1043 0 : if( FD_UNLIKELY( rc ) ) return rc;
1044 0 : return 0;
1045 0 : }
1046 :
1047 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1171
1048 : static int
1049 : merge_kind_merge( merge_kind_t self,
1050 : fd_exec_instr_ctx_t * invoke_context, // not const to log
1051 : merge_kind_t source,
1052 : fd_sol_sysvar_clock_t const * clock,
1053 : fd_stake_state_v2_t * out,
1054 : int * is_some,
1055 0 : uint * custom_err ) {
1056 0 : int rc;
1057 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1177
1058 0 : rc = metas_can_merge( invoke_context, meta( &self ), meta( &source ), clock, custom_err );
1059 0 : if( FD_UNLIKELY( rc ) ) return rc;
1060 :
1061 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1178-L1188
1062 0 : fd_stake_t const * stake = active_stake( &self );
1063 0 : fd_stake_t const * source_ = active_stake( &source );
1064 :
1065 : // FIXME FD_LIKELY
1066 0 : if( stake && source_ ) {
1067 0 : rc = active_delegations_can_merge(
1068 0 : invoke_context, &stake->delegation, &source_->delegation, custom_err );
1069 0 : if( FD_UNLIKELY( rc ) ) return rc;
1070 0 : }
1071 :
1072 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1188
1073 : // FIXME FD_LIKELY
1074 0 : fd_stake_state_v2_t merged_state_ = {0};
1075 0 : fd_stake_state_v2_t * merged_state = &merged_state_;
1076 0 : if( self.discriminant==merge_kind_inactive && source.discriminant==merge_kind_inactive ) {
1077 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1189
1078 0 : merged_state = NULL;
1079 0 : } else if( self.discriminant==merge_kind_inactive && source.discriminant==merge_kind_activation_epoch ) {
1080 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1190
1081 0 : merged_state = NULL;
1082 0 : } else if( self.discriminant==merge_kind_activation_epoch && source.discriminant==merge_kind_inactive ) {
1083 0 : fd_stake_meta_t meta = self.inner.activation_epoch.meta;
1084 0 : fd_stake_t stake = self.inner.activation_epoch.stake;
1085 0 : fd_stake_flags_t stake_flags = self.inner.activation_epoch.stake_flags;
1086 0 : ulong source_lamports = source.inner.inactive.active_stake;
1087 0 : fd_stake_flags_t source_stake_flags = source.inner.inactive.stake_flags;
1088 :
1089 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1195
1090 0 : rc = fd_ulong_checked_add( stake.delegation.stake, source_lamports, &stake.delegation.stake );
1091 0 : if( FD_UNLIKELY( rc ) ) return rc;
1092 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1196
1093 0 : *merged_state = ( fd_stake_state_v2_t ){
1094 0 : .discriminant = fd_stake_state_v2_enum_stake,
1095 0 : .inner = { .stake = { .meta = meta,
1096 0 : .stake = stake,
1097 0 : .stake_flags = { .bits = stake_flags.bits | source_stake_flags.bits } } } };
1098 0 : } else if( self.discriminant==merge_kind_activation_epoch && source.discriminant==merge_kind_activation_epoch ) {
1099 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1203
1100 0 : fd_stake_meta_t meta = self.inner.activation_epoch.meta;
1101 0 : fd_stake_t stake = self.inner.activation_epoch.stake;
1102 0 : fd_stake_flags_t stake_flags = self.inner.activation_epoch.stake_flags;
1103 0 : fd_stake_meta_t source_meta = source.inner.activation_epoch.meta;
1104 0 : fd_stake_t source_stake = source.inner.activation_epoch.stake;
1105 0 : fd_stake_flags_t source_stake_flags = source.inner.activation_epoch.stake_flags;
1106 :
1107 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1206
1108 0 : ulong source_lamports = ULONG_MAX;
1109 0 : rc = fd_ulong_checked_add( source_meta.rent_exempt_reserve, source_stake.delegation.stake, &source_lamports );
1110 0 : if( FD_UNLIKELY( rc ) ) return rc;
1111 :
1112 : // // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1210
1113 0 : rc = merge_delegation_stake_and_credits_observed(invoke_context, &stake, source_lamports, source_stake.credits_observed );
1114 0 : if( FD_UNLIKELY( rc ) ) return rc;
1115 :
1116 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1215
1117 0 : *merged_state = ( fd_stake_state_v2_t ){
1118 0 : .discriminant = fd_stake_state_v2_enum_stake,
1119 0 : .inner = { .stake = { .meta = meta,
1120 0 : .stake = stake,
1121 0 : .stake_flags = { .bits = stake_flags.bits | source_stake_flags.bits } } } };
1122 0 : } else if( self.discriminant==merge_kind_fully_active && source.discriminant==merge_kind_fully_active ) {
1123 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1221
1124 0 : fd_stake_meta_t meta = self.inner.fully_active.meta;
1125 0 : fd_stake_t stake = self.inner.fully_active.stake;
1126 0 : fd_stake_t source_stake = source.inner.fully_active.stake;
1127 0 : rc = merge_delegation_stake_and_credits_observed(
1128 0 : invoke_context, &stake, source_stake.delegation.stake, source_stake.credits_observed );
1129 0 : if( FD_UNLIKELY( rc ) ) return rc;
1130 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L1231
1131 0 : *merged_state = ( fd_stake_state_v2_t ){
1132 0 : .discriminant = fd_stake_state_v2_enum_stake,
1133 0 : .inner = { .stake = { .meta = meta, .stake = stake, .stake_flags = STAKE_FLAGS_EMPTY } } };
1134 0 : } else {
1135 0 : *custom_err = FD_STAKE_ERR_MERGE_MISMATCH;
1136 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1137 0 : }
1138 0 : if( !merged_state ) {
1139 0 : *is_some = 0;
1140 0 : return 0;
1141 0 : }
1142 0 : *is_some = 1;
1143 0 : *out = *merged_state;
1144 0 : return 0;
1145 0 : }
1146 :
1147 : /**********************************************************************/
1148 : /* mod stake_state */
1149 : /**********************************************************************/
1150 :
1151 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L72
1152 : static int
1153 : get_stake_status( fd_exec_instr_ctx_t const * invoke_context,
1154 : fd_stake_t * stake,
1155 : fd_sol_sysvar_clock_t const * clock,
1156 0 : fd_stake_activation_status_t * out ) {
1157 0 : fd_sysvar_cache_t const * sysvar_cache = invoke_context->sysvar_cache;
1158 0 : if( FD_UNLIKELY( !fd_sysvar_cache_stake_history_is_valid( sysvar_cache ) ) ) {
1159 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1160 0 : }
1161 :
1162 0 : fd_epoch_schedule_t epoch_schedule_[1];
1163 0 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( sysvar_cache, epoch_schedule_ );
1164 :
1165 0 : ulong new_rate_activation_epoch = ULONG_MAX;
1166 0 : int err;
1167 0 : int is_some = fd_new_warmup_cooldown_rate_epoch(
1168 0 : epoch_schedule,
1169 0 : fd_bank_features_query( invoke_context->bank ),
1170 0 : &new_rate_activation_epoch,
1171 0 : &err );
1172 0 : if( FD_UNLIKELY( err ) ) return err;
1173 :
1174 0 : fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( sysvar_cache );
1175 0 : *out = stake_activating_and_deactivating(
1176 0 : &stake->delegation,
1177 0 : clock->epoch,
1178 0 : stake_history,
1179 0 : fd_ptr_if( is_some, &new_rate_activation_epoch, NULL ) );
1180 0 : fd_sysvar_cache_stake_history_leave_const( sysvar_cache, stake_history );
1181 :
1182 0 : return 0;
1183 0 : }
1184 :
1185 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/vote/state/mod.rs#L740
1186 : static ulong
1187 0 : get_credits( fd_vote_state_v3_t const * vote_state ) {
1188 :
1189 0 : return ( deq_fd_vote_epoch_credits_t_empty( vote_state->epoch_credits )
1190 0 : ? 0
1191 0 : : deq_fd_vote_epoch_credits_t_peek_index(
1192 0 : vote_state->epoch_credits,
1193 0 : deq_fd_vote_epoch_credits_t_cnt( vote_state->epoch_credits ) - 1 )
1194 0 : ->credits );
1195 0 : }
1196 :
1197 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L85
1198 : static int
1199 : redelegate_stake( fd_exec_instr_ctx_t const * ctx,
1200 : fd_stake_t * stake,
1201 : ulong stake_lamports,
1202 : fd_pubkey_t const * voter_pubkey,
1203 : fd_vote_state_v3_t const * vote_state,
1204 : fd_sol_sysvar_clock_t const * clock,
1205 : fd_stake_history_t const * stake_history,
1206 0 : uint * custom_err ) {
1207 0 : fd_epoch_schedule_t epoch_schedule_[1];
1208 0 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, epoch_schedule_ );
1209 :
1210 0 : ulong new_rate_activation_epoch = ULONG_MAX;
1211 0 : int err;
1212 0 : int is_some = fd_new_warmup_cooldown_rate_epoch(
1213 0 : epoch_schedule,
1214 0 : fd_bank_features_query( ctx->bank ),
1215 0 : &new_rate_activation_epoch,
1216 0 : &err );
1217 0 : if( FD_UNLIKELY( err ) ) return err;
1218 :
1219 : // FIXME FD_LIKELY
1220 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L96
1221 0 : if( delegation_stake( &stake->delegation, clock->epoch, stake_history, fd_ptr_if( is_some, &new_rate_activation_epoch, NULL ) )!=0 ) {
1222 :
1223 0 : if( FD_LIKELY( !memcmp( &stake->delegation.voter_pubkey, voter_pubkey, sizeof( fd_pubkey_t ) ) ) &&
1224 0 : clock->epoch==stake->delegation.deactivation_epoch ) {
1225 0 : stake->delegation.deactivation_epoch = ULONG_MAX;
1226 0 : return 0;
1227 0 : } else {
1228 0 : *custom_err = FD_STAKE_ERR_TOO_SOON_TO_REDELEGATE;
1229 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1230 0 : }
1231 0 : }
1232 :
1233 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L114-L118
1234 0 : stake->delegation.stake = stake_lamports;
1235 0 : stake->delegation.activation_epoch = clock->epoch;
1236 0 : stake->delegation.deactivation_epoch = ULONG_MAX;
1237 0 : stake->delegation.voter_pubkey = *voter_pubkey;
1238 0 : stake->credits_observed = get_credits( vote_state );
1239 0 : return 0;
1240 0 : }
1241 :
1242 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L202
1243 : static fd_stake_t
1244 : new_stake( ulong stake,
1245 : fd_pubkey_t const * voter_pubkey,
1246 : fd_vote_state_v3_t const * vote_state,
1247 0 : ulong activation_epoch ) {
1248 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L208
1249 0 : return ( fd_stake_t ){
1250 0 : .delegation = {.voter_pubkey = *voter_pubkey,
1251 0 : .stake = stake,
1252 0 : .activation_epoch = activation_epoch,
1253 0 : .deactivation_epoch = ULONG_MAX,
1254 0 : .warmup_cooldown_rate = DEFAULT_WARMUP_COOLDOWN_RATE},
1255 0 : .credits_observed = get_credits( vote_state ),
1256 0 : };
1257 0 : }
1258 :
1259 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L214
1260 : static int
1261 : initialize( fd_borrowed_account_t * stake_account,
1262 : fd_stake_authorized_t const * authorized,
1263 : fd_stake_lockup_t const * lockup,
1264 0 : fd_rent_t const * rent ) {
1265 :
1266 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L220
1267 :
1268 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( stake_account )!=stake_state_v2_size_of() ) )
1269 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1270 :
1271 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L224
1272 0 : fd_stake_state_v2_t stake_state = {0};
1273 0 : do {
1274 0 : int rc = get_state( stake_account->meta, &stake_state );
1275 0 : if( FD_UNLIKELY( rc ) ) return rc;
1276 0 : } while(0);
1277 :
1278 0 : if( FD_LIKELY( stake_state.discriminant==fd_stake_state_v2_enum_uninitialized ) ) {
1279 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L225
1280 0 : ulong rent_exempt_reserve = fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( stake_account ) );
1281 :
1282 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L226
1283 0 : if( FD_LIKELY( fd_borrowed_account_get_lamports( stake_account )>=rent_exempt_reserve ) ) {
1284 0 : fd_stake_state_v2_t initialized = {
1285 0 : .discriminant = fd_stake_state_v2_enum_initialized,
1286 0 : .inner = { .initialized = { .meta = { .rent_exempt_reserve = rent_exempt_reserve,
1287 0 : .authorized = *authorized,
1288 0 : .lockup = *lockup } } } };
1289 0 : return set_state( stake_account, &initialized );
1290 0 : } else {
1291 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L233
1292 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1293 0 : }
1294 :
1295 0 : } else {
1296 :
1297 : /// https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L236
1298 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1299 :
1300 0 : }
1301 0 : }
1302 :
1303 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L243
1304 : static int
1305 : authorize( fd_borrowed_account_t * stake_account,
1306 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1307 : ulong signers_cnt,
1308 : fd_pubkey_t const * new_authority,
1309 : fd_stake_authorize_t const * stake_authorize,
1310 : fd_sol_sysvar_clock_t const * clock,
1311 : fd_pubkey_t const * custodian,
1312 0 : uint * custom_err ) {
1313 0 : int rc;
1314 0 : fd_stake_state_v2_t stake_state = {0};
1315 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L251
1316 0 : rc = get_state( stake_account->meta, &stake_state );
1317 0 : if( FD_UNLIKELY( rc ) ) return rc;
1318 0 : switch( stake_state.discriminant ) {
1319 : /* FIXME check if the compiler can optimize away branching (given the layout of `meta` in both
1320 : * union members) and instead fallthrough */
1321 0 : case fd_stake_state_v2_enum_stake: {
1322 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L252
1323 0 : fd_stake_meta_t * meta = &stake_state.inner.stake.meta;
1324 :
1325 0 : fd_stake_lockup_custodian_args_t lockup_custodian_args = {
1326 0 : .lockup = meta->lockup, .clock = *clock, .custodian = (fd_pubkey_t *)custodian };
1327 0 : rc = authorized_authorize(
1328 0 : &meta->authorized, /* &mut self */
1329 0 : signers,
1330 0 : signers_cnt,
1331 0 : new_authority,
1332 0 : stake_authorize,
1333 0 : &lockup_custodian_args,
1334 0 : custom_err );
1335 0 : if( FD_UNLIKELY( rc ) ) return rc;
1336 :
1337 0 : return set_state( stake_account, &stake_state );
1338 0 : }
1339 0 : case fd_stake_state_v2_enum_initialized: {
1340 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L261
1341 0 : fd_stake_meta_t * meta = &stake_state.inner.initialized.meta;
1342 :
1343 0 : fd_stake_lockup_custodian_args_t lockup_custodian_args = {
1344 0 : .lockup = meta->lockup, .clock = *clock, .custodian = (fd_pubkey_t *)custodian };
1345 0 : rc = authorized_authorize(
1346 0 : &meta->authorized,
1347 0 : signers,
1348 0 : signers_cnt,
1349 0 : new_authority,
1350 0 : stake_authorize,
1351 0 : &lockup_custodian_args,
1352 0 : custom_err );
1353 0 : if( FD_UNLIKELY( rc ) ) return rc;
1354 :
1355 0 : return set_state( stake_account, &stake_state );
1356 0 : }
1357 0 : default:
1358 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L270
1359 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1360 0 : }
1361 0 : return rc;
1362 0 : }
1363 :
1364 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L275
1365 : static int
1366 : authorize_with_seed( fd_exec_instr_ctx_t const * ctx,
1367 : fd_borrowed_account_t * stake_account,
1368 : uchar authority_base_index,
1369 : char const * authority_seed,
1370 : ulong authority_seed_len,
1371 : fd_pubkey_t const * authority_owner,
1372 : fd_pubkey_t const * new_authority,
1373 : fd_stake_authorize_t const * stake_authorize,
1374 : fd_sol_sysvar_clock_t const * clock,
1375 0 : fd_pubkey_t const * custodian ) {
1376 0 : int rc;
1377 0 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = {0};
1378 0 : fd_pubkey_t out = {0};
1379 0 : ulong signers_cnt = 0UL;
1380 0 : if( FD_LIKELY( fd_instr_acc_is_signer_idx( ctx->instr, authority_base_index, NULL ) ) ) {
1381 :
1382 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L289
1383 0 : fd_pubkey_t const * base_pubkey = NULL;
1384 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, authority_base_index, &base_pubkey );
1385 0 : if( FD_UNLIKELY( rc ) ) return rc;
1386 :
1387 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L293
1388 0 : rc = fd_pubkey_create_with_seed( ctx,
1389 0 : base_pubkey->uc,
1390 0 : authority_seed,
1391 0 : authority_seed_len,
1392 0 : authority_owner->uc,
1393 0 : /* out */ out.uc );
1394 0 : if( FD_UNLIKELY( rc ) ) return rc;
1395 0 : signers[0] = &out;
1396 0 : signers_cnt = 1UL;
1397 0 : }
1398 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L299
1399 0 : return authorize( stake_account,
1400 0 : signers,
1401 0 : signers_cnt,
1402 0 : new_authority,
1403 0 : stake_authorize,
1404 0 : clock,
1405 0 : custodian,
1406 0 : &ctx->txn_out->err.custom_err );
1407 0 : }
1408 :
1409 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L310
1410 : static int
1411 : delegate( fd_exec_instr_ctx_t const * ctx,
1412 : uchar stake_account_index,
1413 : uchar vote_account_index,
1414 : fd_sol_sysvar_clock_t const * clock,
1415 : fd_stake_history_t const * stake_history,
1416 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1417 0 : ulong signers_cnt ) {
1418 0 : int rc;
1419 :
1420 0 : fd_pubkey_t const * vote_pubkey;
1421 0 : fd_vote_state_versioned_t * vote_state = NULL;
1422 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L321-L322 */
1423 0 : fd_guarded_borrowed_account_t vote_account = {0};
1424 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, vote_account_index, &vote_account );
1425 :
1426 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L323
1427 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &vote_account ), fd_solana_vote_program_id.key, 32UL ) ) )
1428 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1429 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L3326
1430 0 : vote_pubkey = vote_account.pubkey;
1431 : // https://github.com/anza-xyz/agave/blob/a60fbc2288d626a4f1846052c8fcb98d3f9ea58d/programs/stake/src/stake_state.rs#L327
1432 0 : vote_state = fd_vote_get_state( vote_account.meta, ctx->runtime->stake_program.delegate.vote_state_mem );
1433 :
1434 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L328 */
1435 0 : fd_borrowed_account_drop( &vote_account );
1436 :
1437 0 : fd_stake_state_v2_t stake_state = {0};
1438 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L330 */
1439 0 : fd_guarded_borrowed_account_t stake_account = {0};
1440 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
1441 :
1442 0 : rc = get_state( stake_account.meta, &stake_state );
1443 0 : if( FD_UNLIKELY( rc ) ) return rc;
1444 :
1445 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L332
1446 0 : switch( stake_state.discriminant ) {
1447 0 : case fd_stake_state_v2_enum_initialized: {
1448 0 : fd_stake_meta_t meta = stake_state.inner.initialized.meta;
1449 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L334
1450 0 : rc = authorized_check( &meta.authorized, signers, signers_cnt, STAKE_AUTHORIZE_STAKER );
1451 0 : if( FD_UNLIKELY( rc ) ) return rc;
1452 :
1453 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L335-L336
1454 0 : validated_delegated_info_t validated_delegated_info;
1455 0 : rc = validate_delegated_amount( ctx->bank,
1456 0 : &stake_account,
1457 0 : &meta,
1458 0 : &validated_delegated_info,
1459 0 : &ctx->txn_out->err.custom_err );
1460 0 : if( FD_UNLIKELY( rc ) ) return rc;
1461 0 : ulong stake_amount = validated_delegated_info.stake_amount;
1462 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L340
1463 0 : if( FD_UNLIKELY( !vote_state ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1464 0 : fd_vote_convert_to_current( vote_state,
1465 0 : ctx->runtime->stake_program.delegate.authorized_voters_mem,
1466 0 : ctx->runtime->stake_program.delegate.landed_votes_mem );
1467 0 : fd_stake_t stake = new_stake( stake_amount,
1468 0 : vote_pubkey,
1469 0 : &vote_state->inner.v3,
1470 0 : clock->epoch );
1471 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L343
1472 0 : fd_stake_state_v2_t new_stake_state = { .discriminant = fd_stake_state_v2_enum_stake,
1473 0 : .inner = { .stake = {
1474 0 : .meta = meta,
1475 0 : .stake = stake,
1476 0 : .stake_flags = STAKE_FLAGS_EMPTY } } };
1477 0 : return set_state( &stake_account, &new_stake_state );
1478 0 : }
1479 0 : case fd_stake_state_v2_enum_stake: {
1480 0 : fd_stake_meta_t meta = stake_state.inner.stake.meta;
1481 0 : fd_stake_t stake = stake_state.inner.stake.stake;
1482 0 : fd_stake_flags_t stake_flags = stake_state.inner.stake.stake_flags;
1483 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L346
1484 0 : rc = authorized_check( &meta.authorized, signers, signers_cnt, STAKE_AUTHORIZE_STAKER );
1485 0 : if( FD_UNLIKELY( rc ) ) return rc;
1486 :
1487 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L347-L348
1488 0 : validated_delegated_info_t validated_delegated_info;
1489 0 : rc = validate_delegated_amount( ctx->bank,
1490 0 : &stake_account,
1491 0 : &meta,
1492 0 : &validated_delegated_info,
1493 0 : &ctx->txn_out->err.custom_err );
1494 0 : if( FD_UNLIKELY( rc ) ) return rc;
1495 0 : ulong stake_amount = validated_delegated_info.stake_amount;
1496 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L354
1497 0 : if( FD_UNLIKELY( !vote_state ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1498 0 : fd_vote_convert_to_current( vote_state,
1499 0 : ctx->runtime->stake_program.delegate.authorized_voters_mem,
1500 0 : ctx->runtime->stake_program.delegate.landed_votes_mem );
1501 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L349
1502 0 : rc = redelegate_stake( ctx,
1503 0 : &stake,
1504 0 : stake_amount,
1505 0 : vote_pubkey,
1506 0 : &vote_state->inner.v3,
1507 0 : clock,
1508 0 : stake_history,
1509 0 : &ctx->txn_out->err.custom_err );
1510 0 : if( FD_UNLIKELY( rc ) ) return rc;
1511 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L358
1512 0 : fd_stake_state_v2_t new_stake_state = { .discriminant = fd_stake_state_v2_enum_stake,
1513 0 : .inner = { .stake = {
1514 0 : .meta = meta,
1515 0 : .stake = stake,
1516 0 : .stake_flags = stake_flags } } };
1517 :
1518 0 : return set_state( &stake_account, &new_stake_state );
1519 0 : }
1520 0 : default:
1521 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L360
1522 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1523 0 : }
1524 :
1525 : /* implicit drop of stake account */
1526 0 : }
1527 :
1528 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L364
1529 : static int
1530 : deactivate( fd_borrowed_account_t * stake_account,
1531 : fd_sol_sysvar_clock_t const * clock,
1532 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1533 : ulong signers_cnt,
1534 0 : uint * custom_err ) {
1535 0 : int rc;
1536 :
1537 0 : fd_stake_state_v2_t state = {0};
1538 0 : rc = get_state( stake_account->meta, &state );
1539 0 : if( FD_UNLIKELY( rc ) ) return rc;
1540 :
1541 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L370
1542 0 : if( state.discriminant==fd_stake_state_v2_enum_stake ) {
1543 0 : fd_stake_meta_t * meta = &state.inner.stake.meta;
1544 0 : fd_stake_t * stake = &state.inner.stake.stake;
1545 :
1546 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L371
1547 0 : rc = authorized_check( &meta->authorized, signers, signers_cnt, STAKE_AUTHORIZE_STAKER );
1548 0 : if( FD_UNLIKELY( rc ) ) return rc;
1549 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L372
1550 0 : rc = stake_deactivate( stake, clock->epoch, custom_err );
1551 0 : if( FD_UNLIKELY( rc ) ) return rc;
1552 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L373
1553 0 : return set_state( stake_account, &state );
1554 0 : } else {
1555 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L375
1556 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1557 0 : }
1558 0 : }
1559 :
1560 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L379
1561 : static int
1562 : set_lockup( fd_borrowed_account_t * stake_account,
1563 : fd_lockup_args_t const * lockup,
1564 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1565 : ulong signers_cnt,
1566 0 : fd_sol_sysvar_clock_t const * clock ) {
1567 0 : int rc;
1568 :
1569 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L385
1570 0 : fd_stake_state_v2_t state = {0};
1571 0 : rc = get_state( stake_account->meta, &state );
1572 0 : if( FD_UNLIKELY( rc ) ) return rc;
1573 :
1574 0 : switch( state.discriminant ) {
1575 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L386
1576 0 : case fd_stake_state_v2_enum_initialized: {
1577 0 : fd_stake_meta_t * meta = &state.inner.initialized.meta;
1578 0 : rc = set_lockup_meta( meta, lockup, signers, signers_cnt, clock );
1579 0 : if( FD_UNLIKELY( rc ) ) return rc;
1580 0 : return set_state( stake_account, &state );
1581 0 : }
1582 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L390
1583 0 : case fd_stake_state_v2_enum_stake: {
1584 0 : fd_stake_meta_t * meta = &state.inner.stake.meta;
1585 0 : rc = set_lockup_meta( meta, lockup, signers, signers_cnt, clock );
1586 0 : if( FD_UNLIKELY( rc ) ) return rc;
1587 0 : return set_state( stake_account, &state );
1588 0 : }
1589 0 : default:
1590 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L394
1591 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1592 0 : }
1593 0 : }
1594 :
1595 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L398
1596 : static int
1597 : split( fd_exec_instr_ctx_t const * ctx,
1598 : uchar stake_account_index,
1599 : ulong lamports,
1600 : uchar split_index,
1601 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1602 0 : ulong signers_cnt ) {
1603 0 : int rc;
1604 :
1605 0 : ulong split_lamport_balance = 0;
1606 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L407-L408 */
1607 0 : fd_guarded_borrowed_account_t split = {0};
1608 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, split_index, &split );
1609 :
1610 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L409
1611 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &split ), fd_solana_stake_program_id.key, 32UL ) ) )
1612 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1613 :
1614 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L412
1615 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &split )!=stake_state_v2_size_of() ) )
1616 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1617 :
1618 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L415
1619 0 : fd_stake_state_v2_t split_get_state = {0};
1620 0 : rc = get_state( split.meta, &split_get_state );
1621 0 : if( FD_UNLIKELY( rc ) ) return rc;
1622 0 : if( FD_UNLIKELY( split_get_state.discriminant!=fd_stake_state_v2_enum_uninitialized ) ) {
1623 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1624 0 : }
1625 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L418
1626 0 : split_lamport_balance = fd_borrowed_account_get_lamports( &split );
1627 :
1628 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L419 */
1629 0 : fd_borrowed_account_drop( &split );
1630 :
1631 0 : fd_stake_state_v2_t stake_state = {0};
1632 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L420-L421 */
1633 0 : fd_guarded_borrowed_account_t stake_account = {0};
1634 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
1635 :
1636 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L422
1637 0 : if( FD_UNLIKELY( lamports>fd_borrowed_account_get_lamports( &stake_account ) ) )
1638 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1639 :
1640 0 : rc = get_state( stake_account.meta, &stake_state );
1641 0 : if( FD_UNLIKELY( rc ) ) return rc;
1642 :
1643 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L426 */
1644 0 : fd_borrowed_account_drop( &stake_account );
1645 :
1646 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L428
1647 0 : switch( stake_state.discriminant ) {
1648 0 : case fd_stake_state_v2_enum_stake: {
1649 0 : fd_stake_meta_t * meta = &stake_state.inner.stake.meta;
1650 0 : fd_stake_t * stake = &stake_state.inner.stake.stake;
1651 0 : fd_stake_flags_t * stake_flags = &stake_state.inner.stake.stake_flags;
1652 :
1653 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L430
1654 0 : rc = authorized_check( &meta->authorized, signers, signers_cnt, STAKE_AUTHORIZE_STAKER );
1655 0 : if( FD_UNLIKELY( rc ) ) return rc;
1656 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L431
1657 0 : ulong minimum_delegation = get_minimum_delegation( ctx->bank );
1658 :
1659 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L434
1660 0 : fd_sol_sysvar_clock_t clock_;
1661 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
1662 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1663 :
1664 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L435
1665 0 : fd_stake_activation_status_t status = {0};
1666 0 : rc = get_stake_status( ctx, stake, clock, &status );
1667 0 : if( FD_UNLIKELY( rc ) ) return rc;
1668 :
1669 0 : int is_active = status.effective>0;
1670 :
1671 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L438
1672 0 : validated_split_info_t validated_split_info = {0};
1673 0 : rc = validate_split_amount( ctx,
1674 0 : stake_account_index,
1675 0 : split_index,
1676 0 : lamports,
1677 0 : meta,
1678 0 : minimum_delegation,
1679 0 : is_active,
1680 0 : &validated_split_info );
1681 0 : if( FD_UNLIKELY( rc ) ) return rc;
1682 :
1683 0 : ulong remaining_stake_delta;
1684 0 : ulong split_stake_amount;
1685 : // FIXME FD_LIKELY
1686 :
1687 0 : if( validated_split_info.source_remaining_balance==0 ) {
1688 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L456
1689 0 : remaining_stake_delta = fd_ulong_sat_sub( lamports, meta->rent_exempt_reserve );
1690 0 : split_stake_amount = remaining_stake_delta;
1691 0 : } else {
1692 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L469
1693 0 : if( FD_UNLIKELY( fd_ulong_sat_sub( stake->delegation.stake, lamports ) <
1694 0 : minimum_delegation ) ) {
1695 0 : ctx->txn_out->err.custom_err = FD_STAKE_ERR_INSUFFICIENT_DELEGATION;
1696 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1697 0 : }
1698 :
1699 0 : remaining_stake_delta = lamports;
1700 0 : split_stake_amount =
1701 0 : fd_ulong_sat_sub( lamports,
1702 0 : fd_ulong_sat_sub( validated_split_info.destination_rent_exempt_reserve,
1703 0 : split_lamport_balance )
1704 :
1705 0 : );
1706 0 : }
1707 :
1708 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L487
1709 0 : if( FD_UNLIKELY( split_stake_amount<minimum_delegation ) ) {
1710 0 : ctx->txn_out->err.custom_err = FD_STAKE_ERR_INSUFFICIENT_DELEGATION;
1711 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1712 0 : }
1713 :
1714 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L491-L493
1715 0 : fd_stake_t split_stake = {0};
1716 0 : rc = stake_split( stake,
1717 0 : remaining_stake_delta,
1718 0 : split_stake_amount,
1719 0 : &ctx->txn_out->err.custom_err,
1720 0 : &split_stake );
1721 0 : if( FD_UNLIKELY( rc ) ) return rc;
1722 0 : fd_stake_meta_t split_meta = *meta;
1723 0 : split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
1724 :
1725 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L495 */
1726 0 : fd_guarded_borrowed_account_t stake_account = {0};
1727 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
1728 :
1729 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L497
1730 0 : rc = set_state( &stake_account, &stake_state );
1731 0 : if( FD_UNLIKELY( rc ) ) return rc;
1732 :
1733 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L498 */
1734 0 : fd_borrowed_account_drop( &stake_account );
1735 :
1736 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L499 */
1737 0 : fd_guarded_borrowed_account_t split = {0};
1738 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, split_index, &split );
1739 :
1740 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L501
1741 0 : fd_stake_state_v2_t temp = { .discriminant = fd_stake_state_v2_enum_stake,
1742 0 : .inner = { .stake = { .meta = split_meta,
1743 0 : .stake = split_stake,
1744 0 : .stake_flags = *stake_flags } } };
1745 0 : rc = set_state( &split, &temp );
1746 0 : if( FD_UNLIKELY( rc ) ) return rc;
1747 :
1748 : /* implicit drop of split */
1749 0 : break;
1750 0 : }
1751 0 : case fd_stake_state_v2_enum_initialized: {
1752 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L504
1753 0 : fd_stake_meta_t * meta = &stake_state.inner.initialized.meta;
1754 0 : rc = authorized_check( &meta->authorized, signers, signers_cnt, STAKE_AUTHORIZE_STAKER );
1755 0 : if( FD_UNLIKELY( rc ) ) return rc;
1756 :
1757 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L505
1758 0 : validated_split_info_t validated_split_info = {0};
1759 0 : rc = validate_split_amount( ctx,
1760 0 : stake_account_index,
1761 0 : split_index,
1762 0 : lamports,
1763 0 : meta,
1764 0 : 0,
1765 0 : 0,
1766 0 : &validated_split_info );
1767 0 : if( FD_UNLIKELY( rc ) ) return rc;
1768 :
1769 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L516
1770 0 : fd_stake_meta_t split_meta = *meta;
1771 0 : split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
1772 :
1773 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L518 */
1774 0 : fd_guarded_borrowed_account_t split = {0};
1775 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, split_index, &split );
1776 :
1777 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L520
1778 0 : fd_stake_state_v2_t temp = { .discriminant = fd_stake_state_v2_enum_initialized,
1779 0 : .inner = { .initialized = { .meta = split_meta } } };
1780 0 : rc = set_state( &split, &temp );
1781 0 : if( FD_UNLIKELY( rc ) ) return rc;
1782 0 : break;
1783 0 : }
1784 0 : case fd_stake_state_v2_enum_uninitialized: {
1785 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L523
1786 0 : fd_pubkey_t const * stake_pubkey = NULL;
1787 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, stake_account_index, &stake_pubkey );
1788 0 : if( FD_UNLIKELY( rc ) ) return rc;
1789 :
1790 0 : if( FD_UNLIKELY( !fd_signers_contains( signers, signers_cnt, stake_pubkey ) ) ) {
1791 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L527
1792 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1793 0 : }
1794 0 : break;
1795 0 : }
1796 0 : default:
1797 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L531
1798 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1799 0 : }
1800 :
1801 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L535 */
1802 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
1803 :
1804 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L537
1805 0 : if( FD_UNLIKELY( lamports==fd_borrowed_account_get_lamports( &stake_account ) ) ) {
1806 0 : fd_stake_state_v2_t uninitialized = {0};
1807 0 : uninitialized.discriminant = fd_stake_state_v2_enum_uninitialized;
1808 0 : rc = set_state( &stake_account, &uninitialized );
1809 0 : if( FD_UNLIKELY( rc ) ) return rc;
1810 0 : };
1811 :
1812 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L540 */
1813 0 : fd_borrowed_account_drop( &stake_account );
1814 :
1815 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L542 */
1816 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, split_index, &split );
1817 :
1818 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L544
1819 0 : rc = fd_borrowed_account_checked_add_lamports( &split, lamports );
1820 0 : if( FD_UNLIKELY( rc ) ) return rc;
1821 :
1822 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L545 */
1823 0 : fd_borrowed_account_drop( &split );
1824 :
1825 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L546-L547 */
1826 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
1827 :
1828 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L548 */
1829 0 : rc = fd_borrowed_account_checked_sub_lamports( &stake_account, lamports );
1830 0 : if( FD_UNLIKELY( rc ) ) return rc;
1831 :
1832 0 : return 0;
1833 0 : }
1834 :
1835 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L552
1836 : static int
1837 : merge( fd_exec_instr_ctx_t * ctx, // not const to log
1838 : uchar stake_account_index,
1839 : uchar source_account_index,
1840 : fd_sol_sysvar_clock_t const * clock,
1841 : fd_stake_history_t const * stake_history,
1842 : fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
1843 0 : ulong signers_cnt ) {
1844 0 : int rc;
1845 :
1846 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L562 */
1847 0 : fd_guarded_borrowed_account_t source_account = {0};
1848 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, source_account_index, &source_account );
1849 :
1850 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L565
1851 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &source_account ), fd_solana_stake_program_id.key, 32UL ) ) )
1852 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1853 :
1854 0 : ushort stake_acc_idx_in_txn;
1855 0 : ushort source_acc_idx_in_txn;
1856 :
1857 0 : rc = fd_exec_instr_ctx_get_index_of_instr_account_in_transaction( ctx, stake_account_index, &stake_acc_idx_in_txn );
1858 0 : if( FD_UNLIKELY( rc ) ) return rc;
1859 :
1860 0 : rc = fd_exec_instr_ctx_get_index_of_instr_account_in_transaction( ctx, source_account_index, &source_acc_idx_in_txn );
1861 0 : if( FD_UNLIKELY( rc ) ) return rc;
1862 :
1863 : /* Close the stake_account-reference loophole
1864 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L569-L574 */
1865 0 : if( FD_UNLIKELY( stake_acc_idx_in_txn==source_acc_idx_in_txn ) )
1866 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1867 :
1868 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L575 */
1869 0 : fd_guarded_borrowed_account_t stake_account = {0};
1870 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
1871 :
1872 0 : fd_stake_state_v2_t stake_account_state = {0};
1873 0 : rc = get_state( stake_account.meta, &stake_account_state );
1874 0 : if( FD_UNLIKELY( rc ) ) return rc;
1875 :
1876 0 : merge_kind_t stake_merge_kind = {0};
1877 0 : fd_log_collector_msg_literal( ctx, "Checking if destination stake is mergeable" );
1878 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L579
1879 0 : rc = get_if_mergeable( ctx,
1880 0 : &stake_account_state,
1881 0 : fd_borrowed_account_get_lamports( &stake_account ),
1882 0 : clock,
1883 0 : stake_history,
1884 0 : &stake_merge_kind,
1885 0 : &ctx->txn_out->err.custom_err );
1886 0 : if( FD_UNLIKELY( rc ) )
1887 0 : return rc;
1888 :
1889 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L588
1890 0 : rc = authorized_check( &meta( &stake_merge_kind )->authorized, signers, signers_cnt, STAKE_AUTHORIZE_STAKER );
1891 0 : if( FD_UNLIKELY( rc ) )
1892 0 : return rc;
1893 :
1894 0 : fd_stake_state_v2_t source_account_state = {0};
1895 0 : rc = get_state( source_account.meta, &source_account_state );
1896 0 : if( FD_UNLIKELY( rc ) ) return rc;
1897 :
1898 0 : merge_kind_t source_merge_kind = {0};
1899 0 : fd_log_collector_msg_literal( ctx, "Checking if source stake is mergeable" );
1900 : //https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L594
1901 0 : rc = get_if_mergeable( ctx,
1902 0 : &source_account_state,
1903 0 : fd_borrowed_account_get_lamports( &source_account ),
1904 0 : clock,
1905 0 : stake_history,
1906 0 : &source_merge_kind,
1907 0 : &ctx->txn_out->err.custom_err );
1908 0 : if( FD_UNLIKELY( rc ) ) return rc;
1909 :
1910 0 : fd_stake_state_v2_t merged_state = {0};
1911 0 : int is_some = 0;
1912 0 : fd_log_collector_msg_literal( ctx, "Merging stake accounts" );
1913 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L603
1914 0 : rc = merge_kind_merge( stake_merge_kind,
1915 0 : ctx,
1916 0 : source_merge_kind,
1917 0 : clock,
1918 0 : &merged_state,
1919 0 : &is_some,
1920 0 : &ctx->txn_out->err.custom_err );
1921 0 : if( FD_UNLIKELY( rc ) ) return rc;
1922 0 : if( is_some ) {
1923 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L608
1924 0 : rc = set_state( &stake_account, &merged_state );
1925 0 : if( FD_UNLIKELY( rc ) ) return rc;
1926 0 : }
1927 :
1928 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L608
1929 0 : fd_stake_state_v2_t uninitialized = {0};
1930 0 : uninitialized.discriminant = fd_stake_state_v2_enum_uninitialized;
1931 0 : rc = set_state( &source_account, &uninitialized );
1932 0 : if( FD_UNLIKELY( rc ) ) return rc;
1933 :
1934 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L611-L613
1935 0 : ulong lamports = fd_borrowed_account_get_lamports( &source_account );
1936 0 : rc = fd_borrowed_account_checked_sub_lamports( &source_account, lamports );
1937 0 : if( FD_UNLIKELY( rc ) ) return rc;
1938 0 : rc = fd_borrowed_account_checked_add_lamports( &stake_account, lamports );
1939 0 : if( FD_UNLIKELY( rc ) ) return rc;
1940 :
1941 0 : return 0;
1942 0 : }
1943 :
1944 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L136
1945 : static int
1946 : move_stake_or_lamports_shared_checks( fd_exec_instr_ctx_t * invoke_context, // not const to log
1947 : fd_borrowed_account_t * source_account,
1948 : ulong lamports,
1949 : fd_borrowed_account_t * destination_account,
1950 : ushort stake_authority_index,
1951 : merge_kind_t * source_merge_kind,
1952 : merge_kind_t * destination_merge_kind,
1953 0 : uint * custom_err ) {
1954 0 : int rc;
1955 :
1956 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L145-L153
1957 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( invoke_context->instr, stake_authority_index, NULL ) ) ) {
1958 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1959 0 : }
1960 :
1961 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L132
1962 0 : fd_pubkey_t const * stake_authority_pubkey = NULL;
1963 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( invoke_context, stake_authority_index, &stake_authority_pubkey );
1964 0 : if( FD_UNLIKELY( rc ) ) return rc;
1965 :
1966 0 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { stake_authority_pubkey };
1967 :
1968 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L158
1969 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( source_account ), fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ||
1970 0 : memcmp( fd_borrowed_account_get_owner( destination_account ), fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) )
1971 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1972 :
1973 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L163
1974 0 : if( FD_UNLIKELY( !memcmp( source_account->pubkey, destination_account->pubkey, sizeof(fd_pubkey_t) ) ) )
1975 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1976 :
1977 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L168
1978 0 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( source_account ) ||
1979 0 : !fd_borrowed_account_is_writable( destination_account ) ) )
1980 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1981 :
1982 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L173
1983 0 : if( lamports==0 )
1984 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1985 :
1986 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L177-L180
1987 0 : fd_sol_sysvar_clock_t clock_;
1988 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( invoke_context->sysvar_cache, &clock_ );
1989 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1990 :
1991 0 : fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( invoke_context->sysvar_cache );
1992 0 : if( FD_UNLIKELY( !stake_history ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1993 :
1994 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L182
1995 0 : fd_stake_state_v2_t source_account_state = {0};
1996 0 : rc = get_state( source_account->meta, &source_account_state );
1997 0 : if( FD_UNLIKELY( rc ) ) return rc;
1998 :
1999 0 : rc = get_if_mergeable( invoke_context,
2000 0 : &source_account_state,
2001 0 : fd_borrowed_account_get_lamports( source_account ),
2002 0 : clock,
2003 0 : stake_history,
2004 0 : source_merge_kind,
2005 0 : &invoke_context->txn_out->err.custom_err );
2006 0 : if( FD_UNLIKELY( rc ) ) return rc;
2007 :
2008 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L191
2009 0 : rc = authorized_check( &meta( source_merge_kind )->authorized, signers, 1UL, STAKE_AUTHORIZE_STAKER );
2010 0 : if( FD_UNLIKELY( rc ) ) return rc;
2011 :
2012 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L197
2013 0 : fd_stake_state_v2_t destination_account_state = {0};
2014 0 : rc = get_state( destination_account->meta, &destination_account_state );
2015 0 : if( FD_UNLIKELY( rc ) ) return rc;
2016 :
2017 0 : rc = get_if_mergeable( invoke_context,
2018 0 : &destination_account_state,
2019 0 : fd_borrowed_account_get_lamports( destination_account ),
2020 0 : clock,
2021 0 : stake_history,
2022 0 : destination_merge_kind,
2023 0 : &invoke_context->txn_out->err.custom_err );
2024 0 : if( FD_UNLIKELY( rc ) ) return rc;
2025 :
2026 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L206
2027 0 : rc = metas_can_merge( invoke_context, meta( source_merge_kind ), meta( destination_merge_kind ), clock, custom_err );
2028 0 : if( FD_UNLIKELY( rc ) ) return rc;
2029 :
2030 0 : return 0;
2031 0 : }
2032 :
2033 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L789
2034 : static int
2035 : move_stake(fd_exec_instr_ctx_t * ctx, // not const to log
2036 : ushort source_account_index,
2037 : ulong lamports,
2038 : ushort destination_account_index,
2039 : ushort stake_authority_index,
2040 0 : uint * custom_err ) {
2041 0 : int rc;
2042 :
2043 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L626 */
2044 0 : fd_guarded_borrowed_account_t source_account = {0};
2045 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, source_account_index, &source_account );
2046 :
2047 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L629 */
2048 0 : fd_guarded_borrowed_account_t destination_account = {0};
2049 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, destination_account_index, &destination_account );
2050 :
2051 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L804
2052 0 : merge_kind_t source_merge_kind = {0};
2053 0 : merge_kind_t destination_merge_kind = {0};
2054 0 : rc = move_stake_or_lamports_shared_checks( ctx,
2055 0 : &source_account,
2056 0 : lamports,
2057 0 : &destination_account,
2058 0 : stake_authority_index,
2059 0 : &source_merge_kind,
2060 0 : &destination_merge_kind,
2061 0 : &ctx->txn_out->err.custom_err );
2062 0 : if( FD_UNLIKELY( rc ) ) return rc;
2063 :
2064 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L816
2065 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &source_account )!=stake_state_v2_size_of() ||
2066 0 : fd_borrowed_account_get_data_len( &destination_account )!=stake_state_v2_size_of() ) )
2067 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2068 :
2069 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L823
2070 0 : if( source_merge_kind.discriminant!=merge_kind_fully_active )
2071 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2072 0 : fd_stake_meta_t * source_meta = &source_merge_kind.inner.fully_active.meta;
2073 0 : fd_stake_t * source_stake = &source_merge_kind.inner.fully_active.stake;
2074 :
2075 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L827
2076 0 : ulong minimum_delegation = get_minimum_delegation( ctx->bank );
2077 :
2078 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L831
2079 0 : if( FD_UNLIKELY( source_stake->delegation.stake<lamports ) )
2080 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2081 :
2082 0 : ulong source_final_stake = source_stake->delegation.stake - lamports;
2083 :
2084 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L836
2085 0 : if( FD_UNLIKELY( source_final_stake!=0 && source_final_stake<minimum_delegation ) )
2086 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2087 :
2088 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L841
2089 0 : fd_stake_meta_t * destination_meta = NULL;
2090 0 : switch( destination_merge_kind.discriminant ) {
2091 0 : case merge_kind_fully_active: {
2092 0 : fd_stake_t * destination_stake = &destination_merge_kind.inner.fully_active.stake;
2093 :
2094 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L844
2095 0 : if( FD_UNLIKELY( memcmp( &source_stake->delegation.voter_pubkey, &destination_stake->delegation.voter_pubkey, sizeof(fd_pubkey_t) ) ) ) {
2096 0 : *custom_err = FD_STAKE_ERR_VOTE_ADDRESS_MISMATCH;
2097 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
2098 0 : }
2099 :
2100 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L848
2101 0 : ulong destination_effective_stake = 0;
2102 0 : rc = fd_ulong_checked_add( destination_stake->delegation.stake, lamports, &destination_effective_stake );
2103 0 : if( FD_UNLIKELY( rc ) ) return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
2104 :
2105 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L855
2106 0 : if( FD_UNLIKELY( destination_effective_stake<minimum_delegation ) ) {
2107 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2108 0 : }
2109 :
2110 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L859
2111 0 : rc = merge_delegation_stake_and_credits_observed(
2112 0 : ctx, destination_stake, lamports, source_stake->credits_observed );
2113 0 : if( FD_UNLIKELY( rc ) ) return rc;
2114 0 : destination_meta = &destination_merge_kind.inner.fully_active.meta;
2115 :
2116 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L867
2117 0 : fd_stake_state_v2_t new_destination_state = {
2118 0 : .discriminant = fd_stake_state_v2_enum_stake,
2119 0 : .inner = { .stake = {
2120 0 : .meta = *destination_meta,
2121 0 : .stake = *destination_stake,
2122 0 : .stake_flags = STAKE_FLAGS_EMPTY} } };
2123 0 : rc = set_state( &destination_account, &new_destination_state );
2124 0 : if( FD_UNLIKELY( rc ) ) return rc;
2125 :
2126 0 : break;
2127 0 : }
2128 0 : case merge_kind_inactive: {
2129 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L877
2130 0 : if( lamports<minimum_delegation ) {
2131 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2132 0 : }
2133 :
2134 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L881
2135 0 : fd_stake_t * destination_stake = source_stake;
2136 0 : destination_stake->delegation.stake = lamports;
2137 :
2138 0 : destination_meta = &destination_merge_kind.inner.inactive.meta;
2139 :
2140 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L886
2141 0 : fd_stake_state_v2_t new_destination_state = {
2142 0 : .discriminant = fd_stake_state_v2_enum_stake,
2143 0 : .inner = { .stake = {
2144 0 : .meta = *destination_meta,
2145 0 : .stake = *destination_stake,
2146 0 : .stake_flags = STAKE_FLAGS_EMPTY} } };
2147 0 : rc = set_state( &destination_account, &new_destination_state );
2148 0 : if( FD_UNLIKELY( rc ) ) return rc;
2149 0 : break;
2150 0 : }
2151 0 : default:
2152 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L894
2153 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2154 0 : }
2155 :
2156 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L897-L910
2157 0 : if( source_final_stake==0) {
2158 0 : fd_stake_state_v2_t new_source_state = { .discriminant = fd_stake_state_v2_enum_initialized,
2159 0 : .inner = { .initialized = { .meta = *source_meta} } };
2160 0 : rc = set_state( &source_account, &new_source_state );
2161 0 : if( FD_UNLIKELY( rc ) ) return rc;
2162 :
2163 0 : } else {
2164 0 : source_stake->delegation.stake = source_final_stake;
2165 :
2166 0 : fd_stake_state_v2_t new_source_state = { .discriminant = fd_stake_state_v2_enum_stake,
2167 0 : .inner = { .stake = { .meta = *source_meta,
2168 0 : .stake = *source_stake,
2169 0 : .stake_flags = STAKE_FLAGS_EMPTY } } };
2170 0 : rc = set_state( &source_account, &new_source_state );
2171 0 : if( FD_UNLIKELY( rc ) ) return rc;
2172 0 : }
2173 :
2174 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L911-L914
2175 0 : rc = fd_borrowed_account_checked_sub_lamports( &source_account, lamports );
2176 0 : if( FD_UNLIKELY( rc ) ) return rc;
2177 0 : rc = fd_borrowed_account_checked_add_lamports( &destination_account, lamports );
2178 0 : if( FD_UNLIKELY( rc ) ) return rc;
2179 :
2180 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L915-L923
2181 0 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &source_account )<source_meta->rent_exempt_reserve ) ||
2182 0 : fd_borrowed_account_get_lamports( &destination_account )<destination_meta->rent_exempt_reserve ) {
2183 0 : fd_log_collector_msg_literal( ctx, "Delegation calculations violated lamport balance assumptions" );
2184 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2185 0 : }
2186 :
2187 0 : return FD_EXECUTOR_INSTR_SUCCESS;
2188 0 : }
2189 :
2190 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L928
2191 : static int
2192 : move_lamports(fd_exec_instr_ctx_t * ctx, // not const to log
2193 : ushort source_account_index,
2194 : ulong lamports,
2195 : ushort destination_account_index,
2196 0 : ushort stake_authority_index ) {
2197 0 : int rc;
2198 :
2199 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L759 */
2200 0 : fd_guarded_borrowed_account_t source_account = {0};
2201 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(ctx, source_account_index, &source_account );
2202 :
2203 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L762 */
2204 0 : fd_guarded_borrowed_account_t destination_account = {0};
2205 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, destination_account_index, &destination_account );
2206 :
2207 :
2208 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L943
2209 0 : merge_kind_t source_merge_kind = {0};
2210 0 : merge_kind_t destination_merge_kind = {0};
2211 0 : rc = move_stake_or_lamports_shared_checks( ctx,
2212 0 : &source_account,
2213 0 : lamports,
2214 0 : &destination_account,
2215 0 : stake_authority_index,
2216 0 : &source_merge_kind,
2217 0 : &destination_merge_kind,
2218 0 : &ctx->txn_out->err.custom_err );
2219 0 : if( FD_UNLIKELY( rc ) ) return rc;
2220 :
2221 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L953-L963
2222 0 : ulong source_free_lamports;
2223 0 : switch( source_merge_kind.discriminant ) {
2224 0 : case merge_kind_fully_active: {
2225 0 : source_free_lamports = fd_ulong_sat_sub( fd_ulong_sat_sub( fd_borrowed_account_get_lamports( &source_account ),
2226 0 : source_merge_kind.inner.fully_active.stake.delegation.stake ),
2227 0 : source_merge_kind.inner.fully_active.meta.rent_exempt_reserve );
2228 :
2229 0 : break;
2230 0 : }
2231 0 : case merge_kind_inactive: {
2232 0 : source_free_lamports = fd_ulong_sat_sub( source_merge_kind.inner.inactive.active_stake,
2233 0 : source_merge_kind.inner.inactive.meta.rent_exempt_reserve );
2234 0 : break;
2235 0 : }
2236 0 : default:
2237 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2238 0 : }
2239 :
2240 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L964
2241 0 : if( FD_UNLIKELY( lamports>source_free_lamports ) ) {
2242 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2243 0 : }
2244 :
2245 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_state.rs#L968-L970
2246 0 : rc = fd_borrowed_account_checked_sub_lamports( &source_account, lamports );
2247 0 : if( FD_UNLIKELY( rc ) ) return rc;
2248 :
2249 0 : rc = fd_borrowed_account_checked_add_lamports( &destination_account, lamports );
2250 0 : if( FD_UNLIKELY( rc ) ) return rc;
2251 :
2252 0 : return FD_EXECUTOR_INSTR_SUCCESS;
2253 0 : }
2254 :
2255 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L797
2256 : static int
2257 : withdraw( fd_exec_instr_ctx_t const * ctx,
2258 : uchar stake_account_index,
2259 : ulong lamports,
2260 : uchar to_index,
2261 : fd_sol_sysvar_clock_t const * clock,
2262 : fd_stake_history_t const * stake_history,
2263 : uchar withdraw_authority_index,
2264 : uchar * custodian_index,
2265 0 : ulong * new_rate_activation_epoch ) {
2266 :
2267 0 : int rc;
2268 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L809
2269 0 : fd_pubkey_t const * withdraw_authority_pubkey = NULL;
2270 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, withdraw_authority_index, &withdraw_authority_pubkey );
2271 0 : if( FD_UNLIKELY( rc ) ) return rc;
2272 :
2273 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L813
2274 0 : int is_signer = fd_instr_acc_is_signer_idx( ctx->instr, withdraw_authority_index, NULL );
2275 0 : if( FD_UNLIKELY( !is_signer ) ) return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2276 :
2277 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L817
2278 0 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { withdraw_authority_pubkey };
2279 :
2280 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L819 */
2281 0 : fd_guarded_borrowed_account_t stake_account = {0};
2282 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, stake_account_index, &stake_account );
2283 :
2284 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L821
2285 0 : fd_stake_state_v2_t stake_state = {0};
2286 0 : rc = get_state( stake_account.meta, &stake_state );
2287 0 : if( FD_UNLIKELY( rc ) ) return rc;
2288 :
2289 0 : fd_stake_lockup_t lockup;
2290 0 : ulong reserve;
2291 0 : int is_staked;
2292 :
2293 0 : switch( stake_state.discriminant ) {
2294 0 : case fd_stake_state_v2_enum_stake: {
2295 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L822
2296 0 : fd_stake_meta_t * meta = &stake_state.inner.stake.meta;
2297 0 : fd_stake_t * stake = &stake_state.inner.stake.stake;
2298 :
2299 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L823
2300 0 : rc = authorized_check( &meta->authorized, signers, 1UL, STAKE_AUTHORIZE_WITHDRAWER );
2301 0 : if( FD_UNLIKELY( rc ) ) return rc;
2302 :
2303 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L826
2304 0 : ulong staked = fd_ulong_if(
2305 0 : clock->epoch>=stake->delegation.deactivation_epoch,
2306 0 : delegation_stake(
2307 0 : &stake->delegation, clock->epoch, stake_history, new_rate_activation_epoch ),
2308 0 : stake->delegation.stake );
2309 :
2310 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L837
2311 0 : ulong staked_and_reserve = ULONG_MAX;
2312 0 : rc = fd_ulong_checked_add( staked, meta->rent_exempt_reserve, &staked_and_reserve );
2313 0 : if( FD_UNLIKELY( rc ) ) return rc;
2314 :
2315 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L838
2316 0 : lockup = meta->lockup;
2317 0 : reserve = staked_and_reserve;
2318 0 : is_staked = staked!=0;
2319 0 : break;
2320 0 : }
2321 0 : case fd_stake_state_v2_enum_initialized: {
2322 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L840
2323 0 : fd_stake_meta_t * meta = &stake_state.inner.initialized.meta;
2324 :
2325 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L841
2326 0 : rc = authorized_check( &meta->authorized, signers, 1UL, STAKE_AUTHORIZE_WITHDRAWER );
2327 0 : if( FD_UNLIKELY( rc ) ) return rc;
2328 :
2329 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L844
2330 0 : lockup = meta->lockup;
2331 0 : reserve = meta->rent_exempt_reserve;
2332 0 : is_staked = 0;
2333 0 : break;
2334 0 : }
2335 0 : case fd_stake_state_v2_enum_uninitialized: {
2336 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L846
2337 0 : if( FD_UNLIKELY( !fd_signers_contains( signers, 1UL, stake_account.pubkey ) ) ) {
2338 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2339 0 : }
2340 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L850
2341 0 : memset( &lockup, 0, sizeof( fd_stake_lockup_t ) ); /* Lockup::default(); */
2342 0 : reserve = 0;
2343 0 : is_staked = 0;
2344 0 : break;
2345 0 : }
2346 0 : default:
2347 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L852
2348 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2349 0 : }
2350 :
2351 : // FIXME FD_LIKELY
2352 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L857-L871
2353 0 : fd_pubkey_t custodian_pubkey_ = {0};
2354 0 : fd_pubkey_t const * custodian_pubkey = &custodian_pubkey_;
2355 0 : if( custodian_index ) {
2356 0 : int is_signer = fd_instr_acc_is_signer_idx( ctx->instr, *custodian_index, NULL );
2357 0 : if( is_signer ) {
2358 0 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, *custodian_index, &custodian_pubkey );
2359 0 : if( FD_UNLIKELY( err ) ) return err;
2360 0 : } else {
2361 0 : custodian_pubkey = NULL;
2362 0 : }
2363 0 : } else {
2364 0 : custodian_pubkey = NULL;
2365 0 : }
2366 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L871
2367 0 : if( FD_UNLIKELY( lockup_is_in_force( &lockup, clock, custodian_pubkey ) ) ) {
2368 0 : ctx->txn_out->err.custom_err = FD_STAKE_ERR_LOCKUP_IN_FORCE;
2369 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
2370 0 : };
2371 :
2372 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L875
2373 0 : ulong lamports_and_reserve = ULONG_MAX;
2374 0 : rc = fd_ulong_checked_add( lamports, reserve, &lamports_and_reserve );
2375 0 : if( FD_UNLIKELY( rc ) ) return rc;
2376 :
2377 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L877
2378 0 : if( FD_UNLIKELY( is_staked && lamports_and_reserve>fd_borrowed_account_get_lamports( &stake_account ) ) ) {
2379 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
2380 0 : }
2381 :
2382 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L883
2383 0 : if( FD_UNLIKELY( lamports!=fd_borrowed_account_get_lamports( &stake_account ) &&
2384 0 : lamports_and_reserve>fd_borrowed_account_get_lamports( &stake_account ) ) ) {
2385 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L886
2386 0 : FD_TEST( !is_staked );
2387 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
2388 0 : }
2389 :
2390 : // FIXME FD_LIKELY
2391 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L891
2392 0 : if( lamports==fd_borrowed_account_get_lamports( &stake_account ) ) {
2393 0 : fd_stake_state_v2_t uninitialized = {0};
2394 0 : uninitialized.discriminant = fd_stake_state_v2_enum_uninitialized;
2395 0 : rc = set_state( &stake_account, &uninitialized );
2396 0 : if( FD_UNLIKELY( rc ) ) return rc;
2397 0 : }
2398 :
2399 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L895
2400 0 : rc = fd_borrowed_account_checked_sub_lamports( &stake_account, lamports );
2401 0 : if( FD_UNLIKELY( rc ) ) return rc;
2402 :
2403 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L896 */
2404 0 : fd_borrowed_account_drop( &stake_account );
2405 :
2406 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L897 */
2407 0 : fd_guarded_borrowed_account_t to = {0};
2408 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_index, &to );
2409 :
2410 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L899
2411 0 : rc = fd_borrowed_account_checked_add_lamports( &to, lamports );
2412 0 : if( FD_UNLIKELY( rc ) ) return rc;
2413 :
2414 0 : return 0;
2415 0 : }
2416 :
2417 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L903
2418 : static int
2419 : deactivate_delinquent( fd_exec_instr_ctx_t * ctx,
2420 : fd_borrowed_account_t * stake_account,
2421 : ushort delinquent_vote_account_index,
2422 : ushort reference_vote_account_index,
2423 : ulong current_epoch,
2424 0 : uint * custom_err ) {
2425 0 : int rc;
2426 :
2427 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L911
2428 0 : fd_pubkey_t const * delinquent_vote_account_pubkey = NULL;
2429 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, delinquent_vote_account_index, &delinquent_vote_account_pubkey );
2430 0 : if( FD_UNLIKELY( rc ) ) return rc;
2431 :
2432 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L915 */
2433 0 : fd_guarded_borrowed_account_t delinquent_vote_account = {0};
2434 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, delinquent_vote_account_index, &delinquent_vote_account );
2435 :
2436 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L917
2437 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &delinquent_vote_account ), fd_solana_vote_program_id.key, 32UL ) ) )
2438 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
2439 :
2440 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L920-L922
2441 0 : fd_vote_state_versioned_t * delinquent_vote_state_versioned = fd_vote_get_state( delinquent_vote_account.meta, ctx->runtime->stake_program.deactivate_delinquent.delinquent_vote_state_mem );
2442 0 : if( FD_UNLIKELY( !delinquent_vote_state_versioned ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2443 0 : fd_vote_convert_to_current( delinquent_vote_state_versioned,
2444 0 : ctx->runtime->stake_program.deactivate_delinquent.delinquent_authorized_voters_mem,
2445 0 : ctx->runtime->stake_program.deactivate_delinquent.delinquent_landed_votes_mem );
2446 0 : fd_vote_state_v3_t delinquent_vote_state = delinquent_vote_state_versioned->inner.v3;
2447 :
2448 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L924 */
2449 0 : fd_guarded_borrowed_account_t reference_vote_account = {0};
2450 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, reference_vote_account_index, &reference_vote_account );
2451 :
2452 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L926
2453 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &reference_vote_account ), fd_solana_vote_program_id.key, 32UL ) ) )
2454 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
2455 :
2456 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L929-L932
2457 0 : fd_vote_state_versioned_t * reference_vote_state_versioned = fd_vote_get_state( reference_vote_account.meta, ctx->runtime->stake_program.deactivate_delinquent.reference_vote_state_mem );
2458 0 : if( FD_UNLIKELY( !reference_vote_state_versioned ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2459 0 : fd_vote_convert_to_current( reference_vote_state_versioned,
2460 0 : ctx->runtime->stake_program.deactivate_delinquent.reference_authorized_voters_mem,
2461 0 : ctx->runtime->stake_program.deactivate_delinquent.reference_landed_votes_mem );
2462 0 : fd_vote_state_v3_t reference_vote_state = reference_vote_state_versioned->inner.v3;
2463 :
2464 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L933
2465 0 : if( !acceptable_reference_epoch_credits( reference_vote_state.epoch_credits, current_epoch ) ) {
2466 0 : ctx->txn_out->err.custom_err = FD_STAKE_ERR_INSUFFICIENT_REFERENCE_VOTES;
2467 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
2468 0 : }
2469 :
2470 0 : fd_stake_state_v2_t stake_state = {0};
2471 0 : rc = get_state( stake_account->meta, &stake_state );
2472 0 : if( FD_UNLIKELY( rc ) ) return rc;
2473 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L937
2474 0 : if( FD_LIKELY( stake_state.discriminant==fd_stake_state_v2_enum_stake ) ) {
2475 0 : fd_stake_t * stake = &stake_state.inner.stake.stake;
2476 :
2477 0 : if( FD_UNLIKELY( memcmp( &stake->delegation.voter_pubkey, delinquent_vote_account_pubkey, sizeof(fd_pubkey_t) ) ) ) {
2478 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L939
2479 0 : *custom_err = FD_STAKE_ERR_VOTE_ADDRESS_MISMATCH;
2480 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
2481 0 : }
2482 :
2483 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L944
2484 0 : if( FD_LIKELY( eligible_for_deactivate_delinquent( delinquent_vote_state.epoch_credits,
2485 0 : current_epoch ) ) ) {
2486 0 : rc = stake_deactivate( stake, current_epoch, custom_err );
2487 0 : if( FD_UNLIKELY( rc ) ) return rc;
2488 0 : rc = set_state( stake_account, &stake_state );
2489 0 : } else {
2490 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L948
2491 0 : *custom_err = FD_STAKE_ERR_MINIMUM_DELIQUENT_EPOCHS_FOR_DEACTIVATION_NOT_MET;
2492 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
2493 0 : }
2494 0 : } else {
2495 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L951
2496 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2497 0 : }
2498 :
2499 0 : return rc;
2500 0 : }
2501 :
2502 : /**********************************************************************/
2503 : /* mod stake_instruction */
2504 : /**********************************************************************/
2505 :
2506 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L25
2507 : static int
2508 : get_optional_pubkey( fd_exec_instr_ctx_t * ctx,
2509 : ushort acc_idx,
2510 : int should_be_signer,
2511 0 : /* out */ fd_pubkey_t const ** pubkey ) {
2512 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L32
2513 0 : if( FD_LIKELY( acc_idx<ctx->instr->acct_cnt ) ) {
2514 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L33
2515 0 : if( FD_UNLIKELY( should_be_signer && !fd_instr_acc_is_signer_idx( ctx->instr, acc_idx, NULL ) ) ) {
2516 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2517 0 : }
2518 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L39
2519 0 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, acc_idx, pubkey );
2520 0 : if( FD_UNLIKELY( err ) ) return err;
2521 0 : } else {
2522 0 : *pubkey = NULL;
2523 0 : }
2524 0 : return 0;
2525 0 : }
2526 :
2527 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L60
2528 : static int
2529 : get_stake_account( fd_exec_instr_ctx_t const * ctx,
2530 0 : fd_borrowed_account_t * out ) {
2531 0 : int err = fd_exec_instr_ctx_try_borrow_instr_account( ctx, 0, out );
2532 0 : if( FD_UNLIKELY( err ) ) return err;
2533 :
2534 0 : fd_borrowed_account_t * account = out;
2535 :
2536 : // https://github.com/https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L62-L65
2537 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( account ), fd_solana_stake_program_id.key, 32UL ) ) )
2538 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
2539 :
2540 0 : return FD_EXECUTOR_INSTR_SUCCESS;
2541 0 : }
2542 :
2543 : /**********************************************************************/
2544 : /* Public API */
2545 : /**********************************************************************/
2546 :
2547 : int
2548 0 : fd_stake_program_execute( fd_exec_instr_ctx_t * ctx ) {
2549 : /* Prevent execution of migrated native programs */
2550 0 : if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( ctx->bank, migrate_stake_program_to_core_bpf ) ) ) {
2551 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2552 0 : }
2553 :
2554 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
2555 :
2556 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L77
2557 0 : fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = {0};
2558 0 : ulong signers_cnt = 0UL;
2559 0 : fd_exec_instr_ctx_get_signers( ctx, signers, &signers_cnt );
2560 :
2561 0 : if( FD_UNLIKELY( ctx->instr->data_sz>FD_STAKE_INSTR_FOOTPRINT ) ) {
2562 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2563 0 : }
2564 :
2565 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L79
2566 0 : uchar instr_mem[ FD_STAKE_INSTR_FOOTPRINT ] __attribute__((aligned(alignof(fd_stake_instruction_t))));
2567 0 : fd_stake_instruction_t * instruction = fd_bincode_decode_static(
2568 0 : stake_instruction, instr_mem,
2569 0 : ctx->instr->data,
2570 0 : ctx->instr->data_sz,
2571 0 : NULL );
2572 0 : if( FD_UNLIKELY( !instruction ) ) {
2573 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2574 0 : }
2575 :
2576 : /* The EpochRewards sysvar only exists after partitioned epoch rewards is activated.
2577 : If the sysvar exists, check the `active` field */
2578 :
2579 0 : int epoch_rewards_active = 0;
2580 0 : {
2581 0 : fd_sysvar_epoch_rewards_t epoch_rewards[1];
2582 0 : if( fd_sysvar_cache_epoch_rewards_read( ctx->sysvar_cache, epoch_rewards ) ) {
2583 0 : epoch_rewards_active = epoch_rewards->active;
2584 0 : }
2585 0 : }
2586 0 : if( epoch_rewards_active && instruction->discriminant!=fd_stake_instruction_enum_get_minimum_delegation ) {
2587 0 : ctx->txn_out->err.custom_err = FD_STAKE_ERR_EPOCH_REWARDS_ACTIVE;
2588 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
2589 0 : }
2590 :
2591 0 : int rc;
2592 : // PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR AGAVE IMPL:
2593 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L84
2594 0 : switch( instruction->discriminant ) {
2595 :
2596 : /* Initialize
2597 : *
2598 : * Instruction:
2599 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L110
2600 : *
2601 : * Processor:
2602 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L85
2603 : */
2604 0 : case fd_stake_instruction_enum_initialize: {
2605 0 : fd_stake_authorized_t const * authorized = &instruction->inner.initialize.authorized;
2606 0 : fd_stake_lockup_t const * lockup = &instruction->inner.initialize.lockup;
2607 :
2608 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L86
2609 0 : fd_guarded_borrowed_account_t me = {0};
2610 0 : rc = get_stake_account( ctx, &me ); /* acquire_write */
2611 0 : if( FD_UNLIKELY( rc ) ) return rc;
2612 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L87
2613 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
2614 0 : if( FD_UNLIKELY( rc ) ) return rc;
2615 0 : fd_rent_t rent_;
2616 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2617 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2618 :
2619 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L88
2620 0 : rc = initialize( &me, authorized, lockup, rent );
2621 :
2622 : /* implicit drop */
2623 :
2624 0 : break;
2625 0 : }
2626 :
2627 : /* Authorize
2628 : *
2629 : * Instruction:
2630 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L120
2631 : *
2632 : * Processor:
2633 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L90
2634 : */
2635 0 : case fd_stake_instruction_enum_authorize: {
2636 0 : fd_pubkey_t const * authorized_pubkey = &instruction->inner.authorize.pubkey;
2637 0 : fd_stake_authorize_t const * stake_authorize = &instruction->inner.authorize.stake_authorize;
2638 :
2639 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L91
2640 0 : fd_guarded_borrowed_account_t me = {0};
2641 0 : rc = get_stake_account( ctx, &me );
2642 0 : if( FD_UNLIKELY( rc ) ) return rc;
2643 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L92
2644 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2645 0 : if( FD_UNLIKELY( rc ) ) return rc;
2646 0 : fd_sol_sysvar_clock_t clock_;
2647 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2648 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2649 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L94
2650 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<3 ) )
2651 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2652 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L95
2653 0 : fd_pubkey_t const * custodian_pubkey = NULL;
2654 0 : rc = get_optional_pubkey( ctx, 3, 0, &custodian_pubkey );
2655 0 : if( FD_UNLIKELY( rc ) ) return rc;
2656 :
2657 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L98
2658 0 : rc = authorize( &me,
2659 0 : signers,
2660 0 : signers_cnt,
2661 0 : authorized_pubkey,
2662 0 : stake_authorize,
2663 0 : clock,
2664 0 : custodian_pubkey,
2665 0 : &ctx->txn_out->err.custom_err );
2666 :
2667 : /* implicit drop */
2668 0 : break;
2669 0 : }
2670 :
2671 : /* AuthorizeWithSeed
2672 : *
2673 : * Instruction:
2674 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L211
2675 : *
2676 : * Processor:
2677 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L107
2678 : */
2679 0 : case fd_stake_instruction_enum_authorize_with_seed: {
2680 0 : fd_authorize_with_seed_args_t args = instruction->inner.authorize_with_seed;
2681 :
2682 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L108
2683 0 : fd_guarded_borrowed_account_t me = {0};
2684 0 : rc = get_stake_account( ctx, &me );
2685 0 : if( FD_UNLIKELY( rc ) ) return rc;
2686 :
2687 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L109
2688 0 : if( ctx->instr->acct_cnt<2 )
2689 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2690 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L110
2691 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2692 0 : if( FD_UNLIKELY( rc ) ) return rc;
2693 0 : fd_sol_sysvar_clock_t clock_;
2694 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2695 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2696 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L112
2697 0 : fd_pubkey_t const * custodian_pubkey = NULL;
2698 0 : rc = get_optional_pubkey( ctx, 3, 0, &custodian_pubkey );
2699 0 : if( FD_UNLIKELY( rc ) ) return rc;
2700 :
2701 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L115
2702 0 : rc = authorize_with_seed( ctx,
2703 0 : &me,
2704 0 : 1,
2705 0 : (char const *)args.authority_seed,
2706 0 : args.authority_seed_len,
2707 0 : &args.authority_owner,
2708 0 : &args.new_authorized_pubkey,
2709 0 : &args.stake_authorize,
2710 0 : clock,
2711 0 : custodian_pubkey );
2712 :
2713 : /* implicit drop */
2714 0 : break;
2715 0 : }
2716 :
2717 : /* DelegateStake
2718 : *
2719 : * Instruction:
2720 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L135
2721 : *
2722 : * Processor:
2723 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L128
2724 : */
2725 0 : case fd_stake_instruction_enum_delegate_stake: {
2726 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L129
2727 0 : fd_guarded_borrowed_account_t me = {0};
2728 0 : rc = get_stake_account( ctx, &me );
2729 0 : if( FD_UNLIKELY( rc ) ) return rc;
2730 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L130
2731 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<2 ) )
2732 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2733 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L131
2734 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2735 0 : if( FD_UNLIKELY( rc ) ) return rc;
2736 0 : fd_sol_sysvar_clock_t clock_;
2737 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2738 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2739 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L133
2740 0 : rc = fd_sysvar_instr_acct_check( ctx, 3, &fd_sysvar_stake_history_id );
2741 0 : if( FD_UNLIKELY( rc ) ) return rc;
2742 0 : if( FD_UNLIKELY( !fd_sysvar_cache_stake_history_is_valid( ctx->sysvar_cache ) ) ) {
2743 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2744 0 : }
2745 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L138
2746 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<5 ) )
2747 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2748 :
2749 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L138 */
2750 0 : fd_borrowed_account_drop( &me );
2751 :
2752 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L140
2753 0 : fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( ctx->sysvar_cache );
2754 0 : rc = delegate( ctx,
2755 0 : 0,
2756 0 : 1,
2757 0 : clock,
2758 0 : stake_history,
2759 0 : signers,
2760 0 : signers_cnt );
2761 0 : fd_sysvar_cache_stake_history_leave_const( ctx->sysvar_cache, stake_history );
2762 0 : break;
2763 0 : }
2764 :
2765 : /* Split
2766 : *
2767 : * Instruction:
2768 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L143
2769 : *
2770 : * Processor:
2771 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L152
2772 : */
2773 0 : case fd_stake_instruction_enum_split: {
2774 0 : ulong lamports = instruction->inner.split;
2775 :
2776 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L153
2777 0 : fd_guarded_borrowed_account_t me = {0};
2778 0 : rc = get_stake_account( ctx, &me );
2779 0 : if( FD_UNLIKELY( rc ) ) return rc;
2780 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L154
2781 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<2 ) )
2782 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2783 :
2784 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L154 */
2785 0 : fd_borrowed_account_drop( &me );
2786 :
2787 : //https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L156
2788 0 : rc = split( ctx, 0, lamports, 1, signers, signers_cnt );
2789 0 : break;
2790 0 : }
2791 :
2792 : /* Merge
2793 : *
2794 : * Instruction:
2795 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L201
2796 : *
2797 : * Processor:
2798 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L166
2799 : */
2800 0 : case fd_stake_instruction_enum_merge: {
2801 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L167
2802 0 : fd_guarded_borrowed_account_t me = {0};
2803 0 : rc = get_stake_account( ctx, &me );
2804 0 : if( FD_UNLIKELY( rc ) ) return rc;
2805 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L168
2806 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<2 ) )
2807 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2808 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L169
2809 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2810 0 : if( FD_UNLIKELY( rc ) ) return rc;
2811 0 : fd_sol_sysvar_clock_t clock_;
2812 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2813 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2814 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L171
2815 0 : rc = fd_sysvar_instr_acct_check( ctx, 3, &fd_sysvar_stake_history_id );
2816 0 : if( FD_UNLIKELY( rc ) ) return rc;
2817 0 : if( FD_UNLIKELY( !fd_sysvar_cache_stake_history_is_valid( ctx->sysvar_cache ) ) ) {
2818 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2819 0 : }
2820 :
2821 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L175 */
2822 0 : fd_borrowed_account_drop( &me );
2823 :
2824 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L177
2825 0 : fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( ctx->sysvar_cache );
2826 0 : rc = merge( ctx, 0, 1, clock, stake_history, signers, signers_cnt );
2827 0 : fd_sysvar_cache_stake_history_leave_const( ctx->sysvar_cache, stake_history );
2828 0 : break;
2829 0 : }
2830 :
2831 : /* Withdraw
2832 : *
2833 : * Instruction:
2834 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L157
2835 : *
2836 : * Processor:
2837 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L188
2838 : */
2839 0 : case fd_stake_instruction_enum_withdraw: {
2840 0 : ulong lamports = instruction->inner.withdraw;
2841 :
2842 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L189
2843 0 : fd_guarded_borrowed_account_t me = {0};
2844 0 : rc = get_stake_account( ctx, &me ); /* calls acquire_write */
2845 0 : if( FD_UNLIKELY( rc ) ) return rc;
2846 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L190
2847 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<2 ) )
2848 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2849 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L191
2850 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
2851 0 : if( FD_UNLIKELY( rc ) ) return rc;
2852 0 : fd_sol_sysvar_clock_t clock_;
2853 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2854 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2855 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L193
2856 0 : rc = fd_sysvar_instr_acct_check( ctx, 3, &fd_sysvar_stake_history_id );
2857 0 : if( FD_UNLIKELY( rc ) ) return rc;
2858 0 : if( FD_UNLIKELY( !fd_sysvar_cache_stake_history_is_valid( ctx->sysvar_cache ) ) ) {
2859 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2860 0 : }
2861 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L198
2862 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<5 ) )
2863 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2864 :
2865 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L198 */
2866 0 : fd_borrowed_account_drop( &me );
2867 :
2868 0 : fd_epoch_schedule_t epoch_schedule_[1];
2869 0 : fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, epoch_schedule_ );
2870 0 : uchar custodian_index = 5;
2871 0 : ulong new_rate_activation_epoch = ULONG_MAX;
2872 0 : int err;
2873 0 : int is_some = fd_new_warmup_cooldown_rate_epoch(
2874 0 : epoch_schedule,
2875 0 : fd_bank_features_query( ctx->bank ),
2876 0 : &new_rate_activation_epoch,
2877 0 : &err );
2878 0 : if( FD_UNLIKELY( err ) ) return err;
2879 :
2880 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L200
2881 0 : fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( ctx->sysvar_cache );
2882 0 : rc = withdraw(
2883 0 : ctx,
2884 0 : 0,
2885 0 : lamports,
2886 0 : 1,
2887 0 : clock,
2888 0 : stake_history,
2889 0 : 4,
2890 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L209-L215
2891 0 : fd_ptr_if( ctx->instr->acct_cnt>=6, &custodian_index, NULL ),
2892 0 : fd_ptr_if( is_some, &new_rate_activation_epoch, NULL ) );
2893 0 : fd_sysvar_cache_stake_history_leave_const( ctx->sysvar_cache, stake_history );
2894 :
2895 0 : break;
2896 0 : }
2897 : /* Deactivate
2898 : *
2899 : * Instruction:
2900 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L165
2901 : *
2902 : * Processor:
2903 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L217
2904 : */
2905 0 : case fd_stake_instruction_enum_deactivate: {
2906 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L218
2907 0 : fd_guarded_borrowed_account_t me = {0};
2908 0 : rc = get_stake_account( ctx, &me );
2909 0 : if( FD_UNLIKELY( rc ) ) return rc;
2910 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L219
2911 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
2912 0 : if( FD_UNLIKELY( rc ) ) return rc;
2913 0 : fd_sol_sysvar_clock_t clock_;
2914 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2915 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2916 :
2917 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L221
2918 0 : rc = deactivate( &me, clock, signers, signers_cnt, &ctx->txn_out->err.custom_err );
2919 :
2920 : /* implicit drop */
2921 0 : break;
2922 0 : }
2923 :
2924 : /* SetLockup
2925 : *
2926 : * Instruction:
2927 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L175
2928 : *
2929 : * Processor:
2930 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L223
2931 : */
2932 0 : case fd_stake_instruction_enum_set_lockup: {
2933 0 : fd_lockup_args_t * lockup = &instruction->inner.set_lockup;
2934 :
2935 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L224
2936 0 : fd_guarded_borrowed_account_t me = {0};
2937 0 : rc = get_stake_account( ctx, &me );
2938 0 : if( FD_UNLIKELY( rc ) ) return rc;
2939 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L225
2940 0 : fd_sol_sysvar_clock_t clock_;
2941 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
2942 0 : if( FD_UNLIKELY( !clock ) )
2943 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2944 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L226
2945 0 : rc = set_lockup( &me, lockup, signers, signers_cnt, clock );
2946 0 : break;
2947 0 : }
2948 :
2949 : /* InitializeChecked
2950 : *
2951 : * Instruction:
2952 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L224
2953 : *
2954 : * Processor:
2955 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L228
2956 : */
2957 0 : case fd_stake_instruction_enum_initialize_checked: {
2958 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L229
2959 0 : fd_guarded_borrowed_account_t me = {0};
2960 0 : rc = get_stake_account( ctx, &me );
2961 0 : if( FD_UNLIKELY( rc ) ) return rc;
2962 :
2963 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L230
2964 0 : rc = fd_exec_instr_ctx_check_num_insn_accounts( ctx, 4UL );
2965 0 : if( FD_UNLIKELY( rc ) ) return rc;
2966 :
2967 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L230-L236 */
2968 0 : fd_pubkey_t const * staker_pubkey = NULL;
2969 0 : fd_pubkey_t const * withdrawer_pubkey = NULL;
2970 :
2971 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &staker_pubkey );
2972 0 : if( FD_UNLIKELY( rc ) ) return rc;
2973 :
2974 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &withdrawer_pubkey );
2975 0 : if( FD_UNLIKELY( rc ) ) return rc;
2976 :
2977 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L237
2978 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, NULL ) ) )
2979 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2980 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L241
2981 0 : fd_stake_authorized_t authorized = { .staker = *staker_pubkey,
2982 0 : .withdrawer = *withdrawer_pubkey };
2983 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L246
2984 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
2985 0 : if( FD_UNLIKELY( rc ) ) return rc;
2986 0 : fd_rent_t rent_;
2987 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
2988 0 : if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2989 :
2990 0 : fd_stake_lockup_t lockup_default = {0};
2991 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L247
2992 0 : rc = initialize( &me, &authorized, &lockup_default, rent );
2993 :
2994 : /* implicit drop */
2995 0 : break;
2996 0 : }
2997 :
2998 : /* AuthorizeChecked
2999 : *
3000 : * Instruction:
3001 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L238
3002 : *
3003 : * Processor:
3004 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L249
3005 : */
3006 0 : case fd_stake_instruction_enum_authorize_checked: {
3007 0 : fd_stake_authorize_t const * stake_authorize = &instruction->inner.authorize_checked;
3008 :
3009 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L250
3010 0 : fd_guarded_borrowed_account_t me = {0};
3011 0 : rc = get_stake_account( ctx, &me );
3012 0 : if( FD_UNLIKELY( rc ) ) return rc;
3013 :
3014 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L251
3015 0 : rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
3016 0 : if( FD_UNLIKELY( rc ) ) return rc;
3017 0 : fd_sol_sysvar_clock_t clock_;
3018 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
3019 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
3020 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L253
3021 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<4 ) )
3022 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
3023 :
3024 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L253
3025 0 : fd_pubkey_t const * authorized_pubkey = NULL;
3026 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &authorized_pubkey );
3027 0 : if( FD_UNLIKELY( rc ) ) return rc;
3028 :
3029 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L257
3030 0 : int is_signer = fd_instr_acc_is_signer_idx( ctx->instr, 3, NULL );
3031 0 : if( FD_UNLIKELY( !is_signer ) ) return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
3032 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L260
3033 0 : fd_pubkey_t const * custodian_pubkey = NULL;
3034 0 : rc = get_optional_pubkey( ctx, 4, 0, &custodian_pubkey );
3035 0 : if( FD_UNLIKELY( rc ) ) return rc;
3036 :
3037 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L263
3038 0 : rc = authorize( &me,
3039 0 : signers,
3040 0 : signers_cnt,
3041 0 : authorized_pubkey,
3042 0 : stake_authorize,
3043 0 : clock,
3044 0 : custodian_pubkey,
3045 0 : &ctx->txn_out->err.custom_err );
3046 0 : break;
3047 0 : }
3048 :
3049 : /* AuthorizeCheckedWithSeed
3050 : *
3051 : * Instruction:
3052 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L252
3053 : *
3054 : * Processor:
3055 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L272
3056 : */
3057 0 : case fd_stake_instruction_enum_authorize_checked_with_seed: {
3058 0 : fd_authorize_checked_with_seed_args_t const * args =
3059 0 : &instruction->inner.authorize_checked_with_seed;
3060 :
3061 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L273
3062 0 : fd_guarded_borrowed_account_t me = {0};
3063 0 : rc = get_stake_account( ctx, &me );
3064 0 : if( FD_UNLIKELY( rc ) ) return rc;
3065 :
3066 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L274
3067 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<2 ) )
3068 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
3069 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L276
3070 0 : rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
3071 0 : if( FD_UNLIKELY( rc ) ) return rc;
3072 0 : fd_sol_sysvar_clock_t clock_;
3073 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
3074 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
3075 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L277
3076 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( ctx, 4U) ) )
3077 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
3078 :
3079 : // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_instruction.rs#L277-L280
3080 0 : fd_pubkey_t const * authorized_pubkey = NULL;
3081 0 : rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &authorized_pubkey );
3082 0 : if( FD_UNLIKELY( rc ) ) return rc;
3083 :
3084 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L281
3085 0 : int is_signer = fd_instr_acc_is_signer_idx( ctx->instr, 3, NULL );
3086 0 : if( FD_UNLIKELY( !is_signer ) ) return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
3087 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L284
3088 0 : fd_pubkey_t const * custodian_pubkey = NULL;
3089 0 : rc = get_optional_pubkey( ctx, 4, 0, &custodian_pubkey );
3090 0 : if( FD_UNLIKELY( rc ) ) return rc;
3091 :
3092 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L287
3093 0 : rc = authorize_with_seed( ctx,
3094 0 : &me,
3095 0 : 1,
3096 0 : (char const *)args->authority_seed,
3097 0 : args->authority_seed_len,
3098 0 : &args->authority_owner,
3099 0 : authorized_pubkey,
3100 0 : &args->stake_authorize,
3101 0 : clock,
3102 0 : custodian_pubkey );
3103 0 : break;
3104 0 : }
3105 :
3106 : /* SetLockupChecked
3107 : *
3108 : * Instruction:
3109 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L266
3110 : *
3111 : * Processor:
3112 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L300
3113 : */
3114 0 : case fd_stake_instruction_enum_set_lockup_checked: {
3115 0 : fd_lockup_checked_args_t * lockup_checked = &instruction->inner.set_lockup_checked;
3116 :
3117 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L301
3118 0 : fd_guarded_borrowed_account_t me = {0};
3119 0 : rc = get_stake_account( ctx, &me ); /* acquire_write */
3120 0 : if( FD_UNLIKELY( rc ) ) return rc;
3121 :
3122 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L302
3123 0 : fd_pubkey_t const * custodian_pubkey = NULL;
3124 0 : rc = get_optional_pubkey( ctx, 2, 1, &custodian_pubkey );
3125 0 : if( FD_UNLIKELY( rc ) ) return rc;
3126 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L305
3127 0 : fd_lockup_args_t lockup = { .unix_timestamp = lockup_checked->unix_timestamp,
3128 0 : .epoch = lockup_checked->epoch,
3129 0 : .custodian = (fd_pubkey_t *)custodian_pubkey }; // FIXME
3130 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L310
3131 0 : fd_sol_sysvar_clock_t clock_;
3132 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
3133 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
3134 :
3135 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L311
3136 0 : rc = set_lockup( &me, &lockup, signers, signers_cnt, clock );
3137 0 : break;
3138 0 : }
3139 :
3140 : /* GetMinimumDelegation
3141 : *
3142 : * Instruction:
3143 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L278
3144 : *
3145 : * Processor:
3146 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L313
3147 : */
3148 0 : case fd_stake_instruction_enum_get_minimum_delegation: {
3149 0 : ulong minimum_delegation = get_minimum_delegation( ctx->bank );
3150 0 : fd_memcpy( &ctx->txn_out->details.return_data.program_id, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t));
3151 0 : fd_memcpy(ctx->txn_out->details.return_data.data, (uchar*)(&minimum_delegation), sizeof(ulong));
3152 0 : ctx->txn_out->details.return_data.len = sizeof(ulong);
3153 0 : rc = 0;
3154 0 : goto done;
3155 0 : }
3156 :
3157 : /* DeactivateDelinquent
3158 : *
3159 : * Instruction:
3160 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/instruction.rs#L291
3161 : *
3162 : * Processor:
3163 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L321
3164 : */
3165 0 : case fd_stake_instruction_enum_deactivate_delinquent: {
3166 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L322
3167 0 : fd_guarded_borrowed_account_t me = {0};
3168 0 : rc = get_stake_account( ctx, &me );
3169 0 : if( FD_UNLIKELY( rc ) ) return rc;
3170 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L323
3171 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<3 ) )
3172 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
3173 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L325
3174 0 : fd_sol_sysvar_clock_t clock_;
3175 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
3176 0 : if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
3177 :
3178 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L326
3179 0 : rc = deactivate_delinquent( ctx, &me, 1, 2, clock->epoch, &ctx->txn_out->err.custom_err );
3180 0 : break;
3181 0 : }
3182 :
3183 : /* Redelegate
3184 : *
3185 : * Deprecated:
3186 : * https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L336
3187 : */
3188 0 : case fd_stake_instruction_enum_redelegate: {
3189 0 : fd_guarded_borrowed_account_t me = {0};
3190 0 : rc = get_stake_account( ctx, &me );
3191 0 : if( FD_UNLIKELY( rc ) ) return rc;
3192 :
3193 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
3194 0 : }
3195 : /* MoveStake
3196 : *
3197 : * Instruction:
3198 : * https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/sdk/program/src/stake/instruction.rs#L330
3199 : *
3200 : * Processor:
3201 : * https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L356
3202 : */
3203 0 : case fd_stake_instruction_enum_move_stake: {
3204 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L361
3205 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<3 ) )
3206 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
3207 :
3208 0 : ulong lamports = instruction->inner.move_stake;
3209 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L362
3210 0 : rc = move_stake( ctx,
3211 0 : 0UL,
3212 0 : lamports,
3213 0 : 1UL,
3214 0 : 2UL,
3215 0 : &ctx->txn_out->err.custom_err );
3216 :
3217 0 : break;
3218 0 : }
3219 : /* MoveLamports
3220 : *
3221 : * Instruction:
3222 : * https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/sdk/program/src/stake/instruction.rs#L345
3223 : *
3224 : * Processor:
3225 : * https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L375
3226 : */
3227 0 : case fd_stake_instruction_enum_move_lamports: {
3228 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L380
3229 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt<3 ) )
3230 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
3231 :
3232 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L381
3233 0 : ulong lamports = instruction->inner.move_lamports;
3234 :
3235 : // https://github.com/anza-xyz/agave/blob/cdff19c7807b006dd63429114fb1d9573bf74172/programs/stake/src/stake_instruction.rs#L381
3236 0 : rc = move_lamports( ctx,
3237 0 : 0UL,
3238 0 : lamports,
3239 0 : 1UL,
3240 0 : 2UL );
3241 :
3242 0 : break;
3243 0 : }
3244 0 : default:
3245 0 : FD_LOG_ERR(( "unsupported stake instruction: %u", instruction->discriminant ));
3246 0 : }
3247 :
3248 0 : done:
3249 0 : return rc;
3250 0 : }
3251 :
3252 : /* Public API *********************************************************/
3253 :
3254 : static void
3255 : write_stake_config( fd_accdb_user_t * accdb,
3256 : fd_funk_txn_xid_t const * xid,
3257 0 : fd_stake_config_t const * stake_config ) {
3258 0 : ulong data_sz = fd_stake_config_size( stake_config );
3259 0 : fd_pubkey_t const * address = &fd_solana_stake_program_config_id;
3260 :
3261 0 : fd_accdb_rw_t rw[1];
3262 0 : fd_accdb_open_rw( accdb, rw, xid, address, data_sz, FD_ACCDB_FLAG_CREATE );
3263 :
3264 : /* FIXME update capitalization? */
3265 : /* FIXME set owner to Config program? */
3266 : /* FIXME Agave reflink? */
3267 : /* FIXME derive lamport balance from rent instead of hardcoding */
3268 :
3269 0 : fd_accdb_ref_lamports_set( rw, 960480UL );
3270 0 : fd_accdb_ref_exec_bit_set( rw, 0 );
3271 0 : fd_accdb_ref_data_sz_set( accdb, rw, data_sz, 0 );
3272 0 : fd_bincode_encode_ctx_t ctx = {
3273 0 : .data = fd_accdb_ref_data( rw ),
3274 0 : .dataend = (uchar *)fd_accdb_ref_data( rw ) + data_sz
3275 0 : };
3276 0 : if( fd_stake_config_encode( stake_config, &ctx ) )
3277 0 : FD_LOG_ERR( ( "fd_stake_config_encode failed" ) );
3278 :
3279 0 : fd_accdb_close_rw( accdb, rw );
3280 0 : }
3281 :
3282 : void
3283 : fd_stake_program_config_init( fd_accdb_user_t * accdb,
3284 0 : fd_funk_txn_xid_t const * xid ) {
3285 : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/config.rs#L26
3286 0 : fd_stake_config_t stake_config = {
3287 0 : .warmup_cooldown_rate = DEFAULT_WARMUP_COOLDOWN_RATE,
3288 0 : .slash_penalty = DEFAULT_SLASH_PENALTY,
3289 0 : };
3290 0 : write_stake_config( accdb, xid, &stake_config );
3291 0 : }
3292 :
3293 : int
3294 : fd_stake_get_state( fd_account_meta_t const * meta,
3295 0 : fd_stake_state_v2_t * out ) {
3296 0 : return get_state( meta, out );
3297 0 : }
3298 :
3299 : fd_stake_history_entry_t
3300 : fd_stake_activating_and_deactivating( fd_stake_delegation_t const * stake_delegation,
3301 : ulong target_epoch,
3302 : fd_stake_history_t const * stake_history,
3303 0 : ulong * new_rate_activation_epoch ) {
3304 0 : fd_delegation_t delegation = {
3305 0 : .voter_pubkey = stake_delegation->vote_account,
3306 0 : .stake = stake_delegation->stake,
3307 0 : .deactivation_epoch = stake_delegation->deactivation_epoch==USHORT_MAX ? ULONG_MAX : stake_delegation->deactivation_epoch,
3308 0 : .activation_epoch = stake_delegation->activation_epoch==USHORT_MAX ? ULONG_MAX : stake_delegation->activation_epoch,
3309 0 : .warmup_cooldown_rate = fd_stake_delegations_warmup_cooldown_rate_to_double( stake_delegation->warmup_cooldown_rate ),
3310 0 : };
3311 :
3312 0 : return stake_activating_and_deactivating(
3313 0 : &delegation, target_epoch, stake_history, new_rate_activation_epoch );
3314 0 : }
3315 :
|