Line data Source code
1 : #include "fd_system_program.h"
2 : #include "../fd_borrowed_account.h"
3 : #include "../fd_system_ids.h"
4 : #include "../sysvar/fd_sysvar_rent.h"
5 : #include "../sysvar/fd_sysvar_recent_hashes.h"
6 : #include "../../log_collector/fd_log_collector.h"
7 : #include "../../../ballet/sha256/fd_sha256.h"
8 :
9 : static int
10 : require_acct( fd_exec_instr_ctx_t * ctx,
11 : ushort idx,
12 2836 : fd_pubkey_t const * pubkey ) {
13 :
14 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/program-runtime/src/sysvar_cache.rs#L290-L294 */
15 2836 : fd_pubkey_t const * acc_key = NULL;
16 2836 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, idx, &acc_key );
17 2836 : if( FD_UNLIKELY( err ) ) return err;
18 :
19 2814 : if( FD_UNLIKELY( 0!=memcmp( acc_key, pubkey->uc, sizeof(fd_pubkey_t) ) ) )
20 326 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
21 :
22 2488 : return FD_EXECUTOR_INSTR_SUCCESS;
23 2814 : }
24 :
25 : static int
26 : require_acct_rent( fd_exec_instr_ctx_t * ctx,
27 : ushort idx,
28 874 : fd_rent_t * rent ) {
29 :
30 874 : do {
31 874 : int err = require_acct( ctx, idx, &fd_sysvar_rent_id );
32 874 : if( FD_UNLIKELY( err ) ) return err;
33 874 : } while(0);
34 :
35 853 : if( FD_UNLIKELY( !fd_sysvar_cache_rent_read( ctx->sysvar_cache, rent ) ) )
36 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
37 :
38 853 : return FD_EXECUTOR_INSTR_SUCCESS;
39 853 : }
40 :
41 : static int
42 : require_acct_recent_blockhashes( fd_exec_instr_ctx_t * ctx,
43 1964 : ushort idx ) {
44 1964 : int err = require_acct( ctx, idx, &fd_sysvar_recent_block_hashes_id );
45 1964 : if( FD_UNLIKELY( err ) ) return err;
46 :
47 1637 : if( FD_UNLIKELY( !fd_sysvar_cache_recent_hashes_is_valid( ctx->sysvar_cache ) ) ) {
48 82 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
49 82 : }
50 :
51 1555 : return FD_EXECUTOR_INSTR_SUCCESS;
52 1637 : }
53 :
54 : /* most_recent_block_hash mirrors
55 : solana_runtime::bank::Bank::last_blockhash_and_lamports_per_signature
56 :
57 : https://github.com/solana-labs/solana/blob/v1.17.23/runtime/src/bank.rs#L4033-L4040 */
58 :
59 : static int
60 : most_recent_block_hash( fd_exec_instr_ctx_t * ctx,
61 877 : fd_blockhash_info_t * out ) {
62 : /* The environment config blockhash comes from `bank.last_blockhash_and_lamports_per_signature()`,
63 : which takes the top element from the blockhash queue.
64 : https://github.com/anza-xyz/agave/blob/v2.1.6/programs/system/src/system_instruction.rs#L47 */
65 877 : fd_blockhashes_t const * blockhashes = &ctx->bank->f.block_hash_queue;
66 877 : fd_blockhash_info_t const * last_bhash_info = fd_blockhashes_peek_last( blockhashes );
67 877 : if( FD_UNLIKELY( last_bhash_info==NULL ) ) {
68 : // Agave panics if this blockhash was never set at the start of the txn batch
69 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES;
70 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
71 0 : }
72 :
73 877 : *out = *last_bhash_info;
74 877 : return FD_EXECUTOR_INSTR_SUCCESS;
75 877 : }
76 :
77 : static void
78 : fd_durable_nonce_from_blockhash( fd_hash_t * out,
79 7818 : fd_hash_t const * blockhash ) {
80 7818 : uchar buf[45];
81 7818 : memcpy( buf, "DURABLE_NONCE", 13UL );
82 7818 : memcpy( buf+13, blockhash, sizeof(fd_hash_t) );
83 7818 : fd_sha256_hash( buf, sizeof(buf), out );
84 7818 : }
85 :
86 : /* fd_system_program_set_nonce_state is a helper for updating the
87 : contents of a nonce account.
88 :
89 : Matches solana_sdk::transaction_context::BorrowedAccount::set_state
90 : https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1020-L1029 */
91 :
92 : static int
93 : fd_system_program_set_nonce_state( fd_borrowed_account_t * account,
94 1279 : fd_nonce_state_versions_t const * new_state ) {
95 :
96 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1021
97 : => https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L868 */
98 :
99 1279 : uchar * data = NULL;
100 1279 : ulong dlen = 0UL;
101 1279 : int err = fd_borrowed_account_get_data_mut( account, &data, &dlen );
102 1279 : if( FD_UNLIKELY( err ) ) return err;
103 :
104 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1024-L1026 */
105 :
106 1270 : if( FD_UNLIKELY( fd_nonce_state_versions_size( new_state ) > fd_borrowed_account_get_data_len( account ) ) )
107 150 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
108 :
109 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/transaction_context.rs#L1027 */
110 :
111 1120 : ulong written = 0UL;
112 1120 : if( FD_UNLIKELY( fd_nonce_state_versions_encode( new_state, data, dlen, &written ) ) ) {
113 0 : return FD_EXECUTOR_INSTR_ERR_GENERIC_ERR;
114 0 : }
115 :
116 1120 : return FD_EXECUTOR_INSTR_SUCCESS;
117 1120 : }
118 :
119 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L20-L70
120 :
121 : Matches Solana Labs system_instruction::advance_nonce_account */
122 :
123 : static int
124 : fd_system_program_advance_nonce_account( fd_exec_instr_ctx_t * ctx,
125 : fd_borrowed_account_t * account,
126 665 : ushort instr_acc_idx ) {
127 :
128 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L25-L32 */
129 :
130 665 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
131 : /* Max msg_sz: 50 - 2 + 45 = 93 < 127 => we can use printf */
132 18 : FD_BASE58_ENCODE_32_BYTES( account->acc->pubkey, pubkey_b58 );
133 18 : fd_log_collector_printf_dangerous_max_127( ctx,
134 18 : "Advance nonce account: Account %s must be writeable", pubkey_b58 );
135 18 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
136 18 : }
137 :
138 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L34 */
139 :
140 647 : fd_nonce_state_versions_t state[1];
141 647 : if( FD_UNLIKELY( fd_nonce_state_versions_decode(
142 647 : state,
143 647 : fd_borrowed_account_get_data( account ),
144 647 : fd_borrowed_account_get_data_len( account ) ) ) ) {
145 24 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
146 24 : }
147 :
148 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L35 */
149 :
150 623 : if( state->kind==FD_NONCE_STATE_UNINITIALIZED ) {
151 : /* Max msg_sz: 50 - 2 + 45 = 93 < 127 => we can use printf */
152 3 : FD_BASE58_ENCODE_32_BYTES( account->acc->pubkey, pubkey_b58 );
153 3 : fd_log_collector_printf_dangerous_max_127( ctx,
154 3 : "Advance nonce account: Account %s state is invalid", pubkey_b58 );
155 3 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
156 3 : }
157 :
158 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L37-L44 */
159 :
160 620 : if( FD_UNLIKELY( !fd_exec_instr_ctx_any_signed( ctx, &state->authority ) ) ) {
161 : /* Max msg_sz: 50 - 2 + 45 = 93 < 127 => we can use printf */
162 9 : FD_BASE58_ENCODE_32_BYTES( state->authority.key, authority_b58 );
163 9 : fd_log_collector_printf_dangerous_max_127( ctx,
164 9 : "Advance nonce account: Account %s must be a signer", authority_b58 );
165 9 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
166 9 : }
167 :
168 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L45 */
169 :
170 611 : fd_blockhash_info_t blockhash[1];
171 611 : do {
172 611 : int err = most_recent_block_hash( ctx, blockhash );
173 611 : if( FD_UNLIKELY( err ) ) return err;
174 611 : } while(0);
175 :
176 611 : fd_hash_t next_durable_nonce;
177 611 : fd_durable_nonce_from_blockhash( &next_durable_nonce, &blockhash->hash );
178 :
179 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L46-L52 */
180 :
181 611 : if( FD_UNLIKELY( 0==memcmp( state->durable_nonce.hash, next_durable_nonce.hash, sizeof(fd_hash_t) ) ) ) {
182 0 : fd_log_collector_msg_literal( ctx, "Advance nonce account: nonce can only advance once per slot" );
183 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_BLOCKHASH_NOT_EXPIRED;
184 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
185 0 : }
186 :
187 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/programs/system/src/system_instruction.rs#L57-L63 */
188 :
189 611 : fd_nonce_state_versions_t new_state = {
190 611 : .version = FD_NONCE_VERSION_CURRENT,
191 611 : .kind = FD_NONCE_STATE_INITIALIZED,
192 611 : .authority = state->authority,
193 611 : .durable_nonce = next_durable_nonce,
194 611 : .lamports_per_signature = blockhash->lamports_per_signature
195 611 : };
196 :
197 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L59 */
198 :
199 611 : do {
200 611 : int err = fd_system_program_set_nonce_state( account, &new_state );
201 611 : if( FD_UNLIKELY( err ) ) return err;
202 611 : } while(0);
203 :
204 611 : return FD_EXECUTOR_INSTR_SUCCESS;
205 611 : }
206 :
207 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L423-L441
208 :
209 : Matches Solana Labs system_processor SystemInstruction::AdvanceNonceAccount => { ... } */
210 :
211 : int
212 797 : fd_system_program_exec_advance_nonce_account( fd_exec_instr_ctx_t * ctx ) {
213 797 : int err;
214 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L423-L441 */
215 :
216 797 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
217 5 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
218 :
219 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L425-L426 */
220 :
221 792 : uchar const instr_acc_idx = 0;
222 :
223 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/system/src/system_processor.rs#L409-L410 */
224 :
225 792 : fd_guarded_borrowed_account_t account = {0};
226 792 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, instr_acc_idx, &account );
227 :
228 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L427-L432 */
229 :
230 792 : err = require_acct_recent_blockhashes( ctx, 1UL );
231 792 : if( FD_UNLIKELY( err ) ) return err;
232 :
233 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L433-L439 */
234 :
235 674 : if( FD_UNLIKELY( fd_sysvar_cache_recent_hashes_is_empty( ctx->sysvar_cache ) ) ) {
236 9 : fd_log_collector_msg_literal( ctx, "Advance nonce account: recent blockhash list is empty" );
237 9 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES;
238 9 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
239 9 : }
240 :
241 665 : err = fd_system_program_advance_nonce_account( ctx, &account, instr_acc_idx );
242 :
243 : /* Implicit drop */
244 :
245 665 : return err;
246 674 : }
247 :
248 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L99-L109 */
249 :
250 : static int
251 : withdraw_nonce_check_signer( fd_exec_instr_ctx_t * ctx,
252 361 : fd_pubkey_t const * signer ) {
253 361 : if( FD_UNLIKELY( !fd_exec_instr_ctx_any_signed( ctx, signer ) ) ) {
254 : /* Max msg_sz: 44 - 2 + 45 = 87 < 127 => we can use printf */
255 143 : FD_BASE58_ENCODE_32_BYTES( signer->key, signer_b58 );
256 143 : fd_log_collector_printf_dangerous_max_127( ctx,
257 143 : "Withdraw nonce account: Account %s must sign", signer_b58 );
258 143 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
259 143 : }
260 218 : return FD_EXECUTOR_INSTR_SUCCESS;
261 361 : }
262 :
263 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L72-L151
264 :
265 : Matches Solana Labs system_instruction::withdraw_nonce_account */
266 :
267 : static int
268 : fd_system_program_withdraw_nonce_account( fd_exec_instr_ctx_t * ctx,
269 : ulong requested_lamports,
270 424 : fd_rent_t const * rent ) {
271 424 : int err;
272 424 : ushort const from_acct_idx = 0UL;
273 424 : ushort const to_acct_idx = 1UL;
274 :
275 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L82-L83 */
276 :
277 424 : fd_guarded_borrowed_account_t from = {0};
278 424 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, from_acct_idx, &from );
279 :
280 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L84-L91 */
281 :
282 424 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, from_acct_idx ) ) ) {
283 : /* Max msg_sz: 51 - 2 + 45 = 94 < 127 => we can use printf */
284 8 : FD_BASE58_ENCODE_32_BYTES( from.acc->pubkey, pubkey_b58 );
285 8 : fd_log_collector_printf_dangerous_max_127( ctx,
286 8 : "Withdraw nonce account: Account %s must be writeable", pubkey_b58 );
287 8 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
288 8 : }
289 :
290 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L93 */
291 :
292 416 : fd_nonce_state_versions_t state[1];
293 416 : if( FD_UNLIKELY( fd_nonce_state_versions_decode(
294 416 : state,
295 416 : fd_borrowed_account_get_data( &from ),
296 416 : fd_borrowed_account_get_data_len( &from ) ) ) ) {
297 44 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
298 44 : }
299 :
300 : /* TODO: update permalinks once Agave 4.1 tag has been created */
301 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L112-L153 */
302 :
303 372 : if( state->kind==FD_NONCE_STATE_UNINITIALIZED ) {
304 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L113-L124 */
305 :
306 172 : if( FD_UNLIKELY( requested_lamports > fd_borrowed_account_get_lamports( &from ) ) ) {
307 : /* Max msg_sz: 59 - 6 + 20 + 20 = 93 < 127 => we can use printf */
308 3 : fd_log_collector_printf_dangerous_max_127( ctx,
309 3 : "Withdraw nonce account: insufficient lamports %lu, need %lu", fd_borrowed_account_get_lamports( &from ), requested_lamports );
310 3 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
311 3 : }
312 :
313 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L123 */
314 :
315 169 : do {
316 169 : int err = withdraw_nonce_check_signer( ctx, (fd_pubkey_t const*)from.acc->pubkey );
317 169 : if( FD_UNLIKELY( err ) ) return err;
318 169 : } while(0);
319 :
320 200 : } else { /* FD_NONCE_STATE_INITIALIZED */
321 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L125-L152 */
322 :
323 200 : if( requested_lamports == fd_borrowed_account_get_lamports( &from ) ) {
324 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L126-L138 */
325 :
326 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L127-L128 */
327 :
328 30 : fd_blockhash_info_t blockhash[1];
329 30 : do {
330 30 : int err = most_recent_block_hash( ctx, blockhash );
331 30 : if( FD_UNLIKELY( err ) ) return err;
332 30 : } while(0);
333 :
334 30 : fd_hash_t next_durable_nonce;
335 30 : fd_durable_nonce_from_blockhash( &next_durable_nonce, &blockhash->hash );
336 :
337 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L129-L135 */
338 :
339 30 : if( FD_UNLIKELY( 0==memcmp( state->durable_nonce.hash, next_durable_nonce.hash, sizeof(fd_hash_t) ) ) ) {
340 0 : fd_log_collector_msg_literal( ctx, "Withdraw nonce account: nonce can only advance once per slot" );
341 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_BLOCKHASH_NOT_EXPIRED;
342 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
343 0 : }
344 :
345 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L136 */
346 :
347 30 : do {
348 30 : int err = withdraw_nonce_check_signer( ctx, &state->authority );
349 30 : if( FD_UNLIKELY( err ) ) return err;
350 30 : } while(0);
351 :
352 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L137 */
353 :
354 27 : fd_nonce_state_versions_t new_state[1] = {{
355 27 : .version = FD_NONCE_VERSION_CURRENT,
356 27 : .kind = FD_NONCE_STATE_UNINITIALIZED
357 27 : }};
358 :
359 27 : do {
360 27 : int err = fd_system_program_set_nonce_state( &from, new_state );
361 27 : if( FD_UNLIKELY( err ) ) return err;
362 27 : } while(0);
363 :
364 170 : } else {
365 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L138-L151 */
366 :
367 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L139-L140 */
368 :
369 170 : ulong min_balance = fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &from ) );
370 :
371 170 : ulong amount;
372 170 : if( FD_UNLIKELY( __builtin_uaddl_overflow( requested_lamports, min_balance, &amount ) ) )
373 3 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
374 :
375 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L141-L149 */
376 :
377 167 : if( FD_UNLIKELY( amount > fd_borrowed_account_get_lamports( &from ) ) ) {
378 : /* Max msg_sz: 59 - 6 + 20 + 20 = 93 < 127 => we can use printf */
379 5 : fd_log_collector_printf_dangerous_max_127( ctx,
380 5 : "Withdraw nonce account: insufficient lamports %lu, need %lu", fd_borrowed_account_get_lamports( &from ), amount );
381 5 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
382 5 : }
383 :
384 : /* https://github.com/anza-xyz/agave/blob/7585b70d5ea5fcbb1710636ad429580a18f42c2a/programs/system/src/system_instruction.rs#L150 */
385 :
386 162 : do {
387 162 : int err = withdraw_nonce_check_signer( ctx, &state->authority );
388 162 : if( FD_UNLIKELY( err ) ) return err;
389 162 : } while(0);
390 :
391 162 : }
392 200 : }
393 :
394 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L144 */
395 :
396 216 : err = fd_borrowed_account_checked_sub_lamports( &from, requested_lamports );
397 216 : if( FD_UNLIKELY( err ) ) return err;
398 :
399 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L145 */
400 :
401 214 : fd_borrowed_account_drop( &from );
402 :
403 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L146-L147 */
404 :
405 214 : fd_guarded_borrowed_account_t to = {0};
406 214 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_acct_idx, &to );
407 :
408 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L148 */
409 :
410 214 : err = fd_borrowed_account_checked_add_lamports( &to, requested_lamports );
411 214 : if( FD_UNLIKELY( err ) ) return err;
412 :
413 : /* Implicit drop */
414 :
415 207 : return FD_EXECUTOR_INSTR_SUCCESS;
416 214 : }
417 :
418 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L442-L461
419 :
420 : Matches Solana Labs system_processor SystemInstruction::WithdrawNonceAccount { ... } => { ... } */
421 :
422 : int
423 : fd_system_program_exec_withdraw_nonce_account( fd_exec_instr_ctx_t * ctx,
424 696 : ulong requested_lamports ) {
425 :
426 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L443 */
427 :
428 696 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) )
429 9 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
430 :
431 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L445-L449 */
432 :
433 687 : do {
434 687 : int err = require_acct_recent_blockhashes( ctx, 2UL );
435 687 : if( FD_UNLIKELY( err ) ) return err;
436 687 : } while(0);
437 :
438 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L450 */
439 :
440 434 : fd_rent_t rent[1];
441 434 : do {
442 434 : int err = require_acct_rent( ctx, 3UL, rent );
443 434 : if( FD_UNLIKELY( err ) ) return err;
444 434 : } while(0);
445 :
446 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L451-L460 */
447 :
448 424 : return fd_system_program_withdraw_nonce_account( ctx, requested_lamports, rent );
449 434 : }
450 :
451 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L153-L198
452 :
453 : Matches Solana Labs system_instruction::initialize_nonce_account */
454 :
455 : static int
456 : fd_system_program_initialize_nonce_account( fd_exec_instr_ctx_t * ctx,
457 : fd_borrowed_account_t * account,
458 : fd_pubkey_t const * authorized,
459 430 : fd_rent_t const * rent ) {
460 :
461 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/system/src/system_instruction.rs#L167-L174 */
462 :
463 430 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( account ) ) ) {
464 : /* Max msg_sz: 53 - 2 + 45 = 96 < 127 => we can use printf */
465 16 : FD_BASE58_ENCODE_32_BYTES( account->acc->pubkey, pubkey_b58 );
466 16 : fd_log_collector_printf_dangerous_max_127( ctx,
467 16 : "Initialize nonce account: Account %s must be writeable", pubkey_b58 );
468 16 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
469 16 : }
470 :
471 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L168 */
472 :
473 414 : fd_nonce_state_versions_t state[1];
474 414 : if( FD_UNLIKELY( fd_nonce_state_versions_decode(
475 414 : state,
476 414 : fd_borrowed_account_get_data( account ),
477 414 : fd_borrowed_account_get_data_len( account ) ) ) ) {
478 15 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
479 15 : }
480 :
481 399 : if( state->kind==FD_NONCE_STATE_INITIALIZED ) {
482 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L189-L196 */
483 :
484 : /* Max msg_sz: 53 - 2 + 45 = 96 < 127 => we can use printf */
485 159 : FD_BASE58_ENCODE_32_BYTES( account->acc->pubkey, pubkey_b58 );
486 159 : fd_log_collector_printf_dangerous_max_127( ctx,
487 159 : "Initialize nonce account: Account %s state is invalid", pubkey_b58 );
488 :
489 159 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
490 159 : }
491 :
492 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L169-L188 */
493 :
494 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L170 */
495 :
496 240 : ulong min_balance = fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( account ) );
497 :
498 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L171-L179 */
499 :
500 240 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( account ) < min_balance ) ) {
501 : /* Max msg_sz: 61 - 6 + 20 + 20 = 95 < 127 => we can use printf */
502 3 : fd_log_collector_printf_dangerous_max_127( ctx,
503 3 : "Initialize nonce account: insufficient lamports %lu, need %lu", fd_borrowed_account_get_lamports( account ), min_balance );
504 3 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
505 3 : }
506 :
507 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L180 */
508 :
509 237 : fd_blockhash_info_t blockhash[1];
510 237 : do {
511 237 : int err = most_recent_block_hash( ctx, blockhash );
512 237 : if( FD_UNLIKELY( err ) ) return err;
513 237 : } while(0);
514 :
515 237 : fd_hash_t durable_nonce;
516 237 : fd_durable_nonce_from_blockhash( &durable_nonce, &blockhash->hash );
517 :
518 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/programs/system/src/system_instruction.rs#L185-L191 */
519 :
520 237 : fd_nonce_state_versions_t new_state = {
521 237 : .version = FD_NONCE_VERSION_CURRENT,
522 237 : .kind = FD_NONCE_STATE_INITIALIZED,
523 237 : .authority = *authorized,
524 237 : .durable_nonce = durable_nonce,
525 237 : .lamports_per_signature = blockhash->lamports_per_signature
526 237 : };
527 :
528 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L187 */
529 :
530 237 : do {
531 237 : int err = fd_system_program_set_nonce_state( account, &new_state );
532 237 : if( FD_UNLIKELY( err ) ) return err;
533 237 : } while(0);
534 :
535 86 : return FD_EXECUTOR_INSTR_SUCCESS;
536 237 : }
537 :
538 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L462-L481
539 :
540 : Matches Solana Labs system_processor SystemInstruction::InitializeNonceAccount { ... } => { ... } */
541 :
542 : int
543 : fd_system_program_exec_initialize_nonce_account( fd_exec_instr_ctx_t * ctx,
544 490 : fd_pubkey_t const * authorized ) {
545 490 : int err;
546 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L463 */
547 :
548 490 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
549 5 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
550 :
551 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L464-L465 */
552 :
553 485 : uchar const instr_acc_idx = 0;
554 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/system/src/system_processor.rs#L448-L449 */
555 485 : fd_guarded_borrowed_account_t account = {0};
556 485 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, instr_acc_idx, &account );
557 :
558 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L466-L471 */
559 :
560 485 : do {
561 485 : err = require_acct_recent_blockhashes( ctx, 1UL );
562 485 : if( FD_UNLIKELY( err ) ) return err;
563 485 : } while(0);
564 :
565 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L472-L478 */
566 :
567 447 : if( FD_UNLIKELY( fd_sysvar_cache_recent_hashes_is_empty( ctx->sysvar_cache ) ) ) {
568 6 : fd_log_collector_msg_literal( ctx, "Initialize nonce account: recent blockhash list is empty" );
569 6 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES;
570 6 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
571 6 : }
572 :
573 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L479 */
574 :
575 441 : fd_rent_t rent[1];
576 441 : do {
577 441 : err = require_acct_rent( ctx, 2UL, rent );
578 441 : if( FD_UNLIKELY( err ) ) return err;
579 441 : } while(0);
580 :
581 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L480 */
582 :
583 430 : err = fd_system_program_initialize_nonce_account( ctx, &account, authorized, rent );
584 :
585 : /* Implicit drop */
586 :
587 430 : return err;
588 441 : }
589 :
590 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L200-L236
591 :
592 : Matches Solana Labs system_instruction::authorize_nonce_account */
593 :
594 : static int
595 : fd_system_program_authorize_nonce_account( fd_exec_instr_ctx_t * ctx,
596 : fd_borrowed_account_t * account,
597 : ushort instr_acc_idx,
598 694 : fd_pubkey_t const * nonce_authority ) {
599 :
600 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L206-L213 */
601 :
602 694 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
603 : /* Max msg_sz: 52 - 2 + 45 = 95 < 127 => we can use printf */
604 92 : FD_BASE58_ENCODE_32_BYTES( account->acc->pubkey, pubkey_b58 );
605 92 : fd_log_collector_printf_dangerous_max_127( ctx,
606 92 : "Authorize nonce account: Account %s must be writeable", pubkey_b58 );
607 92 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
608 92 : }
609 :
610 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L214-L215 */
611 :
612 602 : fd_nonce_state_versions_t state[1];
613 602 : if( FD_UNLIKELY( fd_nonce_state_versions_decode(
614 602 : state,
615 602 : fd_borrowed_account_get_data( account ),
616 602 : fd_borrowed_account_get_data_len( account ) ) ) ) {
617 172 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
618 172 : }
619 :
620 : /* Inlining solana_program::nonce::state::Versions::authorize
621 : https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L76-L102 */
622 :
623 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L81-L84 */
624 :
625 430 : if( FD_UNLIKELY( state->kind != FD_NONCE_STATE_INITIALIZED ) ) {
626 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L219-L226 */
627 :
628 : /* Max msg_sz: 52 - 2 + 45 = 95 < 127 => we can use printf */
629 12 : FD_BASE58_ENCODE_32_BYTES( account->acc->pubkey, pubkey_b58 );
630 12 : fd_log_collector_printf_dangerous_max_127( ctx,
631 12 : "Authorize nonce account: Account %s state is invalid", pubkey_b58 );
632 :
633 12 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
634 12 : }
635 :
636 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L85-L89 */
637 :
638 418 : if( FD_UNLIKELY( !fd_exec_instr_ctx_any_signed( ctx, &state->authority ) ) ) {
639 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L227-L234 */
640 : /* Max msg_sz: 45 - 2 + 45 = 88 < 127 => we can use printf */
641 62 : FD_BASE58_ENCODE_32_BYTES( state->authority.key, authority_b58 );
642 62 : fd_log_collector_printf_dangerous_max_127( ctx,
643 62 : "Authorize nonce account: Account %s must sign", authority_b58 );
644 62 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
645 62 : }
646 :
647 : /* https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L90-L101
648 :
649 : Versions::authorize preserves the outer version tag (Legacy stays
650 : Legacy, Current stays Current) and replaces only the inner
651 : authority. */
652 :
653 356 : fd_nonce_state_versions_t new_state[1] = {{
654 356 : .version = state->version,
655 356 : .kind = FD_NONCE_STATE_INITIALIZED,
656 356 : .authority = *nonce_authority,
657 356 : .durable_nonce = state->durable_nonce,
658 356 : .lamports_per_signature = state->lamports_per_signature
659 356 : }};
660 :
661 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_instruction.rs#L218 */
662 :
663 356 : do {
664 356 : int err = fd_system_program_set_nonce_state( account, new_state );
665 356 : if( FD_UNLIKELY( err ) ) return err;
666 356 : } while(0);
667 :
668 350 : return FD_EXECUTOR_INSTR_SUCCESS;
669 356 : }
670 :
671 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L482-L487
672 :
673 : Matches Solana Labs system_processor SystemInstruction::AuthorizeNonceAccount { ... } => { ... } */
674 :
675 : int
676 : fd_system_program_exec_authorize_nonce_account( fd_exec_instr_ctx_t * ctx,
677 696 : fd_pubkey_t const * nonce_authority ) {
678 696 : int err;
679 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L483 */
680 :
681 696 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
682 2 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
683 :
684 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L484-L485 */
685 :
686 694 : fd_guarded_borrowed_account_t account = {0};
687 694 : err = fd_exec_instr_ctx_try_borrow_instr_account( ctx, 0, &account );
688 694 : if( FD_UNLIKELY( err ) ) {
689 0 : return err;
690 0 : }
691 :
692 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L486 */
693 :
694 694 : err = fd_system_program_authorize_nonce_account( ctx, &account, 0UL, nonce_authority );
695 :
696 : /* Implicit drop */
697 :
698 694 : return err;
699 694 : }
700 :
701 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L488-L503
702 :
703 : Matches Solana Labs system_processor SystemInstruction::UpgradeNonceAccount { ... } => { ... } */
704 :
705 : int
706 471 : fd_system_program_exec_upgrade_nonce_account( fd_exec_instr_ctx_t * ctx ) {
707 471 : int err;
708 471 : ushort const nonce_acct_idx = 0UL;
709 :
710 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L489 */
711 :
712 471 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
713 2 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
714 :
715 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/system/src/system_processor.rs#L474-475 */
716 :
717 469 : fd_guarded_borrowed_account_t account = {0};
718 469 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, nonce_acct_idx, &account );
719 :
720 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L492-L494 */
721 :
722 469 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &account ), fd_solana_system_program_id.key, sizeof(fd_pubkey_t) ) ) )
723 58 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
724 :
725 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L495-L497 */
726 :
727 411 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, 0 ) ) )
728 7 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
729 :
730 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L498 */
731 :
732 404 : fd_nonce_state_versions_t state[1];
733 404 : if( FD_UNLIKELY( fd_nonce_state_versions_decode(
734 404 : state,
735 404 : fd_borrowed_account_get_data( &account ),
736 404 : fd_borrowed_account_get_data_len( &account ) ) ) ) {
737 73 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
738 73 : }
739 :
740 : /* Inlining solana_program::nonce::state::Versions::upgrade
741 : https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/nonce/state/mod.rs#L55-L73 */
742 :
743 331 : if( FD_UNLIKELY( state->version != FD_NONCE_VERSION_LEGACY ) )
744 277 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
745 :
746 54 : if( FD_UNLIKELY( state->kind != FD_NONCE_STATE_INITIALIZED ) )
747 6 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
748 :
749 48 : fd_durable_nonce_from_blockhash( &state->durable_nonce, &state->durable_nonce );
750 :
751 : /* https://github.com/solana-labs/solana/blob/v1.17.23/programs/system/src/system_processor.rs#L501 */
752 :
753 48 : fd_nonce_state_versions_t new_state[1] = {{
754 48 : .version = FD_NONCE_VERSION_CURRENT,
755 48 : .kind = FD_NONCE_STATE_INITIALIZED,
756 48 : .authority = state->authority,
757 48 : .durable_nonce = state->durable_nonce,
758 48 : .lamports_per_signature = state->lamports_per_signature
759 48 : }};
760 :
761 48 : err = fd_system_program_set_nonce_state( &account, new_state );
762 48 : if( FD_UNLIKELY( err ) ) return err;
763 :
764 : /* Implicit drop */
765 :
766 48 : return FD_EXECUTOR_INSTR_SUCCESS;
767 48 : }
768 :
769 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/runtime/src/bank/check_transactions.rs#L166-L200 */
770 : /* The age of a transaction is valid under two conditions. The first is that
771 : the transactions blockhash is a recent blockhash (within 151) in the block
772 : hash queue. The other condition is that the transaction contains a valid
773 : nonce account. This is the case under several conditions. If neither
774 : condition is met then the transaction is invalid.
775 : Note: We check 151 and not 150 due to a known bug in agave. */
776 : int
777 : fd_check_transaction_age( fd_bank_t * bank,
778 : fd_txn_in_t const * txn_in,
779 6899 : fd_txn_out_t * txn_out ) {
780 :
781 6899 : fd_blockhashes_t const * block_hash_queue = &bank->f.block_hash_queue;
782 6899 : fd_hash_t const * last_blockhash = fd_blockhashes_peek_last_hash( block_hash_queue );
783 6899 : if( FD_UNLIKELY( !last_blockhash ) ) {
784 0 : FD_LOG_CRIT(( "blockhash queue is empty" ));
785 0 : }
786 :
787 : /* check_transaction_age */
788 6899 : fd_hash_t next_durable_nonce = {0};
789 6899 : fd_durable_nonce_from_blockhash( &next_durable_nonce, last_blockhash );
790 6899 : ushort recent_blockhash_off = TXN( txn_in->txn )->recent_blockhash_off;
791 6899 : fd_hash_t * recent_blockhash = (fd_hash_t *)((uchar *)txn_in->txn->payload + recent_blockhash_off);
792 :
793 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3538-L3542 */
794 : /* get_hash_info_if_valid. Check 151 hashes from the block hash queue and its
795 : age to see if it is valid. */
796 :
797 6899 : if( fd_blockhashes_check_age( block_hash_queue, recent_blockhash, FD_SYSVAR_RECENT_HASHES_CAP ) ) {
798 5903 : return FD_RUNTIME_EXECUTE_SUCCESS;
799 5903 : }
800 :
801 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3622-L3633 */
802 : /* check_and_load_message_nonce_account */
803 996 : if( FD_UNLIKELY( !memcmp( &next_durable_nonce, recent_blockhash, sizeof(fd_hash_t) ) ) ) { /* nonce_is_advanceable == false */
804 0 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NONCE_ALREADY_ADVANCED;
805 0 : }
806 :
807 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/runtime/src/bank.rs#L3603-L3620*/
808 : /* load_message_nonce_account */
809 :
810 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/svm-transaction/src/svm_message.rs#L87-L119 */
811 : /* get_durable_nonce */
812 996 : if( FD_UNLIKELY( !TXN( txn_in->txn )->instr_cnt ) ) {
813 160 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
814 160 : }
815 : /* Check the first instruction (nonce instruction) to see if the
816 : program id is the system program. Also make sure that it is an
817 : advance nonce account instruction. Finally make sure that the
818 : first instruction account is writable; if it is, then that account
819 : is a durable nonce account. */
820 836 : fd_txn_instr_t const * txn_instr = &TXN( txn_in->txn )->instr[0];
821 836 : if( FD_UNLIKELY( txn_instr->acct_cnt==0 ) ) {
822 31 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
823 31 : }
824 :
825 805 : fd_acct_addr_t const * tx_accs = fd_txn_get_acct_addrs( TXN( txn_in->txn ), txn_in->txn->payload );
826 805 : fd_acct_addr_t const * prog_id = tx_accs + txn_instr->program_id;
827 805 : if( FD_UNLIKELY( memcmp( prog_id->b, fd_solana_system_program_id.key, sizeof( fd_pubkey_t ) ) ) ) {
828 191 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
829 191 : }
830 614 : uchar const * instr_data = fd_txn_get_instr_data( txn_instr, txn_in->txn->payload );
831 614 : uchar const * instr_accts = fd_txn_get_instr_accts( txn_instr, txn_in->txn->payload );
832 614 : uchar nonce_idx = instr_accts[0];
833 :
834 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/svm-transaction/src/svm_message.rs#L99-L105 */
835 614 : if( FD_UNLIKELY( txn_instr->data_sz<4UL || FD_LOAD( uint, instr_data ) !=
836 614 : (uint)FD_SYSTEM_PROGRAM_INSTR_ADVANCE_NONCE_ACCOUNT ) ) {
837 4 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
838 4 : }
839 :
840 : /* Nonce account must be...
841 : - writable
842 : - statically included in the transaction account keys (if SIMD-242
843 : is active)
844 : https://github.com/anza-xyz/agave/blob/v2.3.1/svm-transaction/src/svm_message.rs#L110-L111 */
845 610 : if( FD_UNLIKELY( !fd_runtime_account_is_writable_idx( txn_in, txn_out, nonce_idx ) ) ) {
846 4 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
847 4 : }
848 606 : if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( bank, require_static_nonce_account ) &&
849 606 : nonce_idx>=TXN( txn_in->txn )->acct_addr_cnt ) ) {
850 28 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
851 28 : }
852 :
853 :
854 : /* The transaction accounts were set up before this check, including
855 : forwarding writable bundle accounts into the current txn_out. */
856 578 : fd_acc_t * nonce_entry = txn_out->accounts.account[ nonce_idx ];
857 578 : if( FD_UNLIKELY( !nonce_entry->lamports ) ) {
858 1 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
859 1 : }
860 :
861 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/src/nonce_account.rs#L28-L42 */
862 : /* verify_nonce_account */
863 577 : fd_pubkey_t const * owner_pubkey = fd_type_pun_const( nonce_entry->owner );
864 577 : if( FD_UNLIKELY( !fd_pubkey_eq( owner_pubkey, &fd_solana_system_program_id ) ) ) {
865 1 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
866 1 : }
867 :
868 576 : fd_nonce_state_versions_t state[1];
869 576 : if( FD_UNLIKELY( fd_nonce_state_versions_decode( state, nonce_entry->data, nonce_entry->data_len ) ) ) {
870 30 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
871 30 : }
872 :
873 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/program/src/nonce/state/mod.rs#L36-L53 */
874 : /* verify_recent_blockhash. This checks that the decoded nonce record is
875 : not a legacy nonce nor uninitialized. If this is the case, then we can
876 : verify by comparing the decoded durable nonce to the recent blockhash */
877 546 : if( FD_UNLIKELY( state->version != FD_NONCE_VERSION_CURRENT ) ) {
878 1 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
879 1 : }
880 :
881 545 : if( FD_UNLIKELY( state->kind != FD_NONCE_STATE_INITIALIZED ) ) {
882 0 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
883 0 : }
884 :
885 545 : if( FD_UNLIKELY( memcmp( &state->durable_nonce, recent_blockhash, sizeof(fd_hash_t) ) ) ) {
886 4 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_WRONG_NONCE;
887 4 : }
888 :
889 : /* Finally check that the nonce is authorized by seeing if any
890 : accounts in the nonce instruction are signers. This is a
891 : successful exit case. */
892 1750 : for( ushort i=0; i<txn_instr->acct_cnt; ++i ) {
893 1705 : if( fd_txn_is_signer( TXN( txn_in->txn ), (int)instr_accts[i] ) ) {
894 955 : if( fd_pubkey_eq( &txn_out->accounts.keys[ instr_accts[i] ], &state->authority ) ) {
895 : /* Mark nonce account to make sure that we modify and hash the
896 : account even if the transaction failed to execute
897 : successfully. */
898 :
899 496 : txn_out->accounts.nonce_idx_in_txn = instr_accts[ 0 ];
900 :
901 496 : fd_blockhashes_t const * blockhashes = &bank->f.block_hash_queue;
902 496 : fd_blockhash_info_t const * last_bhash_info = fd_blockhashes_peek_last( blockhashes );
903 496 : FD_TEST( last_bhash_info ); /* Agave panics here if the blockhash queue is empty */
904 :
905 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/runtime/src/bank/check_transactions.rs#L217-L221*/
906 496 : fd_nonce_state_versions_t new_state = {
907 496 : .version = FD_NONCE_VERSION_CURRENT,
908 496 : .kind = FD_NONCE_STATE_INITIALIZED,
909 496 : .authority = state->authority,
910 496 : .durable_nonce = next_durable_nonce,
911 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/runtime/src/bank/check_transactions.rs#L88-L90 */
912 496 : .lamports_per_signature = last_bhash_info->lamports_per_signature
913 496 : };
914 :
915 496 : FD_TEST( fd_nonce_state_versions_size( &new_state )<=FD_SYSTEM_PROGRAM_NONCE_DLEN );
916 496 : fd_memcpy( txn_out->accounts.nonce_rollback_data, nonce_entry->data, nonce_entry->data_len );
917 496 : txn_out->accounts.nonce_rollback_data_len = nonce_entry->data_len;
918 :
919 496 : ulong written = 0UL;
920 496 : int err = fd_nonce_state_versions_encode( &new_state, txn_out->accounts.nonce_rollback_data, txn_out->accounts.nonce_rollback_data_len, &written );
921 496 : if( FD_UNLIKELY( err ) ) {
922 0 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
923 0 : }
924 :
925 496 : return FD_RUNTIME_EXECUTE_SUCCESS;
926 496 : }
927 955 : }
928 1705 : }
929 : /* This means that the blockhash was not found */
930 45 : return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR;
931 :
932 541 : }
|