Line data Source code
1 : #include "fd_sysvar_slot_history.h" 2 : #include "fd_sysvar.h" 3 : #include "fd_sysvar_rent.h" 4 : #include "../fd_system_ids.h" 5 : #include "../fd_txn_account.h" 6 : #include "../../../funk/fd_funk_txn.h" 7 : 8 : /* FIXME These constants should be header defines */ 9 : 10 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L37 */ 11 0 : #define FD_SLOT_HISTORY_MAX_ENTRIES (1024UL * 1024UL) 12 : 13 : /* TODO: move into separate bitvec library */ 14 0 : #define FD_SLOT_HISTORY_BITS_PER_BLOCK (8UL * sizeof(ulong)) 15 : 16 0 : #define FD_SLOT_HISTORY_BLOCKS_LEN (FD_SLOT_HISTORY_MAX_ENTRIES / FD_SLOT_HISTORY_BITS_PER_BLOCK) 17 : 18 : void 19 : fd_sysvar_slot_history_set( fd_slot_history_global_t * history, 20 0 : ulong i ) { 21 0 : if( FD_UNLIKELY( i > history->next_slot && i - history->next_slot >= FD_SLOT_HISTORY_MAX_ENTRIES ) ) { 22 0 : FD_LOG_WARNING(( "Ignoring out of bounds (i=%lu next_slot=%lu)", i, history->next_slot )); 23 0 : return; 24 0 : } 25 : 26 0 : ulong * blocks = (ulong *)((uchar*)history + history->bits_bitvec_offset); 27 0 : ulong blocks_len = history->bits_bitvec_len; 28 : 29 : // Skipped slots, delete them from history 30 0 : if( FD_UNLIKELY( blocks_len == 0 ) ) return; 31 0 : for( ulong j = history->next_slot; j < i; j++ ) { 32 0 : ulong block_idx = (j / FD_SLOT_HISTORY_BITS_PER_BLOCK) % (blocks_len); 33 0 : blocks[ block_idx ] &= ~( 1UL << ( j % FD_SLOT_HISTORY_BITS_PER_BLOCK ) ); 34 0 : } 35 0 : ulong block_idx = (i / FD_SLOT_HISTORY_BITS_PER_BLOCK) % (blocks_len); 36 0 : blocks[ block_idx ] |= ( 1UL << ( i % FD_SLOT_HISTORY_BITS_PER_BLOCK ) ); 37 0 : } 38 : 39 : 40 : void 41 : fd_sysvar_slot_history_write_history( fd_bank_t * bank, 42 : fd_accdb_user_t * accdb, 43 : fd_funk_txn_xid_t const * xid, 44 : fd_capture_ctx_t * capture_ctx, 45 0 : fd_slot_history_global_t * history ) { 46 0 : uchar __attribute__((aligned(FD_SYSVAR_SLOT_HISTORY_ALIGN))) slot_history_mem[ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ] = {0}; 47 0 : fd_bincode_encode_ctx_t ctx = { 48 0 : .data = slot_history_mem, 49 0 : .dataend = slot_history_mem + FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ 50 0 : }; 51 0 : int err = fd_slot_history_encode_global( history, &ctx ); 52 0 : if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "fd_slot_history_encode_global failed" )); 53 0 : fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_slot_history_id, slot_history_mem, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ); 54 0 : } 55 : 56 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L16 */ 57 : 58 : void 59 : fd_sysvar_slot_history_init( fd_bank_t * bank, 60 : fd_accdb_user_t * accdb, 61 : fd_funk_txn_xid_t const * xid, 62 0 : fd_capture_ctx_t * capture_ctx ) { 63 : /* Create a new slot history instance */ 64 : 65 : /* We need to construct the gaddr-aware slot history object */ 66 0 : uchar __attribute__((aligned(FD_SYSVAR_SLOT_HISTORY_ALIGN))) slot_history_mem[ FD_SYSVAR_SLOT_HISTORY_FOOTPRINT ] = {0}; 67 0 : fd_slot_history_global_t * history = (fd_slot_history_global_t *)slot_history_mem; 68 0 : ulong * blocks = (ulong *)fd_ulong_align_up( (ulong)((uchar*)history + sizeof(fd_slot_history_global_t)), alignof(ulong) ); 69 : 70 0 : history->next_slot = fd_bank_slot_get( bank ) + 1UL; 71 0 : history->bits_bitvec_offset = (ulong)((uchar*)blocks - (uchar*)history); 72 0 : history->bits_len = FD_SLOT_HISTORY_MAX_ENTRIES; 73 0 : history->bits_bitvec_len = FD_SLOT_HISTORY_BLOCKS_LEN; 74 0 : history->has_bits = 1; 75 : 76 : /* TODO: handle slot != 0 init case */ 77 0 : fd_sysvar_slot_history_set( history, fd_bank_slot_get( bank ) ); 78 0 : fd_sysvar_slot_history_write_history( bank, accdb, xid, capture_ctx, history ); 79 0 : } 80 : 81 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/bank.rs#L2345 */ 82 : int 83 : fd_sysvar_slot_history_update( fd_bank_t * bank, 84 : fd_accdb_user_t * accdb, 85 : fd_funk_txn_xid_t const * xid, 86 0 : fd_capture_ctx_t * capture_ctx ) { 87 : /* Set current_slot, and update next_slot */ 88 0 : fd_pubkey_t const * key = &fd_sysvar_slot_history_id; 89 : 90 0 : fd_txn_account_t rec[1]; 91 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, accdb->funk, xid ); 92 0 : if( FD_UNLIKELY( err ) ) FD_LOG_CRIT(( "fd_txn_account_init_from_funk_readonly(slot_history) failed: %d", err )); 93 : 94 0 : fd_bincode_decode_ctx_t ctx = { 95 0 : .data = fd_txn_account_get_data( rec ), 96 0 : .dataend = fd_txn_account_get_data( rec ) + fd_txn_account_get_data_len( rec ) 97 0 : }; 98 : 99 0 : uchar __attribute__((aligned(FD_SYSVAR_SLOT_HISTORY_ALIGN))) slot_history_mem[ FD_SYSVAR_SLOT_HISTORY_FOOTPRINT ] = {0}; 100 0 : fd_slot_history_global_t * history = fd_slot_history_decode_global( slot_history_mem, &ctx ); 101 : 102 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L48 */ 103 0 : fd_sysvar_slot_history_set( history, fd_bank_slot_get( bank ) ); 104 0 : history->next_slot = fd_bank_slot_get( bank ) + 1; 105 : 106 0 : fd_sysvar_slot_history_write_history( bank, accdb, xid, capture_ctx, history ); 107 : 108 0 : return 0; 109 0 : } 110 : 111 : fd_slot_history_global_t * 112 : fd_sysvar_slot_history_read( fd_funk_t * funk, 113 : fd_funk_txn_xid_t const * xid, 114 0 : uchar out_mem[ static FD_SYSVAR_SLOT_HISTORY_FOOTPRINT ] ) { 115 : /* Set current_slot, and update next_slot */ 116 : 117 0 : fd_pubkey_t const * key = &fd_sysvar_slot_history_id; 118 : 119 0 : fd_txn_account_t rec[1]; 120 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, funk, xid ); 121 0 : if( err ) { 122 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_readonly(slot_history) failed: %d", err )); 123 0 : } 124 : 125 : /* This check is needed as a quirk of the fuzzer. If a sysvar account 126 : exists in the accounts database, but doesn't have any lamports, 127 : this means that the account does not exist. This wouldn't happen 128 : in a real execution environment. */ 129 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( rec )==0UL ) ) { 130 0 : return NULL; 131 0 : } 132 : 133 0 : ulong data_len = fd_txn_account_get_data_len( rec ); 134 0 : if( FD_UNLIKELY( data_len>FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ) ) { 135 0 : FD_LOG_ERR(( "corrupt slot history sysvar: sysvar data is too large (%lu bytes)", data_len )); 136 0 : } 137 : 138 0 : fd_bincode_decode_ctx_t ctx = { 139 0 : .data = fd_txn_account_get_data( rec ), 140 0 : .dataend = fd_txn_account_get_data( rec ) + data_len 141 0 : }; 142 : 143 0 : return fd_slot_history_decode_global( out_mem, &ctx ); 144 0 : } 145 : 146 : int 147 : fd_sysvar_slot_history_find_slot( fd_slot_history_global_t const * history, 148 0 : ulong slot ) { 149 : 150 0 : ulong * blocks = (ulong *)((uchar*)history + history->bits_bitvec_offset); 151 0 : if( FD_UNLIKELY( !blocks ) ) { 152 0 : FD_LOG_ERR(( "Unable to find slot history blocks" )); 153 0 : } 154 0 : ulong blocks_len = history->bits_bitvec_len; 155 : 156 0 : if( slot > history->next_slot - 1UL ) { 157 0 : return FD_SLOT_HISTORY_SLOT_FUTURE; 158 0 : } else if ( slot < fd_ulong_sat_sub( history->next_slot, FD_SLOT_HISTORY_MAX_ENTRIES ) ) { 159 0 : return FD_SLOT_HISTORY_SLOT_TOO_OLD; 160 0 : } else { 161 0 : ulong block_idx = (slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % blocks_len; 162 0 : if( blocks[ block_idx ] & ( 1UL << ( slot % FD_SLOT_HISTORY_BITS_PER_BLOCK ) ) ) { 163 0 : return FD_SLOT_HISTORY_SLOT_FOUND; 164 0 : } else { 165 0 : return FD_SLOT_HISTORY_SLOT_NOT_FOUND; 166 0 : } 167 0 : } 168 0 : }