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