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