Line data Source code
1 : #include <stdio.h>
2 : #include <stdlib.h>
3 : #include <stddef.h>
4 : #include <alloca.h>
5 : #include <unistd.h>
6 : #include <sys/types.h>
7 : #include <sys/stat.h>
8 : #include <fcntl.h>
9 : #include <errno.h>
10 : #include <strings.h>
11 : #include "../../choreo/fd_choreo.h"
12 : #include "../../disco/fd_disco.h"
13 : #include "../../util/fd_util.h"
14 : #include "../../flamenco/fd_flamenco.h"
15 : #include "../../flamenco/nanopb/pb_decode.h"
16 : #include "../../flamenco/runtime/fd_hashes.h"
17 : #include "../../funk/fd_funk_filemap.h"
18 : #include "../../flamenco/types/fd_types.h"
19 : #include "../../flamenco/runtime/fd_runtime.h"
20 : #include "../../flamenco/runtime/fd_account.h"
21 : #include "../../flamenco/runtime/fd_rocksdb.h"
22 : #include "../../flamenco/runtime/fd_txncache.h"
23 : #include "../../ballet/base58/fd_base58.h"
24 : #include "../../flamenco/types/fd_solana_block.pb.h"
25 : #include "../../flamenco/runtime/context/fd_capture_ctx.h"
26 : #include "../../flamenco/runtime/fd_blockstore.h"
27 : #include "../../flamenco/runtime/program/fd_builtin_programs.h"
28 : #include "../../flamenco/shredcap/fd_shredcap.h"
29 : #include "../../flamenco/runtime/program/fd_bpf_program_util.h"
30 : #include "../../flamenco/snapshot/fd_snapshot.h"
31 :
32 : extern void fd_write_builtin_bogus_account( fd_exec_slot_ctx_t * slot_ctx, uchar const pubkey[ static 32 ], char const * data, ulong sz );
33 :
34 : struct fd_ledger_args {
35 : fd_wksp_t * wksp; /* wksp for blockstore */
36 : fd_wksp_t * funk_wksp; /* wksp for funk */
37 : fd_wksp_t * status_cache_wksp; /* wksp for status cache. */
38 : fd_blockstore_t * blockstore; /* blockstore for replay */
39 : fd_funk_t * funk; /* handle to funk */
40 : fd_alloc_t * alloc; /* handle to alloc */
41 : char const * cmd; /* user passed command to fd_ledger */
42 : ulong start_slot; /* start slot for offline replay */
43 : ulong end_slot; /* end slot for offline replay */
44 : uint hashseed; /* hashseed */
45 : char const * checkpt; /* wksp checkpoint */
46 : char const * checkpt_funk; /* wksp checkpoint for a funk wksp */
47 : char const * checkpt_archive; /* funk archive format */
48 : char const * checkpt_status_cache; /* status cache checkpoint */
49 : char const * restore; /* wksp restore */
50 : char const * restore_funk; /* wksp restore for a funk wksp */
51 : char const * restore_archive; /* restore from a funk archive */
52 : char const * allocator; /* allocator used during replay (libc/wksp) */
53 : ulong shred_max; /* maximum number of shreds*/
54 : ulong slot_history_max; /* number of slots stored by blockstore*/
55 : ulong txns_max; /* txns_max*/
56 : ulong index_max; /* size of funk index (same as rec max) */
57 : char const * funk_file; /* path to funk backing store */
58 : ulong funk_page_cnt;
59 : fd_funk_close_file_args_t funk_close_args;
60 : char const * snapshot; /* path to agave snapshot */
61 : char const * incremental; /* path to agave incremental snapshot */
62 : char const * genesis; /* path to agave genesis */
63 : char const * mini_db_dir; /* path to minifed rocksdb that's to be created */
64 : int copy_txn_status; /* determine if txns should be copied to the blockstore during minify/replay */
65 : int funk_only; /* determine if only funk should be ingested */
66 : char const * shredcap; /* path to replay using shredcap instead of rocksdb */
67 : int abort_on_mismatch; /* determine if execution should abort on mismatch*/
68 : int on_demand_block_ingest; /* determine if block range should be ingested during execution or beforehand */
69 : ulong on_demand_block_history; /* how many blocks should the blockstore hold at once */
70 : ulong pages_pruned; /* ledger pruning: how many pages should the pruned wksp have */
71 : ulong index_max_pruned; /* ledger pruning: how large should the pruned funk index be */
72 : fd_funk_t * pruned_funk; /* ledger pruning: funk used by the pruned wksp */
73 : char const * capture_fpath; /* solcap: path for solcap file to be created */
74 : int capture_txns; /* solcap: determine if transaction results should be captured for solcap*/
75 : char const * checkpt_path; /* path to dump funk wksp checkpoints during execution*/
76 : ulong checkpt_freq; /* how often funk wksp checkpoints will be dumped (defaults to never) */
77 : int checkpt_mismatch; /* determine if a funk wksp checkpoint should be dumped on a mismatch*/
78 :
79 : int dump_insn_to_pb; /* instruction dumping: should insns be dumped */
80 : int dump_txn_to_pb; /* txn dumping: should txns be dumped */
81 : ulong dump_proto_start_slot; /* instruction / txn dumping: what slot to start dumping*/
82 : char const * dump_proto_sig_filter; /* instruction / txn dumping: specify txn sig to dump at */
83 : char const * dump_proto_output_dir; /* instruction / txn dumping: output directory for protobuf messages */
84 :
85 : int verify_funk; /* verify funk before execution starts */
86 : uint verify_acc_hash; /* verify account hash from the snapshot */
87 : uint check_acc_hash; /* check account hash by reconstructing with data */
88 : ulong trash_hash; /* trash hash to be used for negative cases*/
89 : ulong vote_acct_max; /* max number of vote accounts */
90 : char const * rocksdb_list[32]; /* max number of rocksdb dirs that can be passed in */
91 : ulong rocksdb_list_slot[32]; /* start slot for each rocksdb dir that's passed in assuming there are mulitple */
92 : ulong rocksdb_list_cnt; /* number of rocksdb dirs passed in */
93 : uint cluster_version[3]; /* What version of solana is the genesis block? */
94 : char const * one_off_features[32]; /* List of one off feature pubkeys to enable for execution agnostic of cluster version */
95 : uint one_off_features_cnt; /* Number of one off features */
96 :
97 : /* These values are setup before replay */
98 : fd_capture_ctx_t * capture_ctx; /* capture_ctx is used in runtime_replay for various debugging tasks */
99 : fd_acc_mgr_t acc_mgr[ 1UL ]; /* funk wrapper*/
100 : fd_exec_slot_ctx_t * slot_ctx; /* slot_ctx */
101 : fd_exec_epoch_ctx_t * epoch_ctx; /* epoch_ctx */
102 : fd_tpool_t * tpool; /* thread pool for execution */
103 : uchar tpool_mem[FD_TPOOL_FOOTPRINT( FD_TILE_MAX )] __attribute__( ( aligned( FD_TPOOL_ALIGN ) ) );
104 : fd_spad_t * spads[ 128UL ]; /* scratchpad allocators that are eventually assigned to each txn_ctx */
105 : ulong spad_cnt; /* number of scratchpads, bounded by number of threads */
106 :
107 : char const * lthash;
108 : };
109 : typedef struct fd_ledger_args fd_ledger_args_t;
110 :
111 : /* Runtime Replay *************************************************************/
112 : static int
113 0 : init_tpool( fd_ledger_args_t * ledger_args ) {
114 0 : ulong tcnt = fd_tile_cnt();
115 0 : uchar * tpool_scr_mem = NULL;
116 0 : fd_tpool_t * tpool = NULL;
117 0 : if( tcnt>=1UL ) {
118 0 : tpool = fd_tpool_init( ledger_args->tpool_mem, tcnt );
119 0 : if( tpool == NULL ) {
120 0 : FD_LOG_ERR(( "failed to create thread pool" ));
121 0 : }
122 0 : ulong scratch_sz = fd_scratch_smem_footprint( 256UL<<20UL );
123 0 : tpool_scr_mem = fd_valloc_malloc( ledger_args->slot_ctx->valloc, FD_SCRATCH_SMEM_ALIGN, scratch_sz*(tcnt) );
124 0 : if( tpool_scr_mem == NULL ) {
125 0 : FD_LOG_ERR( ( "failed to allocate thread pool scratch space" ) );
126 0 : }
127 0 : for( ulong i=1UL; i<tcnt; ++i ) {
128 0 : if( fd_tpool_worker_push( tpool, i, tpool_scr_mem + scratch_sz*(i-1UL), scratch_sz ) == NULL ) {
129 0 : FD_LOG_ERR(( "failed to launch worker" ));
130 0 : }
131 0 : else {
132 0 : FD_LOG_NOTICE(( "launched worker" ));
133 0 : }
134 0 : }
135 0 : }
136 0 : ledger_args->tpool = tpool;
137 0 : return 0;
138 0 : }
139 :
140 : int
141 0 : runtime_replay( fd_ledger_args_t * ledger_args ) {
142 0 : fd_features_restore( ledger_args->slot_ctx );
143 :
144 0 : fd_runtime_update_leaders( ledger_args->slot_ctx, ledger_args->slot_ctx->slot_bank.slot );
145 :
146 0 : fd_calculate_epoch_accounts_hash_values( ledger_args->slot_ctx );
147 :
148 0 : long replay_time = -fd_log_wallclock();
149 0 : ulong txn_cnt = 0;
150 0 : ulong slot_cnt = 0;
151 0 : fd_blockstore_t * blockstore = ledger_args->slot_ctx->blockstore;
152 :
153 0 : ulong prev_slot = ledger_args->slot_ctx->slot_bank.slot;
154 0 : ulong start_slot = ledger_args->slot_ctx->slot_bank.slot + 1;
155 :
156 : /* On demand rocksdb ingest */
157 0 : fd_rocksdb_t rocks_db = {0};
158 0 : fd_rocksdb_root_iter_t iter = {0};
159 0 : fd_slot_meta_t slot_meta = {0};
160 0 : ulong curr_rocksdb_idx = 0UL;
161 0 : if( ledger_args->on_demand_block_ingest ) {
162 0 : char * err = fd_rocksdb_init( &rocks_db, ledger_args->rocksdb_list[ 0UL ] );
163 0 : if( FD_UNLIKELY( err!=NULL ) ) {
164 0 : FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", ledger_args->rocksdb_list[ 0UL ], err ));
165 0 : }
166 0 : fd_rocksdb_root_iter_new( &iter );
167 0 : if( fd_rocksdb_root_iter_seek( &iter, &rocks_db, start_slot, &slot_meta, ledger_args->slot_ctx->valloc ) ) {
168 0 : FD_LOG_ERR(( "unable to seek to first slot" ));
169 0 : }
170 0 : }
171 :
172 0 : if( ledger_args->capture_ctx && ledger_args->capture_ctx->pruned_funk != NULL ) {
173 : /* If prune enabled: setup rent partitions */
174 0 : fd_funk_start_write( ledger_args->capture_ctx->pruned_funk );
175 0 : fd_funk_t * funk = ledger_args->slot_ctx->acc_mgr->funk;
176 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
177 0 : fd_funk_partvec_t * partvec = fd_funk_get_partvec( funk, wksp );
178 0 : fd_funk_t * pruned_funk = ledger_args->capture_ctx->pruned_funk;
179 0 : fd_funk_set_num_partitions( pruned_funk, partvec->num_part );
180 0 : fd_funk_end_write( ledger_args->capture_ctx->pruned_funk );
181 0 : }
182 :
183 : /* Setup trash_hash */
184 0 : uchar trash_hash_buf[32];
185 0 : memset( trash_hash_buf, 0xFE, sizeof(trash_hash_buf) );
186 :
187 0 : for( ulong slot = start_slot; slot <= ledger_args->end_slot; ++slot ) {
188 0 : ledger_args->slot_ctx->slot_bank.prev_slot = prev_slot;
189 0 : ledger_args->slot_ctx->slot_bank.slot = slot;
190 :
191 0 : FD_LOG_DEBUG(( "reading slot %lu", slot ));
192 :
193 0 : if( ledger_args->capture_ctx && ledger_args->capture_ctx->pruned_funk != NULL ) {
194 0 : fd_funk_start_write( ledger_args->capture_ctx->pruned_funk );
195 0 : fd_runtime_collect_rent_accounts_prune( slot, ledger_args->slot_ctx, ledger_args->capture_ctx );
196 0 : fd_funk_end_write( ledger_args->capture_ctx->pruned_funk );
197 0 : }
198 :
199 0 : if( ledger_args->on_demand_block_ingest ) {
200 0 : if( fd_blockstore_block_query( blockstore, slot ) == NULL && slot_meta.slot == slot ) {
201 0 : int err = fd_rocksdb_import_block_blockstore( &rocks_db, &slot_meta, blockstore,
202 0 : ledger_args->copy_txn_status, slot == (ledger_args->trash_hash) ? trash_hash_buf : NULL );
203 0 : if( FD_UNLIKELY( err ) ) {
204 0 : FD_LOG_ERR(( "Failed to import block %lu", start_slot ));
205 0 : }
206 0 : }
207 0 : fd_blockstore_slot_remove( blockstore, slot - ledger_args->on_demand_block_history );
208 0 : }
209 :
210 0 : fd_blockstore_start_read( blockstore );
211 0 : fd_block_t * blk = fd_blockstore_block_query( blockstore, slot );
212 0 : if( blk == NULL ) {
213 0 : FD_LOG_WARNING( ( "failed to read slot %lu", slot ) );
214 0 : fd_blockstore_end_read( blockstore );
215 0 : continue;
216 0 : }
217 :
218 0 : uchar * val = fd_blockstore_block_data_laddr( blockstore, blk );
219 0 : ulong sz = blk->data_sz;
220 0 : fd_blockstore_end_read( blockstore );
221 :
222 0 : ulong blk_txn_cnt = 0;
223 0 : FD_TEST( fd_runtime_block_eval_tpool( ledger_args->slot_ctx,
224 0 : ledger_args->capture_ctx,
225 0 : val,
226 0 : sz,
227 0 : ledger_args->tpool,
228 0 : 1,
229 0 : &blk_txn_cnt,
230 0 : ledger_args->spads,
231 0 : ledger_args->spad_cnt ) == FD_RUNTIME_EXECUTE_SUCCESS );
232 0 : txn_cnt += blk_txn_cnt;
233 0 : slot_cnt++;
234 :
235 0 : fd_blockstore_start_read( blockstore );
236 0 : fd_hash_t const * expected = fd_blockstore_block_hash_query( blockstore, slot );
237 0 : if( FD_UNLIKELY( !expected ) ) FD_LOG_ERR( ( "slot %lu is missing its hash", slot ) );
238 0 : else if( FD_UNLIKELY( 0 != memcmp( ledger_args->slot_ctx->slot_bank.poh.hash, expected->hash, 32UL ) ) ) {
239 0 : char expected_hash[ FD_BASE58_ENCODED_32_SZ ];
240 0 : fd_acct_addr_cstr( expected_hash, expected->hash );
241 0 : char poh_hash[ FD_BASE58_ENCODED_32_SZ ];
242 0 : fd_acct_addr_cstr( poh_hash, ledger_args->slot_ctx->slot_bank.poh.hash );
243 0 : FD_LOG_WARNING(( "PoH hash mismatch! slot=%lu expected=%s, got=%s",
244 0 : slot,
245 0 : expected_hash,
246 0 : poh_hash ));
247 :
248 0 : if( ledger_args->checkpt_mismatch ) {
249 0 : fd_runtime_checkpt( ledger_args->capture_ctx, ledger_args->slot_ctx, ULONG_MAX );
250 0 : }
251 0 : if( ledger_args->abort_on_mismatch ) {
252 0 : fd_blockstore_end_read( blockstore );
253 0 : return 1;
254 0 : }
255 0 : }
256 :
257 0 : expected = fd_blockstore_bank_hash_query( blockstore, slot );
258 0 : if( FD_UNLIKELY( !expected ) ) {
259 0 : FD_LOG_ERR(( "slot %lu is missing its bank hash", slot ));
260 0 : } else if( FD_UNLIKELY( 0 != memcmp( ledger_args->slot_ctx->slot_bank.banks_hash.hash,
261 0 : expected->hash,
262 0 : 32UL ) ) ) {
263 :
264 0 : char expected_hash[ FD_BASE58_ENCODED_32_SZ ];
265 0 : fd_acct_addr_cstr( expected_hash, expected->hash );
266 0 : char bank_hash[ FD_BASE58_ENCODED_32_SZ ];
267 0 : fd_acct_addr_cstr( bank_hash, ledger_args->slot_ctx->slot_bank.banks_hash.hash );
268 :
269 0 : FD_LOG_WARNING(( "Bank hash mismatch! slot=%lu expected=%s, got=%s",
270 0 : slot,
271 0 : expected_hash,
272 0 : bank_hash ));
273 :
274 0 : if( ledger_args->checkpt_mismatch ) {
275 0 : fd_runtime_checkpt( ledger_args->capture_ctx, ledger_args->slot_ctx, ULONG_MAX );
276 0 : }
277 0 : if( ledger_args->abort_on_mismatch ) {
278 0 : fd_blockstore_end_read( blockstore );
279 0 : return 1;
280 0 : }
281 0 : }
282 0 : fd_blockstore_end_read( blockstore );
283 :
284 0 : prev_slot = slot;
285 :
286 0 : if( ledger_args->on_demand_block_ingest && slot<ledger_args->end_slot ) {
287 : /* TODO: This currently doesn't support switching over on slots that occur
288 : on a fork */
289 : /* If need to go to next rocksdb, switch over */
290 0 : if( FD_UNLIKELY( ledger_args->rocksdb_list_cnt>1UL &&
291 0 : slot+1UL==ledger_args->rocksdb_list_slot[curr_rocksdb_idx] ) ) {
292 0 : curr_rocksdb_idx++;
293 0 : FD_LOG_WARNING(( "Switching to next rocksdb=%s", ledger_args->rocksdb_list[curr_rocksdb_idx] ));
294 0 : fd_rocksdb_root_iter_destroy( &iter );
295 0 : fd_rocksdb_destroy( &rocks_db );
296 :
297 0 : fd_memset( &rocks_db, 0, sizeof(fd_rocksdb_t) );
298 0 : fd_memset( &iter, 0, sizeof(fd_rocksdb_root_iter_t) );
299 0 : fd_memset( &slot_meta, 0, sizeof(fd_slot_meta_t) );
300 :
301 0 : char * err = fd_rocksdb_init( &rocks_db, ledger_args->rocksdb_list[curr_rocksdb_idx] );
302 0 : if( FD_UNLIKELY( err!=NULL ) ) {
303 0 : FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", ledger_args->rocksdb_list[curr_rocksdb_idx], err ));
304 0 : }
305 0 : fd_rocksdb_root_iter_new( &iter );
306 0 : int ret = fd_rocksdb_root_iter_seek( &iter, &rocks_db, slot+1UL, &slot_meta, ledger_args->slot_ctx->valloc );
307 0 : if( ret<0 ) {
308 0 : FD_LOG_ERR(( "Failed to seek to slot %lu", slot+1UL ));
309 0 : }
310 0 : } else {
311 : /* Otherwise look for next slot in current rocksdb */
312 0 : int ret = fd_rocksdb_root_iter_next( &iter, &slot_meta, ledger_args->slot_ctx->valloc );
313 0 : if( ret<0 ) {
314 0 : ret = fd_rocksdb_get_meta( &rocks_db, slot+1UL, &slot_meta, ledger_args->slot_ctx->valloc );
315 0 : if( ret<0 ) {
316 0 : FD_LOG_ERR(( "Failed to get meta for slot %lu", slot+1UL ));
317 0 : }
318 0 : }
319 0 : }
320 0 : }
321 0 : }
322 :
323 0 : if( ledger_args->tpool ) {
324 0 : fd_tpool_fini( ledger_args->tpool );
325 0 : }
326 :
327 0 : if( ledger_args->on_demand_block_ingest ) {
328 0 : fd_rocksdb_root_iter_destroy( &iter );
329 0 : fd_rocksdb_destroy( &rocks_db );
330 0 : }
331 :
332 0 : replay_time += fd_log_wallclock();
333 0 : double replay_time_s = (double)replay_time * 1e-9;
334 0 : double tps = (double)txn_cnt / replay_time_s;
335 0 : double sec_per_slot = replay_time_s / (double)slot_cnt;
336 0 : FD_LOG_NOTICE((
337 0 : "replay completed - slots: %lu, elapsed: %6.6f s, txns: %lu, tps: %6.6f, sec/slot: %6.6f",
338 0 : slot_cnt,
339 0 : replay_time_s,
340 0 : txn_cnt,
341 0 : tps,
342 0 : sec_per_slot ));
343 :
344 0 : if ( slot_cnt == 0 ) {
345 0 : FD_LOG_ERR(( "No slots replayed" ));
346 0 : }
347 :
348 0 : return 0;
349 0 : }
350 :
351 : /***************************** Helpers ****************************************/
352 0 : fd_valloc_t allocator_setup( fd_wksp_t * wksp, char const * allocator ) {
353 0 : if( strcmp( allocator, "libc" ) == 0 ) {
354 0 : return fd_libc_alloc_virtual();
355 0 : }
356 :
357 0 : if( strcmp( allocator, "wksp" ) != 0 ) {
358 0 : FD_LOG_ERR( ( "unknown allocator specified" ) );
359 0 : }
360 :
361 0 : FD_TEST( wksp );
362 :
363 0 : void * alloc_shmem =
364 0 : fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 3UL );
365 0 : if( FD_UNLIKELY( !alloc_shmem ) ) { FD_LOG_ERR( ( "fd_alloc too large for workspace" ) ); }
366 0 : void * alloc_shalloc = fd_alloc_new( alloc_shmem, 3UL );
367 0 : if( FD_UNLIKELY( !alloc_shalloc ) ) { FD_LOG_ERR( ( "fd_allow_new failed" ) ); }
368 0 : fd_alloc_t * alloc = fd_alloc_join( alloc_shalloc, 3UL );
369 0 : if( FD_UNLIKELY( !alloc ) ) { FD_LOG_ERR( ( "fd_alloc_join failed" ) ); }
370 0 : return fd_alloc_virtual( alloc );
371 0 : }
372 :
373 : void
374 0 : fd_ledger_main_setup( fd_ledger_args_t * args ) {
375 0 : fd_flamenco_boot( NULL, NULL );
376 0 : fd_funk_t * funk = args->funk;
377 :
378 : /* Setup valloc */
379 0 : fd_valloc_t valloc = args->slot_ctx->valloc;
380 :
381 : /* Setup capture context */
382 0 : int has_solcap = args->capture_fpath && args->capture_fpath[0] != '\0';
383 0 : int has_checkpt = args->checkpt_path && args->checkpt_path[0] != '\0';
384 0 : int has_checkpt_funk = args->checkpt_funk && args->checkpt_funk[0] != '\0';
385 0 : int has_checkpt_arch = args->checkpt_archive && args->checkpt_archive[0] != '\0';
386 0 : int has_prune = args->pruned_funk != NULL;
387 0 : int has_dump_to_protobuf = args->dump_insn_to_pb || args->dump_txn_to_pb;
388 :
389 0 : if( has_solcap || has_checkpt || has_checkpt_funk || has_checkpt_arch || has_prune || has_dump_to_protobuf ) {
390 0 : FILE * capture_file = NULL;
391 :
392 0 : void * capture_ctx_mem = fd_valloc_malloc( valloc, FD_CAPTURE_CTX_ALIGN, FD_CAPTURE_CTX_FOOTPRINT );
393 0 : FD_TEST( capture_ctx_mem );
394 0 : fd_memset( capture_ctx_mem, 0, sizeof( fd_capture_ctx_t ) );
395 0 : args->capture_ctx = fd_capture_ctx_new( capture_ctx_mem );
396 :
397 0 : args->capture_ctx->checkpt_freq = ULONG_MAX;
398 :
399 0 : if( has_solcap ) {
400 0 : capture_file = fopen( args->capture_fpath, "w+" );
401 0 : if( FD_UNLIKELY( !capture_file ) ) {
402 0 : FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", args->capture_fpath, errno, strerror( errno ) ));
403 0 : }
404 0 : fd_solcap_writer_init( args->capture_ctx->capture, capture_file );
405 0 : args->capture_ctx->capture_txns = args->capture_txns;
406 0 : } else {
407 0 : args->capture_ctx->capture = NULL;
408 0 : }
409 :
410 0 : if( has_checkpt || has_checkpt_funk || has_checkpt_arch ) {
411 0 : args->capture_ctx->checkpt_path = ( has_checkpt ? args->checkpt_path : args->checkpt_funk );
412 0 : args->capture_ctx->checkpt_archive = args->checkpt_archive;
413 0 : args->capture_ctx->checkpt_freq = args->checkpt_freq;
414 0 : }
415 0 : if( has_prune ) {
416 0 : args->capture_ctx->pruned_funk = args->pruned_funk;
417 0 : }
418 0 : if( has_dump_to_protobuf ) {
419 0 : args->capture_ctx->dump_insn_to_pb = args->dump_insn_to_pb;
420 0 : args->capture_ctx->dump_txn_to_pb = args->dump_txn_to_pb;
421 0 : args->capture_ctx->dump_proto_sig_filter = args->dump_proto_sig_filter;
422 0 : args->capture_ctx->dump_proto_output_dir = args->dump_proto_output_dir;
423 0 : args->capture_ctx->dump_proto_start_slot = args->dump_proto_start_slot;
424 0 : }
425 0 : }
426 :
427 0 : fd_runtime_recover_banks( args->slot_ctx, 0, args->genesis==NULL );
428 :
429 : /* Finish other runtime setup steps */
430 0 : fd_funk_start_write( funk );
431 0 : fd_features_restore( args->slot_ctx );
432 0 : fd_runtime_update_leaders( args->slot_ctx, args->slot_ctx->slot_bank.slot );
433 0 : fd_calculate_epoch_accounts_hash_values( args->slot_ctx );
434 0 : fd_bpf_scan_and_create_bpf_program_cache_entry( args->slot_ctx, args->slot_ctx->funk_txn );
435 0 : fd_funk_end_write( funk );
436 :
437 : /* Allocate memory for the account scratch space. In live execution, each of
438 : the spad allocations should be tied to its respective execution thread.
439 : In the future, the spad should be allocated from its tiles' workspace.
440 : It is important that the spads are only allocated on startup for
441 : performance reasons to avoid dynamic allocation in the critical path. */
442 :
443 0 : args->spad_cnt = fd_tpool_worker_cnt( args->tpool );
444 0 : for( ulong i=0UL; i<args->spad_cnt; i++ ) {
445 0 : ulong total_mem_sz = fd_spad_footprint( MAX_TX_ACCOUNT_LOCKS * fd_ulong_align_up( FD_ACC_TOT_SZ_MAX, FD_ACCOUNT_REC_ALIGN ) );
446 0 : uchar * mem = fd_wksp_alloc_laddr( args->wksp, FD_SPAD_ALIGN, total_mem_sz, 999UL );
447 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( mem, total_mem_sz ) );
448 0 : if( FD_UNLIKELY( !spad ) ) {
449 0 : FD_LOG_ERR(( "failed to allocate spad" ));
450 0 : }
451 0 : args->spads[ i ] = spad;
452 0 : }
453 :
454 0 : }
455 :
456 : void
457 0 : fd_ledger_main_teardown( fd_ledger_args_t * args ) {
458 : /* Flush solcap file and cleanup */
459 0 : if( args->capture_ctx && args->capture_ctx->capture ) {
460 0 : fd_solcap_writer_flush( args->capture_ctx->capture );
461 0 : fd_solcap_writer_delete( args->capture_ctx->capture );
462 0 : }
463 0 : fd_exec_epoch_ctx_delete( fd_exec_epoch_ctx_leave( args->epoch_ctx ) );
464 0 : fd_exec_slot_ctx_delete( fd_exec_slot_ctx_leave( args->slot_ctx ) );
465 0 : }
466 :
467 : void
468 : ingest_rocksdb( fd_alloc_t * alloc,
469 : char const * file,
470 : ulong start_slot,
471 : ulong end_slot,
472 : fd_blockstore_t * blockstore,
473 : int txn_status,
474 0 : ulong trash_hash ) {
475 :
476 0 : fd_valloc_t valloc = fd_alloc_virtual( alloc );
477 0 : fd_rocksdb_t rocks_db;
478 0 : char * err = fd_rocksdb_init( &rocks_db, file );
479 0 : if( FD_UNLIKELY( err!=NULL ) ) {
480 0 : FD_LOG_ERR(( "fd_rocksdb_init returned %s", err ));
481 0 : }
482 :
483 0 : ulong last_slot = fd_rocksdb_last_slot( &rocks_db, &err );
484 0 : if( FD_UNLIKELY( err!=NULL ) ) {
485 0 : FD_LOG_ERR(( "fd_rocksdb_last_slot returned %s", err ));
486 0 : }
487 :
488 0 : if( last_slot < start_slot ) {
489 0 : FD_LOG_ERR(( "rocksdb blocks are older than snapshot. first=%lu last=%lu wanted=%lu",
490 0 : fd_rocksdb_first_slot(&rocks_db, &err), last_slot, start_slot ));
491 0 : }
492 :
493 0 : FD_LOG_NOTICE(( "ingesting rocksdb from start=%lu to end=%lu", start_slot, end_slot ));
494 :
495 0 : fd_rocksdb_root_iter_t iter = {0};
496 0 : fd_rocksdb_root_iter_new( &iter );
497 :
498 0 : fd_slot_meta_t slot_meta = {0};
499 0 : fd_memset( &slot_meta, 0, sizeof(slot_meta) );
500 :
501 0 : int ret = fd_rocksdb_root_iter_seek( &iter, &rocks_db, start_slot, &slot_meta, valloc );
502 0 : if( ret < 0 ) {
503 0 : FD_LOG_ERR(( "fd_rocksdb_root_iter_seek returned %d", ret ));
504 0 : }
505 :
506 0 : uchar trash_hash_buf[32];
507 0 : memset( trash_hash_buf, 0xFE, sizeof(trash_hash_buf) );
508 :
509 0 : ulong blk_cnt = 0;
510 0 : do {
511 0 : ulong slot = slot_meta.slot;
512 0 : if( slot > end_slot ) {
513 0 : break;
514 0 : }
515 :
516 : /* Read and deshred block from RocksDB */
517 0 : if( blk_cnt % 100 == 0 ) {
518 0 : FD_LOG_WARNING(( "imported %lu blocks", blk_cnt ));
519 0 : }
520 :
521 0 : int err = fd_rocksdb_import_block_blockstore( &rocks_db, &slot_meta, blockstore, txn_status,
522 0 : (slot == trash_hash) ? trash_hash_buf : NULL );
523 0 : if( FD_UNLIKELY( err ) ) {
524 0 : FD_LOG_ERR(( "fd_rocksdb_get_block failed" ));
525 0 : }
526 :
527 0 : ++blk_cnt;
528 :
529 0 : fd_bincode_destroy_ctx_t ctx = { .valloc = valloc };
530 0 : fd_slot_meta_destroy( &slot_meta, &ctx );
531 :
532 0 : ret = fd_rocksdb_root_iter_next( &iter, &slot_meta, valloc );
533 0 : if( ret < 0 ) {
534 : // FD_LOG_WARNING(("Failed for slot %lu", slot + 1));
535 0 : ret = fd_rocksdb_get_meta( &rocks_db, slot + 1, &slot_meta, valloc );
536 0 : if( ret < 0 ) {
537 0 : break;
538 0 : }
539 0 : }
540 : // FD_LOG_ERR(("fd_rocksdb_root_iter_seek returned %d", ret));
541 0 : } while (1);
542 :
543 0 : fd_rocksdb_root_iter_destroy( &iter );
544 0 : fd_rocksdb_destroy( &rocks_db );
545 :
546 0 : FD_LOG_NOTICE(( "ingested %lu blocks", blk_cnt ));
547 0 : }
548 :
549 : void
550 0 : parse_one_off_features( fd_ledger_args_t * args, char const * one_off_features ) {
551 0 : if( !one_off_features ) {
552 0 : FD_LOG_NOTICE(( "No one-off features passed in" ));
553 0 : return;
554 0 : }
555 :
556 0 : char * one_off_features_str = strdup( one_off_features );
557 0 : char * token = NULL;
558 0 : token = strtok( one_off_features_str, "," );
559 0 : while( token ) {
560 0 : args->one_off_features[ args->one_off_features_cnt++ ] = token;
561 0 : token = strtok( NULL, "," );
562 0 : }
563 :
564 0 : FD_LOG_NOTICE(( "Found %u one off features to include", args->one_off_features_cnt ));
565 :
566 : /* TODO: Fix the leak here and in parse_rocksdb_list */
567 0 : }
568 :
569 : void
570 : parse_rocksdb_list( fd_ledger_args_t * args,
571 : char const * rocksdb_list,
572 0 : char const * rocksdb_start_slots ) {
573 : /* First parse the paths to the different rocksdb */
574 0 : if( FD_UNLIKELY( !rocksdb_list ) ) {
575 0 : FD_LOG_NOTICE(( "No rocksdb list passed in" ));
576 0 : return;
577 0 : }
578 :
579 0 : char * rocksdb_str = strdup( rocksdb_list );
580 0 : char * token = NULL;
581 0 : token = strtok( rocksdb_str, "," );
582 0 : while( token ) {
583 0 : args->rocksdb_list[ args->rocksdb_list_cnt++ ] = token;
584 0 : token = strtok( NULL, "," );
585 0 : }
586 :
587 : /* Now repeat for the start slots assuming there are multiple */
588 0 : if( rocksdb_start_slots == NULL && args->rocksdb_list_cnt > 1 ) {
589 0 : FD_LOG_ERR(( "Multiple rocksdb dirs passed in but no start slots" ));
590 0 : }
591 0 : ulong index = 0UL;
592 0 : if( rocksdb_start_slots ) {
593 0 : char * rocksdb_start_slot_str = strdup( rocksdb_start_slots );
594 0 : token = NULL;
595 0 : token = strtok( rocksdb_start_slot_str, "," );
596 0 : while( token ) {
597 0 : args->rocksdb_list_slot[ index++ ] = strtoul( token, NULL, 10 );
598 0 : token = strtok( NULL, "," );
599 0 : }
600 0 : }
601 :
602 0 : if( index != args->rocksdb_list_cnt - 1UL ) {
603 0 : FD_LOG_ERR(( "Number of rocksdb dirs passed in doesn't match number of start slots" ));
604 0 : }
605 :
606 :
607 : /* TODO: There is technically a leak here since we don't free the duplicated
608 : string but it's not a big deal. */
609 0 : }
610 :
611 : void
612 0 : init_scratch( fd_wksp_t * wksp ) {
613 0 : #define FD_SCRATCH_TAG (421UL)
614 0 : ulong smax = 1UL << 33UL; /* 8 GiB */
615 0 : ulong sdepth = 2048UL; /* 2048 scratch frames */
616 0 : void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), fd_scratch_smem_footprint( smax ), FD_SCRATCH_TAG );
617 0 : void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( sdepth ), FD_SCRATCH_TAG );
618 0 : #undef FD_SCRATCH_TAG
619 0 : FD_TEST( (!!smem) & (!!fmem) );
620 0 : fd_scratch_attach( smem, fmem, smax, sdepth );
621 0 : }
622 :
623 : void
624 0 : cleanup_scratch( void ) {
625 0 : void * fmem = NULL;
626 0 : void * smem = fd_scratch_detach( &fmem );
627 0 : fd_wksp_free_laddr( smem );
628 0 : fd_wksp_free_laddr( fmem );
629 0 : }
630 :
631 : void
632 0 : init_funk( fd_ledger_args_t * args ) {
633 0 : fd_funk_t * funk;
634 0 : if( args->restore_funk ) {
635 0 : funk = fd_funk_recover_checkpoint( args->funk_file, 1, args->restore_funk, &args->funk_close_args );
636 0 : } else {
637 0 : funk = fd_funk_open_file( args->funk_file, 1, args->hashseed, args->txns_max, args->index_max, args->funk_page_cnt*(1UL<<30), FD_FUNK_OVERWRITE, &args->funk_close_args );
638 0 : }
639 0 : args->funk = funk;
640 0 : args->funk_wksp = fd_funk_wksp( funk );
641 0 : FD_LOG_NOTICE(( "funky at global address 0x%016lx with %lu records", fd_wksp_gaddr_fast( args->funk_wksp, funk ),
642 0 : fd_funk_rec_cnt( fd_funk_rec_map( funk, args->funk_wksp ) ) ));
643 0 : }
644 :
645 : void
646 0 : cleanup_funk( fd_ledger_args_t * args ) {
647 0 : fd_funk_close_file( &args->funk_close_args );
648 0 : }
649 :
650 : void
651 0 : init_blockstore( fd_ledger_args_t * args ) {
652 0 : fd_wksp_tag_query_info_t info;
653 0 : ulong blockstore_tag = FD_BLOCKSTORE_MAGIC;
654 0 : void * shmem;
655 0 : if( fd_wksp_tag_query( args->wksp, &blockstore_tag, 1, &info, 1 ) > 0 ) {
656 0 : shmem = fd_wksp_laddr_fast( args->wksp, info.gaddr_lo );
657 0 : args->blockstore = fd_blockstore_join( shmem );
658 0 : if( args->blockstore == NULL ) {
659 0 : FD_LOG_ERR(( "failed to join a blockstore" ));
660 0 : }
661 0 : FD_LOG_NOTICE(( "joined blockstore" ));
662 0 : } else {
663 0 : shmem = fd_wksp_alloc_laddr( args->wksp, fd_blockstore_align(), fd_blockstore_footprint(), blockstore_tag );
664 0 : if( shmem == NULL ) {
665 0 : FD_LOG_ERR(( "failed to allocate a blockstore" ));
666 0 : }
667 0 : ulong lg_txn_max = 22UL;
668 0 : args->blockstore = fd_blockstore_join( fd_blockstore_new( shmem, 1, args->hashseed, args->shred_max,
669 0 : args->slot_history_max, lg_txn_max ) );
670 0 : if( args->blockstore == NULL ) {
671 0 : fd_wksp_free_laddr( shmem );
672 0 : FD_LOG_ERR(( "failed to allocate a blockstore" ));
673 0 : }
674 0 : FD_LOG_NOTICE(( "allocating a new blockstore" ));
675 0 : }
676 0 : }
677 :
678 : void
679 0 : checkpt( fd_ledger_args_t * args, fd_exec_slot_ctx_t * slot_ctx ) {
680 0 : if( !args->checkpt && !args->checkpt_funk && !args->checkpt_archive && !args->checkpt_status_cache ) {
681 0 : FD_LOG_WARNING(( "No checkpt argument specified" ));
682 0 : }
683 :
684 0 : if( args->checkpt_archive ) {
685 0 : FD_LOG_NOTICE(( "writing funk archive %s", args->checkpt_archive ));
686 :
687 : /* Switch to archival format */
688 0 : fd_funk_start_write( args->funk );
689 0 : int err = fd_runtime_save_slot_bank_archival( slot_ctx );
690 0 : if( err ) FD_LOG_ERR(( "funk archive failed: error %d", err ));
691 0 : err = fd_runtime_save_epoch_bank_archival( slot_ctx );
692 0 : if( err ) FD_LOG_ERR(( "funk archive failed: error %d", err ));
693 0 : fd_funk_end_write( args->funk );
694 :
695 0 : err = fd_funk_archive( args->funk, args->checkpt_archive );
696 0 : if( err ) FD_LOG_ERR(( "funk archive failed: error %d", err ));
697 0 : }
698 0 : if( args->checkpt_funk ) {
699 0 : if( args->funk_wksp == NULL ) {
700 0 : FD_LOG_ERR(( "funk_wksp is NULL" ));
701 0 : }
702 0 : FD_LOG_NOTICE(( "writing funk checkpt %s", args->checkpt_funk ));
703 0 : unlink( args->checkpt_funk );
704 : #ifdef FD_FUNK_WKSP_PROTECT
705 : fd_wksp_mprotect( args->funk_wksp, 0 );
706 : #endif
707 0 : int err = fd_wksp_checkpt( args->funk_wksp, args->checkpt_funk, 0666, 0, NULL );
708 : #ifdef FD_FUNK_WKSP_PROTECT
709 : fd_wksp_mprotect( args->funk_wksp, 1 );
710 : #endif
711 0 : if( err ) {
712 0 : FD_LOG_ERR(( "funk checkpt failed: error %d", err ));
713 0 : }
714 0 : }
715 0 : if( args->checkpt ) {
716 0 : FD_LOG_NOTICE(( "writing %s", args->checkpt ));
717 0 : unlink( args->checkpt );
718 0 : int err = fd_wksp_checkpt( args->wksp, args->checkpt, 0666, 0, NULL );
719 0 : if( err ) {
720 0 : FD_LOG_ERR(( "checkpt failed: error %d", err ));
721 0 : }
722 0 : }
723 0 : if( args->checkpt_status_cache ) {
724 0 : FD_LOG_NOTICE(( "writing %s", args->checkpt_status_cache ));
725 0 : unlink( args->checkpt_status_cache );
726 0 : int err = fd_wksp_checkpt( args->status_cache_wksp, args->checkpt_status_cache, 0666, 0, NULL );
727 0 : if( err ) {
728 0 : FD_LOG_ERR(( "status cache checkpt failed: error %d", err ));
729 0 : }
730 0 : }
731 0 : }
732 :
733 : void
734 0 : archive_restore( fd_ledger_args_t * args ) {
735 0 : if( args->restore_archive != NULL ) {
736 0 : FD_LOG_NOTICE(( "restoring archive %s", args->restore_archive ));
737 0 : fd_funk_unarchive( args->funk, args->restore_archive );
738 0 : }
739 0 : }
740 :
741 : void
742 0 : wksp_restore( fd_ledger_args_t * args ) {
743 0 : if( args->restore != NULL ) {
744 0 : FD_LOG_NOTICE(( "restoring wksp %s", args->restore ));
745 0 : fd_wksp_restore( args->wksp, args->restore, args->hashseed );
746 0 : }
747 0 : }
748 :
749 : /********************* Main Command Functions and Setup ***********************/
750 : void
751 0 : minify( fd_ledger_args_t * args ) {
752 : /* Example commmand:
753 : fd_ledger --cmd minify --rocksdb <LARGE_ROCKSDB> --minified-rocksdb <MINI_ROCKSDB>
754 : --start-slot <START_SLOT> --end-slot <END_SLOT> --copy-txn-status 1
755 : */
756 0 : if( args->rocksdb_list[ 0UL ] == NULL ) {
757 0 : FD_LOG_ERR(( "rocksdb path is NULL" ));
758 0 : }
759 0 : if( args->mini_db_dir == NULL ) {
760 0 : FD_LOG_ERR(( "minified rocksdb path is NULL" ));
761 0 : }
762 :
763 :
764 0 : fd_rocksdb_t big_rocksdb;
765 0 : char * err = fd_rocksdb_init( &big_rocksdb, args->rocksdb_list[ 0UL ] );
766 0 : if( FD_UNLIKELY( err!=NULL ) ) {
767 0 : FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", args->rocksdb_list[ 0UL ], err ));
768 0 : }
769 :
770 : /* If the directory for the minified rocksdb already exists, error out */
771 0 : struct stat statbuf;
772 0 : if( stat( args->mini_db_dir, &statbuf ) == 0 ) {
773 0 : FD_LOG_ERR(( "path for mini_db_dir=%s already exists", args->mini_db_dir ));
774 0 : }
775 :
776 : /* Create a new smaller rocksdb */
777 0 : fd_rocksdb_t mini_rocksdb;
778 0 : fd_rocksdb_new( &mini_rocksdb, args->mini_db_dir );
779 :
780 : /* Correctly bound off start and end slot */
781 0 : ulong first_slot = fd_rocksdb_first_slot( &big_rocksdb, &err );
782 0 : ulong last_slot = fd_rocksdb_last_slot( &big_rocksdb, &err );
783 0 : if( args->start_slot < first_slot ) { args->start_slot = first_slot; }
784 0 : if( args->end_slot > last_slot ) { args->end_slot = last_slot; }
785 :
786 0 : FD_LOG_NOTICE(( "copying over rocks db for range [%lu, %lu]", args->start_slot, args->end_slot ));
787 :
788 : /* Copy over all slot indexed columns */
789 0 : for( ulong cf_idx = 1; cf_idx < FD_ROCKSDB_CF_CNT; ++cf_idx ) {
790 0 : fd_rocksdb_copy_over_slot_indexed_range( &big_rocksdb, &mini_rocksdb, cf_idx,
791 0 : args->start_slot, args->end_slot );
792 0 : }
793 0 : FD_LOG_NOTICE(("copied over all slot indexed columns"));
794 :
795 : /* Copy over transactions. This is more complicated because first, a temporary
796 : blockstore will be populated. This will be used to look up transactions
797 : which can be quickly queried */
798 0 : if( args->copy_txn_status ) {
799 0 : init_blockstore( args );
800 : /* Ingest block range into blockstore */
801 0 : ingest_rocksdb( args->alloc, args->rocksdb_list[ 0UL ], args->start_slot,
802 0 : args->end_slot, args->blockstore, 0, ULONG_MAX );
803 :
804 0 : fd_rocksdb_copy_over_txn_status_range( &big_rocksdb, &mini_rocksdb, args->blockstore,
805 0 : args->start_slot, args->end_slot );
806 0 : FD_LOG_NOTICE(( "copied over all transaction statuses" ));
807 0 : } else {
808 0 : FD_LOG_NOTICE(( "skipping copying of transaction statuses" ));
809 0 : }
810 :
811 : /* TODO: Currently, the address signatures column family isn't copied as it
812 : is indexed on the pubkey. */
813 :
814 0 : fd_rocksdb_destroy( &big_rocksdb );
815 0 : fd_rocksdb_destroy( &mini_rocksdb );
816 0 : }
817 :
818 : void
819 0 : ingest( fd_ledger_args_t * args ) {
820 : /* Setup funk, blockstore, epoch_ctx, and slot_ctx */
821 0 : wksp_restore( args );
822 0 : init_funk( args );
823 0 : if( !args->funk_only ) {
824 0 : init_blockstore( args );
825 0 : }
826 :
827 0 : fd_funk_t * funk = args->funk;
828 :
829 0 : fd_alloc_t * alloc = fd_alloc_join( fd_wksp_laddr_fast( fd_funk_wksp( funk ), funk->alloc_gaddr ), 0UL );
830 0 : if( FD_UNLIKELY( !alloc ) ) FD_LOG_ERR(( "fd_alloc_join(gaddr=%#lx) failed", funk->alloc_gaddr ));
831 :
832 0 : fd_valloc_t valloc = allocator_setup( args->wksp, args->allocator );
833 0 : uchar * epoch_ctx_mem = fd_valloc_malloc( valloc, fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( args->vote_acct_max ) );
834 0 : fd_memset( epoch_ctx_mem, 0, fd_exec_epoch_ctx_footprint( args->vote_acct_max ) );
835 0 : fd_exec_epoch_ctx_t * epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, args->vote_acct_max ) );
836 :
837 0 : uchar slot_ctx_mem[FD_EXEC_SLOT_CTX_FOOTPRINT] __attribute__((aligned(FD_EXEC_SLOT_CTX_ALIGN)));
838 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem, valloc ) );
839 0 : slot_ctx->epoch_ctx = epoch_ctx;
840 0 : args->slot_ctx = slot_ctx;
841 :
842 0 : fd_acc_mgr_t mgr[1];
843 0 : slot_ctx->acc_mgr = fd_acc_mgr_new( mgr, funk );
844 0 : slot_ctx->blockstore = args->blockstore;
845 :
846 0 : if( args->status_cache_wksp ) {
847 0 : void * status_cache_mem = fd_wksp_alloc_laddr( args->status_cache_wksp, fd_txncache_align(), fd_txncache_footprint(FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS, FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS, MAX_CACHE_TXNS_PER_SLOT), FD_TXNCACHE_MAGIC );
848 0 : FD_TEST( status_cache_mem );
849 0 : slot_ctx->status_cache = fd_txncache_join( fd_txncache_new( status_cache_mem, FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS, FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS, MAX_CACHE_TXNS_PER_SLOT ) );
850 0 : FD_TEST( slot_ctx->status_cache );
851 0 : }
852 :
853 0 : init_tpool( args );
854 :
855 : /* Load in snapshot(s) */
856 0 : if( args->snapshot ) {
857 0 : fd_snapshot_load( args->snapshot, slot_ctx, args->tpool, args->verify_acc_hash, args->check_acc_hash , FD_SNAPSHOT_TYPE_FULL );
858 0 : FD_LOG_NOTICE(( "imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
859 0 : }
860 0 : if( args->incremental ) {
861 0 : fd_snapshot_load( args->incremental, slot_ctx, args->tpool, args->verify_acc_hash, args->check_acc_hash, FD_SNAPSHOT_TYPE_INCREMENTAL );
862 0 : FD_LOG_NOTICE(( "imported %lu records from incremental snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
863 0 : }
864 :
865 0 : if( args->genesis ) {
866 0 : fd_runtime_read_genesis( slot_ctx, args->genesis, args->snapshot != NULL, NULL );
867 0 : }
868 :
869 0 : if( !args->snapshot && (args->restore_funk != NULL || args->restore != NULL) ) {
870 0 : fd_runtime_recover_banks( slot_ctx, 0, 1 );
871 0 : }
872 :
873 : /* At this point the account state has been ingested into funk. Intake rocksdb */
874 0 : if( args->start_slot == 0 ) {
875 0 : args->start_slot = slot_ctx->slot_bank.slot + 1;
876 0 : }
877 0 : fd_blockstore_t * blockstore = args->blockstore;
878 0 : if( blockstore ) {
879 0 : blockstore->min = blockstore->max = blockstore->lps =
880 0 : blockstore->hcs = blockstore->smr = slot_ctx->slot_bank.slot;
881 0 : }
882 :
883 0 : if( args->funk_only ) {
884 0 : FD_LOG_NOTICE(( "using funk only, skipping blockstore ingest" ));
885 0 : } else if( args->shredcap ) {
886 0 : FD_LOG_NOTICE(( "using shredcap" ));
887 0 : fd_shredcap_populate_blockstore( args->shredcap, blockstore, args->start_slot, args->end_slot );
888 0 : } else if( args->rocksdb_list[ 0UL ] ) {
889 0 : if( args->end_slot >= slot_ctx->slot_bank.slot + args->slot_history_max ) {
890 0 : args->end_slot = slot_ctx->slot_bank.slot + args->slot_history_max - 1;
891 0 : }
892 0 : ingest_rocksdb( args->alloc, args->rocksdb_list[ 0UL ], args->start_slot, args->end_slot,
893 0 : blockstore, args->copy_txn_status, args->trash_hash );
894 0 : }
895 :
896 : /* Verification */
897 0 : for( fd_feature_id_t const * id = fd_feature_iter_init();
898 0 : !fd_feature_iter_done( id );
899 0 : id = fd_feature_iter_next( id ) ) {
900 0 : ulong activated_at = fd_features_get( &slot_ctx->epoch_ctx->features, id );
901 0 : if( activated_at ) {
902 0 : FD_LOG_DEBUG(( "feature %s activated at slot %lu", FD_BASE58_ENC_32_ALLOCA( id->id.key ), activated_at ));
903 0 : }
904 0 : }
905 :
906 0 : if( args->verify_funk ) {
907 0 : FD_LOG_NOTICE(( "verifying funky" ));
908 0 : if( fd_funk_verify( funk ) ) {
909 0 : FD_LOG_ERR(( "verification failed" ));
910 0 : }
911 0 : }
912 :
913 0 : checkpt( args, slot_ctx );
914 :
915 0 : cleanup_funk( args );
916 :
917 0 : cleanup_scratch();
918 0 : }
919 :
920 : int
921 0 : replay( fd_ledger_args_t * args ) {
922 : /* Allows for ingest and direct replay. This can be done with a full checkpoint
923 : that contains a blockstore and funk, a checkpoint that just has funk, or directly
924 : using a rocksdb and snapshot.
925 :
926 : On demand block ingest is enabled by default and can be disabled with
927 : '--on-demand-block-ingest 0'. The number of blocks retained in a blockstore during
928 : on demand block ingest can be set with '--on-demand-block-history <N slots>'
929 :
930 : In order to replay from a checkpoint, use '--checkpoint <path to checkpoint>'.
931 :
932 : To use a checkpoint, but to consume blocks on demand use '--funkonly true'.
933 : This option MUST be used if the checkpoint was generated during a replay with
934 : on demand block ingest.
935 :
936 : For blocks to contain transaction status information use '--txnstatus true'
937 :
938 : Example command loading in from on demand checkpoint and replaying with on demand block ingest.
939 : It creates a checkpoint every 1000 slots.
940 : fd_ledger --funk-restore <CHECKPOINT_TO_LOAD_IN> --cmd replay --page-cnt 20
941 : --abort-on-mismatch 1 --tile-cpus 5-21 --allocator wksp
942 : --rocksdb dump/rocksdb --checkpt-path dump/checkpoint_new
943 : --checkpt-freq 1000 --funk-only 1 --on-demand-block-ingest 1 --funk-page-cnt 350
944 :
945 : Example command directly loading in a rocksdb and snapshot and replaying.
946 : fd_ledger --reset 1 --cmd replay --rocksdb dump/mainnet-257068890/rocksdb --index-max 5000000
947 : --end-slot 257068895 --txn-max 100 --page-cnt 16 --verify-acc-hash 1
948 : --snapshot dump/mainnet-257068890/snapshot-257068890-uRVtagPzKhYorycp4CRtKdWrYPij6iBxCYYXmqRvdSp.tar.zst
949 : --slot-history 5000 --allocator wksp --tile-cpus 5-21 --funk-page-cnt 16
950 : */
951 :
952 0 : wksp_restore( args ); /* Restores checkpointed workspace(s) */
953 :
954 0 : init_funk( args ); /* Joins or creates funk based on if one exists in the workspace */
955 0 : init_blockstore( args ); /* Does the same for the blockstore */
956 :
957 0 : archive_restore( args ); /* Restores checkpointed workspace(s) */
958 :
959 0 : fd_funk_t * funk = args->funk;
960 :
961 : /* Setup slot_ctx */
962 0 : fd_valloc_t valloc = allocator_setup( args->wksp, args->allocator );
963 :
964 0 : void * epoch_ctx_mem = fd_wksp_alloc_laddr( args->wksp, fd_exec_epoch_ctx_align(),
965 0 : fd_exec_epoch_ctx_footprint( args->vote_acct_max ), FD_EXEC_EPOCH_CTX_MAGIC );
966 0 : fd_memset( epoch_ctx_mem, 0, fd_exec_epoch_ctx_footprint( args->vote_acct_max ) );
967 0 : void * slot_ctx_mem = fd_wksp_alloc_laddr( args->wksp, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT, FD_EXEC_SLOT_CTX_MAGIC );
968 0 : args->epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, args->vote_acct_max ) );
969 0 : fd_exec_epoch_ctx_bank_mem_clear( args->epoch_ctx );
970 :
971 0 : args->epoch_ctx->epoch_bank.cluster_version[0] = args->cluster_version[0];
972 0 : args->epoch_ctx->epoch_bank.cluster_version[1] = args->cluster_version[1];
973 0 : args->epoch_ctx->epoch_bank.cluster_version[2] = args->cluster_version[2];
974 :
975 0 : fd_features_enable_cleaned_up( &args->epoch_ctx->features, args->epoch_ctx->epoch_bank.cluster_version );
976 0 : fd_features_enable_one_offs( &args->epoch_ctx->features, args->one_off_features, args->one_off_features_cnt, 0UL );
977 :
978 0 : args->slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem, valloc ) );
979 0 : args->slot_ctx->epoch_ctx = args->epoch_ctx;
980 0 : args->slot_ctx->valloc = valloc;
981 0 : args->slot_ctx->acc_mgr = fd_acc_mgr_new( args->acc_mgr, funk );
982 0 : args->slot_ctx->blockstore = args->blockstore;
983 0 : void * status_cache_mem = fd_wksp_alloc_laddr( args->wksp, FD_TXNCACHE_ALIGN, fd_txncache_footprint( FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS, FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS, MAX_CACHE_TXNS_PER_SLOT), FD_TXNCACHE_MAGIC );
984 0 : args->slot_ctx->status_cache = fd_txncache_join( fd_txncache_new( status_cache_mem, FD_TXNCACHE_DEFAULT_MAX_ROOTED_SLOTS, FD_TXNCACHE_DEFAULT_MAX_LIVE_SLOTS, MAX_CACHE_TXNS_PER_SLOT ) );
985 0 : FD_TEST( args->slot_ctx->status_cache );
986 :
987 0 : init_tpool( args );
988 :
989 : /* Check number of records in funk. If rec_cnt == 0, then it can be assumed
990 : that you need to load in snapshot(s). */
991 0 : ulong rec_cnt = fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) );
992 0 : if( !rec_cnt ) {
993 : /* Load in snapshot(s) */
994 0 : if( args->snapshot ) {
995 0 : fd_snapshot_load( args->snapshot, args->slot_ctx, args->tpool, args->verify_acc_hash, args->check_acc_hash, FD_SNAPSHOT_TYPE_FULL );
996 0 : FD_LOG_NOTICE(( "imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
997 0 : }
998 0 : if( args->incremental ) {
999 0 : fd_snapshot_load( args->incremental, args->slot_ctx, args->tpool, args->verify_acc_hash, args->check_acc_hash, FD_SNAPSHOT_TYPE_INCREMENTAL );
1000 0 : FD_LOG_NOTICE(( "imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
1001 0 : }
1002 0 : if( args->genesis ) {
1003 0 : fd_runtime_read_genesis( args->slot_ctx, args->genesis, args->snapshot != NULL, NULL );
1004 0 : }
1005 0 : } else {
1006 0 : FD_LOG_NOTICE(( "found funk with %lu records", rec_cnt ));
1007 0 : }
1008 :
1009 0 : fd_ledger_main_setup( args );
1010 :
1011 0 : if( !args->on_demand_block_ingest ) {
1012 0 : ingest_rocksdb( args->alloc, args->rocksdb_list[ 0UL ], args->start_slot, args->end_slot, args->blockstore, 0, args->trash_hash );
1013 0 : }
1014 :
1015 0 : FD_LOG_WARNING(( "setup done" ));
1016 :
1017 0 : int ret = runtime_replay( args );
1018 :
1019 0 : fd_ledger_main_teardown( args );
1020 :
1021 0 : cleanup_funk( args );
1022 :
1023 0 : return ret;
1024 0 : }
1025 :
1026 : void
1027 0 : prune( fd_ledger_args_t * args ) {
1028 0 : if( args->restore || args->restore_funk ) {
1029 0 : FD_LOG_NOTICE(("restoring workspace"));
1030 0 : fd_wksp_restore( args->funk_wksp == NULL ? args->wksp : args->funk_wksp, args->restore_funk == NULL ? args->restore : args->restore_funk, args->hashseed );
1031 0 : }
1032 :
1033 : /* Setup data structures required for the unpruned workspace & replay ********/
1034 0 : init_funk( args );
1035 0 : init_blockstore( args );
1036 :
1037 0 : archive_restore( args );
1038 :
1039 0 : fd_funk_t * funk = args->funk;
1040 :
1041 0 : fd_valloc_t valloc = allocator_setup( args->wksp, args->allocator );
1042 :
1043 0 : void * epoch_ctx_mem = fd_wksp_alloc_laddr( args->wksp, fd_exec_epoch_ctx_align(),
1044 0 : fd_exec_epoch_ctx_footprint( args->vote_acct_max ), FD_EXEC_EPOCH_CTX_MAGIC );
1045 0 : fd_memset( epoch_ctx_mem, 0, fd_exec_epoch_ctx_footprint( args->vote_acct_max ) );
1046 0 : void * slot_ctx_mem = fd_wksp_alloc_laddr( args->wksp, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT, FD_EXEC_SLOT_CTX_MAGIC );
1047 0 : args->epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, args->vote_acct_max ) );
1048 0 : args->slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem, valloc ) );
1049 0 : args->slot_ctx->epoch_ctx = args->epoch_ctx;
1050 0 : args->slot_ctx->valloc = valloc;
1051 0 : args->slot_ctx->acc_mgr = fd_acc_mgr_new( args->acc_mgr, funk );
1052 0 : args->slot_ctx->blockstore = args->blockstore;
1053 :
1054 0 : ulong rec_cnt = fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) );
1055 0 : if( !rec_cnt ) {
1056 : /* Load in snapshot(s) */
1057 0 : if( args->snapshot ) {
1058 0 : fd_snapshot_load( args->snapshot, args->slot_ctx, args->tpool, args->verify_acc_hash, args->check_acc_hash, FD_SNAPSHOT_TYPE_FULL );
1059 0 : FD_LOG_NOTICE(( "imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
1060 0 : }
1061 0 : if( args->incremental ) {
1062 0 : fd_snapshot_load( args->incremental, args->slot_ctx, args->tpool, args->verify_acc_hash, args->check_acc_hash, FD_SNAPSHOT_TYPE_INCREMENTAL );
1063 0 : FD_LOG_NOTICE(( "imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
1064 0 : }
1065 0 : }
1066 :
1067 : /* Repeat for the pruned worksapce ******************************************/
1068 : /* Create wksp */
1069 0 : fd_wksp_t * pruned_wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, args->pages_pruned, 0, "prunedwksp", 0UL );
1070 0 : if( pruned_wksp == NULL ) {
1071 0 : FD_LOG_ERR(( "failed to create and attach to a pruned_wksp" ));
1072 0 : }
1073 : /* Create blockstore */
1074 0 : fd_blockstore_t * pruned_blockstore;
1075 0 : void * shmem = fd_wksp_alloc_laddr( pruned_wksp, fd_blockstore_align(), fd_blockstore_footprint(), FD_BLOCKSTORE_MAGIC );
1076 0 : if( shmem == NULL ) {
1077 0 : FD_LOG_ERR(( "failed to allocate a blockstore" ));
1078 0 : }
1079 0 : pruned_blockstore = fd_blockstore_join( fd_blockstore_new( shmem, 1, args->hashseed, args->shred_max,
1080 0 : args->slot_history_max, 22 ) );
1081 0 : if( pruned_blockstore == NULL ) {
1082 0 : fd_wksp_free_laddr( shmem );
1083 0 : FD_LOG_ERR(( "failed to allocate a blockstore" ));
1084 0 : }
1085 0 : FD_LOG_NOTICE(( "pruned blockstore at global address 0x%016lx", fd_wksp_gaddr_fast( pruned_wksp, shmem ) ));
1086 :
1087 : /* Create funk */
1088 0 : fd_funk_t * pruned_funk = NULL;
1089 0 : shmem = fd_wksp_alloc_laddr( pruned_wksp, fd_funk_align(), fd_funk_footprint(), 1 );
1090 0 : if( shmem == NULL ) {
1091 0 : FD_LOG_ERR(( "failed to allocate a funky" ));
1092 0 : }
1093 0 : pruned_funk = fd_funk_join( fd_funk_new( shmem, 1, args->hashseed,
1094 0 : args->txns_max, args->index_max_pruned ) );
1095 0 : if( pruned_funk == NULL ) {
1096 0 : fd_wksp_free_laddr( shmem );
1097 0 : FD_LOG_ERR(( "failed to allocate a funky" ));
1098 0 : }
1099 0 : FD_LOG_NOTICE(( "pruned funky at global address 0x%016lx", fd_wksp_gaddr_fast( pruned_wksp, shmem ) ));
1100 :
1101 : /* Junk xid for pruning transaction */
1102 0 : fd_funk_txn_xid_t prune_xid = {0};
1103 0 : fd_memset( &prune_xid, 0x42, sizeof(fd_funk_txn_xid_t) );
1104 0 : fd_funk_start_write( pruned_funk );
1105 0 : fd_funk_txn_t * prune_txn = fd_funk_txn_prepare( pruned_funk, NULL, &prune_xid, 1 );
1106 0 : fd_funk_end_write( pruned_funk );
1107 0 : FD_TEST(( !!prune_txn ));
1108 :
1109 : /* Setup slot/epoch contexts */
1110 0 : fd_valloc_t pruned_valloc = allocator_setup( pruned_wksp, args->allocator );
1111 :
1112 0 : void * epoch_ctx_mem_pruned = fd_wksp_alloc_laddr( pruned_wksp, fd_exec_epoch_ctx_align(),
1113 0 : fd_exec_epoch_ctx_footprint( args->vote_acct_max ), FD_EXEC_EPOCH_CTX_MAGIC );
1114 0 : fd_memset( epoch_ctx_mem_pruned, 0, fd_exec_epoch_ctx_footprint( args->vote_acct_max ) );
1115 0 : void * slot_ctx_mem_pruned = fd_wksp_alloc_laddr( pruned_wksp, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT, FD_EXEC_SLOT_CTX_MAGIC );
1116 0 : fd_exec_epoch_ctx_t * epoch_ctx_pruned = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem_pruned, args->vote_acct_max ) );
1117 0 : fd_exec_slot_ctx_t * slot_ctx_pruned = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem_pruned, pruned_valloc ) );
1118 0 : slot_ctx_pruned->epoch_ctx = epoch_ctx_pruned;
1119 0 : slot_ctx_pruned->valloc = pruned_valloc;
1120 0 : fd_acc_mgr_t acc_mgr_pruned[1];
1121 0 : slot_ctx_pruned->acc_mgr = fd_acc_mgr_new( acc_mgr_pruned, pruned_funk );
1122 0 : slot_ctx_pruned->blockstore = pruned_blockstore;
1123 :
1124 0 : args->pruned_funk = pruned_funk;
1125 :
1126 : /* Replay through the desired slot range ************************************/
1127 :
1128 0 : init_tpool( args );
1129 0 : fd_ledger_main_setup( args );
1130 0 : runtime_replay( args );
1131 :
1132 0 : FD_LOG_NOTICE(("There are currently %lu records in the pruned funk", fd_funk_rec_cnt( fd_funk_rec_map( pruned_funk, pruned_wksp ) ) ));
1133 :
1134 : /* Reset the unpruned wksp, reload snapshot *********************************/
1135 : /* Reset the wksp */
1136 0 : fd_funk_delete( fd_funk_leave( args->funk ) );
1137 0 : ulong funk_tag = FD_FUNK_MAGIC;
1138 0 : fd_wksp_tag_free( args->wksp, &funk_tag, 1 );
1139 0 : fd_wksp_reset( args->wksp, args->hashseed );
1140 0 : fd_wksp_reset( args->funk_wksp, args->hashseed );
1141 :
1142 : /* Setup funk again */
1143 0 : if( args->restore || args->restore_funk ) {
1144 0 : FD_LOG_NOTICE(("restoring workspace"));
1145 0 : fd_wksp_restore( args->funk_wksp == NULL ? args->wksp : args->funk_wksp, args->restore_funk == NULL ? args->restore : args->restore_funk, args->hashseed );
1146 0 : }
1147 0 : init_funk( args );
1148 0 : init_blockstore( args );
1149 0 : init_scratch( args->wksp );
1150 :
1151 : /* Setup contexts */
1152 0 : valloc = allocator_setup( args->wksp, args->allocator );
1153 :
1154 0 : epoch_ctx_mem = fd_wksp_alloc_laddr( args->wksp, fd_exec_epoch_ctx_align(),
1155 0 : fd_exec_epoch_ctx_footprint( args->vote_acct_max ), FD_EXEC_EPOCH_CTX_MAGIC );
1156 0 : fd_memset( epoch_ctx_mem, 0, fd_exec_epoch_ctx_footprint( args->vote_acct_max ) );
1157 0 : slot_ctx_mem = fd_wksp_alloc_laddr( args->wksp, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT, FD_EXEC_SLOT_CTX_MAGIC );
1158 0 : args->epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, args->vote_acct_max ) );
1159 0 : args->slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem, valloc ) );
1160 0 : args->slot_ctx->epoch_ctx = args->epoch_ctx;
1161 0 : args->slot_ctx->valloc = valloc;
1162 0 : fd_acc_mgr_t mgr[1];
1163 0 : args->slot_ctx->acc_mgr = fd_acc_mgr_new( mgr, args->funk );
1164 :
1165 : /* Load in snapshot(s) */
1166 0 : if( args->snapshot ) {
1167 0 : fd_snapshot_load( args->snapshot, args->slot_ctx, args->tpool, 0, 0, FD_SNAPSHOT_TYPE_FULL );
1168 0 : FD_LOG_NOTICE(( "reload: imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
1169 0 : }
1170 0 : if( args->incremental ) {
1171 0 : fd_snapshot_load( args->incremental, args->slot_ctx, args->tpool, 0, 0, FD_SNAPSHOT_TYPE_INCREMENTAL );
1172 0 : FD_LOG_NOTICE(( "reload: imported %lu records from snapshot", fd_funk_rec_cnt( fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) ) ));
1173 0 : }
1174 :
1175 : /* Copy over funk record state **********************************************/
1176 : /* After replaying, update all touched accounts to contain the data that is
1177 : present before execution begins. Look up the corresponding account in the
1178 : unpruned funk and copy over the contents */
1179 0 : fd_funk_t * unpruned_funk = args->funk;
1180 0 : fd_funk_start_write( pruned_funk );
1181 0 : fd_funk_start_write( unpruned_funk );
1182 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( pruned_funk, fd_funk_wksp( pruned_funk ) );
1183 0 : ulong txn_rec_cnt = 0UL;
1184 0 : for( const fd_funk_rec_t * rec = fd_funk_txn_rec_head( prune_txn, rec_map );
1185 0 : rec; rec = fd_funk_txn_next_rec( pruned_funk, rec ) ) {
1186 :
1187 0 : const fd_funk_rec_t * original_rec = fd_funk_rec_query_global( unpruned_funk, NULL, rec->pair.key, NULL );
1188 0 : if( original_rec != NULL ) {
1189 0 : txn_rec_cnt++;
1190 0 : fd_funk_rec_t * mod_rec = fd_funk_rec_modify( pruned_funk, rec );
1191 0 : mod_rec = fd_funk_val_copy( mod_rec, fd_funk_val_const( original_rec, fd_funk_wksp( unpruned_funk ) ),
1192 0 : fd_funk_val_sz( original_rec ), fd_funk_val_sz( original_rec ),
1193 0 : fd_funk_alloc( pruned_funk, pruned_wksp ), pruned_wksp, NULL );
1194 0 : FD_TEST(( memcmp( fd_funk_val( original_rec, fd_funk_wksp( unpruned_funk ) ), fd_funk_val_const( rec, pruned_wksp ),
1195 0 : fd_funk_val_sz( original_rec ) ) == 0 ));
1196 0 : } else {
1197 0 : fd_funk_rec_t * mod_rec = fd_funk_rec_modify( pruned_funk, rec );
1198 0 : int res = fd_funk_rec_remove( pruned_funk, mod_rec, 1 );
1199 0 : FD_TEST(( res == 0 ));
1200 0 : }
1201 0 : }
1202 0 : FD_LOG_NOTICE(( "Copied over %lu records from transactions", txn_rec_cnt ));
1203 :
1204 : /* Repeat above steps with all features */
1205 0 : ulong features_cnt = 0UL;
1206 0 : for( fd_feature_id_t const * id = fd_feature_iter_init();
1207 0 : !fd_feature_iter_done( id ); id = fd_feature_iter_next( id ) ) {
1208 0 : features_cnt++;
1209 :
1210 0 : fd_pubkey_t const * pubkey = (fd_pubkey_t *) id->id.key;
1211 0 : fd_funk_rec_key_t feature_id = fd_acc_funk_key( pubkey );
1212 0 : fd_funk_rec_t const * feature_rec = fd_funk_rec_query_global( unpruned_funk, NULL, &feature_id, NULL );
1213 0 : if( !feature_rec ) {
1214 0 : continue;
1215 0 : }
1216 0 : fd_funk_rec_t * new_feature_rec = fd_funk_rec_write_prepare( pruned_funk, prune_txn, &feature_id,
1217 0 : 0, 1, NULL, NULL );
1218 0 : FD_TEST(( !!new_feature_rec ));
1219 0 : new_feature_rec = fd_funk_val_copy( new_feature_rec, fd_funk_val_const( feature_rec, fd_funk_wksp( unpruned_funk ) ),
1220 0 : fd_funk_val_sz( feature_rec ), fd_funk_val_sz( feature_rec ),
1221 0 : fd_funk_alloc( pruned_funk, fd_funk_wksp( pruned_funk ) ), pruned_wksp, NULL );
1222 0 : FD_TEST(( !!new_feature_rec ));
1223 0 : }
1224 0 : FD_LOG_NOTICE(( "Copied over %lu features", features_cnt ));
1225 :
1226 : /* Do the same with the epoch/slot bank keys and sysvars */
1227 0 : fd_runtime_recover_banks( args->slot_ctx, 0, 1 );
1228 :
1229 0 : fd_funk_rec_key_t id_epoch_bank = fd_runtime_epoch_bank_key();
1230 0 : fd_funk_rec_key_t id_slot_bank = fd_runtime_slot_bank_key();
1231 0 : fd_funk_rec_key_t recent_block_hashes = fd_acc_funk_key( &fd_sysvar_recent_block_hashes_id );
1232 0 : fd_funk_rec_key_t clock = fd_acc_funk_key( &fd_sysvar_clock_id );
1233 0 : fd_funk_rec_key_t slot_history = fd_acc_funk_key( &fd_sysvar_slot_history_id );
1234 0 : fd_funk_rec_key_t slot_hashes = fd_acc_funk_key( &fd_sysvar_slot_hashes_id );
1235 0 : fd_funk_rec_key_t epoch_schedule = fd_acc_funk_key( &fd_sysvar_epoch_schedule_id );
1236 0 : fd_funk_rec_key_t epoch_rewards = fd_acc_funk_key( &fd_sysvar_epoch_rewards_id );
1237 0 : fd_funk_rec_key_t sysvar_fees = fd_acc_funk_key( &fd_sysvar_fees_id );
1238 0 : fd_funk_rec_key_t rent = fd_acc_funk_key( &fd_sysvar_rent_id );
1239 0 : fd_funk_rec_key_t stake_history = fd_acc_funk_key( &fd_sysvar_stake_history_id );
1240 0 : fd_funk_rec_key_t owner = fd_acc_funk_key( &fd_sysvar_owner_id );
1241 0 : fd_funk_rec_key_t last_restart_slot = fd_acc_funk_key( &fd_sysvar_last_restart_slot_id );
1242 0 : fd_funk_rec_key_t instructions = fd_acc_funk_key( &fd_sysvar_instructions_id );
1243 0 : fd_funk_rec_key_t incinerator = fd_acc_funk_key( &fd_sysvar_incinerator_id );
1244 :
1245 0 : fd_funk_rec_key_t records[15] = { id_epoch_bank, id_slot_bank, recent_block_hashes, clock, slot_history,
1246 0 : slot_hashes, epoch_schedule, epoch_rewards, sysvar_fees, rent,
1247 0 : stake_history, owner, last_restart_slot, instructions, incinerator };
1248 0 : for( uint i = 0; i < sizeof( records ) / sizeof( fd_funk_rec_key_t ); ++i ) {
1249 0 : fd_funk_rec_t const * original_rec = fd_funk_rec_query_global( unpruned_funk, NULL, &records[i], NULL );
1250 0 : if( !original_rec ) {
1251 : /* Some sysvars aren't touched during execution. Not a problem. */
1252 0 : char record[ FD_BASE58_ENCODED_32_SZ ];
1253 0 : fd_acct_addr_cstr( record, (uchar*) &records[i] );
1254 0 : FD_LOG_DEBUG(( "Record is not in account pubkey=%s at index=%u", record, i ));
1255 0 : continue;
1256 0 : }
1257 0 : fd_funk_rec_t * new_rec = fd_funk_rec_write_prepare( pruned_funk, prune_txn, &records[i], 0, 1, NULL, NULL );
1258 0 : FD_TEST(( !!new_rec ));
1259 0 : new_rec = fd_funk_val_copy( new_rec, fd_funk_val_const( original_rec, fd_funk_wksp( unpruned_funk) ),
1260 0 : fd_funk_val_sz( original_rec ), fd_funk_val_sz( original_rec ),
1261 0 : fd_funk_alloc( pruned_funk, pruned_wksp ), pruned_wksp, NULL );
1262 0 : FD_TEST( memcmp( fd_funk_val( original_rec, fd_funk_wksp( unpruned_funk ) ), fd_funk_val_const( new_rec, pruned_wksp ),
1263 0 : fd_funk_val_sz( original_rec ) ) == 0 );
1264 0 : FD_TEST(( !!new_rec ));
1265 0 : }
1266 0 : FD_LOG_NOTICE(( "Copied over all sysvars and bank keys" ));
1267 :
1268 : /* Publish transaction with pruned records to the root of funk */
1269 0 : if( fd_funk_txn_publish( pruned_funk, prune_txn, 1 )==0 ) {
1270 0 : FD_LOG_ERR(( "failed to publish transaction into pruned funk" ));
1271 0 : }
1272 :
1273 : /* Verify that the pruned records are in the funk */
1274 0 : FD_LOG_NOTICE(( "Pruned funk record count is %lu", fd_funk_rec_global_cnt( pruned_funk, pruned_wksp ) ));
1275 :
1276 0 : fd_funk_leave( unpruned_funk );
1277 :
1278 0 : if( fd_funk_verify( pruned_funk ) ) {
1279 0 : FD_LOG_ERR(( "pruned funk verification failed" ));
1280 0 : }
1281 :
1282 0 : slot_ctx_pruned->funk_txn = NULL;
1283 0 : fd_funk_end_write( pruned_funk );
1284 0 : fd_funk_end_write( unpruned_funk );
1285 0 : args->funk = pruned_funk;
1286 0 : args->wksp = pruned_wksp;
1287 0 : checkpt( args, slot_ctx_pruned );
1288 :
1289 0 : cleanup_funk( args );
1290 :
1291 0 : cleanup_scratch();
1292 0 : }
1293 :
1294 : /* Parse user arguments and setup shared data structures used across commands */
1295 : int
1296 0 : initial_setup( int argc, char ** argv, fd_ledger_args_t * args ) {
1297 0 : if( FD_UNLIKELY( argc==1 ) ) {
1298 0 : return 1;
1299 0 : }
1300 :
1301 0 : fd_boot( &argc, &argv );
1302 0 : fd_flamenco_boot( &argc, &argv );
1303 :
1304 0 : char const * wksp_name = fd_env_strip_cmdline_cstr ( &argc, &argv, "--wksp-name", NULL, NULL );
1305 0 : ulong funk_page_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--funk-page-cnt", NULL, 5 );
1306 0 : ulong page_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt", NULL, 5 );
1307 0 : int reset = fd_env_strip_cmdline_int ( &argc, &argv, "--reset", NULL, 0 );
1308 0 : char const * cmd = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cmd", NULL, NULL );
1309 0 : ulong index_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--index-max", NULL, 450000000 );
1310 0 : ulong txns_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--txn-max", NULL, 1000 );
1311 0 : char const * funk_file = fd_env_strip_cmdline_cstr ( &argc, &argv, "--funk-file", NULL, NULL );
1312 0 : int verify_funk = fd_env_strip_cmdline_int ( &argc, &argv, "--verify-funky", NULL, 0 );
1313 0 : char const * snapshot = fd_env_strip_cmdline_cstr ( &argc, &argv, "--snapshot", NULL, NULL );
1314 0 : char const * incremental = fd_env_strip_cmdline_cstr ( &argc, &argv, "--incremental", NULL, NULL );
1315 0 : char const * genesis = fd_env_strip_cmdline_cstr ( &argc, &argv, "--genesis", NULL, NULL );
1316 0 : int copy_txn_status = fd_env_strip_cmdline_int ( &argc, &argv, "--copy-txn-status", NULL, 0 );
1317 0 : ulong slot_history_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--slot-history", NULL, FD_BLOCK_MAX );
1318 0 : ulong shred_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--shred-max", NULL, 1UL << 17 );
1319 0 : ulong start_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--start-slot", NULL, 0UL );
1320 0 : ulong end_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--end-slot", NULL, ULONG_MAX );
1321 0 : uint verify_acc_hash = fd_env_strip_cmdline_uint ( &argc, &argv, "--verify-acc-hash", NULL, 0 );
1322 0 : uint check_acc_hash = fd_env_strip_cmdline_uint ( &argc, &argv, "--check-acc-hash", NULL, 0 );
1323 0 : char const * restore = fd_env_strip_cmdline_cstr ( &argc, &argv, "--restore", NULL, NULL );
1324 0 : char const * restore_funk = fd_env_strip_cmdline_cstr ( &argc, &argv, "--funk-restore", NULL, NULL );
1325 0 : char const * restore_archive = fd_env_strip_cmdline_cstr ( &argc, &argv, "--restore-archive", NULL, NULL );
1326 0 : char const * shredcap = fd_env_strip_cmdline_cstr ( &argc, &argv, "--shred-cap", NULL, NULL );
1327 0 : ulong trash_hash = fd_env_strip_cmdline_ulong( &argc, &argv, "--trash-hash", NULL, ULONG_MAX );
1328 0 : char const * mini_db_dir = fd_env_strip_cmdline_cstr ( &argc, &argv, "--minified-rocksdb", NULL, NULL );
1329 0 : ulong index_max_pruned = fd_env_strip_cmdline_ulong( &argc, &argv, "--pruned-index-max", NULL, 450000000 );
1330 0 : ulong pages_pruned = fd_env_strip_cmdline_ulong( &argc, &argv, "--pruned-page-cnt", NULL, ULONG_MAX );
1331 0 : int funk_only = fd_env_strip_cmdline_int ( &argc, &argv, "--funk-only", NULL, 0 );
1332 0 : char const * checkpt = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt", NULL, NULL );
1333 0 : char const * checkpt_funk = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-funk", NULL, NULL );
1334 0 : char const * checkpt_archive = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-archive", NULL, NULL );
1335 0 : char const * capture_fpath = fd_env_strip_cmdline_cstr ( &argc, &argv, "--capture-solcap", NULL, NULL );
1336 0 : int capture_txns = fd_env_strip_cmdline_int ( &argc, &argv, "--capture-txns", NULL, 1 );
1337 0 : char const * checkpt_path = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-path", NULL, NULL );
1338 0 : ulong checkpt_freq = fd_env_strip_cmdline_ulong( &argc, &argv, "--checkpt-freq", NULL, ULONG_MAX );
1339 0 : int checkpt_mismatch = fd_env_strip_cmdline_int ( &argc, &argv, "--checkpt-mismatch", NULL, 0 );
1340 0 : char const * allocator = fd_env_strip_cmdline_cstr ( &argc, &argv, "--allocator", NULL, "wksp" );
1341 0 : int abort_on_mismatch = fd_env_strip_cmdline_int ( &argc, &argv, "--abort-on-mismatch", NULL, 1 );
1342 0 : int on_demand_block_ingest = fd_env_strip_cmdline_int ( &argc, &argv, "--on-demand-block-ingest", NULL, 1 );
1343 0 : ulong on_demand_block_history = fd_env_strip_cmdline_ulong( &argc, &argv, "--on-demand-block-history", NULL, 100 );
1344 0 : int dump_insn_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-insn-to-pb", NULL, 0 );
1345 0 : int dump_txn_to_pb = fd_env_strip_cmdline_int ( &argc, &argv, "--dump-txn-to-pb", NULL, 0 );
1346 0 : ulong dump_proto_start_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--dump-proto-start-slot", NULL, 0 );
1347 0 : char const * dump_proto_sig_filter = fd_env_strip_cmdline_cstr ( &argc, &argv, "--dump-proto-sig-filter", NULL, NULL );
1348 0 : char const * dump_proto_output_dir = fd_env_strip_cmdline_cstr ( &argc, &argv, "--dump-proto-output-dir", NULL, NULL );
1349 0 : ulong vote_acct_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--vote_acct_max", NULL, 2000000UL );
1350 0 : char const * rocksdb_list = fd_env_strip_cmdline_cstr ( &argc, &argv, "--rocksdb", NULL, NULL );
1351 0 : char const * rocksdb_list_starts = fd_env_strip_cmdline_cstr ( &argc, &argv, "--rocksdb-starts", NULL, NULL );
1352 0 : char const * cluster_version = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cluster-version", NULL, "2.0.0" );
1353 0 : char const * checkpt_status_cache = fd_env_strip_cmdline_cstr ( &argc, &argv, "--checkpt-status-cache", NULL, NULL );
1354 0 : char const * one_off_features = fd_env_strip_cmdline_cstr ( &argc, &argv, "--one-off-features", NULL, NULL );
1355 0 : char const * lthash = fd_env_strip_cmdline_cstr ( &argc, &argv, "--lthash", NULL, "false" );
1356 :
1357 : // TODO: Add argument validation. Make sure that we aren't including any arguments that aren't parsed for
1358 :
1359 0 : char hostname[64];
1360 0 : gethostname( hostname, sizeof(hostname) );
1361 0 : ulong hashseed = fd_hash( 0, hostname, strnlen( hostname, sizeof(hostname) ) );
1362 0 : args->hashseed = (uint)hashseed;
1363 :
1364 : /* Setup workspace */
1365 0 : fd_wksp_t * wksp;
1366 0 : if( wksp_name == NULL ) {
1367 0 : FD_LOG_NOTICE(( "--wksp not specified, using an anonymous local workspace" ));
1368 0 : wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, page_cnt, 0, "wksp", 0UL );
1369 0 : } else {
1370 0 : fd_shmem_info_t shmem_info[1];
1371 0 : if( FD_UNLIKELY( fd_shmem_info( wksp_name, 0UL, shmem_info ) ) )
1372 0 : FD_LOG_ERR(( "unable to query region \"%s\"\n\tprobably does not exist or bad permissions", wksp_name ));
1373 0 : wksp = fd_wksp_attach( wksp_name );
1374 0 : }
1375 :
1376 0 : if( wksp == NULL ) {
1377 0 : FD_LOG_ERR(( "failed to attach to workspace %s", wksp_name ));
1378 0 : }
1379 0 : if( reset ) {
1380 0 : fd_wksp_reset( wksp, args->hashseed );
1381 0 : }
1382 0 : args->wksp = wksp;
1383 :
1384 0 : init_scratch( wksp );
1385 :
1386 0 : if( checkpt_status_cache && checkpt_status_cache[0] != '\0' ) {
1387 0 : FD_LOG_NOTICE(( "Creating status cache wksp" ));
1388 0 : fd_wksp_t * status_cache_wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 23UL, 0, "status_cache_wksp", 0UL );
1389 0 : fd_wksp_reset( status_cache_wksp, args->hashseed );
1390 0 : args->status_cache_wksp = status_cache_wksp;
1391 0 : } else {
1392 0 : args->status_cache_wksp = NULL;
1393 0 : }
1394 :
1395 : /* Setup alloc and valloc */
1396 0 : #define FD_ALLOC_TAG (422UL)
1397 0 : void * alloc_shmem = fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), FD_ALLOC_TAG );
1398 0 : if( FD_UNLIKELY( !alloc_shmem ) ) { FD_LOG_ERR( ( "fd_alloc too large for workspace" ) ); }
1399 0 : void * alloc_shalloc = fd_alloc_new( alloc_shmem, FD_ALLOC_TAG );
1400 0 : if( FD_UNLIKELY( !alloc_shalloc ) ) { FD_LOG_ERR( ( "fd_allow_new failed" ) ); }
1401 0 : fd_alloc_t * alloc = fd_alloc_join( alloc_shalloc, FD_ALLOC_TAG );
1402 0 : args->alloc = alloc;
1403 0 : #undef FD_ALLOC_TAG
1404 :
1405 : /* Copy over arguments */
1406 0 : args->cmd = cmd;
1407 0 : args->start_slot = start_slot;
1408 0 : args->end_slot = end_slot;
1409 0 : args->checkpt = checkpt;
1410 0 : args->checkpt_funk = checkpt_funk;
1411 0 : args->checkpt_archive = checkpt_archive;
1412 0 : args->shred_max = shred_max;
1413 0 : args->slot_history_max = slot_history_max;
1414 0 : args->txns_max = txns_max;
1415 0 : args->index_max = index_max;
1416 0 : args->funk_page_cnt = funk_page_cnt;
1417 0 : args->funk_file = funk_file;
1418 0 : args->restore = restore;
1419 0 : args->restore_funk = restore_funk;
1420 0 : args->restore_archive = restore_archive;
1421 0 : args->mini_db_dir = mini_db_dir;
1422 0 : args->funk_only = funk_only;
1423 0 : args->copy_txn_status = copy_txn_status;
1424 0 : args->snapshot = snapshot;
1425 0 : args->incremental = incremental;
1426 0 : args->genesis = genesis;
1427 0 : args->shredcap = shredcap;
1428 0 : args->verify_funk = verify_funk;
1429 0 : args->check_acc_hash = check_acc_hash;
1430 0 : args->verify_acc_hash = verify_acc_hash;
1431 0 : args->trash_hash = trash_hash;
1432 0 : args->index_max_pruned = index_max_pruned;
1433 0 : args->pages_pruned = pages_pruned;
1434 0 : args->capture_fpath = capture_fpath;
1435 0 : args->capture_txns = capture_txns;
1436 0 : args->checkpt_path = checkpt_path;
1437 0 : args->checkpt_freq = checkpt_freq;
1438 0 : args->checkpt_mismatch = checkpt_mismatch;
1439 0 : args->allocator = allocator;
1440 0 : args->abort_on_mismatch = abort_on_mismatch;
1441 0 : args->on_demand_block_ingest = on_demand_block_ingest;
1442 0 : args->on_demand_block_history = on_demand_block_history;
1443 0 : args->dump_insn_to_pb = dump_insn_to_pb;
1444 0 : args->dump_txn_to_pb = dump_txn_to_pb;
1445 0 : args->dump_proto_start_slot = dump_proto_start_slot;
1446 0 : args->dump_proto_sig_filter = dump_proto_sig_filter;
1447 0 : args->dump_proto_output_dir = dump_proto_output_dir;
1448 0 : args->vote_acct_max = vote_acct_max;
1449 0 : args->rocksdb_list_cnt = 0UL;
1450 0 : args->checkpt_status_cache = checkpt_status_cache;
1451 0 : args->one_off_features_cnt = 0UL;
1452 0 : parse_one_off_features( args, one_off_features );
1453 0 : parse_rocksdb_list( args, rocksdb_list, rocksdb_list_starts );
1454 :
1455 0 : if( FD_UNLIKELY( sscanf( cluster_version, "%u.%u.%u", &args->cluster_version[0], &args->cluster_version[1], &args->cluster_version[2] )!=3 ) ) {
1456 0 : FD_LOG_ERR(( "failed to decode cluster version" ));;
1457 0 : }
1458 :
1459 0 : if( args->rocksdb_list_cnt==1UL ) {
1460 0 : FD_LOG_NOTICE(( "rocksdb=%s", args->rocksdb_list[0] ));
1461 0 : } else {
1462 0 : for( ulong i=0UL; i<args->rocksdb_list_cnt; ++i ) {
1463 0 : FD_LOG_NOTICE(( "rocksdb_list[ %lu ]=%s slot=%lu", i, args->rocksdb_list[i], args->rocksdb_list_slot[i-1] ));
1464 0 : }
1465 0 : }
1466 :
1467 0 : args->lthash = lthash;
1468 :
1469 0 : return 0;
1470 0 : }
1471 :
1472 : int main( int argc, char ** argv ) {
1473 : fd_ledger_args_t args = {0};
1474 : initial_setup( argc, argv, &args );
1475 :
1476 : if( args.cmd == NULL ) {
1477 : FD_LOG_ERR(( "no command specified" ));
1478 : } else if( strcmp( args.cmd, "replay" ) == 0 ) {
1479 : return replay( &args );
1480 : } else if( strcmp( args.cmd, "ingest" ) == 0 ) {
1481 : ingest( &args );
1482 : } else if( strcmp( args.cmd, "minify" ) == 0 ) {
1483 : minify( &args );
1484 : } else if( strcmp( args.cmd, "prune" ) == 0 ) {
1485 : prune( &args );
1486 : } else {
1487 : FD_LOG_ERR(( "unknown command=%s", args.cmd ));
1488 : }
1489 : return 0;
1490 : }
|