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