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