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