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