Line data Source code
1 : #include "fd_address_lookup_table_program.h"
2 : #include "../fd_executor.h"
3 : #include "../fd_pubkey_utils.h"
4 : #include "../fd_borrowed_account.h"
5 : #include "../sysvar/fd_sysvar_slot_hashes.h"
6 : #include "../../vm/syscall/fd_vm_syscall.h"
7 : #include "fd_native_cpi.h"
8 : #include "../fd_runtime_err.h"
9 : #include "../fd_system_ids.h"
10 :
11 : #include <string.h>
12 :
13 : struct fd_addrlut {
14 : fd_address_lookup_table_state_t state;
15 :
16 : fd_pubkey_t const * addr; /* points into account data */
17 : ulong addr_cnt;
18 : };
19 :
20 : typedef struct fd_addrlut fd_addrlut_t;
21 :
22 0 : #define FD_ADDRLUT_META_SZ (56UL)
23 0 : #define FD_ADDRLUT_MAX_ADDR_CNT (256UL)
24 : #define DEFAULT_COMPUTE_UNITS (750UL)
25 0 : #define MAX_ENTRIES FD_SYSVAR_SLOT_HASHES_CAP
26 :
27 : static fd_addrlut_t *
28 0 : fd_addrlut_new( void * mem ) {
29 :
30 0 : if( FD_UNLIKELY( !mem ) ) {
31 0 : FD_LOG_WARNING(( "NULL mem" ));
32 0 : return NULL;
33 0 : }
34 :
35 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_addrlut_t) ) ) ) {
36 0 : FD_LOG_WARNING(( "misaligned mem" ));
37 0 : return NULL;
38 0 : }
39 :
40 0 : return fd_type_pun( mem );
41 0 : }
42 :
43 : static int
44 : fd_addrlut_deserialize( fd_addrlut_t * lut,
45 : uchar const * data,
46 0 : ulong data_sz ) {
47 :
48 0 : lut = fd_addrlut_new( lut ); FD_TEST( lut );
49 :
50 : /* We anticipate that we require no allocations to decode the address lookup
51 : table state size and that the data is already preallocated. */
52 0 : fd_bincode_decode_ctx_t decode = {
53 0 : .data = data,
54 0 : .dataend = data+data_sz
55 0 : };
56 :
57 0 : ulong total_sz = 0UL;
58 0 : if( FD_UNLIKELY( fd_address_lookup_table_state_decode_footprint( &decode, &total_sz )!=FD_BINCODE_SUCCESS ) ) {
59 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
60 0 : }
61 :
62 : /* NOTE: We technically don't need this check if we assume that the caller
63 : is correctly handling the memory that is passed into this function. */
64 0 : if( FD_UNLIKELY( total_sz!=sizeof(fd_address_lookup_table_state_t) ) ) {
65 0 : FD_LOG_ERR(( "Unexpected total size of address lookup table state" ));
66 0 : }
67 :
68 0 : fd_address_lookup_table_state_decode( &lut->state, &decode );
69 :
70 0 : if( lut->state.discriminant==fd_address_lookup_table_state_enum_uninitialized )
71 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
72 0 : FD_TEST( lut->state.discriminant == fd_address_lookup_table_state_enum_lookup_table );
73 :
74 0 : if( data_sz < FD_ADDRLUT_META_SZ )
75 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
76 :
77 0 : uchar const * raw_addr_data = data +FD_ADDRLUT_META_SZ;
78 0 : ulong raw_addr_data_sz = data_sz-FD_ADDRLUT_META_SZ;
79 :
80 0 : if( !fd_ulong_is_aligned( raw_addr_data_sz, 32UL ) )
81 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
82 :
83 0 : lut->addr = fd_type_pun_const( raw_addr_data );
84 0 : lut->addr_cnt = raw_addr_data_sz / 32UL;
85 :
86 0 : return FD_EXECUTOR_INSTR_SUCCESS;
87 0 : }
88 :
89 : static int
90 : fd_addrlut_serialize_meta( fd_address_lookup_table_state_t const * state,
91 : uchar * data,
92 0 : ulong data_sz ) {
93 :
94 : /* TODO can this ever get hit? All code paths to this function seem
95 : to check account data size during deserialization. */
96 0 : if( FD_UNLIKELY( data_sz<FD_ADDRLUT_META_SZ ) )
97 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
98 :
99 0 : fd_bincode_encode_ctx_t encode =
100 0 : { .data = data,
101 0 : .dataend = data+FD_ADDRLUT_META_SZ };
102 0 : fd_memset( data, 0, (ulong)encode.dataend - (ulong)encode.data );
103 :
104 0 : int bin_err = fd_address_lookup_table_state_encode( state, &encode );
105 0 : FD_TEST( !bin_err );
106 :
107 0 : return FD_EXECUTOR_INSTR_SUCCESS;
108 0 : }
109 :
110 : static ulong
111 : slot_hashes_position( fd_slot_hash_t const * hashes, /* deque */
112 0 : ulong slot ) {
113 : /* Logic is copied from slice::binary_search_by() in Rust. While not fully optimized,
114 : it aims to achieve fuzzing conformance for both sorted and unsorted inputs.
115 : Returns the slot hash position of the input slot. */
116 0 : ulong size = deq_fd_slot_hash_t_cnt( hashes );
117 0 : if( FD_UNLIKELY( size==0UL ) ) return ULONG_MAX;
118 :
119 0 : ulong base = 0UL;
120 0 : while( size>1UL ) {
121 0 : ulong half = size / 2UL;
122 0 : ulong mid = base + half;
123 0 : ulong mid_slot = deq_fd_slot_hash_t_peek_index_const( hashes, mid )->slot;
124 0 : base = (slot>mid_slot) ? base : mid;
125 0 : size -= half;
126 0 : }
127 :
128 0 : return deq_fd_slot_hash_t_peek_index_const( hashes, base )->slot==slot ? base : ULONG_MAX;
129 0 : }
130 :
131 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L81-L104 */
132 : static uchar
133 : fd_addrlut_status( fd_lookup_table_meta_t const * state,
134 : ulong current_slot,
135 : fd_slot_hash_t const * slot_hashes, /* deque */
136 12 : ulong * remaining_blocks ) {
137 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L82-L83 */
138 12 : if( state->deactivation_slot==ULONG_MAX ) {
139 12 : return FD_ADDRLUT_STATUS_ACTIVATED;
140 12 : }
141 :
142 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L84-L87 */
143 0 : if( state->deactivation_slot==current_slot ) {
144 0 : *remaining_blocks = MAX_ENTRIES + 1UL;
145 0 : return FD_ADDRLUT_STATUS_DEACTIVATING;
146 0 : }
147 :
148 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L88-L100 */
149 0 : ulong slot_hash_position = slot_hashes_position( slot_hashes, state->deactivation_slot );
150 0 : if( slot_hash_position!=ULONG_MAX ) {
151 0 : *remaining_blocks = MAX_ENTRIES - slot_hash_position;
152 0 : return FD_ADDRLUT_STATUS_DEACTIVATING;
153 0 : }
154 :
155 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L102 */
156 0 : return FD_ADDRLUT_STATUS_DEACTIVATED;
157 0 : }
158 :
159 : /* Note on uses of fd_borrowed_account_acquire_write_is_safe:
160 :
161 : In some places of this program, the Agave implementation acquires a
162 : "mutable borrow" on the account that is immediately dropped before
163 : any borrow can occur. In other words, this borrow attempt only
164 : introduces a "borrow failed" error case into the protocol but
165 : otherwise introduces no side effects. i.e.
166 :
167 : if not fd_borrowed_account_acquire_write():
168 : return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED
169 : ... read only operations ...
170 : fd_borrowed_account_release_write()
171 : ... arbitrary logic ...
172 :
173 : Is equivalent to
174 :
175 : if not fd_borrowed_account_acquire_write_is_safe():
176 : return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED
177 : ... read only operations ...
178 : ... arbitrary logic ... */
179 :
180 : static int
181 : create_lookup_table( fd_exec_instr_ctx_t * ctx,
182 0 : fd_addrlut_create_t const * create ) {
183 :
184 0 : # define ACC_IDX_LUT (0UL)
185 0 : # define ACC_IDX_AUTHORITY (1UL)
186 0 : # define ACC_IDX_PAYER (2UL)
187 :
188 0 : int err;
189 :
190 0 : ulong lut_lamports = 0UL;
191 0 : fd_pubkey_t const * lut_key = NULL;
192 0 : fd_pubkey_t const * lut_owner = NULL;
193 0 : fd_pubkey_t const * authority_key = NULL;
194 0 : fd_pubkey_t const * payer_key = NULL;
195 :
196 : /* Prepare LUT account **********************************************/
197 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L59-L60 */
198 : /* try_borrow_instruction_account => get_index_of_instruction_account_in_transaction */
199 0 : fd_guarded_borrowed_account_t lut_acct = {0};
200 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
201 :
202 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L60-L62 */
203 0 : lut_lamports = fd_borrowed_account_get_lamports( &lut_acct );
204 0 : lut_key = lut_acct.acct->pubkey;
205 0 : lut_owner = fd_borrowed_account_get_owner( &lut_acct );
206 :
207 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L63-L70 */
208 0 : if( !FD_FEATURE_ACTIVE_BANK( ctx->txn_ctx->bank, relax_authority_signer_check_for_lookup_table_creation )
209 0 : && fd_borrowed_account_get_data_len( &lut_acct ) != 0UL ) {
210 0 : fd_log_collector_msg_literal( ctx, "Table account must not be allocated" );
211 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
212 0 : }
213 :
214 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L72 */
215 0 : fd_borrowed_account_drop( &lut_acct );
216 :
217 : /* Prepare authority account ****************************************/
218 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L74-L75 */
219 : /* try_borrow_instruction_account => get_index_of_instruction_account_in_transaction */
220 0 : fd_guarded_borrowed_account_t authority_acct = {0};
221 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_AUTHORITY, &authority_acct );
222 :
223 :
224 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L75 */
225 0 : authority_key = authority_acct.acct->pubkey;
226 :
227 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L76-L83 */
228 0 : if( !FD_FEATURE_ACTIVE_BANK( ctx->txn_ctx->bank, relax_authority_signer_check_for_lookup_table_creation )
229 0 : && !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_AUTHORITY, NULL ) ) {
230 0 : fd_log_collector_msg_literal( ctx, "Authority account must be a signer" );
231 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
232 0 : }
233 :
234 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L85 */
235 0 : fd_borrowed_account_drop( &authority_acct );
236 :
237 : /* Prepare payer account ********************************************/
238 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L87-L88 */
239 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
240 0 : fd_guarded_borrowed_account_t payer_acct = {0};
241 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_PAYER, &payer_acct );
242 :
243 0 : payer_key = payer_acct.acct->pubkey;
244 :
245 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L89-L92 */
246 0 : if( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_PAYER, NULL ) ) {
247 0 : fd_log_collector_msg_literal( ctx, "Payer account must be a signer" );
248 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
249 0 : }
250 :
251 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L94 */
252 0 : fd_borrowed_account_drop( &payer_acct );
253 :
254 0 : ulong derivation_slot = 1UL;
255 :
256 0 : do {
257 0 : fd_slot_hash_t const * slot_hash = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
258 0 : if( FD_UNLIKELY( !slot_hash ) ) {
259 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
260 0 : }
261 :
262 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L97 */
263 0 : ulong is_recent_slot = slot_hashes_position( slot_hash, create->recent_slot )!=ULONG_MAX;
264 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hash );
265 0 : if( FD_UNLIKELY( !is_recent_slot ) ) {
266 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L100-L105 */
267 : /* Max msg_sz: 24 - 3 + 20 = 41 < 127 => we can use printf */
268 0 : fd_log_collector_printf_dangerous_max_127( ctx, "%lu is not a recent slot", create->recent_slot );
269 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
270 0 : } else {
271 0 : derivation_slot = create->recent_slot;
272 0 : }
273 0 : } while(0);
274 :
275 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L109-L118 */
276 0 : fd_pubkey_t derived_tbl_key[1];
277 0 : uchar const * seeds[2];
278 0 : ulong seed_szs[2] = { sizeof(fd_pubkey_t), sizeof(ulong) };
279 0 : seeds[0] = (uchar const *)authority_key;
280 0 : seeds[1] = (uchar const *)&derivation_slot;
281 0 : err = fd_pubkey_derive_pda( &fd_solana_address_lookup_table_program_id, 2UL, seeds,
282 0 : seed_szs, (uchar*)&create->bump_seed, derived_tbl_key, &ctx->txn_ctx->custom_err );
283 0 : if( FD_UNLIKELY( err ) ) {
284 0 : return err;
285 0 : }
286 :
287 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L120-L127 */
288 0 : if( FD_UNLIKELY( 0!=memcmp( lut_key->key, derived_tbl_key->key, sizeof(fd_pubkey_t) ) ) ) {
289 : /* Max msg_sz: 44 - 2 + 45 = 87 < 127 => we can use printf */
290 0 : fd_log_collector_printf_dangerous_max_127( ctx,
291 0 : "Table address must match derived address: %s", FD_BASE58_ENC_32_ALLOCA( derived_tbl_key ) );
292 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
293 0 : }
294 :
295 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L129-L135 */
296 0 : if( FD_FEATURE_ACTIVE_BANK( ctx->txn_ctx->bank, relax_authority_signer_check_for_lookup_table_creation )
297 0 : && 0==memcmp( lut_owner, fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) {
298 0 : return FD_EXECUTOR_INSTR_SUCCESS;
299 0 : }
300 :
301 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L137-L142 */
302 :
303 0 : fd_rent_t const rent = fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache );
304 0 : ulong tbl_acct_data_len = 0x38UL;
305 0 : ulong required_lamports = fd_rent_exempt_minimum_balance( &rent, tbl_acct_data_len );
306 0 : /* */ required_lamports = fd_ulong_max( required_lamports, 1UL );
307 0 : /* */ required_lamports = fd_ulong_sat_sub( required_lamports, lut_lamports );
308 :
309 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L144-L149 */
310 0 : if( required_lamports>0UL ) {
311 : // Create account metas
312 0 : FD_SPAD_FRAME_BEGIN( ctx->txn_ctx->spad ) {
313 0 : fd_vm_rust_account_meta_t * acct_metas = (fd_vm_rust_account_meta_t *)
314 0 : fd_spad_alloc( ctx->txn_ctx->spad, FD_VM_RUST_ACCOUNT_META_ALIGN, 2 * sizeof(fd_vm_rust_account_meta_t) );
315 0 : fd_native_cpi_create_account_meta( payer_key, 1, 1, &acct_metas[0] );
316 0 : fd_native_cpi_create_account_meta( lut_key, 0, 1, &acct_metas[1] );
317 :
318 : // Create signers list
319 0 : fd_pubkey_t signers[16];
320 0 : ulong signers_cnt = 1;
321 0 : signers[0] = *payer_key;
322 :
323 : // Create system program instruction
324 0 : uchar instr_data[FD_TXN_MTU];
325 0 : fd_system_program_instruction_t instr = {
326 0 : .discriminant = fd_system_program_instruction_enum_transfer,
327 0 : .inner = {
328 0 : .transfer = required_lamports,
329 0 : }
330 0 : };
331 :
332 0 : fd_bincode_encode_ctx_t encode_ctx = {
333 0 : .data = instr_data,
334 0 : .dataend = instr_data + FD_TXN_MTU
335 0 : };
336 :
337 : // This should never fail.
338 0 : int err = fd_system_program_instruction_encode( &instr, &encode_ctx );
339 0 : if( FD_UNLIKELY( err ) ) {
340 0 : return FD_EXECUTOR_INSTR_ERR_FATAL;
341 0 : }
342 :
343 0 : err = fd_native_cpi_native_invoke( ctx,
344 0 : &fd_solana_system_program_id,
345 0 : instr_data,
346 0 : FD_TXN_MTU,
347 0 : acct_metas,
348 0 : 2UL,
349 0 : signers,
350 0 : signers_cnt );
351 0 : if( FD_UNLIKELY( err ) ) {
352 0 : return err;
353 0 : }
354 0 : } FD_SPAD_FRAME_END;
355 0 : }
356 :
357 0 : FD_SPAD_FRAME_BEGIN( ctx->txn_ctx->spad ) {
358 0 : fd_vm_rust_account_meta_t * acct_metas = ( fd_vm_rust_account_meta_t * )
359 0 : fd_spad_alloc( ctx->txn_ctx->spad, FD_VM_RUST_ACCOUNT_META_ALIGN, sizeof(fd_vm_rust_account_meta_t) );
360 0 : fd_native_cpi_create_account_meta( lut_key, 1, 1, &acct_metas[0] );
361 :
362 : // Create signers list
363 0 : fd_pubkey_t signers[16];
364 0 : ulong signers_cnt = 1;
365 0 : signers[0] = *lut_key;
366 :
367 : // Create system program allocate instruction
368 0 : uchar instr_data[FD_TXN_MTU];
369 0 : fd_system_program_instruction_t instr = {
370 0 : .discriminant = fd_system_program_instruction_enum_allocate,
371 0 : .inner = {
372 0 : .allocate = FD_LOOKUP_TABLE_META_SIZE,
373 0 : }
374 0 : };
375 :
376 0 : fd_bincode_encode_ctx_t encode_ctx = {
377 0 : .data = instr_data,
378 0 : .dataend = instr_data + FD_TXN_MTU
379 0 : };
380 :
381 : // This should never fail.
382 0 : int err = fd_system_program_instruction_encode( &instr, &encode_ctx );
383 0 : if( FD_UNLIKELY( err ) ) {
384 0 : return FD_EXECUTOR_INSTR_ERR_FATAL;
385 0 : }
386 :
387 : // Execute allocate instruction
388 0 : err = fd_native_cpi_native_invoke( ctx,
389 0 : &fd_solana_system_program_id,
390 0 : instr_data,
391 0 : FD_TXN_MTU,
392 0 : acct_metas,
393 0 : 1UL,
394 0 : signers,
395 0 : signers_cnt );
396 0 : if( FD_UNLIKELY( err ) ) {
397 0 : return err;
398 0 : }
399 :
400 : // Prepare system program assign instruction
401 0 : instr = (fd_system_program_instruction_t) {
402 0 : .discriminant = fd_system_program_instruction_enum_assign,
403 0 : .inner = {
404 0 : .assign = fd_solana_address_lookup_table_program_id,
405 0 : }
406 0 : };
407 :
408 0 : encode_ctx = (fd_bincode_encode_ctx_t) {
409 0 : .data = instr_data,
410 0 : .dataend = instr_data + FD_TXN_MTU
411 0 : };
412 :
413 : // This should never fail.
414 0 : err = fd_system_program_instruction_encode( &instr, &encode_ctx );
415 0 : if( FD_UNLIKELY( err ) ) {
416 0 : return FD_EXECUTOR_INSTR_ERR_FATAL;
417 0 : }
418 :
419 : // Execute assign instruction
420 0 : err = fd_native_cpi_native_invoke( ctx,
421 0 : &fd_solana_system_program_id,
422 0 : instr_data,
423 0 : FD_TXN_MTU,
424 0 : acct_metas,
425 0 : 1UL,
426 0 : signers,
427 0 : signers_cnt );
428 0 : if( FD_UNLIKELY( err ) ) {
429 0 : return err;
430 0 : }
431 0 : } FD_SPAD_FRAME_END;
432 :
433 :
434 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L164-L165 */
435 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
436 :
437 0 : fd_address_lookup_table_state_t state[1];
438 0 : fd_address_lookup_table_state_new( state );
439 0 : state->discriminant = fd_address_lookup_table_state_enum_lookup_table;
440 0 : fd_address_lookup_table_new( &state->inner.lookup_table );
441 0 : fd_memcpy( state->inner.lookup_table.meta.authority.key, authority_key->key, 32UL );
442 0 : state->inner.lookup_table.meta.has_authority = 1;
443 0 : state->inner.lookup_table.meta.deactivation_slot = ULONG_MAX;
444 :
445 0 : uchar * data = NULL;
446 0 : ulong dlen = 0UL;
447 0 : err = fd_borrowed_account_get_data_mut( &lut_acct, &data, &dlen );
448 0 : if( FD_UNLIKELY( err ) ) {
449 0 : return err;
450 0 : }
451 :
452 0 : int state_err = fd_addrlut_serialize_meta( state, data, sizeof(fd_address_lookup_table_state_t) );
453 0 : if( FD_UNLIKELY( state_err ) ) { return state_err; }
454 :
455 : /* implicit drop of lut_acct */
456 :
457 0 : return FD_EXECUTOR_INSTR_SUCCESS;
458 :
459 0 : # undef ACC_IDX_LUT
460 0 : # undef ACC_IDX_AUTHORITY
461 0 : # undef ACC_IDX_PAYER
462 0 : }
463 :
464 : static int
465 0 : freeze_lookup_table( fd_exec_instr_ctx_t * ctx ) {
466 :
467 0 : # define ACC_IDX_LUT (0UL)
468 0 : # define ACC_IDX_AUTHORITY (1UL)
469 0 : int err;
470 :
471 : /* Prepare LUT account **********************************************/
472 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L177-L178 */
473 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
474 0 : fd_guarded_borrowed_account_t lut_acct = {0};
475 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
476 :
477 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L178-L181 */
478 0 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &lut_acct ), fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
479 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
480 0 : }
481 :
482 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L182 */
483 0 : fd_borrowed_account_drop( &lut_acct );
484 :
485 : /* Prepare authority account ****************************************/
486 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L184-L185 */
487 0 : fd_pubkey_t const * authority_key = NULL;
488 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
489 0 : fd_guarded_borrowed_account_t authority_acct = {0};
490 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_AUTHORITY, &authority_acct );
491 :
492 0 : authority_key = authority_acct.acct->pubkey;
493 :
494 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L186-L189 */
495 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_AUTHORITY, NULL ) ) ) {
496 0 : fd_log_collector_msg_literal( ctx, "Authority account must be a signer" );
497 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
498 0 : }
499 :
500 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L191 */
501 0 : fd_borrowed_account_drop( &authority_acct );
502 :
503 : /* Update lookup table account **************************************/
504 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L193-L194 */
505 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
506 :
507 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L194 */
508 0 : uchar const * lut_data = fd_borrowed_account_get_data( &lut_acct );
509 0 : ulong lut_data_sz = fd_borrowed_account_get_data_len( &lut_acct );
510 :
511 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L195 */
512 0 : fd_addrlut_t lut[1];
513 0 : err = fd_addrlut_deserialize( lut, lut_data, lut_data_sz );
514 0 : if( FD_UNLIKELY( err ) ) {
515 0 : return err;
516 0 : }
517 :
518 0 : fd_address_lookup_table_t * state = &lut->state.inner.lookup_table;
519 :
520 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L197-L200 */
521 0 : if( FD_UNLIKELY( !state->meta.has_authority ) ) {
522 0 : fd_log_collector_msg_literal( ctx, "Lookup table is already frozen");
523 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
524 0 : }
525 :
526 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L201-L203 */
527 0 : if( FD_UNLIKELY( 0!=memcmp( state->meta.authority.key, authority_key->key, sizeof(fd_pubkey_t) ) ) ) {
528 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
529 0 : }
530 :
531 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L204-L207 */
532 0 : if( FD_UNLIKELY( state->meta.deactivation_slot!=ULONG_MAX ) ) {
533 0 : fd_log_collector_msg_literal( ctx, "Deactivated tables cannot be frozen" );
534 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
535 0 : }
536 :
537 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L208-L211 */
538 0 : if( FD_UNLIKELY( !lut->addr_cnt ) ) {
539 0 : fd_log_collector_msg_literal( ctx, "Empty lookup tables cannot be frozen" );
540 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
541 0 : }
542 :
543 0 : uchar *data = NULL;
544 0 : ulong dlen = 0UL;
545 0 : err = fd_borrowed_account_get_data_mut( &lut_acct, &data, &dlen );
546 0 : if( FD_UNLIKELY( err ) ) {
547 0 : return err;
548 0 : }
549 :
550 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L213-L218 */
551 0 : state->meta.has_authority = 0;
552 :
553 0 : err = fd_addrlut_serialize_meta( &lut->state, data, dlen );
554 0 : if( FD_UNLIKELY( err ) ) {
555 0 : return err;
556 0 : }
557 :
558 : /* implicit drop of lut_acct */
559 :
560 0 : return FD_EXECUTOR_INSTR_SUCCESS;
561 0 : # undef ACC_IDX_LUT
562 0 : # undef ACC_IDX_AUTHORITY
563 0 : }
564 :
565 : static int
566 : extend_lookup_table( fd_exec_instr_ctx_t * ctx,
567 0 : fd_addrlut_extend_t const * extend ) {
568 :
569 0 : # define ACC_IDX_LUT (0UL)
570 0 : # define ACC_IDX_AUTHORITY (1UL)
571 0 : # define ACC_IDX_PAYER (2UL)
572 0 : int err;
573 :
574 : /* Prepare LUT account **********************************************/
575 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L230-L236 */
576 0 : fd_pubkey_t const * lut_key = NULL;
577 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
578 :
579 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L231-L232 */
580 0 : fd_guarded_borrowed_account_t lut_acct = {0};
581 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
582 :
583 0 : lut_key = lut_acct.acct->pubkey;
584 :
585 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L233-235 */
586 0 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &lut_acct ), fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) )
587 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
588 :
589 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L237 */
590 0 : fd_borrowed_account_drop( &lut_acct );
591 :
592 : /* Prepare authority account ****************************************/
593 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L238-L245 */
594 0 : fd_pubkey_t const * authority_key = NULL;
595 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
596 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L239-L240 */
597 0 : fd_guarded_borrowed_account_t authority_acct = {0};
598 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_AUTHORITY, &authority_acct );
599 :
600 0 : authority_key = authority_acct.acct->pubkey;
601 :
602 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L241-L244 */
603 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_AUTHORITY, NULL ) ) ) {
604 0 : fd_log_collector_msg_literal( ctx, "Authority account must be a signer" );
605 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
606 0 : }
607 :
608 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L246 */
609 0 : fd_borrowed_account_drop( &authority_acct );
610 :
611 0 : uchar const * lut_data = NULL;
612 0 : ulong lut_data_sz = 0UL;
613 0 : ulong lut_lamports = 0UL;
614 0 : ulong new_table_data_sz = 0UL;
615 :
616 : /* Update lookup table account **************************************/
617 :
618 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L248-L249 */
619 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
620 :
621 0 : lut_data = fd_borrowed_account_get_data( &lut_acct );
622 0 : lut_data_sz = fd_borrowed_account_get_data_len( &lut_acct );
623 0 : lut_lamports = fd_borrowed_account_get_lamports( &lut_acct );
624 :
625 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L251 */
626 0 : fd_addrlut_t lut[1];
627 0 : err = fd_addrlut_deserialize( lut, (uchar *)lut_data, lut_data_sz );
628 0 : if( FD_UNLIKELY( err ) ) {
629 0 : return err;
630 0 : }
631 :
632 0 : fd_address_lookup_table_t * state = &lut->state.inner.lookup_table;
633 :
634 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L253-L255 */
635 0 : if( FD_UNLIKELY( !state->meta.has_authority ) ) {
636 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
637 0 : }
638 :
639 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L256-L258 */
640 0 : if( FD_UNLIKELY( 0!=memcmp( state->meta.authority.key, authority_key->key, sizeof(fd_pubkey_t) ) ) ) {
641 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
642 0 : }
643 :
644 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L259-L262 */
645 0 : if( FD_UNLIKELY( state->meta.deactivation_slot != ULONG_MAX ) ) {
646 0 : fd_log_collector_msg_literal( ctx, "Deactivated tables cannot be extended" );
647 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
648 0 : }
649 :
650 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L263-L269 */
651 0 : if( FD_UNLIKELY( lut->addr_cnt >= FD_ADDRLUT_MAX_ADDR_CNT ) ) {
652 0 : fd_log_collector_msg_literal( ctx, "Lookup table is full and cannot contain more addresses" );
653 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
654 0 : }
655 :
656 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L271-L274 */
657 0 : if( FD_UNLIKELY( !extend->new_addrs_len ) ) {
658 0 : fd_log_collector_msg_literal( ctx, "Must extend with at least one address" );
659 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
660 0 : }
661 :
662 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L276-L279 */
663 0 : ulong old_addr_cnt = lut->addr_cnt;
664 0 : ulong new_addr_cnt = lut->addr_cnt + extend->new_addrs_len;
665 0 : if( FD_UNLIKELY( new_addr_cnt > FD_ADDRLUT_MAX_ADDR_CNT ) ) {
666 : /* Max msg_sz: 65 - 6 + 20*2 = 99 < 127 => we can use printf */
667 0 : fd_log_collector_printf_dangerous_max_127( ctx,
668 0 : "Extended lookup table length %lu would exceed max capacity of %lu", new_addr_cnt, FD_ADDRLUT_MAX_ADDR_CNT );
669 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
670 0 : }
671 :
672 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L290 */
673 0 : fd_sol_sysvar_clock_t clock_;
674 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
675 0 : if( FD_UNLIKELY( !clock ) ) {
676 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
677 0 : }
678 :
679 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L291-L299 */
680 0 : if( clock->slot!=state->meta.last_extended_slot ) {
681 0 : state->meta.last_extended_slot = clock->slot;
682 0 : state->meta.last_extended_slot_start_index = (uchar)lut->addr_cnt;
683 0 : }
684 :
685 : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/address-lookup-table/src/processor.rs#L302
686 0 : new_table_data_sz = FD_ADDRLUT_META_SZ + new_addr_cnt * sizeof(fd_pubkey_t);
687 :
688 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/address-lookup-table/src/processor.rs#L286 */
689 0 : uchar * lut_data_mut = NULL;
690 0 : ulong lut_data_mut_len = 0;
691 0 : err = fd_borrowed_account_get_data_mut( &lut_acct, &lut_data_mut, &lut_data_mut_len );
692 0 : if( FD_UNLIKELY( err ) ) {
693 0 : return err;
694 0 : }
695 0 : fd_txn_account_resize( lut_acct.acct, new_table_data_sz );
696 :
697 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L307-L310 */
698 0 : err = fd_addrlut_serialize_meta( &lut->state, lut_data_mut, lut_data_mut_len );
699 0 : if( FD_UNLIKELY( err ) ) {
700 0 : return err;
701 0 : }
702 :
703 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L311-L313 */
704 0 : do {
705 0 : uchar * new_keys = lut_data_mut + FD_ADDRLUT_META_SZ + old_addr_cnt * sizeof(fd_pubkey_t);
706 0 : fd_memcpy( new_keys, extend->new_addrs, extend->new_addrs_len * sizeof(fd_pubkey_t) );
707 0 : } while(0);
708 0 : fd_borrowed_account_set_data_length( &lut_acct, new_table_data_sz );
709 0 : lut->addr = (fd_pubkey_t *)(lut_data_mut + FD_ADDRLUT_META_SZ);
710 0 : lut->addr_cnt = new_addr_cnt;
711 :
712 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L316 */
713 0 : fd_borrowed_account_drop( &lut_acct );
714 :
715 :
716 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L317-L321 */
717 0 : fd_rent_t rent[1] = { fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache ) };
718 0 : ulong required_lamports = fd_rent_exempt_minimum_balance( rent, new_table_data_sz );
719 0 : /* */ required_lamports = fd_ulong_max ( required_lamports, 1UL );
720 0 : /* */ required_lamports = fd_ulong_sat_sub( required_lamports, lut_lamports );
721 :
722 0 : if( required_lamports ) {
723 0 : fd_pubkey_t const * payer_key = NULL;
724 :
725 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
726 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L325-L326 */
727 0 : fd_guarded_borrowed_account_t payer_acct = {0};
728 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_PAYER, &payer_acct );
729 :
730 0 : payer_key = payer_acct.acct->pubkey;
731 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L327-L330 */
732 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_PAYER, NULL ) ) ) {
733 0 : fd_log_collector_msg_literal( ctx, "Payer account must be a signer" );
734 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
735 0 : }
736 :
737 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L332 */
738 0 : fd_borrowed_account_drop( &payer_acct );
739 :
740 :
741 0 : FD_SPAD_FRAME_BEGIN( ctx->txn_ctx->spad ) {
742 : // Create account metas
743 0 : fd_vm_rust_account_meta_t * acct_metas = (fd_vm_rust_account_meta_t *)
744 0 : fd_spad_alloc( ctx->txn_ctx->spad, FD_VM_RUST_ACCOUNT_META_ALIGN, 2 * sizeof(fd_vm_rust_account_meta_t) );
745 0 : fd_native_cpi_create_account_meta( payer_key, 1, 1, &acct_metas[0] );
746 0 : fd_native_cpi_create_account_meta( lut_key, 0, 1, &acct_metas[1] );
747 :
748 : // Create signers list
749 0 : fd_pubkey_t signers[16];
750 0 : ulong signers_cnt = 1UL;
751 0 : signers[0] = *payer_key;
752 :
753 : // Create system program instruction
754 0 : uchar instr_data[FD_TXN_MTU];
755 0 : fd_system_program_instruction_t instr = {
756 0 : .discriminant = fd_system_program_instruction_enum_transfer,
757 0 : .inner = {
758 0 : .transfer = required_lamports,
759 0 : }
760 0 : };
761 :
762 0 : fd_bincode_encode_ctx_t encode_ctx = {
763 0 : .data = instr_data,
764 0 : .dataend = instr_data + FD_TXN_MTU
765 0 : };
766 :
767 : // This should never fail.
768 0 : int err = fd_system_program_instruction_encode( &instr, &encode_ctx );
769 0 : if( FD_UNLIKELY( err ) ) {
770 0 : return FD_EXECUTOR_INSTR_ERR_FATAL;
771 0 : }
772 :
773 0 : err = fd_native_cpi_native_invoke( ctx,
774 0 : &fd_solana_system_program_id,
775 0 : instr_data,
776 0 : FD_TXN_MTU,
777 0 : acct_metas,
778 0 : 2UL,
779 0 : signers,
780 0 : signers_cnt );
781 0 : if( FD_UNLIKELY( err ) ) {
782 0 : return err;
783 0 : }
784 0 : } FD_SPAD_FRAME_END;
785 0 : }
786 :
787 0 : return FD_EXECUTOR_INSTR_SUCCESS;
788 :
789 0 : # undef ACC_IDX_LUT
790 0 : # undef ACC_IDX_AUTHORITY
791 0 : # undef ACC_IDX_PAYER
792 0 : }
793 :
794 : static int
795 0 : deactivate_lookup_table( fd_exec_instr_ctx_t * ctx ) {
796 :
797 0 : # define ACC_IDX_LUT (0UL)
798 0 : # define ACC_IDX_AUTHORITY (1UL)
799 0 : int err;
800 :
801 : /* Prepare LUT account **********************************************/
802 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L346-L351 */
803 : /* try_borrow_instruction_account => get_index_of_instruction_account_in_transaction */
804 :
805 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L347-L348 */
806 0 : fd_guarded_borrowed_account_t lut_acct = {0};
807 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
808 :
809 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L348-L350 */
810 0 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &lut_acct ), fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) )
811 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
812 :
813 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L352 */
814 0 : fd_borrowed_account_drop( &lut_acct );
815 :
816 : /* Prepare authority account ****************************************/
817 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L353-L360 */
818 0 : fd_pubkey_t const * authority_key = NULL;
819 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
820 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L354-L355 */
821 0 : fd_guarded_borrowed_account_t authority_acct = {0};
822 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_AUTHORITY, &authority_acct );
823 :
824 0 : authority_key = authority_acct.acct->pubkey;
825 :
826 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L356-L359 */
827 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_AUTHORITY, NULL ) ) ) {
828 0 : fd_log_collector_msg_literal( ctx, "Authority account must be a signer" );
829 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
830 0 : }
831 :
832 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L361 */
833 0 : fd_borrowed_account_drop( &authority_acct );
834 :
835 : /* Update lookup table account **************************************/
836 :
837 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L363-L364 */
838 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
839 :
840 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L364 */
841 0 : uchar const * lut_data = fd_borrowed_account_get_data( &lut_acct );
842 0 : ulong lut_data_sz = fd_borrowed_account_get_data_len( &lut_acct );
843 :
844 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L365 */
845 0 : fd_addrlut_t lut[1];
846 0 : err = fd_addrlut_deserialize( lut, (uchar *)lut_data, lut_data_sz );
847 0 : if( FD_UNLIKELY( err ) ) {
848 0 : return err;
849 0 : }
850 :
851 0 : fd_address_lookup_table_t * state = &lut->state.inner.lookup_table;
852 :
853 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L367-L370 */
854 0 : if( FD_UNLIKELY( !state->meta.has_authority ) ) {
855 0 : fd_log_collector_msg_literal( ctx, "Lookup table is frozen" );
856 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
857 0 : }
858 :
859 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L371-L373 */
860 0 : if( FD_UNLIKELY( 0!=memcmp( state->meta.authority.key, authority_key->key, sizeof(fd_pubkey_t) ) ) ) {
861 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
862 0 : }
863 :
864 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L374-L377 */
865 0 : if( FD_UNLIKELY( state->meta.deactivation_slot != ULONG_MAX ) ) {
866 0 : fd_log_collector_msg_literal( ctx, "Lookup table is already deactivated" );
867 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
868 0 : }
869 :
870 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L380 */
871 0 : fd_sol_sysvar_clock_t clock_;
872 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
873 0 : if( FD_UNLIKELY( !clock ) ) {
874 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
875 0 : }
876 :
877 0 : uchar * data = NULL;
878 0 : ulong dlen = 0UL;
879 0 : err = fd_borrowed_account_get_data_mut ( &lut_acct, &data, &dlen );
880 0 : if( FD_UNLIKELY( err ) ) {
881 0 : return err;
882 0 : }
883 :
884 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L381 */
885 0 : state->meta.deactivation_slot = clock->slot;
886 :
887 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L383-L386 */
888 0 : err = fd_addrlut_serialize_meta( &lut->state, data, dlen );
889 0 : if( FD_UNLIKELY( err ) ) {
890 0 : return err;
891 0 : }
892 :
893 : /* implicit drop of lut_acct */
894 :
895 0 : return FD_EXECUTOR_INSTR_SUCCESS;
896 :
897 0 : # undef ACC_IDX_LUT
898 0 : # undef ACC_IDX_AUTHORITY
899 0 : }
900 :
901 : static int
902 0 : close_lookup_table( fd_exec_instr_ctx_t * ctx ) {
903 :
904 0 : # define ACC_IDX_LUT (0UL)
905 0 : # define ACC_IDX_AUTHORITY (1UL)
906 0 : # define ACC_IDX_RECIPIENT (2UL)
907 0 : int err;
908 :
909 : /* Prepare LUT account **********************************************/
910 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L395-L400 */
911 : /* try_borrow_instruction_account => get_index_of_instruction_account_in_transaction */
912 :
913 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L396-L397 */
914 0 : fd_guarded_borrowed_account_t lut_acct = {0};
915 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
916 :
917 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L397-L399 */
918 0 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &lut_acct ), fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) )
919 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
920 :
921 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L401 */
922 0 : fd_borrowed_account_drop( &lut_acct );
923 :
924 : /* Prepare authority account ****************************************/
925 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L402-L409 */
926 0 : fd_pubkey_t const * authority_key = NULL;
927 : /* try_borrow_account => get_index_of_instruction_account_in_transaction */
928 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L403-L404 */
929 0 : fd_guarded_borrowed_account_t authority_acct = {0};
930 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_AUTHORITY, &authority_acct );
931 :
932 0 : authority_key = authority_acct.acct->pubkey;
933 :
934 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L405-L408 */
935 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_AUTHORITY, NULL ) ) ) {
936 0 : fd_log_collector_msg_literal( ctx, "Authority account must be a signer" );
937 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
938 0 : }
939 :
940 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L410 */
941 0 : fd_borrowed_account_drop( &authority_acct );
942 :
943 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L411 */
944 0 : err = fd_exec_instr_ctx_check_num_insn_accounts( ctx, 3 );
945 0 : if( FD_UNLIKELY( err ) ) {
946 0 : return err;
947 0 : }
948 :
949 : /* It's ok to directly access the instruction accounts because we already verified 3 expected instruction accounts.
950 : https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L412-L420 */
951 0 : if( FD_UNLIKELY( ctx->instr->accounts[0].index_in_transaction ==
952 0 : ctx->instr->accounts[2].index_in_transaction ) ) {
953 0 : fd_log_collector_msg_literal( ctx, "Lookup table cannot be the recipient of reclaimed lamports" );
954 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
955 0 : }
956 :
957 0 : ulong withdrawn_lamports = 0UL;
958 0 : uchar const * lut_data = NULL;
959 0 : ulong lut_data_sz = 0UL;
960 :
961 : /* Update lookup table account **************************************/
962 :
963 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L423-L424 */
964 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
965 :
966 0 : withdrawn_lamports = fd_borrowed_account_get_lamports( &lut_acct );
967 0 : lut_data = fd_borrowed_account_get_data( &lut_acct );
968 0 : lut_data_sz = fd_borrowed_account_get_data_len( &lut_acct );
969 :
970 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L426 */
971 0 : fd_addrlut_t lut[1];
972 0 : err = fd_addrlut_deserialize( lut, (uchar *)lut_data, lut_data_sz );
973 0 : if( FD_UNLIKELY( err ) ) {
974 0 : return err;
975 0 : }
976 :
977 0 : fd_address_lookup_table_t * state = &lut->state.inner.lookup_table;
978 :
979 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L428-L431 */
980 0 : if( FD_UNLIKELY( !state->meta.has_authority ) ) {
981 0 : fd_log_collector_msg_literal( ctx, "Lookup table is frozen" );
982 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
983 0 : }
984 :
985 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L432-L434 */
986 0 : if( FD_UNLIKELY( 0!=memcmp( state->meta.authority.key, authority_key->key, sizeof(fd_pubkey_t) ) ) ) {
987 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
988 0 : }
989 :
990 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L437 */
991 0 : fd_sol_sysvar_clock_t clock_;
992 0 : fd_sol_sysvar_clock_t * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
993 0 : if( FD_UNLIKELY( !clock ) ) {
994 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
995 0 : }
996 :
997 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L438 */
998 0 : fd_slot_hash_t const * slot_hash = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
999 0 : if( FD_UNLIKELY( !slot_hash ) ) {
1000 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1001 0 : }
1002 :
1003 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L440 */
1004 0 : ulong remaining_blocks = 0UL;
1005 0 : int status = fd_addrlut_status( &state->meta, clock->slot, slot_hash, &remaining_blocks );
1006 0 : fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hash );
1007 :
1008 0 : switch( status ) {
1009 0 : case FD_ADDRLUT_STATUS_ACTIVATED:
1010 0 : fd_log_collector_msg_literal( ctx, "Lookup table is not deactivated" );
1011 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1012 0 : case FD_ADDRLUT_STATUS_DEACTIVATING:
1013 : /* Max msg_sz: 65 - 3 + 20 = 82 < 127 => we can use printf */
1014 0 : fd_log_collector_printf_dangerous_max_127( ctx,
1015 0 : "Table cannot be closed until it's fully deactivated in %lu blocks", remaining_blocks );
1016 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1017 0 : case FD_ADDRLUT_STATUS_DEACTIVATED:
1018 0 : break;
1019 0 : default:
1020 0 : __builtin_unreachable();
1021 0 : }
1022 :
1023 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L456 */
1024 0 : fd_borrowed_account_drop( &lut_acct );
1025 :
1026 : /* Add lamports to recipient ****************************************/
1027 : /* try_borrow_instruction_account => get_index_of_instruction_account_in_transaction */
1028 :
1029 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L458-L459 */
1030 0 : fd_guarded_borrowed_account_t recipient_acct = {0};
1031 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_RECIPIENT, &recipient_acct );
1032 :
1033 0 : err = fd_borrowed_account_checked_add_lamports( &recipient_acct, withdrawn_lamports );
1034 0 : if( FD_UNLIKELY( err ) ) {
1035 0 : return err;
1036 0 : }
1037 :
1038 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L461 */
1039 0 : fd_borrowed_account_drop( &recipient_acct );
1040 :
1041 : /* Delete LUT account ***********************************************/
1042 :
1043 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/address-lookup-table/src/processor.rs#L463-L464 */
1044 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_LUT, &lut_acct );
1045 :
1046 0 : err = fd_borrowed_account_set_data_length( &lut_acct, 0UL );
1047 0 : if( FD_UNLIKELY( err ) ) {
1048 0 : return err;
1049 0 : }
1050 :
1051 0 : err = fd_borrowed_account_set_lamports( &lut_acct, 0UL );
1052 0 : if( FD_UNLIKELY( err ) ) {
1053 0 : return err;
1054 0 : }
1055 :
1056 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1057 :
1058 0 : # undef ACC_IDX_LUT
1059 0 : # undef ACC_IDX_AUTHORITY
1060 0 : # undef ACC_IDX_RECIPIENT
1061 0 : }
1062 :
1063 : int
1064 0 : fd_address_lookup_table_program_execute( fd_exec_instr_ctx_t * ctx ) {
1065 : /* Prevent execution of migrated native programs */
1066 0 : if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( ctx->txn_ctx->bank, migrate_address_lookup_table_program_to_core_bpf ) ) ) {
1067 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
1068 0 : }
1069 :
1070 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
1071 :
1072 0 : uchar const * instr_data = ctx->instr->data;
1073 0 : ulong instr_data_sz = ctx->instr->data_sz;
1074 0 : if( FD_UNLIKELY( instr_data==NULL ) ) {
1075 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1076 0 : }
1077 :
1078 0 : FD_SPAD_FRAME_BEGIN( ctx->txn_ctx->spad ) {
1079 : /* https://github.com/solana-labs/solana/blob/v1.17.4/programs/address-lookup-table/src/processor.rs#L28 */
1080 0 : ulong decoded_sz;
1081 0 : fd_addrlut_instruction_t * instr = fd_bincode_decode1_spad(
1082 0 : addrlut_instruction, ctx->txn_ctx->spad,
1083 0 : instr_data, instr_data_sz,
1084 0 : NULL,
1085 0 : &decoded_sz );
1086 0 : if( FD_UNLIKELY( !instr || decoded_sz > FD_TXN_MTU ) ) {
1087 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1088 0 : }
1089 :
1090 0 : switch( instr->discriminant ) {
1091 0 : case fd_addrlut_instruction_enum_create_lut:
1092 0 : return create_lookup_table( ctx, &instr->inner.create_lut );
1093 0 : case fd_addrlut_instruction_enum_freeze_lut:
1094 0 : return freeze_lookup_table( ctx );
1095 0 : case fd_addrlut_instruction_enum_extend_lut:
1096 0 : return extend_lookup_table( ctx, &instr->inner.extend_lut );
1097 0 : case fd_addrlut_instruction_enum_deactivate_lut:
1098 0 : return deactivate_lookup_table( ctx );
1099 0 : case fd_addrlut_instruction_enum_close_lut:
1100 0 : return close_lookup_table( ctx );
1101 0 : default:
1102 0 : break;
1103 0 : }
1104 0 : } FD_SPAD_FRAME_END;
1105 :
1106 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1107 0 : }
1108 :
1109 : /**********************************************************************/
1110 : /* Public API */
1111 : /**********************************************************************/
1112 :
1113 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L72-L78 */
1114 : static uchar
1115 : is_active( fd_address_lookup_table_t const * self,
1116 : ulong current_slot,
1117 12 : fd_slot_hash_t const * slot_hashes ) { /* deque */
1118 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L73-L77 */
1119 12 : ulong _dummy[1];
1120 12 : switch( fd_addrlut_status( &self->meta, current_slot, slot_hashes, _dummy ) ) {
1121 12 : case FD_ADDRLUT_STATUS_ACTIVATED:
1122 12 : case FD_ADDRLUT_STATUS_DEACTIVATING:
1123 12 : return 1;
1124 0 : case FD_ADDRLUT_STATUS_DEACTIVATED:
1125 0 : return 0;
1126 0 : default:
1127 0 : __builtin_unreachable();
1128 12 : }
1129 12 : }
1130 :
1131 : /* Sets active_addresses_len on success
1132 : https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L142-L164 */
1133 : int
1134 : fd_get_active_addresses_len( fd_address_lookup_table_t * self,
1135 : ulong current_slot,
1136 : fd_slot_hash_t const * slot_hashes, /* deque */
1137 : ulong addresses_len,
1138 12 : ulong * active_addresses_len ) {
1139 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L147-L152 */
1140 12 : if( FD_UNLIKELY( !is_active( self, current_slot, slot_hashes ) ) ) {
1141 0 : return FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND;
1142 0 : }
1143 :
1144 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L157-L161 */
1145 12 : *active_addresses_len = ( current_slot > self->meta.last_extended_slot )
1146 12 : ? addresses_len
1147 12 : : self->meta.last_extended_slot_start_index;
1148 :
1149 12 : return FD_RUNTIME_EXECUTE_SUCCESS;
1150 12 : }
|