1use alloy_primitives::{Address, B256, BlockNumber, Bytes, StorageKey, StorageValue};
2use metrics::Gauge;
3use reth_errors::ProviderResult;
4use reth_metrics::{Metrics, metrics::Histogram};
5use reth_primitives_traits::{Account, Bytecode};
6use reth_storage_api::{
7 AccountReader, BlockHashReader, BytecodeReader, HashedPostStateProvider, StateProofProvider,
8 StateProvider, StateRootProvider, StorageRootProvider,
9};
10use reth_trie_common::{
11 AccountProof, ExecutionWitnessMode, HashedPostState, HashedStorage, MultiProof,
12 MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, updates::TrieUpdates,
13};
14use std::time::Instant;
15use tracing::debug_span;
16
17#[derive(Metrics, Clone)]
18#[metrics(scope = "tempo_payload_builder")]
19pub(crate) struct TempoPayloadBuilderMetrics {
20 pub(crate) block_time_millis: Histogram,
22 pub(crate) block_time_millis_last: Gauge,
24 pub(crate) total_transactions: Histogram,
26 pub(crate) total_transactions_last: Gauge,
28 pub(crate) payment_transactions: Histogram,
30 pub(crate) payment_transactions_last: Gauge,
32 pub(crate) pool_transactions_yielded: Histogram,
34 pub(crate) pool_transactions_yielded_last: Gauge,
36 pub(crate) pool_transactions_included: Histogram,
38 pub(crate) pool_transactions_included_last: Gauge,
40 pub(crate) invalid_pool_transaction_execution_attempts: Histogram,
42 pub(crate) pool_transactions_inclusion_ratio: Histogram,
44 pub(crate) pool_transactions_inclusion_ratio_last: Gauge,
46 pub(crate) subblocks: Histogram,
48 pub(crate) subblocks_last: Gauge,
50 pub(crate) subblock_transactions: Histogram,
52 pub(crate) subblock_transactions_last: Gauge,
54 pub(crate) gas_used: Histogram,
56 pub(crate) gas_used_last: Gauge,
58 pub(crate) state_gas_used: Histogram,
60 pub(crate) state_gas_used_last: Gauge,
62 pub(crate) general_gas_used_last: Gauge,
64 pub(crate) payment_gas_used_last: Gauge,
66 pub(crate) general_gas_limit_last: Gauge,
68 pub(crate) payment_gas_limit_last: Gauge,
70 pub(crate) shared_gas_limit_last: Gauge,
72 pub(crate) pool_fetch_duration_seconds: Histogram,
74 pub(crate) state_setup_duration_seconds: Histogram,
76 pub(crate) prepare_system_transactions_duration_seconds: Histogram,
78 pub(crate) total_normal_transaction_fill_duration_seconds: Histogram,
80 pub(crate) normal_transaction_fill_idle_duration_seconds: Histogram,
82 pub(crate) total_transaction_execution_duration_seconds: Histogram,
84 pub(crate) total_subblock_transaction_execution_duration_seconds: Histogram,
86 pub(crate) subblock_execution_duration_seconds: Histogram,
88 pub(crate) subblock_transaction_count: Histogram,
90 pub(crate) system_transactions_execution_duration_seconds: Histogram,
92 pub(crate) payload_finalization_duration_seconds: Histogram,
94 pub(crate) sparse_trie_state_root_wait_duration_seconds: Histogram,
96 pub(crate) builder_finish_duration_seconds: Histogram,
98 pub(crate) payload_build_duration_seconds: Histogram,
100 pub(crate) gas_per_second: Histogram,
102 pub(crate) gas_per_second_last: Gauge,
104 pub(crate) rlp_block_size_bytes: Histogram,
106 pub(crate) rlp_block_size_bytes_last: Gauge,
108 pub(crate) hashed_post_state_duration_seconds: Histogram,
110 pub(crate) state_root_with_updates_duration_seconds: Histogram,
112}
113
114pub(crate) enum BlockBuildStopReason {
116 GasLimit,
117 RlpBlockSizeLimit,
118 TxPoolEmpty,
119 BuildBudget,
120}
121
122impl BlockBuildStopReason {
123 const fn as_str(&self) -> &'static str {
124 match self {
125 Self::GasLimit => "gas_limit",
126 Self::RlpBlockSizeLimit => "rlp_block_size_limit",
127 Self::TxPoolEmpty => "tx_pool_empty",
128 Self::BuildBudget => "build_budget",
129 }
130 }
131}
132
133impl TempoPayloadBuilderMetrics {
134 #[inline]
139 pub(crate) fn inc_pool_tx_skipped(&self, reason: &'static str) {
140 metrics::counter!("tempo_payload_builder_pool_transactions_skipped_total", "reason" => reason)
141 .increment(1);
142 }
143
144 #[inline]
146 pub(crate) fn inc_build_failure(&self, reason: &'static str) {
147 metrics::counter!("tempo_payload_builder_build_failures_total", "reason" => reason)
148 .increment(1);
149 }
150
151 #[inline]
153 pub(crate) fn inc_block_build_stop_reason(&self, reason: BlockBuildStopReason) {
154 metrics::counter!("tempo_payload_builder_block_build_stop_total", "reason" => reason.as_str())
155 .increment(1);
156 }
157
158 #[inline]
160 pub(crate) fn inc_subblocks_expired(&self) {
161 metrics::counter!("tempo_payload_builder_subblocks_expired_total").increment(1);
162 }
163}
164
165pub(crate) struct InstrumentedFinishProvider<'a> {
168 pub(crate) inner: &'a dyn StateProvider,
169 pub(crate) metrics: TempoPayloadBuilderMetrics,
170}
171
172impl<'a> AsRef<dyn StateProvider + 'a> for InstrumentedFinishProvider<'a> {
173 fn as_ref(&self) -> &(dyn StateProvider + 'a) {
174 self.inner
175 }
176}
177
178reth_storage_api::delegate_impls_to_as_ref!(
179 for InstrumentedFinishProvider<'_> =>
180 AccountReader {
181 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>>;
182 }
183 BlockHashReader {
184 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>>;
185 fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> ProviderResult<Vec<B256>>;
186 }
187 StateProvider {
188 fn storage(&self, account: Address, storage_key: StorageKey) -> ProviderResult<Option<StorageValue>>;
189 }
190 BytecodeReader {
191 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>>;
192 }
193 StorageRootProvider {
194 fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256>;
195 fn storage_proof(&self, address: Address, slot: B256, storage: HashedStorage) -> ProviderResult<StorageProof>;
196 fn storage_multiproof(&self, address: Address, slots: &[B256], storage: HashedStorage) -> ProviderResult<StorageMultiProof>;
197 }
198 StateProofProvider {
199 fn proof(&self, input: TrieInput, address: Address, slots: &[B256]) -> ProviderResult<AccountProof>;
200 fn multiproof(&self, input: TrieInput, targets: MultiProofTargets) -> ProviderResult<MultiProof>;
201 fn witness(&self, input: TrieInput, target: HashedPostState, mode: ExecutionWitnessMode) -> ProviderResult<Vec<Bytes>>;
202 }
203);
204
205impl HashedPostStateProvider for InstrumentedFinishProvider<'_> {
206 fn hashed_post_state(&self, bundle_state: &reth_revm::db::BundleState) -> HashedPostState {
207 let start = Instant::now();
208 let _span = debug_span!(target: "payload_builder", "hashed_post_state").entered();
209 let result = self.inner.hashed_post_state(bundle_state);
210 drop(_span);
211 self.metrics
212 .hashed_post_state_duration_seconds
213 .record(start.elapsed());
214 result
215 }
216}
217
218impl StateRootProvider for InstrumentedFinishProvider<'_> {
219 fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
220 let start = Instant::now();
221 let _span = debug_span!(target: "payload_builder", "state_root").entered();
222 let result = self.inner.state_root(hashed_state);
223 drop(_span);
224 self.metrics
225 .state_root_with_updates_duration_seconds
226 .record(start.elapsed());
227 result
228 }
229
230 fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> {
231 self.inner.state_root_from_nodes(input)
232 }
233
234 fn state_root_with_updates(
235 &self,
236 hashed_state: HashedPostState,
237 ) -> ProviderResult<(B256, TrieUpdates)> {
238 let start = Instant::now();
239 let _span = debug_span!(target: "payload_builder", "state_root_with_updates").entered();
240 let result = self.inner.state_root_with_updates(hashed_state);
241 drop(_span);
242 self.metrics
243 .state_root_with_updates_duration_seconds
244 .record(start.elapsed());
245 result
246 }
247
248 fn state_root_from_nodes_with_updates(
249 &self,
250 input: TrieInput,
251 ) -> ProviderResult<(B256, TrieUpdates)> {
252 self.inner.state_root_from_nodes_with_updates(input)
253 }
254}