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, BlockHashReader, BlockNumReader, BlockReader, DBProvider,
39 DatabaseProviderFactory, ExecutionOutcome, HashedPostStateProvider, HeaderProvider,
40 ProviderError, StateProvider, StateProviderFactory, 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, debug_span, 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}
61
62impl<'a, N: NodePrimitives> std::fmt::Debug for TreeCtx<'a, N> {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 f.debug_struct("TreeCtx")
65 .field("state", &"EngineApiTreeState")
66 .field("persistence_info", &self.persistence)
67 .field("canonical_in_memory_state", &self.canonical_in_memory_state)
68 .finish()
69 }
70}
71
72impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
73 pub const fn new(
75 state: &'a mut EngineApiTreeState<N>,
76 persistence: &'a PersistenceState,
77 canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
78 ) -> Self {
79 Self { state, persistence, canonical_in_memory_state }
80 }
81
82 pub const fn state(&self) -> &EngineApiTreeState<N> {
84 &*self.state
85 }
86
87 pub const fn state_mut(&mut self) -> &mut EngineApiTreeState<N> {
89 self.state
90 }
91
92 pub const fn persistence(&self) -> &PersistenceState {
94 self.persistence
95 }
96
97 pub const fn canonical_in_memory_state(&self) -> &'a CanonicalInMemoryState<N> {
99 self.canonical_in_memory_state
100 }
101
102 pub fn persisting_kind_for(&self, block: BlockWithParent) -> PersistingKind {
109 let Some(action) = self.persistence().current_action() else {
111 return PersistingKind::NotPersisting
112 };
113 let CurrentPersistenceAction::SavingBlocks { highest } = action else {
115 return PersistingKind::PersistingNotDescendant
116 };
117
118 if block.block.number > highest.number &&
121 self.state().tree_state.is_descendant(*highest, block)
122 {
123 return PersistingKind::PersistingDescendant
124 }
125
126 PersistingKind::PersistingNotDescendant
128 }
129}
130
131#[derive(derive_more::Debug)]
139pub struct BasicEngineValidator<P, Evm, V>
140where
141 Evm: ConfigureEvm,
142{
143 provider: P,
145 consensus: Arc<dyn FullConsensus<Evm::Primitives, Error = ConsensusError>>,
147 evm_config: Evm,
149 config: TreeConfig,
151 payload_processor: PayloadProcessor<Evm>,
153 precompile_cache_map: PrecompileCacheMap<SpecFor<Evm>>,
155 precompile_cache_metrics: HashMap<alloy_primitives::Address, CachedPrecompileMetrics>,
157 #[debug(skip)]
159 invalid_block_hook: Box<dyn InvalidBlockHook<Evm::Primitives>>,
160 metrics: EngineApiMetrics,
162 validator: V,
164}
165
166impl<N, P, Evm, V> BasicEngineValidator<P, Evm, V>
167where
168 N: NodePrimitives,
169 P: DatabaseProviderFactory<Provider: BlockReader>
170 + BlockReader<Header = N::BlockHeader>
171 + StateProviderFactory
172 + StateReader
173 + HashedPostStateProvider
174 + Clone
175 + 'static,
176 Evm: ConfigureEvm<Primitives = N> + 'static,
177{
178 #[allow(clippy::too_many_arguments)]
180 pub fn new(
181 provider: P,
182 consensus: Arc<dyn FullConsensus<N, Error = ConsensusError>>,
183 evm_config: Evm,
184 validator: V,
185 config: TreeConfig,
186 invalid_block_hook: Box<dyn InvalidBlockHook<N>>,
187 ) -> Self {
188 let precompile_cache_map = PrecompileCacheMap::default();
189 let payload_processor = PayloadProcessor::new(
190 WorkloadExecutor::default(),
191 evm_config.clone(),
192 &config,
193 precompile_cache_map.clone(),
194 );
195 Self {
196 provider,
197 consensus,
198 evm_config,
199 payload_processor,
200 precompile_cache_map,
201 precompile_cache_metrics: HashMap::new(),
202 config,
203 invalid_block_hook,
204 metrics: EngineApiMetrics::default(),
205 validator,
206 }
207 }
208
209 pub fn convert_to_block<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
211 &self,
212 input: BlockOrPayload<T>,
213 ) -> Result<RecoveredBlock<N::Block>, NewPayloadError>
214 where
215 V: PayloadValidator<T, Block = N::Block>,
216 {
217 match input {
218 BlockOrPayload::Payload(payload) => self.validator.ensure_well_formed_payload(payload),
219 BlockOrPayload::Block(block) => Ok(block),
220 }
221 }
222
223 pub fn evm_env_for<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
225 &self,
226 input: &BlockOrPayload<T>,
227 ) -> EvmEnvFor<Evm>
228 where
229 V: PayloadValidator<T, Block = N::Block>,
230 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
231 {
232 match input {
233 BlockOrPayload::Payload(payload) => self.evm_config.evm_env_for_payload(payload),
234 BlockOrPayload::Block(block) => self.evm_config.evm_env(block.header()),
235 }
236 }
237
238 pub fn tx_iterator_for<'a, T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
240 &'a self,
241 input: &'a BlockOrPayload<T>,
242 ) -> Result<impl ExecutableTxIterator<Evm> + 'a, NewPayloadError>
243 where
244 V: PayloadValidator<T, Block = N::Block>,
245 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
246 {
247 match input {
248 BlockOrPayload::Payload(payload) => Ok(Either::Left(
249 self.evm_config.tx_iterator_for_payload(payload).map(|res| res.map(Either::Left)),
250 )),
251 BlockOrPayload::Block(block) => {
252 let transactions = block.clone_transactions_recovered().collect::<Vec<_>>();
253 Ok(Either::Right(transactions.into_iter().map(|tx| Ok(Either::Right(tx)))))
254 }
255 }
256 }
257
258 pub fn execution_ctx_for<'a, T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
260 &self,
261 input: &'a BlockOrPayload<T>,
262 ) -> ExecutionCtxFor<'a, Evm>
263 where
264 V: PayloadValidator<T, Block = N::Block>,
265 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
266 {
267 match input {
268 BlockOrPayload::Payload(payload) => self.evm_config.context_for_payload(payload),
269 BlockOrPayload::Block(block) => self.evm_config.context_for_block(block),
270 }
271 }
272
273 fn handle_execution_error<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
278 &self,
279 input: BlockOrPayload<T>,
280 execution_err: InsertBlockErrorKind,
281 parent_block: &SealedHeader<N::BlockHeader>,
282 ) -> Result<ExecutedBlockWithTrieUpdates<N>, InsertPayloadError<N::Block>>
283 where
284 V: PayloadValidator<T, Block = N::Block>,
285 {
286 debug!(
287 target: "engine::tree",
288 ?execution_err,
289 block = ?input.num_hash(),
290 "Block execution failed, checking for header validation errors"
291 );
292
293 let block = self.convert_to_block(input)?;
296
297 if let Err(consensus_err) = self.validate_block_inner(&block) {
299 return Err(InsertBlockError::new(block.into_sealed_block(), consensus_err.into()).into())
301 }
302
303 if let Err(consensus_err) =
305 self.consensus.validate_header_against_parent(block.sealed_header(), parent_block)
306 {
307 return Err(InsertBlockError::new(block.into_sealed_block(), consensus_err.into()).into())
309 }
310
311 Err(InsertBlockError::new(block.into_sealed_block(), execution_err).into())
313 }
314
315 pub fn validate_block_with_state<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
323 &mut self,
324 input: BlockOrPayload<T>,
325 mut ctx: TreeCtx<'_, N>,
326 ) -> ValidationOutcome<N, InsertPayloadError<N::Block>>
327 where
328 V: PayloadValidator<T, Block = N::Block>,
329 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
330 {
331 macro_rules! ensure_ok {
333 ($expr:expr) => {
334 match $expr {
335 Ok(val) => val,
336 Err(e) => {
337 let block = self.convert_to_block(input)?;
338 return Err(
339 InsertBlockError::new(block.into_sealed_block(), e.into()).into()
340 )
341 }
342 }
343 };
344 }
345
346 let parent_hash = input.parent_hash();
347 let block_num_hash = input.num_hash();
348
349 trace!(target: "engine::tree", block=?block_num_hash, parent=?parent_hash, "Fetching block state provider");
350 let Some(provider_builder) =
351 ensure_ok!(self.state_provider_builder(parent_hash, ctx.state()))
352 else {
353 return Err(InsertBlockError::new(
355 self.convert_to_block(input)?.into_sealed_block(),
356 ProviderError::HeaderNotFound(parent_hash.into()).into(),
357 )
358 .into())
359 };
360
361 let state_provider = ensure_ok!(provider_builder.build());
362
363 let Some(parent_block) = ensure_ok!(self.sealed_header_by_hash(parent_hash, ctx.state()))
365 else {
366 return Err(InsertBlockError::new(
367 self.convert_to_block(input)?.into_sealed_block(),
368 ProviderError::HeaderNotFound(parent_hash.into()).into(),
369 )
370 .into())
371 };
372
373 let evm_env = self.evm_env_for(&input);
374
375 let env = ExecutionEnv { evm_env, hash: input.hash(), parent_hash: input.parent_hash() };
376
377 let persisting_kind = ctx.persisting_kind_for(input.block_with_parent());
387 let run_parallel_state_root =
389 persisting_kind.can_run_parallel_state_root() && !self.config.state_root_fallback();
390
391 let has_ancestors_with_missing_trie_updates =
399 self.has_ancestors_with_missing_trie_updates(input.block_with_parent(), ctx.state());
400 let mut use_state_root_task = run_parallel_state_root &&
401 self.config.use_state_root_task() &&
402 !has_ancestors_with_missing_trie_updates;
403
404 debug!(
405 target: "engine::tree",
406 block=?block_num_hash,
407 run_parallel_state_root,
408 has_ancestors_with_missing_trie_updates,
409 use_state_root_task,
410 config_allows_state_root_task=self.config.use_state_root_task(),
411 "Deciding which state root algorithm to run"
412 );
413
414 let txs = self.tx_iterator_for(&input)?;
416 let mut handle = if use_state_root_task {
417 let consistent_view =
419 ensure_ok!(ConsistentDbView::new_with_latest_tip(self.provider.clone()));
420
421 let allocated_trie_input = self.payload_processor.take_trie_input();
423
424 let trie_input_start = Instant::now();
426 let trie_input = ensure_ok!(self.compute_trie_input(
427 persisting_kind,
428 ensure_ok!(consistent_view.provider_ro()),
429 parent_hash,
430 ctx.state(),
431 allocated_trie_input,
432 ));
433
434 self.metrics
435 .block_validation
436 .trie_input_duration
437 .record(trie_input_start.elapsed().as_secs_f64());
438
439 let spawn_payload_processor_start = Instant::now();
443 let handle = if trie_input.prefix_sets.is_empty() {
444 self.payload_processor.spawn(
445 env.clone(),
446 txs,
447 provider_builder,
448 consistent_view,
449 trie_input,
450 &self.config,
451 )
452 } else {
453 debug!(target: "engine::tree", block=?block_num_hash, "Disabling state root task due to non-empty prefix sets");
454 use_state_root_task = false;
455 self.payload_processor.spawn_cache_exclusive(env.clone(), txs, provider_builder)
456 };
457
458 self.metrics
460 .block_validation
461 .spawn_payload_processor
462 .record(spawn_payload_processor_start.elapsed().as_secs_f64());
463 handle
464 } else {
465 let prewarming_start = Instant::now();
466 let handle =
467 self.payload_processor.spawn_cache_exclusive(env.clone(), txs, provider_builder);
468
469 self.metrics
471 .block_validation
472 .spawn_payload_processor
473 .record(prewarming_start.elapsed().as_secs_f64());
474 handle
475 };
476
477 let state_provider = CachedStateProvider::new_with_caches(
480 state_provider,
481 handle.caches(),
482 handle.cache_metrics(),
483 );
484
485 let output = match if self.config.state_provider_metrics() {
487 let state_provider = InstrumentedStateProvider::from_state_provider(&state_provider);
488 let result = self.execute_block(&state_provider, env, &input, &mut handle);
489 state_provider.record_total_latency();
490 result
491 } else {
492 self.execute_block(&state_provider, env, &input, &mut handle)
493 } {
494 Ok(output) => output,
495 Err(err) => return self.handle_execution_error(input, err, &parent_block),
496 };
497
498 handle.stop_prewarming_execution();
500
501 let block = self.convert_to_block(input)?;
502
503 macro_rules! ensure_ok {
505 ($expr:expr) => {
506 match $expr {
507 Ok(val) => val,
508 Err(e) => return Err(InsertBlockError::new(block.into_sealed_block(), e.into()).into()),
509 }
510 };
511 }
512
513 let post_execution_start = Instant::now();
514 trace!(target: "engine::tree", block=?block_num_hash, "Validating block consensus");
515 ensure_ok!(self.validate_block_inner(&block));
517
518 if let Err(e) =
520 self.consensus.validate_header_against_parent(block.sealed_header(), &parent_block)
521 {
522 warn!(target: "engine::tree", ?block, "Failed to validate header {} against parent: {e}", block.hash());
523 return Err(InsertBlockError::new(block.into_sealed_block(), e.into()).into())
524 }
525
526 if let Err(err) = self.consensus.validate_block_post_execution(&block, &output) {
527 self.on_invalid_block(&parent_block, &block, &output, None, ctx.state_mut());
529 return Err(InsertBlockError::new(block.into_sealed_block(), err.into()).into())
530 }
531
532 let hashed_state = self.provider.hashed_post_state(&output.state);
533
534 if let Err(err) =
535 self.validator.validate_block_post_execution_with_hashed_state(&hashed_state, &block)
536 {
537 self.on_invalid_block(&parent_block, &block, &output, None, ctx.state_mut());
539 return Err(InsertBlockError::new(block.into_sealed_block(), err.into()).into())
540 }
541
542 self.metrics
544 .block_validation
545 .post_execution_validation_duration
546 .record(post_execution_start.elapsed().as_secs_f64());
547
548 debug!(target: "engine::tree", block=?block_num_hash, "Calculating block state root");
549
550 let root_time = Instant::now();
551
552 let mut maybe_state_root = None;
553
554 if run_parallel_state_root {
555 if use_state_root_task {
558 debug!(target: "engine::tree", block=?block_num_hash, "Using sparse trie state root algorithm");
559 match handle.state_root() {
560 Ok(StateRootComputeOutcome { state_root, trie_updates }) => {
561 let elapsed = root_time.elapsed();
562 info!(target: "engine::tree", ?state_root, ?elapsed, "State root task finished");
563 if state_root == block.header().state_root() {
565 maybe_state_root = Some((state_root, trie_updates, elapsed))
566 } else {
567 warn!(
568 target: "engine::tree",
569 ?state_root,
570 block_state_root = ?block.header().state_root(),
571 "State root task returned incorrect state root"
572 );
573 }
574 }
575 Err(error) => {
576 debug!(target: "engine::tree", %error, "State root task failed");
577 }
578 }
579 } else {
580 debug!(target: "engine::tree", block=?block_num_hash, "Using parallel state root algorithm");
581 match self.compute_state_root_parallel(
582 persisting_kind,
583 block.parent_hash(),
584 &hashed_state,
585 ctx.state(),
586 ) {
587 Ok(result) => {
588 info!(
589 target: "engine::tree",
590 block = ?block_num_hash,
591 regular_state_root = ?result.0,
592 "Regular root task finished"
593 );
594 maybe_state_root = Some((result.0, result.1, root_time.elapsed()));
595 }
596 Err(error) => {
597 debug!(target: "engine::tree", %error, "Parallel state root computation failed");
598 }
599 }
600 }
601 }
602
603 let (state_root, trie_output, root_elapsed) = if let Some(maybe_state_root) =
604 maybe_state_root
605 {
606 maybe_state_root
607 } else {
608 if self.config.state_root_fallback() {
610 debug!(target: "engine::tree", block=?block_num_hash, "Using state root fallback for testing");
611 } else {
612 warn!(target: "engine::tree", block=?block_num_hash, ?persisting_kind, "Failed to compute state root in parallel");
613 self.metrics.block_validation.state_root_parallel_fallback_total.increment(1);
614 }
615
616 let (root, updates) =
617 ensure_ok!(state_provider.state_root_with_updates(hashed_state.clone()));
618 (root, updates, root_time.elapsed())
619 };
620
621 self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64());
622 debug!(target: "engine::tree", ?root_elapsed, block=?block_num_hash, "Calculated state root");
623
624 if state_root != block.header().state_root() {
626 self.on_invalid_block(
628 &parent_block,
629 &block,
630 &output,
631 Some((&trie_output, state_root)),
632 ctx.state_mut(),
633 );
634 let block_state_root = block.header().state_root();
635 return Err(InsertBlockError::new(
636 block.into_sealed_block(),
637 ConsensusError::BodyStateRootDiff(
638 GotExpected { got: state_root, expected: block_state_root }.into(),
639 )
640 .into(),
641 )
642 .into())
643 }
644
645 handle.terminate_caching(Some(output.state.clone()));
647
648 let connects_to_last_persisted =
656 ensure_ok!(self.block_connects_to_last_persisted(ctx, &block));
657 let should_discard_trie_updates =
658 !connects_to_last_persisted || has_ancestors_with_missing_trie_updates;
659 debug!(
660 target: "engine::tree",
661 block = ?block_num_hash,
662 connects_to_last_persisted,
663 has_ancestors_with_missing_trie_updates,
664 should_discard_trie_updates,
665 "Checking if should discard trie updates"
666 );
667 let trie_updates = if should_discard_trie_updates {
668 ExecutedTrieUpdates::Missing
669 } else {
670 ExecutedTrieUpdates::Present(Arc::new(trie_output))
671 };
672
673 Ok(ExecutedBlockWithTrieUpdates {
674 block: ExecutedBlock {
675 recovered_block: Arc::new(block),
676 execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))),
677 hashed_state: Arc::new(hashed_state),
678 },
679 trie: trie_updates,
680 })
681 }
682
683 fn sealed_header_by_hash(
685 &self,
686 hash: B256,
687 state: &EngineApiTreeState<N>,
688 ) -> ProviderResult<Option<SealedHeader<N::BlockHeader>>> {
689 let header = state.tree_state.sealed_header_by_hash(&hash);
691
692 if header.is_some() {
693 Ok(header)
694 } else {
695 self.provider.sealed_header_by_hash(hash)
696 }
697 }
698
699 fn validate_block_inner(&self, block: &RecoveredBlock<N::Block>) -> Result<(), ConsensusError> {
702 if let Err(e) = self.consensus.validate_header(block.sealed_header()) {
703 error!(target: "engine::tree", ?block, "Failed to validate header {}: {e}", block.hash());
704 return Err(e)
705 }
706
707 if let Err(e) = self.consensus.validate_block_pre_execution(block.sealed_block()) {
708 error!(target: "engine::tree", ?block, "Failed to validate block {}: {e}", block.hash());
709 return Err(e)
710 }
711
712 Ok(())
713 }
714
715 fn execute_block<S, Err, T>(
717 &mut self,
718 state_provider: S,
719 env: ExecutionEnv<Evm>,
720 input: &BlockOrPayload<T>,
721 handle: &mut PayloadHandle<impl ExecutableTxFor<Evm>, Err>,
722 ) -> Result<BlockExecutionOutput<N::Receipt>, InsertBlockErrorKind>
723 where
724 S: StateProvider,
725 Err: core::error::Error + Send + Sync + 'static,
726 V: PayloadValidator<T, Block = N::Block>,
727 T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
728 Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
729 {
730 let num_hash = NumHash::new(env.evm_env.block_env.number.to(), env.hash);
731
732 let span = debug_span!(target: "engine::tree", "execute_block", num = ?num_hash.number, hash = ?num_hash.hash);
733 let _enter = span.enter();
734 debug!(target: "engine::tree", "Executing block");
735
736 let mut db = State::builder()
737 .with_database(StateProviderDatabase::new(&state_provider))
738 .with_bundle_update()
739 .without_state_clear()
740 .build();
741
742 let evm = self.evm_config.evm_with_env(&mut db, env.evm_env.clone());
743 let ctx = self.execution_ctx_for(input);
744 let mut executor = self.evm_config.create_executor(evm, ctx);
745
746 if !self.config.precompile_cache_disabled() {
747 executor.evm_mut().precompiles_mut().map_pure_precompiles(|address, precompile| {
749 let metrics = self
750 .precompile_cache_metrics
751 .entry(*address)
752 .or_insert_with(|| CachedPrecompileMetrics::new_with_address(*address))
753 .clone();
754 CachedPrecompile::wrap(
755 precompile,
756 self.precompile_cache_map.cache_for_address(*address),
757 *env.evm_env.spec_id(),
758 Some(metrics),
759 )
760 });
761 }
762
763 let execution_start = Instant::now();
764 let state_hook = Box::new(handle.state_hook());
765 let output = self.metrics.execute_metered(
766 executor,
767 handle.iter_transactions().map(|res| res.map_err(BlockExecutionError::other)),
768 state_hook,
769 )?;
770 let execution_finish = Instant::now();
771 let execution_time = execution_finish.duration_since(execution_start);
772 debug!(target: "engine::tree", elapsed = ?execution_time, number=?num_hash.number, "Executed block");
773 Ok(output)
774 }
775
776 fn compute_state_root_parallel(
785 &self,
786 persisting_kind: PersistingKind,
787 parent_hash: B256,
788 hashed_state: &HashedPostState,
789 state: &EngineApiTreeState<N>,
790 ) -> Result<(B256, TrieUpdates), ParallelStateRootError> {
791 let consistent_view = ConsistentDbView::new_with_latest_tip(self.provider.clone())?;
792
793 let mut input = self.compute_trie_input(
794 persisting_kind,
795 consistent_view.provider_ro()?,
796 parent_hash,
797 state,
798 None,
799 )?;
800 input.append_ref(hashed_state);
802
803 ParallelStateRoot::new(consistent_view, input).incremental_root_with_updates()
804 }
805
806 fn block_connects_to_last_persisted(
811 &self,
812 ctx: TreeCtx<'_, N>,
813 block: &RecoveredBlock<N::Block>,
814 ) -> ProviderResult<bool> {
815 let provider = self.provider.database_provider_ro()?;
816 let last_persisted_block = provider.best_block_number()?;
817 let last_persisted_hash = provider
818 .block_hash(last_persisted_block)?
819 .ok_or(ProviderError::HeaderNotFound(last_persisted_block.into()))?;
820 let last_persisted = NumHash::new(last_persisted_block, last_persisted_hash);
821
822 let parent_num_hash = |hash: B256| -> ProviderResult<NumHash> {
823 let parent_num_hash =
824 if let Some(header) = ctx.state().tree_state.sealed_header_by_hash(&hash) {
825 Some(header.parent_num_hash())
826 } else {
827 provider.sealed_header_by_hash(hash)?.map(|header| header.parent_num_hash())
828 };
829
830 parent_num_hash.ok_or(ProviderError::BlockHashNotFound(hash))
831 };
832
833 let mut parent_block = block.parent_num_hash();
834 while parent_block.number > last_persisted.number {
835 parent_block = parent_num_hash(parent_block.hash)?;
836 }
837
838 let connects = parent_block == last_persisted;
839
840 debug!(
841 target: "engine::tree",
842 num_hash = ?block.num_hash(),
843 ?last_persisted,
844 ?parent_block,
845 "Checking if block connects to last persisted block"
846 );
847
848 Ok(connects)
849 }
850
851 fn has_ancestors_with_missing_trie_updates(
853 &self,
854 target_header: BlockWithParent,
855 state: &EngineApiTreeState<N>,
856 ) -> bool {
857 let mut current_hash = target_header.parent;
859 while let Some(block) = state.tree_state.blocks_by_hash.get(¤t_hash) {
860 if block.trie.is_missing() {
862 return true;
863 }
864
865 current_hash = block.recovered_block().parent_hash();
867 }
868
869 false
870 }
871
872 fn state_provider_builder(
877 &self,
878 hash: B256,
879 state: &EngineApiTreeState<N>,
880 ) -> ProviderResult<Option<StateProviderBuilder<N, P>>> {
881 if let Some((historical, blocks)) = state.tree_state.blocks_by_hash(hash) {
882 debug!(target: "engine::tree", %hash, %historical, "found canonical state for block in memory, creating provider builder");
883 return Ok(Some(StateProviderBuilder::new(
885 self.provider.clone(),
886 historical,
887 Some(blocks),
888 )))
889 }
890
891 if let Some(header) = self.provider.header(&hash)? {
893 debug!(target: "engine::tree", %hash, number = %header.number(), "found canonical state for block in database, creating provider builder");
894 return Ok(Some(StateProviderBuilder::new(self.provider.clone(), hash, None)))
897 }
898
899 debug!(target: "engine::tree", %hash, "no canonical state found for block");
900 Ok(None)
901 }
902
903 fn on_invalid_block(
905 &self,
906 parent_header: &SealedHeader<N::BlockHeader>,
907 block: &RecoveredBlock<N::Block>,
908 output: &BlockExecutionOutput<N::Receipt>,
909 trie_updates: Option<(&TrieUpdates, B256)>,
910 state: &mut EngineApiTreeState<N>,
911 ) {
912 if state.invalid_headers.get(&block.hash()).is_some() {
913 return
915 }
916 self.invalid_block_hook.on_invalid_block(parent_header, block, output, trie_updates);
917 }
918
919 fn compute_trie_input<TP: DBProvider + BlockNumReader>(
935 &self,
936 persisting_kind: PersistingKind,
937 provider: TP,
938 parent_hash: B256,
939 state: &EngineApiTreeState<N>,
940 allocated_trie_input: Option<TrieInput>,
941 ) -> ProviderResult<TrieInput> {
942 let mut input = allocated_trie_input.unwrap_or_default();
944
945 let best_block_number = provider.best_block_number()?;
946
947 let (mut historical, mut blocks) = state
948 .tree_state
949 .blocks_by_hash(parent_hash)
950 .map_or_else(|| (parent_hash.into(), vec![]), |(hash, blocks)| (hash.into(), blocks));
951
952 if persisting_kind.is_descendant() {
955 while let Some(block) = blocks.last() {
957 let recovered_block = block.recovered_block();
958 if recovered_block.number() <= best_block_number {
959 blocks.pop();
962 } else {
963 break
966 }
967 }
968
969 historical = if let Some(block) = blocks.last() {
970 (block.recovered_block().number() - 1).into()
973 } else {
974 parent_hash.into()
976 };
977 }
978
979 if blocks.is_empty() {
980 debug!(target: "engine::tree", %parent_hash, "Parent found on disk");
981 } else {
982 debug!(target: "engine::tree", %parent_hash, %historical, blocks = blocks.len(), "Parent found in memory");
983 }
984
985 let block_number = provider
987 .convert_hash_or_number(historical)?
988 .ok_or_else(|| ProviderError::BlockHashNotFound(historical.as_hash().unwrap()))?;
989
990 let revert_state = if block_number == best_block_number {
992 debug!(target: "engine::tree", block_number, best_block_number, "Empty revert state");
995 HashedPostState::default()
996 } else {
997 let revert_state = HashedPostState::from_reverts::<KeccakKeyHasher>(
998 provider.tx_ref(),
999 block_number + 1,
1000 )
1001 .map_err(ProviderError::from)?;
1002 debug!(
1003 target: "engine::tree",
1004 block_number,
1005 best_block_number,
1006 accounts = revert_state.accounts.len(),
1007 storages = revert_state.storages.len(),
1008 "Non-empty revert state"
1009 );
1010 revert_state
1011 };
1012 input.append(revert_state);
1013
1014 input.extend_with_blocks(
1016 blocks.iter().rev().map(|block| (block.hashed_state(), block.trie_updates())),
1017 );
1018
1019 Ok(input)
1020 }
1021}
1022
1023pub type ValidationOutcome<N, E = InsertPayloadError<BlockTy<N>>> =
1025 Result<ExecutedBlockWithTrieUpdates<N>, E>;
1026
1027pub trait EngineValidator<
1031 Types: PayloadTypes,
1032 N: NodePrimitives = <<Types as PayloadTypes>::BuiltPayload as BuiltPayload>::Primitives,
1033>: Send + Sync + 'static
1034{
1035 fn validate_payload_attributes_against_header(
1045 &self,
1046 attr: &Types::PayloadAttributes,
1047 header: &N::BlockHeader,
1048 ) -> Result<(), InvalidPayloadAttributesError>;
1049
1050 fn ensure_well_formed_payload(
1059 &self,
1060 payload: Types::ExecutionData,
1061 ) -> Result<RecoveredBlock<N::Block>, NewPayloadError>;
1062
1063 fn validate_payload(
1065 &mut self,
1066 payload: Types::ExecutionData,
1067 ctx: TreeCtx<'_, N>,
1068 ) -> ValidationOutcome<N>;
1069
1070 fn validate_block(
1072 &mut self,
1073 block: RecoveredBlock<N::Block>,
1074 ctx: TreeCtx<'_, N>,
1075 ) -> ValidationOutcome<N>;
1076}
1077
1078impl<N, Types, P, Evm, V> EngineValidator<Types> for BasicEngineValidator<P, Evm, V>
1079where
1080 P: DatabaseProviderFactory<Provider: BlockReader>
1081 + BlockReader<Header = N::BlockHeader>
1082 + StateProviderFactory
1083 + StateReader
1084 + HashedPostStateProvider
1085 + Clone
1086 + 'static,
1087 N: NodePrimitives,
1088 V: PayloadValidator<Types, Block = N::Block>,
1089 Evm: ConfigureEngineEvm<Types::ExecutionData, Primitives = N> + 'static,
1090 Types: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
1091{
1092 fn validate_payload_attributes_against_header(
1093 &self,
1094 attr: &Types::PayloadAttributes,
1095 header: &N::BlockHeader,
1096 ) -> Result<(), InvalidPayloadAttributesError> {
1097 self.validator.validate_payload_attributes_against_header(attr, header)
1098 }
1099
1100 fn ensure_well_formed_payload(
1101 &self,
1102 payload: Types::ExecutionData,
1103 ) -> Result<RecoveredBlock<N::Block>, NewPayloadError> {
1104 let block = self.validator.ensure_well_formed_payload(payload)?;
1105 Ok(block)
1106 }
1107
1108 fn validate_payload(
1109 &mut self,
1110 payload: Types::ExecutionData,
1111 ctx: TreeCtx<'_, N>,
1112 ) -> ValidationOutcome<N> {
1113 self.validate_block_with_state(BlockOrPayload::Payload(payload), ctx)
1114 }
1115
1116 fn validate_block(
1117 &mut self,
1118 block: RecoveredBlock<N::Block>,
1119 ctx: TreeCtx<'_, N>,
1120 ) -> ValidationOutcome<N> {
1121 self.validate_block_with_state(BlockOrPayload::Block(block), ctx)
1122 }
1123}
1124
1125#[derive(Debug)]
1127pub enum BlockOrPayload<T: PayloadTypes> {
1128 Payload(T::ExecutionData),
1130 Block(RecoveredBlock<BlockTy<<T::BuiltPayload as BuiltPayload>::Primitives>>),
1132}
1133
1134impl<T: PayloadTypes> BlockOrPayload<T> {
1135 pub fn hash(&self) -> B256 {
1137 match self {
1138 Self::Payload(payload) => payload.block_hash(),
1139 Self::Block(block) => block.hash(),
1140 }
1141 }
1142
1143 pub fn num_hash(&self) -> NumHash {
1145 match self {
1146 Self::Payload(payload) => payload.num_hash(),
1147 Self::Block(block) => block.num_hash(),
1148 }
1149 }
1150
1151 pub fn parent_hash(&self) -> B256 {
1153 match self {
1154 Self::Payload(payload) => payload.parent_hash(),
1155 Self::Block(block) => block.parent_hash(),
1156 }
1157 }
1158
1159 pub fn block_with_parent(&self) -> BlockWithParent {
1161 match self {
1162 Self::Payload(payload) => payload.block_with_parent(),
1163 Self::Block(block) => block.block_with_parent(),
1164 }
1165 }
1166}