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

Generated by: LCOV version 1.14