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