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