LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_alut_interp.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 52 74 70.3 %
Date: 2025-10-27 04:40:00 Functions: 3 8 37.5 %

          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 */

Generated by: LCOV version 1.14