Line data Source code
1 : #include "fd_system_program.h"
2 : #include "../fd_account.h"
3 : #include "../fd_acc_mgr.h"
4 : #include "../fd_system_ids.h"
5 : #include "../context/fd_exec_slot_ctx.h"
6 : #include "../context/fd_exec_txn_ctx.h"
7 : #include "../sysvar/fd_sysvar_rent.h"
8 : #include "../fd_executor.h"
9 :
10 : static int
11 : require_acct( fd_exec_instr_ctx_t * ctx,
12 : ulong idx,
13 4617 : fd_pubkey_t const * pubkey ) {
14 :
15 : /* https://github.com/solana-labs/solana/blob/v1.17.23/program-runtime/src/sysvar_cache.rs#L228-L229 */
16 :
17 4617 : if( FD_UNLIKELY( ctx->instr->acct_cnt <= idx ) )
18 66 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
19 :
20 : /* https://github.com/solana-labs/solana/blob/v1.17.23/program-runtime/src/sysvar_cache.rs#L230-L232 */
21 :
22 4551 : if( FD_UNLIKELY( 0!=memcmp( ctx->instr->acct_pubkeys[idx].uc, pubkey->uc, sizeof(fd_pubkey_t) ) ) )
23 981 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
24 :
25 3570 : return FD_EXECUTOR_INSTR_SUCCESS;
26 4551 : }
27 :
28 : static int
29 : require_acct_rent( fd_exec_instr_ctx_t * ctx,
30 : ulong idx,
31 987 : fd_rent_t const ** out_rent ) {
32 :
33 987 : do {
34 987 : int err = require_acct( ctx, idx, &fd_sysvar_rent_id );
35 987 : if( FD_UNLIKELY( err ) ) return err;
36 987 : } while(0);
37 :
38 927 : fd_rent_t const * rent = fd_sysvar_cache_rent( ctx->slot_ctx->sysvar_cache );
39 927 : if( FD_UNLIKELY( !rent ) )
40 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
41 :
42 927 : *out_rent = rent;
43 927 : return FD_EXECUTOR_INSTR_SUCCESS;
44 927 : }
45 :
46 : static int
47 : require_acct_recent_blockhashes(
48 : fd_exec_instr_ctx_t * ctx,
49 : ulong idx,
50 3630 : fd_recent_block_hashes_t const ** out ) {
51 :
52 3630 : do {
53 3630 : int err = require_acct( ctx, idx, &fd_sysvar_recent_block_hashes_id );
54 3630 : if( FD_UNLIKELY( err ) ) return err;
55 3630 : } while(0);
56 :
57 2643 : fd_recent_block_hashes_t const * rbh = fd_sysvar_cache_recent_block_hashes( ctx->slot_ctx->sysvar_cache );
58 2643 : if( FD_UNLIKELY( !rbh ) )
59 27 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
60 :
61 2616 : *out = rbh;
62 2616 : return FD_EXECUTOR_INSTR_SUCCESS;
63 2643 : }
64 :
65 : /* most_recent_block_hash mirrors
66 : solana_runtime::bank::Bank::last_blockhash_and_lamports_per_signature
67 :
68 : https://github.com/solana-labs/solana/blob/v1.17.23/runtime/src/bank.rs#L4033-L4040 */
69 :
70 : static int
71 : most_recent_block_hash( fd_exec_instr_ctx_t * ctx,
72 1752 : fd_hash_t * out ) {
73 :
74 1752 : fd_block_block_hash_entry_t * hashes = ctx->slot_ctx->slot_bank.recent_block_hashes.hashes;
75 1752 : if( deq_fd_block_block_hash_entry_t_empty( hashes ) ) {
76 0 : ctx->txn_ctx->custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES;
77 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
78 0 : }
79 :
80 1752 : *out = deq_fd_block_block_hash_entry_t_peek_head_const( hashes )->blockhash;
81 1752 : return FD_EXECUTOR_INSTR_SUCCESS;
82 1752 : }
83 :
84 : static void
85 : fd_durable_nonce_from_blockhash( fd_hash_t * out,
86 9159 : fd_hash_t const * blockhash ) {
87 9159 : uchar buf[45];
88 9159 : memcpy( buf, "DURABLE_NONCE", 13UL );
89 9159 : memcpy( buf+13, blockhash, 32UL );
90 9159 : fd_sha256_hash( buf, sizeof(buf), out );
91 9159 : }
92 :
93 : /* fd_system_program_set_nonce_state is a helper for updating the
94 : contents of a nonce account.
95 :
96 : Matches solana_sdk::transaction_context::BorrowedAccount::set_state
97 : https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1020-L1029 */
98 :
99 : static int
100 : fd_system_program_set_nonce_state( fd_exec_instr_ctx_t * ctx,
101 : ulong acct_idx,
102 2166 : fd_nonce_state_versions_t const * new_state ) {
103 :
104 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1021
105 : => https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L868 */
106 :
107 2166 : do {
108 2166 : int err = 99999;
109 2166 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx->instr, acct_idx, &err ) ) )
110 129 : return err;
111 2166 : } while(0);
112 :
113 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1024-L1026 */
114 :
115 2037 : fd_borrowed_account_t * account = NULL;
116 2037 : do {
117 2037 : int err = fd_instr_borrowed_account_modify_idx( ctx, acct_idx, 0UL, &account );
118 2037 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
119 2037 : } while(0);
120 :
121 2037 : if( FD_UNLIKELY( fd_nonce_state_versions_size( new_state ) > account->meta->dlen ) )
122 9 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
123 :
124 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1027 */
125 :
126 2028 : do {
127 2028 : fd_bincode_encode_ctx_t encode =
128 2028 : { .data = account->data,
129 2028 : .dataend = account->data + account->meta->dlen };
130 2028 : int err = fd_nonce_state_versions_encode( new_state, &encode );
131 2028 : if( FD_UNLIKELY( err ) ) {
132 0 : return FD_EXECUTOR_INSTR_ERR_GENERIC_ERR;
133 0 : }
134 2028 : } while(0);
135 :
136 2028 : return FD_EXECUTOR_INSTR_SUCCESS;
137 2028 : }
138 :
139 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L20-L70
140 :
141 : Matches Solana Labs system_instruction::advance_nonce_account */
142 :
143 : static int
144 : fd_system_program_advance_nonce_account( fd_exec_instr_ctx_t * ctx,
145 : fd_borrowed_account_t * account,
146 1590 : ulong instr_acc_idx ) {
147 :
148 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L25-L32 */
149 :
150 1590 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
151 : /* Max msg_sz: 52 - 2 + 45 = 95 < 127 => we can use printf */
152 45 : fd_log_collector_printf_dangerous_max_127( ctx,
153 45 : "Authorize nonce account: Account %s must be writable", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ instr_acc_idx ] ) );
154 45 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
155 45 : }
156 :
157 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L34 */
158 :
159 1545 : fd_nonce_state_versions_t versions[1] = {{0}};
160 1545 : fd_bincode_decode_ctx_t decode =
161 1545 : { .data = account->const_data,
162 1545 : .dataend = account->const_data + account->const_meta->dlen,
163 1545 : .valloc = fd_scratch_virtual() };
164 1545 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( versions, &decode )!=FD_BINCODE_SUCCESS ) )
165 75 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
166 :
167 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L35 */
168 :
169 1470 : fd_nonce_state_t * state = NULL;
170 1470 : switch( versions->discriminant ) {
171 141 : case fd_nonce_state_versions_enum_legacy:
172 141 : state = &versions->inner.legacy;
173 141 : break;
174 1329 : case fd_nonce_state_versions_enum_current:
175 1329 : state = &versions->inner.current;
176 1329 : break;
177 0 : default:
178 0 : __builtin_unreachable();
179 1470 : }
180 :
181 1470 : switch( state->discriminant ) {
182 :
183 1443 : case fd_nonce_state_enum_initialized: {
184 1443 : fd_nonce_data_t * data = &state->inner.initialized;
185 :
186 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L37-L44 */
187 :
188 1443 : if( FD_UNLIKELY( !fd_instr_any_signed( ctx->instr, &data->authority ) ) ) {
189 : /* Max msg_sz: 50 - 2 + 45 = 93 < 127 => we can use printf */
190 18 : fd_log_collector_printf_dangerous_max_127( ctx,
191 18 : "Advance nonce account: Account %s must be a signer", FD_BASE58_ENC_32_ALLOCA( &data->authority ) );
192 18 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
193 18 : }
194 :
195 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L45 */
196 :
197 1425 : fd_hash_t blockhash;
198 1425 : do {
199 1425 : int err = most_recent_block_hash( ctx, &blockhash );
200 1425 : if( FD_UNLIKELY( err ) ) return err;
201 1425 : } while(0);
202 :
203 1425 : fd_hash_t next_durable_nonce;
204 1425 : fd_durable_nonce_from_blockhash( &next_durable_nonce, &blockhash );
205 :
206 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L46-L52 */
207 :
208 1425 : if( FD_UNLIKELY( 0==memcmp( data->durable_nonce.hash, next_durable_nonce.hash, sizeof(fd_hash_t) ) ) ) {
209 15 : fd_log_collector_msg_literal( ctx, "Advance nonce account: nonce can only advance once per slot" );
210 15 : ctx->txn_ctx->custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_BLOCKHASH_NOT_EXPIRED;
211 15 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
212 15 : }
213 :
214 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L54-L58 */
215 :
216 1410 : fd_nonce_state_versions_t new_state = {
217 1410 : .discriminant = fd_nonce_state_versions_enum_current,
218 1410 : .inner = { .current = {
219 1410 : .discriminant = fd_nonce_state_enum_initialized,
220 1410 : .inner = { .initialized = {
221 1410 : .authority = data->authority,
222 1410 : .durable_nonce = next_durable_nonce,
223 1410 : .fee_calculator = {
224 1410 : .lamports_per_signature = ctx->slot_ctx->prev_lamports_per_signature
225 1410 : }
226 1410 : } }
227 1410 : } }
228 1410 : };
229 :
230 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L59 */
231 :
232 1410 : do {
233 1410 : int err = fd_system_program_set_nonce_state( ctx, instr_acc_idx, &new_state );
234 1410 : if( FD_UNLIKELY( err ) ) return err;
235 1410 : } while(0);
236 :
237 : /* Mark this as a nonce account usage. This prevents the account
238 : state from reverting in case the transaction fails. */
239 :
240 1362 : ctx->txn_ctx->nonce_accounts[ ctx->instr->acct_txn_idxs[ instr_acc_idx ] ] = 1;
241 :
242 1362 : break;
243 1410 : }
244 :
245 27 : case fd_nonce_state_enum_uninitialized: {
246 : /* Max msg_sz: 50 - 2 + 45 = 93 < 127 => we can use printf */
247 27 : fd_log_collector_printf_dangerous_max_127( ctx,
248 27 : "Advance nonce account: Account %s state is invalid", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ instr_acc_idx ] ) );
249 27 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
250 27 : }
251 :
252 1470 : } /* switch */
253 :
254 1362 : return FD_EXECUTOR_INSTR_SUCCESS;
255 1470 : }
256 :
257 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L423-L441
258 :
259 : Matches Solana Labs system_processor SystemInstruction::AdvanceNonceAccount => { ... } */
260 :
261 : int
262 1746 : fd_system_program_exec_advance_nonce_account( fd_exec_instr_ctx_t * ctx ) {
263 1746 : int err;
264 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L423-L441 */
265 :
266 1746 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
267 15 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
268 :
269 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L425-L426 */
270 :
271 1731 : uchar const instr_acc_idx = 0;
272 6924 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, instr_acc_idx, account ) {
273 :
274 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L427-L432 */
275 :
276 1731 : fd_recent_block_hashes_t const * recent_blockhashes = NULL;
277 1731 : do {
278 1731 : err = require_acct_recent_blockhashes( ctx, 1UL, &recent_blockhashes );
279 1731 : if( FD_UNLIKELY( err ) ) return err;
280 1731 : } while(0);
281 :
282 1611 : fd_block_block_hash_entry_t const * hashes = recent_blockhashes->hashes;
283 :
284 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L433-L439 */
285 :
286 1611 : if( FD_UNLIKELY( deq_fd_block_block_hash_entry_t_empty( hashes ) ) ) {
287 21 : fd_log_collector_msg_literal( ctx, "Advance nonce account: recent blockhash list is empty" );
288 21 : ctx->txn_ctx->custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES;
289 21 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
290 21 : }
291 :
292 1590 : err = fd_system_program_advance_nonce_account( ctx, account, instr_acc_idx );
293 :
294 : /* Implicit drop */
295 1731 : } FD_BORROWED_ACCOUNT_DROP( account );
296 :
297 1590 : return err;
298 1731 : }
299 :
300 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L72-L151
301 :
302 : Matches Solana Labs system_instruction::withdraw_nonce_account */
303 :
304 : static int
305 : fd_system_program_withdraw_nonce_account( fd_exec_instr_ctx_t * ctx,
306 : ulong requested_lamports,
307 534 : fd_rent_t const * rent ) {
308 :
309 534 : ulong const from_acct_idx = 0UL;
310 534 : ulong const to_acct_idx = 1UL;
311 :
312 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L82-L83 */
313 :
314 2136 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, from_acct_idx, from ) {
315 :
316 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L84-L91 */
317 :
318 534 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, 0 ) ) ) {
319 : /* Max msg_sz: 51 - 2 + 45 = 94 < 127 => we can use printf */
320 21 : fd_log_collector_printf_dangerous_max_127( ctx,
321 21 : "Withdraw nonce account: Account %s must be writable", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ 0 ] ) );
322 21 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
323 21 : }
324 :
325 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L93 */
326 :
327 513 : fd_nonce_state_versions_t versions[1] = {{0}};
328 513 : fd_bincode_decode_ctx_t decode =
329 513 : { .data = from->const_data,
330 513 : .dataend = from->const_data + from->const_meta->dlen,
331 513 : .valloc = fd_scratch_virtual() };
332 513 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( versions, &decode )!=FD_BINCODE_SUCCESS ) )
333 111 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
334 :
335 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L94 */
336 :
337 402 : fd_nonce_state_t * state = NULL;
338 402 : switch( versions->discriminant ) {
339 234 : case fd_nonce_state_versions_enum_legacy:
340 234 : state = &versions->inner.legacy;
341 234 : break;
342 168 : case fd_nonce_state_versions_enum_current:
343 168 : state = &versions->inner.current;
344 168 : break;
345 0 : default:
346 0 : __builtin_unreachable();
347 402 : }
348 :
349 402 : fd_pubkey_t signer[1] = {0};
350 402 : switch( state->discriminant ) {
351 :
352 141 : case fd_nonce_state_enum_uninitialized: {
353 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L95-L106 */
354 :
355 141 : if( FD_UNLIKELY( requested_lamports > from->const_meta->info.lamports ) ) {
356 : /* Max msg_sz: 59 - 6 + 20 + 20 = 93 < 127 => we can use printf */
357 9 : fd_log_collector_printf_dangerous_max_127( ctx,
358 9 : "Withdraw nonce account: insufficient lamports %lu, need %lu", from->const_meta->info.lamports, requested_lamports );
359 9 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
360 9 : }
361 :
362 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L105 */
363 :
364 132 : *signer = *from->pubkey;
365 :
366 132 : break;
367 141 : }
368 :
369 261 : case fd_nonce_state_enum_initialized: {
370 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L107-L132 */
371 261 : fd_nonce_data_t * data = &state->inner.initialized;
372 :
373 261 : if( requested_lamports == from->const_meta->info.lamports ) {
374 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L108-L117 */
375 :
376 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L109 */
377 :
378 45 : fd_hash_t blockhash;
379 45 : do {
380 45 : int err = most_recent_block_hash( ctx, &blockhash );
381 45 : if( FD_UNLIKELY( err ) ) return err;
382 45 : } while(0);
383 :
384 45 : fd_hash_t next_durable_nonce;
385 45 : fd_durable_nonce_from_blockhash( &next_durable_nonce, &blockhash );
386 :
387 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L110-L116 */
388 :
389 45 : if( FD_UNLIKELY( 0==memcmp( data->durable_nonce.hash, next_durable_nonce.hash, sizeof(fd_hash_t) ) ) ) {
390 0 : fd_log_collector_msg_literal( ctx, "Withdraw nonce account: nonce can only advance once per slot" );
391 0 : ctx->txn_ctx->custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_BLOCKHASH_NOT_EXPIRED;
392 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
393 0 : }
394 :
395 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L117 */
396 :
397 45 : fd_nonce_state_versions_t new_state[1] = {{
398 45 : .discriminant = fd_nonce_state_versions_enum_current,
399 45 : .inner = { .current = {
400 45 : .discriminant = fd_nonce_state_enum_uninitialized
401 45 : } }
402 45 : }};
403 :
404 45 : do {
405 45 : int err = fd_system_program_set_nonce_state( ctx, from_acct_idx, new_state );
406 45 : if( FD_UNLIKELY( err ) ) return err;
407 45 : } while(0);
408 :
409 216 : } else {
410 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L118-L130 */
411 :
412 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L120 */
413 :
414 216 : ulong min_balance = fd_rent_exempt_minimum_balance( rent, from->const_meta->dlen );
415 :
416 216 : ulong amount;
417 216 : if( FD_UNLIKELY( __builtin_uaddl_overflow( requested_lamports, min_balance, &amount ) ) )
418 9 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
419 :
420 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L121-L129 */
421 :
422 207 : if( FD_UNLIKELY( amount > from->const_meta->info.lamports ) ) {
423 : /* Max msg_sz: 59 - 6 + 20 + 20 = 93 < 127 => we can use printf */
424 15 : fd_log_collector_printf_dangerous_max_127( ctx,
425 15 : "Withdraw nonce account: insufficient lamports %lu, need %lu", from->const_meta->info.lamports, amount );
426 15 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
427 15 : }
428 :
429 207 : }
430 :
431 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L131 */
432 :
433 228 : *signer = data->authority;
434 :
435 228 : break;
436 261 : }
437 :
438 402 : } /* switch */
439 :
440 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L135-L142 */
441 :
442 360 : if( FD_UNLIKELY( !fd_instr_any_signed( ctx->instr, signer ) ) ) {
443 : /* Max msg_sz: 44 - 2 + 45 = 87 < 127 => we can use printf */
444 24 : fd_log_collector_printf_dangerous_max_127( ctx,
445 24 : "Withdraw nonce account: Account %s must sign", FD_BASE58_ENC_32_ALLOCA( signer ) );
446 24 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
447 24 : }
448 :
449 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L144 */
450 :
451 336 : do {
452 : /* TODO verify that account is writable before calling this API */
453 336 : int err = fd_account_checked_sub_lamports( ctx, from_acct_idx, requested_lamports );
454 336 : if( FD_UNLIKELY( err ) ) return err;
455 336 : } while(0);
456 :
457 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L145 */
458 :
459 534 : } FD_BORROWED_ACCOUNT_DROP( from );
460 :
461 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L146-L147 */
462 :
463 324 : fd_borrowed_account_t * to = NULL;
464 324 : do {
465 324 : int err = fd_instr_borrowed_account_modify_idx( ctx, to_acct_idx, 0UL, &to );
466 324 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
467 324 : } while(0);
468 :
469 324 : if( FD_UNLIKELY( !fd_borrowed_account_acquire_write( to ) ) )
470 0 : return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED;
471 :
472 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L148 */
473 :
474 324 : do {
475 324 : int err = fd_account_checked_add_lamports( ctx, to_acct_idx, requested_lamports );
476 324 : if( FD_UNLIKELY( err ) ) return err;
477 324 : } while(0);
478 :
479 : /* Implicit drop */
480 300 : fd_borrowed_account_release_write( to );
481 :
482 300 : return FD_EXECUTOR_INSTR_SUCCESS;
483 324 : }
484 :
485 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L442-L461
486 :
487 : Matches Solana Labs system_processor SystemInstruction::WithdrawNonceAccount { ... } => { ... } */
488 :
489 : int
490 : fd_system_program_exec_withdraw_nonce_account( fd_exec_instr_ctx_t * ctx,
491 1359 : ulong requested_lamports ) {
492 :
493 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L443 */
494 :
495 1359 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) )
496 27 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
497 :
498 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L445-L449 */
499 :
500 1332 : fd_recent_block_hashes_t const * recent_blockhashes = NULL;
501 1332 : do {
502 1332 : int err = require_acct_recent_blockhashes( ctx, 2UL, &recent_blockhashes );
503 1332 : if( FD_UNLIKELY( err ) ) return err;
504 1332 : } while(0);
505 :
506 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L450 */
507 :
508 564 : fd_rent_t const * rent = NULL;
509 564 : do {
510 564 : int err = require_acct_rent( ctx, 3UL, &rent );
511 564 : if( FD_UNLIKELY( err ) ) return err;
512 564 : } while(0);
513 :
514 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L451-L460 */
515 :
516 534 : return fd_system_program_withdraw_nonce_account( ctx, requested_lamports, rent );
517 564 : }
518 :
519 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L153-L198
520 :
521 : Matches Solana Labs system_instruction::initialize_nonce_account */
522 :
523 : static int
524 : fd_system_program_initialize_nonce_account( fd_exec_instr_ctx_t * ctx,
525 : fd_borrowed_account_t * account,
526 : ulong instr_acc_idx,
527 : fd_pubkey_t const * authorized,
528 393 : fd_rent_t const * rent ) {
529 :
530 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L159-L166 */
531 :
532 393 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
533 : /* Max msg_sz: 53 - 2 + 45 = 96 < 127 => we can use printf */
534 39 : fd_log_collector_printf_dangerous_max_127( ctx,
535 39 : "Initialize nonce account: Account %s must be writable", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ instr_acc_idx ] ) );
536 39 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
537 39 : }
538 :
539 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L168 */
540 :
541 354 : fd_nonce_state_versions_t versions[1] = {{0}};
542 354 : fd_bincode_decode_ctx_t decode =
543 354 : { .data = account->const_data,
544 354 : .dataend = account->const_data + account->const_meta->dlen,
545 354 : .valloc = fd_scratch_virtual() };
546 354 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( versions, &decode )!=FD_BINCODE_SUCCESS ) )
547 48 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
548 :
549 306 : fd_nonce_state_t * state = NULL;
550 306 : switch( versions->discriminant ) {
551 129 : case fd_nonce_state_versions_enum_legacy:
552 129 : state = &versions->inner.legacy;
553 129 : break;
554 177 : case fd_nonce_state_versions_enum_current:
555 177 : state = &versions->inner.current;
556 177 : break;
557 0 : default:
558 0 : __builtin_unreachable();
559 306 : }
560 :
561 306 : switch( state->discriminant ) {
562 :
563 294 : case fd_nonce_state_enum_uninitialized: {
564 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L169-L188 */
565 :
566 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L170 */
567 :
568 294 : ulong min_balance = fd_rent_exempt_minimum_balance( rent, account->const_meta->dlen );
569 :
570 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L171-L179 */
571 :
572 294 : if( FD_UNLIKELY( account->const_meta->info.lamports < min_balance ) ) {
573 : /* Max msg_sz: 61 - 6 + 20 + 20 = 95 < 127 => we can use printf */
574 12 : fd_log_collector_printf_dangerous_max_127( ctx,
575 12 : "Initialize nonce account: insufficient lamports %lu, need %lu", account->const_meta->info.lamports, min_balance );
576 12 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
577 12 : }
578 :
579 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L180 */
580 :
581 282 : fd_hash_t blockhash;
582 282 : do {
583 282 : int err = most_recent_block_hash( ctx, &blockhash );
584 282 : if( FD_UNLIKELY( err ) ) return err;
585 282 : } while(0);
586 :
587 282 : fd_hash_t durable_nonce;
588 282 : fd_durable_nonce_from_blockhash( &durable_nonce, &blockhash );
589 :
590 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L181-L186 */
591 :
592 282 : fd_nonce_state_versions_t new_state = {
593 282 : .discriminant = fd_nonce_state_versions_enum_current,
594 282 : .inner = { .current = {
595 282 : .discriminant = fd_nonce_state_enum_initialized,
596 282 : .inner = { .initialized = {
597 282 : .authority = *authorized,
598 282 : .durable_nonce = durable_nonce,
599 282 : .fee_calculator = {
600 282 : .lamports_per_signature = ctx->slot_ctx->prev_lamports_per_signature
601 282 : }
602 282 : } }
603 282 : } }
604 282 : };
605 :
606 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L187 */
607 :
608 282 : do {
609 282 : int err = fd_system_program_set_nonce_state( ctx, instr_acc_idx, &new_state );
610 282 : if( FD_UNLIKELY( err ) ) return err;
611 282 : } while(0);
612 :
613 258 : break;
614 282 : }
615 :
616 258 : case fd_nonce_state_enum_initialized: {
617 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L189-L196 */
618 :
619 : /* Max msg_sz: 53 - 2 + 45 = 96 < 127 => we can use printf */
620 12 : fd_log_collector_printf_dangerous_max_127( ctx,
621 12 : "Initialize nonce account: Account %s state is invalid", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ instr_acc_idx ] ) );
622 :
623 12 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
624 12 : }
625 :
626 306 : } /* switch */
627 :
628 258 : return FD_EXECUTOR_INSTR_SUCCESS;
629 306 : }
630 :
631 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L462-L481
632 :
633 : Matches Solana Labs system_processor SystemInstruction::InitializeNonceAccount { ... } => { ... } */
634 :
635 : int
636 : fd_system_program_exec_initialize_nonce_account( fd_exec_instr_ctx_t * ctx,
637 582 : fd_pubkey_t const * authorized ) {
638 582 : int err;
639 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L463 */
640 :
641 582 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
642 15 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
643 :
644 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L464-L465 */
645 :
646 567 : uchar const instr_acc_idx = 0;
647 2268 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, instr_acc_idx, account ) {
648 :
649 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L466-L471 */
650 :
651 567 : fd_recent_block_hashes_t const * recent_blockhashes = NULL;
652 567 : do {
653 567 : err = require_acct_recent_blockhashes( ctx, 1UL, &recent_blockhashes );
654 567 : if( FD_UNLIKELY( err ) ) return err;
655 567 : } while(0);
656 :
657 441 : fd_block_block_hash_entry_t const * hashes = recent_blockhashes->hashes;
658 :
659 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L472-L478 */
660 :
661 441 : if( FD_UNLIKELY( deq_fd_block_block_hash_entry_t_empty( hashes ) ) ) {
662 18 : fd_log_collector_msg_literal( ctx, "Initialize nonce account: recent blockhash list is empty" );
663 18 : ctx->txn_ctx->custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES;
664 18 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
665 18 : }
666 :
667 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L479 */
668 :
669 423 : fd_rent_t const * rent = NULL;
670 423 : do {
671 423 : err = require_acct_rent( ctx, 2UL, &rent );
672 423 : if( FD_UNLIKELY( err ) ) return err;
673 423 : } while(0);
674 :
675 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L480 */
676 :
677 393 : err = fd_system_program_initialize_nonce_account( ctx, account, instr_acc_idx, authorized, rent );
678 :
679 : /* Implicit drop */
680 567 : } FD_BORROWED_ACCOUNT_DROP( account );
681 :
682 393 : return err;
683 567 : }
684 :
685 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L200-L236
686 :
687 : Matches Solana Labs system_instruction::authorize_nonce_account */
688 :
689 : static int
690 : fd_system_program_authorize_nonce_account( fd_exec_instr_ctx_t * ctx,
691 : fd_borrowed_account_t * account,
692 : ulong instr_acc_idx,
693 1215 : fd_pubkey_t const * nonce_authority ) {
694 :
695 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L206-L213 */
696 :
697 1215 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
698 : /* Max msg_sz: 52 - 2 + 45 = 95 < 127 => we can use printf */
699 258 : fd_log_collector_printf_dangerous_max_127( ctx,
700 258 : "Authorize nonce account: Account %s must be writable", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ instr_acc_idx ] ) );
701 258 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
702 258 : }
703 :
704 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L214-L215 */
705 :
706 957 : fd_nonce_state_versions_t versions[1] = {{0}};
707 957 : fd_bincode_decode_ctx_t decode =
708 957 : { .data = account->const_data,
709 957 : .dataend = account->const_data + account->const_meta->dlen,
710 957 : .valloc = fd_scratch_virtual() };
711 957 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( versions, &decode )!=FD_BINCODE_SUCCESS ) )
712 492 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
713 :
714 : /* Inlining solana_program::nonce::state::Versions::authorize
715 : https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L76-L102 */
716 :
717 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L81 */
718 :
719 465 : fd_nonce_state_t * state = NULL;
720 465 : switch( versions->discriminant ) {
721 162 : case fd_nonce_state_versions_enum_legacy:
722 162 : state = &versions->inner.legacy;
723 162 : break;
724 303 : case fd_nonce_state_versions_enum_current:
725 303 : state = &versions->inner.current;
726 303 : break;
727 0 : default:
728 0 : __builtin_unreachable();
729 465 : }
730 :
731 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L81-L84 */
732 :
733 465 : if( FD_UNLIKELY( state->discriminant != fd_nonce_state_enum_initialized ) ) {
734 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L219-L226 */
735 :
736 : /* Max msg_sz: 52 - 2 + 45 = 95 < 127 => we can use printf */
737 33 : fd_log_collector_printf_dangerous_max_127( ctx,
738 33 : "Authorize nonce account: Account %s state is invalid", FD_BASE58_ENC_32_ALLOCA( &ctx->instr->acct_pubkeys[ instr_acc_idx ] ) );
739 :
740 33 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
741 33 : }
742 :
743 432 : fd_nonce_data_t * data = &state->inner.initialized;
744 :
745 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L85-L89 */
746 :
747 432 : if( FD_UNLIKELY( !fd_instr_any_signed( ctx->instr, &data->authority ) ) ) {
748 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L227-L234 */
749 : /* Max msg_sz: 45 - 2 + 45 = 88 < 127 => we can use printf */
750 162 : fd_log_collector_printf_dangerous_max_127( ctx,
751 162 : "Authorize nonce account: Account %s must sign", FD_BASE58_ENC_32_ALLOCA( &data->authority ) );
752 162 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
753 162 : }
754 :
755 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L90-L95 */
756 :
757 270 : fd_nonce_state_t new_state[1] = {{
758 270 : .discriminant = fd_nonce_state_enum_initialized,
759 270 : .inner = { .initialized = {
760 270 : .authority = *nonce_authority,
761 270 : .durable_nonce = data->durable_nonce,
762 270 : .fee_calculator = data->fee_calculator
763 270 : } }
764 270 : }};
765 :
766 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L96-L101 */
767 :
768 270 : fd_nonce_state_versions_t new_versioned[1] = {{0}};
769 270 : new_versioned->discriminant = versions->discriminant;
770 270 : switch( versions->discriminant ) {
771 105 : case fd_nonce_state_versions_enum_legacy:
772 105 : new_versioned->inner.legacy = *new_state;
773 105 : break;
774 165 : case fd_nonce_state_versions_enum_current:
775 165 : new_versioned->inner.current = *new_state;
776 165 : break;
777 0 : default:
778 0 : __builtin_unreachable();
779 270 : }
780 :
781 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L218 */
782 :
783 270 : do {
784 270 : int err = fd_system_program_set_nonce_state( ctx, instr_acc_idx, new_versioned );
785 270 : if( FD_UNLIKELY( err ) ) return err;
786 270 : } while(0);
787 :
788 222 : return FD_EXECUTOR_INSTR_SUCCESS;
789 270 : }
790 :
791 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L482-L487
792 :
793 : Matches Solana Labs system_processor SystemInstruction::AuthorizeNonceAccount { ... } => { ... } */
794 :
795 : int
796 : fd_system_program_exec_authorize_nonce_account( fd_exec_instr_ctx_t * ctx,
797 1221 : fd_pubkey_t const * nonce_authority ) {
798 1221 : int err;
799 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L483 */
800 :
801 1221 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
802 6 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
803 :
804 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L484-L485 */
805 :
806 4860 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, 0, account ) {
807 :
808 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L486 */
809 :
810 1215 : err = fd_system_program_authorize_nonce_account( ctx, account, 0UL, nonce_authority );
811 :
812 : /* Implicit drop */
813 1215 : } FD_BORROWED_ACCOUNT_DROP( account );
814 :
815 1215 : return err;
816 1215 : }
817 :
818 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L488-L503
819 :
820 : Matches Solana Labs system_processor SystemInstruction::UpgradeNonceAccount { ... } => { ... } */
821 :
822 : int
823 612 : fd_system_program_exec_upgrade_nonce_account( fd_exec_instr_ctx_t * ctx ) {
824 :
825 612 : ulong const nonce_acct_idx = 0UL;
826 :
827 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L489 */
828 :
829 612 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
830 6 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
831 :
832 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L490-L491 */
833 :
834 2424 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, nonce_acct_idx, account ) {
835 :
836 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L492-L494 */
837 :
838 606 : if( FD_UNLIKELY( 0!=memcmp( account->const_meta->info.owner, fd_solana_system_program_id.key, sizeof(fd_pubkey_t) ) ) )
839 114 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
840 :
841 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L495-L497 */
842 :
843 492 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, 0 ) ) )
844 21 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
845 :
846 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L498 */
847 :
848 471 : fd_nonce_state_versions_t versions[1] = {{0}};
849 471 : fd_bincode_decode_ctx_t decode =
850 471 : { .data = account->const_data,
851 471 : .dataend = account->const_data + account->const_meta->dlen,
852 471 : .valloc = fd_scratch_virtual() };
853 471 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( versions, &decode )!=FD_BINCODE_SUCCESS ) )
854 258 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
855 :
856 : /* Inlining solana_program::nonce::state::Versions::upgrade
857 : https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L55-L73 */
858 :
859 213 : if( FD_UNLIKELY( versions->discriminant != fd_nonce_state_versions_enum_legacy ) )
860 30 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
861 :
862 183 : fd_nonce_state_t * state = &versions->inner.legacy;
863 183 : if( FD_UNLIKELY( state->discriminant != fd_nonce_state_enum_initialized ) )
864 24 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
865 :
866 159 : fd_durable_nonce_from_blockhash( &state->inner.initialized.durable_nonce, &state->inner.initialized.durable_nonce );
867 :
868 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L501 */
869 :
870 159 : fd_nonce_state_versions_t new_state[1] = {{
871 159 : .discriminant = fd_nonce_state_versions_enum_current,
872 159 : .inner = { .current = *state }
873 159 : }};
874 :
875 159 : do {
876 159 : int err = fd_system_program_set_nonce_state( ctx, nonce_acct_idx, new_state );
877 159 : if( FD_UNLIKELY( err ) ) return err;
878 159 : } while(0);
879 :
880 : /* Implicit drop */
881 606 : } FD_BORROWED_ACCOUNT_DROP( account );
882 :
883 150 : return FD_EXECUTOR_INSTR_SUCCESS;
884 606 : }
885 :
886 : int
887 : fd_load_nonce_account( fd_exec_txn_ctx_t const * txn_ctx,
888 : fd_nonce_state_versions_t * state,
889 : fd_valloc_t valloc,
890 0 : int * perr ) {
891 :
892 0 : *perr = 0;
893 :
894 0 : fd_txn_t const * txn_descriptor = txn_ctx->txn_descriptor;
895 0 : fd_rawtxn_b_t const * txn_raw = txn_ctx->_txn_raw;
896 :
897 0 : if( txn_descriptor->instr_cnt == 0 ) {
898 0 : *perr = FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
899 0 : return 0;
900 0 : }
901 :
902 0 : fd_txn_instr_t const * txn_instr = &txn_descriptor->instr[0];
903 :
904 0 : if( FD_UNLIKELY( txn_instr->program_id >= txn_descriptor->acct_addr_cnt ) )
905 0 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
906 :
907 0 : fd_acct_addr_t const * tx_accs = fd_txn_get_acct_addrs( txn_descriptor, txn_raw->raw );
908 0 : fd_acct_addr_t const * prog_id = tx_accs + txn_instr->program_id;
909 :
910 0 : if( memcmp( prog_id->b, fd_solana_system_program_id.key, sizeof( fd_pubkey_t ) ) ) {
911 0 : return 0;
912 0 : }
913 :
914 0 : uchar const * instr_data = fd_txn_get_instr_data( txn_instr, txn_raw->raw );
915 0 : uchar const * instr_acct_idxs = fd_txn_get_instr_accts( txn_instr, txn_raw->raw );
916 :
917 0 : if( FD_UNLIKELY(
918 0 : txn_instr->data_sz != 4UL ||
919 0 : FD_LOAD( uint, instr_data ) != (uint)fd_system_program_instruction_enum_advance_nonce_account ) ) {
920 0 : *perr = FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
921 0 : return 0;
922 0 : }
923 :
924 0 : if( FD_UNLIKELY( ( txn_descriptor->acct_addr_cnt < 1 ) |
925 0 : ( txn_instr->acct_cnt < 1 ) ) ) {
926 0 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
927 0 : }
928 0 : if( FD_UNLIKELY( instr_acct_idxs[0] >= txn_descriptor->acct_addr_cnt ) ) {
929 0 : *perr = FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
930 0 : return 0;
931 0 : }
932 :
933 0 : fd_acct_addr_t const * me = &tx_accs[ instr_acct_idxs[0] ];
934 :
935 : // https://github.com/firedancer-io/solana/blob/ffd63324f988e1b2151dab34983c71d6ff4087f6/runtime/src/nonce_keyed_account.rs#L66
936 :
937 0 : FD_BORROWED_ACCOUNT_DECL(me_rec);
938 0 : int err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, (fd_pubkey_t const *)me, me_rec );
939 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
940 :
941 0 : *perr = FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
942 0 : return 0;
943 0 : }
944 :
945 0 : fd_bincode_decode_ctx_t decode = {
946 0 : .data = me_rec->const_data,
947 0 : .dataend = me_rec->const_data + me_rec->const_meta->dlen,
948 0 : .valloc = valloc
949 0 : };
950 :
951 0 : if( fd_nonce_state_versions_decode( state, &decode ) ) {
952 0 : *perr = FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
953 0 : return 0;
954 0 : }
955 :
956 0 : return 1;
957 0 : }
958 :
959 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3529-L3554 */
960 : /* The age of a transaction is valid under two conditions. The first is that
961 : the transactions blockhash is a recent blockhash (within 151) in the block
962 : hash queue. The other condition is that the transaction contains a valid
963 : nonce account. This is the case under several conditions. If neither
964 : condition is met then the transaction is invalid.
965 : Note: We check 151 and not 150 due to a known bug in agave. */
966 : int
967 7248 : fd_check_transaction_age( fd_exec_txn_ctx_t const * txn_ctx ) {
968 7248 : fd_block_hash_queue_t hash_queue = txn_ctx->slot_ctx->slot_bank.block_hash_queue;
969 7248 : fd_hash_t * last_blockhash = hash_queue.last_hash;
970 :
971 : /* check_transaction_age */
972 7248 : fd_hash_t next_durable_nonce = {0};
973 7248 : fd_durable_nonce_from_blockhash( &next_durable_nonce, last_blockhash );
974 7248 : ushort recent_blockhash_off = txn_ctx->txn_descriptor->recent_blockhash_off;
975 7248 : fd_hash_t * recent_blockhash = (fd_hash_t *)((uchar *)txn_ctx->_txn_raw->raw + recent_blockhash_off);
976 :
977 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3538-L3542 */
978 : /* get_hash_info_if_valid. Check 151 hashes from the block hash queue and its
979 : age to see if it is valid. */
980 :
981 7248 : if( fd_executor_is_blockhash_valid_for_age( &hash_queue, recent_blockhash, FD_RECENT_BLOCKHASHES_MAX_ENTRIES ) ) {
982 4755 : return FD_RUNTIME_EXECUTE_SUCCESS;
983 4755 : }
984 :
985 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3622-L3633 */
986 : /* check_and_load_message_nonce_account */
987 2493 : if( FD_UNLIKELY( !memcmp( &next_durable_nonce, recent_blockhash, sizeof(fd_hash_t) ) ) ) { /* nonce_is_advanceable == false */
988 0 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
989 0 : }
990 :
991 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3603-L3620*/
992 : /* load_message_nonce_account */
993 :
994 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/program/src/message/sanitized.rs#L345-L371 */
995 : /* get_durable_nonce */
996 2493 : if( FD_UNLIKELY( !txn_ctx->txn_descriptor->instr_cnt ) ) {
997 483 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
998 483 : }
999 : /* Check the first instruction (nonce instruction) to see if the program id
1000 : is the system program. Also make sure that it is an advance nonce account
1001 : instruction. Finally make sure that the first insutrction account is
1002 : writeable; if it is, then that account is a durable nonce account. */
1003 2010 : fd_txn_instr_t const * txn_instr = &txn_ctx->txn_descriptor->instr[0];
1004 2010 : fd_acct_addr_t const * tx_accs = fd_txn_get_acct_addrs( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
1005 2010 : fd_acct_addr_t const * prog_id = tx_accs + txn_instr->program_id;
1006 2010 : if( FD_UNLIKELY( memcmp( prog_id->b, fd_solana_system_program_id.key, sizeof( fd_pubkey_t ) ) ) ) {
1007 354 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1008 354 : }
1009 1656 : uchar const * instr_data = fd_txn_get_instr_data( txn_instr, txn_ctx->_txn_raw->raw );
1010 1656 : uchar const * instr_accts = fd_txn_get_instr_accts( txn_instr, txn_ctx->_txn_raw->raw );
1011 :
1012 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/program/src/message/sanitized.rs#L356-L358 */
1013 1656 : if( FD_UNLIKELY( txn_instr->data_sz<4UL || FD_LOAD( uint, instr_data ) !=
1014 1656 : (uint)fd_system_program_instruction_enum_advance_nonce_account ) ) {
1015 12 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1016 12 : }
1017 1644 : if( FD_UNLIKELY( !fd_txn_account_is_writable_idx( txn_ctx, instr_accts[0] ) ) ) {
1018 21 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1019 21 : }
1020 :
1021 1623 : FD_BORROWED_ACCOUNT_DECL( durable_nonce_rec );
1022 1623 : int err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->slot_ctx->funk_txn, &txn_ctx->accounts[ instr_accts[0] ], durable_nonce_rec );
1023 1623 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
1024 3 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1025 3 : }
1026 :
1027 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/src/nonce_account.rs#L28-L42 */
1028 : /* verify_nonce_account */
1029 1620 : uchar const * owner_pubkey = durable_nonce_rec->const_meta->info.owner;
1030 1620 : if( FD_UNLIKELY( memcmp( owner_pubkey, fd_solana_system_program_id.key, sizeof( fd_pubkey_t ) ) ) ) {
1031 3 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1032 3 : }
1033 :
1034 1617 : fd_bincode_decode_ctx_t decode = {
1035 1617 : .data = durable_nonce_rec->const_data,
1036 1617 : .dataend = durable_nonce_rec->const_data + durable_nonce_rec->const_meta->dlen,
1037 1617 : .valloc = fd_scratch_virtual()
1038 1617 : };
1039 :
1040 1617 : fd_nonce_state_versions_t state = {0};
1041 1617 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( &state, &decode ) ) ) {
1042 120 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1043 120 : }
1044 :
1045 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/program/src/nonce/state/mod.rs#L36-L53 */
1046 : /* verify_recent_blockhash. Thjis checks that the decoded nonce record is
1047 : not a legacy nonce nor uninitialized. If this is the case, then we can
1048 : verify by comparing the decoded durable nonce to the recent blockhash */
1049 1497 : if( FD_UNLIKELY( fd_nonce_state_versions_is_legacy( &state ) ) ) {
1050 3 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1051 3 : }
1052 :
1053 1494 : fd_nonce_state_t nonce_state = state.inner.current;
1054 1494 : if( FD_UNLIKELY( fd_nonce_state_is_uninitialized( &nonce_state ) ) ) {
1055 3 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1056 3 : }
1057 :
1058 1491 : if( FD_UNLIKELY( memcmp( &nonce_state.inner.initialized.durable_nonce, recent_blockhash, sizeof(fd_hash_t) ) ) ) {
1059 21 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1060 21 : }
1061 :
1062 : /* Finally check that the nonce is authorized by seeing if any accounts in
1063 : the nonce instruction are signers. This is a successful exit case. */
1064 4512 : for( ushort i=0; i<txn_instr->acct_cnt; ++i ) {
1065 4497 : if( fd_txn_is_signer( txn_ctx->txn_descriptor, (int)instr_accts[i] ) ) {
1066 3240 : if( !memcmp( &txn_ctx->accounts[ instr_accts[i] ], &state.inner.current.inner.initialized.authority, sizeof( fd_pubkey_t ) ) ) {
1067 1455 : return FD_RUNTIME_EXECUTE_SUCCESS;
1068 1455 : }
1069 3240 : }
1070 4497 : }
1071 : /* This means that the blockhash was not found */
1072 15 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1073 :
1074 1470 : }
|