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