1use crate::tree::{
4 cached_state::CachedStateProvider,
5 error::{InsertBlockError, InsertBlockErrorKind, InsertPayloadError},
6 executor::WorkloadExecutor,
7 instrumented_state::InstrumentedStateProvider,
8 payload_processor::PayloadProcessor,
9 persistence_state::CurrentPersistenceAction,
10 precompile_cache::{CachedPrecompile, CachedPrecompileMetrics, PrecompileCacheMap},
11 sparse_trie::StateRootComputeOutcome,
12 ConsistentDbView, EngineApiMetrics, EngineApiTreeState, ExecutionEnv, PayloadHandle,
13 PersistenceState, PersistingKind, StateProviderBuilder, StateProviderDatabase, TreeConfig,
14};
15use alloy_consensus::transaction::Either;
16use alloy_eips::{eip1898::BlockWithParent, NumHash};
17use alloy_evm::Evm;
18use alloy_primitives::B256;
19use reth_chain_state::{
20 CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates,
21};
22use reth_consensus::{ConsensusError, FullConsensus};
23use reth_engine_primitives::{
24 ConfigureEngineEvm, ExecutableTxIterator, ExecutionPayload, InvalidBlockHook, PayloadValidator,
25};
26use reth_errors::{BlockExecutionError, ProviderResult};
27use reth_evm::{
28 block::BlockExecutor, execute::ExecutableTxFor, ConfigureEvm, EvmEnvFor, ExecutionCtxFor,
29 SpecFor,
30};
31use reth_payload_primitives::{
32 BuiltPayload, InvalidPayloadAttributesError, NewPayloadError, PayloadTypes,
33};
34use reth_primitives_traits::{
35 AlloyBlockHeader, BlockTy, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader,
36};
37use reth_provider::{
38 BlockExecutionOutput, BlockNumReader, BlockReader, DBProvider, DatabaseProviderFactory,
39 ExecutionOutcome, HashedPostStateProvider, ProviderError, StateProvider, StateProviderFactory,
40 StateReader, StateRootProvider,
41};
42use reth_revm::db::State;
43use reth_trie::{updates::TrieUpdates, HashedPostState, KeccakKeyHasher, TrieInput};
44use reth_trie_db::DatabaseHashedPostState;
45use reth_trie_parallel::root::{ParallelStateRoot, ParallelStateRootError};
46use std::{collections::HashMap, sync::Arc, time::Instant};
47use tracing::{debug, error, info, trace, warn};
48
49pub struct TreeCtx<'a, N: NodePrimitives> {
54 state: &'a mut EngineApiTreeState<N>,
56 persistence: &'a PersistenceState,
58 canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
60 is_fork: bool,
62}
63
64impl<'a, N: NodePrimitives> std::fmt::Debug for TreeCtx<'a, N> {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct("TreeCtx")
67 .field("state", &"EngineApiTreeState")
68 .field("persistence_info", &self.persistence)
69 .field("canonical_in_memory_state", &self.canonical_in_memory_state)
70 .finish()
71 }
72}
73
74impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
75 pub const fn new(
77 state: &'a mut EngineApiTreeState<N>,
78 persistence: &'a PersistenceState,
79 canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
80 is_fork: bool,
81 ) -> Self {
82 Self { state, persistence, canonical_in_memory_state, is_fork }
83 }
84
85 pub const fn state(&self) -> &EngineApiTreeState<N> {
87 &*self.state
88 }
89
90 pub const fn state_mut(&mut self) -> &mut EngineApiTreeState<N> {
92 self.state
93 }
94
95 pub const fn persistence(&self) -> &PersistenceState {
97 self.persistence
98 }
99
100 pub const fn canonical_in_memory_state(&self) -> &'a CanonicalInMemoryState<N> {
102 self.canonical_in_memory_state
103 }
104
105 pub const fn is_fork(&self) -> bool {
107 self.is_fork
108 }
109
110 pub fn persisting_kind_for(&self, block: BlockWithParent) -> PersistingKind {
117 let Some(action) = self.persistence().current_action() else {
119 return PersistingKind::NotPersisting
120 };
121 let CurrentPersistenceAction::SavingBlocks { highest } = action else {
123 return PersistingKind::PersistingNotDescendant
124 };
125
126 if block.block.number > highest.number &&
129 self.state().tree_state.is_descendant(*highest, block)
130 {
131 return PersistingKind::PersistingDescendant
132 }
133
134 PersistingKind::PersistingNotDescendant
136 }
137}
138
139#[derive(derive_more::Debug)]
147pub struct BasicEngineValidator<P, Evm, V>
148where
149 Evm: ConfigureEvm,
150{
151 provider: P,
153 consensus: Arc<dyn FullConsensus<Evm::Primitives, Error = ConsensusError>>,
155 evm_config: Evm,
157 config: TreeConfig,
159 payload_processor: PayloadProcessor<Evm>,
161 precompile_cache_map: PrecompileCacheMap<SpecFor<Evm>>,
163 precompile_cache_metrics: HashMap<alloy_primitives::Address, CachedPrecompileMetrics>,
165 #[debug(skip)]
167 invalid_block_hook: Box<dyn InvalidBlockHook<Evm::Primitives>>,
168 metrics: EngineApiMetrics,
170 validator: V,
172}
173
174impl<N, P, Evm, V> BasicEngineValidator<P, Evm, V>
175where
176 N: NodePrimitives,
177 P: DatabaseProviderFactory<Provider: BlockReader>
178 + BlockReader<Header = N::BlockHeader>
179 + StateProviderFactory
180 + StateReader
181 + HashedPostStateProvider
182 + Clone
183 + 'static,
184 Evm: ConfigureEvm<Primitives = N> + 'static,
185{
186 #[allow(clippy::too_many_arguments)]
188 pub fn new(
189 provider: P,
190 consensus: Arc<dyn FullConsensus<N, Error = ConsensusError>>,
191 evm_config: Evm,
192 validator: V,
193 config: TreeConfig,
194 invalid_block_hook: Box<dyn InvalidBlockHook<N>>,
195 ) -> Self {
196 let precompile_cache_map = PrecompileCacheMap::default();
197 let payload_processor = PayloadProcessor::new(
198 WorkloadExecutor::default(),
199 evm_config.clone(),
200 &config,
201 precompile_cache_map.clone(),
202 );
203 Self {
204 provider,
205 consensus,
206 evm_config,
207 payload_processor,
208 precompile_cache_map,
209 precompile_cache_metrics: HashMap::new(),
210 config,
211 invalid_block_hook,
212 metrics: EngineApiMetrics::default(),
213 validator,
214 }
215 }
216
217 pub fn convert_to_block<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
219 &self,
220 input: BlockOrPayload<T>,
221 ) -> Result<RecoveredBlock<N::Block>, NewPayloadError>
222 where
223 V: PayloadValidator<T, Block = N::Block>,
224 {
225 match input {
226 BlockOrPayload::Payload(payload) => self.validator.ensure_well_formed_payload(payload),
227 BlockOrPayload::Block(block) => Ok(block),
228 }
229 }
230
231 pub fn evm_env_for<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
233 &self,
234 input: &BlockOrPayload<T>,
235 ) -> EvmEnvFor<Evm>
236 where
237 V: PayloadValidator<T, Block = N::Block>,
238 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
239 {
240 match input {
241 BlockOrPayload::Payload(payload) => self.evm_config.evm_env_for_payload(payload),
242 BlockOrPayload::Block(block) => self.evm_config.evm_env(block.header()),
243 }
244 }
245
246 pub fn tx_iterator_for<'a, T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
248 &'a self,
249 input: &'a BlockOrPayload<T>,
250 ) -> Result<impl ExecutableTxIterator<Evm> + 'a, NewPayloadError>
251 where
252 V: PayloadValidator<T, Block = N::Block>,
253 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
254 {
255 match input {
256 BlockOrPayload::Payload(payload) => Ok(Either::Left(
257 self.evm_config.tx_iterator_for_payload(payload).map(|res| res.map(Either::Left)),
258 )),
259 BlockOrPayload::Block(block) => {
260 let transactions = block.clone_transactions_recovered().collect::<Vec<_>>();
261 Ok(Either::Right(transactions.into_iter().map(|tx| Ok(Either::Right(tx)))))
262 }
263 }
264 }
265
266 pub fn execution_ctx_for<'a, T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
268 &self,
269 input: &'a BlockOrPayload<T>,
270 ) -> ExecutionCtxFor<'a, Evm>
271 where
272 V: PayloadValidator<T, Block = N::Block>,
273 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
274 {
275 match input {
276 BlockOrPayload::Payload(payload) => self.evm_config.context_for_payload(payload),
277 BlockOrPayload::Block(block) => self.evm_config.context_for_block(block),
278 }
279 }
280
281 pub fn validate_block_with_state<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
289 &mut self,
290 input: BlockOrPayload<T>,
291 mut ctx: TreeCtx<'_, N>,
292 ) -> ValidationOutcome<N, InsertPayloadError<N::Block>>
293 where
294 V: PayloadValidator<T, Block = N::Block>,
295 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
296 {
297 macro_rules! ensure_ok {
299 ($expr:expr) => {
300 match $expr {
301 Ok(val) => val,
302 Err(e) => {
303 let block = self.convert_to_block(input)?;
304 return Err(InsertBlockError::new(block.into_sealed_block(), e.into()).into())
305 }
306 }
307 };
308 }
309
310 let parent_hash = input.parent_hash();
311 let block_num_hash = input.num_hash();
312
313 trace!(target: "engine::tree", block=?block_num_hash, parent=?parent_hash, "Fetching block state provider");
314 let Some(provider_builder) =
315 ensure_ok!(self.state_provider_builder(parent_hash, ctx.state()))
316 else {
317 return Err(InsertBlockError::new(
319 self.convert_to_block(input)?.into_sealed_block(),
320 ProviderError::HeaderNotFound(parent_hash.into()).into(),
321 )
322 .into())
323 };
324
325 let state_provider = ensure_ok!(provider_builder.build());
326
327 let Some(parent_block) = ensure_ok!(self.sealed_header_by_hash(parent_hash, ctx.state()))
329 else {
330 return Err(InsertBlockError::new(
331 self.convert_to_block(input)?.into_sealed_block(),
332 ProviderError::HeaderNotFound(parent_hash.into()).into(),
333 )
334 .into())
335 };
336
337 let evm_env = self.evm_env_for(&input);
338
339 let env = ExecutionEnv { evm_env, hash: input.hash(), parent_hash: input.parent_hash() };
340
341 let persisting_kind = ctx.persisting_kind_for(input.block_with_parent());
351 let run_parallel_state_root =
353 persisting_kind.can_run_parallel_state_root() && !self.config.state_root_fallback();
354
355 let has_ancestors_with_missing_trie_updates =
363 self.has_ancestors_with_missing_trie_updates(input.block_with_parent(), ctx.state());
364 let mut use_state_root_task = run_parallel_state_root &&
365 self.config.use_state_root_task() &&
366 !has_ancestors_with_missing_trie_updates;
367
368 debug!(
369 target: "engine::tree",
370 block=?block_num_hash,
371 run_parallel_state_root,
372 has_ancestors_with_missing_trie_updates,
373 use_state_root_task,
374 config_allows_state_root_task=self.config.use_state_root_task(),
375 "Deciding which state root algorithm to run"
376 );
377
378 let txs = self.tx_iterator_for(&input)?;
380 let mut handle = if use_state_root_task {
381 let consistent_view =
383 ensure_ok!(ConsistentDbView::new_with_latest_tip(self.provider.clone()));
384
385 let allocated_trie_input = self.payload_processor.take_trie_input();
387
388 let trie_input_start = Instant::now();
390 let trie_input = ensure_ok!(self.compute_trie_input(
391 persisting_kind,
392 ensure_ok!(consistent_view.provider_ro()),
393 parent_hash,
394 ctx.state(),
395 allocated_trie_input,
396 ));
397
398 self.metrics
399 .block_validation
400 .trie_input_duration
401 .record(trie_input_start.elapsed().as_secs_f64());
402
403 if trie_input.prefix_sets.is_empty() {
407 self.payload_processor.spawn(
408 env.clone(),
409 txs,
410 provider_builder,
411 consistent_view,
412 trie_input,
413 &self.config,
414 )
415 } else {
416 debug!(target: "engine::tree", block=?block_num_hash, "Disabling state root task due to non-empty prefix sets");
417 use_state_root_task = false;
418 self.payload_processor.spawn_cache_exclusive(env.clone(), txs, provider_builder)
419 }
420 } else {
421 self.payload_processor.spawn_cache_exclusive(env.clone(), txs, provider_builder)
422 };
423
424 let state_provider = CachedStateProvider::new_with_caches(
427 state_provider,
428 handle.caches(),
429 handle.cache_metrics(),
430 );
431
432 let output = if self.config.state_provider_metrics() {
433 let state_provider = InstrumentedStateProvider::from_state_provider(&state_provider);
434 let output = ensure_ok!(self.execute_block(&state_provider, env, &input, &mut handle));
435 state_provider.record_total_latency();
436 output
437 } else {
438 ensure_ok!(self.execute_block(&state_provider, env, &input, &mut handle))
439 };
440
441 handle.stop_prewarming_execution();
443
444 let block = self.convert_to_block(input)?;
445
446 macro_rules! ensure_ok {
448 ($expr:expr) => {
449 match $expr {
450 Ok(val) => val,
451 Err(e) => return Err(InsertBlockError::new(block.into_sealed_block(), e.into()).into()),
452 }
453 };
454 }
455
456 trace!(target: "engine::tree", block=?block_num_hash, "Validating block consensus");
457 ensure_ok!(self.validate_block_inner(&block));
459
460 if let Err(e) =
462 self.consensus.validate_header_against_parent(block.sealed_header(), &parent_block)
463 {
464 warn!(target: "engine::tree", ?block, "Failed to validate header {} against parent: {e}", block.hash());
465 return Err(InsertBlockError::new(block.into_sealed_block(), e.into()).into())
466 }
467
468 if let Err(err) = self.consensus.validate_block_post_execution(&block, &output) {
469 self.on_invalid_block(&parent_block, &block, &output, None, ctx.state_mut());
471 return Err(InsertBlockError::new(block.into_sealed_block(), err.into()).into())
472 }
473
474 let hashed_state = self.provider.hashed_post_state(&output.state);
475
476 if let Err(err) =
477 self.validator.validate_block_post_execution_with_hashed_state(&hashed_state, &block)
478 {
479 self.on_invalid_block(&parent_block, &block, &output, None, ctx.state_mut());
481 return Err(InsertBlockError::new(block.into_sealed_block(), err.into()).into())
482 }
483
484 debug!(target: "engine::tree", block=?block_num_hash, "Calculating block state root");
485
486 let root_time = Instant::now();
487
488 let mut maybe_state_root = None;
489
490 if run_parallel_state_root {
491 if use_state_root_task {
494 debug!(target: "engine::tree", block=?block_num_hash, "Using sparse trie state root algorithm");
495 match handle.state_root() {
496 Ok(StateRootComputeOutcome { state_root, trie_updates }) => {
497 let elapsed = root_time.elapsed();
498 info!(target: "engine::tree", ?state_root, ?elapsed, "State root task finished");
499 if state_root == block.header().state_root() {
501 maybe_state_root = Some((state_root, trie_updates, elapsed))
502 } else {
503 warn!(
504 target: "engine::tree",
505 ?state_root,
506 block_state_root = ?block.header().state_root(),
507 "State root task returned incorrect state root"
508 );
509 }
510 }
511 Err(error) => {
512 debug!(target: "engine::tree", %error, "Background parallel state root computation failed");
513 }
514 }
515 } else {
516 debug!(target: "engine::tree", block=?block_num_hash, "Using parallel state root algorithm");
517 match self.compute_state_root_parallel(
518 persisting_kind,
519 block.parent_hash(),
520 &hashed_state,
521 ctx.state(),
522 ) {
523 Ok(result) => {
524 info!(
525 target: "engine::tree",
526 block = ?block_num_hash,
527 regular_state_root = ?result.0,
528 "Regular root task finished"
529 );
530 maybe_state_root = Some((result.0, result.1, root_time.elapsed()));
531 }
532 Err(ParallelStateRootError::Provider(ProviderError::ConsistentView(error))) => {
533 debug!(target: "engine::tree", %error, "Parallel state root computation failed consistency check, falling back");
534 }
535 Err(error) => {
536 return Err(InsertBlockError::new(
537 block.into_sealed_block(),
538 InsertBlockErrorKind::Other(Box::new(error)),
539 )
540 .into())
541 }
542 }
543 }
544 }
545
546 let (state_root, trie_output, root_elapsed) = if let Some(maybe_state_root) =
547 maybe_state_root
548 {
549 maybe_state_root
550 } else {
551 if self.config.state_root_fallback() {
553 debug!(target: "engine::tree", block=?block_num_hash, "Using state root fallback for testing");
554 } else {
555 warn!(target: "engine::tree", block=?block_num_hash, ?persisting_kind, "Failed to compute state root in parallel");
556 self.metrics.block_validation.state_root_parallel_fallback_total.increment(1);
557 }
558
559 let (root, updates) =
560 ensure_ok!(state_provider.state_root_with_updates(hashed_state.clone()));
561 (root, updates, root_time.elapsed())
562 };
563
564 self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64());
565 debug!(target: "engine::tree", ?root_elapsed, block=?block_num_hash, "Calculated state root");
566
567 if state_root != block.header().state_root() {
569 self.on_invalid_block(
571 &parent_block,
572 &block,
573 &output,
574 Some((&trie_output, state_root)),
575 ctx.state_mut(),
576 );
577 let block_state_root = block.header().state_root();
578 return Err(InsertBlockError::new(
579 block.into_sealed_block(),
580 ConsensusError::BodyStateRootDiff(
581 GotExpected { got: state_root, expected: block_state_root }.into(),
582 )
583 .into(),
584 )
585 .into())
586 }
587
588 handle.terminate_caching(Some(output.state.clone()));
590
591 let trie_updates = if ctx.is_fork() {
594 ExecutedTrieUpdates::Missing
595 } else {
596 ExecutedTrieUpdates::Present(Arc::new(trie_output))
597 };
598
599 Ok(ExecutedBlockWithTrieUpdates {
600 block: ExecutedBlock {
601 recovered_block: Arc::new(block),
602 execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))),
603 hashed_state: Arc::new(hashed_state),
604 },
605 trie: trie_updates,
606 })
607 }
608
609 fn sealed_header_by_hash(
611 &self,
612 hash: B256,
613 state: &EngineApiTreeState<N>,
614 ) -> ProviderResult<Option<SealedHeader<N::BlockHeader>>> {
615 let header = state.tree_state.sealed_header_by_hash(&hash);
617
618 if header.is_some() {
619 Ok(header)
620 } else {
621 self.provider.sealed_header_by_hash(hash)
622 }
623 }
624
625 fn validate_block_inner(&self, block: &RecoveredBlock<N::Block>) -> Result<(), ConsensusError> {
628 if let Err(e) = self.consensus.validate_header(block.sealed_header()) {
629 error!(target: "engine::tree", ?block, "Failed to validate header {}: {e}", block.hash());
630 return Err(e)
631 }
632
633 if let Err(e) = self.consensus.validate_block_pre_execution(block.sealed_block()) {
634 error!(target: "engine::tree", ?block, "Failed to validate block {}: {e}", block.hash());
635 return Err(e)
636 }
637
638 Ok(())
639 }
640
641 fn execute_block<S, Err, T>(
643 &mut self,
644 state_provider: S,
645 env: ExecutionEnv<Evm>,
646 input: &BlockOrPayload<T>,
647 handle: &mut PayloadHandle<impl ExecutableTxFor<Evm>, Err>,
648 ) -> Result<BlockExecutionOutput<N::Receipt>, InsertBlockErrorKind>
649 where
650 S: StateProvider,
651 Err: core::error::Error + Send + Sync + 'static,
652 V: PayloadValidator<T, Block = N::Block>,
653 T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
654 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
655 {
656 let num_hash = NumHash::new(env.evm_env.block_env.number.to(), env.hash);
657 debug!(target: "engine::tree", block=?num_hash, "Executing block");
658 let mut db = State::builder()
659 .with_database(StateProviderDatabase::new(&state_provider))
660 .with_bundle_update()
661 .without_state_clear()
662 .build();
663
664 let evm = self.evm_config.evm_with_env(&mut db, env.evm_env.clone());
665 let ctx = self.execution_ctx_for(input);
666 let mut executor = self.evm_config.create_executor(evm, ctx);
667
668 if !self.config.precompile_cache_disabled() {
669 executor.evm_mut().precompiles_mut().map_pure_precompiles(|address, precompile| {
671 let metrics = self
672 .precompile_cache_metrics
673 .entry(*address)
674 .or_insert_with(|| CachedPrecompileMetrics::new_with_address(*address))
675 .clone();
676 CachedPrecompile::wrap(
677 precompile,
678 self.precompile_cache_map.cache_for_address(*address),
679 *env.evm_env.spec_id(),
680 Some(metrics),
681 )
682 });
683 }
684
685 let execution_start = Instant::now();
686 let state_hook = Box::new(handle.state_hook());
687 let output = self.metrics.execute_metered(
688 executor,
689 handle.iter_transactions().map(|res| res.map_err(BlockExecutionError::other)),
690 state_hook,
691 )?;
692 let execution_finish = Instant::now();
693 let execution_time = execution_finish.duration_since(execution_start);
694 debug!(target: "engine::tree", elapsed = ?execution_time, number=?num_hash.number, "Executed block");
695 Ok(output)
696 }
697
698 fn compute_state_root_parallel(
707 &self,
708 persisting_kind: PersistingKind,
709 parent_hash: B256,
710 hashed_state: &HashedPostState,
711 state: &EngineApiTreeState<N>,
712 ) -> Result<(B256, TrieUpdates), ParallelStateRootError> {
713 let consistent_view = ConsistentDbView::new_with_latest_tip(self.provider.clone())?;
714
715 let mut input = self.compute_trie_input(
716 persisting_kind,
717 consistent_view.provider_ro()?,
718 parent_hash,
719 state,
720 None,
721 )?;
722 input.append_ref(hashed_state);
724
725 ParallelStateRoot::new(consistent_view, input).incremental_root_with_updates()
726 }
727
728 fn has_ancestors_with_missing_trie_updates(
730 &self,
731 target_header: BlockWithParent,
732 state: &EngineApiTreeState<N>,
733 ) -> bool {
734 let mut current_hash = target_header.parent;
736 while let Some(block) = state.tree_state.blocks_by_hash.get(¤t_hash) {
737 if block.trie.is_missing() {
739 return true;
740 }
741
742 current_hash = block.recovered_block().parent_hash();
744 }
745
746 false
747 }
748
749 fn state_provider_builder(
754 &self,
755 hash: B256,
756 state: &EngineApiTreeState<N>,
757 ) -> ProviderResult<Option<StateProviderBuilder<N, P>>> {
758 if let Some((historical, blocks)) = state.tree_state.blocks_by_hash(hash) {
759 debug!(target: "engine::tree", %hash, %historical, "found canonical state for block in memory, creating provider builder");
760 return Ok(Some(StateProviderBuilder::new(
762 self.provider.clone(),
763 historical,
764 Some(blocks),
765 )))
766 }
767
768 if let Some(header) = self.provider.header(&hash)? {
770 debug!(target: "engine::tree", %hash, number = %header.number(), "found canonical state for block in database, creating provider builder");
771 return Ok(Some(StateProviderBuilder::new(self.provider.clone(), hash, None)))
774 }
775
776 debug!(target: "engine::tree", %hash, "no canonical state found for block");
777 Ok(None)
778 }
779
780 fn on_invalid_block(
782 &self,
783 parent_header: &SealedHeader<N::BlockHeader>,
784 block: &RecoveredBlock<N::Block>,
785 output: &BlockExecutionOutput<N::Receipt>,
786 trie_updates: Option<(&TrieUpdates, B256)>,
787 state: &mut EngineApiTreeState<N>,
788 ) {
789 if state.invalid_headers.get(&block.hash()).is_some() {
790 return;
792 }
793 self.invalid_block_hook.on_invalid_block(parent_header, block, output, trie_updates);
794 }
795
796 fn compute_trie_input<TP: DBProvider + BlockNumReader>(
812 &self,
813 persisting_kind: PersistingKind,
814 provider: TP,
815 parent_hash: B256,
816 state: &EngineApiTreeState<N>,
817 allocated_trie_input: Option<TrieInput>,
818 ) -> ProviderResult<TrieInput> {
819 let mut input = allocated_trie_input.unwrap_or_default();
821
822 let best_block_number = provider.best_block_number()?;
823
824 let (mut historical, mut blocks) = state
825 .tree_state
826 .blocks_by_hash(parent_hash)
827 .map_or_else(|| (parent_hash.into(), vec![]), |(hash, blocks)| (hash.into(), blocks));
828
829 if persisting_kind.is_descendant() {
832 while let Some(block) = blocks.last() {
834 let recovered_block = block.recovered_block();
835 if recovered_block.number() <= best_block_number {
836 blocks.pop();
839 } else {
840 break
843 }
844 }
845
846 historical = if let Some(block) = blocks.last() {
847 (block.recovered_block().number() - 1).into()
850 } else {
851 parent_hash.into()
853 };
854 }
855
856 if blocks.is_empty() {
857 debug!(target: "engine::tree", %parent_hash, "Parent found on disk");
858 } else {
859 debug!(target: "engine::tree", %parent_hash, %historical, blocks = blocks.len(), "Parent found in memory");
860 }
861
862 let block_number = provider
864 .convert_hash_or_number(historical)?
865 .ok_or_else(|| ProviderError::BlockHashNotFound(historical.as_hash().unwrap()))?;
866
867 let revert_state = if block_number == best_block_number {
869 debug!(target: "engine::tree", block_number, best_block_number, "Empty revert state");
872 HashedPostState::default()
873 } else {
874 let revert_state = HashedPostState::from_reverts::<KeccakKeyHasher>(
875 provider.tx_ref(),
876 block_number + 1,
877 )
878 .map_err(ProviderError::from)?;
879 debug!(
880 target: "engine::tree",
881 block_number,
882 best_block_number,
883 accounts = revert_state.accounts.len(),
884 storages = revert_state.storages.len(),
885 "Non-empty revert state"
886 );
887 revert_state
888 };
889 input.append(revert_state);
890
891 input.extend_with_blocks(
893 blocks.iter().rev().map(|block| (block.hashed_state(), block.trie_updates())),
894 );
895
896 Ok(input)
897 }
898}
899
900pub type ValidationOutcome<N, E = InsertPayloadError<BlockTy<N>>> =
902 Result<ExecutedBlockWithTrieUpdates<N>, E>;
903
904pub trait EngineValidator<
908 Types: PayloadTypes,
909 N: NodePrimitives = <<Types as PayloadTypes>::BuiltPayload as BuiltPayload>::Primitives,
910>: Send + Sync + 'static
911{
912 fn validate_payload_attributes_against_header(
922 &self,
923 attr: &Types::PayloadAttributes,
924 header: &N::BlockHeader,
925 ) -> Result<(), InvalidPayloadAttributesError>;
926
927 fn ensure_well_formed_payload(
936 &self,
937 payload: Types::ExecutionData,
938 ) -> Result<RecoveredBlock<N::Block>, NewPayloadError>;
939
940 fn validate_payload(
942 &mut self,
943 payload: Types::ExecutionData,
944 ctx: TreeCtx<'_, N>,
945 ) -> ValidationOutcome<N>;
946
947 fn validate_block(
949 &mut self,
950 block: RecoveredBlock<N::Block>,
951 ctx: TreeCtx<'_, N>,
952 ) -> ValidationOutcome<N>;
953}
954
955impl<N, Types, P, Evm, V> EngineValidator<Types> for BasicEngineValidator<P, Evm, V>
956where
957 P: DatabaseProviderFactory<Provider: BlockReader>
958 + BlockReader<Header = N::BlockHeader>
959 + StateProviderFactory
960 + StateReader
961 + HashedPostStateProvider
962 + Clone
963 + 'static,
964 N: NodePrimitives,
965 V: PayloadValidator<Types, Block = N::Block>,
966 Evm: ConfigureEngineEvm<Types::ExecutionData, Primitives = N> + 'static,
967 Types: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
968{
969 fn validate_payload_attributes_against_header(
970 &self,
971 attr: &Types::PayloadAttributes,
972 header: &N::BlockHeader,
973 ) -> Result<(), InvalidPayloadAttributesError> {
974 self.validator.validate_payload_attributes_against_header(attr, header)
975 }
976
977 fn ensure_well_formed_payload(
978 &self,
979 payload: Types::ExecutionData,
980 ) -> Result<RecoveredBlock<N::Block>, NewPayloadError> {
981 let block = self.validator.ensure_well_formed_payload(payload)?;
982 Ok(block)
983 }
984
985 fn validate_payload(
986 &mut self,
987 payload: Types::ExecutionData,
988 ctx: TreeCtx<'_, N>,
989 ) -> ValidationOutcome<N> {
990 self.validate_block_with_state(BlockOrPayload::Payload(payload), ctx)
991 }
992
993 fn validate_block(
994 &mut self,
995 block: RecoveredBlock<N::Block>,
996 ctx: TreeCtx<'_, N>,
997 ) -> ValidationOutcome<N> {
998 self.validate_block_with_state(BlockOrPayload::Block(block), ctx)
999 }
1000}
1001
1002#[derive(Debug)]
1004pub enum BlockOrPayload<T: PayloadTypes> {
1005 Payload(T::ExecutionData),
1007 Block(RecoveredBlock<BlockTy<<T::BuiltPayload as BuiltPayload>::Primitives>>),
1009}
1010
1011impl<T: PayloadTypes> BlockOrPayload<T> {
1012 pub fn hash(&self) -> B256 {
1014 match self {
1015 Self::Payload(payload) => payload.block_hash(),
1016 Self::Block(block) => block.hash(),
1017 }
1018 }
1019
1020 pub fn num_hash(&self) -> NumHash {
1022 match self {
1023 Self::Payload(payload) => payload.num_hash(),
1024 Self::Block(block) => block.num_hash(),
1025 }
1026 }
1027
1028 pub fn parent_hash(&self) -> B256 {
1030 match self {
1031 Self::Payload(payload) => payload.parent_hash(),
1032 Self::Block(block) => block.parent_hash(),
1033 }
1034 }
1035
1036 pub fn block_with_parent(&self) -> BlockWithParent {
1038 match self {
1039 Self::Payload(payload) => payload.block_with_parent(),
1040 Self::Block(block) => block.block_with_parent(),
1041 }
1042 }
1043}