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