Line data Source code
1 : #include <errno.h>
2 : #include "../../flamenco/fd_flamenco.h"
3 : #include "../../flamenco/runtime/fd_hashes.h"
4 : #include "../../flamenco/types/fd_types.h"
5 : #include "../../flamenco/runtime/fd_runtime.h"
6 : #include "../../flamenco/runtime/fd_runtime_public.h"
7 : #include "../../flamenco/runtime/fd_rocksdb.h"
8 : #include "../../flamenco/runtime/fd_txncache.h"
9 : #include "../../flamenco/rewards/fd_rewards.h"
10 : #include "../../ballet/base58/fd_base58.h"
11 : #include "../../flamenco/runtime/context/fd_capture_ctx.h"
12 : #include "../../flamenco/runtime/fd_blockstore.h"
13 : #include "../../flamenco/shredcap/fd_shredcap.h"
14 : #include "../../flamenco/runtime/program/fd_bpf_program_util.h"
15 : #include "../../flamenco/snapshot/fd_snapshot.h"
16 :
17 : struct fd_ledger_args {
18 : fd_wksp_t * wksp; /* wksp for blockstore */
19 : fd_wksp_t * funk_wksp; /* wksp for funk */
20 : fd_wksp_t * status_cache_wksp; /* wksp for status cache. */
21 : fd_blockstore_t blockstore_ljoin;
22 : fd_blockstore_t * blockstore; /* blockstore for replay */
23 : fd_funk_t funk[1]; /* handle to funk */
24 : fd_alloc_t * alloc; /* handle to alloc */
25 : char const * cmd; /* user passed command to fd_ledger */
26 : ulong start_slot; /* start slot for offline replay */
27 : ulong end_slot; /* end slot for offline replay */
28 : uint hashseed; /* hashseed */
29 : char const * checkpt; /* wksp checkpoint */
30 : char const * checkpt_funk; /* wksp checkpoint for a funk wksp */
31 : char const * checkpt_status_cache; /* status cache checkpoint */
32 : char const * restore; /* wksp restore */
33 : char const * allocator; /* allocator used during replay (libc/wksp) */
34 : ulong shred_max; /* maximum number of shreds*/
35 : ulong slot_history_max; /* number of slots stored by blockstore*/
36 : ulong txns_max; /* txns_max*/
37 : uint index_max; /* size of funk index (same as rec max) */
38 : ulong funk_page_cnt;
39 : char const * snapshot; /* path to agave snapshot */
40 : char const * incremental; /* path to agave incremental snapshot */
41 : char const * genesis; /* path to agave genesis */
42 : char const * mini_db_dir; /* path to minifed rocksdb that's to be created */
43 : int copy_txn_status; /* determine if txns should be copied to the blockstore during minify/replay */
44 : int funk_only; /* determine if only funk should be ingested */
45 : char const * shredcap; /* path to replay using shredcap instead of rocksdb */
46 : int abort_on_mismatch; /* determine if execution should abort on mismatch*/
47 : char const * capture_fpath; /* solcap: path for solcap file to be created */
48 : ulong solcap_start_slot; /* solcap capture start slot */
49 : int capture_txns; /* solcap: determine if transaction results should be captured for solcap*/
50 : char const * checkpt_path; /* path to dump funk wksp checkpoints during execution*/
51 : ulong checkpt_freq; /* how often funk wksp checkpoints will be dumped (defaults to never) */
52 : int checkpt_mismatch; /* determine if a funk wksp checkpoint should be dumped on a mismatch*/
53 :
54 : int dump_instr_to_pb; /* instruction dumping: should insns be dumped */
55 : int dump_txn_to_pb; /* txn dumping: should txns be dumped */
56 : int dump_block_to_pb; /* block dumping: should blocks be dumped */
57 : int dump_syscall_to_pb; /* syscall dumping: should syscalls be dumped */
58 : int dump_elf_to_pb; /* elf dumping: should elfs be dumped */
59 : ulong dump_proto_start_slot; /* instruction / txn dumping: what slot to start dumping*/
60 : char const * dump_proto_sig_filter; /* instruction / txn dumping: specify txn sig to dump at */
61 : char const * dump_proto_output_dir; /* instruction / txn dumping: output directory for protobuf messages */
62 :
63 : int verify_funk; /* verify funk before execution starts */
64 : uint verify_acc_hash; /* verify account hash from the snapshot */
65 : uint check_acc_hash; /* check account hash by reconstructing with data */
66 : ulong trash_hash; /* trash hash to be used for negative cases*/
67 : ulong vote_acct_max; /* max number of vote accounts */
68 : char const * rocksdb_list[32]; /* max number of rocksdb dirs that can be passed in */
69 : ulong rocksdb_list_slot[32]; /* start slot for each rocksdb dir that's passed in assuming there are mulitple */
70 : ulong rocksdb_list_cnt; /* number of rocksdb dirs passed in */
71 : char * rocksdb_list_strdup;
72 : uint cluster_version[3]; /* What version of solana is the genesis block? */
73 : char const * one_off_features[32]; /* List of one off feature pubkeys to enable for execution agnostic of cluster version */
74 : uint one_off_features_cnt; /* Number of one off features */
75 : char * one_off_features_strdup;
76 : double allowed_mem_delta; /* Percent of memory in the blockstore wksp that can be
77 : used and not freed between the start of end of execution.
78 : If the difference in usage exceeds this value, error out. */
79 :
80 : /* These values are setup and maintained before replay */
81 : fd_capture_ctx_t * capture_ctx; /* capture_ctx is used in runtime_replay for various debugging tasks */
82 : fd_exec_slot_ctx_t * slot_ctx; /* slot_ctx */
83 : fd_tpool_t * tpool; /* thread pool for execution */
84 : uchar tpool_mem[FD_TPOOL_FOOTPRINT( FD_TILE_MAX )] __attribute__( ( aligned( FD_TPOOL_ALIGN ) ) );
85 :
86 : fd_spad_t * exec_spads[ 128UL ]; /* bump allocators that are eventually assigned to each txn_ctx */
87 : ulong exec_spad_cnt; /* number of bump allocators, bounded by number of threads */
88 : fd_spad_t * runtime_spad; /* bump allocator used for runtime scoped allocations */
89 : ulong thread_mem_bound; /* how much spad is allocated by a tpool thread. The default
90 : value is the full runtime bound. If a value of 0 is passed
91 : in, then a reduced bound will be used. */
92 : ulong runtime_mem_bound; /* how much to allocate for a runtime-scoped spad */
93 :
94 : fd_valloc_t valloc; /* wksp valloc that should NOT be used for runtime allocations */
95 :
96 : char const * lthash;
97 : };
98 : typedef struct fd_ledger_args fd_ledger_args_t;
99 :
100 : /* Allocations ****************************************************************/
101 :
102 : static void
103 0 : init_exec_spads( fd_ledger_args_t * args, int has_tpool ) {
104 :
105 0 : FD_LOG_NOTICE(( "setting up exec_spads" ));
106 :
107 : /* Allocate memory for the account mem space. In live execution, each of
108 : the spad allocations should be tied to its respective execution thread.
109 : In the future, the spad should be allocated from its tiles' workspace.
110 : It is important that the exec_spads are only allocated on startup for
111 : performance reasons to avoid dynamic allocation in the critical path. */
112 :
113 0 : if( has_tpool ) {
114 0 : args->exec_spad_cnt = fd_tpool_worker_cnt( args->tpool );
115 0 : for( ulong i=0UL; i<fd_tpool_worker_cnt( args->tpool ); i++ ) {
116 0 : ulong total_mem_sz = args->thread_mem_bound;
117 0 : uchar * mem = fd_wksp_alloc_laddr( args->wksp, FD_SPAD_ALIGN, FD_SPAD_FOOTPRINT( total_mem_sz ), 999UL );
118 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( mem, total_mem_sz ) );
119 0 : if( FD_UNLIKELY( !spad ) ) {
120 0 : FD_LOG_ERR(( "failed to allocate spad" ));
121 0 : }
122 0 : args->exec_spads[ i ] = spad;
123 0 : }
124 0 : }
125 0 : }
126 :
127 : /* Runtime Replay *************************************************************/
128 : static int
129 0 : init_tpool( fd_ledger_args_t * ledger_args ) {
130 :
131 0 : ulong tcnt = fd_tile_cnt();
132 0 : fd_tpool_t * tpool = NULL;
133 :
134 0 : ulong start_idx = 1UL;
135 0 : if( tcnt>=1UL ) {
136 0 : tpool = fd_tpool_init( ledger_args->tpool_mem, tcnt, 0UL );
137 0 : if( tpool == NULL ) {
138 0 : FD_LOG_ERR(( "failed to create thread pool" ));
139 0 : }
140 0 : for( ulong i=1UL; i<tcnt; ++i ) {
141 0 : if( fd_tpool_worker_push( tpool, start_idx++ ) == NULL ) {
142 0 : FD_LOG_ERR(( "failed to launch worker" ));
143 0 : }
144 0 : else {
145 0 : FD_LOG_INFO(( "launched worker %lu", start_idx - 1UL ));
146 0 : }
147 0 : }
148 0 : }
149 :
150 0 : ledger_args->tpool = tpool;
151 :
152 0 : return 0;
153 0 : }
154 :
155 : void
156 0 : args_cleanup( fd_ledger_args_t * ledger_args ) {
157 0 : if( ledger_args->rocksdb_list_strdup ) free( ledger_args->rocksdb_list_strdup );
158 0 : if( ledger_args->one_off_features_strdup ) free( ledger_args->one_off_features_strdup );
159 0 : }
160 :
161 : int
162 0 : runtime_replay( fd_ledger_args_t * ledger_args ) {
163 0 : int ret = 0;
164 :
165 0 : fd_features_restore( ledger_args->slot_ctx, ledger_args->runtime_spad );
166 :
167 0 : fd_runtime_update_leaders( ledger_args->slot_ctx->bank, ledger_args->slot_ctx->slot, ledger_args->runtime_spad );
168 :
169 0 : fd_calculate_epoch_accounts_hash_values( ledger_args->slot_ctx );
170 :
171 0 : long replay_time = -fd_log_wallclock();
172 0 : ulong txn_cnt = 0;
173 0 : ulong slot_cnt = 0;
174 0 : fd_blockstore_t * blockstore = ledger_args->blockstore;
175 :
176 0 : ulong prev_slot = ledger_args->slot_ctx->slot;
177 0 : ulong start_slot = ledger_args->slot_ctx->slot + 1;
178 :
179 : /* On demand rocksdb ingest */
180 0 : fd_rocksdb_t rocks_db = {0};
181 0 : fd_rocksdb_root_iter_t iter = {0};
182 0 : fd_slot_meta_t slot_meta = {0};
183 0 : ulong curr_rocksdb_idx = 0UL;
184 :
185 0 : char * err = fd_rocksdb_init( &rocks_db, ledger_args->rocksdb_list[ 0UL ] );
186 0 : if( FD_UNLIKELY( err!=NULL ) ) {
187 0 : FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", ledger_args->rocksdb_list[ 0UL ], err ));
188 0 : }
189 0 : fd_rocksdb_root_iter_new( &iter );
190 :
191 0 : int block_found = -1;
192 0 : while ( block_found!=0 && start_slot<=ledger_args->end_slot ) {
193 0 : block_found = fd_rocksdb_root_iter_seek( &iter, &rocks_db, start_slot, &slot_meta, ledger_args->valloc );
194 0 : if ( block_found!=0 ) {
195 0 : start_slot++;
196 0 : }
197 0 : }
198 :
199 : /* Setup trash_hash */
200 0 : uchar trash_hash_buf[32];
201 0 : memset( trash_hash_buf, 0xFE, sizeof(trash_hash_buf) );
202 :
203 : /* Calculate and store wksp free size before execution. */
204 0 : fd_wksp_usage_t init_usage = {0};
205 0 : fd_wksp_usage( fd_blockstore_wksp( ledger_args->blockstore ), NULL, 0UL, &init_usage );
206 :
207 0 : ulong block_slot = start_slot;
208 0 : uchar aborted = 0U;
209 :
210 : // set up to let us easily jump to the end of execution
211 0 : do {
212 :
213 0 : if( FD_UNLIKELY( block_found!=0 ) ) {
214 0 : if( 0 == ledger_args->end_slot )
215 0 : break; // special case just letting us do the genesis block
216 0 : FD_LOG_ERR(( "unable to seek to any slot" ));
217 0 : }
218 :
219 0 : for( ulong slot = start_slot; slot<=ledger_args->end_slot && !aborted; ++slot ) {
220 :
221 0 : fd_bank_prev_slot_set( ledger_args->slot_ctx->bank, prev_slot );
222 :
223 0 : FD_LOG_DEBUG(( "reading slot %lu", slot ));
224 :
225 : /* If we have reached a new block, load one in from rocksdb to the blockstore */
226 0 : bool block_exists = fd_blockstore_shreds_complete( blockstore, slot);
227 0 : if( !block_exists && slot_meta.slot == slot ) {
228 0 : int err = fd_rocksdb_import_block_blockstore( &rocks_db,
229 0 : &slot_meta, blockstore,
230 0 : ledger_args->copy_txn_status,
231 0 : slot == (ledger_args->trash_hash) ? trash_hash_buf : NULL,
232 0 : ledger_args->valloc );
233 0 : if( FD_UNLIKELY( err ) ) {
234 0 : FD_LOG_ERR(( "Failed to import block %lu", start_slot ));
235 0 : }
236 :
237 : /* Remove the previous block from the blockstore */
238 0 : if( FD_LIKELY( block_slot < slot ) ) {
239 : /* Mark the block as successfully processed */
240 :
241 0 : fd_block_map_query_t query[1] = {0};
242 0 : int err = fd_block_map_prepare( blockstore->block_map, &block_slot, NULL, query, FD_MAP_FLAG_BLOCKING );
243 0 : fd_block_info_t * block_info = fd_block_map_query_ele( query );
244 :
245 0 : if( FD_UNLIKELY( err || block_info->slot != block_slot ) ) FD_LOG_ERR(( "failed to prepare block map query" ));
246 :
247 0 : block_info->flags = fd_uchar_clear_bit( block_info->flags, FD_BLOCK_FLAG_REPLAYING );
248 0 : block_info->flags = fd_uchar_set_bit( block_info->flags, FD_BLOCK_FLAG_PROCESSED );
249 :
250 0 : fd_block_map_publish( query );
251 :
252 : /* Remove the old block from the blockstore */
253 : /*for( uint idx = 0; idx <= slot_complete_idx; idx++ ) {
254 : fd_blockstore_shred_remove( blockstore, block_slot, idx );
255 : }*/
256 0 : fd_blockstore_block_allocs_remove( blockstore, block_slot );
257 0 : fd_blockstore_slot_remove( blockstore, block_slot );
258 0 : }
259 : /* Mark the new block as replaying */
260 0 : fd_block_map_query_t query[1] = {0};
261 0 : err = fd_block_map_prepare( blockstore->block_map, &slot, NULL, query, FD_MAP_FLAG_BLOCKING );
262 0 : fd_block_info_t * block_info = fd_block_map_query_ele( query );
263 0 : if( FD_UNLIKELY( err || block_info->slot != slot ) ) FD_LOG_ERR(( "failed to prepare block map query" ));
264 0 : block_info->flags = fd_uchar_set_bit( block_info->flags, FD_BLOCK_FLAG_REPLAYING );
265 0 : fd_block_map_publish( query );
266 :
267 0 : block_slot = slot;
268 0 : }
269 :
270 0 : fd_block_t * blk = fd_blockstore_block_query( blockstore, slot );
271 0 : if( blk == NULL ) {
272 0 : FD_LOG_WARNING(( "failed to read slot %lu", slot ));
273 0 : continue;
274 0 : }
275 :
276 0 : fd_bank_tick_height_set( ledger_args->slot_ctx->bank, fd_bank_max_tick_height_get( ledger_args->slot_ctx->bank ) );
277 :
278 0 : ulong * max_tick_height = fd_bank_max_tick_height_modify( ledger_args->slot_ctx->bank );
279 0 : ulong ticks_per_slot = fd_bank_ticks_per_slot_get( ledger_args->slot_ctx->bank );
280 0 : if( FD_UNLIKELY( FD_RUNTIME_EXECUTE_SUCCESS != fd_runtime_compute_max_tick_height( ticks_per_slot, slot, max_tick_height ) ) ) {
281 0 : FD_LOG_ERR(( "couldn't compute max tick height slot %lu ticks_per_slot %lu", slot, ticks_per_slot ));
282 0 : }
283 :
284 0 : ledger_args->slot_ctx->bank = fd_banks_clone_from_parent( ledger_args->slot_ctx->banks, slot, prev_slot );
285 :
286 0 : ulong blk_txn_cnt = 0UL;
287 0 : FD_LOG_NOTICE(( "Used memory in spad before slot=%lu %lu", slot, ledger_args->runtime_spad->mem_used ));
288 0 : FD_TEST( fd_runtime_block_eval_tpool( ledger_args->slot_ctx,
289 0 : slot,
290 0 : blk,
291 0 : ledger_args->capture_ctx,
292 0 : ledger_args->tpool,
293 0 : 1,
294 0 : &blk_txn_cnt,
295 0 : ledger_args->exec_spads,
296 0 : ledger_args->exec_spad_cnt,
297 0 : ledger_args->runtime_spad,
298 0 : ledger_args->blockstore ) == FD_RUNTIME_EXECUTE_SUCCESS );
299 0 : txn_cnt += blk_txn_cnt;
300 0 : slot_cnt++;
301 :
302 0 : fd_hash_t expected;
303 0 : int err = fd_blockstore_block_hash_query( blockstore, slot, &expected );
304 0 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR( ( "slot %lu is missing its hash", slot ) );
305 0 : else if( FD_UNLIKELY( 0 != memcmp( fd_bank_poh_query( ledger_args->slot_ctx->bank ), expected.hash, sizeof(fd_hash_t) ) ) ) {
306 0 : char expected_hash[ FD_BASE58_ENCODED_32_SZ ];
307 0 : fd_acct_addr_cstr( expected_hash, expected.hash );
308 0 : char poh_hash[ FD_BASE58_ENCODED_32_SZ ];
309 0 : fd_acct_addr_cstr( poh_hash, fd_bank_poh_query( ledger_args->slot_ctx->bank )->hash );
310 0 : FD_LOG_WARNING(( "PoH hash mismatch! slot=%lu expected=%s, got=%s",
311 0 : slot,
312 0 : expected_hash,
313 0 : poh_hash ));
314 :
315 0 : if( ledger_args->checkpt_mismatch ) {
316 0 : fd_runtime_checkpt( ledger_args->capture_ctx, ledger_args->slot_ctx, ULONG_MAX );
317 0 : }
318 0 : if( ledger_args->abort_on_mismatch ) {
319 0 : ret = 1;
320 0 : aborted = 1U;
321 0 : break;
322 0 : }
323 0 : }
324 :
325 0 : fd_hash_t const * bank_hash_bm = fd_bank_bank_hash_query( ledger_args->slot_ctx->bank );
326 0 : err = fd_blockstore_bank_hash_query( blockstore, slot, &expected );
327 0 : if( FD_UNLIKELY( err) ) {
328 0 : FD_LOG_ERR(( "slot %lu is missing its bank hash", slot ));
329 0 : } else if( FD_UNLIKELY( 0 != memcmp( bank_hash_bm,
330 0 : expected.hash,
331 0 : 32UL ) ) ) {
332 :
333 0 : char expected_hash[ FD_BASE58_ENCODED_32_SZ ];
334 0 : fd_acct_addr_cstr( expected_hash, expected.hash );
335 0 : char bank_hash[ FD_BASE58_ENCODED_32_SZ ];
336 0 : fd_acct_addr_cstr( bank_hash, bank_hash_bm->hash );
337 :
338 0 : FD_LOG_WARNING(( "Bank hash mismatch! slot=%lu expected=%s, got=%s",
339 0 : slot,
340 0 : expected_hash,
341 0 : bank_hash ));
342 :
343 0 : if( ledger_args->checkpt_mismatch ) {
344 0 : fd_runtime_checkpt( ledger_args->capture_ctx, ledger_args->slot_ctx, ULONG_MAX );
345 0 : }
346 0 : if( ledger_args->abort_on_mismatch ) {
347 0 : ret = 1;
348 0 : break;
349 0 : }
350 0 : }
351 :
352 0 : prev_slot = slot;
353 :
354 0 : if( slot<ledger_args->end_slot ) {
355 : /* TODO: This currently doesn't support switching over on slots that occur on a fork */
356 : /* If need to go to next rocksdb, switch over */
357 0 : if( FD_UNLIKELY( ledger_args->rocksdb_list_cnt>1UL &&
358 0 : slot+1UL==ledger_args->rocksdb_list_slot[curr_rocksdb_idx] ) ) {
359 0 : curr_rocksdb_idx++;
360 0 : FD_LOG_WARNING(( "Switching to next rocksdb=%s", ledger_args->rocksdb_list[curr_rocksdb_idx] ));
361 0 : fd_rocksdb_root_iter_destroy( &iter );
362 0 : fd_rocksdb_destroy( &rocks_db );
363 :
364 0 : fd_memset( &rocks_db, 0, sizeof(fd_rocksdb_t) );
365 0 : fd_memset( &iter, 0, sizeof(fd_rocksdb_root_iter_t) );
366 0 : fd_memset( &slot_meta, 0, sizeof(fd_slot_meta_t) );
367 :
368 0 : char * err = fd_rocksdb_init( &rocks_db, ledger_args->rocksdb_list[curr_rocksdb_idx] );
369 0 : if( FD_UNLIKELY( err!=NULL ) ) {
370 0 : FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", ledger_args->rocksdb_list[curr_rocksdb_idx], err ));
371 0 : }
372 0 : fd_rocksdb_root_iter_new( &iter );
373 0 : int ret = fd_rocksdb_root_iter_seek( &iter, &rocks_db, slot+1UL, &slot_meta, ledger_args->valloc );
374 0 : if( ret<0 ) {
375 0 : FD_LOG_ERR(( "Failed to seek to slot %lu", slot+1UL ));
376 0 : }
377 0 : } else {
378 : /* Otherwise look for next slot in current rocksdb */
379 0 : int ret = fd_rocksdb_root_iter_next( &iter, &slot_meta, ledger_args->valloc );
380 0 : if( ret<0 ) {
381 0 : ret = fd_rocksdb_get_meta( &rocks_db, slot+1UL, &slot_meta, ledger_args->valloc );
382 0 : if( ret<0 ) {
383 0 : FD_LOG_ERR(( "Failed to get meta for slot %lu", slot+1UL ));
384 0 : }
385 0 : }
386 0 : }
387 0 : }
388 0 : }
389 :
390 0 : } while(0);
391 :
392 : /* Throw an error if the blockstore wksp has a usage which exceeds the allowed
393 : threshold. This likely indicates that a memory leak was introduced. */
394 :
395 0 : fd_wksp_usage_t final_usage = {0};
396 0 : fd_wksp_usage( fd_blockstore_wksp( ledger_args->blockstore ), NULL, 0UL, &final_usage );
397 :
398 0 : ulong mem_delta = fd_ulong_sat_sub( init_usage.free_sz, final_usage.free_sz );
399 0 : double pcnt_mem_delta = (double)mem_delta / (double)init_usage.free_sz;
400 0 : if( pcnt_mem_delta > ledger_args->allowed_mem_delta ) {
401 0 : FD_LOG_ERR(( "Memory usage delta (%4f%%) exceeded allowed limit (%4f%%)", 100UL * pcnt_mem_delta, 100UL * ledger_args->allowed_mem_delta ));
402 0 : } else {
403 0 : FD_LOG_NOTICE(( "Memory usage delta (%4f%%) within allowed limit (%4f%%)", 100UL * pcnt_mem_delta, 100UL * ledger_args->allowed_mem_delta ));
404 0 : }
405 :
406 : #if FD_SPAD_TRACK_USAGE
407 : for( ulong i=0UL; i<ledger_args->exec_spad_cnt; i++ ) {
408 : fd_spad_t * spad = ledger_args->exec_spads[ i ];
409 : double pcnt_mem_wmark = (double)fd_spad_mem_wmark( spad ) / (double)fd_spad_mem_max( spad );
410 : pcnt_mem_wmark *= 100;
411 : FD_LOG_NOTICE(( "spad %2lu mem_wmark %10lu (%6.2f%%) mem_max %10lu", i, fd_spad_mem_wmark( spad ), pcnt_mem_wmark, fd_spad_mem_max( spad ) ));
412 : }
413 : #endif
414 :
415 0 : if( ledger_args->tpool ) {
416 0 : fd_tpool_fini( ledger_args->tpool );
417 0 : }
418 :
419 0 : fd_rocksdb_root_iter_destroy( &iter );
420 0 : fd_rocksdb_destroy( &rocks_db );
421 :
422 0 : replay_time += fd_log_wallclock();
423 0 : double replay_time_s = (double)replay_time * 1e-9;
424 0 : double tps = (double)txn_cnt / replay_time_s;
425 0 : double sec_per_slot = replay_time_s / (double)slot_cnt;
426 0 : FD_LOG_NOTICE((
427 0 : "replay completed - slots: %lu, elapsed: %6.6f s, txns: %lu, tps: %6.6f, sec/slot: %6.6f",
428 0 : slot_cnt,
429 0 : replay_time_s,
430 0 : txn_cnt,
431 0 : tps,
432 0 : sec_per_slot ));
433 :
434 0 : if( slot_cnt == 0 ) {
435 0 : if( 0 != ledger_args->end_slot )
436 0 : FD_LOG_ERR(( "No slots replayed" ));
437 0 : else
438 0 : FD_LOG_WARNING(( "No slots replayed" ));
439 0 : }
440 :
441 0 : args_cleanup( ledger_args );
442 :
443 0 : return ret;
444 0 : }
445 :
446 : /***************************** Helpers ****************************************/
447 : static fd_valloc_t
448 0 : allocator_setup( fd_wksp_t * wksp ) {
449 :
450 0 : if( FD_UNLIKELY( !wksp ) ) {
451 0 : FD_LOG_ERR(( "workspace is NULL" ));
452 0 : }
453 :
454 0 : void * alloc_shmem = fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 3UL );
455 0 : if( FD_UNLIKELY( !alloc_shmem ) ) { FD_LOG_ERR( ( "fd_alloc too large for workspace" ) ); }
456 0 : void * alloc_shalloc = fd_alloc_new( alloc_shmem, 3UL );
457 0 : if( FD_UNLIKELY( !alloc_shalloc ) ) { FD_LOG_ERR( ( "fd_alloc_new failed" ) ); }
458 0 : fd_alloc_t * alloc = fd_alloc_join( alloc_shalloc, 3UL );
459 0 : if( FD_UNLIKELY( !alloc ) ) { FD_LOG_ERR( ( "fd_alloc_join failed" ) ); }
460 0 : fd_valloc_t valloc = fd_alloc_virtual( alloc );
461 0 : return valloc;
462 :
463 : /* NOTE: Enable this if leak hunting */
464 : //return fd_backtracing_alloc_virtual( &valloc );
465 :
466 0 : }
467 :
468 : void
469 0 : fd_ledger_capture_setup( fd_ledger_args_t * args ) {
470 0 : fd_flamenco_boot( NULL, NULL );
471 :
472 : /* Setup capture context */
473 0 : int has_solcap = args->capture_fpath && args->capture_fpath[0] != '\0';
474 0 : int has_checkpt = args->checkpt_path && args->checkpt_path[0] != '\0';
475 0 : int has_checkpt_funk = args->checkpt_funk && args->checkpt_funk[0] != '\0';
476 0 : int has_dump_to_protobuf = args->dump_instr_to_pb || args->dump_txn_to_pb || args->dump_block_to_pb || args->dump_syscall_to_pb || args->dump_elf_to_pb;
477 :
478 0 : if( has_solcap || has_checkpt || has_checkpt_funk || has_dump_to_protobuf ) {
479 0 : FILE * capture_file = NULL;
480 :
481 0 : void * capture_ctx_mem = fd_valloc_malloc( args->valloc, FD_CAPTURE_CTX_ALIGN, FD_CAPTURE_CTX_FOOTPRINT );
482 0 : FD_TEST( capture_ctx_mem );
483 0 : fd_memset( capture_ctx_mem, 0, sizeof( fd_capture_ctx_t ) );
484 0 : args->capture_ctx = fd_capture_ctx_new( capture_ctx_mem );
485 :
486 0 : args->capture_ctx->checkpt_freq = ULONG_MAX;
487 0 : args->capture_ctx->solcap_start_slot = args->solcap_start_slot;
488 :
489 0 : if( has_solcap ) {
490 0 : capture_file = fopen( args->capture_fpath, "w+" );
491 0 : if( FD_UNLIKELY( !capture_file ) ) {
492 0 : FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", args->capture_fpath, errno, strerror( errno ) ));
493 0 : }
494 0 : fd_solcap_writer_init( args->capture_ctx->capture, capture_file );
495 0 : args->capture_ctx->capture_txns = args->capture_txns;
496 0 : } else {
497 0 : args->capture_ctx->capture = NULL;
498 0 : }
499 :
500 0 : if( has_checkpt || has_checkpt_funk ) {
501 0 : args->capture_ctx->checkpt_path = ( has_checkpt ? args->checkpt_path : args->checkpt_funk );
502 0 : args->capture_ctx->checkpt_freq = args->checkpt_freq;
503 0 : }
504 0 : if( has_dump_to_protobuf ) {
505 0 : args->capture_ctx->dump_instr_to_pb = args->dump_instr_to_pb;
506 0 : args->capture_ctx->dump_txn_to_pb = args->dump_txn_to_pb;
507 0 : args->capture_ctx->dump_block_to_pb = args->dump_block_to_pb;
508 0 : args->capture_ctx->dump_syscall_to_pb = args->dump_syscall_to_pb;
509 0 : args->capture_ctx->dump_elf_to_pb = args->dump_elf_to_pb;
510 0 : args->capture_ctx->dump_proto_sig_filter = args->dump_proto_sig_filter;
511 0 : args->capture_ctx->dump_proto_output_dir = args->dump_proto_output_dir;
512 0 : args->capture_ctx->dump_proto_start_slot = args->dump_proto_start_slot;
513 0 : }
514 0 : }
515 0 : }
516 :
517 : void
518 0 : fd_ledger_main_setup( fd_ledger_args_t * args ) {
519 0 : fd_flamenco_boot( NULL, NULL );
520 :
521 : /* Finish other runtime setup steps */
522 0 : fd_features_restore( args->slot_ctx, args->runtime_spad );
523 0 : fd_runtime_update_leaders( args->slot_ctx->bank, args->slot_ctx->slot, args->runtime_spad );
524 0 : fd_calculate_epoch_accounts_hash_values( args->slot_ctx );
525 :
526 : /* After both snapshots have been loaded in, we can determine if we should
527 : start distributing rewards. */
528 :
529 0 : fd_rewards_recalculate_partitioned_rewards( args->slot_ctx,
530 0 : args->tpool,
531 0 : args->exec_spads,
532 0 : args->exec_spad_cnt,
533 0 : args->runtime_spad );
534 :
535 0 : }
536 :
537 : void
538 0 : fd_ledger_main_teardown( fd_ledger_args_t * args ) {
539 : /* Flush solcap file and cleanup */
540 0 : if( args->capture_ctx && args->capture_ctx->capture ) {
541 0 : fd_solcap_writer_flush( args->capture_ctx->capture );
542 0 : fd_solcap_writer_delete( args->capture_ctx->capture );
543 0 : }
544 :
545 0 : fd_exec_slot_ctx_delete( fd_exec_slot_ctx_leave( args->slot_ctx ) );
546 0 : }
547 :
548 : void
549 : ingest_rocksdb( char const * file,
550 : ulong start_slot,
551 : ulong end_slot,
552 : fd_blockstore_t * blockstore,
553 : int txn_status,
554 : ulong trash_hash,
555 0 : fd_valloc_t valloc ) {
556 :
557 0 : fd_rocksdb_t rocks_db;
558 0 : char * err = fd_rocksdb_init( &rocks_db, file );
559 0 : if( FD_UNLIKELY( err!=NULL ) ) {
560 0 : FD_LOG_ERR(( "fd_rocksdb_init returned %s", err ));
561 0 : }
562 :
563 0 : ulong last_slot = fd_rocksdb_last_slot( &rocks_db, &err );
564 0 : if( FD_UNLIKELY( err!=NULL ) ) {
565 0 : FD_LOG_ERR(( "fd_rocksdb_last_slot returned %s", err ));
566 0 : }
567 :
568 0 : if( last_slot < start_slot ) {
569 0 : FD_LOG_ERR(( "rocksdb blocks are older than snapshot. first=%lu last=%lu wanted=%lu",
570 0 : fd_rocksdb_first_slot(&rocks_db, &err), last_slot, start_slot ));
571 0 : }
572 :
573 0 : FD_LOG_NOTICE(( "ingesting rocksdb from start=%lu to end=%lu", start_slot, end_slot ));
574 :
575 0 : fd_rocksdb_root_iter_t iter = {0};
576 0 : fd_rocksdb_root_iter_new( &iter );
577 :
578 0 : fd_slot_meta_t slot_meta = {0};
579 0 : fd_memset( &slot_meta, 0, sizeof(slot_meta) );
580 :
581 0 : int block_found = -1;
582 0 : while ( block_found!=0 && start_slot<=end_slot ) {
583 0 : block_found = fd_rocksdb_root_iter_seek( &iter, &rocks_db, start_slot, &slot_meta, valloc );
584 0 : if ( block_found!=0 ) {
585 0 : start_slot++;
586 0 : }
587 0 : }
588 0 : if( FD_UNLIKELY( block_found!=0 ) ) {
589 0 : FD_LOG_ERR(( "unable to seek to any slot" ));
590 0 : }
591 :
592 0 : uchar trash_hash_buf[32];
593 0 : memset( trash_hash_buf, 0xFE, sizeof(trash_hash_buf) );
594 :
595 0 : ulong blk_cnt = 0;
596 0 : do {
597 0 : ulong slot = slot_meta.slot;
598 0 : if( slot > end_slot ) {
599 0 : break;
600 0 : }
601 :
602 : /* Read and deshred block from RocksDB */
603 0 : if( blk_cnt % 100 == 0 ) {
604 0 : FD_LOG_WARNING(( "imported %lu blocks", blk_cnt ));
605 0 : }
606 :
607 0 : int err = fd_rocksdb_import_block_blockstore( &rocks_db,
608 0 : &slot_meta,
609 0 : blockstore,
610 0 : txn_status,
611 0 : (slot == trash_hash) ? trash_hash_buf : NULL,
612 0 : valloc );
613 0 : if( FD_UNLIKELY( err ) ) {
614 0 : FD_LOG_ERR(( "fd_rocksdb_get_block failed" ));
615 0 : }
616 :
617 0 : ++blk_cnt;
618 :
619 0 : memset( &slot_meta, 0, sizeof(fd_slot_meta_t) );
620 :
621 0 : int ret = fd_rocksdb_root_iter_next( &iter, &slot_meta, valloc );
622 0 : if( ret < 0 ) {
623 : // FD_LOG_WARNING(("Failed for slot %lu", slot + 1));
624 0 : ret = fd_rocksdb_get_meta( &rocks_db, slot + 1, &slot_meta, valloc );
625 0 : if( ret < 0 ) {
626 0 : break;
627 0 : }
628 0 : }
629 : // FD_LOG_ERR(("fd_rocksdb_root_iter_seek returned %d", ret));
630 0 : } while (1);
631 :
632 0 : fd_rocksdb_root_iter_destroy( &iter );
633 0 : fd_rocksdb_destroy( &rocks_db );
634 :
635 0 : FD_LOG_NOTICE(( "ingested %lu blocks", blk_cnt ));
636 0 : }
637 :
638 : void
639 0 : parse_one_off_features( fd_ledger_args_t * args, char const * one_off_features ) {
640 0 : if( !one_off_features ) {
641 0 : FD_LOG_NOTICE(( "No one-off features passed in" ));
642 0 : return;
643 0 : }
644 :
645 0 : char * one_off_features_str = strdup( one_off_features );
646 0 : args->one_off_features_strdup = one_off_features_str;
647 0 : char * token = NULL;
648 0 : token = strtok( one_off_features_str, "," );
649 0 : while( token ) {
650 0 : args->one_off_features[ args->one_off_features_cnt++ ] = token;
651 0 : token = strtok( NULL, "," );
652 0 : }
653 :
654 0 : FD_LOG_NOTICE(( "Found %u one off features to include", args->one_off_features_cnt ));
655 0 : }
656 :
657 : void
658 : parse_rocksdb_list( fd_ledger_args_t * args,
659 : char const * rocksdb_list,
660 0 : char const * rocksdb_start_slots ) {
661 : /* First parse the paths to the different rocksdb */
662 0 : if( FD_UNLIKELY( !rocksdb_list ) ) {
663 0 : FD_LOG_NOTICE(( "No rocksdb list passed in" ));
664 0 : return;
665 0 : }
666 :
667 0 : char * rocksdb_str = strdup( rocksdb_list );
668 0 : args->rocksdb_list_strdup = rocksdb_str;
669 0 : char * token = NULL;
670 0 : token = strtok( rocksdb_str, "," );
671 0 : while( token ) {
672 0 : args->rocksdb_list[ args->rocksdb_list_cnt++ ] = token;
673 0 : token = strtok( NULL, "," );
674 0 : }
675 :
676 : /* Now repeat for the start slots assuming there are multiple */
677 0 : if( rocksdb_start_slots == NULL && args->rocksdb_list_cnt > 1 ) {
678 0 : FD_LOG_ERR(( "Multiple rocksdb dirs passed in but no start slots" ));
679 0 : }
680 0 : ulong index = 0UL;
681 0 : if( rocksdb_start_slots ) {
682 0 : char * rocksdb_start_slot_str = strdup( rocksdb_start_slots );
683 0 : token = NULL;
684 0 : token = strtok( rocksdb_start_slot_str, "," );
685 0 : while( token ) {
686 0 : args->rocksdb_list_slot[ index++ ] = strtoul( token, NULL, 10 );
687 0 : token = strtok( NULL, "," );
688 0 : }
689 0 : }
690 :
691 0 : if( index != args->rocksdb_list_cnt - 1UL ) {
692 0 : FD_LOG_ERR(( "Number of rocksdb dirs passed in doesn't match number of start slots" ));
693 0 : }
694 0 : }
695 :
696 : void
697 0 : init_funk( fd_ledger_args_t * args ) {
698 0 : ulong funk_tag = 42UL;
699 0 : void * funk_shmem = fd_funk_new( fd_wksp_alloc_laddr(
700 0 : args->funk_wksp,
701 0 : fd_funk_align(),
702 0 : fd_funk_footprint( args->txns_max, args->index_max ),
703 0 : funk_tag
704 0 : ),
705 0 : funk_tag,
706 0 : args->hashseed,
707 0 : args->txns_max,
708 0 : args->index_max
709 0 : );
710 0 : if( FD_UNLIKELY( !funk_shmem ) ) {
711 0 : FD_LOG_ERR(( "Failed to allocate shmem for funk" ));
712 0 : }
713 0 : fd_funk_join( args->funk, funk_shmem );
714 0 : FD_LOG_NOTICE(( "Funk database is at %s:0x%lx", fd_wksp_name( args->wksp ), fd_wksp_gaddr_fast( args->funk_wksp, args->funk ) ));
715 0 : }
716 :
717 : void
718 0 : init_blockstore( fd_ledger_args_t * args ) {
719 0 : fd_wksp_tag_query_info_t info;
720 0 : ulong blockstore_tag = FD_BLOCKSTORE_MAGIC;
721 0 : void * shmem;
722 0 : if( fd_wksp_tag_query( args->wksp, &blockstore_tag, 1, &info, 1 ) > 0 ) {
723 0 : shmem = fd_wksp_laddr_fast( args->wksp, info.gaddr_lo );
724 0 : args->blockstore = fd_blockstore_join( &args->blockstore_ljoin, shmem );
725 0 : if( args->blockstore->shmem->magic != FD_BLOCKSTORE_MAGIC ) {
726 0 : FD_LOG_ERR(( "failed to join a blockstore" ));
727 0 : }
728 0 : FD_LOG_NOTICE(( "joined blockstore" ));
729 0 : } else {
730 0 : ulong txn_max = 256UL;
731 0 : shmem = fd_wksp_alloc_laddr( args->wksp, fd_blockstore_align(), fd_blockstore_footprint( args->shred_max, args->slot_history_max, 16, txn_max ), blockstore_tag );
732 0 : if( shmem == NULL ) {
733 0 : FD_LOG_ERR(( "failed to allocate a blockstore" ));
734 0 : }
735 0 : args->blockstore = fd_blockstore_join( &args->blockstore_ljoin, fd_blockstore_new( shmem, 1, args->hashseed, args->shred_max, args->slot_history_max, 16, txn_max ) );
736 0 : if( args->blockstore->shmem->magic != FD_BLOCKSTORE_MAGIC ) {
737 0 : fd_wksp_free_laddr( shmem );
738 0 : FD_LOG_ERR(( "failed to allocate a blockstore" ));
739 0 : }
740 0 : FD_LOG_NOTICE(( "allocating a new blockstore" ));
741 0 : }
742 0 : }
743 :
744 : void
745 0 : checkpt( fd_ledger_args_t * args ) {
746 0 : if( !args->checkpt && !args->checkpt_funk && !args->checkpt_status_cache ) {
747 0 : FD_LOG_WARNING(( "No checkpt argument specified" ));
748 0 : }
749 :
750 0 : if( args->checkpt_funk ) {
751 0 : if( args->funk_wksp == NULL ) {
752 0 : FD_LOG_ERR(( "funk_wksp is NULL" ));
753 0 : }
754 0 : FD_LOG_NOTICE(( "writing funk checkpt %s", args->checkpt_funk ));
755 0 : unlink( args->checkpt_funk );
756 0 : int err = fd_wksp_checkpt( args->funk_wksp, args->checkpt_funk, 0666, 0, NULL );
757 0 : if( err ) {
758 0 : FD_LOG_ERR(( "funk checkpt failed: error %d", err ));
759 0 : }
760 0 : }
761 0 : if( args->checkpt ) {
762 0 : FD_LOG_NOTICE(( "writing %s", args->checkpt ));
763 0 : unlink( args->checkpt );
764 0 : int err = fd_wksp_checkpt( args->wksp, args->checkpt, 0666, 0, NULL );
765 0 : if( err ) {
766 0 : FD_LOG_ERR(( "checkpt failed: error %d", err ));
767 0 : }
768 0 : }
769 0 : if( args->checkpt_status_cache ) {
770 0 : FD_LOG_NOTICE(( "writing %s", args->checkpt_status_cache ));
771 0 : unlink( args->checkpt_status_cache );
772 0 : int err = fd_wksp_checkpt( args->status_cache_wksp, args->checkpt_status_cache, 0666, 0, NULL );
773 0 : if( err ) {
774 0 : FD_LOG_ERR(( "status cache checkpt failed: error %d", err ));
775 0 : }
776 0 : }
777 0 : }
778 :
779 : void
780 0 : wksp_restore( fd_ledger_args_t * args ) {
781 0 : if( args->restore != NULL ) {
782 0 : FD_LOG_NOTICE(( "restoring wksp %s", args->restore ));
783 0 : fd_wksp_restore( args->wksp, args->restore, args->hashseed );
784 0 : }
785 0 : }
786 :
787 : /********************* Main Command Functions and Setup ***********************/
788 : void
789 0 : minify( fd_ledger_args_t * args ) {
790 : /* Example commmand:
791 : fd_ledger --cmd minify --rocksdb <LARGE_ROCKSDB> --minified-rocksdb <MINI_ROCKSDB>
792 : --start-slot <START_SLOT> --end-slot <END_SLOT> --copy-txn-status 1
793 : */
794 0 : if( args->rocksdb_list[ 0UL ] == NULL ) {
795 0 : FD_LOG_ERR(( "rocksdb path is NULL" ));
796 0 : }
797 0 : if( args->mini_db_dir == NULL ) {
798 0 : FD_LOG_ERR(( "minified rocksdb path is NULL" ));
799 0 : }
800 :
801 0 : args->valloc = allocator_setup( args->wksp );
802 0 : init_exec_spads( args, 0 );
803 :
804 0 : fd_rocksdb_t big_rocksdb;
805 0 : char * err = fd_rocksdb_init( &big_rocksdb, args->rocksdb_list[ 0UL ] );
806 0 : if( FD_UNLIKELY( err!=NULL ) ) {
807 0 : FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", args->rocksdb_list[ 0UL ], err ));
808 0 : }
809 :
810 : /* If the directory for the minified rocksdb already exists, error out */
811 0 : struct stat statbuf;
812 0 : if( stat( args->mini_db_dir, &statbuf ) == 0 ) {
813 0 : FD_LOG_ERR(( "path for mini_db_dir=%s already exists", args->mini_db_dir ));
814 0 : }
815 :
816 : /* Create a new smaller rocksdb */
817 0 : fd_rocksdb_t mini_rocksdb;
818 0 : fd_rocksdb_new( &mini_rocksdb, args->mini_db_dir );
819 :
820 : /* Correctly bound off start and end slot */
821 0 : ulong first_slot = fd_rocksdb_first_slot( &big_rocksdb, &err );
822 0 : ulong last_slot = fd_rocksdb_last_slot( &big_rocksdb, &err );
823 0 : if( args->start_slot < first_slot ) { args->start_slot = first_slot; }
824 0 : if( args->end_slot > last_slot ) { args->end_slot = last_slot; }
825 :
826 0 : FD_LOG_NOTICE(( "copying over rocks db for range [%lu, %lu]", args->start_slot, args->end_slot ));
827 :
828 : /* Copy over all slot indexed columns */
829 0 : for( ulong cf_idx = 1; cf_idx < FD_ROCKSDB_CF_CNT; ++cf_idx ) {
830 0 : fd_rocksdb_copy_over_slot_indexed_range( &big_rocksdb, &mini_rocksdb, cf_idx,
831 0 : args->start_slot, args->end_slot );
832 0 : }
833 0 : FD_LOG_NOTICE(("copied over all slot indexed columns"));
834 :
835 : /* Copy over transactions. This is more complicated because first, a temporary
836 : blockstore will be populated. This will be used to look up transactions
837 : which can be quickly queried */
838 0 : if( args->copy_txn_status ) {
839 0 : init_blockstore( args );
840 : /* Ingest block range into blockstore */
841 0 : ingest_rocksdb( args->rocksdb_list[ 0UL ],
842 0 : args->start_slot,
843 0 : args->end_slot,
844 0 : args->blockstore,
845 0 : 0,
846 0 : ULONG_MAX,
847 0 : args->valloc );
848 :
849 0 : fd_rocksdb_copy_over_txn_status_range( &big_rocksdb, &mini_rocksdb, args->blockstore,
850 0 : args->start_slot, args->end_slot );
851 0 : FD_LOG_NOTICE(( "copied over all transaction statuses" ));
852 0 : } else {
853 0 : FD_LOG_NOTICE(( "skipping copying of transaction statuses" ));
854 0 : }
855 :
856 : /* TODO: Currently, the address signatures column family isn't copied as it
857 : is indexed on the pubkey. */
858 :
859 0 : fd_rocksdb_destroy( &big_rocksdb );
860 0 : fd_rocksdb_destroy( &mini_rocksdb );
861 0 : }
862 :
863 : void
864 0 : ingest( fd_ledger_args_t * args ) {
865 : /* Setup funk, blockstore, and slot_ctx */
866 0 : wksp_restore( args );
867 0 : init_funk( args );
868 0 : if( !args->funk_only ) {
869 0 : init_blockstore( args );
870 0 : }
871 :
872 0 : init_tpool( args );
873 0 : init_exec_spads( args, 1 );
874 :
875 0 : fd_funk_t * funk = args->funk;
876 :
877 0 : args->valloc = allocator_setup( args->wksp );
878 :
879 0 : uchar slot_ctx_mem[FD_EXEC_SLOT_CTX_FOOTPRINT] __attribute__((aligned(FD_EXEC_SLOT_CTX_ALIGN)));
880 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem ) );
881 0 : args->slot_ctx = slot_ctx;
882 :
883 0 : slot_ctx->funk = funk;
884 :
885 : // if( args->status_cache_wksp ) {
886 : // void * status_cache_mem = fd_spad_alloc_check( spad,
887 : // fd_txncache_align(),
888 : // fd_txncache_footprint( FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS,
889 : // FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS,
890 : // MAX_CACHE_TXNS_PER_SLOT,
891 : // FD_TXNCACHE_DEFAULT_MAX_CONSTIPATED_SLOTS ) );
892 : // FD_TEST( status_cache_mem );
893 : // slot_ctx->status_cache = fd_txncache_join( fd_txncache_new( status_cache_mem,
894 : // FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS,
895 : // FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS,
896 : // MAX_CACHE_TXNS_PER_SLOT,
897 : // FD_TXNCACHE_DEFAULT_MAX_CONSTIPATED_SLOTS ) );
898 : // FD_TEST( slot_ctx->status_cache );
899 : // }
900 :
901 : /* Load in snapshot(s) */
902 0 : if( args->snapshot ) {
903 0 : fd_snapshot_load_all( args->snapshot,
904 0 : FD_SNAPSHOT_SRC_FILE,
905 0 : NULL,
906 0 : slot_ctx,
907 0 : NULL,
908 0 : args->tpool,
909 0 : args->verify_acc_hash,
910 0 : args->check_acc_hash ,
911 0 : FD_SNAPSHOT_TYPE_FULL,
912 0 : args->exec_spads,
913 0 : args->exec_spad_cnt,
914 0 : args->runtime_spad );
915 0 : FD_LOG_NOTICE(( "imported records from snapshot" ));
916 0 : }
917 0 : if( args->incremental ) {
918 0 : fd_snapshot_load_all( args->incremental,
919 0 : FD_SNAPSHOT_SRC_FILE,
920 0 : NULL,
921 0 : slot_ctx,
922 0 : NULL,
923 0 : args->tpool,
924 0 : args->verify_acc_hash,
925 0 : args->check_acc_hash,
926 0 : FD_SNAPSHOT_TYPE_INCREMENTAL,
927 0 : args->exec_spads,
928 0 : args->exec_spad_cnt,
929 0 : args->runtime_spad );
930 0 : FD_LOG_NOTICE(( "imported records from incremental snapshot" ));
931 0 : }
932 :
933 0 : if( args->genesis ) {
934 0 : fd_runtime_read_genesis( slot_ctx, args->genesis, args->snapshot != NULL, NULL, args->runtime_spad );
935 0 : }
936 :
937 : /* At this point the account state has been ingested into funk. Intake rocksdb */
938 0 : if( args->start_slot == 0 ) {
939 0 : args->start_slot = slot_ctx->slot + 1;
940 0 : }
941 0 : fd_blockstore_t * blockstore = args->blockstore;
942 0 : if( blockstore ) {
943 0 : blockstore->shmem->lps = blockstore->shmem->hcs = blockstore->shmem->wmk = slot_ctx->slot;
944 0 : }
945 :
946 0 : if( args->funk_only ) {
947 0 : FD_LOG_NOTICE(( "using funk only, skipping blockstore ingest" ));
948 0 : } else if( args->shredcap ) {
949 0 : FD_LOG_NOTICE(( "using shredcap" ));
950 0 : fd_shredcap_populate_blockstore( args->shredcap, blockstore, args->start_slot, args->end_slot );
951 0 : } else if( args->rocksdb_list[ 0UL ] ) {
952 0 : if( args->end_slot >= slot_ctx->slot + args->slot_history_max ) {
953 0 : args->end_slot = slot_ctx->slot + args->slot_history_max - 1;
954 0 : }
955 0 : ingest_rocksdb( args->rocksdb_list[ 0UL ], args->start_slot, args->end_slot,
956 0 : blockstore, args->copy_txn_status, args->trash_hash, args->valloc );
957 0 : }
958 :
959 : #ifdef FD_FUNK_HANDHOLDING
960 : if( args->verify_funk ) {
961 : FD_LOG_NOTICE(( "fd_funk_verify() start" ));
962 : if( fd_funk_verify( funk ) ) {
963 : FD_LOG_ERR(( "fd_funk_verify() failed" ));
964 : }
965 : }
966 : #endif
967 :
968 0 : checkpt( args );
969 0 : }
970 :
971 : int
972 0 : replay( fd_ledger_args_t * args ) {
973 : /* Allows for ingest and direct replay. This can be done with a full checkpoint
974 : that contains a blockstore and funk, a checkpoint that just has funk, or directly
975 : using a rocksdb and snapshot.
976 :
977 : On demand block ingest is enabled by default and can be disabled with
978 : '--on-demand-block-ingest 0'. The number of blocks retained in a blockstore during
979 : on demand block ingest can be set with '--on-demand-block-history <N slots>'
980 :
981 : In order to replay from a checkpoint, use '--checkpoint <path to checkpoint>'.
982 :
983 : To use a checkpoint, but to consume blocks on demand use '--funkonly true'.
984 : This option MUST be used if the checkpoint was generated during a replay with
985 : on demand block ingest.
986 :
987 : For blocks to contain transaction status information use '--txnstatus true'
988 :
989 : Example command loading in from on demand checkpoint and replaying with on demand block ingest.
990 : It creates a checkpoint every 1000 slots.
991 : fd_ledger --funk-restore <CHECKPOINT_TO_LOAD_IN> --cmd replay --page-cnt 20
992 : --abort-on-mismatch 1 --tile-cpus 5-21 --allocator wksp
993 : --rocksdb dump/rocksdb --checkpt-path dump/checkpoint_new
994 : --checkpt-freq 1000 --funk-only 1 --on-demand-block-ingest 1 --funk-page-cnt 350
995 :
996 : Example command directly loading in a rocksdb and snapshot and replaying.
997 : fd_ledger --reset 1 --cmd replay --rocksdb dump/mainnet-257068890/rocksdb --index-max 5000000
998 : --end-slot 257068895 --txn-max 100 --page-cnt 16 --verify-acc-hash 1
999 : --snapshot dump/mainnet-257068890/snapshot-257068890-uRVtagPzKhYorycp4CRtKdWrYPij6iBxCYYXmqRvdSp.tar.zst
1000 : --slot-history 5000 --allocator wksp --tile-cpus 5-21 --funk-page-cnt 16
1001 : */
1002 :
1003 0 : args->valloc = allocator_setup( args->wksp );
1004 :
1005 0 : wksp_restore( args ); /* Restores checkpointed workspace(s) */
1006 :
1007 0 : init_funk( args ); /* Joins or creates funk based on if one exists in the workspace */
1008 0 : init_blockstore( args ); /* Does the same for the blockstore */
1009 :
1010 0 : init_tpool( args ); /* Sets up tpool */
1011 0 : init_exec_spads( args, 1 ); /* Sets up spad */
1012 :
1013 0 : uchar * banks_mem = fd_wksp_alloc_laddr( args->wksp, fd_banks_align(), fd_banks_footprint( 8UL ), 0xABCABC123 );
1014 0 : fd_banks_t * banks = fd_banks_join( fd_banks_new( banks_mem, 8UL ) );
1015 0 : FD_TEST( banks );
1016 :
1017 0 : void * runtime_public_mem = fd_wksp_alloc_laddr( args->wksp,
1018 0 : fd_runtime_public_align(),
1019 0 : fd_runtime_public_footprint( args->runtime_mem_bound ), 0x3E64F44C9F44366AUL );
1020 0 : if( FD_UNLIKELY( !runtime_public_mem ) ) {
1021 0 : FD_LOG_ERR(( "Unable to allocate runtime_public mem" ));
1022 0 : }
1023 :
1024 0 : fd_runtime_public_t * runtime_public = fd_runtime_public_join( fd_runtime_public_new( runtime_public_mem, args->runtime_mem_bound ) );
1025 0 : args->runtime_spad = fd_spad_join( fd_wksp_laddr( args->wksp, runtime_public->runtime_spad_gaddr ) );
1026 0 : if( FD_UNLIKELY( !args->runtime_spad ) ) {
1027 0 : FD_LOG_ERR(( "Unable to join runtime spad" ));
1028 0 : }
1029 :
1030 0 : fd_spad_t * spad = args->runtime_spad;
1031 :
1032 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1033 :
1034 : /* Setup slot_ctx */
1035 0 : fd_funk_t * funk = args->funk;
1036 :
1037 : /* TODO: This is very hacky, needs to be cleaned up */
1038 :
1039 0 : void * slot_ctx_mem = fd_spad_alloc_check( spad, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
1040 0 : args->slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem ) );
1041 0 : args->slot_ctx->funk = funk;
1042 :
1043 0 : args->slot_ctx->banks = banks;
1044 0 : FD_TEST( args->slot_ctx->banks );
1045 :
1046 0 : args->slot_ctx->bank = fd_banks_init_bank( args->slot_ctx->banks, 0UL );
1047 :
1048 0 : fd_cluster_version_t * cluster_version = fd_bank_cluster_version_modify( args->slot_ctx->bank );
1049 0 : cluster_version->major = args->cluster_version[0];
1050 0 : cluster_version->minor = args->cluster_version[1];
1051 0 : cluster_version->patch = args->cluster_version[2];
1052 :
1053 0 : fd_features_t * features = fd_bank_features_modify( args->slot_ctx->bank );
1054 :
1055 0 : fd_features_enable_cleaned_up( features, fd_bank_cluster_version_query( args->slot_ctx->bank ) );
1056 0 : fd_features_enable_one_offs( features, args->one_off_features, args->one_off_features_cnt, 0UL );
1057 :
1058 : // void * status_cache_mem = fd_spad_alloc_check( spad,
1059 : // FD_TXNCACHE_ALIGN,
1060 : // fd_txncache_footprint( FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS,
1061 : // FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS,
1062 : // MAX_CACHE_TXNS_PER_SLOT,
1063 : // FD_TXNCACHE_DEFAULT_MAX_CONSTIPATED_SLOTS) );
1064 : // args->slot_ctx->status_cache = fd_txncache_join( fd_txncache_new( status_cache_mem,
1065 : // FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS,
1066 : // FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS,
1067 : // MAX_CACHE_TXNS_PER_SLOT,
1068 : // FD_TXNCACHE_DEFAULT_MAX_CONSTIPATED_SLOTS ) );
1069 : // if( FD_UNLIKELY( !args->slot_ctx->status_cache ) ) {
1070 : // FD_LOG_ERR(( "Status cache was not allocated" ));
1071 : // }
1072 :
1073 : /* Check number of records in funk. If rec_cnt == 0, then it can be assumed
1074 : that you need to load in snapshot(s). */
1075 :
1076 : /* Load in snapshot(s) */
1077 0 : if( args->snapshot ) {
1078 0 : fd_snapshot_load_all( args->snapshot,
1079 0 : FD_SNAPSHOT_SRC_FILE,
1080 0 : NULL,
1081 0 : args->slot_ctx,
1082 0 : NULL,
1083 0 : args->tpool,
1084 0 : args->verify_acc_hash,
1085 0 : args->check_acc_hash,
1086 0 : FD_SNAPSHOT_TYPE_FULL,
1087 0 : args->exec_spads,
1088 0 : args->exec_spad_cnt,
1089 0 : args->runtime_spad );
1090 0 : FD_LOG_NOTICE(( "imported from snapshot" ));
1091 0 : if( args->incremental ) {
1092 0 : fd_snapshot_load_all( args->incremental,
1093 0 : FD_SNAPSHOT_SRC_FILE,
1094 0 : NULL,
1095 0 : args->slot_ctx,
1096 0 : NULL,
1097 0 : args->tpool,
1098 0 : args->verify_acc_hash,
1099 0 : args->check_acc_hash,
1100 0 : FD_SNAPSHOT_TYPE_INCREMENTAL,
1101 0 : args->exec_spads,
1102 0 : args->exec_spad_cnt,
1103 0 : args->runtime_spad );
1104 0 : FD_LOG_NOTICE(( "imported from snapshot" ));
1105 0 : }
1106 0 : }
1107 :
1108 0 : FD_LOG_NOTICE(( "Used memory in spad after loading in snapshot %lu", args->runtime_spad->mem_used ));
1109 :
1110 0 : fd_ledger_capture_setup( args );
1111 :
1112 0 : if( args->genesis ) {
1113 0 : fd_runtime_read_genesis( args->slot_ctx, args->genesis, args->snapshot != NULL, args->capture_ctx, args->runtime_spad );
1114 0 : }
1115 :
1116 0 : fd_ledger_main_setup( args );
1117 :
1118 0 : fd_blockstore_init( args->blockstore,
1119 0 : -1,
1120 0 : FD_BLOCKSTORE_ARCHIVE_MIN_SIZE,
1121 0 : args->slot_ctx->slot );
1122 0 : fd_buf_shred_pool_reset( args->blockstore->shred_pool, 0 );
1123 :
1124 0 : FD_LOG_WARNING(( "setup done" ));
1125 :
1126 0 : int ret = runtime_replay( args );
1127 :
1128 0 : fd_ledger_main_teardown( args );
1129 :
1130 0 : return ret;
1131 :
1132 0 : } FD_SPAD_FRAME_END;
1133 0 : }
1134 :
1135 : /* Parse user arguments and setup shared data structures used across commands */
1136 : int
1137 0 : initial_setup( int argc, char ** argv, fd_ledger_args_t * args ) {
1138 0 : if( FD_UNLIKELY( argc==1 ) ) {
1139 0 : return 1;
1140 0 : }
1141 :
1142 0 : fd_boot( &argc, &argv );
1143 0 : fd_flamenco_boot( &argc, &argv );
1144 :
1145 0 : char const * wksp_name = fd_env_strip_cmdline_cstr ( &argc, &argv, "--wksp-name", NULL, NULL );
1146 0 : ulong funk_page_cnt = fd_env_strip_cmdline_ulong ( &argc, &argv, "--funk-page-cnt", NULL, 5 );
1147 0 : ulong page_cnt = fd_env_strip_cmdline_ulong ( &argc, &argv, "--page-cnt", NULL, 5 );
1148 0 : int reset = fd_env_strip_cmdline_int ( &argc, &argv, "--reset", NULL, 0 );
1149 0 : char const * cmd = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cmd", NULL, NULL );
1150 0 : uint index_max = fd_env_strip_cmdline_uint ( &argc, &argv, "--index-max", NULL, 450000000 );
1151 0 : ulong txns_max = fd_env_strip_cmdline_ulong ( &argc, &argv, "--txn-max", NULL, 100 );
1152 0 : int verify_funk = fd_env_strip_cmdline_int ( &argc, &argv, "--verify-funky", NULL, 0 );
1153 0 : char const * snapshot = fd_env_strip_cmdline_cstr ( &argc, &argv, "--snapshot", NULL, NULL );
1154 0 : char const * incremental = fd_env_strip_cmdline_cstr ( &argc, &argv, "--incremental", NULL, NULL );
1155 0 : char const * genesis = fd_env_strip_cmdline_cstr ( &argc, &argv, "--genesis", NULL, NULL );
1156 0 : int copy_txn_status = fd_env_strip_cmdline_int ( &argc, &argv, "--copy-txn-status", NULL, 0 );
1157 0 : ulong slot_history_max = fd_env_strip_cmdline_ulong ( &argc, &argv, "--slot-history", NULL, 100UL );
1158 0 : ulong shred_max = fd_env_strip_cmdline_ulong ( &argc, &argv, "--shred-max", NULL, 1UL << 17 );
1159 0 : ulong start_slot = fd_env_strip_cmdline_ulong ( &argc, &argv, "--start-slot", NULL, 0UL );
1160 0 : ulong end_slot = fd_env_strip_cmdline_ulong ( &argc, &argv, "--end-slot", NULL, ULONG_MAX );
1161 0 : uint verify_acc_hash = fd_env_strip_cmdline_uint ( &argc, &argv, "--verify-acc-hash", NULL, 1 );
1162 0 : uint check_acc_hash = fd_env_strip_cmdline_uint ( &argc, &argv, "--check-acc-hash", NULL, 1 );
1163 0 : char const * restore = fd_env_strip_cmdline_cstr ( &argc, &argv, "--restore", NULL, NULL );
1164 0 : char const * shredcap = fd_env_strip_cmdline_cstr ( &argc, &argv, "--shred-cap", NULL, NULL );
1165 0 : ulong trash_hash = fd_env_strip_cmdline_ulong ( &argc, &argv, "--trash-hash", NULL, ULONG_MAX );
1166 0 : char const * mini_db_dir = fd_env_strip_cmdline_cstr ( &argc, &argv, "--minified-rocksdb", NULL, NULL );
1167 0 : int funk_only = fd_env_strip_cmdline_int ( &argc, &argv, "--funk-only", NULL, 0 );
1168 0 : char const * checkpt = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt", NULL, NULL );
1169 0 : char const * checkpt_funk = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-funk", NULL, NULL );
1170 0 : char const * capture_fpath = fd_env_strip_cmdline_cstr ( &argc, &argv, "--capture-solcap", NULL, NULL );
1171 0 : ulong solcap_start_slot = fd_env_strip_cmdline_ulong ( &argc, &argv, "--solcap-start-slot", NULL, 0 );
1172 0 : int capture_txns = fd_env_strip_cmdline_int ( &argc, &argv, "--capture-txns", NULL, 1 );
1173 0 : char const * checkpt_path = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-path", NULL, NULL );
1174 0 : ulong checkpt_freq = fd_env_strip_cmdline_ulong ( &argc, &argv, "--checkpt-freq", NULL, ULONG_MAX );
1175 0 : int checkpt_mismatch = fd_env_strip_cmdline_int ( &argc, &argv, "--checkpt-mismatch", NULL, 0 );
1176 0 : char const * allocator = fd_env_strip_cmdline_cstr ( &argc, &argv, "--allocator", NULL, "wksp" );
1177 0 : int abort_on_mismatch = fd_env_strip_cmdline_int ( &argc, &argv, "--abort-on-mismatch", NULL, 1 );
1178 0 : int dump_instr_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-insn-to-pb", NULL, 0 );
1179 0 : int dump_txn_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-txn-to-pb", NULL, 0 );
1180 0 : int dump_block_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-block-to-pb", NULL, 0 );
1181 0 : int dump_syscall_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-syscall-to-pb", NULL, 0 );
1182 0 : int dump_elf_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-elf-to-pb", NULL, 0 );
1183 0 : ulong dump_proto_start_slot = fd_env_strip_cmdline_ulong ( &argc, &argv, "--dump-proto-start-slot", NULL, 0 );
1184 0 : char const * dump_proto_sig_filter = fd_env_strip_cmdline_cstr ( &argc, &argv, "--dump-proto-sig-filter", NULL, NULL );
1185 0 : char const * dump_proto_output_dir = fd_env_strip_cmdline_cstr ( &argc, &argv, "--dump-proto-output-dir", NULL, NULL );
1186 0 : ulong vote_acct_max = fd_env_strip_cmdline_ulong ( &argc, &argv, "--vote_acct_max", NULL, 2000000UL );
1187 0 : char const * rocksdb_list = fd_env_strip_cmdline_cstr ( &argc, &argv, "--rocksdb", NULL, NULL );
1188 0 : char const * rocksdb_list_starts = fd_env_strip_cmdline_cstr ( &argc, &argv, "--rocksdb-starts", NULL, NULL );
1189 0 : char const * cluster_version = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cluster-version", NULL, "2.0.0" );
1190 0 : char const * checkpt_status_cache = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-status-cache", NULL, NULL );
1191 0 : char const * one_off_features = fd_env_strip_cmdline_cstr ( &argc, &argv, "--one-off-features", NULL, NULL );
1192 0 : char const * lthash = fd_env_strip_cmdline_cstr ( &argc, &argv, "--lthash", NULL, "false" );
1193 0 : double allowed_mem_delta = fd_env_strip_cmdline_double( &argc, &argv, "--allowed-mem-delta", NULL, 0.1 );
1194 0 : ulong thread_mem_bound = fd_env_strip_cmdline_ulong ( &argc, &argv, "--thread-mem-bound", NULL, FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT );
1195 0 : ulong runtime_mem_bound = fd_env_strip_cmdline_ulong ( &argc, &argv, "--runtime-mem-bound", NULL, (ulong)10e9 );
1196 :
1197 0 : if( FD_UNLIKELY( !verify_acc_hash ) ) {
1198 : /* We've got full snapshots that contain all 0s for the account
1199 : hash in account meta. Running hash verify allows us to
1200 : populate the hash in account meta with real values. */
1201 0 : FD_LOG_WARNING(( "verify-acc-hash should be 1" ));
1202 0 : }
1203 :
1204 : // TODO: Add argument validation. Make sure that we aren't including any arguments that aren't parsed for
1205 :
1206 0 : char hostname[64];
1207 0 : gethostname( hostname, sizeof(hostname) );
1208 0 : ulong hashseed = fd_hash( 0, hostname, strnlen( hostname, sizeof(hostname) ) );
1209 0 : args->hashseed = (uint)hashseed;
1210 :
1211 : /* Setup workspace */
1212 0 : fd_wksp_t * wksp;
1213 0 : if( wksp_name == NULL ) {
1214 0 : FD_LOG_NOTICE(( "--wksp not specified, using an anonymous local workspace" ));
1215 0 : wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, page_cnt, 0, "wksp", 0UL );
1216 0 : } else {
1217 0 : fd_shmem_info_t shmem_info[1];
1218 0 : if( FD_UNLIKELY( fd_shmem_info( wksp_name, 0UL, shmem_info ) ) )
1219 0 : FD_LOG_ERR(( "unable to query region \"%s\"\n\tprobably does not exist or bad permissions", wksp_name ));
1220 0 : wksp = fd_wksp_attach( wksp_name );
1221 0 : }
1222 :
1223 0 : if( wksp == NULL ) {
1224 0 : FD_LOG_ERR(( "failed to attach to workspace %s", wksp_name ));
1225 0 : }
1226 0 : if( reset ) {
1227 0 : fd_wksp_reset( wksp, args->hashseed );
1228 0 : }
1229 0 : args->wksp = wksp;
1230 :
1231 0 : args->funk_wksp = fd_wksp_new_anonymous( FD_SHMEM_NORMAL_PAGE_SZ,
1232 0 : funk_page_cnt*(1UL<<18),
1233 0 : 0,
1234 0 : "funk",
1235 0 : 0
1236 0 : );
1237 0 : if( FD_UNLIKELY( !args->funk_wksp ) ) {
1238 0 : FD_LOG_ERR(( "failed to create funk workspace" ));
1239 0 : }
1240 :
1241 0 : if( checkpt_status_cache && checkpt_status_cache[0] != '\0' ) {
1242 0 : FD_LOG_NOTICE(( "Creating status cache wksp" ));
1243 0 : fd_wksp_t * status_cache_wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 23UL, 0, "status_cache_wksp", 0UL );
1244 0 : fd_wksp_reset( status_cache_wksp, args->hashseed );
1245 0 : args->status_cache_wksp = status_cache_wksp;
1246 0 : } else {
1247 0 : args->status_cache_wksp = NULL;
1248 0 : }
1249 :
1250 : /* Setup alloc */
1251 0 : #define FD_ALLOC_TAG (422UL)
1252 0 : void * alloc_shmem = fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), FD_ALLOC_TAG );
1253 0 : if( FD_UNLIKELY( !alloc_shmem ) ) { FD_LOG_ERR( ( "fd_alloc too large for workspace" ) ); }
1254 0 : void * alloc_shalloc = fd_alloc_new( alloc_shmem, FD_ALLOC_TAG );
1255 0 : if( FD_UNLIKELY( !alloc_shalloc ) ) { FD_LOG_ERR( ( "fd_alloc_new failed" ) ); }
1256 0 : fd_alloc_t * alloc = fd_alloc_join( alloc_shalloc, FD_ALLOC_TAG );
1257 0 : args->alloc = alloc;
1258 0 : #undef FD_ALLOC_TAG
1259 :
1260 : /* Copy over arguments */
1261 0 : args->cmd = cmd;
1262 0 : args->start_slot = start_slot;
1263 0 : args->end_slot = end_slot;
1264 0 : args->checkpt = checkpt;
1265 0 : args->checkpt_funk = checkpt_funk;
1266 0 : args->shred_max = shred_max;
1267 0 : args->slot_history_max = slot_history_max;
1268 0 : args->txns_max = txns_max;
1269 0 : args->index_max = index_max;
1270 0 : args->funk_page_cnt = funk_page_cnt;
1271 0 : args->restore = restore;
1272 0 : args->mini_db_dir = mini_db_dir;
1273 0 : args->funk_only = funk_only;
1274 0 : args->copy_txn_status = copy_txn_status;
1275 0 : args->snapshot = snapshot;
1276 0 : args->incremental = incremental;
1277 0 : args->genesis = genesis;
1278 0 : args->shredcap = shredcap;
1279 0 : args->verify_funk = verify_funk;
1280 0 : args->check_acc_hash = check_acc_hash;
1281 0 : args->verify_acc_hash = verify_acc_hash;
1282 0 : args->trash_hash = trash_hash;
1283 0 : args->capture_fpath = capture_fpath;
1284 0 : args->solcap_start_slot = solcap_start_slot;
1285 0 : args->capture_txns = capture_txns;
1286 0 : args->checkpt_path = checkpt_path;
1287 0 : args->checkpt_freq = checkpt_freq;
1288 0 : args->checkpt_mismatch = checkpt_mismatch;
1289 0 : args->allocator = allocator;
1290 0 : args->abort_on_mismatch = abort_on_mismatch;
1291 0 : args->dump_instr_to_pb = dump_instr_to_pb;
1292 0 : args->dump_txn_to_pb = dump_txn_to_pb;
1293 0 : args->dump_block_to_pb = dump_block_to_pb;
1294 0 : args->dump_syscall_to_pb = dump_syscall_to_pb;
1295 0 : args->dump_elf_to_pb = dump_elf_to_pb;
1296 0 : args->dump_proto_start_slot = dump_proto_start_slot;
1297 0 : args->dump_proto_sig_filter = dump_proto_sig_filter;
1298 0 : args->dump_proto_output_dir = dump_proto_output_dir;
1299 0 : args->vote_acct_max = vote_acct_max;
1300 0 : args->rocksdb_list_cnt = 0UL;
1301 0 : args->checkpt_status_cache = checkpt_status_cache;
1302 0 : args->one_off_features_cnt = 0UL;
1303 0 : args->allowed_mem_delta = allowed_mem_delta;
1304 0 : args->lthash = lthash;
1305 0 : args->thread_mem_bound = thread_mem_bound ? thread_mem_bound : FD_RUNTIME_BORROWED_ACCOUNT_FOOTPRINT;
1306 0 : args->runtime_mem_bound = runtime_mem_bound;
1307 0 : parse_one_off_features( args, one_off_features );
1308 0 : parse_rocksdb_list( args, rocksdb_list, rocksdb_list_starts );
1309 :
1310 0 : if( FD_UNLIKELY( sscanf( cluster_version, "%u.%u.%u", &args->cluster_version[0], &args->cluster_version[1], &args->cluster_version[2] )!=3 ) ) {
1311 0 : FD_LOG_ERR(( "failed to decode cluster version" ));;
1312 0 : }
1313 :
1314 0 : if( args->rocksdb_list_cnt==1UL ) {
1315 0 : FD_LOG_NOTICE(( "rocksdb=%s", args->rocksdb_list[0] ));
1316 0 : } else {
1317 0 : for( ulong i=0UL; i<args->rocksdb_list_cnt; ++i ) {
1318 0 : FD_LOG_NOTICE(( "rocksdb_list[ %lu ]=%s slot=%lu", i, args->rocksdb_list[i], args->rocksdb_list_slot[i-1] ));
1319 0 : }
1320 0 : }
1321 :
1322 0 : return 0;
1323 0 : }
1324 :
1325 : int main( int argc, char ** argv ) {
1326 : /* Declaring this on the stack gets the alignment wrong when using asan */
1327 : fd_ledger_args_t * args = fd_alloca( alignof(fd_ledger_args_t), sizeof(fd_ledger_args_t) );
1328 : memset( args, 0, sizeof(fd_ledger_args_t) );
1329 : initial_setup( argc, argv, args );
1330 :
1331 : /* TODO: Need to implement snapshot minification. */
1332 :
1333 : if( args->cmd == NULL ) {
1334 : FD_LOG_ERR(( "no command specified" ));
1335 : } else if( strcmp( args->cmd, "replay" ) == 0 ) {
1336 : return replay( args );
1337 : } else if( strcmp( args->cmd, "ingest" ) == 0 ) {
1338 : ingest( args );
1339 : } else if( strcmp( args->cmd, "minify" ) == 0 ) {
1340 : minify( args );
1341 : } else {
1342 : FD_LOG_ERR(( "unknown command=%s", args->cmd ));
1343 : }
1344 :
1345 : return 0;
1346 : }
|