Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_alut_h 2 : #define HEADER_fd_src_flamenco_runtime_fd_alut_h 3 : 4 : /* fd_alut_interp.h provides APIs for interpreting Solana address lookup 5 : table usages. 6 : 7 : https://solana.com/de/developers/guides/advanced/lookup-tables */ 8 : 9 : #include "../../ballet/txn/fd_txn.h" 10 : #include "../../ballet/base58/fd_base58.h" 11 : #include "fd_runtime_err.h" 12 : #include "fd_system_ids.h" 13 : #include "program/fd_address_lookup_table_program.h" 14 : 15 : /* fd_alut_interp_t interprets indirect account references of a txn. */ 16 : 17 : struct fd_alut_interp { 18 : fd_acct_addr_t * out_accts_alt; 19 : 20 : fd_txn_t const * txn; 21 : uchar const * txn_payload; 22 : fd_slot_hash_t const * hashes; /* deque */ 23 : ulong slot; 24 : 25 : ulong alut_idx; 26 : ulong ro_indir_cnt; 27 : ulong rw_indir_cnt; 28 : }; 29 : 30 : typedef struct fd_alut_interp fd_alut_interp_t; 31 : 32 : FD_PROTOTYPES_BEGIN 33 : 34 : /* fd_alut_interp_new creates a new ALUT interpreter. 35 : Will write indirectly referenced addresses to out_addrs. 36 : txn_payload points to a valid serialized transaction, txn points to 37 : the associated transaction descriptor. alut_interp retains a write 38 : interest in out_addrs, and a read interest in txn, txn_payload, and 39 : hashes until it is destroyed. */ 40 : 41 : FD_FN_UNUSED static fd_alut_interp_t * 42 : fd_alut_interp_new( fd_alut_interp_t * interp, 43 : fd_acct_addr_t * out_addrs, 44 : fd_txn_t const * txn, 45 : uchar const * txn_payload, 46 : fd_slot_hash_t const * hashes, /* deque */ 47 12 : ulong slot ) { 48 12 : *interp = (fd_alut_interp_t){ 49 12 : .out_accts_alt = out_addrs, 50 12 : .txn = txn, 51 12 : .txn_payload = txn_payload, 52 12 : .hashes = hashes, 53 12 : .slot = slot, 54 12 : .alut_idx = 0UL, 55 12 : .ro_indir_cnt = 0UL, 56 12 : .rw_indir_cnt = 0UL 57 12 : }; 58 12 : return interp; 59 12 : } 60 : 61 : /* fd_alut_interp_delete destroys an ALUT interpreter object. Releases 62 : references to out_addrs, txn, and txn_payload. */ 63 : 64 : FD_FN_UNUSED static void * 65 0 : fd_alut_interp_delete( fd_alut_interp_t * interp ) { 66 0 : return interp; 67 0 : } 68 : 69 : static inline int 70 12 : fd_alut_interp_done( fd_alut_interp_t const * interp ) { 71 12 : return interp->alut_idx >= interp->txn->addr_table_lookup_cnt; 72 12 : } 73 : 74 : /* fd_alut_interp_next resolves a subset of a txn's indirect account 75 : references. Resolves all addresses that are specified in the ALUT 76 : at index alut_idx. Returns one of: 77 : - FD_RUNTIME_EXECUTE_SUCCESS 78 : - FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_OWNER 79 : - FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA 80 : - FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX 81 : - FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND */ 82 : 83 : FD_FN_UNUSED static int 84 : fd_alut_interp_next( fd_alut_interp_t * interp, 85 : void const * alut_addr, 86 : void const * alut_owner, 87 : uchar const * alut_data, 88 12 : ulong alut_data_sz ) { 89 12 : if( FD_UNLIKELY( fd_alut_interp_done( interp ) ) ) FD_LOG_CRIT(( "invariant violation" )); 90 12 : fd_acct_addr_t alut_addr_expected = 91 12 : FD_LOAD( fd_acct_addr_t, interp->txn_payload+fd_txn_get_address_tables_const( interp->txn )[ interp->alut_idx ].addr_off ); 92 12 : if( FD_UNLIKELY( !fd_memeq( alut_addr, &alut_addr_expected, sizeof(fd_acct_addr_t) ) ) ) { 93 0 : FD_BASE58_ENCODE_32_BYTES( alut_addr, alut_addr_b58 ); 94 0 : FD_BASE58_ENCODE_32_BYTES( alut_addr_expected.b, alut_addr_expected_b58 ); 95 0 : FD_LOG_CRIT(( "expected address lookup table account %s but got %s", 96 0 : alut_addr_expected_b58, alut_addr_b58 )); 97 0 : } 98 12 : fd_txn_acct_addr_lut_t const * addr_lut = 99 12 : &fd_txn_get_address_tables_const( interp->txn )[ interp->alut_idx ]; 100 : 101 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L96-L114 */ 102 12 : if( FD_UNLIKELY( !fd_memeq( alut_owner, fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) ) { 103 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_OWNER; 104 0 : } 105 : 106 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L205-L209 */ 107 12 : if( FD_UNLIKELY( alut_data_sz < FD_LOOKUP_TABLE_META_SIZE ) ) { 108 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA; 109 0 : } 110 : 111 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/accounts-db/src/accounts.rs#L141-L142 */ 112 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L197-L214 */ 113 12 : fd_address_lookup_table_state_t table[1]; 114 12 : if( FD_UNLIKELY( !fd_bincode_decode_static( address_lookup_table_state, table, alut_data, FD_LOOKUP_TABLE_META_SIZE, NULL ) ) ) { 115 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA; 116 0 : } 117 : 118 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L200-L203 */ 119 12 : if( FD_UNLIKELY( table->discriminant != fd_address_lookup_table_state_enum_lookup_table ) ) { 120 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA; 121 0 : } 122 : 123 : /* Again probably an impossible case, but the ALUT data needs to be 32-byte aligned 124 : https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L210-L214 */ 125 12 : if( FD_UNLIKELY( (alut_data_sz - FD_LOOKUP_TABLE_META_SIZE) & 0x1fUL ) ) { 126 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA; 127 0 : } 128 : 129 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L101-L112 */ 130 12 : fd_acct_addr_t const * lookup_addrs = fd_type_pun_const( alut_data+FD_LOOKUP_TABLE_META_SIZE ); 131 12 : ulong lookup_addrs_cnt = (alut_data_sz - FD_LOOKUP_TABLE_META_SIZE) >> 5UL; // = (dlen - 56) / 32 132 : 133 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L175-L176 */ 134 12 : ulong active_addresses_len; 135 12 : int err = fd_get_active_addresses_len( &table->inner.lookup_table, 136 12 : interp->slot, 137 12 : interp->hashes, 138 12 : lookup_addrs_cnt, 139 12 : &active_addresses_len ); 140 12 : if( FD_UNLIKELY( err ) ) return err; 141 : 142 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L169-L182 */ 143 12 : uchar const * writable_lut_idxs = interp->txn_payload + addr_lut->writable_off; 144 24 : for( ulong j=0UL; j<addr_lut->writable_cnt; j++ ) { 145 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */ 146 12 : if( writable_lut_idxs[j] >= active_addresses_len ) { 147 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX; 148 0 : } 149 12 : interp->out_accts_alt[ interp->rw_indir_cnt++ ] = lookup_addrs[ writable_lut_idxs[ j ] ]; 150 12 : } 151 : 152 12 : uchar const * readonly_lut_idxs = interp->txn_payload + addr_lut->readonly_off; 153 12 : fd_acct_addr_t * out_accts_ro = interp->out_accts_alt + interp->txn->addr_table_adtl_writable_cnt; 154 24 : for( ulong j=0UL; j<addr_lut->readonly_cnt; j++ ) { 155 : /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */ 156 12 : if( readonly_lut_idxs[j] >= active_addresses_len ) { 157 0 : return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX; 158 0 : } 159 12 : out_accts_ro[ interp->ro_indir_cnt++ ] = lookup_addrs[ readonly_lut_idxs[ j ] ]; 160 12 : } 161 : 162 12 : interp->alut_idx++; 163 12 : return FD_RUNTIME_EXECUTE_SUCCESS; 164 12 : } 165 : 166 : FD_PROTOTYPES_END 167 : 168 : 169 : #endif /* HEADER_fd_src_flamenco_runtime_fd_alut_h */