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