Line data Source code
1 : #include "fd_forks.h"
2 : #include "../../flamenco/runtime/context/fd_exec_slot_ctx.h"
3 : #include "../../flamenco/runtime/fd_acc_mgr.h"
4 : #include "../../flamenco/runtime/fd_borrowed_account.h"
5 : #include "../../flamenco/runtime/fd_runtime.h"
6 : #include "../../flamenco/runtime/program/fd_program_util.h"
7 : #include "../../flamenco/runtime/program/fd_vote_program.h"
8 :
9 : void *
10 0 : fd_forks_new( void * shmem, ulong max, ulong seed ) {
11 :
12 0 : if( FD_UNLIKELY( !shmem ) ) {
13 0 : FD_LOG_WARNING(( "NULL mem" ));
14 0 : return NULL;
15 0 : }
16 :
17 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_forks_align() ) ) ) {
18 0 : FD_LOG_WARNING(( "misaligned mem" ));
19 0 : return NULL;
20 0 : }
21 :
22 0 : ulong footprint = fd_forks_footprint( max );
23 0 : if( FD_UNLIKELY( !footprint ) ) {
24 0 : FD_LOG_WARNING(( "bad mem" ));
25 0 : return NULL;
26 0 : }
27 :
28 0 : fd_memset( shmem, 0, footprint );
29 0 : ulong laddr = (ulong)shmem;
30 :
31 0 : laddr = fd_ulong_align_up( laddr, alignof( fd_forks_t ) );
32 0 : laddr += sizeof( fd_forks_t );
33 :
34 0 : laddr = fd_ulong_align_up( laddr, fd_fork_pool_align() );
35 0 : fd_fork_pool_new( (void *)laddr, max );
36 0 : laddr += fd_fork_pool_footprint( max );
37 :
38 0 : laddr = fd_ulong_align_up( laddr, fd_fork_frontier_align() );
39 0 : fd_fork_frontier_new( (void *)laddr, max, seed );
40 0 : laddr += fd_fork_frontier_footprint( max );
41 :
42 0 : return shmem;
43 0 : }
44 :
45 : fd_forks_t *
46 0 : fd_forks_join( void * shforks ) {
47 :
48 0 : if( FD_UNLIKELY( !shforks ) ) {
49 0 : FD_LOG_WARNING(( "NULL forks" ));
50 0 : return NULL;
51 0 : }
52 :
53 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shforks, fd_forks_align() ) ) ) {
54 0 : FD_LOG_WARNING(( "misaligned forks" ));
55 0 : return NULL;
56 0 : }
57 :
58 0 : ulong laddr = (ulong)shforks;
59 0 : fd_forks_t * forks = (void *)laddr;
60 :
61 0 : laddr = fd_ulong_align_up( laddr, alignof( fd_forks_t ) );
62 0 : laddr += sizeof( fd_forks_t );
63 :
64 0 : laddr = fd_ulong_align_up( laddr, fd_fork_pool_align() );
65 0 : forks->pool = fd_fork_pool_join( (void *)laddr );
66 0 : ulong max = fd_fork_pool_max( forks->pool );
67 0 : laddr += fd_fork_pool_footprint( max );
68 :
69 0 : laddr = fd_ulong_align_up( laddr, fd_fork_frontier_align() );
70 0 : forks->frontier = fd_fork_frontier_join( (void *)laddr );
71 0 : laddr += fd_fork_frontier_footprint( max );
72 :
73 0 : return (fd_forks_t *)shforks;
74 0 : }
75 :
76 : void *
77 0 : fd_forks_leave( fd_forks_t const * forks ) {
78 :
79 0 : if( FD_UNLIKELY( !forks ) ) {
80 0 : FD_LOG_WARNING(( "NULL forks" ));
81 0 : return NULL;
82 0 : }
83 :
84 0 : return (void *)forks;
85 0 : }
86 :
87 : void *
88 0 : fd_forks_delete( void * forks ) {
89 :
90 0 : if( FD_UNLIKELY( !forks ) ) {
91 0 : FD_LOG_WARNING(( "NULL forks" ));
92 0 : return NULL;
93 0 : }
94 :
95 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)forks, fd_forks_align() ) ) ) {
96 0 : FD_LOG_WARNING(( "misaligned forks" ));
97 0 : return NULL;
98 0 : }
99 :
100 0 : return forks;
101 0 : }
102 :
103 : fd_fork_t *
104 0 : fd_forks_init( fd_forks_t * forks, fd_exec_slot_ctx_t const * slot_ctx ) {
105 :
106 0 : if( FD_UNLIKELY( !forks ) ) {
107 0 : FD_LOG_WARNING(( "NULL forks" ));
108 0 : return NULL;
109 0 : }
110 :
111 0 : if( FD_UNLIKELY( !slot_ctx ) ) {
112 0 : FD_LOG_WARNING(( "NULL slot_ctx" ));
113 0 : return NULL;
114 0 : }
115 :
116 0 : fd_fork_t * fork = fd_fork_pool_ele_acquire( forks->pool );
117 0 : fork->slot = slot_ctx->slot_bank.slot;
118 0 : fork->prev = fd_fork_pool_idx_null( forks->pool );
119 0 : fork->lock = 0;
120 0 : fork->slot_ctx = *slot_ctx; /* this shallow copy is only safe if
121 : the lifetimes of slot_ctx's pointers
122 : are as long as fork */
123 0 : if( FD_UNLIKELY( !fd_fork_frontier_ele_insert( forks->frontier, fork, forks->pool ) ) ) {
124 0 : FD_LOG_WARNING(( "Failed to insert fork into frontier" ));
125 0 : }
126 :
127 0 : return fork;
128 0 : }
129 :
130 : fd_fork_t *
131 0 : fd_forks_query( fd_forks_t * forks, ulong slot ) {
132 0 : return fd_fork_frontier_ele_query( forks->frontier, &slot, NULL, forks->pool );
133 0 : }
134 :
135 : fd_fork_t const *
136 0 : fd_forks_query_const( fd_forks_t const * forks, ulong slot ) {
137 0 : return fd_fork_frontier_ele_query_const( forks->frontier, &slot, NULL, forks->pool );
138 0 : }
139 :
140 : // fd_fork_t *
141 : // fd_forks_advance( fd_forks_t * forks,
142 : // fd_fork_t * fork,
143 : // ulong slot,
144 : // fd_acc_mgr_t * acc_mgr,
145 : // fd_blockstore_t * blockstore,
146 : // fd_exec_epoch_ctx_t * epoch_ctx,
147 : // fd_funk_t * funk,
148 : // fd_valloc_t valloc ) {
149 : // // Remove slot ctx from frontier
150 : // fd_fork_t * child = fd_fork_frontier_ele_remove( forks->frontier,
151 : // &fork->slot,
152 : // NULL,
153 : // forks->pool );
154 : // child->slot = curr_slot;
155 : // if( FD_UNLIKELY( fd_fork_frontier_ele_query( forks->frontier,
156 : // &curr_slot,
157 : // NULL,
158 : // forks->pool ) ) ) {
159 : // FD_LOG_ERR( ( "invariant violation: child slot %lu was already in the
160 : // frontier", curr_slot ) );
161 : // }
162 : // fd_fork_frontier_ele_insert( forks->frontier, child, forks->pool );
163 : // FD_TEST( fork == child );
164 :
165 : // // fork is advancing
166 : // FD_LOG_DEBUG(( "new block execution - slot: %lu, parent_slot: %lu", curr_slot, parent_slot ));
167 :
168 : // fork->slot_ctx.slot_bank.prev_slot = fork->slot_ctx.slot_bank.slot;
169 : // fork->slot_ctx.slot_bank.slot = curr_slot;
170 :
171 : // fork->slot_ctx.status_cache = status_cache;
172 : // fd_funk_txn_xid_t xid;
173 :
174 : // fd_memcpy( xid.uc, blockhash.uc, sizeof( fd_funk_txn_xid_t));
175 : // xid.ul[0] = fork->slot_ctx.slot_bank.slot;
176 : // /* push a new transaction on the stack */
177 : // fd_funk_start_write( funk );
178 : // fork->slot_ctx.funk_txn = fd_funk_txn_prepare( funk, fork->slot_ctx.funk_txn, &xid, 1 );
179 : // fd_funk_end_write( funk );
180 :
181 : // int res = fd_runtime_publish_old_txns( &fork->slot_ctx, capture_ctx );
182 : // if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
183 : // FD_LOG_ERR(( "txn publishing failed" ));
184 : // }
185 : // }
186 :
187 : static void
188 : slot_ctx_restore( ulong slot,
189 : fd_acc_mgr_t * acc_mgr,
190 : fd_blockstore_t * blockstore,
191 : fd_exec_epoch_ctx_t * epoch_ctx,
192 : fd_funk_t * funk,
193 : fd_valloc_t valloc,
194 0 : fd_exec_slot_ctx_t * slot_ctx_out ) {
195 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( funk, fd_funk_wksp( funk ) );
196 0 : fd_block_map_t * block = fd_block_map_query( fd_blockstore_block_map( blockstore ), &slot, NULL );
197 0 : FD_LOG_DEBUG(( "Current slot %lu", slot ));
198 0 : if( !block || !block->block_gaddr ) FD_LOG_ERR(( "missing block at slot we're trying to restore" ));
199 0 : fd_funk_txn_xid_t xid;
200 0 : memcpy( xid.uc, block->block_hash.uc, sizeof(fd_funk_txn_xid_t) );
201 0 : xid.ul[0] = slot;
202 0 : fd_funk_rec_key_t id = fd_runtime_slot_bank_key();
203 0 : fd_funk_txn_t * txn = fd_funk_txn_query( &xid, txn_map );
204 0 : if( !txn ) {
205 0 : memset( xid.uc, 0, sizeof(fd_funk_txn_xid_t) );
206 0 : xid.ul[0] = slot;
207 0 : txn = fd_funk_txn_query( &xid, txn_map );
208 0 : if( !txn ) {
209 0 : FD_LOG_ERR(( "missing txn, parent slot %lu", slot ));
210 0 : }
211 0 : }
212 0 : fd_funk_rec_t const * rec = fd_funk_rec_query_global( funk, txn, &id, NULL );
213 0 : if( rec == NULL ) FD_LOG_ERR(( "failed to read banks record" ));
214 0 : void * val = fd_funk_val( rec, fd_funk_wksp( funk ) );
215 :
216 0 : uint magic = *(uint*)val;
217 :
218 0 : fd_bincode_decode_ctx_t decode_ctx;
219 0 : decode_ctx.data = (uchar *)val + sizeof( uint );
220 0 : decode_ctx.dataend = (uchar *)val + fd_funk_val_sz( rec );
221 0 : decode_ctx.valloc = valloc;
222 :
223 0 : FD_TEST( slot_ctx_out->magic == FD_EXEC_SLOT_CTX_MAGIC );
224 :
225 0 : slot_ctx_out->funk_txn = txn;
226 :
227 0 : slot_ctx_out->acc_mgr = acc_mgr;
228 0 : slot_ctx_out->blockstore = blockstore;
229 0 : slot_ctx_out->epoch_ctx = epoch_ctx;
230 0 : slot_ctx_out->valloc = valloc;
231 :
232 0 : fd_bincode_destroy_ctx_t destroy_ctx = {
233 0 : .valloc = valloc,
234 0 : };
235 :
236 0 : fd_slot_bank_destroy( &slot_ctx_out->slot_bank, &destroy_ctx );
237 0 : if( magic == FD_RUNTIME_ENC_BINCODE ) {
238 0 : FD_TEST( fd_slot_bank_decode( &slot_ctx_out->slot_bank, &decode_ctx ) == FD_BINCODE_SUCCESS );
239 0 : } else if( magic == FD_RUNTIME_ENC_ARCHIVE ) {
240 0 : FD_TEST( fd_slot_bank_decode_archival( &slot_ctx_out->slot_bank, &decode_ctx )==FD_BINCODE_SUCCESS );
241 0 : } else {
242 0 : FD_LOG_ERR(( "failed to read banks record: invalid magic number" ));
243 0 : }
244 0 : FD_TEST( !fd_runtime_sysvar_cache_load( slot_ctx_out ) );
245 0 : slot_ctx_out->leader = fd_epoch_leaders_get( fd_exec_epoch_ctx_leaders( slot_ctx_out->epoch_ctx ),
246 0 : slot );
247 :
248 : // TODO how do i get this info, ignoring rewards for now
249 : // slot_ctx_out->epoch_reward_status = ???
250 :
251 : // signature_cnt, account_delta_hash, prev_banks_hash are used for the banks
252 : // hash calculation and not needed when restoring parent
253 :
254 0 : FD_LOG_NOTICE(( "recovered slot_bank for slot=%lu banks_hash=%s poh_hash %s",
255 0 : slot_ctx_out->slot_bank.slot,
256 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx_out->slot_bank.banks_hash.hash ),
257 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx_out->slot_bank.poh.hash ) ));
258 :
259 : /* Prepare bank for next slot */
260 0 : slot_ctx_out->slot_bank.slot = slot;
261 0 : slot_ctx_out->slot_bank.collected_execution_fees = 0;
262 0 : slot_ctx_out->slot_bank.collected_priority_fees = 0;
263 0 : slot_ctx_out->slot_bank.collected_rent = 0;
264 :
265 : /* FIXME epoch boundary stuff when replaying */
266 : // fd_features_restore( slot_ctx );
267 : // fd_runtime_update_leaders( slot_ctx, slot_ctx->slot_bank.slot );
268 : // fd_calculate_epoch_accounts_hash_values( slot_ctx );
269 0 : }
270 :
271 : fd_fork_t *
272 : fd_forks_prepare( fd_forks_t const * forks,
273 : ulong parent_slot,
274 : fd_acc_mgr_t * acc_mgr,
275 : fd_blockstore_t * blockstore,
276 : fd_exec_epoch_ctx_t * epoch_ctx,
277 : fd_funk_t * funk,
278 0 : fd_valloc_t valloc ) {
279 :
280 : /* Check the parent block is present in the blockstore and executed. */
281 :
282 0 : fd_block_t * block = fd_blockstore_block_query( blockstore, parent_slot );
283 0 : if( FD_UNLIKELY( !block ) ) {
284 0 : FD_LOG_WARNING(( "fd_forks_prepare missing parent_slot %lu", parent_slot ));
285 0 : }
286 :
287 : /* Query for parent_slot in the frontier. */
288 :
289 0 : fd_fork_t * fork = fd_fork_frontier_ele_query( forks->frontier, &parent_slot, NULL, forks->pool );
290 :
291 : /* If the parent block is both present and executed, but isn't in the
292 : frontier, that means this block is starting a new fork and needs to
293 : be added to the frontier. This requires recovering the slot_ctx
294 : as of that parent_slot by executing a funky rollback. */
295 :
296 0 : if( FD_UNLIKELY( !fork ) ) {
297 :
298 : /* Alloc a new slot_ctx */
299 :
300 0 : fork = fd_fork_pool_ele_acquire( forks->pool );
301 0 : fork->prev = fd_fork_pool_idx_null( forks->pool );
302 0 : fork->slot = parent_slot;
303 0 : fork->lock = 1;
304 :
305 : /* Format and join the slot_ctx */
306 :
307 0 : fd_exec_slot_ctx_t * slot_ctx =
308 0 : fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( &fork->slot_ctx, valloc ) );
309 0 : if( FD_UNLIKELY( !slot_ctx ) ) {
310 0 : FD_LOG_ERR(( "failed to new and join slot_ctx" ));
311 0 : }
312 :
313 : /* Restore and decode w/ funk */
314 :
315 0 : slot_ctx_restore( fork->slot, acc_mgr, blockstore, epoch_ctx, funk, valloc, slot_ctx );
316 :
317 : /* Add to frontier */
318 :
319 0 : fd_fork_frontier_ele_insert( forks->frontier, fork, forks->pool );
320 0 : }
321 :
322 0 : return fork;
323 0 : }
324 :
325 : void
326 0 : fd_forks_publish( fd_forks_t * forks, ulong slot, fd_ghost_t const * ghost ) {
327 0 : fd_fork_t * tail = NULL;
328 0 : fd_fork_t * curr = NULL;
329 :
330 0 : for( fd_fork_frontier_iter_t iter = fd_fork_frontier_iter_init( forks->frontier, forks->pool );
331 0 : !fd_fork_frontier_iter_done( iter, forks->frontier, forks->pool );
332 0 : iter = fd_fork_frontier_iter_next( iter, forks->frontier, forks->pool ) ) {
333 0 : fd_fork_t * fork = fd_fork_frontier_iter_ele( iter, forks->frontier, forks->pool );
334 :
335 : /* Prune any forks not in the ancestry from root.
336 :
337 : Optimize for unlikely because there is usually just one fork. */
338 :
339 0 : int stale = fork->slot < slot || !fd_ghost_is_descendant( ghost, fork->slot, slot );
340 0 : if( FD_UNLIKELY( !fork->lock && stale ) ) {
341 0 : FD_LOG_NOTICE(( "adding %lu to prune. root %lu", fork->slot, slot ));
342 0 : if( FD_LIKELY( !curr ) ) {
343 0 : tail = fork;
344 0 : curr = fork;
345 0 : } else {
346 0 : curr->prev = fd_fork_pool_idx( forks->pool, fork );
347 0 : curr = fd_fork_pool_ele( forks->pool, curr->prev );
348 0 : }
349 0 : }
350 0 : }
351 :
352 0 : while( FD_UNLIKELY( tail ) ) {
353 0 : fd_fork_t * fork = fd_fork_frontier_ele_query( forks->frontier,
354 0 : &tail->slot,
355 0 : NULL,
356 0 : forks->pool );
357 0 : if( FD_UNLIKELY( !fd_exec_slot_ctx_delete( fd_exec_slot_ctx_leave( &fork->slot_ctx ) ) ) ) {
358 0 : FD_LOG_ERR(( "could not delete fork slot ctx" ));
359 0 : }
360 0 : ulong remove = fd_fork_frontier_idx_remove( forks->frontier,
361 0 : &tail->slot,
362 0 : fd_fork_pool_idx_null( forks->pool ),
363 0 : forks->pool );
364 0 : #if FD_FORKS_USE_HANDHOLDING
365 0 : if( FD_UNLIKELY( remove == fd_fork_pool_idx_null( forks->pool ) ) ) {
366 0 : FD_LOG_ERR(( "failed to remove fork we added to prune." ));
367 0 : }
368 0 : #endif
369 :
370 : /* pool_idx_release cannot fail given we just removed this from the
371 : frontier directly above. */
372 0 : fd_fork_pool_idx_release( forks->pool, remove );
373 0 : tail = fd_ptr_if( tail->prev != fd_fork_pool_idx_null( forks->pool ),
374 0 : fd_fork_pool_ele( forks->pool, tail->prev ),
375 0 : NULL );
376 0 : }
377 0 : }
|