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

          Line data    Source code
       1             : #include "fd_solcap_writer.h"
       2             : #include "fd_solcap.pb.h"
       3             : #include "fd_solcap_proto.h"
       4             : #include "../nanopb/pb_encode.h"
       5             : 
       6             : #if !FD_HAS_HOSTED
       7             : #error "fd_solcap_writer requires FD_HAS_HOSTED"
       8             : #endif
       9             : 
      10             : #include <errno.h>
      11             : #include <stdio.h>
      12             : 
      13             : /* Note on suffixes:
      14             : 
      15             :     goff: file offset (as returned by fseek)
      16             :     foff: file offset from beginning of solcap stream
      17             :     coff: file offset from beginning of current chunk */
      18             : 
      19             : /* fd_solcap_writer is the state of a capture writer.  Currently, it
      20             :    is only able to capture the bank hash pre-image and chagned accounts.
      21             : 
      22             :    The writer progresses with each API call to the writer functions.
      23             : 
      24             :    Typically, the order is the following:
      25             : 
      26             :    - fd_solcap_writer_set_slot advances to the next slot.  If there was
      27             :      a previous slot in progress but not finished, discards buffers.
      28             :    - fd_solcap_write_account writes an account chunk and buffers an
      29             :      entry for the accounts table.
      30             :    - fd_solcap_write_bank_preimage flushes the buffered accounts table
      31             :      and writes the preimage chunk.  Slot is finished and ready for
      32             :      next iteration. */
      33             : 
      34             : struct fd_solcap_writer {
      35             :   FILE * file;
      36             : 
      37             :   /* Number of bytes between start of file and start of stream.
      38             :      Usually 0.  Non-zero if the bank capture is contained in some
      39             :      other file format. */
      40             :   ulong stream_goff;
      41             : 
      42             :   /* In-flight write of accounts table.
      43             :      account_idx==0UL implies no chunk header has been written yet.
      44             :      account_idx>=0UL implies AccountTable chunk write is pending.
      45             :      account_idx>=FD_SOLCAP_ACC_TBL_CNT implies that AccountTable is
      46             :      unable to fit records.  Table record will be skipped. */
      47             : 
      48             :   ulong                   slot;
      49             :   fd_solcap_account_tbl_t accounts[ FD_SOLCAP_ACC_TBL_CNT ];
      50             :   uint                    account_idx;
      51             :   ulong                   account_table_goff;
      52             : 
      53             :   ulong first_slot;
      54             : };
      55             : 
      56             : /* FTELL_BAIL calls ftell on the given file, and bails the current
      57             :    function with return code EIO if it fails. */
      58             : 
      59             : #define FTELL_BAIL( file )                      \
      60           0 :   (__extension__({                              \
      61           0 :     long n = ftell( (file) );                   \
      62           0 :     if( FD_UNLIKELY( n<0L ) ) {                 \
      63           0 :       FD_LOG_WARNING(( "ftell failed (%d-%s)",  \
      64           0 :         errno, strerror( errno ) ));            \
      65           0 :       return EIO;                               \
      66           0 :     }                                           \
      67           0 :     (ulong)n;                                   \
      68           0 :   }))
      69             : 
      70             : /* FSEEK_BAIL calls fseek on the given file, and bails the current
      71             :    function with return code EIO if it fails. */
      72             : 
      73             : #define FSEEK_BAIL( file, off, whence )         \
      74           0 :   (__extension__({                              \
      75           0 :     int err = fseek( (file), (off), (whence) ); \
      76           0 :     if( FD_UNLIKELY( err<0L ) ) {               \
      77           0 :       FD_LOG_WARNING(( "fseek failed (%d-%s)",  \
      78           0 :         errno, strerror( errno ) ));            \
      79           0 :       return EIO;                               \
      80           0 :     }                                           \
      81           0 :     0;                                          \
      82           0 :   }))
      83             : 
      84             : /* FWRITE_BAIL calls fwrite on the given file, and bails the current
      85             :    function with return code EIO if it fails. */
      86             : 
      87             : #define FWRITE_BAIL( ptr, sz, cnt, file )          \
      88           0 :   (__extension__({                                 \
      89           0 :     ulong _cnt = (cnt);                            \
      90           0 :     ulong n = fwrite( (ptr), (sz), _cnt, (file) ); \
      91           0 :     if( FD_UNLIKELY( n!=_cnt ) ) {                 \
      92           0 :       FD_LOG_WARNING(( "fwrite failed (%d-%s)",    \
      93           0 :         errno, strerror( errno ) ));               \
      94           0 :       return EIO;                                  \
      95           0 :     }                                              \
      96           0 :     0;                                             \
      97           0 :   }))
      98             : 
      99             : /* _skip_file writes zeros to the file */
     100             : 
     101             : static int
     102             : _skip_file( FILE * file,
     103           0 :             ulong  skip ) {
     104           0 :   if (skip == 0) return 0;
     105             : 
     106           0 :   uchar zero[ skip ];
     107           0 :   fd_memset( zero, 0, skip );
     108             : 
     109           0 :   FWRITE_BAIL( zero, 1UL, skip, file );
     110           0 :   return 0;
     111           0 : }
     112             : 
     113             : #define FSKIP_BAIL( file, skip )            \
     114           0 :   do {                                      \
     115           0 :     int err = _skip_file( (file), (skip) ); \
     116           0 :     if( FD_UNLIKELY( err!=0 ) ) return err; \
     117           0 :   } while(0)
     118             : 
     119             : /* _align_file pads file with zero up to meet given align requirement.
     120             :    align is a positive power of two. */
     121             : 
     122             : static int
     123             : _align_file( FILE * file,
     124           0 :              ulong  align ) {
     125           0 :   ulong pos  = FTELL_BAIL( file );
     126           0 :   ulong skip = fd_ulong_align_up( pos, align ) - pos;
     127           0 :   return _skip_file( file, skip );
     128           0 : }
     129             : 
     130             : #define FALIGN_BAIL( file, align )            \
     131           0 :   do {                                        \
     132           0 :     int err = _align_file( (file), (align) ); \
     133           0 :     if( FD_UNLIKELY( err!=0 ) ) return err;   \
     134           0 :   } while(0)
     135             : 
     136             : 
     137             : ulong
     138           0 : fd_solcap_writer_align( void ) {
     139           0 :   return alignof(fd_solcap_writer_t);
     140           0 : }
     141             : 
     142             : ulong
     143           0 : fd_solcap_writer_footprint( void ) {
     144           0 :   return sizeof(fd_solcap_writer_t);
     145           0 : }
     146             : 
     147             : fd_solcap_writer_t *
     148           0 : fd_solcap_writer_new( void * mem ) {
     149             : 
     150           0 :   if( FD_UNLIKELY( !mem ) ) {
     151           0 :     FD_LOG_WARNING(( "NULL mem" ));
     152           0 :     return NULL;
     153           0 :   }
     154             : 
     155           0 :   memset( mem, 0, sizeof(fd_solcap_writer_t) );
     156           0 :   return (fd_solcap_writer_t *)mem;
     157           0 : }
     158             : 
     159             : void *
     160           0 : fd_solcap_writer_delete( fd_solcap_writer_t * writer ) {
     161             : 
     162           0 :   if( FD_UNLIKELY( !writer ) ) return NULL;
     163             : 
     164           0 :   writer->file = NULL;
     165           0 :   return writer;
     166           0 : }
     167             : 
     168             : 
     169             : fd_solcap_writer_t *
     170             : fd_solcap_writer_init( fd_solcap_writer_t * writer,
     171           0 :                        void *               file ) {
     172             : 
     173           0 :   if( FD_UNLIKELY( !writer ) ) {
     174           0 :     FD_LOG_WARNING(( "NULL writer" ));
     175           0 :     return NULL;
     176           0 :   }
     177           0 :   if( FD_UNLIKELY( !file ) ) {
     178           0 :     FD_LOG_WARNING(( "NULL file" ));
     179           0 :     return NULL;
     180           0 :   }
     181             : 
     182             :   /* Leave space for file headers */
     183             : 
     184           0 :   long pos = ftell( file );
     185           0 :   if( FD_UNLIKELY( pos<0L ) ) {
     186           0 :     FD_LOG_WARNING(( "ftell failed (%d-%s)", errno, strerror( errno ) ));
     187           0 :     return NULL;
     188           0 :   }
     189           0 :   ulong stream_goff = (ulong)pos;
     190             : 
     191           0 :   uchar zero[ FD_SOLCAP_FHDR_SZ ] = {0};
     192           0 :   ulong n = fwrite( zero, FD_SOLCAP_FHDR_SZ, 1UL, file );
     193           0 :   if( FD_UNLIKELY( n!=1UL ) ) {
     194           0 :     FD_LOG_WARNING(( "fwrite failed (%d-%s)", errno, strerror( errno ) ));
     195           0 :     return NULL;
     196           0 :   }
     197             : 
     198             :   /* Init writer */
     199           0 :   writer->file        = file;
     200           0 :   writer->stream_goff = stream_goff;
     201             : 
     202           0 :   return writer;
     203           0 : }
     204             : 
     205             : /* fd_solcap_writer_flush writes the file header. */
     206             : 
     207             : fd_solcap_writer_t *
     208           0 : fd_solcap_writer_flush( fd_solcap_writer_t * writer ) {
     209             : 
     210           0 :   if( FD_LIKELY( !writer ) ) return NULL;
     211             : 
     212             :   /* Flush stream */
     213           0 :   fflush( writer->file );
     214             : 
     215             :   /* Remember stream cursor */
     216             : 
     217           0 :   long cursor = ftell( writer->file );
     218           0 :   if( FD_UNLIKELY( cursor<0L ) ) {
     219           0 :     FD_LOG_WARNING(( "ftell failed (%d-%s)", errno, strerror( errno ) ));
     220           0 :     return NULL;
     221           0 :   }
     222             : 
     223             :   /* Construct file header */
     224             : 
     225           0 :   fd_solcap_FileMeta fmeta = {
     226           0 :     .first_slot       = writer->first_slot,
     227           0 :     .slot_cnt         = (ulong)fd_long_max( 0L, (long)writer->slot - (long)writer->first_slot ),
     228           0 :     .main_block_magic = FD_SOLCAP_V1_BANK_MAGIC,
     229           0 :   };
     230             : 
     231           0 :   uchar meta[ 128UL ];
     232           0 :   pb_ostream_t stream = pb_ostream_from_buffer( meta, sizeof(meta) );
     233           0 :   if( FD_UNLIKELY( !pb_encode( &stream, fd_solcap_FileMeta_fields, &fmeta ) ) ) {
     234           0 :     FD_LOG_WARNING(( "pb_encode failed (%s)", PB_GET_ERROR(&stream) ));
     235           0 :     return NULL;
     236           0 :   }
     237             : 
     238           0 :   fd_solcap_fhdr_t fhdr = {
     239           0 :     .magic       = FD_SOLCAP_V1_FILE_MAGIC,
     240           0 :     .chunk0_foff = FD_SOLCAP_FHDR_SZ,
     241           0 :     .meta_sz     = (uint)stream.bytes_written,
     242           0 :   };
     243             : 
     244             :   /* Write out file headers */
     245             : 
     246           0 :   if( FD_UNLIKELY( 0!=fseek( writer->file, (long)writer->stream_goff, SEEK_SET ) ) ) {
     247           0 :     FD_LOG_WARNING(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
     248           0 :     return NULL;
     249           0 :   }
     250             : 
     251           0 :   if( FD_UNLIKELY( 1UL!=fwrite( &fhdr, sizeof(fd_solcap_fhdr_t), 1UL, writer->file ) ) ) {
     252           0 :     FD_LOG_WARNING(( "fwrite file header failed (%d-%s)", errno, strerror( errno ) ));
     253           0 :     return NULL;
     254           0 :   }
     255             : 
     256           0 :   if( FD_UNLIKELY( stream.bytes_written != fwrite( meta, 1UL, stream.bytes_written, writer->file ) ) ) {
     257           0 :     FD_LOG_WARNING(( "fwrite file meta failed (%d-%s)", ferror( writer->file ), strerror( ferror( writer->file ) ) ));
     258           0 :     return NULL;
     259           0 :   }
     260             : 
     261             :   /* Restore stream cursor */
     262             : 
     263           0 :   if( FD_UNLIKELY( 0!=fseek( writer->file, cursor, SEEK_SET ) ) ) {
     264           0 :     FD_LOG_WARNING(( "fseek failed (%d-%s)", errno, strerror( errno ) ));
     265           0 :     return NULL;
     266           0 :   }
     267             : 
     268           0 :   return writer;
     269           0 : }
     270             : 
     271             : /* fd_solcap_flush_account_table writes the buffered account table out
     272             :    to the stream. */
     273             : 
     274             : static int
     275           0 : fd_solcap_flush_account_table( fd_solcap_writer_t * writer ) {
     276             : 
     277             :   /* Only flush if at least one account present. */
     278             : 
     279           0 :   if( writer->account_idx == 0UL ) return 0;
     280             : 
     281             :   /* Skip if table was overflowed. */
     282             : 
     283             :   /* FIXME: This breaks account recording for epoch boundaries and needs to be fixed */
     284           0 :   if( writer->account_idx >= FD_SOLCAP_ACC_TBL_CNT ) {
     285           0 :     FD_LOG_WARNING(( "too many records in solcap accounts table - try increasing FD_SOLCAP_ACC_TBL_CNT" ));
     286           0 :     writer->account_idx = 0UL;
     287           0 :     return 0;
     288           0 :   }
     289             : 
     290             :   /* Leave space for header */
     291             : 
     292           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     293           0 :   FSKIP_BAIL( writer->file, sizeof(fd_solcap_chunk_t) );
     294             : 
     295             :   /* Translate account table to chunk-relative addressing */
     296             : 
     297           0 :   for( uint i=0U; i<writer->account_idx; i++ )
     298           0 :     writer->accounts[i].acc_coff -= (long)chunk_goff;
     299             : 
     300             :   /* Write account table (at beginning of chunk) */
     301             : 
     302           0 :   ulong account_table_coff = sizeof(fd_solcap_chunk_t);
     303           0 :   ulong account_table_cnt  = writer->account_idx;
     304             : 
     305           0 :   FWRITE_BAIL( writer->accounts,
     306           0 :                sizeof(fd_solcap_account_tbl_t),
     307           0 :                account_table_cnt,
     308           0 :                writer->file );
     309             : 
     310             :   /* Serialize account chunk metadata */
     311             : 
     312           0 :   ulong meta_goff = FTELL_BAIL( writer->file );
     313           0 :   fd_solcap_AccountTableMeta meta = {
     314           0 :     .slot               = writer->slot,
     315           0 :     .account_table_coff = account_table_coff,
     316           0 :     .account_table_cnt  = account_table_cnt
     317           0 :   };
     318             : 
     319           0 :   uchar encoded[ FD_SOLCAP_ACTB_META_FOOTPRINT ];
     320           0 :   pb_ostream_t stream = pb_ostream_from_buffer( encoded, sizeof(encoded) );
     321           0 :   if( FD_UNLIKELY( !pb_encode( &stream, fd_solcap_AccountTableMeta_fields, &meta ) ) ) {
     322           0 :     FD_LOG_WARNING(( "pb_encode failed (%s)", PB_GET_ERROR(&stream) ));
     323           0 :     return EPROTO;
     324           0 :   }
     325             : 
     326           0 :   FWRITE_BAIL( encoded, 1UL,  stream.bytes_written, writer->file );
     327           0 :   FALIGN_BAIL( writer->file, 8UL );
     328             : 
     329             :   /* Serialize chunk header */
     330             : 
     331           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     332             : 
     333           0 :   fd_solcap_chunk_t chunk = {
     334           0 :     .magic     = FD_SOLCAP_V1_ACTB_MAGIC,
     335           0 :     .meta_coff = (uint)( meta_goff - chunk_goff ),
     336           0 :     .meta_sz   = (uint)stream.bytes_written,
     337           0 :     .total_sz  = chunk_end_goff - chunk_goff
     338           0 :   };
     339             : 
     340             :   /* Write out chunk */
     341             : 
     342           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     343           0 :   FWRITE_BAIL( &chunk,  sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     344             : 
     345             :   /* Restore stream cursor */
     346             : 
     347           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     348             : 
     349             :   /* Wind up for next iteration */
     350             : 
     351           0 :   writer->account_table_goff = chunk_goff;
     352           0 :   writer->account_idx        = 0U;
     353             : 
     354           0 :   return 0;
     355           0 : }
     356             : 
     357             : int
     358             : fd_solcap_write_account( fd_solcap_writer_t *             writer,
     359             :                          void const *                     key,
     360             :                          fd_solana_account_meta_t const * meta,
     361             :                          void const *                     data,
     362             :                          ulong                            data_sz,
     363           0 :                          void const *                     hash ) {
     364             : 
     365           0 :   if( FD_LIKELY( !writer ) ) return 0;
     366             : 
     367           0 :   fd_solcap_account_tbl_t rec[1];
     368           0 :   memset( rec, 0, sizeof(fd_solcap_account_tbl_t) );
     369           0 :   memcpy( rec->key,  key,  32UL );
     370           0 :   memcpy( rec->hash, hash, 32UL );
     371             : 
     372           0 :   fd_solcap_AccountMeta meta_pb[1] = {{
     373           0 :     .lamports   = meta->lamports,
     374           0 :     .rent_epoch = meta->rent_epoch,
     375           0 :     .executable = meta->executable,
     376           0 :     .data_sz    = data_sz,
     377           0 :   }};
     378           0 :   memcpy( meta_pb->owner, meta->owner, 32UL );
     379             : 
     380           0 :   return fd_solcap_write_account2( writer, rec, meta_pb, data, data_sz );
     381           0 : }
     382             : 
     383             : int
     384             : fd_solcap_write_account2( fd_solcap_writer_t *             writer,
     385             :                           fd_solcap_account_tbl_t const *  tbl,
     386             :                           fd_solcap_AccountMeta *          meta_pb,
     387             :                           void const *                     data,
     388           0 :                           ulong                            data_sz ) {
     389             : 
     390           0 :   if( FD_LIKELY( !writer ) ) return 0;
     391             : 
     392             :   /* Locate chunk */
     393             : 
     394           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     395             : 
     396             :   /* Write data */
     397             : 
     398           0 :   ulong data_coff = sizeof(fd_solcap_chunk_t);
     399           0 :   FSKIP_BAIL ( writer->file, data_coff );
     400           0 :   FWRITE_BAIL( data, 1UL, data_sz, writer->file );
     401           0 :   FALIGN_BAIL( writer->file, 8UL );
     402             : 
     403             :   /* Serialize account meta */
     404             : 
     405           0 :   ulong meta_goff = FTELL_BAIL( writer->file );
     406             : 
     407           0 :   meta_pb->slot      = writer->slot;
     408           0 :   meta_pb->data_coff = (long)data_coff;
     409           0 :   meta_pb->data_sz   = data_sz;
     410             : 
     411           0 :   uchar meta_pb_enc[ FD_SOLCAP_ACCOUNT_META_FOOTPRINT ];
     412           0 :   pb_ostream_t stream = pb_ostream_from_buffer( meta_pb_enc, sizeof(meta_pb_enc) );
     413           0 :   FD_TEST( pb_encode( &stream, fd_solcap_AccountMeta_fields, meta_pb ) );
     414             : 
     415             :   /* Write account meta */
     416             : 
     417           0 :   ulong meta_coff = meta_goff - chunk_goff;
     418           0 :   FWRITE_BAIL( meta_pb_enc, 1UL, stream.bytes_written, writer->file );
     419           0 :   FALIGN_BAIL( writer->file, 8UL );
     420             : 
     421             :   /* Remember account table entry */
     422             : 
     423           0 :   if( writer->account_idx < FD_SOLCAP_ACC_TBL_CNT ) {
     424           0 :     fd_solcap_account_tbl_t * account = &writer->accounts[ writer->account_idx ];
     425           0 :     memcpy( account, tbl, sizeof(fd_solcap_account_tbl_t) );
     426             : 
     427             :     /* Since we don't yet know the final position of the account table,
     428             :        we temporarily store a global offset.  This will later get
     429             :        converted into a chunk offset. */
     430           0 :     account->acc_coff = (long)chunk_goff;
     431           0 :   }
     432             : 
     433             :   /* Serialize chunk header */
     434             : 
     435           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     436             : 
     437           0 :   fd_solcap_chunk_t chunk = {
     438           0 :     .magic     = FD_SOLCAP_V1_ACCT_MAGIC,
     439           0 :     .meta_coff = (uint)meta_coff,
     440           0 :     .meta_sz   = (uint)stream.bytes_written,
     441           0 :     .total_sz  = chunk_end_goff - chunk_goff
     442           0 :   };
     443             : 
     444             :   /* Write out chunk */
     445             : 
     446           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     447           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     448             : 
     449             :   /* Restore stream cursor */
     450             : 
     451           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     452             : 
     453             :   /* Wind up for next iteration */
     454             : 
     455           0 :   writer->account_idx += 1U;
     456             : 
     457           0 :   return 0;
     458           0 : }
     459             : 
     460             : void
     461             : fd_solcap_writer_set_slot( fd_solcap_writer_t * writer,
     462           0 :                           ulong                slot ) {
     463             : 
     464           0 :   if( FD_LIKELY( !writer ) ) return;
     465             : 
     466             :   /* Discard account table buffer */
     467           0 :   writer->account_table_goff = 0UL;
     468           0 :   writer->account_idx        = 0UL;
     469           0 :   writer->slot               = slot;
     470           0 : }
     471             : 
     472             : int
     473             : fd_solcap_write_bank_preimage( fd_solcap_writer_t * writer,
     474             :                                void const *         bank_hash,
     475             :                                void const *         prev_bank_hash,
     476             :                                void const *         account_delta_hash,
     477             :                                void const *         poh_hash,
     478           0 :                                ulong                signature_cnt ) {
     479             : 
     480           0 :   if( FD_LIKELY( !writer ) ) return 0;
     481             : 
     482           0 :   fd_solcap_BankPreimage preimage_pb[1] = {{0}};
     483           0 :   preimage_pb->signature_cnt = signature_cnt;
     484           0 :   preimage_pb->account_cnt   = writer->account_idx;
     485           0 :   memcpy( preimage_pb->bank_hash,          bank_hash,          32UL );
     486           0 :   memcpy( preimage_pb->prev_bank_hash,     prev_bank_hash,     32UL );
     487           0 :   memcpy( preimage_pb->account_delta_hash, account_delta_hash, 32UL );
     488           0 :   memcpy( preimage_pb->poh_hash,           poh_hash,           32UL );
     489             : 
     490           0 :   return fd_solcap_write_bank_preimage2( writer, preimage_pb );
     491           0 : }
     492             : 
     493             : 
     494             : int
     495             : fd_solcap_write_bank_preimage2( fd_solcap_writer_t *     writer,
     496           0 :                                 fd_solcap_BankPreimage * preimage_pb ) {
     497             : 
     498           0 :   if( FD_LIKELY( !writer ) ) return 0;
     499             : 
     500           0 :   int err = fd_solcap_flush_account_table( writer );
     501           0 :   if( FD_UNLIKELY( err!=0 ) ) return err;
     502             : 
     503             :   /* Leave space for header */
     504             : 
     505           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     506           0 :   FSKIP_BAIL( writer->file, sizeof(fd_solcap_chunk_t) );
     507             : 
     508             :   /* Fixup predefined entries */
     509             : 
     510           0 :   preimage_pb->slot               = writer->slot;
     511           0 :   if( writer->account_table_goff ) {
     512           0 :     preimage_pb->account_cnt        = writer->account_idx;
     513           0 :     preimage_pb->account_table_coff = (long)writer->account_table_goff - (long)chunk_goff;
     514           0 :   }
     515             : 
     516             :   /* Serialize bank preimage */
     517             : 
     518           0 :   uchar preimage_pb_enc[ FD_SOLCAP_BANK_PREIMAGE_FOOTPRINT ] = {0};
     519           0 :   pb_ostream_t stream = pb_ostream_from_buffer( preimage_pb_enc, sizeof(preimage_pb_enc) );
     520           0 :   FD_TEST( pb_encode( &stream, fd_solcap_BankPreimage_fields, preimage_pb ) );
     521           0 :   ulong meta_sz = stream.bytes_written;
     522             : 
     523           0 :   FWRITE_BAIL( preimage_pb_enc, 1UL, meta_sz, writer->file );
     524           0 :   FALIGN_BAIL( writer->file, 8UL );
     525           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     526             : 
     527             :   /* Serialize chunk header */
     528             : 
     529           0 :   fd_solcap_chunk_t chunk = {
     530           0 :     .magic     = FD_SOLCAP_V1_BANK_MAGIC,
     531           0 :     .meta_coff = (uint)sizeof(fd_solcap_chunk_t),
     532           0 :     .meta_sz   = (uint)meta_sz,
     533           0 :     .total_sz  = chunk_end_goff - chunk_goff
     534           0 :   };
     535             : 
     536             :   /* Write out chunk */
     537             : 
     538           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     539           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     540             : 
     541             :   /* Restore stream cursor */
     542             : 
     543           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     544             : 
     545           0 :   return 0;
     546           0 : }
     547             : 
     548             : int fd_solcap_write_transaction2( fd_solcap_writer_t *    writer,
     549           0 :                                   fd_solcap_Transaction * txn ) {
     550             : 
     551           0 :   if( FD_LIKELY( !writer ) ) return 0;
     552             : 
     553             :   /* Locate chunk */
     554           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     555           0 :   FSKIP_BAIL( writer->file, sizeof(fd_solcap_chunk_t) );
     556             : 
     557             :   /* Serialize and write transaction */
     558           0 :   uchar txn_pb_enc[ FD_SOLCAP_TRANSACTION_FOOTPRINT ];
     559           0 :   pb_ostream_t stream = pb_ostream_from_buffer( txn_pb_enc, sizeof(txn_pb_enc) );
     560           0 :   FD_TEST( pb_encode( &stream, fd_solcap_Transaction_fields, txn ) );
     561             : 
     562           0 :   FWRITE_BAIL( txn_pb_enc, 1UL, stream.bytes_written, writer->file );
     563           0 :   FALIGN_BAIL( writer->file, 8UL );
     564           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     565             : 
     566             :   /* Serialize chunk header */
     567           0 :   fd_solcap_chunk_t chunk = {
     568           0 :     .magic     = FD_SOLCAP_V1_TRXN_MAGIC,
     569           0 :     .meta_coff = (uint)sizeof(fd_solcap_chunk_t),
     570           0 :     .meta_sz   = (uint)stream.bytes_written,
     571           0 :     .total_sz  = chunk_end_goff - chunk_goff
     572           0 :   };
     573             : 
     574             :   /* Write out chunk */
     575           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     576           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     577             : 
     578             :   /* Restore stream cursor */
     579             : 
     580           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     581             : 
     582           0 :   return 0;
     583           0 : }

Generated by: LCOV version 1.14