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 : static const ulong slot_history_min_account_size = 131097; 11 : 12 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L37 */ 13 : static const ulong slot_history_max_entries = 1024 * 1024; 14 : 15 : /* TODO: move into separate bitvec library */ 16 : static const ulong bits_per_block = 8 * sizeof(ulong); 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 >= 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 / bits_per_block) % (blocks_len); 33 0 : blocks[ block_idx ] &= ~( 1UL << ( j % bits_per_block ) ); 34 0 : } 35 0 : ulong block_idx = (i / bits_per_block) % (blocks_len); 36 0 : blocks[ block_idx ] |= ( 1UL << ( i % bits_per_block ) ); 37 0 : } 38 : 39 : FD_FN_UNUSED static const ulong blocks_len = slot_history_max_entries / bits_per_block; 40 : 41 : void 42 : fd_sysvar_slot_history_write_history( fd_exec_slot_ctx_t * slot_ctx, 43 0 : fd_slot_history_global_t * history ) { 44 0 : ulong sz = slot_history_min_account_size; 45 0 : uchar enc[ sz ]; 46 0 : fd_memset( enc, 0, sz ); 47 0 : fd_bincode_encode_ctx_t ctx; 48 0 : ctx.data = enc; 49 0 : ctx.dataend = enc + sz; 50 0 : int err = fd_slot_history_encode_global( history, &ctx ); 51 0 : if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "fd_slot_history_encode_global failed" )); 52 0 : fd_sysvar_account_update( slot_ctx, &fd_sysvar_slot_history_id, enc, sz ); 53 0 : } 54 : 55 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L16 */ 56 : 57 : void 58 0 : fd_sysvar_slot_history_init( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) { 59 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) { 60 : /* Create a new slot history instance */ 61 : 62 : /* We need to construct the gaddr-aware slot history object */ 63 0 : ulong total_sz = sizeof(fd_slot_history_global_t) + alignof(fd_slot_history_global_t) + 64 0 : (sizeof(ulong) + alignof(ulong)) * blocks_len; 65 : 66 0 : uchar * mem = fd_spad_alloc( runtime_spad, alignof(fd_slot_history_global_t), total_sz ); 67 0 : fd_slot_history_global_t * history = (fd_slot_history_global_t *)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( slot_ctx->bank ) + 1UL; 71 0 : history->bits_bitvec_offset = (ulong)((uchar*)blocks - (uchar*)history); 72 0 : history->bits_len = slot_history_max_entries; 73 0 : history->bits_bitvec_len = blocks_len; 74 0 : history->has_bits = 1; 75 0 : memset( blocks, 0, sizeof(ulong) * blocks_len ); 76 : 77 : /* TODO: handle slot != 0 init case */ 78 0 : fd_sysvar_slot_history_set( history, fd_bank_slot_get( slot_ctx->bank ) ); 79 0 : fd_sysvar_slot_history_write_history( slot_ctx, history ); 80 0 : } FD_SPAD_FRAME_END; 81 0 : } 82 : 83 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/bank.rs#L2345 */ 84 : int 85 0 : fd_sysvar_slot_history_update( fd_exec_slot_ctx_t * slot_ctx ) { 86 : /* Set current_slot, and update next_slot */ 87 0 : fd_pubkey_t const * key = &fd_sysvar_slot_history_id; 88 : 89 0 : FD_TXN_ACCOUNT_DECL( rec ); 90 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, slot_ctx->funk, slot_ctx->funk_txn ); 91 0 : if (err) 92 0 : 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 : ulong total_sz = 0UL; 100 0 : err = fd_slot_history_decode_footprint( &ctx, &total_sz ); 101 0 : if( FD_UNLIKELY( err ) ) { 102 0 : FD_LOG_CRIT(( "fd_slot_history_decode_footprint failed %d", err )); 103 0 : } 104 : 105 0 : uchar mem[ total_sz ]; 106 0 : fd_slot_history_global_t * history = fd_slot_history_decode_global( mem, &ctx ); 107 : 108 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L48 */ 109 0 : fd_sysvar_slot_history_set( history, fd_bank_slot_get( slot_ctx->bank ) ); 110 0 : history->next_slot = fd_bank_slot_get( slot_ctx->bank ) + 1; 111 : 112 0 : fd_sysvar_slot_history_write_history( slot_ctx, history ); 113 : 114 0 : return 0; 115 0 : } 116 : 117 : fd_slot_history_global_t * 118 : fd_sysvar_slot_history_read( fd_funk_t * funk, 119 : fd_funk_txn_t * funk_txn, 120 0 : fd_spad_t * spad ) { 121 : 122 : /* Set current_slot, and update next_slot */ 123 : 124 0 : fd_pubkey_t const * key = &fd_sysvar_slot_history_id; 125 : 126 0 : FD_TXN_ACCOUNT_DECL( rec ); 127 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, funk, funk_txn ); 128 0 : if( err ) { 129 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_readonly(slot_history) failed: %d", err )); 130 0 : } 131 : 132 : /* This check is needed as a quirk of the fuzzer. If a sysvar account 133 : exists in the accounts database, but doesn't have any lamports, 134 : this means that the account does not exist. This wouldn't happen 135 : in a real execution environment. */ 136 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( rec )==0UL ) ) { 137 0 : return NULL; 138 0 : } 139 : 140 0 : fd_bincode_decode_ctx_t ctx = { 141 0 : .data = fd_txn_account_get_data( rec ), 142 0 : .dataend = fd_txn_account_get_data( rec ) + fd_txn_account_get_data_len( rec ) 143 0 : }; 144 : 145 0 : ulong total_sz = 0UL; 146 0 : err = fd_slot_history_decode_footprint( &ctx, &total_sz ); 147 0 : if( err ) { 148 0 : FD_LOG_ERR(( "fd_slot_history_decode_footprint failed" )); 149 0 : } 150 : 151 0 : uchar * mem = fd_spad_alloc( spad, fd_slot_history_align(), total_sz ); 152 0 : if( !mem ) { 153 0 : FD_LOG_ERR(( "Unable to allocate memory for slot history" )); 154 0 : } 155 : 156 0 : return fd_slot_history_decode_global( mem, &ctx ); 157 0 : } 158 : 159 : int 160 : fd_sysvar_slot_history_find_slot( fd_slot_history_global_t const * history, 161 : ulong slot, 162 0 : fd_wksp_t * wksp ) { 163 0 : (void)wksp; 164 0 : ulong * blocks = (ulong *)((uchar*)history + history->bits_bitvec_offset); 165 0 : if( FD_UNLIKELY( !blocks ) ) { 166 0 : FD_LOG_ERR(( "Unable to find slot history blocks" )); 167 0 : } 168 0 : ulong blocks_len = history->bits_bitvec_len; 169 : 170 : 171 0 : if( slot > history->next_slot - 1UL ) { 172 0 : return FD_SLOT_HISTORY_SLOT_FUTURE; 173 0 : } else if ( slot < fd_ulong_sat_sub( history->next_slot, slot_history_max_entries ) ) { 174 0 : return FD_SLOT_HISTORY_SLOT_TOO_OLD; 175 0 : } else { 176 0 : ulong block_idx = (slot / bits_per_block) % blocks_len; 177 0 : if( blocks[ block_idx ] & ( 1UL << ( slot % bits_per_block ) ) ) { 178 0 : return FD_SLOT_HISTORY_SLOT_FOUND; 179 0 : } else { 180 0 : return FD_SLOT_HISTORY_SLOT_NOT_FOUND; 181 0 : } 182 0 : } 183 0 : }