LCOV - code coverage report
Current view: top level - flamenco/capture - fd_solcap_yaml.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 311 0.0 %
Date: 2025-09-18 04:41:32 Functions: 0 5 0.0 %

          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             : }

Generated by: LCOV version 1.14