Line data Source code
1 : #include "fd_system_program.h"
2 : #include "../fd_executor.h"
3 : #include "../fd_borrowed_account.h"
4 : #include "../fd_system_ids.h"
5 : #include "../fd_pubkey_utils.h"
6 : #include "../../log_collector/fd_log_collector.h"
7 :
8 : /* The dynamically sized portion of the system program instruction only
9 : comes from the seed. This means in the worst case assuming that the
10 : seed takes up the entire transaction MTU, the worst case footprint
11 : is the sum of the size of the instruction and the transaction MTU.
12 : This is not the tightest bound, but it's a reasonable bound. */
13 :
14 : #define FD_SYSTEM_PROGRAM_INSTR_FOOTPRINT (FD_TXN_MTU + sizeof(fd_system_program_instruction_t))
15 :
16 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L42-L68
17 :
18 : Partial port of system_processor::Address::create, only covering the
19 : case of the "seed" input actually existing. Note that this doesn't
20 : "create" an address, but rather re-derives from PDA inputs and checks
21 : that the result matches some expected value. */
22 :
23 : static int
24 : verify_seed_address( fd_exec_instr_ctx_t * ctx,
25 : fd_pubkey_t const * expected,
26 : fd_pubkey_t const * base,
27 : char const * seed,
28 : ulong seed_sz,
29 0 : fd_pubkey_t const * owner ) {
30 :
31 0 : fd_pubkey_t actual[1];
32 0 : do {
33 0 : int err = fd_pubkey_create_with_seed(
34 0 : ctx,
35 0 : base->uc,
36 0 : seed,
37 0 : seed_sz,
38 0 : owner->uc,
39 0 : actual->uc );
40 0 : if( FD_UNLIKELY( err ) ) return err;
41 0 : } while(0);
42 :
43 0 : if( FD_UNLIKELY( 0!=memcmp( actual->uc, expected->uc, sizeof(fd_pubkey_t) ) ) ) {
44 : /* Log msg_sz can be more or less than 127 bytes */
45 0 : FD_BASE58_ENCODE_32_BYTES( expected->key, expected_b58 );
46 0 : FD_BASE58_ENCODE_32_BYTES( actual->key, actual_b58 );
47 0 : fd_log_collector_printf_inefficient_max_512( ctx,
48 0 : "Create: address %s does not match derived address %s",
49 0 : expected_b58, actual_b58 );
50 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_ADDR_WITH_SEED_MISMATCH;
51 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
52 0 : }
53 :
54 0 : return 0;
55 0 : }
56 :
57 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L183
58 : https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L182
59 :
60 : Matches Solana Labs system_processor::transfer_verified */
61 :
62 : static int
63 : fd_system_program_transfer_verified( fd_exec_instr_ctx_t * ctx,
64 : ulong transfer_amount,
65 : ushort from_acct_idx,
66 24 : ushort to_acct_idx ) {
67 24 : int err;
68 :
69 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L191-L192 */
70 :
71 24 : fd_guarded_borrowed_account_t from = {0};
72 24 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, from_acct_idx, &from );
73 :
74 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L193-L196 */
75 :
76 24 : if( fd_borrowed_account_get_data_len( &from ) != 0UL ) {
77 0 : fd_log_collector_msg_literal( ctx, "Transfer: `from` must not carry data" );
78 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
79 0 : }
80 :
81 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L197-L205 */
82 :
83 24 : if( transfer_amount > fd_borrowed_account_get_lamports( &from ) ) {
84 : /* Max msg_sz: 45 - 6 + 20 + 20 = 79 < 127 => we can use printf */
85 0 : fd_log_collector_printf_dangerous_max_127( ctx, "Transfer: insufficient lamports %lu, need %lu", fd_borrowed_account_get_lamports( &from ), transfer_amount );
86 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_RESULT_WITH_NEGATIVE_LAMPORTS;
87 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
88 0 : }
89 :
90 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L207 */
91 :
92 24 : err = fd_borrowed_account_checked_sub_lamports( &from, transfer_amount );
93 : /* Note: this err can never happen because of the check above */
94 24 : if( FD_UNLIKELY( err ) ) return err;
95 :
96 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L208 */
97 :
98 24 : fd_borrowed_account_drop( &from );
99 :
100 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L209-L210 */
101 :
102 24 : fd_guarded_borrowed_account_t to = {0};
103 24 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_acct_idx, &to );
104 :
105 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L211 */
106 :
107 24 : err = fd_borrowed_account_checked_add_lamports( &to, transfer_amount );
108 24 : if( FD_UNLIKELY( err ) ) return err;
109 :
110 24 : return 0;
111 24 : }
112 :
113 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L214
114 :
115 : Matches system_processor::transfer */
116 :
117 : static int
118 : fd_system_program_transfer( fd_exec_instr_ctx_t * ctx,
119 : ulong transfer_amount,
120 : ushort from_acct_idx,
121 24 : ushort to_acct_idx ) {
122 :
123 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L222-L232 */
124 :
125 24 : int instr_err_code = 0;
126 24 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, from_acct_idx, &instr_err_code ) ) ) {
127 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
128 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
129 : /* Max msg_sz: 37 - 2 + 45 = 80 < 127 => we can use printf */
130 0 : ushort idx_in_txn = ctx->instr->accounts[ from_acct_idx ].index_in_transaction;
131 0 : FD_BASE58_ENCODE_32_BYTES( ctx->txn_out->accounts.keys[ idx_in_txn ].key, key_b58 );
132 0 : fd_log_collector_printf_dangerous_max_127( ctx,
133 0 : "Transfer: `from` account %s must sign", key_b58 );
134 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
135 0 : }
136 :
137 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L234-L241 */
138 :
139 24 : return fd_system_program_transfer_verified( ctx, transfer_amount, from_acct_idx, to_acct_idx );
140 24 : }
141 :
142 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L71-L111
143 : https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L70
144 :
145 : Based on Solana Labs system_processor::allocate() */
146 :
147 : static int
148 : fd_system_program_allocate( fd_exec_instr_ctx_t * ctx,
149 : fd_borrowed_account_t * account,
150 : ulong space,
151 : fd_pubkey_t const * authority,
152 0 : fd_pubkey_t const * base ) {
153 0 : int err;
154 :
155 : /* Assumes that acct_idx was bounds checked */
156 :
157 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L78-L85 */
158 :
159 0 : if( FD_UNLIKELY( !fd_exec_instr_ctx_any_signed( ctx, authority ) ) ) {
160 : /* Max msg_sz: 35 - 2 + 125 = 158 */
161 0 : FD_BASE58_ENCODE_32_BYTES( account->pubkey->key, account_b58 );
162 0 : FD_BASE58_ENCODE_32_BYTES( base->key, base_b58 );
163 0 : fd_log_collector_printf_inefficient_max_512( ctx,
164 0 : "Allocate: 'to' (account %s, base %s) must sign",
165 0 : account_b58,
166 0 : base ? base_b58 : "None" );
167 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
168 0 : }
169 :
170 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L87-L96 */
171 :
172 0 : if( FD_UNLIKELY( ( fd_borrowed_account_get_data_len( account ) != 0UL ) ||
173 0 : ( 0!=memcmp( fd_borrowed_account_get_owner( account ), fd_solana_system_program_id.uc, 32UL ) ) ) ) {
174 : /* Max msg_sz: 35 - 2 + 125 = 158 */
175 0 : FD_BASE58_ENCODE_32_BYTES( account->pubkey->key, account_b58 );
176 0 : FD_BASE58_ENCODE_32_BYTES( base->key, base_b58 );
177 0 : fd_log_collector_printf_inefficient_max_512( ctx,
178 0 : "Allocate: account (account %s, base %s) already in use",
179 0 : account_b58,
180 0 : base ? base_b58 : "None" );
181 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_ACCT_ALREADY_IN_USE;
182 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
183 0 : }
184 :
185 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L98-L106 */
186 :
187 0 : if( FD_UNLIKELY( space > FD_RUNTIME_ACC_SZ_MAX ) ) {
188 : /* Max msg_sz: 48 - 6 + 2*20 = 82 < 127 => we can use printf */
189 0 : fd_log_collector_printf_dangerous_max_127( ctx,
190 0 : "Allocate: requested %lu, max allowed %lu", space, FD_RUNTIME_ACC_SZ_MAX );
191 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_INVALID_ACCT_DATA_LEN;
192 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
193 0 : }
194 :
195 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L108 */
196 :
197 0 : err = fd_borrowed_account_set_data_length( account, space );
198 0 : if( FD_UNLIKELY( err ) ) {
199 0 : return err;
200 0 : }
201 :
202 0 : return FD_EXECUTOR_INSTR_SUCCESS;
203 0 : }
204 :
205 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L113-L131
206 : https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L112
207 :
208 : Based on Solana Labs system_processor::assign() */
209 :
210 : static int
211 : fd_system_program_assign( fd_exec_instr_ctx_t * ctx,
212 : fd_borrowed_account_t * account,
213 : fd_pubkey_t const * owner,
214 : fd_pubkey_t const * authority,
215 0 : fd_pubkey_t const * base ) {
216 : /* Assumes addr_idx was bounds checked */
217 :
218 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L121-L123 */
219 :
220 0 : if( 0==memcmp( fd_borrowed_account_get_owner( account ), owner->uc, sizeof(fd_pubkey_t) ) )
221 0 : return 0;
222 :
223 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L125-L128 */
224 :
225 0 : if( FD_UNLIKELY( !fd_exec_instr_ctx_any_signed( ctx, authority ) ) ) {
226 : /* Max msg_sz: 28 - 2 + 125 = 151 */
227 0 : FD_BASE58_ENCODE_32_BYTES( account->pubkey->key, account_b58 );
228 0 : FD_BASE58_ENCODE_32_BYTES( base->key, base_b58 );
229 0 : fd_log_collector_printf_inefficient_max_512( ctx,
230 0 : "Allocate: 'to' (account %s, base %s) must sign",
231 0 : account_b58,
232 0 : base ? base_b58 : "None" );
233 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
234 0 : }
235 :
236 0 : return fd_borrowed_account_set_owner( account, owner );
237 0 : }
238 :
239 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L133-L143
240 :
241 : Based on Solana Labs system_processor::allocate_and_assign() */
242 :
243 : static int
244 : fd_system_program_allocate_and_assign( fd_exec_instr_ctx_t * ctx,
245 : fd_borrowed_account_t * account,
246 : ulong space,
247 : fd_pubkey_t const * owner,
248 : fd_pubkey_t const * authority,
249 0 : fd_pubkey_t const * base ) {
250 :
251 0 : do {
252 0 : int err = fd_system_program_allocate( ctx, account, space, authority, base );
253 0 : if( FD_UNLIKELY( err ) ) return err;
254 0 : } while(0);
255 0 : return fd_system_program_assign( ctx, account, owner, authority, base );
256 :
257 0 : }
258 :
259 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L146-L181
260 : https://github.com/anza-xyz/agave/blob/v2.0.9/programs/system/src/system_processor.rs#L145
261 :
262 : Matches Solana Labs system_processor::create_account() */
263 :
264 : static int
265 : fd_system_program_create_account( fd_exec_instr_ctx_t * ctx,
266 : ushort from_acct_idx,
267 : ushort to_acct_idx,
268 : ulong lamports,
269 : ulong space,
270 : fd_pubkey_t const * owner,
271 : fd_pubkey_t const * authority,
272 0 : fd_pubkey_t const * base ) {
273 0 : int err;
274 :
275 : /* if it looks like the to account is already in use, bail
276 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/system/src/system_processor.rs#L159-L172 */
277 :
278 0 : do {
279 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L160-L161 */
280 :
281 0 : fd_guarded_borrowed_account_t to = {0};
282 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_acct_idx, &to );
283 :
284 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L162-L169 */
285 :
286 0 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &to ) ) ) {
287 : /* Max msg_sz: 41 - 2 + 125 = 164 */
288 0 : FD_BASE58_ENCODE_32_BYTES( to.pubkey->key, to_b58 );
289 0 : FD_BASE58_ENCODE_32_BYTES( base->key, base_b58 );
290 0 : fd_log_collector_printf_inefficient_max_512( ctx,
291 0 : "Allocate: 'to' (account %s, base %s) already in use",
292 0 : to_b58,
293 0 : base ? base_b58 : "None" );
294 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_ACCT_ALREADY_IN_USE;
295 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
296 0 : }
297 :
298 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L171 */
299 :
300 0 : err = fd_system_program_allocate_and_assign( ctx, &to, space, owner, authority, base );
301 0 : if( FD_UNLIKELY( err ) ) return err;
302 :
303 : /* Implicit drop
304 : https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L172 */
305 0 : } while (0);
306 :
307 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L173-L180 */
308 :
309 0 : return fd_system_program_transfer( ctx, lamports, from_acct_idx, to_acct_idx );
310 0 : }
311 :
312 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L327-L352
313 :
314 : Matches Solana Labs system_processor SystemInstruction::CreateAccount { ... } => { ... } */
315 :
316 : int
317 : fd_system_program_exec_create_account( fd_exec_instr_ctx_t * ctx,
318 0 : fd_system_program_instruction_create_account_t const * create_acc ) {
319 :
320 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L332 */
321 :
322 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) )
323 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
324 :
325 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L333-L339
326 : Authorization check is lifted out from 'allocate' to here. */
327 :
328 0 : ushort const from_acct_idx = 0UL;
329 0 : ushort const to_acct_idx = 1UL;
330 :
331 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/system/src/system_processor.rs#L317-L320 */
332 0 : fd_pubkey_t const * authority = NULL;
333 0 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, to_acct_idx, &authority );
334 0 : if( FD_UNLIKELY( err ) ) return err;
335 :
336 0 : return fd_system_program_create_account(
337 0 : ctx,
338 0 : from_acct_idx,
339 0 : to_acct_idx,
340 0 : create_acc->lamports,
341 0 : create_acc->space,
342 0 : &create_acc->owner,
343 0 : authority,
344 0 : NULL );
345 0 : }
346 :
347 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L381-L393
348 :
349 : Matches Solana Labs system_processor SystemInstruction::Assign { ... } => { ... } */
350 :
351 : int
352 : fd_system_program_exec_assign( fd_exec_instr_ctx_t * ctx,
353 0 : fd_pubkey_t const * owner ) {
354 0 : int err;
355 :
356 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L382 */
357 :
358 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
359 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
360 :
361 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L383-L384 */
362 :
363 0 : fd_guarded_borrowed_account_t account = {0};
364 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &account );
365 :
366 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L385-L391
367 : system_processor::Address::create eliminated (dead code) */
368 :
369 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L392 */
370 :
371 0 : err = fd_system_program_assign( ctx, &account, owner, account.pubkey, NULL );
372 0 : if( FD_UNLIKELY( err ) ) return err;
373 :
374 : /* Implicit drop */
375 :
376 0 : return 0;
377 0 : }
378 :
379 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L394-L404
380 :
381 : Matches Solana Labs system_processor SystemInstruction::Transfer { ... } => { ... } */
382 :
383 : int
384 : fd_system_program_exec_transfer( fd_exec_instr_ctx_t * ctx,
385 24 : ulong transfer_amount ) {
386 :
387 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L395 */
388 :
389 24 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) )
390 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
391 :
392 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L396-L402 */
393 :
394 24 : return fd_system_program_transfer( ctx, transfer_amount, 0UL, 1UL );
395 24 : }
396 :
397 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L353
398 :
399 : Matches Solana Labs system_processor SystemInstruction::CreateAccountWithSeed { ... } => { ... } */
400 :
401 : int
402 : fd_system_program_exec_create_account_with_seed( fd_exec_instr_ctx_t * ctx,
403 0 : fd_system_program_instruction_create_account_with_seed_t const * args ) {
404 :
405 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L360 */
406 :
407 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( ctx, 2UL) ) )
408 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
409 :
410 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L361-L367 */
411 :
412 0 : fd_pubkey_t const * to_address = NULL;
413 0 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1UL, &to_address );
414 0 : if( FD_UNLIKELY( err ) ) return err;
415 :
416 0 : do {
417 0 : int err = verify_seed_address(
418 0 : ctx,
419 0 : to_address,
420 0 : &args->base,
421 0 : (char const *)args->seed,
422 0 : args->seed_len,
423 0 : &args->owner );
424 0 : if( FD_UNLIKELY( err ) ) return err;
425 0 : } while(0);
426 :
427 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L368-L379 */
428 :
429 0 : ushort const from_acct_idx = 0UL;
430 0 : ushort const to_acct_idx = 1UL;
431 0 : return fd_system_program_create_account(
432 0 : ctx,
433 0 : from_acct_idx,
434 0 : to_acct_idx,
435 0 : args->lamports,
436 0 : args->space,
437 0 : &args->owner,
438 0 : &args->base,
439 0 : &args->base );
440 0 : }
441 :
442 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L504-L516
443 :
444 : Matches Solana Labs system_processor SystemInstruction::Allocate { ... } => { ... } */
445 :
446 : int
447 : fd_system_program_exec_allocate( fd_exec_instr_ctx_t * ctx,
448 0 : ulong space ) {
449 0 : int err;
450 :
451 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L505 */
452 :
453 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
454 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
455 :
456 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L506-L507 */
457 0 : fd_guarded_borrowed_account_t account = {0};
458 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &account );
459 :
460 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L508-L514
461 : system_processor::Address::create eliminated (dead code) */
462 :
463 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L515
464 : Authorization check is lifted out from 'allocate' to here. */
465 :
466 0 : err = fd_system_program_allocate( ctx, &account, space, account.pubkey, NULL );
467 0 : if( FD_UNLIKELY( err ) ) return err;
468 :
469 : /* Implicit drop */
470 :
471 0 : return 0;
472 0 : }
473 :
474 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L517-L541
475 :
476 : Matches Solana Labs system_processor SystemInstruction::AllocateWithSeed { ... } => { ... } */
477 :
478 : int
479 : fd_system_program_exec_allocate_with_seed( fd_exec_instr_ctx_t * ctx,
480 0 : fd_system_program_instruction_allocate_with_seed_t const * args ) {
481 0 : int err;
482 :
483 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L523 */
484 :
485 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
486 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
487 :
488 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#524-525 */
489 :
490 0 : fd_guarded_borrowed_account_t account = {0};
491 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &account );
492 :
493 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L526-L532 */
494 :
495 0 : err = verify_seed_address(
496 0 : ctx,
497 0 : account.pubkey,
498 0 : &args->base,
499 0 : (char const *)args->seed,
500 0 : args->seed_len,
501 0 : &args->owner );
502 0 : if( FD_UNLIKELY( err ) ) return err;
503 :
504 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L533-L540
505 : Authorization check is lifted out from 'allocate' to here. */
506 :
507 0 : err = fd_system_program_allocate_and_assign(
508 0 : ctx,
509 0 : &account,
510 0 : args->space,
511 0 : &args->owner,
512 0 : &args->base,
513 0 : &args->base );
514 0 : if( FD_UNLIKELY( err ) ) return err;
515 :
516 : /* Implicit drop */
517 :
518 0 : return 0;
519 0 : }
520 :
521 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L542-L554
522 :
523 : Matches Solana Labs system_processor SystemInstruction::AssignWithSeed { ... } => { ... } */
524 :
525 : int
526 : fd_system_program_exec_assign_with_seed( fd_exec_instr_ctx_t * ctx,
527 0 : fd_system_program_instruction_assign_with_seed_t const * args ) {
528 0 : int err;
529 :
530 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#543 */
531 :
532 0 : if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) )
533 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
534 :
535 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L544-L545 */
536 :
537 0 : fd_guarded_borrowed_account_t account = {0};
538 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &account );
539 :
540 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L546-L552 */
541 :
542 0 : err = verify_seed_address(
543 0 : ctx,
544 0 : account.pubkey,
545 0 : &args->base,
546 0 : (char const *)args->seed,
547 0 : args->seed_len,
548 0 : &args->owner );
549 0 : if( FD_UNLIKELY( err ) ) return err;
550 :
551 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L553
552 : Authorization check is lifted out from 'assign' to here. */
553 :
554 0 : err = fd_system_program_assign( ctx, &account, &args->owner, &args->base, &args->base );
555 0 : if( FD_UNLIKELY( err ) ) return err;
556 :
557 : /* Implicit drop */
558 :
559 0 : return 0;
560 0 : }
561 :
562 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L405-L422
563 :
564 : Matches Solana Labs system_processor SystemInstruction::TransferWithSeed { ... } => { ... } */
565 :
566 : int
567 : fd_system_program_exec_transfer_with_seed( fd_exec_instr_ctx_t * ctx,
568 0 : fd_system_program_instruction_transfer_with_seed_t const * args ) {
569 :
570 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L410 */
571 :
572 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( ctx, 3UL ) ) )
573 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
574 :
575 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L411-L421
576 : Inlined call to system_processor::transfer_with_seed */
577 :
578 0 : ushort const from_idx = 0UL;
579 0 : ushort const from_base_idx = 1UL;
580 0 : ushort const to_idx = 2UL;
581 :
582 0 : int instr_err_code = 0;
583 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, from_base_idx, &instr_err_code ) ) ) {
584 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
585 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
586 : /* Max msg_sz: 37 - 2 + 45 = 80 < 127 => we can use printf */
587 0 : ushort idx_in_txn = ctx->instr->accounts[ from_base_idx ].index_in_transaction;
588 0 : FD_BASE58_ENCODE_32_BYTES( ctx->txn_out->accounts.keys[ idx_in_txn ].key, key_b58 );
589 0 : fd_log_collector_printf_dangerous_max_127( ctx,
590 0 : "Transfer: 'from' account %s must sign", key_b58 );
591 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
592 0 : }
593 :
594 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/system/src/system_processor.rs#L267-L274 */
595 :
596 0 : fd_pubkey_t const * base = NULL;
597 0 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, from_base_idx, &base );
598 0 : if( FD_UNLIKELY( err ) ) return err;
599 :
600 0 : fd_pubkey_t address_from_seed[1];
601 0 : do {
602 0 : int err = fd_pubkey_create_with_seed(
603 0 : ctx,
604 0 : base->uc,
605 0 : (char const *)args->from_seed,
606 0 : args->from_seed_len,
607 0 : args->from_owner.uc,
608 0 : address_from_seed->uc );
609 0 : if( FD_UNLIKELY( err ) ) return err;
610 0 : } while(0);
611 :
612 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/system/src/system_processor.rs#L276-L287 */
613 0 : fd_pubkey_t const * from_key = NULL;
614 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, from_idx, &from_key );
615 0 : if( FD_UNLIKELY( err ) ) return err;
616 :
617 0 : if( FD_UNLIKELY( 0!=memcmp( address_from_seed->uc,
618 0 : from_key->uc,
619 0 : sizeof(fd_pubkey_t) ) ) ) {
620 : /* Log msg_sz can be more or less than 127 bytes */
621 0 : FD_BASE58_ENCODE_32_BYTES( from_key->key, from_key_b58 );
622 0 : FD_BASE58_ENCODE_32_BYTES( address_from_seed->key, address_from_seed_b58 );
623 0 : fd_log_collector_printf_inefficient_max_512( ctx,
624 0 : "Transfer: 'from' address %s does not match derived address %s",
625 0 : from_key_b58,
626 0 : address_from_seed_b58 );
627 0 : ctx->txn_out->err.custom_err = FD_SYSTEM_PROGRAM_ERR_ADDR_WITH_SEED_MISMATCH;
628 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
629 0 : }
630 :
631 : /* https://github.com/solana-labs/solana/blob/v1.17.22/programs/system/src/system_processor.rs#L305-L312 */
632 0 : return fd_system_program_transfer_verified( ctx, args->lamports, from_idx, to_idx );
633 0 : }
634 :
635 : int
636 24 : fd_system_program_execute( fd_exec_instr_ctx_t * ctx ) {
637 24 : FD_EXEC_CU_UPDATE( ctx, 150UL );
638 :
639 24 : if( FD_UNLIKELY( ctx->instr->data_sz>FD_SYSTEM_PROGRAM_INSTR_FOOTPRINT ) ) {
640 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
641 0 : }
642 :
643 24 : uchar instr_mem[ FD_SYSTEM_PROGRAM_INSTR_FOOTPRINT ] __attribute__((aligned(alignof(fd_system_program_instruction_t))));
644 :
645 24 : int decode_err;
646 24 : fd_system_program_instruction_t * instruction = fd_bincode_decode_static_limited_deserialize(
647 24 : system_program_instruction,
648 24 : instr_mem,
649 24 : ctx->instr->data,
650 24 : ctx->instr->data_sz,
651 24 : FD_TXN_MTU,
652 24 : &decode_err
653 24 : );
654 24 : if( FD_UNLIKELY( decode_err ) ) {
655 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
656 0 : }
657 :
658 24 : int result = FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
659 :
660 24 : switch( instruction->discriminant ) {
661 0 : case fd_system_program_instruction_enum_create_account: {
662 0 : result = fd_system_program_exec_create_account(
663 0 : ctx, &instruction->inner.create_account );
664 0 : break;
665 0 : }
666 0 : case fd_system_program_instruction_enum_assign: {
667 0 : result = fd_system_program_exec_assign(
668 0 : ctx, &instruction->inner.assign );
669 0 : break;
670 0 : }
671 24 : case fd_system_program_instruction_enum_transfer: {
672 24 : result = fd_system_program_exec_transfer(
673 24 : ctx, instruction->inner.transfer );
674 24 : break;
675 0 : }
676 0 : case fd_system_program_instruction_enum_create_account_with_seed: {
677 0 : result = fd_system_program_exec_create_account_with_seed(
678 0 : ctx, &instruction->inner.create_account_with_seed );
679 0 : break;
680 0 : }
681 0 : case fd_system_program_instruction_enum_advance_nonce_account: {
682 0 : result = fd_system_program_exec_advance_nonce_account( ctx );
683 0 : break;
684 0 : }
685 0 : case fd_system_program_instruction_enum_withdraw_nonce_account: {
686 0 : result = fd_system_program_exec_withdraw_nonce_account(
687 0 : ctx, instruction->inner.withdraw_nonce_account );
688 0 : break;
689 0 : }
690 0 : case fd_system_program_instruction_enum_initialize_nonce_account: {
691 0 : result = fd_system_program_exec_initialize_nonce_account(
692 0 : ctx, &instruction->inner.initialize_nonce_account );
693 0 : break;
694 0 : }
695 0 : case fd_system_program_instruction_enum_authorize_nonce_account: {
696 0 : result = fd_system_program_exec_authorize_nonce_account(
697 0 : ctx, &instruction->inner.authorize_nonce_account );
698 0 : break;
699 0 : }
700 0 : case fd_system_program_instruction_enum_allocate: {
701 0 : result = fd_system_program_exec_allocate( ctx, instruction->inner.allocate );
702 0 : break;
703 0 : }
704 0 : case fd_system_program_instruction_enum_allocate_with_seed: {
705 : // https://github.com/solana-labs/solana/blob/b00d18cec4011bb452e3fe87a3412a3f0146942e/runtime/src/system_instruction_processor.rs#L525
706 0 : result = fd_system_program_exec_allocate_with_seed(
707 0 : ctx, &instruction->inner.allocate_with_seed );
708 0 : break;
709 0 : }
710 0 : case fd_system_program_instruction_enum_assign_with_seed: {
711 : // https://github.com/solana-labs/solana/blob/b00d18cec4011bb452e3fe87a3412a3f0146942e/runtime/src/system_instruction_processor.rs#L545
712 0 : result = fd_system_program_exec_assign_with_seed(
713 0 : ctx, &instruction->inner.assign_with_seed );
714 0 : break;
715 0 : }
716 0 : case fd_system_program_instruction_enum_transfer_with_seed: {
717 : // https://github.com/solana-labs/solana/blob/b00d18cec4011bb452e3fe87a3412a3f0146942e/runtime/src/system_instruction_processor.rs#L412
718 0 : result = fd_system_program_exec_transfer_with_seed(
719 0 : ctx, &instruction->inner.transfer_with_seed );
720 0 : break;
721 0 : }
722 0 : case fd_system_program_instruction_enum_upgrade_nonce_account: {
723 : // https://github.com/solana-labs/solana/blob/b00d18cec4011bb452e3fe87a3412a3f0146942e/runtime/src/system_instruction_processor.rs#L491
724 0 : result = fd_system_program_exec_upgrade_nonce_account( ctx );
725 0 : break;
726 0 : }
727 24 : }
728 :
729 24 : return result;
730 24 : }
731 :
732 : /**********************************************************************/
733 : /* Public API */
734 : /**********************************************************************/
735 :
736 : int
737 0 : fd_get_system_account_kind( fd_account_meta_t const * meta ) {
738 : /* https://github.com/anza-xyz/solana-sdk/blob/nonce-account%40v2.2.1/nonce-account/src/lib.rs#L56 */
739 0 : if( FD_UNLIKELY( memcmp( meta->owner, fd_solana_system_program_id.uc, sizeof(fd_pubkey_t) ) ) ) {
740 0 : return FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN;
741 0 : }
742 :
743 : /* https://github.com/anza-xyz/solana-sdk/blob/nonce-account%40v2.2.1/nonce-account/src/lib.rs#L57-L58 */
744 0 : if( FD_LIKELY( !meta->dlen ) ) {
745 0 : return FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_SYSTEM;
746 0 : }
747 :
748 : /* https://github.com/anza-xyz/solana-sdk/blob/nonce-account%40v2.2.1/nonce-account/src/lib.rs#L59 */
749 0 : if( FD_UNLIKELY( meta->dlen!=FD_SYSTEM_PROGRAM_NONCE_DLEN ) ) {
750 0 : return FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN;
751 0 : }
752 :
753 : /* https://github.com/anza-xyz/solana-sdk/blob/nonce-account%40v2.2.1/nonce-account/src/lib.rs#L60-L64 */
754 0 : fd_nonce_state_versions_t versions[1];
755 0 : if( FD_UNLIKELY( !fd_bincode_decode_static(
756 0 : nonce_state_versions, versions,
757 0 : fd_account_data( meta ),
758 0 : meta->dlen,
759 0 : NULL ) ) ) {
760 0 : return FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN;
761 0 : }
762 :
763 0 : fd_nonce_state_t * state = NULL;
764 0 : if( fd_nonce_state_versions_is_current( versions ) ) {
765 0 : state = &versions->inner.current;
766 0 : } else {
767 0 : state = &versions->inner.legacy;
768 0 : }
769 :
770 0 : if( FD_LIKELY( fd_nonce_state_is_initialized( state ) ) ) {
771 0 : return FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_NONCE;
772 0 : }
773 :
774 0 : return FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN;
775 0 : }
|