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 "../runtime/fd_executor_err.h"
6 : #include "../../ballet/nanopb/pb_decode.h"
7 : #include "../../util/textstream/fd_textstream.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 : " - accounts_lt_hash_checksum: '%s'\n"
351 0 : " - poh_hash: '%s'\n"
352 0 : " - signature_cnt: %lu\n",
353 0 : FD_BASE58_ENC_32_ALLOCA( meta.prev_bank_hash ),
354 0 : FD_BASE58_ENC_32_ALLOCA( meta.account_delta_hash ),
355 0 : FD_BASE58_ENC_32_ALLOCA( meta.accounts_lt_hash_checksum ),
356 0 : FD_BASE58_ENC_32_ALLOCA( meta.poh_hash ),
357 0 : meta.signature_cnt );
358 0 : }
359 :
360 : /* Accounts */
361 :
362 0 : if( verbose >= 2 ) {
363 0 : if( meta.account_table_coff==0L ) {
364 0 : if( meta.account_cnt > 0UL )
365 0 : FD_LOG_WARNING(( "Capture does not include account info for slot=%lu", meta.slot ));
366 0 : return 0;
367 0 : }
368 :
369 0 : if( FD_UNLIKELY( 0!=fseek( file, chunk_gaddr + (long)meta.account_table_coff, SEEK_SET ) ) ) {
370 0 : FD_LOG_ERR(( "fseek to account table failed (%d-%s)", errno, strerror( errno ) ));
371 0 : return errno;
372 0 : }
373 :
374 0 : printf( " - accounts_delta:\n" );
375 0 : if( FD_UNLIKELY( 0!=process_account_table( file, meta.slot, verbose ) ) )
376 0 : return errno;
377 0 : }
378 :
379 0 : return 0;
380 0 : }
381 :
382 : static ulong
383 : process_txn( fd_solcap_chunk_t const * chunk,
384 : FILE * file,
385 : int verbose,
386 : long chunk_gaddr,
387 : ulong prev_slot,
388 : ulong start_slot,
389 0 : ulong end_slot ) {
390 :
391 0 : if ( verbose < 3 )
392 0 : return 0;
393 :
394 0 : # define FD_SOLCAP_TRANSACTION_FOOTPRINT (128UL)
395 0 : if( FD_UNLIKELY( chunk->meta_sz > FD_SOLCAP_TRANSACTION_FOOTPRINT ) ) {
396 0 : FD_LOG_ERR(( "invalid transaction meta size (%u)", chunk->meta_sz ));
397 0 : }
398 :
399 : /* Read transaction meta */
400 :
401 0 : uchar meta_buf[ 128UL ];
402 0 : if( FD_UNLIKELY( 0!=fseek( file, chunk_gaddr + (long)chunk->meta_coff, SEEK_SET ) ) ) {
403 0 : FD_LOG_ERR(( "fseek transaction meta failed (%d-%s)", errno, strerror( errno ) ));
404 0 : }
405 0 : if( FD_UNLIKELY( chunk->meta_sz != fread( meta_buf, 1UL, chunk->meta_sz, file ) ) ) {
406 0 : FD_LOG_ERR(( "fread transaction meta failed (%d-%s)", errno, strerror( errno ) ));
407 0 : }
408 :
409 : /* Deserialize transaction meta */
410 :
411 0 : pb_istream_t stream = pb_istream_from_buffer( meta_buf, chunk->meta_sz );
412 :
413 0 : fd_solcap_Transaction meta;
414 0 : if( FD_UNLIKELY( !pb_decode( &stream, fd_solcap_Transaction_fields, &meta ) ) ) {
415 0 : FD_LOG_HEXDUMP_DEBUG(( "transaction meta", meta_buf, chunk->meta_sz ));
416 0 : FD_LOG_ERR(( "pb_decode transaction meta failed (%s)", PB_GET_ERROR(&stream) ));
417 0 : }
418 :
419 0 : if ( meta.slot < start_slot || meta.slot > end_slot ) {
420 0 : return meta.slot;
421 0 : }
422 :
423 : /* Write YAML */
424 0 : if ( prev_slot == 0 || prev_slot != meta.slot ) {
425 0 : printf(
426 0 : "- slot: %lu\n"
427 0 : " - txns:\n", meta.slot
428 0 : );
429 0 : }
430 :
431 0 : printf(
432 0 : " - txn_sig: '%s'\n"
433 0 : " txn_err: %d\n"
434 0 : " cus_used: %lu\n"
435 0 : " instr_err_idx: %d\n",
436 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ),
437 0 : meta.fd_txn_err,
438 0 : meta.fd_cus_used,
439 0 : meta.instr_err_idx);
440 :
441 : /* Only print custom error if it has been set*/
442 0 : if ( meta.fd_txn_err == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR) {
443 0 : printf( " custom_err: %u\n", meta.fd_custom_err );
444 0 : }
445 :
446 0 : if ( verbose < 4 )
447 0 : return meta.slot;
448 :
449 0 : if ( meta.solana_txn_err != ULONG_MAX || meta.solana_cus_used != ULONG_MAX ) {
450 0 : printf(
451 0 : " solana_txn_err: %lu\n"
452 0 : " solana_cus_used: %lu\n",
453 0 : meta.solana_txn_err,
454 0 : meta.solana_cus_used );
455 0 : }
456 :
457 0 : printf(
458 0 : " explorer: 'https://explorer.solana.com/tx/%s'\n"
459 0 : " solscan: 'https://solscan.io/tx/%s'\n"
460 0 : " solanafm: 'https://solana.fm/tx/%s'\n",
461 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ),
462 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ),
463 0 : FD_BASE58_ENC_64_ALLOCA( meta.txn_sig ) );
464 :
465 0 : return meta.slot;
466 0 : }
467 :
468 : int
469 : main( int argc,
470 : char ** argv ) {
471 : fd_boot( &argc, &argv );
472 : fd_flamenco_boot( &argc, &argv );
473 :
474 : /* Command line handling */
475 :
476 : for( int i=1; i<argc; i++ )
477 : if( 0==strcmp( argv[i], "--help" ) ) return usage();
478 :
479 : char const * _page_sz = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz", NULL, "gigantic" );
480 : ulong page_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt", NULL, 2UL );
481 : ulong scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL );
482 : int verbose = fd_env_strip_cmdline_int ( &argc, &argv, "-v", NULL, 0 );
483 : ulong start_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--start-slot", NULL, 0 );
484 : ulong end_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--end-slot", NULL, ULONG_MAX );
485 :
486 : ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
487 : if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
488 :
489 : if( argc!=2 ) {
490 : fprintf( stderr, "ERROR: expected 1 argument, got %d\n", argc-1 );
491 : usage();
492 : return 1;
493 : }
494 :
495 : /* Create workspace and scratch allocator */
496 :
497 : fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
498 : if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
499 :
500 : ulong smax = scratch_mb << 20;
501 : void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
502 : if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
503 : ulong scratch_depth = 4UL;
504 : void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( scratch_depth ), 2UL );
505 : if( FD_UNLIKELY( !fmem ) ) FD_LOG_ERR(( "Failed to alloc scratch frames" ));
506 :
507 : fd_scratch_attach( smem, fmem, smax, scratch_depth );
508 :
509 : /* Open file */
510 :
511 : char const * path = argv[ 1 ];
512 : FILE * file = fopen( path, "rb" );
513 : if( FD_UNLIKELY( !file ) )
514 : FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", path, errno, strerror( errno ) ));
515 :
516 : /* Read file header */
517 :
518 : fd_solcap_fhdr_t fhdr[1];
519 : ulong n = fread( fhdr, sizeof(fd_solcap_fhdr_t), 1UL, file );
520 : if( FD_UNLIKELY( n!=1UL ) ) {
521 : FD_LOG_ERR(( "fread file header failed (%d-%s)", errno, strerror( errno ) ));
522 : return errno;
523 : }
524 :
525 : /* TODO Read file meta */
526 :
527 : /* Seek to first chunk */
528 :
529 : int err = fseek( file, (long)fhdr->chunk0_foff - (long)sizeof(fd_solcap_fhdr_t), SEEK_CUR );
530 : if( FD_UNLIKELY( err<0L ) ) {
531 : FD_LOG_ERR(( "fseek chunk0 failed (%d-%s)", errno, strerror( errno ) ));
532 : return errno;
533 : }
534 :
535 : /* Read chunks */
536 :
537 : fd_solcap_chunk_iter_t iter[1];
538 : fd_solcap_chunk_iter_new( iter, file );
539 : ulong previous_slot = 0;
540 : /* TODO replace this with fd_solcap_chunk_iter_find */
541 : for(;;) {
542 : long chunk_gaddr = fd_solcap_chunk_iter_next( iter );
543 : if( FD_UNLIKELY( chunk_gaddr<0L ) ) {
544 : int err = fd_solcap_chunk_iter_err( iter );
545 : if( err==0 ) break;
546 : FD_LOG_ERR(( "fd_solcap_chunk_iter_next() failed (%d-%s)", err, strerror( err ) ));
547 : }
548 :
549 : if( fd_solcap_chunk_iter_done( iter ) ) break;
550 :
551 : fd_solcap_chunk_t const * chunk = fd_solcap_chunk_iter_item( iter );
552 : if( FD_UNLIKELY( !chunk ) ) FD_LOG_ERR(( "fd_solcap_chunk_item() failed" ));
553 :
554 : /* TODO: figure out how to make solana.solcap yamls print slot */
555 : if( chunk->magic == FD_SOLCAP_V1_BANK_MAGIC )
556 : process_bank( chunk, file, verbose, chunk_gaddr, start_slot, end_slot, previous_slot != 0 );
557 : else if ( chunk->magic == FD_SOLCAP_V1_TRXN_MAGIC )
558 : previous_slot = process_txn( chunk, file, verbose, chunk_gaddr, previous_slot, start_slot, end_slot );
559 : }
560 :
561 : /* Cleanup */
562 :
563 : FD_LOG_NOTICE(( "Done" ));
564 : FD_TEST( fd_scratch_frame_used()==0UL );
565 : fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
566 : fd_wksp_free_laddr( fmem );
567 : fclose( file );
568 : fd_flamenco_halt();
569 : fd_halt();
570 : return 0;
571 : }
|