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