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

Generated by: LCOV version 1.14