LCOV - code coverage report
Current view: top level - flamenco/capture - fd_solcap_writer.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 362 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 20 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 "../../ballet/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 :     .executable = meta->executable,
     375           0 :     .data_sz    = data_sz,
     376           0 :   }};
     377           0 :   memcpy( meta_pb->owner, meta->owner, 32UL );
     378             : 
     379           0 :   return fd_solcap_write_account2( writer, rec, meta_pb, data, data_sz );
     380           0 : }
     381             : 
     382             : int
     383             : fd_solcap_write_account2( fd_solcap_writer_t *             writer,
     384             :                           fd_solcap_account_tbl_t const *  tbl,
     385             :                           fd_solcap_AccountMeta *          meta_pb,
     386             :                           void const *                     data,
     387           0 :                           ulong                            data_sz ) {
     388             : 
     389           0 :   if( FD_LIKELY( !writer ) ) return 0;
     390             : 
     391             :   /* Locate chunk */
     392             : 
     393           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     394             : 
     395             :   /* Write data */
     396             : 
     397           0 :   ulong data_coff = sizeof(fd_solcap_chunk_t);
     398           0 :   FSKIP_BAIL ( writer->file, data_coff );
     399           0 :   FWRITE_BAIL( data, 1UL, data_sz, writer->file );
     400           0 :   FALIGN_BAIL( writer->file, 8UL );
     401             : 
     402             :   /* Serialize account meta */
     403             : 
     404           0 :   ulong meta_goff = FTELL_BAIL( writer->file );
     405             : 
     406           0 :   meta_pb->slot      = writer->slot;
     407           0 :   meta_pb->data_coff = (long)data_coff;
     408           0 :   meta_pb->data_sz   = data_sz;
     409             : 
     410           0 :   uchar meta_pb_enc[ FD_SOLCAP_ACCOUNT_META_FOOTPRINT ];
     411           0 :   pb_ostream_t stream = pb_ostream_from_buffer( meta_pb_enc, sizeof(meta_pb_enc) );
     412           0 :   FD_TEST( pb_encode( &stream, fd_solcap_AccountMeta_fields, meta_pb ) );
     413             : 
     414             :   /* Write account meta */
     415             : 
     416           0 :   ulong meta_coff = meta_goff - chunk_goff;
     417           0 :   FWRITE_BAIL( meta_pb_enc, 1UL, stream.bytes_written, writer->file );
     418           0 :   FALIGN_BAIL( writer->file, 8UL );
     419             : 
     420             :   /* Remember account table entry */
     421             : 
     422           0 :   if( writer->account_idx < FD_SOLCAP_ACC_TBL_CNT ) {
     423           0 :     fd_solcap_account_tbl_t * account = &writer->accounts[ writer->account_idx ];
     424           0 :     *account = *tbl;
     425             : 
     426             :     /* Since we don't yet know the final position of the account table,
     427             :        we temporarily store a global offset.  This will later get
     428             :        converted into a chunk offset. */
     429           0 :     account->acc_coff = (long)chunk_goff;
     430           0 :   }
     431             : 
     432             :   /* Serialize chunk header */
     433             : 
     434           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     435             : 
     436           0 :   fd_solcap_chunk_t chunk = {
     437           0 :     .magic     = FD_SOLCAP_V1_ACCT_MAGIC,
     438           0 :     .meta_coff = (uint)meta_coff,
     439           0 :     .meta_sz   = (uint)stream.bytes_written,
     440           0 :     .total_sz  = chunk_end_goff - chunk_goff
     441           0 :   };
     442             : 
     443             :   /* Write out chunk */
     444             : 
     445           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     446           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     447             : 
     448             :   /* Restore stream cursor */
     449             : 
     450           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     451             : 
     452             :   /* Wind up for next iteration */
     453             : 
     454           0 :   writer->account_idx += 1U;
     455             : 
     456           0 :   return 0;
     457           0 : }
     458             : 
     459             : void
     460             : fd_solcap_writer_set_slot( fd_solcap_writer_t * writer,
     461           0 :                           ulong                slot ) {
     462             : 
     463           0 :   if( FD_LIKELY( !writer ) ) return;
     464             : 
     465             :   /* Discard account table buffer */
     466           0 :   writer->account_table_goff = 0UL;
     467           0 :   writer->account_idx        = 0UL;
     468           0 :   writer->slot               = slot;
     469           0 : }
     470             : 
     471             : int
     472             : fd_solcap_write_bank_preimage( fd_solcap_writer_t * writer,
     473             :                                void const *         bank_hash,
     474             :                                void const *         prev_bank_hash,
     475             :                                void const *         account_delta_hash,
     476             :                                void const *         accounts_lt_hash_checksum,
     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 :   if (NULL != account_delta_hash )
     488           0 :     memcpy( preimage_pb->account_delta_hash,        account_delta_hash, 32UL );
     489           0 :   else
     490           0 :     fd_memset( preimage_pb->account_delta_hash, 0, 32UL );
     491           0 :   if( NULL != accounts_lt_hash_checksum )
     492           0 :     memcpy( preimage_pb->accounts_lt_hash_checksum, accounts_lt_hash_checksum, 32UL );
     493           0 :   else
     494           0 :     fd_memset(preimage_pb->accounts_lt_hash_checksum, 0, 32UL );
     495           0 :   memcpy( preimage_pb->poh_hash,                  poh_hash,           32UL );
     496             : 
     497           0 :   return fd_solcap_write_bank_preimage2( writer, preimage_pb );
     498           0 : }
     499             : 
     500             : 
     501             : int
     502             : fd_solcap_write_bank_preimage2( fd_solcap_writer_t *     writer,
     503           0 :                                 fd_solcap_BankPreimage * preimage_pb ) {
     504             : 
     505           0 :   if( FD_LIKELY( !writer ) ) return 0;
     506             : 
     507           0 :   int err = fd_solcap_flush_account_table( writer );
     508           0 :   if( FD_UNLIKELY( err!=0 ) ) return err;
     509             : 
     510             :   /* Leave space for header */
     511             : 
     512           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     513           0 :   FSKIP_BAIL( writer->file, sizeof(fd_solcap_chunk_t) );
     514             : 
     515             :   /* Fixup predefined entries */
     516             : 
     517           0 :   preimage_pb->slot               = writer->slot;
     518           0 :   if( writer->account_table_goff ) {
     519           0 :     preimage_pb->account_cnt        = writer->account_idx;
     520           0 :     preimage_pb->account_table_coff = (long)writer->account_table_goff - (long)chunk_goff;
     521           0 :   }
     522             : 
     523             :   /* Serialize bank preimage */
     524             : 
     525           0 :   uchar preimage_pb_enc[ FD_SOLCAP_BANK_PREIMAGE_FOOTPRINT ] = {0};
     526           0 :   pb_ostream_t stream = pb_ostream_from_buffer( preimage_pb_enc, sizeof(preimage_pb_enc) );
     527           0 :   FD_TEST( pb_encode( &stream, fd_solcap_BankPreimage_fields, preimage_pb ) );
     528           0 :   ulong meta_sz = stream.bytes_written;
     529             : 
     530           0 :   FWRITE_BAIL( preimage_pb_enc, 1UL, meta_sz, writer->file );
     531           0 :   FALIGN_BAIL( writer->file, 8UL );
     532           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     533             : 
     534             :   /* Serialize chunk header */
     535             : 
     536           0 :   fd_solcap_chunk_t chunk = {
     537           0 :     .magic     = FD_SOLCAP_V1_BANK_MAGIC,
     538           0 :     .meta_coff = (uint)sizeof(fd_solcap_chunk_t),
     539           0 :     .meta_sz   = (uint)meta_sz,
     540           0 :     .total_sz  = chunk_end_goff - chunk_goff
     541           0 :   };
     542             : 
     543             :   /* Write out chunk */
     544             : 
     545           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     546           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     547             : 
     548             :   /* Restore stream cursor */
     549             : 
     550           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     551             : 
     552           0 :   return 0;
     553           0 : }
     554             : 
     555             : int fd_solcap_write_transaction2( fd_solcap_writer_t *    writer,
     556           0 :                                   fd_solcap_Transaction * txn ) {
     557             : 
     558           0 :   if( FD_LIKELY( !writer ) ) return 0;
     559             : 
     560             :   /* Locate chunk */
     561           0 :   ulong chunk_goff = FTELL_BAIL( writer->file );
     562           0 :   FSKIP_BAIL( writer->file, sizeof(fd_solcap_chunk_t) );
     563             : 
     564             :   /* Serialize and write transaction */
     565           0 :   uchar txn_pb_enc[ FD_SOLCAP_TRANSACTION_FOOTPRINT ];
     566           0 :   pb_ostream_t stream = pb_ostream_from_buffer( txn_pb_enc, sizeof(txn_pb_enc) );
     567           0 :   FD_TEST( pb_encode( &stream, fd_solcap_Transaction_fields, txn ) );
     568             : 
     569           0 :   FWRITE_BAIL( txn_pb_enc, 1UL, stream.bytes_written, writer->file );
     570           0 :   FALIGN_BAIL( writer->file, 8UL );
     571           0 :   ulong chunk_end_goff = FTELL_BAIL( writer->file );
     572             : 
     573             :   /* Serialize chunk header */
     574           0 :   fd_solcap_chunk_t chunk = {
     575           0 :     .magic     = FD_SOLCAP_V1_TRXN_MAGIC,
     576           0 :     .meta_coff = (uint)sizeof(fd_solcap_chunk_t),
     577           0 :     .meta_sz   = (uint)stream.bytes_written,
     578           0 :     .total_sz  = chunk_end_goff - chunk_goff
     579           0 :   };
     580             : 
     581             :   /* Write out chunk */
     582           0 :   FSEEK_BAIL( writer->file, (long)chunk_goff, SEEK_SET );
     583           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     584             : 
     585             :   /* Restore stream cursor */
     586             : 
     587           0 :   FSEEK_BAIL( writer->file, (long)chunk_end_goff, SEEK_SET );
     588             : 
     589           0 :   return 0;
     590           0 : }
     591             : 
     592             : int
     593             : fd_solcap_writer_stake_rewards_begin(
     594             :     fd_solcap_writer_t * writer,
     595             :     ulong                payout_epoch,
     596             :     ulong                reward_epoch,
     597             :     ulong                inflation_lamports,
     598             :     uint128              total_points
     599           0 : ) {
     600           0 :   fd_solcap_StakeRewardEpoch epoch_pb = {
     601           0 :     .payout_epoch       = payout_epoch,
     602           0 :     .reward_epoch       = reward_epoch,
     603           0 :     .inflation_lamports = inflation_lamports,
     604           0 :   };
     605           0 :   FD_STORE( uint128, epoch_pb.points, total_points );
     606           0 :   return fd_solcap_write_protobuf( writer, &epoch_pb, fd_solcap_StakeRewardEpoch_fields, FD_SOLCAP_V1_REWARD_BEGIN_MAGIC );
     607           0 : }
     608             : 
     609             : int
     610             : fd_solcap_write_stake_reward_event(
     611             :     fd_solcap_writer_t * writer,
     612             :     fd_pubkey_t const *  stake_acc_addr,
     613             :     fd_pubkey_t const *  vote_acc_addr,
     614             :     uint                 commission,
     615             :     long                 vote_rewards,
     616             :     long                 stake_rewards,
     617             :     long                 new_credits_observed
     618           0 : ) {
     619           0 :   fd_solcap_StakeRewardEvent event = {
     620           0 :     .commission           = commission,
     621           0 :     .vote_rewards         = vote_rewards,
     622           0 :     .stake_rewards        = stake_rewards,
     623           0 :     .new_credits_observed = new_credits_observed
     624           0 :   };
     625           0 :   memcpy( event.stake_account_address, stake_acc_addr, 32UL );
     626           0 :   memcpy( event.vote_account_address,  vote_acc_addr,  32UL );
     627           0 :   return fd_solcap_write_protobuf( writer, &event, fd_solcap_StakeRewardEvent_fields, FD_SOLCAP_V1_REWARD_CALC_MAGIC );
     628           0 : }
     629             : 
     630             : int
     631             : fd_solcap_write_vote_account_payout(
     632             :     fd_solcap_writer_t * writer,
     633             :     fd_pubkey_t const *  vote_acc_addr,
     634             :     ulong                update_slot,
     635             :     ulong                lamports,
     636             :     long                 lamports_delta
     637           0 : ) {
     638           0 :   fd_solcap_VoteAccountPayout payout = {
     639           0 :     .update_slot    = update_slot,
     640           0 :     .lamports       = lamports,
     641           0 :     .lamports_delta = lamports_delta
     642           0 :   };
     643           0 :   memcpy( payout.address, vote_acc_addr, 32UL );
     644           0 :   return fd_solcap_write_protobuf( writer, &payout, fd_solcap_VoteAccountPayout_fields, FD_SOLCAP_V1_REWARD_VOTE_MAGIC );
     645           0 : }
     646             : 
     647             : int
     648             : fd_solcap_write_stake_account_payout(
     649             :     fd_solcap_writer_t * writer,
     650             :     fd_pubkey_t const *  stake_acc_addr,
     651             :     ulong                update_slot,
     652             :     ulong                lamports,
     653             :     long                 lamports_delta,
     654             :     ulong                credits_observed,
     655             :     long                 credits_observed_delta,
     656             :     ulong                delegation_stake,
     657             :     long                 delegation_stake_delta
     658           0 : ) {
     659           0 :   fd_solcap_StakeAccountPayout payout = {
     660           0 :     .update_slot            = update_slot,
     661           0 :     .lamports               = lamports,
     662           0 :     .lamports_delta         = lamports_delta,
     663           0 :     .credits_observed       = credits_observed,
     664           0 :     .credits_observed_delta = credits_observed_delta,
     665           0 :     .delegation_stake       = delegation_stake,
     666           0 :     .delegation_stake_delta = delegation_stake_delta
     667           0 :   };
     668           0 :   memcpy( payout.address, stake_acc_addr, 32UL );
     669           0 :   return fd_solcap_write_protobuf( writer, &payout, fd_solcap_StakeAccountPayout_fields, FD_SOLCAP_V1_REWARD_STAKE_MAGIC );
     670           0 : }
     671             : 
     672             : int
     673             : fd_solcap_write_protobuf( fd_solcap_writer_t *        writer,
     674             :                           void const *                msg,
     675             :                           struct pb_msgdesc_s const * desc,
     676           0 :                           ulong                       magic ) {
     677           0 :   if( FD_UNLIKELY( !writer ) ) return 0;
     678             : 
     679           0 :   uchar buf[ 1UL<<20 ];
     680           0 :   pb_ostream_t stream = pb_ostream_from_buffer( buf, sizeof(buf) );
     681           0 :   if( FD_UNLIKELY( !pb_encode( &stream, desc, msg ) ) ) {
     682           0 :     FD_LOG_WARNING(( "pb_encode failed (%s)", PB_GET_ERROR(&stream) ));
     683           0 :     return EPROTO;
     684           0 :   }
     685             : 
     686           0 :   fd_solcap_chunk_t chunk = {
     687           0 :     .magic     = magic,
     688           0 :     .meta_coff = (uint)sizeof(fd_solcap_chunk_t),
     689           0 :     .meta_sz   = (uint)stream.bytes_written,
     690           0 :     .total_sz  = stream.bytes_written + sizeof(fd_solcap_chunk_t)
     691           0 :   };
     692             : 
     693           0 :   FWRITE_BAIL( &chunk, sizeof(fd_solcap_chunk_t), 1UL, writer->file );
     694           0 :   FWRITE_BAIL( buf, 1UL, stream.bytes_written,         writer->file );
     695           0 :   return 0;
     696           0 : }

Generated by: LCOV version 1.14