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