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