Line data Source code
1 : #include "fd_sysvar_slot_history.h"
2 : #include "fd_sysvar.h"
3 : #include "../fd_system_ids.h"
4 : #include "../fd_accdb_svm.h"
5 :
6 714 : #define FD_SLOT_HISTORY_BITS_PER_BLOCK (8UL * sizeof(ulong))
7 :
8 18 : #define FD_SLOT_HISTORY_BLOCKS_LEN (FD_SLOT_HISTORY_MAX_ENTRIES / FD_SLOT_HISTORY_BITS_PER_BLOCK)
9 :
10 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L16 */
11 :
12 : void
13 : fd_sysvar_slot_history_init( fd_bank_t * bank,
14 : fd_accdb_user_t * accdb,
15 : fd_funk_txn_xid_t const * xid,
16 9 : fd_capture_ctx_t * capture_ctx ) {
17 9 : uchar data[ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ];
18 9 : uchar * p = data;
19 :
20 : /* has_bits */
21 9 : *p = 1;
22 9 : p++;
23 :
24 : /* bits_bitvec_len */
25 9 : FD_STORE( ulong, p, FD_SLOT_HISTORY_BLOCKS_LEN );
26 9 : p += sizeof(ulong);
27 :
28 : /* content */
29 9 : fd_memset( p, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
30 9 : p += FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong);
31 :
32 : /* bits_len */
33 9 : FD_STORE( ulong, p, FD_SLOT_HISTORY_MAX_ENTRIES );
34 9 : p += sizeof(ulong);
35 :
36 : /* next_slot */
37 9 : FD_STORE( ulong, p, bank->f.slot + 1UL );
38 9 : p += sizeof(ulong);
39 :
40 9 : FD_STATIC_ASSERT( FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ == 1 + sizeof(ulong) + FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) + sizeof(ulong) + sizeof(ulong), "bin code size mismatch" );
41 :
42 9 : fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_slot_history_id, data, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ );
43 9 : }
44 :
45 : void
46 : fd_sysvar_slot_history_update( fd_bank_t * bank,
47 : fd_accdb_user_t * accdb,
48 : fd_funk_txn_xid_t const * xid,
49 96 : fd_capture_ctx_t * capture_ctx ) {
50 :
51 96 : ulong cur_slot = bank->f.slot;
52 :
53 96 : fd_accdb_rw_t rw[1];
54 96 : fd_accdb_svm_update_t update[1];
55 96 : if( FD_UNLIKELY( !fd_accdb_svm_open_rw( accdb, bank, xid, rw, update, &fd_sysvar_slot_history_id, 0UL, 0 ) ) ) {
56 0 : FD_LOG_ERR(( "state is missing slot history sysvar" ));
57 0 : }
58 96 : if( FD_UNLIKELY( 0!=memcmp( fd_accdb_ref_owner( rw->ro ), &fd_sysvar_owner_id, sizeof(fd_pubkey_t) ) ) ) {
59 0 : FD_LOG_ERR(( "slot history sysvar not owned by sysvar owner" ));
60 0 : }
61 96 : uchar * data = fd_accdb_ref_data ( rw );
62 96 : ulong data_sz = fd_accdb_ref_data_sz( rw->ro );
63 96 : if( FD_UNLIKELY( data[0]!=1 ) ) {
64 : /* initialize if !has_bits */
65 0 : if( FD_UNLIKELY( data_sz < FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ) ) {
66 0 : FD_LOG_HEXDUMP_ERR(( "invalid slot history sysvar (data_sz too small)", data, data_sz ));
67 0 : }
68 0 : data[0] = 1;
69 0 : FD_STORE( ulong, data+1, FD_SLOT_HISTORY_BLOCKS_LEN );
70 0 : fd_memset( data+9, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
71 0 : FD_STORE( ulong, data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), FD_SLOT_HISTORY_MAX_ENTRIES );
72 0 : FD_STORE( ulong, data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong)+8UL, 0UL );
73 0 : }
74 96 : ulong bits_bitvec_len = FD_LOAD( ulong, data+1 );
75 96 : if( FD_UNLIKELY( !bits_bitvec_len ) ) {
76 3 : fd_accdb_svm_close_rw( accdb, bank, capture_ctx, rw, update );
77 3 : return;
78 3 : }
79 93 : ulong min_sz;
80 93 : if( FD_UNLIKELY( __builtin_umull_overflow( bits_bitvec_len, sizeof(ulong), &min_sz ) ) ) {
81 0 : FD_LOG_ERR(( "invalid slot history sysvar: bits_bitvec_len overflow (%lu)", bits_bitvec_len ));
82 0 : }
83 93 : if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) {
84 0 : FD_LOG_ERR(( "invalid slot history sysvar: min_sz overflow" ));
85 0 : }
86 93 : if( FD_UNLIKELY( data_sz < min_sz ) ) {
87 0 : FD_LOG_ERR(( "invalid slot history sysvar: data_sz too small (%lu, required %lu)", data_sz, min_sz ));
88 0 : }
89 93 : uchar * bits = data + 9UL;
90 93 : uchar * footer = data + 9UL + bits_bitvec_len * sizeof(ulong);
91 93 : ulong next_slot = FD_LOAD( ulong, footer+8UL );
92 :
93 : /* https://github.com/anza-xyz/solana-sdk/blob/slot-history%40v2.2.1/slot-history/src/lib.rs#L62-L74 */
94 93 : if( FD_UNLIKELY( cur_slot > next_slot && cur_slot - next_slot >= FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
95 3 : fd_memset( bits, 0, bits_bitvec_len * sizeof(ulong) );
96 90 : } else {
97 603 : for( ulong i=next_slot; i<cur_slot; i++ ) {
98 513 : ulong block_idx = (i / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
99 513 : uchar * word = &bits[ block_idx*sizeof(ulong) ];
100 513 : FD_STORE( ulong, word, FD_LOAD( ulong, word ) & (~(1UL << (i % FD_SLOT_HISTORY_BITS_PER_BLOCK))) );
101 513 : }
102 90 : }
103 :
104 : /* new slot */
105 93 : ulong block_idx = (cur_slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
106 93 : uchar * word = &bits[ block_idx*sizeof(ulong) ];
107 93 : FD_STORE( ulong, word, FD_LOAD( ulong, word ) | (1UL << (cur_slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) );
108 :
109 93 : FD_STORE( ulong, footer+8UL, cur_slot+1UL );
110 :
111 93 : fd_accdb_svm_close_rw( accdb, bank, capture_ctx, rw, update );
112 93 : }
113 :
114 : int
115 : fd_sysvar_slot_history_validate( uchar const * data,
116 6993 : ulong sz ) {
117 6993 : if( FD_UNLIKELY( sz < 17UL ) ) return 0;
118 6984 : uchar has_bits = data[0];
119 6984 : if( FD_UNLIKELY( has_bits>1 ) ) return 0;
120 6981 : if( !has_bits ) return 1;
121 6981 : if( FD_UNLIKELY( sz < 25UL ) ) return 0;
122 6972 : ulong blocks_len = FD_LOAD( ulong, data+1 );
123 6972 : ulong min_sz;
124 6972 : if( FD_UNLIKELY( __builtin_umull_overflow( blocks_len, sizeof(ulong), &min_sz ) ) ) return 0;
125 6969 : if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) return 0;
126 6966 : if( FD_UNLIKELY( sz < min_sz ) ) return 0;
127 6963 : return 1;
128 6966 : }
129 :
130 : fd_slot_history_view_t *
131 : fd_sysvar_slot_history_view( fd_slot_history_view_t * view,
132 : uchar const * data,
133 33 : ulong sz ) {
134 33 : if( FD_UNLIKELY( !fd_sysvar_slot_history_validate( data, sz ) ) ) return NULL;
135 27 : if( FD_UNLIKELY( !data[0] ) ) {
136 0 : view->bits = NULL;
137 0 : view->blocks_len = 0UL;
138 0 : view->bits_len = FD_LOAD( ulong, data+1 );
139 0 : view->next_slot = FD_LOAD( ulong, data+1+8UL );
140 0 : return view;
141 0 : }
142 27 : ulong blocks_len = FD_LOAD( ulong, data+1 );
143 27 : uchar const * footer = data + 9UL + blocks_len * sizeof(ulong);
144 27 : view->bits = data + 9UL;
145 27 : view->blocks_len = blocks_len;
146 27 : view->bits_len = FD_LOAD( ulong, footer );
147 27 : view->next_slot = FD_LOAD( ulong, footer+8UL );
148 27 : return view;
149 27 : }
150 :
151 : int
152 : fd_sysvar_slot_history_find_slot( fd_slot_history_view_t const * view,
153 81 : ulong slot ) {
154 81 : if( FD_UNLIKELY( !view->blocks_len ) ) return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
155 78 : if( slot > view->next_slot - 1UL ) {
156 21 : return FD_SLOT_HISTORY_SLOT_FUTURE;
157 21 : }
158 57 : if( slot < fd_ulong_sat_sub( view->next_slot, FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
159 12 : return FD_SLOT_HISTORY_SLOT_TOO_OLD;
160 12 : }
161 45 : ulong block_idx = (slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % view->blocks_len;
162 45 : ulong word = FD_LOAD( ulong, view->bits + block_idx*sizeof(ulong) );
163 45 : if( word & (1UL << (slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) ) {
164 21 : return FD_SLOT_HISTORY_SLOT_FOUND;
165 21 : }
166 24 : return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
167 45 : }
|