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