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