1use crate::tree::{
4    cached_state::CachedStateProvider,
5    error::{InsertBlockError, InsertBlockErrorKind, InsertPayloadError},
6    executor::WorkloadExecutor,
7    instrumented_state::InstrumentedStateProvider,
8    payload_processor::{multiproof::MultiProofConfig, PayloadProcessor},
9    precompile_cache::{CachedPrecompile, CachedPrecompileMetrics, PrecompileCacheMap},
10    sparse_trie::StateRootComputeOutcome,
11    EngineApiMetrics, EngineApiTreeState, ExecutionEnv, PayloadHandle, StateProviderBuilder,
12    StateProviderDatabase, TreeConfig,
13};
14use alloy_consensus::transaction::Either;
15use alloy_eips::{eip1898::BlockWithParent, NumHash};
16use alloy_evm::Evm;
17use alloy_primitives::B256;
18use reth_chain_state::{CanonicalInMemoryState, ExecutedBlock};
19use reth_consensus::{ConsensusError, FullConsensus};
20use reth_engine_primitives::{
21    ConfigureEngineEvm, ExecutableTxIterator, ExecutionPayload, InvalidBlockHook, PayloadValidator,
22};
23use reth_errors::{BlockExecutionError, ProviderResult};
24use reth_evm::{
25    block::BlockExecutor, execute::ExecutableTxFor, ConfigureEvm, EvmEnvFor, ExecutionCtxFor,
26    SpecFor,
27};
28use reth_payload_primitives::{
29    BuiltPayload, InvalidPayloadAttributesError, NewPayloadError, PayloadTypes,
30};
31use reth_primitives_traits::{
32    AlloyBlockHeader, BlockTy, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader,
33};
34use reth_provider::{
35    providers::OverlayStateProviderFactory, BlockExecutionOutput, BlockReader,
36    DatabaseProviderFactory, ExecutionOutcome, HashedPostStateProvider, ProviderError,
37    PruneCheckpointReader, StageCheckpointReader, StateProvider, StateProviderFactory, StateReader,
38    StateRootProvider, TrieReader,
39};
40use reth_revm::db::State;
41use reth_trie::{updates::TrieUpdates, HashedPostState, TrieInput};
42use reth_trie_parallel::root::{ParallelStateRoot, ParallelStateRootError};
43use std::{collections::HashMap, sync::Arc, time::Instant};
44use tracing::{debug, debug_span, error, info, instrument, trace, warn};
45
46pub struct TreeCtx<'a, N: NodePrimitives> {
51    state: &'a mut EngineApiTreeState<N>,
53    canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
55}
56
57impl<'a, N: NodePrimitives> std::fmt::Debug for TreeCtx<'a, N> {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        f.debug_struct("TreeCtx")
60            .field("state", &"EngineApiTreeState")
61            .field("canonical_in_memory_state", &self.canonical_in_memory_state)
62            .finish()
63    }
64}
65
66impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
67    pub const fn new(
69        state: &'a mut EngineApiTreeState<N>,
70        canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
71    ) -> Self {
72        Self { state, canonical_in_memory_state }
73    }
74
75    pub const fn state(&self) -> &EngineApiTreeState<N> {
77        &*self.state
78    }
79
80    pub const fn state_mut(&mut self) -> &mut EngineApiTreeState<N> {
82        self.state
83    }
84
85    pub const fn canonical_in_memory_state(&self) -> &'a CanonicalInMemoryState<N> {
87        self.canonical_in_memory_state
88    }
89}
90
91#[derive(derive_more::Debug)]
99pub struct BasicEngineValidator<P, Evm, V>
100where
101    Evm: ConfigureEvm,
102{
103    provider: P,
105    consensus: Arc<dyn FullConsensus<Evm::Primitives, Error = ConsensusError>>,
107    evm_config: Evm,
109    config: TreeConfig,
111    payload_processor: PayloadProcessor<Evm>,
113    precompile_cache_map: PrecompileCacheMap<SpecFor<Evm>>,
115    precompile_cache_metrics: HashMap<alloy_primitives::Address, CachedPrecompileMetrics>,
117    #[debug(skip)]
119    invalid_block_hook: Box<dyn InvalidBlockHook<Evm::Primitives>>,
120    metrics: EngineApiMetrics,
122    validator: V,
124    trie_input: Option<TrieInput>,
126}
127
128impl<N, P, Evm, V> BasicEngineValidator<P, Evm, V>
129where
130    N: NodePrimitives,
131    P: DatabaseProviderFactory<
132            Provider: BlockReader + TrieReader + StageCheckpointReader + PruneCheckpointReader,
133        > + BlockReader<Header = N::BlockHeader>
134        + StateProviderFactory
135        + StateReader
136        + HashedPostStateProvider
137        + Clone
138        + 'static,
139    Evm: ConfigureEvm<Primitives = N> + 'static,
140{
141    #[allow(clippy::too_many_arguments)]
143    pub fn new(
144        provider: P,
145        consensus: Arc<dyn FullConsensus<N, Error = ConsensusError>>,
146        evm_config: Evm,
147        validator: V,
148        config: TreeConfig,
149        invalid_block_hook: Box<dyn InvalidBlockHook<N>>,
150    ) -> Self {
151        let precompile_cache_map = PrecompileCacheMap::default();
152        let payload_processor = PayloadProcessor::new(
153            WorkloadExecutor::default(),
154            evm_config.clone(),
155            &config,
156            precompile_cache_map.clone(),
157        );
158        Self {
159            provider,
160            consensus,
161            evm_config,
162            payload_processor,
163            precompile_cache_map,
164            precompile_cache_metrics: HashMap::new(),
165            config,
166            invalid_block_hook,
167            metrics: EngineApiMetrics::default(),
168            validator,
169            trie_input: Default::default(),
170        }
171    }
172
173    pub fn convert_to_block<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
175        &self,
176        input: BlockOrPayload<T>,
177    ) -> Result<RecoveredBlock<N::Block>, NewPayloadError>
178    where
179        V: PayloadValidator<T, Block = N::Block>,
180    {
181        match input {
182            BlockOrPayload::Payload(payload) => self.validator.ensure_well_formed_payload(payload),
183            BlockOrPayload::Block(block) => Ok(block),
184        }
185    }
186
187    pub fn evm_env_for<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
189        &self,
190        input: &BlockOrPayload<T>,
191    ) -> Result<EvmEnvFor<Evm>, Evm::Error>
192    where
193        V: PayloadValidator<T, Block = N::Block>,
194        Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
195    {
196        match input {
197            BlockOrPayload::Payload(payload) => Ok(self.evm_config.evm_env_for_payload(payload)?),
198            BlockOrPayload::Block(block) => Ok(self.evm_config.evm_env(block.header())?),
199        }
200    }
201
202    pub fn tx_iterator_for<'a, T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
204        &'a self,
205        input: &'a BlockOrPayload<T>,
206    ) -> Result<impl ExecutableTxIterator<Evm> + 'a, NewPayloadError>
207    where
208        V: PayloadValidator<T, Block = N::Block>,
209        Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
210    {
211        match input {
212            BlockOrPayload::Payload(payload) => Ok(Either::Left(
213                self.evm_config
214                    .tx_iterator_for_payload(payload)
215                    .map_err(NewPayloadError::other)?
216                    .map(|res| res.map(Either::Left)),
217            )),
218            BlockOrPayload::Block(block) => {
219                let transactions = block.clone_transactions_recovered().collect::<Vec<_>>();
220                Ok(Either::Right(transactions.into_iter().map(|tx| Ok(Either::Right(tx)))))
221            }
222        }
223    }
224
225    pub fn execution_ctx_for<'a, T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
227        &self,
228        input: &'a BlockOrPayload<T>,
229    ) -> Result<ExecutionCtxFor<'a, Evm>, Evm::Error>
230    where
231        V: PayloadValidator<T, Block = N::Block>,
232        Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
233    {
234        match input {
235            BlockOrPayload::Payload(payload) => Ok(self.evm_config.context_for_payload(payload)?),
236            BlockOrPayload::Block(block) => Ok(self.evm_config.context_for_block(block)?),
237        }
238    }
239
240    fn handle_execution_error<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
245        &self,
246        input: BlockOrPayload<T>,
247        execution_err: InsertBlockErrorKind,
248        parent_block: &SealedHeader<N::BlockHeader>,
249    ) -> Result<ExecutedBlock<N>, InsertPayloadError<N::Block>>
250    where
251        V: PayloadValidator<T, Block = N::Block>,
252    {
253        debug!(
254            target: "engine::tree::payload_validator",
255            ?execution_err,
256            block = ?input.num_hash(),
257            "Block execution failed, checking for header validation errors"
258        );
259
260        let block = self.convert_to_block(input)?;
263
264        if let Err(consensus_err) = self.validate_block_inner(&block) {
266            return Err(InsertBlockError::new(block.into_sealed_block(), consensus_err.into()).into())
268        }
269
270        if let Err(consensus_err) =
272            self.consensus.validate_header_against_parent(block.sealed_header(), parent_block)
273        {
274            return Err(InsertBlockError::new(block.into_sealed_block(), consensus_err.into()).into())
276        }
277
278        Err(InsertBlockError::new(block.into_sealed_block(), execution_err).into())
280    }
281
282    #[instrument(
290        level = "debug",
291        target = "engine::tree::payload_validator",
292        skip_all,
293        fields(
294            parent = ?input.parent_hash(),
295            type_name = ?input.type_name(),
296        )
297    )]
298    pub fn validate_block_with_state<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
299        &mut self,
300        input: BlockOrPayload<T>,
301        mut ctx: TreeCtx<'_, N>,
302    ) -> ValidationOutcome<N, InsertPayloadError<N::Block>>
303    where
304        V: PayloadValidator<T, Block = N::Block>,
305        Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
306    {
307        macro_rules! ensure_ok {
310            ($expr:expr) => {
311                match $expr {
312                    Ok(val) => val,
313                    Err(e) => {
314                        let block = self.convert_to_block(input)?;
315                        return Err(
316                            InsertBlockError::new(block.into_sealed_block(), e.into()).into()
317                        )
318                    }
319                }
320            };
321        }
322
323        macro_rules! ensure_ok_post_block {
325            ($expr:expr, $block:expr) => {
326                match $expr {
327                    Ok(val) => val,
328                    Err(e) => {
329                        return Err(
330                            InsertBlockError::new($block.into_sealed_block(), e.into()).into()
331                        )
332                    }
333                }
334            };
335        }
336
337        let parent_hash = input.parent_hash();
338        let block_num_hash = input.num_hash();
339
340        trace!(target: "engine::tree::payload_validator", "Fetching block state provider");
341        let _enter =
342            debug_span!(target: "engine::tree::payload_validator", "state provider").entered();
343        let Some(provider_builder) =
344            ensure_ok!(self.state_provider_builder(parent_hash, ctx.state()))
345        else {
346            return Err(InsertBlockError::new(
348                self.convert_to_block(input)?.into_sealed_block(),
349                ProviderError::HeaderNotFound(parent_hash.into()).into(),
350            )
351            .into())
352        };
353        let state_provider = ensure_ok!(provider_builder.build());
354        drop(_enter);
355
356        let Some(parent_block) = ensure_ok!(self.sealed_header_by_hash(parent_hash, ctx.state()))
358        else {
359            return Err(InsertBlockError::new(
360                self.convert_to_block(input)?.into_sealed_block(),
361                ProviderError::HeaderNotFound(parent_hash.into()).into(),
362            )
363            .into())
364        };
365
366        let evm_env = debug_span!(target: "engine::tree::payload_validator", "evm env")
367            .in_scope(|| self.evm_env_for(&input))
368            .map_err(NewPayloadError::other)?;
369
370        let env = ExecutionEnv { evm_env, hash: input.hash(), parent_hash: input.parent_hash() };
371
372        let state_root_plan = self.plan_state_root_computation();
374        let strategy = state_root_plan.strategy;
375
376        debug!(
377            target: "engine::tree::payload_validator",
378            ?strategy,
379            "Deciding which state root algorithm to run"
380        );
381
382        let txs = self.tx_iterator_for(&input)?;
384
385        let (mut handle, strategy) = ensure_ok!(self.spawn_payload_processor(
387            env.clone(),
388            txs,
389            provider_builder,
390            parent_hash,
391            ctx.state(),
392            strategy,
393        ));
394
395        let state_provider = CachedStateProvider::new_with_caches(
398            state_provider,
399            handle.caches(),
400            handle.cache_metrics(),
401        );
402
403        let output = match if self.config.state_provider_metrics() {
405            let state_provider = InstrumentedStateProvider::from_state_provider(&state_provider);
406            let result = self.execute_block(&state_provider, env, &input, &mut handle);
407            state_provider.record_total_latency();
408            result
409        } else {
410            self.execute_block(&state_provider, env, &input, &mut handle)
411        } {
412            Ok(output) => output,
413            Err(err) => return self.handle_execution_error(input, err, &parent_block),
414        };
415
416        handle.stop_prewarming_execution();
418
419        let block = self.convert_to_block(input)?;
420
421        let hashed_state = ensure_ok_post_block!(
422            self.validate_post_execution(&block, &parent_block, &output, &mut ctx),
423            block
424        );
425
426        debug!(target: "engine::tree::payload_validator", "Calculating block state root");
427
428        let root_time = Instant::now();
429
430        let mut maybe_state_root = None;
431
432        match strategy {
433            StateRootStrategy::StateRootTask => {
434                debug!(target: "engine::tree::payload_validator", "Using sparse trie state root algorithm");
435                match handle.state_root() {
436                    Ok(StateRootComputeOutcome { state_root, trie_updates }) => {
437                        let elapsed = root_time.elapsed();
438                        info!(target: "engine::tree::payload_validator", ?state_root, ?elapsed, "State root task finished");
439                        if state_root == block.header().state_root() {
441                            maybe_state_root = Some((state_root, trie_updates, elapsed))
442                        } else {
443                            warn!(
444                                target: "engine::tree::payload_validator",
445                                ?state_root,
446                                block_state_root = ?block.header().state_root(),
447                                "State root task returned incorrect state root"
448                            );
449                        }
450                    }
451                    Err(error) => {
452                        debug!(target: "engine::tree::payload_validator", %error, "State root task failed");
453                    }
454                }
455            }
456            StateRootStrategy::Parallel => {
457                debug!(target: "engine::tree::payload_validator", "Using parallel state root algorithm");
458                match self.compute_state_root_parallel(
459                    block.parent_hash(),
460                    &hashed_state,
461                    ctx.state(),
462                ) {
463                    Ok(result) => {
464                        let elapsed = root_time.elapsed();
465                        info!(
466                            target: "engine::tree::payload_validator",
467                            regular_state_root = ?result.0,
468                            ?elapsed,
469                            "Regular root task finished"
470                        );
471                        maybe_state_root = Some((result.0, result.1, elapsed));
472                    }
473                    Err(error) => {
474                        debug!(target: "engine::tree::payload_validator", %error, "Parallel state root computation failed");
475                    }
476                }
477            }
478            StateRootStrategy::Synchronous => {}
479        }
480
481        let (state_root, trie_output, root_elapsed) = if let Some(maybe_state_root) =
485            maybe_state_root
486        {
487            maybe_state_root
488        } else {
489            if self.config.state_root_fallback() {
491                debug!(target: "engine::tree::payload_validator", "Using state root fallback for testing");
492            } else {
493                warn!(target: "engine::tree::payload_validator", "Failed to compute state root in parallel");
494                self.metrics.block_validation.state_root_parallel_fallback_total.increment(1);
495            }
496
497            let (root, updates) = ensure_ok_post_block!(
498                state_provider.state_root_with_updates(hashed_state.clone()),
499                block
500            );
501            (root, updates, root_time.elapsed())
502        };
503
504        self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64());
505        debug!(target: "engine::tree::payload_validator", ?root_elapsed, "Calculated state root");
506
507        if state_root != block.header().state_root() {
509            self.on_invalid_block(
511                &parent_block,
512                &block,
513                &output,
514                Some((&trie_output, state_root)),
515                ctx.state_mut(),
516            );
517            let block_state_root = block.header().state_root();
518            return Err(InsertBlockError::new(
519                block.into_sealed_block(),
520                ConsensusError::BodyStateRootDiff(
521                    GotExpected { got: state_root, expected: block_state_root }.into(),
522                )
523                .into(),
524            )
525            .into())
526        }
527
528        handle.terminate_caching(Some(&output.state));
530
531        Ok(ExecutedBlock {
532            recovered_block: Arc::new(block),
533            execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))),
534            hashed_state: Arc::new(hashed_state),
535            trie_updates: Arc::new(trie_output),
536        })
537    }
538
539    fn sealed_header_by_hash(
541        &self,
542        hash: B256,
543        state: &EngineApiTreeState<N>,
544    ) -> ProviderResult<Option<SealedHeader<N::BlockHeader>>> {
545        let header = state.tree_state.sealed_header_by_hash(&hash);
547
548        if header.is_some() {
549            Ok(header)
550        } else {
551            self.provider.sealed_header_by_hash(hash)
552        }
553    }
554
555    fn validate_block_inner(&self, block: &RecoveredBlock<N::Block>) -> Result<(), ConsensusError> {
558        if let Err(e) = self.consensus.validate_header(block.sealed_header()) {
559            error!(target: "engine::tree::payload_validator", ?block, "Failed to validate header {}: {e}", block.hash());
560            return Err(e)
561        }
562
563        if let Err(e) = self.consensus.validate_block_pre_execution(block.sealed_block()) {
564            error!(target: "engine::tree::payload_validator", ?block, "Failed to validate block {}: {e}", block.hash());
565            return Err(e)
566        }
567
568        Ok(())
569    }
570
571    #[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)]
573    fn execute_block<S, Err, T>(
574        &mut self,
575        state_provider: S,
576        env: ExecutionEnv<Evm>,
577        input: &BlockOrPayload<T>,
578        handle: &mut PayloadHandle<impl ExecutableTxFor<Evm>, Err>,
579    ) -> Result<BlockExecutionOutput<N::Receipt>, InsertBlockErrorKind>
580    where
581        S: StateProvider,
582        Err: core::error::Error + Send + Sync + 'static,
583        V: PayloadValidator<T, Block = N::Block>,
584        T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
585        Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
586    {
587        debug!(target: "engine::tree::payload_validator", "Executing block");
588
589        let mut db = State::builder()
590            .with_database(StateProviderDatabase::new(&state_provider))
591            .with_bundle_update()
592            .without_state_clear()
593            .build();
594
595        let evm = self.evm_config.evm_with_env(&mut db, env.evm_env.clone());
596        let ctx =
597            self.execution_ctx_for(input).map_err(|e| InsertBlockErrorKind::Other(Box::new(e)))?;
598        let mut executor = self.evm_config.create_executor(evm, ctx);
599
600        if !self.config.precompile_cache_disabled() {
601            executor.evm_mut().precompiles_mut().map_pure_precompiles(|address, precompile| {
603                let metrics = self
604                    .precompile_cache_metrics
605                    .entry(*address)
606                    .or_insert_with(|| CachedPrecompileMetrics::new_with_address(*address))
607                    .clone();
608                CachedPrecompile::wrap(
609                    precompile,
610                    self.precompile_cache_map.cache_for_address(*address),
611                    *env.evm_env.spec_id(),
612                    Some(metrics),
613                )
614            });
615        }
616
617        let execution_start = Instant::now();
618        let state_hook = Box::new(handle.state_hook());
619        let output = self.metrics.execute_metered(
620            executor,
621            handle.iter_transactions().map(|res| res.map_err(BlockExecutionError::other)),
622            state_hook,
623        )?;
624        let execution_finish = Instant::now();
625        let execution_time = execution_finish.duration_since(execution_start);
626        debug!(target: "engine::tree::payload_validator", elapsed = ?execution_time, "Executed block");
627        Ok(output)
628    }
629
630    #[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)]
639    fn compute_state_root_parallel(
640        &self,
641        parent_hash: B256,
642        hashed_state: &HashedPostState,
643        state: &EngineApiTreeState<N>,
644    ) -> Result<(B256, TrieUpdates), ParallelStateRootError> {
645        let (mut input, block_hash) = self.compute_trie_input(parent_hash, state, None)?;
646
647        input.append_ref(hashed_state);
649
650        let (_, multiproof_config) = MultiProofConfig::from_input(input);
653
654        let factory = OverlayStateProviderFactory::new(self.provider.clone())
655            .with_block_hash(Some(block_hash))
656            .with_trie_overlay(Some(multiproof_config.nodes_sorted))
657            .with_hashed_state_overlay(Some(multiproof_config.state_sorted));
658
659        let prefix_sets = Arc::into_inner(multiproof_config.prefix_sets)
663            .expect("MultiProofConfig was never cloned")
664            .freeze();
665
666        ParallelStateRoot::new(factory, prefix_sets).incremental_root_with_updates()
667    }
668
669    fn validate_post_execution<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
676        &self,
677        block: &RecoveredBlock<N::Block>,
678        parent_block: &SealedHeader<N::BlockHeader>,
679        output: &BlockExecutionOutput<N::Receipt>,
680        ctx: &mut TreeCtx<'_, N>,
681    ) -> Result<HashedPostState, InsertBlockErrorKind>
682    where
683        V: PayloadValidator<T, Block = N::Block>,
684    {
685        let start = Instant::now();
686
687        trace!(target: "engine::tree::payload_validator", block=?block.num_hash(), "Validating block consensus");
688        if let Err(e) = self.validate_block_inner(block) {
690            return Err(e.into())
691        }
692
693        if let Err(e) =
695            self.consensus.validate_header_against_parent(block.sealed_header(), parent_block)
696        {
697            warn!(target: "engine::tree::payload_validator", ?block, "Failed to validate header {} against parent: {e}", block.hash());
698            return Err(e.into())
699        }
700
701        if let Err(err) = self.consensus.validate_block_post_execution(block, output) {
702            self.on_invalid_block(parent_block, block, output, None, ctx.state_mut());
704            return Err(err.into())
705        }
706
707        let hashed_state = self.provider.hashed_post_state(&output.state);
708
709        if let Err(err) =
710            self.validator.validate_block_post_execution_with_hashed_state(&hashed_state, block)
711        {
712            self.on_invalid_block(parent_block, block, output, None, ctx.state_mut());
714            return Err(err.into())
715        }
716
717        self.metrics
719            .block_validation
720            .post_execution_validation_duration
721            .record(start.elapsed().as_secs_f64());
722
723        Ok(hashed_state)
724    }
725
726    #[allow(clippy::too_many_arguments)]
737    #[instrument(
738        level = "debug",
739        target = "engine::tree::payload_validator",
740        skip_all,
741        fields(strategy)
742    )]
743    fn spawn_payload_processor<T: ExecutableTxIterator<Evm>>(
744        &mut self,
745        env: ExecutionEnv<Evm>,
746        txs: T,
747        provider_builder: StateProviderBuilder<N, P>,
748        parent_hash: B256,
749        state: &EngineApiTreeState<N>,
750        strategy: StateRootStrategy,
751    ) -> Result<
752        (
753            PayloadHandle<
754                impl ExecutableTxFor<Evm> + use<N, P, Evm, V, T>,
755                impl core::error::Error + Send + Sync + 'static + use<N, P, Evm, V, T>,
756            >,
757            StateRootStrategy,
758        ),
759        InsertBlockErrorKind,
760    > {
761        match strategy {
762            StateRootStrategy::StateRootTask => {
763                let allocated_trie_input = self.trie_input.take();
765
766                let trie_input_start = Instant::now();
768                let (trie_input, block_hash) =
769                    self.compute_trie_input(parent_hash, state, allocated_trie_input)?;
770
771                self.metrics
772                    .block_validation
773                    .trie_input_duration
774                    .record(trie_input_start.elapsed().as_secs_f64());
775
776                let (trie_input, multiproof_config) = MultiProofConfig::from_input(trie_input);
779                self.trie_input.replace(trie_input);
780
781                let multiproof_provider_factory =
784                    OverlayStateProviderFactory::new(self.provider.clone())
785                        .with_block_hash(Some(block_hash))
786                        .with_trie_overlay(Some(multiproof_config.nodes_sorted))
787                        .with_hashed_state_overlay(Some(multiproof_config.state_sorted));
788
789                let spawn_start = Instant::now();
792                let (handle, strategy) = match self.payload_processor.spawn(
793                    env,
794                    txs,
795                    provider_builder,
796                    multiproof_provider_factory,
797                    &self.config,
798                ) {
799                    Ok(handle) => {
800                        (handle, StateRootStrategy::StateRootTask)
802                    }
803                    Err((error, txs, env, provider_builder)) => {
804                        error!(
806                            target: "engine::tree::payload_validator",
807                            ?error,
808                            "Failed to spawn proof workers, falling back to parallel state root"
809                        );
810                        (
811                            self.payload_processor.spawn_cache_exclusive(
812                                env,
813                                txs,
814                                provider_builder,
815                            ),
816                            StateRootStrategy::Parallel,
817                        )
818                    }
819                };
820
821                self.metrics
823                    .block_validation
824                    .spawn_payload_processor
825                    .record(spawn_start.elapsed().as_secs_f64());
826
827                Ok((handle, strategy))
828            }
829            strategy @ (StateRootStrategy::Parallel | StateRootStrategy::Synchronous) => {
830                let start = Instant::now();
831                let handle =
832                    self.payload_processor.spawn_cache_exclusive(env, txs, provider_builder);
833
834                self.metrics
836                    .block_validation
837                    .spawn_payload_processor
838                    .record(start.elapsed().as_secs_f64());
839
840                Ok((handle, strategy))
841            }
842        }
843    }
844
845    fn state_provider_builder(
850        &self,
851        hash: B256,
852        state: &EngineApiTreeState<N>,
853    ) -> ProviderResult<Option<StateProviderBuilder<N, P>>> {
854        if let Some((historical, blocks)) = state.tree_state.blocks_by_hash(hash) {
855            debug!(target: "engine::tree::payload_validator", %hash, %historical, "found canonical state for block in memory, creating provider builder");
856            return Ok(Some(StateProviderBuilder::new(
858                self.provider.clone(),
859                historical,
860                Some(blocks),
861            )))
862        }
863
864        if let Some(header) = self.provider.header(hash)? {
866            debug!(target: "engine::tree::payload_validator", %hash, number = %header.number(), "found canonical state for block in database, creating provider builder");
867            return Ok(Some(StateProviderBuilder::new(self.provider.clone(), hash, None)))
870        }
871
872        debug!(target: "engine::tree::payload_validator", %hash, "no canonical state found for block");
873        Ok(None)
874    }
875
876    #[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)]
878    fn plan_state_root_computation(&self) -> StateRootPlan {
879        let strategy = if self.config.state_root_fallback() {
880            StateRootStrategy::Synchronous
881        } else if self.config.use_state_root_task() {
882            StateRootStrategy::StateRootTask
883        } else {
884            StateRootStrategy::Parallel
885        };
886
887        debug!(
888            target: "engine::tree::payload_validator",
889            ?strategy,
890            "Planned state root computation strategy"
891        );
892
893        StateRootPlan { strategy }
894    }
895
896    fn on_invalid_block(
898        &self,
899        parent_header: &SealedHeader<N::BlockHeader>,
900        block: &RecoveredBlock<N::Block>,
901        output: &BlockExecutionOutput<N::Receipt>,
902        trie_updates: Option<(&TrieUpdates, B256)>,
903        state: &mut EngineApiTreeState<N>,
904    ) {
905        if state.invalid_headers.get(&block.hash()).is_some() {
906            return
908        }
909        self.invalid_block_hook.on_invalid_block(parent_header, block, output, trie_updates);
910    }
911
912    #[instrument(
926        level = "debug",
927        target = "engine::tree::payload_validator",
928        skip_all,
929        fields(parent_hash)
930    )]
931    fn compute_trie_input(
932        &self,
933        parent_hash: B256,
934        state: &EngineApiTreeState<N>,
935        allocated_trie_input: Option<TrieInput>,
936    ) -> ProviderResult<(TrieInput, B256)> {
937        let mut input = allocated_trie_input.unwrap_or_default();
939
940        let (block_hash, blocks) =
941            state.tree_state.blocks_by_hash(parent_hash).unwrap_or_else(|| (parent_hash, vec![]));
942
943        if blocks.is_empty() {
944            debug!(target: "engine::tree::payload_validator", "Parent found on disk");
945        } else {
946            debug!(target: "engine::tree::payload_validator", historical = ?block_hash, blocks = blocks.len(), "Parent found in memory");
947        }
948
949        input.extend_with_blocks(
951            blocks.iter().rev().map(|block| (block.hashed_state(), block.trie_updates())),
952        );
953
954        Ok((input, block_hash))
955    }
956}
957
958pub type ValidationOutcome<N, E = InsertPayloadError<BlockTy<N>>> = Result<ExecutedBlock<N>, E>;
960
961#[derive(Debug, Clone, Copy, PartialEq, Eq)]
963enum StateRootStrategy {
964    StateRootTask,
966    Parallel,
968    Synchronous,
970}
971
972struct StateRootPlan {
974    strategy: StateRootStrategy,
976}
977
978pub trait EngineValidator<
982    Types: PayloadTypes,
983    N: NodePrimitives = <<Types as PayloadTypes>::BuiltPayload as BuiltPayload>::Primitives,
984>: Send + Sync + 'static
985{
986    fn validate_payload_attributes_against_header(
996        &self,
997        attr: &Types::PayloadAttributes,
998        header: &N::BlockHeader,
999    ) -> Result<(), InvalidPayloadAttributesError>;
1000
1001    fn ensure_well_formed_payload(
1010        &self,
1011        payload: Types::ExecutionData,
1012    ) -> Result<RecoveredBlock<N::Block>, NewPayloadError>;
1013
1014    fn validate_payload(
1016        &mut self,
1017        payload: Types::ExecutionData,
1018        ctx: TreeCtx<'_, N>,
1019    ) -> ValidationOutcome<N>;
1020
1021    fn validate_block(
1023        &mut self,
1024        block: RecoveredBlock<N::Block>,
1025        ctx: TreeCtx<'_, N>,
1026    ) -> ValidationOutcome<N>;
1027}
1028
1029impl<N, Types, P, Evm, V> EngineValidator<Types> for BasicEngineValidator<P, Evm, V>
1030where
1031    P: DatabaseProviderFactory<
1032            Provider: BlockReader + TrieReader + StageCheckpointReader + PruneCheckpointReader,
1033        > + BlockReader<Header = N::BlockHeader>
1034        + StateProviderFactory
1035        + StateReader
1036        + HashedPostStateProvider
1037        + Clone
1038        + 'static,
1039    N: NodePrimitives,
1040    V: PayloadValidator<Types, Block = N::Block>,
1041    Evm: ConfigureEngineEvm<Types::ExecutionData, Primitives = N> + 'static,
1042    Types: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
1043{
1044    fn validate_payload_attributes_against_header(
1045        &self,
1046        attr: &Types::PayloadAttributes,
1047        header: &N::BlockHeader,
1048    ) -> Result<(), InvalidPayloadAttributesError> {
1049        self.validator.validate_payload_attributes_against_header(attr, header)
1050    }
1051
1052    fn ensure_well_formed_payload(
1053        &self,
1054        payload: Types::ExecutionData,
1055    ) -> Result<RecoveredBlock<N::Block>, NewPayloadError> {
1056        let block = self.validator.ensure_well_formed_payload(payload)?;
1057        Ok(block)
1058    }
1059
1060    fn validate_payload(
1061        &mut self,
1062        payload: Types::ExecutionData,
1063        ctx: TreeCtx<'_, N>,
1064    ) -> ValidationOutcome<N> {
1065        self.validate_block_with_state(BlockOrPayload::Payload(payload), ctx)
1066    }
1067
1068    fn validate_block(
1069        &mut self,
1070        block: RecoveredBlock<N::Block>,
1071        ctx: TreeCtx<'_, N>,
1072    ) -> ValidationOutcome<N> {
1073        self.validate_block_with_state(BlockOrPayload::Block(block), ctx)
1074    }
1075}
1076
1077#[derive(Debug)]
1079pub enum BlockOrPayload<T: PayloadTypes> {
1080    Payload(T::ExecutionData),
1082    Block(RecoveredBlock<BlockTy<<T::BuiltPayload as BuiltPayload>::Primitives>>),
1084}
1085
1086impl<T: PayloadTypes> BlockOrPayload<T> {
1087    pub fn hash(&self) -> B256 {
1089        match self {
1090            Self::Payload(payload) => payload.block_hash(),
1091            Self::Block(block) => block.hash(),
1092        }
1093    }
1094
1095    pub fn num_hash(&self) -> NumHash {
1097        match self {
1098            Self::Payload(payload) => payload.num_hash(),
1099            Self::Block(block) => block.num_hash(),
1100        }
1101    }
1102
1103    pub fn parent_hash(&self) -> B256 {
1105        match self {
1106            Self::Payload(payload) => payload.parent_hash(),
1107            Self::Block(block) => block.parent_hash(),
1108        }
1109    }
1110
1111    pub fn block_with_parent(&self) -> BlockWithParent {
1113        match self {
1114            Self::Payload(payload) => payload.block_with_parent(),
1115            Self::Block(block) => block.block_with_parent(),
1116        }
1117    }
1118
1119    pub const fn type_name(&self) -> &'static str {
1121        match self {
1122            Self::Payload(_) => "payload",
1123            Self::Block(_) => "block",
1124        }
1125    }
1126}