Line data Source code
1 : #include "../fd_flamenco.h"
2 : #include "fd_solcap_proto.h"
3 : #include "fd_solcap_reader.h"
4 : #include "fd_solcap.pb.h"
5 : #include "../nanopb/pb_decode.h"
6 : #include "../../util/textstream/fd_textstream.h"
7 : #include "../runtime/fd_executor.h"
8 : #include <errno.h>
9 : #include <stdio.h>
10 :
11 : static int
12 0 : usage( void ) {
13 0 : fprintf( stderr,
14 0 : "Usage: fd_solcap_yaml [options] {FILE}\n"
15 0 : "\n"
16 0 : "Print a runtime capture file as YAML.\n"
17 0 : "\n"
18 0 : "Options:\n"
19 0 : " --page-sz {gigantic|huge|normal} Page size\n"
20 0 : " --page-cnt {count} Page count\n"
21 0 : " --scratch-mb 1024 Scratch mem MiB\n"
22 0 : " -v {level} YAML verbosity\n"
23 0 : " --start-slot {slot} Start slot\n"
24 0 : " --end-slot {slot} End slot\n"
25 0 : "\n" );
26 0 : return 0;
27 0 : }
28 :
29 : /* process_account reads and dumps a single account. If verbose>4,
30 : includes a base64 dump of account content. */
31 :
32 : static int
33 : process_account( FILE * file,
34 : long goff,
35 0 : int verbose ) {
36 :
37 : /* Remember stream cursor */
38 :
39 0 : long pos = ftell( file );
40 0 : if( FD_UNLIKELY( pos<0L ) ) {
41 0 : FD_LOG_ERR(( "ftell failed (%d-%s)", errno, strerror( errno ) ));
42 0 : return 0;
43 0 : }
44 :
45 : /* Seek to chunk */
46 :
47 0 : if( FD_UNLIKELY( 0!=fseek( file, goff, SEEK_SET ) ) ) {
48 0 : FD_LOG_ERR(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
49 0 : return 0;
50 0 : }
51 :
52 : /* Read chunk */
53 :
54 0 : fd_solcap_chunk_t chunk[1];
55 0 : ulong n = fread( chunk, sizeof(fd_solcap_chunk_t), 1UL, file );
56 0 : if( FD_UNLIKELY( n!=1UL ) ) {
57 0 : FD_LOG_ERR(( "fread chunk failed (%d-%s)", errno, strerror( errno ) ));
58 0 : return 0;
59 0 : }
60 0 : if( FD_UNLIKELY( chunk->magic != FD_SOLCAP_V1_ACCT_MAGIC ) ) {
61 0 : FD_LOG_ERR(( "expected account table chunk at %#lx, got magic=0x%016lx", (ulong)goff, chunk->magic ));
62 0 : return 0;
63 0 : }
64 :
65 : /* Read metadata */
66 :
67 0 : fd_solcap_AccountMeta meta[1];
68 0 : do {
69 :
70 0 : uchar meta_buf[ 512UL ];
71 0 : ulong meta_sz = chunk->meta_sz;
72 0 : if( FD_UNLIKELY( meta_sz > sizeof(meta_buf ) ) )
73 0 : FD_LOG_ERR(( "invalid account meta size (%lu)", meta_sz ));
74 :
75 0 : if( FD_UNLIKELY( 0!=fseek( file, (long)chunk->meta_coff - (long)sizeof(fd_solcap_chunk_t), SEEK_CUR ) ) )
76 0 : FD_LOG_ERR(( "fseek to account meta failed (%d-%s)", errno, strerror( errno ) ));
77 :
78 0 : if( FD_UNLIKELY( meta_sz != fread( meta_buf, 1UL, meta_sz, file ) ) )
79 0 : FD_LOG_ERR(( "fread account meta failed (%d-%s)", errno, strerror( errno ) ));
80 :
81 0 : pb_istream_t stream = pb_istream_from_buffer( meta_buf, meta_sz );
82 0 : if( FD_UNLIKELY( !pb_decode( &stream, fd_solcap_AccountMeta_fields, meta ) ) ) {
83 0 : FD_LOG_HEXDUMP_DEBUG(( "account meta", meta_buf, meta_sz ));
84 0 : FD_LOG_ERR(( "pb_decode account meta failed (%s)", PB_GET_ERROR(&stream) ));
85 0 : }
86 :
87 0 : long rewind = (long)chunk->meta_coff + (long)meta_sz;
88 0 : if( FD_UNLIKELY( 0!=fseek( file, -rewind, SEEK_CUR ) ) )
89 0 : FD_LOG_ERR(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
90 :
91 0 : } while(0);
92 :
93 0 : printf(
94 0 : " owner: '%s'\n"
95 0 : " lamports: %lu\n"
96 0 : " slot: %lu\n"
97 0 : " rent_epoch: %lu\n"
98 0 : " executable: %s\n"
99 0 : " data_sz: %lu\n",
100 0 : FD_BASE58_ENC_32_ALLOCA( meta->owner ),
101 0 : meta->lamports,
102 0 : meta->slot,
103 0 : meta->rent_epoch,
104 0 : meta->executable ? "true" : "false",
105 0 : meta->data_sz );
106 :
107 : /* Optionally print account data */
108 :
109 0 : if( verbose>4 ) {
110 0 : printf( " data: '" );
111 :
112 : /* Seek to account data */
113 0 : if( FD_UNLIKELY( 0!=fseek( file, goff + meta->data_coff, SEEK_SET ) ) )
114 0 : FD_LOG_ERR(( "fseek to account data failed (%d-%s)", errno, strerror( errno ) ));
115 :
116 : /* Streaming Base64 encode.
117 :
118 : Process inputs in "parts" with length divided by 3, 4 such that
119 : no padding is in the middle of the encoding. Technically Base64
120 : allows padding in the middle, but it's cleaner to only have
121 : padding at the end of the message. */
122 0 : # define PART_RAW_SZ (720UL)
123 0 : # define PART_BLK_SZ (4UL*(PART_RAW_SZ+2UL)/3UL) /* see fd_textstream_encode_base64 */
124 0 : ulong data_sz = meta->data_sz;
125 0 : while( data_sz>0UL ) {
126 0 : ulong n = fd_ulong_min( data_sz, PART_RAW_SZ );
127 :
128 : /* Read chunk */
129 0 : uchar buf[ PART_RAW_SZ ];
130 0 : if( FD_UNLIKELY( 1UL!=fread( buf, n, 1UL, file ) ) )
131 0 : FD_LOG_ERR(( "fread account data failed (%d-%s)", errno, strerror( errno ) ));
132 :
133 : /* Encode chunk */
134 0 : fd_valloc_t valloc = fd_scratch_virtual();
135 0 : fd_scratch_push();
136 :
137 0 : fd_textstream_t _data_out[1];
138 0 : fd_textstream_t * data_out = fd_textstream_new( _data_out, valloc, PART_BLK_SZ );
139 0 : fd_textstream_encode_base64( data_out, buf, n );
140 :
141 : /* Get pointer to encoded chunk */
142 0 : FD_TEST( 1UL==fd_textstream_get_iov_count( data_out ) );
143 0 : struct fd_iovec iov[1];
144 0 : FD_TEST( 0 ==fd_textstream_get_iov( data_out, iov ) );
145 :
146 : /* Print encoded chunk */
147 0 : FD_TEST( 1UL==fwrite( iov[0].iov_base, iov[0].iov_len, 1UL, stdout ) );
148 :
149 : /* Wind up for next iteration */
150 0 : data_sz -= n;
151 0 : fd_textstream_destroy( data_out ); /* technically noop */
152 0 : fd_scratch_pop();
153 0 : }
154 0 : # undef PART_RAW_SZ
155 0 : # undef PART_BLK_SZ
156 :
157 : /* Finish YAML entry */
158 0 : printf( "'\n" );
159 0 : }
160 :
161 : /* Restore cursor */
162 :
163 0 : if( FD_UNLIKELY( 0!=fseek( file, pos, SEEK_SET ) ) ) {
164 0 : FD_LOG_ERR(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
165 0 : return 0;
166 0 : }
167 :
168 0 : return 1;
169 0 : }
170 :
171 : /* process_account_table reads and dumps an account table chunk.
172 : If verbose>3, also prints account content. Returns 1 on success and
173 : 0 on failure. On success, restores stream cursor to position on
174 : function entry. */
175 :
176 : static int
177 : process_account_table( FILE * file,
178 : ulong slot,
179 0 : int verbose ) {
180 :
181 : /* Remember stream cursor */
182 :
183 0 : long pos = ftell( file );
184 0 : if( FD_UNLIKELY( pos<0L ) ) {
185 0 : FD_LOG_ERR(( "ftell failed (%d-%s)", errno, strerror( errno ) ));
186 0 : return 0;
187 0 : }
188 :
189 : /* Read chunk */
190 :
191 0 : fd_solcap_chunk_t chunk[1];
192 0 : ulong n = fread( chunk, sizeof(fd_solcap_chunk_t), 1UL, file );
193 0 : if( FD_UNLIKELY( n!=1UL ) ) {
194 0 : FD_LOG_ERR(( "fread chunk failed (%d-%s)", errno, strerror( errno ) ));
195 0 : return 0;
196 0 : }
197 0 : if( FD_UNLIKELY( chunk->magic != FD_SOLCAP_V1_ACTB_MAGIC ) ) {
198 0 : FD_LOG_ERR(( "expected account table chunk, got 0x%016lx", chunk->magic ));
199 0 : return 0;
200 0 : }
201 :
202 : /* Read metadata */
203 :
204 0 : fd_solcap_AccountTableMeta meta[1];
205 0 : do {
206 :
207 0 : uchar meta_buf[ 512UL ];
208 0 : ulong meta_sz = chunk->meta_sz;
209 0 : if( FD_UNLIKELY( meta_sz > sizeof(meta_buf ) ) ) {
210 0 : FD_LOG_ERR(( "invalid accounts table meta size (%lu)", meta_sz ));
211 0 : return 0;
212 0 : }
213 :
214 0 : if( FD_UNLIKELY( 0!=fseek( file, (long)chunk->meta_coff - (long)sizeof(fd_solcap_chunk_t), SEEK_CUR ) ) ) {
215 0 : FD_LOG_ERR(( "fseek to accounts table meta failed (%d-%s)", errno, strerror( errno ) ));
216 0 : return 0;
217 0 : }
218 :
219 0 : if( FD_UNLIKELY( meta_sz != fread( meta_buf, 1UL, meta_sz, file ) ) ) {
220 0 : FD_LOG_ERR(( "fread accounts table meta failed (%d-%s)", errno, strerror( errno ) ));
221 0 : return 0;
222 0 : }
223 :
224 0 : pb_istream_t stream = pb_istream_from_buffer( meta_buf, meta_sz );
225 0 : if( FD_UNLIKELY( !pb_decode( &stream, fd_solcap_AccountTableMeta_fields, meta ) ) ) {
226 0 : FD_LOG_HEXDUMP_DEBUG(( "accounts table meta", meta_buf, meta_sz ));
227 0 : FD_LOG_ERR(( "pb_decode accounts table meta failed (%s)", PB_GET_ERROR(&stream) ));
228 0 : return 0;
229 0 : }
230 :
231 0 : long rewind = (long)chunk->meta_coff + (long)meta_sz;
232 0 : if( FD_UNLIKELY( 0!=fseek( file, -rewind, SEEK_CUR ) ) ) {
233 0 : FD_LOG_ERR(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
234 0 : return 0;
235 0 : }
236 :
237 0 : } while(0);
238 :
239 : /* TODO verify meta.slot */
240 :
241 : /* Seek to accounts table */
242 :
243 0 : if( FD_UNLIKELY( 0!=fseek( file, (long)meta->account_table_coff, SEEK_CUR ) ) ) {
244 0 : FD_LOG_ERR(( "fseek to accounts table failed (%d-%s)", errno, strerror( errno ) ));
245 0 : return 0;
246 0 : }
247 :
248 : /* Read accounts table */
249 :
250 0 : for( ulong i=0UL; i < meta->account_table_cnt; i++ ) {
251 : /* Read account */
252 :
253 0 : fd_solcap_account_tbl_t entry[1];
254 0 : if( FD_UNLIKELY( 1UL!=fread( entry, sizeof(fd_solcap_account_tbl_t), 1UL, file ) ) ) {
255 0 : FD_LOG_ERR(( "fread accounts table entry failed (%d-%s)", errno, strerror( errno ) ));
256 0 : return 0;
257 0 : }
258 :
259 : /* Write to YAML */
260 :
261 0 : printf(
262 0 : " - pubkey: '%s'\n"
263 0 : " hash: '%s'\n"
264 0 : " explorer: 'https://explorer.solana.com/block/%lu?accountFilter=%s&filter=all'\n",
265 0 : FD_BASE58_ENC_32_ALLOCA( entry->key ),
266 0 : FD_BASE58_ENC_32_ALLOCA( entry->hash ),
267 0 : slot,
268 0 : FD_BASE58_ENC_32_ALLOCA( entry->key ) );
269 :
270 : /* Fetch account details */
271 :
272 0 : if( verbose > 3 ) {
273 0 : long acc_goff = (long)pos + entry->acc_coff;
274 0 : if( FD_UNLIKELY( !process_account( file, acc_goff, verbose ) ) ) {
275 0 : FD_LOG_ERR(( "process_account() failed" ));
276 0 : return 0;
277 0 : }
278 0 : }
279 :
280 0 : } /* end for */
281 :
282 : /* Restore cursor */
283 :
284 0 : if( FD_UNLIKELY( 0!=fseek( file, pos, SEEK_SET ) ) ) {
285 0 : FD_LOG_ERR(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
286 0 : return 0;
287 0 : }
288 :
289 0 : return 0;
290 0 : }
291 :
292 : /* process_bank reads and dumps a bank chunk. If verbose>1, also
293 : processes account table. Returns errno (0 on success). Stream
294 : cursor is undefined on return. */
295 :
296 : static int
297 : process_bank( fd_solcap_chunk_t const * chunk,
298 : FILE * file,
299 : int verbose,
300 : long chunk_gaddr,
301 : ulong start_slot,
302 : ulong end_slot,
303 0 : int has_txns ) {
304 :
305 0 : # define FD_SOLCAP_BANK_PREIMAGE_FOOTPRINT (512UL)
306 0 : if( FD_UNLIKELY( chunk->meta_sz > FD_SOLCAP_BANK_PREIMAGE_FOOTPRINT ) ) {
307 0 : FD_LOG_ERR(( "invalid bank preimage meta size (%u)", chunk->meta_sz ));
308 0 : return ENOMEM;
309 0 : }
310 :
311 : /* Read bank preimage meta */
312 :
313 0 : uchar meta_buf[ 512UL ];
314 0 : if( FD_UNLIKELY( 0!=fseek( file, chunk_gaddr + (long)chunk->meta_coff, SEEK_SET ) ) ) {
315 0 : FD_LOG_ERR(( "fseek bank preimage meta failed (%d-%s)", errno, strerror( errno ) ));
316 0 : return errno;
317 0 : }
318 0 : if( FD_UNLIKELY( chunk->meta_sz != fread( meta_buf, 1UL, chunk->meta_sz, file ) ) ) {
319 0 : FD_LOG_ERR(( "fread bank preimage meta failed (%d-%s)", errno, strerror( errno ) ));
320 0 : return errno;
321 0 : }
322 :
323 : /* Deserialize bank preimage meta */
324 :
325 0 : pb_istream_t stream = pb_istream_from_buffer( meta_buf, chunk->meta_sz );
326 :
327 0 : fd_solcap_BankPreimage meta;
328 0 : if( FD_UNLIKELY( !pb_decode( &stream, fd_solcap_BankPreimage_fields, &meta ) ) ) {
329 0 : FD_LOG_HEXDUMP_DEBUG(( "bank preimage meta", meta_buf, chunk->meta_sz ));
330 0 : FD_LOG_ERR(( "pb_decode bank preimage meta failed (%s)", PB_GET_ERROR(&stream) ));
331 0 : return EPROTO;
332 0 : }
333 :
334 0 : if ( meta.slot < start_slot || meta.slot > end_slot ) {
335 0 : return 0;
336 0 : }
337 :
338 : /* Write YAML */
339 0 : if ( verbose < 3 || !has_txns )
340 0 : printf( "- slot: %lu\n", meta.slot );
341 :
342 0 : printf(
343 0 : " - bank_hash: '%s'\n",
344 0 : FD_BASE58_ENC_32_ALLOCA( meta.bank_hash ) );
345 :
346 0 : if( verbose>=1 ) {
347 0 : printf(
348 0 : " - prev_bank_hash: '%s'\n"
349 0 : " - account_delta_hash: '%s'\n"
350 0 : " - poh_hash: '%s'\n"
351 0 : " - signature_cnt: %lu\n",
352 0 : FD_BASE58_ENC_32_ALLOCA( meta.prev_bank_hash ),
353 0 : FD_BASE58_ENC_32_ALLOCA( meta.account_delta_hash ),
354 0 : FD_BASE58_ENC_32_ALLOCA( meta.poh_hash ),
355 0 : meta.signature_cnt );
356 0 : }
357 :
358 : /* Accounts */
359 :
360 0 : if( verbose >= 2 ) {
361 0 : if( meta.account_table_coff==0L ) {
362 0 : if( meta.account_cnt > 0UL )
363 0 : FD_LOG_WARNING(( "Capture does not include account info for slot=%lu", meta.slot ));
364 0 : return 0;
365 0 : }
366 :
367 0 : if( FD_UNLIKELY( 0!=fseek( file, chunk_gaddr + (long)meta.account_table_coff, SEEK_SET ) ) ) {
368 0 : FD_LOG_ERR(( "fseek to account table failed (%d-%s)", errno, strerror( errno ) ));
369 0 : return errno;
370 0 : }
371 :
372 0 : printf( " - accounts_delta:\n" );
373 0 : if( FD_UNLIKELY( 0!=process_account_table( file, meta.slot, verbose ) ) )
374 0 : return errno;
375 0 : }
376 :
377 0 : return 0;
378 0 : }
379 :
380 : static ulong
381 : process_txn( fd_solcap_chunk_t const * chunk,
382 : FILE * file,
383 : int verbose,
384 : long chunk_gaddr,
385 : ulong prev_slot,
386 : ulong start_slot,
387 0 : ulong end_slot ) {
388 :
389 0 : if ( verbose < 3 )
390 0 : return 0;
391 :
392 0 : # define FD_SOLCAP_TRANSACTION_FOOTPRINT (128UL)
393 0 : if( FD_UNLIKELY( chunk->meta_sz > FD_SOLCAP_TRANSACTION_FOOTPRINT ) ) {
394 0 : FD_LOG_ERR(( "invalid transaction meta size (%u)", chunk->meta_sz ));
395 0 : }
396 :
397 : /* Read transaction meta */
398 :
399 0 : uchar meta_buf[ 128UL ];
400 0 : if( FD_UNLIKELY( 0!=fseek( file, chunk_gaddr + (long)chunk->meta_coff, SEEK_SET ) ) ) {
401 0 : FD_LOG_ERR(( "fseek transaction meta failed (%d-%s)", errno, strerror( errno ) ));
402 0 : }
403 0 : if( FD_UNLIKELY( chunk->meta_sz != fread( meta_buf, 1UL, chunk->meta_sz, file ) ) ) {
404 0 : FD_LOG_ERR(( "fread transaction meta failed (%d-%s)", errno, strerror( errno ) ));
405 0 : }
406 :
407 : /* Deserialize transaction meta */
408 :
409 0 : pb_istream_t stream = pb_istream_from_buffer( meta_buf, chunk->meta_sz );
410 :
411 0 : fd_solcap_Transaction meta;
412 0 : if( FD_UNLIKELY( !pb_decode( &stream, fd_solcap_Transaction_fields, &meta ) ) ) {
413 0 : FD_LOG_HEXDUMP_DEBUG(( "transaction meta", meta_buf, chunk->meta_sz ));
414 0 : FD_LOG_ERR(( "pb_decode transaction meta failed (%s)", PB_GET_ERROR(&stream) ));
415 0 : }
416 :
417 0 : if ( meta.slot < start_slot || meta.slot > end_slot ) {
418 0 : return meta.slot;
419 0 : }
420 :
421 : /* Write YAML */
422 0 : if ( prev_slot == 0 || prev_slot != meta.slot ) {
423 0 : printf(
424 0 : "- slot: %lu\n"
425 0 : " - txns:\n", meta.slot
426 0 : );
427 0 : }
428 :
429 0 : printf(
430 0 : " - txn_sig: '%s'\n"
431 0 : " txn_err: %d\n"
432 0 : " cus_used: %lu\n"
433 0 : " instr_err_idx: %d\n",
434 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ),
435 0 : meta.fd_txn_err,
436 0 : meta.fd_cus_used,
437 0 : meta.instr_err_idx);
438 :
439 : /* Only print custom error if it has been set*/
440 0 : if ( meta.fd_txn_err == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR) {
441 0 : printf( " custom_err: %u\n", meta.fd_custom_err );
442 0 : }
443 :
444 0 : if ( verbose < 4 )
445 0 : return meta.slot;
446 :
447 0 : if ( meta.solana_txn_err != ULONG_MAX || meta.solana_cus_used != ULONG_MAX ) {
448 0 : printf(
449 0 : " solana_txn_err: %lu\n"
450 0 : " solana_cus_used: %lu\n",
451 0 : meta.solana_txn_err,
452 0 : meta.solana_cus_used );
453 0 : }
454 :
455 0 : printf(
456 0 : " explorer: 'https://explorer.solana.com/tx/%s'\n"
457 0 : " solscan: 'https://solscan.io/tx/%s'\n"
458 0 : " solanafm: 'https://solana.fm/tx/%s'\n",
459 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ),
460 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ),
461 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ) );
462 :
463 0 : return meta.slot;
464 0 : }
465 :
466 : int
467 : main( int argc,
468 : char ** argv ) {
469 : fd_boot( &argc, &argv );
470 : fd_flamenco_boot( &argc, &argv );
471 :
472 : /* Command line handling */
473 :
474 : for( int i=1; i<argc; i++ )
475 : if( 0==strcmp( argv[i], "--help" ) ) return usage();
476 :
477 : char const * _page_sz = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz", NULL, "gigantic" );
478 : ulong page_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt", NULL, 2UL );
479 : ulong scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL );
480 : int verbose = fd_env_strip_cmdline_int ( &argc, &argv, "-v", NULL, 0 );
481 : ulong start_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--start-slot", NULL, 0 );
482 : ulong end_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--end-slot", NULL, ULONG_MAX );
483 :
484 : ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
485 : if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
486 :
487 : if( argc!=2 ) {
488 : fprintf( stderr, "ERROR: expected 1 argument, got %d\n", argc-1 );
489 : usage();
490 : return 1;
491 : }
492 :
493 : /* Create workspace and scratch allocator */
494 :
495 : fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
496 : if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
497 :
498 : ulong smax = scratch_mb << 20;
499 : void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
500 : if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
501 : ulong scratch_depth = 4UL;
502 : void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( scratch_depth ), 2UL );
503 : if( FD_UNLIKELY( !fmem ) ) FD_LOG_ERR(( "Failed to alloc scratch frames" ));
504 :
505 : fd_scratch_attach( smem, fmem, smax, scratch_depth );
506 :
507 : /* Open file */
508 :
509 : char const * path = argv[ 1 ];
510 : FILE * file = fopen( path, "rb" );
511 : if( FD_UNLIKELY( !file ) )
512 : FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", path, errno, strerror( errno ) ));
513 :
514 : /* Read file header */
515 :
516 : fd_solcap_fhdr_t fhdr[1];
517 : ulong n = fread( fhdr, sizeof(fd_solcap_fhdr_t), 1UL, file );
518 : if( FD_UNLIKELY( n!=1UL ) ) {
519 : FD_LOG_ERR(( "fread file header failed (%d-%s)", errno, strerror( errno ) ));
520 : return errno;
521 : }
522 :
523 : /* TODO Read file meta */
524 :
525 : /* Seek to first chunk */
526 :
527 : int err = fseek( file, (long)fhdr->chunk0_foff - (long)sizeof(fd_solcap_fhdr_t), SEEK_CUR );
528 : if( FD_UNLIKELY( err<0L ) ) {
529 : FD_LOG_ERR(( "fseek chunk0 failed (%d-%s)", errno, strerror( errno ) ));
530 : return errno;
531 : }
532 :
533 : /* Read chunks */
534 :
535 : fd_solcap_chunk_iter_t iter[1];
536 : fd_solcap_chunk_iter_new( iter, file );
537 : ulong previous_slot = 0;
538 : /* TODO replace this with fd_solcap_chunk_iter_find */
539 : for(;;) {
540 : long chunk_gaddr = fd_solcap_chunk_iter_next( iter );
541 : if( FD_UNLIKELY( chunk_gaddr<0L ) ) {
542 : int err = fd_solcap_chunk_iter_err( iter );
543 : if( err==0 ) break;
544 : FD_LOG_ERR(( "fd_solcap_chunk_iter_next() failed (%d-%s)", err, strerror( err ) ));
545 : }
546 :
547 : if( fd_solcap_chunk_iter_done( iter ) ) break;
548 :
549 : fd_solcap_chunk_t const * chunk = fd_solcap_chunk_iter_item( iter );
550 : if( FD_UNLIKELY( !chunk ) ) FD_LOG_ERR(( "fd_solcap_chunk_item() failed" ));
551 :
552 : /* TODO: figure out how to make solana.solcap yamls print slot */
553 : if( chunk->magic == FD_SOLCAP_V1_BANK_MAGIC )
554 : process_bank( chunk, file, verbose, chunk_gaddr, start_slot, end_slot, previous_slot != 0 );
555 : else if ( chunk->magic == FD_SOLCAP_V1_TRXN_MAGIC )
556 : previous_slot = process_txn( chunk, file, verbose, chunk_gaddr, previous_slot, start_slot, end_slot );
557 : }
558 :
559 : /* Cleanup */
560 :
561 : FD_LOG_NOTICE(( "Done" ));
562 : FD_TEST( fd_scratch_frame_used()==0UL );
563 : fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
564 : fd_wksp_free_laddr( fmem );
565 : fclose( file );
566 : fd_flamenco_halt();
567 : fd_halt();
568 : return 0;
569 : }
|