LCOV - code coverage report
Current view: top level - flamenco/capture - fd_solcap_import.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 148 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "../fd_flamenco.h"
       2             : #include "fd_solcap.pb.h"
       3             : #include "fd_solcap_proto.h"
       4             : #include "fd_solcap_writer.h"
       5             : #include "../../ballet/base58/fd_base58.h"
       6             : #include "../../ballet/base64/fd_base64.h"
       7             : #include "../../ballet/json/cJSON.h"
       8             : 
       9             : #include <errno.h>
      10             : #include <fcntl.h>
      11             : #include <stdio.h>
      12             : #include <unistd.h>
      13             : #include <sys/stat.h>
      14             : #include <math.h>
      15             : #include <dirent.h>
      16             : 
      17             : #ifndef DT_REG
      18           0 : #define DT_REG (8UL)
      19             : #endif
      20             : 
      21             : static int
      22           0 : usage( void ) {
      23           0 :   fprintf( stderr,
      24           0 :     "Usage: fd_solcap_import [options] {IN_DIR} {OUT_FILE}\n"
      25           0 :     "\n"
      26           0 :     "Imports a runtime capture directory from JSON.\n"
      27           0 :     "\n"
      28           0 :     "Options:\n"
      29           0 :     "  --page-sz      {gigantic|huge|normal}    Page size\n"
      30           0 :     "  --page-cnt     {count}                   Page count\n"
      31           0 :     "  --scratch-mb   1024                      Scratch mem MiB\n"
      32           0 :     "\n" );
      33           0 :   return 0;
      34           0 : }
      35             : 
      36             : /* fd_alloc wrapper */
      37             : 
      38             : static fd_alloc_t * current_alloc;
      39           0 : static void * my_malloc( ulong sz ) { return fd_alloc_malloc( current_alloc, 1UL, sz ); }
      40           0 : static void   my_free  ( void * p ) {        fd_alloc_free  ( current_alloc, p       ); }
      41             : 
      42             : static cJSON *
      43             : read_json_file( fd_wksp_t *  wksp,
      44             :                 fd_alloc_t * alloc,
      45           0 :                 char const * path ) {
      46             : 
      47             :   /* Wire up fd_alloc with cJSON */
      48             : 
      49           0 :   current_alloc = alloc;
      50           0 :   cJSON_Hooks hooks = {
      51           0 :     .malloc_fn = my_malloc,
      52           0 :     .free_fn   = my_free
      53           0 :   };
      54           0 :   cJSON_InitHooks( &hooks );
      55             : 
      56             :   /* Open file */
      57             : 
      58           0 :   int fd;
      59           0 :   fd = open( path, O_RDONLY );
      60           0 :   if( FD_UNLIKELY( fd<0 ) ) {
      61           0 :     FD_LOG_WARNING(( "open(%s) failed (%d-%s)", path, errno, strerror( errno ) ));
      62           0 :     return NULL;
      63           0 :   }
      64             : 
      65             :   /* Figure out file size */
      66             : 
      67           0 :   struct stat stat;
      68           0 :   if( FD_UNLIKELY( fstat( fd, &stat )<0 ) ) {
      69           0 :     FD_LOG_WARNING(( "fstat(%s) failed (%d-%s)", path, errno, strerror( errno ) ));
      70           0 :     close( fd );
      71           0 :     return NULL;
      72           0 :   }
      73             : 
      74             :   /* Allocate buffer to store file content */
      75             : 
      76           0 :   char * buf = fd_wksp_alloc_laddr( wksp, 1UL, (ulong)stat.st_size, 1UL );
      77           0 :   if( FD_UNLIKELY( !buf ) ) {
      78           0 :     FD_LOG_WARNING(( "Failed to alloc memory region to fit file content" ));
      79           0 :     close( fd );
      80           0 :     return NULL;
      81           0 :   }
      82             : 
      83             :   /* Copy file content to memory */
      84             : 
      85           0 :   char * cursor = buf;
      86           0 :   ulong rem = (ulong)stat.st_size;
      87           0 :   while( rem>0UL ) {
      88           0 :     long n = read( fd, cursor, rem );
      89           0 :     if( FD_UNLIKELY( n<0L ) ) {
      90           0 :       FD_LOG_WARNING(( "read(%s) failed (%d-%s)", path, errno, strerror( errno ) ));
      91           0 :       close( fd );
      92           0 :       return NULL;
      93           0 :     }
      94           0 :     if( FD_UNLIKELY( n==0L ) ) {
      95           0 :       FD_LOG_WARNING(( "read(%s) failed: unexpected EOF", path ));
      96           0 :       close( fd );
      97           0 :       return NULL;
      98           0 :     }
      99             : 
     100           0 :     cursor += (ulong)n;
     101           0 :     rem    -= (ulong)n;
     102           0 :   }
     103             : 
     104             :   /* Call parser */
     105             : 
     106           0 :   cJSON * json = cJSON_ParseWithLength( buf, (ulong)stat.st_size );
     107             : 
     108             :   /* Clean up */
     109             : 
     110           0 :   fd_wksp_free_laddr( buf );
     111           0 :   close( fd );
     112           0 :   return json;
     113           0 : }
     114             : 
     115             : /* unmarshal_hash interprets given JSON node as a string containing
     116             :    the Base58 encoding of 32 bytes.  Copies the bytes out to out_buf.
     117             :    Returns NULL if json is NULL, json is not string, or is not a valid
     118             :    32-byte Base58 encoding.  Returns out_buf on success. */
     119             : 
     120             : static uchar *
     121             : unmarshal_hash( cJSON const * json,
     122           0 :                 uchar         out_buf[static 32] ) {
     123             : 
     124           0 :   char const * str = cJSON_GetStringValue( json );
     125           0 :   if( FD_UNLIKELY( !str ) ) return NULL;
     126             : 
     127           0 :   return fd_base58_decode_32( str, out_buf );
     128           0 : }
     129             : 
     130             : /* unmarshal_bank_preimage reads top-level bank preimage information
     131             :    from given JSON dictionary.   Copies values into given out struct.
     132             :    Aborts application via error log on failure. */
     133             : 
     134             : static void
     135             : unmarshal_bank_preimage( cJSON const *            json,
     136           0 :                          fd_solcap_BankPreimage * out ) {
     137             : 
     138           0 :   cJSON * head = (cJSON *)json;
     139             : 
     140           0 :   cJSON * slot = cJSON_GetObjectItem( head, "slot" );
     141           0 :   out->slot = slot ? slot->valueulong : 0UL;
     142             : 
     143           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( head, "bank_hash"           ), out->bank_hash          ) );
     144           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( head, "parent_bank_hash"    ), out->prev_bank_hash     ) );
     145           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( head, "accounts_delta_hash" ), out->account_delta_hash ) );
     146           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( head, "last_blockhash"      ), out->poh_hash           ) );
     147             : 
     148           0 :   if ( cJSON_GetObjectItem( head, "signature_count" ) != NULL )
     149           0 :     out->signature_cnt = cJSON_GetObjectItem( head, "signature_count" )->valueulong;
     150           0 :   else
     151           0 :     out->signature_cnt = 0;
     152             : 
     153           0 :   cJSON * accs = cJSON_GetObjectItem( head, "accounts" );
     154           0 :   FD_TEST( accs );
     155           0 :   out->account_cnt = (ulong)cJSON_GetArraySize( accs );
     156           0 : }
     157             : 
     158             : /* unmarshal_account reads account meta/data from given JSON object.
     159             :    Object should be a dictionary and is found as an element of the
     160             :    "accounts" array.  On success, returns pointer to account data
     161             :    (allocated in current scratch frame), and copies metadata to given
     162             :    out structs.  On failure, aborts application via error log. */
     163             : 
     164             : static void *
     165             : unmarshal_account( cJSON const *             json,
     166             :                    fd_solcap_account_tbl_t * rec,
     167           0 :                    fd_solcap_AccountMeta *   meta ) {
     168             : 
     169             :   /* TODO !!! THIS IS OBVIOUSLY UNSAFE
     170             :      Representing lamports as double causes precision-loss for values
     171             :      exceeding 2^53-1.  This appears to be a limitation of the cJSON
     172             :      library. */
     173           0 :   meta->lamports = cJSON_GetObjectItem( json, "lamports" )->valueulong;
     174             : 
     175           0 :   meta->rent_epoch = cJSON_GetObjectItem( json, "rent_epoch" )->valueulong;
     176             : 
     177           0 :   cJSON * executable_o = cJSON_GetObjectItem( json, "executable" );
     178           0 :   FD_TEST( executable_o );
     179           0 :   meta->executable = cJSON_IsBool( executable_o ) & cJSON_IsTrue( executable_o );
     180             : 
     181           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( json, "pubkey" ), rec->key    ) );
     182           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( json, "hash"   ), rec->hash   ) );
     183           0 :   FD_TEST( unmarshal_hash( cJSON_GetObjectItem( json, "owner"  ), meta->owner ) );
     184             : 
     185             :   /* Data handling ... Base64 decode */
     186             : 
     187           0 :   char const * data_b64 = cJSON_GetStringValue( cJSON_GetObjectItem( json, "data" ) );
     188           0 :   FD_TEST( data_b64 );
     189             : 
     190             :   /* sigh ... cJSON doesn't remember string length, although it
     191             :      obviously had this information while parsing. */
     192           0 :   ulong data_b64_len = strlen( data_b64 );
     193             : 
     194             :   /* Very rough upper bound for decoded data sz.
     195             :      Could do better here, but better to be on the safe side. */
     196           0 :   ulong approx_data_sz = 3UL + data_b64_len/2UL;
     197             : 
     198             :   /* Grab scratch memory suitable for storing account data */
     199           0 :   void * data = fd_scratch_alloc( /* align */ 1UL, /* sz */ approx_data_sz );
     200           0 :   FD_TEST( data );
     201             : 
     202             :   /* Base64 decode */
     203           0 :   long data_sz = fd_base64_decode( data, data_b64, data_b64_len );
     204           0 :   FD_TEST( data_sz>=0L ); /* check for corruption */
     205           0 :   meta->data_sz = (ulong)data_sz;
     206             : 
     207           0 :   return data;
     208           0 : }
     209             : 
     210             : void write_slots( const char * in_path,
     211             :                   fd_solcap_writer_t * writer,
     212             :                   fd_wksp_t * wksp,
     213           0 :                   fd_alloc_t * alloc ) {
     214             :   /* Iterate through the directory to get all of the bank hash details file */
     215           0 :   struct dirent * ent;
     216           0 :   DIR * dir = opendir( in_path );
     217             : 
     218           0 :   if ( dir == NULL ) {
     219           0 :     FD_LOG_ERR(( "unable to open the directory=%s", in_path ));
     220           0 :   }
     221             :   /* TODO: sort the files that are read in. The API makes no guarantee that the
     222             :      files are alphabetically sorted, but in practice they are. */
     223           0 :   for ( ent = readdir( dir ); ent != NULL; ent = readdir( dir ) ) {
     224           0 :     if ( ent->d_type != DT_REG ) {
     225           0 :       continue;
     226           0 :     }
     227             : 
     228           0 :     char path_buf[ 256UL ];
     229           0 :     char * path_buf_ptr = path_buf;
     230           0 :     fd_memset( path_buf_ptr, '\0', sizeof( path_buf ) );
     231           0 :     fd_memcpy( path_buf_ptr, in_path, strlen( in_path ) );
     232           0 :     fd_memcpy( path_buf_ptr + strlen( in_path ), ent->d_name, strlen( ent->d_name ) );
     233           0 :     FD_LOG_NOTICE(( "Reading input file=%s", path_buf_ptr ));
     234             : 
     235           0 :     cJSON * json = read_json_file( wksp, alloc, path_buf_ptr );
     236           0 :     if( FD_UNLIKELY( !json ) ) {
     237           0 :       FD_LOG_ERR(( "Failed to read input file=%s", path_buf_ptr ));
     238           0 :     }
     239             : 
     240             :     // The structure of 1.18 is different to 1.17, and includes bank_hash_details
     241           0 :     cJSON * bank_hash_details = cJSON_GetObjectItem( json, "bank_hash_details" );
     242           0 :     if ( bank_hash_details != NULL ) {
     243           0 :       json = cJSON_GetArrayItem( bank_hash_details, 0 );
     244           0 :     }
     245             : 
     246           0 :     fd_solcap_BankPreimage preimg[1] = {{0}};
     247           0 :     unmarshal_bank_preimage( json, preimg );
     248             : 
     249           0 :     fd_solcap_writer_set_slot( writer, preimg->slot );
     250             : 
     251           0 :     cJSON * json_acc = cJSON_GetObjectItem( json, "accounts" );
     252           0 :     cJSON * acc = cJSON_GetArrayItem( json_acc, 0 );
     253           0 :     int n = cJSON_GetArraySize( json_acc );
     254             : 
     255           0 :     for( int i=0; i<n; i++ ) {
     256           0 :       fd_scratch_push();
     257             : 
     258           0 :       fd_solcap_account_tbl_t  rec[1]; memset( rec,  0, sizeof(fd_solcap_account_tbl_t) );
     259           0 :       fd_solcap_AccountMeta   meta[1]; memset( meta, 0, sizeof(fd_solcap_AccountMeta  ) );
     260           0 :       void * data = unmarshal_account( acc, rec, meta );
     261             : 
     262           0 :       FD_TEST( 0==fd_solcap_write_account2( writer, rec, meta, data, meta->data_sz ) );
     263             : 
     264           0 :       fd_scratch_pop();
     265             : 
     266           0 :       acc = acc->next;
     267           0 :     }
     268             : 
     269           0 :     FD_TEST( 0==fd_solcap_write_bank_preimage2( writer, preimg ) );
     270           0 :     cJSON_free( json );
     271           0 :   }
     272           0 :   closedir( dir );
     273           0 : }
     274             : 
     275             : int
     276             : main( int     argc,
     277             :       char ** argv ) {
     278             :   fd_boot( &argc, &argv );
     279             :   fd_flamenco_boot( &argc, &argv );
     280             : 
     281             :   /* Command line handling */
     282             : 
     283             :   for( int i=1; i<argc; i++ )
     284             :     if( 0==strcmp( argv[i], "--help" ) ) return usage();
     285             : 
     286             :   char const * _page_sz   = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz",    NULL, "gigantic" );
     287             :   ulong        page_cnt   = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt",   NULL, 2UL        );
     288             :   ulong        scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL     );
     289             : 
     290             :   ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
     291             :   if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
     292             : 
     293             :   if( argc!=3 ) {
     294             :     fprintf( stderr, "ERROR: expected 2 arguments, got %d\n", argc-1 );
     295             :     usage();
     296             :     return 1;
     297             :   }
     298             : 
     299             :   /* Acquire workspace */
     300             : 
     301             :   fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
     302             :   if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
     303             : 
     304             :   /* Create scratch allocator */
     305             : 
     306             :   ulong  smax = scratch_mb << 20;
     307             :   void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
     308             :   if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
     309             : 
     310             : # define SCRATCH_DEPTH (4UL)
     311             :   ulong fmem[ SCRATCH_DEPTH ] __attribute((aligned(FD_SCRATCH_FMEM_ALIGN)));
     312             : 
     313             :   fd_scratch_attach( smem, fmem, smax, SCRATCH_DEPTH );
     314             : 
     315             :   /* Create heap allocator */
     316             : 
     317             :   void * alloc_buf = fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2UL );
     318             :   fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( alloc_buf, 2UL ), 0UL );
     319             :   if( FD_UNLIKELY( !alloc ) ) FD_LOG_ERR(( "Failed to create heap" ));
     320             : 
     321             :   /* Create output file */
     322             : 
     323             :   FILE * out_file = fopen( argv[2], "wb" );
     324             :   if( FD_UNLIKELY( !out_file ) )
     325             :     FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", argv[2], errno, strerror( errno ) ));
     326             :   if( FD_UNLIKELY( 0!=ftruncate( fileno( out_file ), 0L ) ) )
     327             :     FD_LOG_ERR(( "ftruncate failed (%d-%s)", errno, strerror( errno ) ));
     328             : 
     329             :   /* Create solcap writer */
     330             : 
     331             :   void * writer_mem = fd_wksp_alloc_laddr( wksp, fd_solcap_writer_align(), fd_solcap_writer_footprint(), 1UL );
     332             :   fd_solcap_writer_t * writer = fd_solcap_writer_init( fd_solcap_writer_new( writer_mem ), out_file );
     333             :   if( FD_UNLIKELY( !writer ))
     334             :     FD_LOG_ERR(( "Failed to create solcap writer" ));
     335             : 
     336             :   write_slots( argv[1], writer, wksp, alloc );
     337             : 
     338             :   /* Cleanup */
     339             : 
     340             :   fd_wksp_free_laddr( fd_solcap_writer_delete( fd_solcap_writer_flush( writer ) ) );
     341             :   fclose( out_file );
     342             :   fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
     343             :   FD_TEST( fd_scratch_frame_used()==0UL );
     344             :   fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
     345             :   fd_flamenco_halt();
     346             :   fd_halt();
     347             :   return 0;
     348             : }

Generated by: LCOV version 1.14