LCOV - code coverage report
Current view: top level - flamenco/events - fd_event_runtime.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 6 447 1.3 %
Date: 2026-11-12 04:25:03 Functions: 2 22 9.1 %

          Line data    Source code
       1             : #include "fd_event_runtime.h"
       2             : #include "../../disco/events/fd_event_report.h"
       3             : #include "../runtime/fd_system_ids.h"
       4             : #include "../runtime/sysvar/fd_sysvar_last_restart_slot.h"
       5             : #include "../runtime/sysvar/fd_sysvar_recent_hashes.h"
       6             : #include "../../ballet/blake3/fd_blake3.h"
       7             : 
       8             : FD_TL fd_event_reporter_t * fd_event_runtime_diff_tl = NULL;
       9             : 
      10             : FD_STATIC_ASSERT( sizeof(fd_event_runtime_stake_delegation_t)<=FD_EVENT_RUNTIME_DIFF_MTU, diff_mtu );
      11             : FD_STATIC_ASSERT( sizeof(fd_event_runtime_vote_account_t    )<=FD_EVENT_RUNTIME_DIFF_MTU, diff_mtu );
      12             : FD_STATIC_ASSERT( sizeof(fd_event_runtime_epoch_t           )<=FD_EVENT_RUNTIME_DIFF_MTU, diff_mtu );
      13             : FD_STATIC_ASSERT( sizeof(fd_event_runtime_rooted_t          )<=FD_EVENT_RUNTIME_DIFF_MTU, diff_mtu );
      14             : FD_STATIC_ASSERT( sizeof(fd_event_runtime_reward_t          )<=FD_EVENT_RUNTIME_DIFF_MTU, diff_mtu );
      15             : 
      16           0 : #define REPORT_DIFF( call ) do {                                      \
      17           0 :     fd_event_reporter_t * _tl = fd_event_tl;                          \
      18           0 :     if( fd_event_runtime_diff_tl ) fd_event_tl = fd_event_runtime_diff_tl; \
      19           0 :     call;                                                             \
      20           0 :     fd_event_tl = _tl;                                                \
      21           0 :   } while(0)
      22             : 
      23             : void
      24             : fd_event_runtime_txn_emit( fd_txn_in_t  const * txn_in,
      25             :                            fd_txn_out_t const * txn_out,
      26           0 :                            fd_bank_t    const * bank ) {
      27           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
      28           0 :   if( FD_UNLIKELY( !txn_in || !txn_in->txn || !bank ) ) return;
      29             : 
      30           0 :   fd_event_runtime_txn_t ev = {0};
      31             : 
      32             :   /* Identity */
      33           0 :   uchar const *    payload = (uchar const *)txn_in->txn->payload;
      34           0 :   fd_txn_t const * txn_d   = TXN( txn_in->txn );
      35           0 :   fd_memcpy( ev.signature, payload + txn_d->signature_off, 64UL );
      36           0 :   fd_memcpy( ev.blockhash,       txn_out->details.blockhash.uc, 32UL );
      37           0 :   fd_memcpy( ev.fec_merkle_root, txn_in->fec_merkle_root,       32UL );
      38           0 :   if( FD_LIKELY( txn_out->accounts.cnt>0UL ) ) {
      39           0 :     fd_memcpy( ev.fee_payer, txn_out->accounts.keys[ 0 ].uc, 32UL );
      40           0 :   }
      41             : 
      42           0 :   ev.bank_seq             = bank->bank_seq;
      43           0 :   ev.slot                 = bank->f.slot;
      44           0 :   ev.epoch                = bank->f.epoch;
      45           0 :   ev.index_in_slot        = txn_in->index_in_slot;
      46           0 :   ev.commit_index_in_slot = txn_out->err.is_committable ? txn_out->details.commit_index_in_slot : 0UL;
      47             : 
      48             :   /* Flags */
      49           0 :   ev.is_bundle      = !!txn_in->bundle.is_bundle;
      50           0 :   ev.is_committable = !!txn_out->err.is_committable;
      51           0 :   ev.is_fees_only   = !!txn_out->err.is_fees_only;
      52             : 
      53             :   /* Errors */
      54           0 :   ev.txn_err       = fd_event_txn_err_from_txn_err( txn_out->err.txn_err );
      55           0 :   int is_instr_err = ( txn_out->err.txn_err==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR );
      56           0 :   ev.exec_err      = is_instr_err ? fd_event_exec_err_from_exec_err          ( txn_out->err.exec_err      ) : FD_EVENT_RUNTIME_TXN_EXEC_ERR_SUCCESS;
      57           0 :   ev.exec_err_kind = is_instr_err ? fd_event_exec_err_kind_from_exec_err_kind( txn_out->err.exec_err_kind ) : FD_EVENT_RUNTIME_TXN_EXEC_ERR_KIND_NONE;
      58           0 :   ev.exec_err_idx  = is_instr_err ? txn_out->err.exec_err_idx : UINT_MAX;
      59           0 :   ev.custom_err    = ( is_instr_err && txn_out->err.exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR )
      60           0 :                        ? txn_out->err.custom_err : UINT_MAX;
      61             : 
      62             :   /* Compute budget */
      63           0 :   fd_compute_budget_details_t const * cb = &txn_out->details.compute_budget;
      64           0 :   ev.compute_unit_limit              = cb->compute_unit_limit;
      65           0 :   ev.compute_unit_price              = cb->compute_unit_price;
      66           0 :   ev.compute_units_consumed          = (cb->compute_unit_limit > cb->compute_meter)
      67           0 :                                          ? cb->compute_unit_limit - cb->compute_meter : 0UL;
      68           0 :   ev.heap_size                       = cb->heap_size;
      69           0 :   ev.num_builtin_instrs              = cb->num_builtin_instrs;
      70           0 :   ev.num_non_builtin_instrs          = cb->num_non_builtin_instrs;
      71           0 :   ev.loaded_accounts_data_size       = txn_out->details.loaded_accounts_data_size;
      72           0 :   ev.loaded_accounts_data_size_limit = cb->loaded_accounts_data_size_limit;
      73           0 :   ev.accounts_resize_delta           = txn_out->details.accounts_resize_delta;
      74             : 
      75             :   /* Fees */
      76           0 :   ev.execution_fee   = txn_out->details.execution_fee;
      77           0 :   ev.priority_fee    = txn_out->details.priority_fee;
      78           0 :   ev.tips            = txn_out->details.tips;
      79           0 :   ev.signature_count = txn_out->details.signature_count;
      80             : 
      81             :   /* Cost tracker */
      82           0 :   fd_usage_cost_details_t const * c = &txn_out->details.txn_cost.transaction;
      83           0 :   ev.cost_signature                    = c->signature_cost;
      84           0 :   ev.cost_write_lock                   = c->write_lock_cost;
      85           0 :   ev.cost_data_bytes                   = c->data_bytes_cost;
      86           0 :   ev.cost_programs_execution           = c->programs_execution_cost;
      87           0 :   ev.cost_loaded_accounts_data_size    = c->loaded_accounts_data_size_cost;
      88           0 :   ev.cost_allocated_accounts_data_size = c->allocated_accounts_data_size;
      89             : 
      90             :   /* account_diffs: walk per-txn writable accounts, compare prior vs current */
      91           0 :   ulong diff_cnt = 0UL;
      92           0 :   for( ulong i=0UL; i<txn_out->accounts.cnt; i++ ) {
      93           0 :     if( diff_cnt>=64UL ) break;
      94           0 :     fd_acc_t const * acc = txn_out->accounts.account[ i ];
      95           0 :     if( FD_UNLIKELY( !acc ) ) continue;
      96           0 :     if( !txn_out->accounts.is_writable[ i ] ) continue;
      97             : 
      98           0 :     int changed = ( acc->prior_lamports   != acc->lamports   ) ||
      99           0 :                   ( acc->prior_executable != acc->executable ) ||
     100           0 :                   ( acc->prior_data_len   != acc->data_len   ) ||
     101           0 :                   ( memcmp( acc->prior_owner, acc->owner, 32UL )!=0 );
     102           0 :     if( !changed && acc->prior_data && acc->data &&
     103           0 :         memcmp( acc->prior_data, acc->data, acc->data_len )!=0 ) {
     104           0 :       changed = 1;
     105           0 :     }
     106           0 :     if( !changed ) continue;
     107             : 
     108           0 :     fd_event_runtime_txn_account_diffs_t * d = &ev.account_diffs[ diff_cnt++ ];
     109           0 :     fd_memcpy( d->pubkey,     txn_out->accounts.keys[ i ].uc, 32UL );
     110           0 :     fd_memcpy( d->owner,      acc->owner,                     32UL );
     111           0 :     fd_memcpy( d->prev_owner, acc->prior_owner,               32UL );
     112           0 :     d->lamports      = acc->lamports;
     113           0 :     d->prev_lamports = acc->prior_lamports;
     114           0 :     d->data_sz       = acc->data_len;
     115           0 :     d->prev_data_sz  = acc->prior_data_len;
     116           0 :     d->is_executable   = !!acc->executable;
     117           0 :     d->is_stake_update = !!txn_out->accounts.stake_update[ i ];
     118           0 :     d->is_vote_update  = !!txn_out->accounts.vote_update [ i ];
     119           0 :     d->is_new_vote     = !!txn_out->accounts.new_vote    [ i ];
     120           0 :     d->is_rm_vote      = !!txn_out->accounts.rm_vote     [ i ];
     121           0 :   }
     122           0 :   ev.account_diffs_cnt = diff_cnt;
     123             : 
     124             :   /* writable / readonly account lists */
     125           0 :   ulong w_cnt = 0UL, r_cnt = 0UL;
     126           0 :   for( ulong i=0UL; i<txn_out->accounts.cnt; i++ ) {
     127           0 :     fd_acc_t const * acc = txn_out->accounts.account[ i ];
     128           0 :     if( FD_UNLIKELY( !acc ) ) continue;
     129           0 :     if( txn_out->accounts.is_writable[ i ] ) {
     130           0 :       if( w_cnt<64UL ) fd_memcpy( ev.writable_accounts[ w_cnt++ ], txn_out->accounts.keys[ i ].uc, 32UL );
     131           0 :     } else {
     132           0 :       if( r_cnt<64UL ) fd_memcpy( ev.readonly_accounts[ r_cnt++ ], txn_out->accounts.keys[ i ].uc, 32UL );
     133           0 :     }
     134           0 :   }
     135           0 :   ev.writable_accounts_cnt = w_cnt;
     136           0 :   ev.readonly_accounts_cnt = r_cnt;
     137             : 
     138             :   /* program_ids: walk top-level instructions, dedupe in first-occurrence order */
     139           0 :   ulong p_cnt = 0UL;
     140           0 :   for( ushort ii=0; ii<txn_d->instr_cnt; ii++ ) {
     141           0 :     if( p_cnt>=64UL ) break;
     142           0 :     uchar pid_idx = txn_d->instr[ ii ].program_id;
     143           0 :     if( (ulong)pid_idx>=txn_out->accounts.cnt ) continue;
     144           0 :     uchar const * pid = txn_out->accounts.keys[ pid_idx ].uc;
     145           0 :     int seen = 0;
     146           0 :     for( ulong j=0UL; j<p_cnt; j++ ) {
     147           0 :       if( memcmp( ev.program_ids[ j ], pid, 32UL )==0 ) { seen = 1; break; }
     148           0 :     }
     149           0 :     if( !seen ) fd_memcpy( ev.program_ids[ p_cnt++ ], pid, 32UL );
     150           0 :   }
     151           0 :   ev.program_ids_cnt = p_cnt;
     152             : 
     153           0 :   fd_event_report_runtime_txn( &ev );
     154           0 : }
     155             : 
     156             : void
     157             : fd_event_runtime_stake_delegation_emit( fd_txn_in_t      const * txn_in,
     158             :                                         fd_bank_t        const * bank,
     159             :                                         fd_pubkey_t      const * pubkey,
     160           0 :                                         fd_stake_state_t const * stake_state ) {
     161           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     162           0 :   if( FD_UNLIKELY( !txn_in || !txn_in->txn || !bank ) ) return;
     163             : 
     164           0 :   fd_event_runtime_stake_delegation_t ev = {0};
     165             : 
     166           0 :   ev.bank_seq      = bank->bank_seq;
     167           0 :   ev.slot          = bank->f.slot;
     168           0 :   ev.epoch         = bank->f.epoch;
     169           0 :   ev.index_in_slot = txn_in->index_in_slot;
     170             : 
     171           0 :   uchar const *    payload = (uchar const *)txn_in->txn->payload;
     172           0 :   fd_txn_t const * txn_d   = TXN( txn_in->txn );
     173           0 :   fd_memcpy( ev.signature, payload + txn_d->signature_off, 64UL );
     174             : 
     175           0 :   fd_memcpy( ev.stake_account, pubkey->uc, 32UL );
     176             : 
     177           0 :   if( FD_LIKELY( stake_state ) ) {
     178           0 :     ev.kind = FD_EVENT_RUNTIME_STAKE_DELEGATION_KIND_UPSERT;
     179           0 :     fd_memcpy( ev.vote_account, stake_state->stake.stake.delegation.voter_pubkey.uc, 32UL );
     180           0 :     ev.stake              = stake_state->stake.stake.delegation.stake;
     181           0 :     ev.activation_epoch   = stake_state->stake.stake.delegation.activation_epoch;
     182           0 :     ev.deactivation_epoch = stake_state->stake.stake.delegation.deactivation_epoch;
     183           0 :     ev.credits_observed   = stake_state->stake.stake.credits_observed;
     184           0 :   } else {
     185           0 :     ev.kind = FD_EVENT_RUNTIME_STAKE_DELEGATION_KIND_REMOVE;
     186           0 :   }
     187             : 
     188           0 :   fd_event_report_runtime_stake_delegation( &ev );
     189           0 : }
     190             : 
     191             : static void
     192             : fd_event_runtime_stake_delegation_entry_emit( ulong             bank_seq,
     193             :                                               ulong             slot,
     194             :                                               ulong             epoch,
     195             :                                               int               kind,
     196             :                                               uchar const *     stake_account,
     197             :                                               uchar const *     vote_account,
     198             :                                               ulong             stake,
     199             :                                               ulong             activation_epoch,
     200             :                                               ulong             deactivation_epoch,
     201           0 :                                               ulong             credits_observed ) {
     202           0 :   if( FD_UNLIKELY( !fd_event_tl ) ) return;
     203             : 
     204           0 :   fd_event_runtime_stake_delegation_t ev = {0};
     205             : 
     206           0 :   ev.bank_seq      = bank_seq;
     207           0 :   ev.slot          = slot;
     208           0 :   ev.epoch         = epoch;
     209           0 :   ev.index_in_slot = ULONG_MAX;
     210           0 :   ev.kind          = kind;
     211             : 
     212           0 :   fd_memcpy( ev.stake_account, stake_account, 32UL );
     213           0 :   fd_memcpy( ev.vote_account,  vote_account,  32UL );
     214           0 :   ev.stake              = stake;
     215           0 :   ev.activation_epoch   = activation_epoch;
     216           0 :   ev.deactivation_epoch = deactivation_epoch;
     217           0 :   ev.credits_observed   = credits_observed;
     218             : 
     219           0 :   REPORT_DIFF( fd_event_report_runtime_stake_delegation( &ev ) );
     220           0 : }
     221             : 
     222             : void
     223             : fd_event_runtime_stake_delegation_bootup_emit( ulong         slot,
     224             :                                                ulong         epoch,
     225             :                                                uchar const * stake_account,
     226             :                                                uchar const * vote_account,
     227             :                                                ulong         stake,
     228             :                                                ulong         activation_epoch,
     229             :                                                ulong         deactivation_epoch,
     230           0 :                                                ulong         credits_observed ) {
     231           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     232           0 :   fd_event_runtime_stake_delegation_entry_emit( 0UL, slot, epoch, FD_EVENT_RUNTIME_STAKE_DELEGATION_KIND_BOOTUP,
     233           0 :                                                 stake_account, vote_account, stake,
     234           0 :                                                 activation_epoch, deactivation_epoch, credits_observed );
     235           0 : }
     236             : 
     237             : void
     238             : fd_event_runtime_stake_delegation_remove_emit( fd_bank_t const * bank,
     239           0 :                                                uchar const *     stake_account ) {
     240           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     241             : 
     242           0 :   fd_event_runtime_stake_delegation_t ev = {0};
     243           0 :   ev.bank_seq      = bank->bank_seq;
     244           0 :   ev.slot          = bank->f.slot;
     245           0 :   ev.epoch         = bank->f.epoch;
     246           0 :   ev.index_in_slot = ULONG_MAX;
     247           0 :   ev.kind          = FD_EVENT_RUNTIME_STAKE_DELEGATION_KIND_REMOVE;
     248           0 :   fd_memcpy( ev.stake_account, stake_account, 32UL );
     249             : 
     250           0 :   REPORT_DIFF( fd_event_report_runtime_stake_delegation( &ev ) );
     251           0 : }
     252             : 
     253             : void
     254             : fd_event_runtime_stake_delegation_payout_emit( fd_bank_t const * bank,
     255             :                                                uchar const *     stake_account,
     256             :                                                uchar const *     vote_account,
     257             :                                                ulong             stake,
     258             :                                                ulong             activation_epoch,
     259             :                                                ulong             deactivation_epoch,
     260           0 :                                                ulong             credits_observed ) {
     261           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     262           0 :   fd_event_runtime_stake_delegation_entry_emit( bank->bank_seq, bank->f.slot, bank->f.epoch, FD_EVENT_RUNTIME_STAKE_DELEGATION_KIND_REWARD,
     263           0 :                                                 stake_account, vote_account, stake,
     264           0 :                                                 activation_epoch, deactivation_epoch, credits_observed );
     265           0 : }
     266             : 
     267             : /* Boundary processing runs single-threaded; pieces of the runtime_epoch event computed
     268             :    at different points are stashed here and emitted together at the end of the boundary. */
     269             : 
     270             : static FD_TL struct {
     271             :   fd_event_runtime_epoch_stake_history_sysvar_t stake_history;
     272             :   fd_event_runtime_epoch_epoch_rewards_sysvar_t epoch_rewards;
     273             : 
     274             :   ulong staked_vote_accounts;
     275             :   ulong top_votes_eligible;
     276             : 
     277             :   ulong vote_cnt;  /* runtime_vote_account rows emitted this boundary */
     278             :   ulong min_stake; /* smallest stake among them */
     279             : 
     280             :   uchar features[ 16 ][ 32 ];
     281             :   ulong feature_cnt;
     282             : 
     283             :   ulong vat_burn_per_vote_account;
     284             : } fd_event_runtime_epoch_summary;
     285             : 
     286             : void
     287           0 : fd_event_runtime_epoch_vat_burn( ulong burn_per_vote_account ) {
     288           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     289           0 :   fd_event_runtime_epoch_summary.vat_burn_per_vote_account = burn_per_vote_account;
     290           0 : }
     291             : 
     292             : void
     293           0 : fd_event_runtime_epoch_feature( uchar const * feature_id ) {
     294           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     295           0 :   if( FD_UNLIKELY( fd_event_runtime_epoch_summary.feature_cnt>=16UL ) ) {
     296           0 :     FD_LOG_WARNING(( "runtime_epoch feature_activations overflow (cap 16), dropping activation" ));
     297           0 :     return;
     298           0 :   }
     299           0 :   fd_memcpy( fd_event_runtime_epoch_summary.features[ fd_event_runtime_epoch_summary.feature_cnt++ ], feature_id, 32UL );
     300           0 : }
     301             : 
     302             : void
     303           0 : fd_event_runtime_epoch_stake_history( fd_stake_history_entry_t const * entry ) {
     304           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     305           0 :   fd_event_runtime_epoch_summary.stake_history.epoch        = entry->epoch;
     306           0 :   fd_event_runtime_epoch_summary.stake_history.effective    = entry->effective;
     307           0 :   fd_event_runtime_epoch_summary.stake_history.activating   = entry->activating;
     308           0 :   fd_event_runtime_epoch_summary.stake_history.deactivating = entry->deactivating;
     309           0 : }
     310             : 
     311             : void
     312           0 : fd_event_runtime_epoch_rewards( fd_sysvar_epoch_rewards_t const * epoch_rewards ) {
     313           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     314           0 :   fd_event_runtime_epoch_summary.epoch_rewards.total_rewards                      = epoch_rewards->total_rewards;
     315           0 :   fd_event_runtime_epoch_summary.epoch_rewards.distributed                        = epoch_rewards->distributed_rewards;
     316           0 :   fd_event_runtime_epoch_summary.epoch_rewards.distribution_starting_block_height = epoch_rewards->distribution_starting_block_height;
     317           0 :   fd_event_runtime_epoch_summary.epoch_rewards.num_partitions                     = epoch_rewards->num_partitions;
     318           0 :   fd_event_runtime_epoch_summary.epoch_rewards.total_points                       = epoch_rewards->total_points.ud;
     319           0 :   fd_memcpy( fd_event_runtime_epoch_summary.epoch_rewards.parent_blockhash, epoch_rewards->parent_blockhash.uc, 32UL );
     320           0 : }
     321             : 
     322             : void
     323             : fd_event_runtime_epoch_votes( ulong staked_vote_accounts,
     324           0 :                               ulong top_votes_eligible ) {
     325           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     326           0 :   fd_event_runtime_epoch_summary.staked_vote_accounts = staked_vote_accounts;
     327           0 :   fd_event_runtime_epoch_summary.top_votes_eligible   = top_votes_eligible;
     328           0 : }
     329             : 
     330             : void
     331             : fd_event_runtime_vote_account_emit( fd_bank_t const *          bank,
     332             :                                     uchar const *              pubkey,
     333             :                                     uchar const *              node_account,
     334             :                                     ulong                      stake,
     335             :                                     uint                       commission_bps,
     336             :                                     int                        has_commission_t_2,
     337             :                                     uint                       commission_t_2_bps,
     338             :                                     int                        has_commission_t_3,
     339             :                                     uint                       commission_t_3_bps,
     340             :                                     uint                       reward_commission_bps,
     341           0 :                                     fd_epoch_credits_t const * epoch_credits ) {
     342           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     343             : 
     344           0 :   if( !fd_event_runtime_epoch_summary.vote_cnt || stake<fd_event_runtime_epoch_summary.min_stake ) {
     345           0 :     fd_event_runtime_epoch_summary.min_stake = stake;
     346           0 :   }
     347             : 
     348           0 :   fd_event_runtime_epoch_summary.vote_cnt++;
     349             : 
     350           0 :   fd_event_runtime_vote_account_t ev = {0};
     351             : 
     352           0 :   ev.bank_seq = bank->bank_seq;
     353           0 :   ev.slot     = bank->f.slot;
     354           0 :   ev.epoch    = bank->f.epoch;
     355             : 
     356           0 :   fd_memcpy( ev.pubkey,       pubkey,       32UL );
     357           0 :   fd_memcpy( ev.node_account, node_account, 32UL );
     358             : 
     359           0 :   ev.stake                 = stake;
     360           0 :   ev.commission_bps        = commission_bps;
     361           0 :   ev.has_commission_t_2    = !!has_commission_t_2;
     362           0 :   ev.commission_t_2_bps    = has_commission_t_2 ? commission_t_2_bps : 0U;
     363           0 :   ev.has_commission_t_3    = !!has_commission_t_3;
     364           0 :   ev.commission_t_3_bps    = has_commission_t_3 ? commission_t_3_bps : 0U;
     365           0 :   ev.reward_commission_bps = reward_commission_bps;
     366           0 :   ev.credits               = epoch_credits->cnt ? epoch_credits->base_credits + epoch_credits->credits_delta     [ epoch_credits->cnt-1UL ] : 0UL;
     367           0 :   ev.prev_credits          = epoch_credits->cnt ? epoch_credits->base_credits + epoch_credits->prev_credits_delta[ epoch_credits->cnt-1UL ] : 0UL;
     368           0 :   ev.epoch_credits_cnt     = epoch_credits->cnt;
     369             : 
     370           0 :   REPORT_DIFF( fd_event_report_runtime_vote_account( &ev ) );
     371           0 : }
     372             : 
     373             : void
     374           0 : fd_event_runtime_epoch_emit( fd_bank_t const * bank ) {
     375           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     376             : 
     377           0 :   fd_event_runtime_epoch_t ev = {0};
     378             : 
     379           0 :   ev.bank_seq    = bank->bank_seq;
     380           0 :   ev.slot        = bank->f.slot;
     381           0 :   ev.parent_slot = bank->f.parent_slot;
     382           0 :   ev.epoch       = bank->f.epoch;
     383             : 
     384           0 :   ev.total_effective_stake    = bank->f.total_effective_stake;
     385           0 :   ev.total_activating_stake   = bank->f.total_activating_stake;
     386           0 :   ev.total_deactivating_stake = bank->f.total_deactivating_stake;
     387           0 :   ev.total_epoch_stake        = bank->f.total_epoch_stake;
     388             : 
     389           0 :   ev.num_staked_vote_accounts = fd_event_runtime_epoch_summary.staked_vote_accounts;
     390           0 :   ev.num_vote_accounts        = fd_event_runtime_epoch_summary.vote_cnt;
     391           0 :   ev.num_top_votes_eligible   = fd_event_runtime_epoch_summary.top_votes_eligible;
     392           0 :   ev.top_votes_min_stake      = fd_event_runtime_epoch_summary.min_stake;
     393             : 
     394           0 :   ev.stake_history_sysvar = fd_event_runtime_epoch_summary.stake_history;
     395             : 
     396           0 :   ev.epoch_schedule_sysvar.slots_per_epoch             = bank->f.epoch_schedule.slots_per_epoch;
     397           0 :   ev.epoch_schedule_sysvar.leader_schedule_slot_offset = bank->f.epoch_schedule.leader_schedule_slot_offset;
     398           0 :   ev.epoch_schedule_sysvar.warmup                      = !!bank->f.epoch_schedule.warmup;
     399           0 :   ev.epoch_schedule_sysvar.first_normal_epoch          = bank->f.epoch_schedule.first_normal_epoch;
     400           0 :   ev.epoch_schedule_sysvar.first_normal_slot           = bank->f.epoch_schedule.first_normal_slot;
     401             : 
     402           0 :   for( ulong i=0UL; i<fd_event_runtime_epoch_summary.feature_cnt; i++ ) {
     403           0 :     fd_memcpy( ev.feature_activations[ i ], fd_event_runtime_epoch_summary.features[ i ], 32UL );
     404           0 :   }
     405           0 :   ev.feature_activations_cnt = fd_event_runtime_epoch_summary.feature_cnt;
     406             : 
     407           0 :   ev.epoch_rewards_sysvar = fd_event_runtime_epoch_summary.epoch_rewards;
     408             : 
     409           0 :   ev.vat_burn_per_vote_account = fd_event_runtime_epoch_summary.vat_burn_per_vote_account;
     410             : 
     411           0 :   REPORT_DIFF( fd_event_report_runtime_epoch( &ev ) );
     412             : 
     413           0 :   fd_memset( &fd_event_runtime_epoch_summary, 0, sizeof(fd_event_runtime_epoch_summary) );
     414           0 : }
     415             : 
     416             : void
     417           0 : fd_event_runtime_epoch_reset( void ) {
     418           0 :   fd_memset( &fd_event_runtime_epoch_summary, 0, sizeof(fd_event_runtime_epoch_summary) );
     419           0 : }
     420             : 
     421             : void
     422             : fd_event_runtime_rooted_emit( fd_bank_t const *                          bank,
     423             :                               ulong                                      prev_root_slot,
     424             :                               fd_stake_delegations_t const *             stake_delegations,
     425           0 :                               fd_stake_delegations_delta_stats_t const * stake_delegations_delta_stats ) {
     426           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     427             : 
     428           0 :   fd_event_runtime_rooted_t ev = {0};
     429             : 
     430           0 :   ev.bank_seq       = bank->bank_seq;
     431           0 :   ev.slot           = bank->f.slot;
     432           0 :   ev.epoch          = bank->f.epoch;
     433           0 :   ev.prev_root_slot = prev_root_slot;
     434             : 
     435           0 :   fd_stake_history_entry_t totals = fd_stake_delegations_totals( stake_delegations );
     436           0 :   ev.stake_delegations_upserts = stake_delegations_delta_stats->upserts;
     437           0 :   ev.stake_delegations_removes = stake_delegations_delta_stats->removes;
     438           0 :   ev.stake_delegations_cnt     = stake_delegations_delta_stats->root_cnt;
     439           0 :   ev.effective_stake           = totals.effective;
     440           0 :   ev.activating_stake          = totals.activating;
     441           0 :   ev.deactivating_stake        = totals.deactivating;
     442             : 
     443           0 :   fd_memcpy( ev.block_id, bank->f.block_id.uc, 32UL );
     444             : 
     445           0 :   REPORT_DIFF( fd_event_report_runtime_rooted( &ev ) );
     446           0 : }
     447             : 
     448             : void
     449             : fd_event_runtime_reward_emit( fd_bank_t const * bank,
     450             :                               int               kind,
     451             :                               uchar const *     pubkey,
     452             :                               uchar const *     owner,
     453             :                               ulong             prev_lamports,
     454             :                               ulong             lamports,
     455             :                               ulong             partition_idx,
     456             :                               ulong             credits_observed,
     457           0 :                               ulong             stake ) {
     458           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     459             : 
     460           0 :   fd_event_runtime_reward_t ev = {
     461           0 :     .bank_seq         = bank->bank_seq,
     462           0 :     .slot             = bank->f.slot,
     463           0 :     .epoch            = bank->f.epoch,
     464           0 :     .kind             = kind,
     465           0 :     .prev_lamports    = prev_lamports,
     466           0 :     .lamports         = lamports,
     467           0 :     .partition_idx    = partition_idx,
     468           0 :     .credits_observed = credits_observed,
     469           0 :     .stake            = stake,
     470           0 :   };
     471           0 :   fd_memcpy( ev.pubkey, pubkey, 32UL );
     472           0 :   fd_memcpy( ev.owner,  owner,  32UL );
     473           0 :   REPORT_DIFF( fd_event_report_runtime_reward( &ev ) );
     474           0 : }
     475             : 
     476             : /* Accumulator for slot-level account diffs, which get staged in the bank
     477             :    and emitted to the runtime_block event at block finalize. */
     478             : 
     479             : struct fd_event_runtime_account_diff {
     480             :   uchar pubkey    [ 32 ];
     481             :   uchar owner     [ 32 ];
     482             :   uchar prev_owner[ 32 ];
     483             :   ulong prev_lamports;
     484             :   ulong lamports;
     485             :   ulong prev_data_sz;
     486             :   ulong data_sz;
     487             :   int   executable;
     488             : };
     489             : 
     490             : typedef struct fd_event_runtime_account_diff fd_event_runtime_account_diff_t;
     491             : 
     492             : struct fd_event_runtime_slot_diffs {
     493             :   fd_event_runtime_account_diff_t sysvar[ 16UL ]; ulong sysvar_cnt;
     494             :   fd_event_runtime_account_diff_t other [ 32UL ]; ulong other_cnt;
     495             : };
     496             : 
     497             : typedef struct fd_event_runtime_slot_diffs fd_event_runtime_slot_diffs_t;
     498             : 
     499             : FD_STATIC_ASSERT( sizeof (fd_event_runtime_slot_diffs_t)<=FD_EVENT_RUNTIME_SLOT_DIFFS_FOOTPRINT, event_slot_diffs_fit );
     500             : FD_STATIC_ASSERT( alignof(fd_event_runtime_slot_diffs_t)<=8UL,                                   event_slot_diffs_align );
     501             : 
     502             : static FD_TL uchar * fd_event_runtime_slot_diffs_mem;
     503             : static FD_TL ulong   fd_event_runtime_slot_diffs_max;
     504             : 
     505             : void
     506             : fd_event_runtime_slot_diffs_init( void * mem,
     507           0 :                                   ulong  bank_max ) {
     508           0 :   fd_memset( mem, 0, bank_max*FD_EVENT_RUNTIME_SLOT_DIFFS_FOOTPRINT );
     509           0 :   fd_event_runtime_slot_diffs_mem = mem;
     510           0 :   fd_event_runtime_slot_diffs_max = bank_max;
     511           0 : }
     512             : 
     513             : static inline fd_event_runtime_slot_diffs_t *
     514       12732 : fd_event_runtime_slot_diffs_at( ulong bank_idx ) {
     515       12732 :   if( FD_UNLIKELY( !fd_event_runtime_slot_diffs_mem || bank_idx>=fd_event_runtime_slot_diffs_max ) ) return NULL;
     516           0 :   return fd_type_pun( fd_event_runtime_slot_diffs_mem + bank_idx*FD_EVENT_RUNTIME_SLOT_DIFFS_FOOTPRINT );
     517       12732 : }
     518             : 
     519             : void
     520       12732 : fd_event_runtime_slot_diffs_reset( ulong bank_idx ) {
     521       12732 :   fd_event_runtime_slot_diffs_t * diffs = fd_event_runtime_slot_diffs_at( bank_idx );
     522       12732 :   if( FD_UNLIKELY( !diffs ) ) return;
     523           0 :   diffs->sysvar_cnt = 0UL;
     524           0 :   diffs->other_cnt  = 0UL;
     525           0 : }
     526             : 
     527             : static void
     528             : fd_event_runtime_account_diff_set( fd_event_runtime_account_diff_t * e,
     529             :                                    uchar const *                     pubkey,
     530             :                                    uchar const *                     prev_owner,
     531             :                                    uchar const *                     owner,
     532             :                                    ulong                             prev_lamports,
     533             :                                    ulong                             lamports,
     534             :                                    ulong                             prev_data_sz,
     535             :                                    ulong                             data_sz,
     536           0 :                                    int                               executable ) {
     537           0 :   fd_memcpy( e->pubkey,     pubkey,     32UL );
     538           0 :   fd_memcpy( e->owner,      owner,      32UL );
     539           0 :   fd_memcpy( e->prev_owner, prev_owner, 32UL );
     540           0 :   e->prev_lamports = prev_lamports;
     541           0 :   e->lamports      = lamports;
     542           0 :   e->prev_data_sz  = prev_data_sz;
     543           0 :   e->data_sz       = data_sz;
     544           0 :   e->executable    = executable;
     545           0 : }
     546             : 
     547             : void
     548             : fd_event_runtime_block_account( fd_bank_t *   bank,
     549             :                                 uchar const * pubkey,
     550             :                                 uchar const * prev_owner,
     551             :                                 uchar const * owner,
     552             :                                 ulong         prev_lamports,
     553             :                                 ulong         lamports,
     554             :                                 ulong         prev_data_sz,
     555             :                                 ulong         data_sz,
     556           0 :                                 int           executable ) {
     557           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     558             : 
     559           0 :   fd_event_runtime_slot_diffs_t * diffs = fd_event_runtime_slot_diffs_at( bank->idx );
     560           0 :   if( FD_UNLIKELY( !diffs ) ) return;
     561             : 
     562           0 :   fd_event_runtime_account_diff_t * arr; ulong * cnt; ulong cap;
     563           0 :   if( FD_UNLIKELY( !memcmp( owner, fd_sysvar_owner_id.uc, 32UL ) ) ) {
     564           0 :     arr = diffs->sysvar;
     565           0 :     cnt = &diffs->sysvar_cnt;
     566           0 :     cap = 16UL;
     567           0 :   } else {
     568           0 :     arr = diffs->other;
     569           0 :     cnt = &diffs->other_cnt;
     570           0 :     cap = 32UL;
     571           0 :   }
     572             : 
     573             :   /* Coalesce repeated writes to one account into a single diff: first
     574             :      pre-state, last post-state. */
     575           0 :   for( ulong i=0UL; i<*cnt; i++ ) {
     576           0 :     if( FD_UNLIKELY( !memcmp( arr[ i ].pubkey, pubkey, 32UL ) ) ) {
     577           0 :       fd_memcpy( arr[ i ].owner, owner, 32UL );
     578           0 :       arr[ i ].lamports   = lamports;
     579           0 :       arr[ i ].data_sz    = data_sz;
     580           0 :       arr[ i ].executable = executable;
     581           0 :       return;
     582           0 :     }
     583           0 :   }
     584             : 
     585           0 :   if( FD_UNLIKELY( *cnt>=cap ) ) {
     586           0 :     FD_LOG_WARNING(( "runtime_block account diff overflow (cap %lu), dropping diff", cap ));
     587           0 :     return;
     588           0 :   }
     589           0 :   fd_event_runtime_account_diff_set( &arr[ (*cnt)++ ], pubkey, prev_owner, owner, prev_lamports, lamports, prev_data_sz, data_sz, executable );
     590           0 : }
     591             : 
     592             : void
     593             : fd_event_runtime_block_emit( fd_bank_t const *             bank,
     594             :                              uchar const *                 block_id,
     595             :                              uchar const *                 parent_block_id,
     596             :                              uchar const *                 leader,
     597             :                              ulong                         execution_fees,
     598             :                              ulong                         priority_fees,
     599             :                              ulong                         tips,
     600             :                              ulong                         num_shreds,
     601             :                              fd_sol_sysvar_clock_t const * clock,
     602             :                              fd_hash_t const *             fec_mrs,
     603           0 :                              ulong                         fec_mr_cnt ) {
     604           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     605             : 
     606           0 :   static FD_TL fd_event_runtime_block_t ev;
     607           0 :   fd_memset( &ev, 0, sizeof(ev) );
     608             : 
     609           0 :   ev.bank_seq     = bank->bank_seq;
     610           0 :   ev.slot         = bank->f.slot;
     611           0 :   ev.parent_slot  = bank->f.parent_slot;
     612           0 :   ev.epoch        = bank->f.epoch;
     613           0 :   ev.block_height = bank->f.block_height;
     614             : 
     615           0 :   fd_memcpy( ev.block_id,        block_id,        32UL );
     616           0 :   fd_memcpy( ev.parent_block_id, parent_block_id, 32UL );
     617           0 :   fd_memcpy( ev.leader,          leader,          32UL );
     618             : 
     619           0 :   fd_memcpy( ev.bank_hash,      bank->f.bank_hash.uc,      32UL );
     620           0 :   fd_memcpy( ev.prev_bank_hash, bank->f.prev_bank_hash.uc, 32UL );
     621           0 :   fd_memcpy( ev.poh_hash,       bank->f.poh.uc,            32UL );
     622             : 
     623           0 :   fd_lthash_value_t const * lthash = fd_bank_lthash_locking_query( (fd_bank_t *)bank );
     624           0 :   fd_blake3_hash( lthash->bytes, FD_LTHASH_LEN_BYTES, ev.accounts_lt_hash_checksum );
     625           0 :   fd_bank_lthash_end_locking_query( (fd_bank_t *)bank );
     626             : 
     627           0 :   ev.num_transactions        = bank->f.txn_count;
     628           0 :   ev.num_failed_txns         = bank->f.failed_txn_count;
     629           0 :   ev.num_nonvote_txns        = bank->f.nonvote_txn_count;
     630           0 :   ev.num_nonvote_failed_txns = bank->f.nonvote_failed_txn_count;
     631           0 :   ev.num_signatures          = bank->f.signature_count;
     632           0 :   ev.num_shreds              = num_shreds;
     633           0 :   ev.tick_height             = bank->f.max_tick_height;
     634             : 
     635           0 :   ulong fees_burned, leader_fee_reward;
     636           0 :   fd_runtime_fee_split( execution_fees, priority_fees, &fees_burned, &leader_fee_reward );
     637             : 
     638           0 :   ev.execution_fees    = execution_fees;
     639           0 :   ev.priority_fees     = priority_fees;
     640           0 :   ev.tips              = tips;
     641           0 :   ev.fees_burned       = fees_burned;
     642           0 :   ev.leader_fee_reward = leader_fee_reward;
     643             : 
     644           0 :   ev.capitalization           = bank->f.capitalization;
     645           0 :   ev.total_effective_stake    = bank->f.total_effective_stake;
     646           0 :   ev.total_activating_stake   = bank->f.total_activating_stake;
     647           0 :   ev.total_deactivating_stake = bank->f.total_deactivating_stake;
     648           0 :   ev.total_epoch_stake        = bank->f.total_epoch_stake;
     649             : 
     650           0 :   ev.clock_sysvar.slot                  = clock->slot;
     651           0 :   ev.clock_sysvar.epoch_start_timestamp = clock->epoch_start_timestamp;
     652           0 :   ev.clock_sysvar.epoch                 = clock->epoch;
     653           0 :   ev.clock_sysvar.leader_schedule_epoch = clock->leader_schedule_epoch;
     654           0 :   ev.clock_sysvar.unix_timestamp        = clock->unix_timestamp;
     655             : 
     656           0 :   fd_event_runtime_slot_diffs_t const * diffs = fd_event_runtime_slot_diffs_at( bank->idx );
     657           0 :   if( FD_UNLIKELY( !diffs ) ) return;
     658             : 
     659             :   /* The two lists cut off differently once hash_index values skip (see
     660             :      fd_blockhashes.h).  recent_blockhashes is the set a child block may
     661             :      reference, so it stops at age 150, measured in hash_index like
     662             :      fd_blockhashes_check_age.  The sysvar is the newest 150 entries
     663             :      (agave takes the 150 highest hash_index entries), which may reach
     664             :      past age 150 when indices skip. */
     665           0 :   ulong bh_cnt     = 0UL;
     666           0 :   ulong sysvar_cnt = 0UL;
     667           0 :   fd_blockhashes_t const *    queue = &bank->f.block_hash_queue;
     668           0 :   fd_blockhash_info_t const * bhq   = queue->d.deque;
     669           0 :   for( fd_blockhash_deq_iter_t iter = fd_blockhash_deq_iter_init_rev( bhq );
     670           0 :        !fd_blockhash_deq_iter_done_rev( bhq, iter );
     671           0 :        iter = fd_blockhash_deq_iter_prev( bhq, iter ) ) {
     672           0 :     fd_blockhash_info_t const * info = fd_blockhash_deq_iter_ele_const( bhq, iter );
     673           0 :     int in_window = fd_blockhashes_age( queue, info )<=FD_SYSVAR_RECENT_HASHES_CAP;
     674           0 :     if( FD_UNLIKELY( !in_window && sysvar_cnt>=FD_SYSVAR_RECENT_HASHES_CAP ) ) break;
     675           0 :     if( FD_LIKELY( sysvar_cnt<FD_SYSVAR_RECENT_HASHES_CAP ) ) {
     676           0 :       fd_memcpy( ev.recent_blockhashes_sysvar[ sysvar_cnt ].blockhash, info->hash.uc, 32UL );
     677           0 :       ev.recent_blockhashes_sysvar[ sysvar_cnt ].lamports_per_signature = info->lamports_per_signature;
     678           0 :       sysvar_cnt++;
     679           0 :     }
     680           0 :     if( FD_LIKELY( in_window ) ) fd_memcpy( ev.recent_blockhashes[ bh_cnt++ ], info->hash.uc, 32UL );
     681           0 :   }
     682           0 :   ev.recent_blockhashes_cnt        = bh_cnt;
     683           0 :   ev.recent_blockhashes_sysvar_cnt = sysvar_cnt;
     684             : 
     685           0 :   ev.last_restart_slot_sysvar = fd_sysvar_last_restart_slot_derive( bank );
     686             : 
     687           0 :   for( ulong i=0UL; i<diffs->sysvar_cnt; i++ ) {
     688           0 :     fd_event_runtime_block_sysvar_diffs_t * d = &ev.sysvar_diffs[ i ];
     689           0 :     fd_memcpy( d->pubkey,     diffs->sysvar[ i ].pubkey,     32UL );
     690           0 :     fd_memcpy( d->owner,      diffs->sysvar[ i ].owner,      32UL );
     691           0 :     fd_memcpy( d->prev_owner, diffs->sysvar[ i ].prev_owner, 32UL );
     692           0 :     d->lamports      = diffs->sysvar[ i ].lamports;
     693           0 :     d->prev_lamports = diffs->sysvar[ i ].prev_lamports;
     694           0 :     d->data_sz       = diffs->sysvar[ i ].data_sz;
     695           0 :     d->prev_data_sz  = diffs->sysvar[ i ].prev_data_sz;
     696           0 :     d->is_executable = diffs->sysvar[ i ].executable;
     697           0 :   }
     698           0 :   ev.sysvar_diffs_cnt = diffs->sysvar_cnt;
     699             : 
     700           0 :   for( ulong i=0UL; i<diffs->other_cnt; i++ ) {
     701           0 :     fd_event_runtime_block_other_diffs_t * d = &ev.other_diffs[ i ];
     702           0 :     fd_memcpy( d->pubkey,     diffs->other[ i ].pubkey,     32UL );
     703           0 :     fd_memcpy( d->owner,      diffs->other[ i ].owner,      32UL );
     704           0 :     fd_memcpy( d->prev_owner, diffs->other[ i ].prev_owner, 32UL );
     705           0 :     d->lamports      = diffs->other[ i ].lamports;
     706           0 :     d->prev_lamports = diffs->other[ i ].prev_lamports;
     707           0 :     d->data_sz       = diffs->other[ i ].data_sz;
     708           0 :     d->prev_data_sz  = diffs->other[ i ].prev_data_sz;
     709           0 :     d->is_executable = diffs->other[ i ].executable;
     710           0 :   }
     711           0 :   ev.other_diffs_cnt = diffs->other_cnt;
     712             : 
     713           0 :   ulong fec_cnt = fd_ulong_min( fec_mr_cnt, sizeof(ev.fec_merkle_roots)/sizeof(ev.fec_merkle_roots[0]) );
     714           0 :   for( ulong i=0UL; i<fec_cnt; i++ ) fd_memcpy( ev.fec_merkle_roots[ i ], fec_mrs[ i ].uc, 32UL );
     715           0 :   ev.fec_count            = fec_mr_cnt;
     716           0 :   ev.fec_merkle_roots_cnt = fec_cnt;
     717             : 
     718           0 :   fd_event_report_runtime_block( &ev );
     719           0 : }

Generated by: LCOV version 1.14