Skip to content

feat(flashblocks): Decompose build_pending_state into Testable Components #311

@refcell

Description

@refcell

Summary

Break the large build_pending_state method into distinct, unit-testable components.

Problem

StateProcessor::build_pending_state() conflates 4 concerns:

  1. Block reconstruction from flashblocks
  2. EVM context initialization
  3. Parallel sender recovery
  4. Sequential transaction execution

This makes it impossible to test any piece in isolation.

Solution

Extract into separate components:

1. BlockAssembler - Reconstructs blocks from flashblocks

pub struct AssembledBlock {
    pub block: OpBlock,
    pub base: FlashblockBase,
    pub flashblocks: Vec<Flashblock>,
    pub header: Sealed<Header>,
}

impl BlockAssembler {
    pub fn assemble(flashblocks: &[&Flashblock]) -> eyre::Result<AssembledBlock>;
}

2. SenderRecoveryService - Parallel ECDSA with caching

impl SenderRecoveryService {
    pub fn recover_all(
        transactions: impl IntoIterator<Item = &OpTxEnvelope>,
        cached_senders: &HashMap<B256, Address>,
    ) -> eyre::Result<Vec<(OpTxEnvelope, Address)>>;
}

3. BlockExecutor trait - Prep for future EVM mocking

pub trait BlockExecutor {
    fn execute_transaction(
        &mut self,
        index: usize,
        transaction: Recovered<OpTxEnvelope>,
    ) -> eyre::Result<ExecutedTransaction>;
}

Refactored build_pending_state

fn build_pending_state(&self, prev: Option<Arc<PendingBlocks>>, flashblocks: &[Flashblock]) -> Result<...> {
    let grouped = Self::group_by_block_number(flashblocks);
    let mut builder = PendingBlocksBuilder::new();

    for (_block_num, fbs) in grouped {
        let assembled = BlockAssembler::assemble(&fbs)?;
        let txs_with_senders = SenderRecoveryService::recover_all(&assembled.block.body.transactions, &cached)?;
        let executed = self.execute_block(&assembled, txs_with_senders, ...)?;

        for tx in executed {
            builder.with_executed_transaction(tx.rpc_tx, tx.receipt, tx.evm_state, tx.sender);
        }
    }

    Ok(Some(Arc::new(builder.build()?)))
}

Acceptance

  • BlockAssembler unit tests (single/multiple flashblocks, missing base)
  • SenderRecoveryService unit tests (cache hits, cache misses)
  • Integration tests still pass

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-clientArea: client crates

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions