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 : }
|