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_executor_err.h"
5 : #include "../fd_system_ids.h"
6 :
7 : /* FIXME These constants should be header defines */
8 :
9 : static const ulong slot_history_min_account_size = 131097;
10 :
11 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L37 */
12 : static const ulong slot_history_max_entries = 1024 * 1024;
13 :
14 : /* TODO: move into separate bitvec library */
15 : static const ulong bits_per_block = 8 * sizeof(ulong);
16 :
17 : void
18 : fd_sysvar_slot_history_set( fd_slot_history_global_t * history,
19 0 : ulong i ) {
20 0 : if( FD_UNLIKELY( i > history->next_slot && i - history->next_slot >= slot_history_max_entries ) ) {
21 0 : FD_LOG_WARNING(( "Ignoring out of bounds (i=%lu next_slot=%lu)", i, history->next_slot ));
22 0 : return;
23 0 : }
24 :
25 0 : ulong * blocks = (ulong *)((uchar*)history + history->bits_bitvec_offset);
26 0 : ulong blocks_len = history->bits_bitvec_len;
27 :
28 : // Skipped slots, delete them from history
29 0 : if( FD_UNLIKELY( blocks_len == 0 ) ) return;
30 0 : for( ulong j = history->next_slot; j < i; j++ ) {
31 0 : ulong block_idx = (j / bits_per_block) % (blocks_len);
32 0 : blocks[ block_idx ] &= ~( 1UL << ( j % bits_per_block ) );
33 0 : }
34 0 : ulong block_idx = (i / bits_per_block) % (blocks_len);
35 0 : blocks[ block_idx ] |= ( 1UL << ( i % bits_per_block ) );
36 0 : }
37 :
38 : FD_FN_UNUSED static const ulong blocks_len = slot_history_max_entries / bits_per_block;
39 :
40 : int
41 : fd_sysvar_slot_history_write_history( fd_exec_slot_ctx_t * slot_ctx,
42 0 : fd_slot_history_global_t * history ) {
43 0 : ulong sz = slot_history_min_account_size;
44 0 : uchar enc[ sz ];
45 0 : fd_memset( enc, 0, sz );
46 0 : fd_bincode_encode_ctx_t ctx;
47 0 : ctx.data = enc;
48 0 : ctx.dataend = enc + sz;
49 0 : int err = fd_slot_history_encode_global( history, &ctx );
50 0 : if (0 != err)
51 0 : return err;
52 0 : return fd_sysvar_set( slot_ctx->bank, slot_ctx->funk, slot_ctx->funk_txn, &fd_sysvar_owner_id, &fd_sysvar_slot_history_id, enc, sz, slot_ctx->slot );
53 0 : }
54 :
55 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L16 */
56 :
57 : void
58 0 : fd_sysvar_slot_history_init( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) {
59 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
60 : /* Create a new slot history instance */
61 :
62 : /* We need to construct the gaddr-aware slot history object */
63 0 : ulong total_sz = sizeof(fd_slot_history_global_t) + alignof(fd_slot_history_global_t) +
64 0 : (sizeof(ulong) + alignof(ulong)) * blocks_len;
65 :
66 0 : uchar * mem = fd_spad_alloc( runtime_spad, alignof(fd_slot_history_global_t), total_sz );
67 0 : fd_slot_history_global_t * history = (fd_slot_history_global_t *)mem;
68 0 : ulong * blocks = (ulong *)fd_ulong_align_up( (ulong)((uchar*)history + sizeof(fd_slot_history_global_t)), alignof(ulong) );
69 :
70 0 : history->next_slot = slot_ctx->slot + 1UL;
71 0 : history->bits_bitvec_offset = (ulong)((uchar*)blocks - (uchar*)history);
72 0 : history->bits_len = slot_history_max_entries;
73 0 : history->bits_bitvec_len = blocks_len;
74 0 : history->has_bits = 1;
75 0 : memset( blocks, 0, sizeof(ulong) * blocks_len );
76 :
77 : /* TODO: handle slot != 0 init case */
78 0 : fd_sysvar_slot_history_set( history, slot_ctx->slot );
79 0 : fd_sysvar_slot_history_write_history( slot_ctx, history );
80 0 : } FD_SPAD_FRAME_END;
81 0 : }
82 :
83 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/bank.rs#L2345 */
84 : int
85 0 : fd_sysvar_slot_history_update( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) {
86 : /* Set current_slot, and update next_slot */
87 :
88 0 : fd_pubkey_t const * key = &fd_sysvar_slot_history_id;
89 :
90 0 : FD_TXN_ACCOUNT_DECL( rec );
91 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, slot_ctx->funk, slot_ctx->funk_txn );
92 0 : if (err)
93 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_readonly(slot_history) failed: %d", err ));
94 :
95 0 : fd_bincode_decode_ctx_t ctx = {
96 0 : .data = rec->vt->get_data( rec ),
97 0 : .dataend = rec->vt->get_data( rec ) + rec->vt->get_data_len( rec )
98 0 : };
99 :
100 0 : ulong total_sz = 0UL;
101 0 : err = fd_slot_history_decode_footprint( &ctx, &total_sz );
102 0 : if( FD_UNLIKELY( err ) ) {
103 0 : FD_LOG_CRIT(( "fd_slot_history_decode_footprint failed %d", err ));
104 0 : }
105 :
106 0 : uchar * mem = fd_spad_alloc( runtime_spad, fd_slot_history_align(), total_sz );
107 0 : if( FD_UNLIKELY( !mem ) ) {
108 0 : FD_LOG_CRIT(( "Unable to allocate memory for slot history" ));
109 0 : }
110 :
111 0 : fd_slot_history_global_t * history = fd_slot_history_decode_global( mem, &ctx );
112 :
113 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_history.rs#L48 */
114 0 : fd_sysvar_slot_history_set( history, slot_ctx->slot );
115 0 : history->next_slot = slot_ctx->slot + 1;
116 :
117 0 : ulong sz = slot_history_min_account_size;
118 :
119 0 : err = fd_txn_account_init_from_funk_mutable( rec, key, slot_ctx->funk, slot_ctx->funk_txn, 1, sz );
120 0 : if (err)
121 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_mutable(slot_history) failed: %d", err ));
122 :
123 0 : fd_bincode_encode_ctx_t e_ctx = {
124 0 : .data = rec->vt->get_data_mut( rec ),
125 0 : .dataend = rec->vt->get_data_mut( rec )+sz,
126 0 : };
127 :
128 0 : if( FD_UNLIKELY( fd_slot_history_encode_global( history, &e_ctx ) ) ) {
129 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
130 0 : }
131 :
132 0 : fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
133 0 : rec->vt->set_lamports( rec, fd_rent_exempt_minimum_balance( rent, sz ) );
134 :
135 0 : rec->vt->set_data_len( rec, sz );
136 0 : rec->vt->set_owner( rec, &fd_sysvar_owner_id );
137 :
138 0 : fd_txn_account_mutable_fini( rec, slot_ctx->funk, slot_ctx->funk_txn );
139 :
140 0 : return 0;
141 0 : }
142 :
143 : fd_slot_history_global_t *
144 : fd_sysvar_slot_history_read( fd_funk_t * funk,
145 : fd_funk_txn_t * funk_txn,
146 0 : fd_spad_t * spad ) {
147 :
148 : /* Set current_slot, and update next_slot */
149 :
150 0 : fd_pubkey_t const * key = &fd_sysvar_slot_history_id;
151 :
152 0 : FD_TXN_ACCOUNT_DECL( rec );
153 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, funk, funk_txn );
154 0 : if( err ) {
155 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_readonly(slot_history) failed: %d", err ));
156 0 : }
157 :
158 : /* This check is needed as a quirk of the fuzzer. If a sysvar account
159 : exists in the accounts database, but doesn't have any lamports,
160 : this means that the account does not exist. This wouldn't happen
161 : in a real execution environment. */
162 0 : if( FD_UNLIKELY( rec->vt->get_lamports( rec ) == 0UL ) ) {
163 0 : return NULL;
164 0 : }
165 :
166 0 : fd_bincode_decode_ctx_t ctx = {
167 0 : .data = rec->vt->get_data( rec ),
168 0 : .dataend = rec->vt->get_data( rec ) + rec->vt->get_data_len( rec )
169 0 : };
170 :
171 0 : ulong total_sz = 0UL;
172 0 : err = fd_slot_history_decode_footprint( &ctx, &total_sz );
173 0 : if( err ) {
174 0 : FD_LOG_ERR(( "fd_slot_history_decode_footprint failed" ));
175 0 : }
176 :
177 0 : uchar * mem = fd_spad_alloc( spad, fd_slot_history_align(), total_sz );
178 0 : if( !mem ) {
179 0 : FD_LOG_ERR(( "Unable to allocate memory for slot history" ));
180 0 : }
181 :
182 0 : return fd_slot_history_decode_global( mem, &ctx );
183 0 : }
184 :
185 : int
186 : fd_sysvar_slot_history_find_slot( fd_slot_history_global_t const * history,
187 : ulong slot,
188 0 : fd_wksp_t * wksp ) {
189 0 : (void)wksp;
190 0 : ulong * blocks = (ulong *)((uchar*)history + history->bits_bitvec_offset);
191 0 : if( FD_UNLIKELY( !blocks ) ) {
192 0 : FD_LOG_ERR(( "Unable to find slot history blocks" ));
193 0 : }
194 0 : ulong blocks_len = history->bits_bitvec_len;
195 :
196 :
197 0 : if( slot > history->next_slot - 1UL ) {
198 0 : return FD_SLOT_HISTORY_SLOT_FUTURE;
199 0 : } else if ( slot < fd_ulong_sat_sub( history->next_slot, slot_history_max_entries ) ) {
200 0 : return FD_SLOT_HISTORY_SLOT_TOO_OLD;
201 0 : } else {
202 0 : ulong block_idx = (slot / bits_per_block) % blocks_len;
203 0 : if( blocks[ block_idx ] & ( 1UL << ( slot % bits_per_block ) ) ) {
204 0 : return FD_SLOT_HISTORY_SLOT_FOUND;
205 0 : } else {
206 0 : return FD_SLOT_HISTORY_SLOT_NOT_FOUND;
207 0 : }
208 0 : }
209 0 : }
|