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