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 182866272 : #define FD_SLOT_HISTORY_BITS_PER_BLOCK (8UL * sizeof(ulong))
7 :
8 21 : #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_t * accdb,
15 9 : fd_capture_ctx_t * capture_ctx ) {
16 9 : uchar data[ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ];
17 9 : uchar * p = data;
18 :
19 : /* has_bits */
20 9 : *p = 1;
21 9 : p++;
22 :
23 : /* bits_bitvec_len */
24 9 : FD_STORE( ulong, p, FD_SLOT_HISTORY_BLOCKS_LEN );
25 9 : p += sizeof(ulong);
26 :
27 : /* content */
28 9 : fd_memset( p, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
29 9 : p += FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong);
30 :
31 : /* bits_len */
32 9 : FD_STORE( ulong, p, FD_SLOT_HISTORY_MAX_ENTRIES );
33 9 : p += sizeof(ulong);
34 :
35 : /* next_slot */
36 9 : FD_STORE( ulong, p, bank->f.slot + 1UL );
37 9 : p += sizeof(ulong);
38 :
39 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" );
40 :
41 9 : fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_slot_history_id, data, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ );
42 9 : }
43 :
44 : void
45 : fd_sysvar_slot_history_update( fd_bank_t * bank,
46 : fd_accdb_t * accdb,
47 432 : fd_capture_ctx_t * capture_ctx ) {
48 432 : fd_accdb_svm_update_t update[1];
49 432 : fd_acc_t acc = fd_accdb_svm_open_rw( bank, accdb, update, &fd_sysvar_slot_history_id, 1 );
50 432 : if( FD_UNLIKELY( !acc.lamports ) ) {
51 : /* SlotHistory::default() contains slot zero, with next_slot=1. */
52 9 : acc.data_len = FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ;
53 9 : acc.executable = 0;
54 9 : fd_memcpy( acc.owner, fd_sysvar_owner_id.uc, sizeof(fd_pubkey_t) );
55 9 : fd_memset( acc.data, 0, acc.data_len );
56 9 : acc.data[0] = 1;
57 9 : FD_STORE( ulong, acc.data+1UL, FD_SLOT_HISTORY_BLOCKS_LEN );
58 9 : FD_STORE( ulong, acc.data+9UL, 1UL );
59 9 : FD_STORE( ulong, acc.data+9UL+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), FD_SLOT_HISTORY_MAX_ENTRIES );
60 9 : FD_STORE( ulong, acc.data+17UL+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), 1UL );
61 9 : }
62 432 : FD_TEST( !memcmp( acc.owner, fd_sysvar_owner_id.uc, sizeof(fd_pubkey_t) ) ); /* Slot history account must be owned by sysvar owner */
63 432 : if( FD_UNLIKELY( !fd_sysvar_slot_history_validate( acc.data, acc.data_len ) ) ) {
64 0 : FD_LOG_HEXDUMP_ERR(( "invalid slot history sysvar", acc.data, acc.data_len ));
65 0 : }
66 :
67 432 : if( FD_UNLIKELY( acc.data[ 0 ]!=1 ) ) {
68 : /* initialize if !has_bits */
69 3 : if( FD_UNLIKELY( acc.data_len<FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ) ) FD_LOG_HEXDUMP_ERR(( "invalid slot history sysvar (data_sz too small)", acc.data, acc.data_len ));
70 3 : acc.data[ 0 ] = 1;
71 3 : FD_STORE( ulong, acc.data+1, FD_SLOT_HISTORY_BLOCKS_LEN );
72 3 : fd_memset( acc.data+9, 0, FD_SLOT_HISTORY_BLOCKS_LEN * sizeof(ulong) );
73 3 : FD_STORE( ulong, acc.data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong), FD_SLOT_HISTORY_MAX_ENTRIES );
74 3 : FD_STORE( ulong, acc.data+9+FD_SLOT_HISTORY_BLOCKS_LEN*sizeof(ulong)+8UL, 0UL );
75 3 : }
76 :
77 432 : ulong bits_bitvec_len = FD_LOAD( ulong, acc.data+1UL );
78 432 : if( FD_UNLIKELY( !bits_bitvec_len ) ) {
79 3 : fd_sysvar_adjust_balance_for_rent( bank, &acc );
80 3 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &acc, update );
81 3 : return;
82 3 : }
83 429 : ulong min_sz;
84 429 : if( FD_UNLIKELY( __builtin_umull_overflow( bits_bitvec_len, sizeof(ulong), &min_sz ) ) ) {
85 0 : FD_LOG_ERR(( "invalid slot history sysvar: bits_bitvec_len overflow (%lu)", bits_bitvec_len ));
86 0 : }
87 429 : if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) {
88 0 : FD_LOG_ERR(( "invalid slot history sysvar: min_sz overflow" ));
89 0 : }
90 429 : if( FD_UNLIKELY( acc.data_len < min_sz ) ) {
91 0 : FD_LOG_ERR(( "invalid slot history sysvar: data_sz too small (%lu, required %lu)", acc.data_len, min_sz ));
92 0 : }
93 429 : uchar * bits = acc.data + 9UL;
94 429 : uchar * footer = acc.data + 9UL + bits_bitvec_len * sizeof(ulong);
95 429 : ulong next_slot = FD_LOAD( ulong, footer+8UL );
96 :
97 : /* https://github.com/anza-xyz/solana-sdk/blob/slot-history%40v2.2.1/slot-history/src/lib.rs#L62-L74 */
98 429 : ulong cur_slot = bank->f.slot;
99 429 : if( FD_UNLIKELY( cur_slot>next_slot && cur_slot-next_slot>=FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
100 6 : fd_memset( bits, 0, bits_bitvec_len * sizeof(ulong) );
101 423 : } else {
102 5937 : for( ulong i=next_slot; i<cur_slot; i++ ) {
103 5514 : ulong block_idx = (i / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
104 5514 : uchar * word = &bits[ block_idx*sizeof(ulong) ];
105 5514 : FD_STORE( ulong, word, FD_LOAD( ulong, word ) & (~(1UL << (i % FD_SLOT_HISTORY_BITS_PER_BLOCK))) );
106 5514 : }
107 423 : }
108 :
109 : /* new slot */
110 429 : ulong block_idx = (cur_slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % bits_bitvec_len;
111 429 : uchar * word = &bits[ block_idx*sizeof(ulong) ];
112 429 : FD_STORE( ulong, word, FD_LOAD( ulong, word ) | (1UL << (cur_slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) );
113 :
114 429 : FD_STORE( ulong, footer+8UL, cur_slot+1UL );
115 :
116 429 : fd_sysvar_adjust_balance_for_rent( bank, &acc );
117 429 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &acc, update );
118 429 : }
119 :
120 : int
121 : fd_sysvar_slot_history_validate( uchar const * data,
122 92228 : ulong sz ) {
123 92228 : if( FD_UNLIKELY( sz < 17UL ) ) return 0;
124 92168 : uchar has_bits = data[0];
125 92168 : if( FD_UNLIKELY( has_bits>1 ) ) return 0;
126 92165 : if( !has_bits ) return 1;
127 92162 : if( FD_UNLIKELY( sz < 25UL ) ) return 0;
128 92132 : ulong blocks_len = FD_LOAD( ulong, data+1 );
129 92132 : ulong min_sz;
130 92132 : if( FD_UNLIKELY( __builtin_umull_overflow( blocks_len, sizeof(ulong), &min_sz ) ) ) return 0;
131 92129 : if( FD_UNLIKELY( __builtin_uaddl_overflow( min_sz, 25UL, &min_sz ) ) ) return 0;
132 92126 : if( FD_UNLIKELY( sz < min_sz ) ) return 0;
133 92123 : return 1;
134 92126 : }
135 :
136 : fd_slot_history_view_t *
137 : fd_sysvar_slot_history_view( fd_slot_history_view_t * view,
138 : uchar const * data,
139 91691 : ulong sz ) {
140 91691 : if( FD_UNLIKELY( !fd_sysvar_slot_history_validate( data, sz ) ) ) return NULL;
141 91685 : if( FD_UNLIKELY( !data[0] ) ) {
142 0 : view->bits = NULL;
143 0 : view->blocks_len = 0UL;
144 0 : view->bits_len = FD_LOAD( ulong, data+1 );
145 0 : view->next_slot = FD_LOAD( ulong, data+1+8UL );
146 0 : return view;
147 0 : }
148 91685 : ulong blocks_len = FD_LOAD( ulong, data+1 );
149 91685 : uchar const * footer = data + 9UL + blocks_len * sizeof(ulong);
150 91685 : view->bits = data + 9UL;
151 91685 : view->blocks_len = blocks_len;
152 91685 : view->bits_len = FD_LOAD( ulong, footer );
153 91685 : view->next_slot = FD_LOAD( ulong, footer+8UL );
154 91685 : return view;
155 91685 : }
156 :
157 : int
158 : fd_sysvar_slot_history_find_slot( fd_slot_history_view_t const * view,
159 91430202 : ulong slot ) {
160 91430202 : if( FD_UNLIKELY( !view->blocks_len ) ) return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
161 91430199 : if( slot > view->next_slot - 1UL ) {
162 30 : return FD_SLOT_HISTORY_SLOT_FUTURE;
163 30 : }
164 91430169 : if( slot < fd_ulong_sat_sub( view->next_slot, FD_SLOT_HISTORY_MAX_ENTRIES ) ) {
165 15 : return FD_SLOT_HISTORY_SLOT_TOO_OLD;
166 15 : }
167 91430154 : ulong block_idx = (slot / FD_SLOT_HISTORY_BITS_PER_BLOCK) % view->blocks_len;
168 91430154 : ulong word = FD_LOAD( ulong, view->bits + block_idx*sizeof(ulong) );
169 91430154 : if( word & (1UL << (slot % FD_SLOT_HISTORY_BITS_PER_BLOCK)) ) {
170 16326 : return FD_SLOT_HISTORY_SLOT_FOUND;
171 16326 : }
172 91413828 : return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
173 91430154 : }
|