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