From b49129a6d2061d27d25d04a2975f119bd33864c3 Mon Sep 17 00:00:00 2001 From: Solar Mithril Date: Fri, 1 Aug 2025 21:16:41 +0500 Subject: [PATCH 1/4] WIP right now it has consensus error 2025-08-01T16:10:44.956046Z ERROR engine::persistence: Persistence service failed err=ProviderError(Database(Write(DatabaseWriteError { info: DatabaseErrorInfo { message: "the given key value is mismatched to the current cursor position", code: -30418 }, operation: CursorAppendDup, table_name: "AccountChangeSets", key: [0, 0, 0, 0, 0, 0, 0, 9] }))) --- crates/op-rbuilder/src/builders/flashblocks/payload.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 24107313..0331f276 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -48,6 +48,9 @@ use std::{ }, time::Instant, }; +use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates}; +use reth_optimism_payload_builder::OpPayloadPrimitives; +use reth_primitives_traits::RecoveredBlock; use tokio::sync::{ mpsc, mpsc::{error::SendError, Sender}, From c8c782d4a0bc5c475b9857e945bd40b96f5e4f3a Mon Sep 17 00:00:00 2001 From: Solar Mithril Date: Mon, 4 Aug 2025 13:45:32 +0500 Subject: [PATCH 2/4] fmt --- crates/op-rbuilder/src/builders/flashblocks/payload.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 0331f276..c2d359af 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -33,7 +33,9 @@ use reth_provider::{ StorageRootProvider, }; use reth_revm::{ - database::StateProviderDatabase, db::states::bundle_state::BundleRetention, State, + database::StateProviderDatabase, + db::states::bundle_state::BundleRetention, + State, }; use revm::Database; use rollup_boost::{ @@ -48,9 +50,6 @@ use std::{ }, time::Instant, }; -use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates}; -use reth_optimism_payload_builder::OpPayloadPrimitives; -use reth_primitives_traits::RecoveredBlock; use tokio::sync::{ mpsc, mpsc::{error::SendError, Sender}, From a60ebd2f27c206cc0efafdf369de966f6fbb05f3 Mon Sep 17 00:00:00 2001 From: Ash Kunda <18058966+akundaz@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:52:24 -0400 Subject: [PATCH 3/4] cache reth db reads in flashblocks payload generation --- crates/op-rbuilder/src/builders/flashblocks/payload.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index c2d359af..2b7aae9a 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -195,9 +195,9 @@ where ) -> Result<(), PayloadBuilderError> { let block_build_start_time = Instant::now(); let BuildArguments { + mut cached_reads, config, cancel: block_cancel, - .. } = args; // We log only every 100th block to reduce usage @@ -266,7 +266,7 @@ where // 1. execute the pre steps and seal an early block with that let sequencer_tx_start_time = Instant::now(); let mut state = State::builder() - .with_database(db) + .with_database(cached_reads.as_db_mut(db)) .with_bundle_update() .build(); From 26ef5bd92e7d7b1551606dde62b6168a9bd12e45 Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 1 Aug 2025 18:38:15 +1000 Subject: [PATCH 4/4] save tip to cache on new committed state --- .../src/builders/flashblocks/payload.rs | 4 +- crates/op-rbuilder/src/builders/generator.rs | 55 +++++++++++++++++-- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 2b7aae9a..38a88178 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -33,9 +33,7 @@ use reth_provider::{ StorageRootProvider, }; use reth_revm::{ - database::StateProviderDatabase, - db::states::bundle_state::BundleRetention, - State, + database::StateProviderDatabase, db::states::bundle_state::BundleRetention, State, }; use revm::Database; use rollup_boost::{ diff --git a/crates/op-rbuilder/src/builders/generator.rs b/crates/op-rbuilder/src/builders/generator.rs index 2132d105..8b62d2c5 100644 --- a/crates/op-rbuilder/src/builders/generator.rs +++ b/crates/op-rbuilder/src/builders/generator.rs @@ -1,15 +1,19 @@ +use alloy_primitives::B256; use futures_util::{Future, FutureExt}; use reth::{ providers::{BlockReaderIdExt, StateProviderFactory}, tasks::TaskSpawner, }; -use reth_basic_payload_builder::{BasicPayloadJobGeneratorConfig, HeaderForPayload, PayloadConfig}; -use reth_node_api::{PayloadBuilderAttributes, PayloadKind}; +use reth_basic_payload_builder::{ + BasicPayloadJobGeneratorConfig, HeaderForPayload, PayloadConfig, PrecachedState, +}; +use reth_node_api::{NodePrimitives, PayloadBuilderAttributes, PayloadKind}; use reth_payload_builder::{ KeepPayloadJobAlive, PayloadBuilderError, PayloadJob, PayloadJobGenerator, }; use reth_payload_primitives::BuiltPayload; use reth_primitives_traits::HeaderTy; +use reth_provider::CanonStateNotification; use reth_revm::cached::CachedReads; use std::{ sync::{Arc, Mutex}, @@ -74,6 +78,8 @@ pub struct BlockPayloadJobGenerator { last_payload: Arc>, /// The extra block deadline in seconds extra_block_deadline: std::time::Duration, + /// Stored `cached_reads` for new payload jobs. + pre_cached: Option, } // === impl EmptyBlockPayloadJobGenerator === @@ -97,8 +103,18 @@ impl BlockPayloadJobGenerator { ensure_only_one_payload, last_payload: Arc::new(Mutex::new(CancellationToken::new())), extra_block_deadline, + pre_cached: None, } } + + /// Returns the pre-cached reads for the given parent header if it matches the cached state's + /// block. + fn maybe_pre_cached(&self, parent: B256) -> Option { + self.pre_cached + .as_ref() + .filter(|pc| pc.block == parent) + .map(|pc| pc.cached.clone()) + } } impl PayloadJobGenerator @@ -182,12 +198,38 @@ where cancel: cancel_token, deadline, build_complete: None, + cached_reads: self.maybe_pre_cached(parent_header.hash()), }; job.spawn_build_job(); Ok(job) } + + fn on_new_state(&mut self, new_state: CanonStateNotification) { + let mut cached = CachedReads::default(); + + // extract the state from the notification and put it into the cache + let committed = new_state.committed(); + let new_execution_outcome = committed.execution_outcome(); + for (addr, acc) in new_execution_outcome.bundle_accounts_iter() { + if let Some(info) = acc.info.clone() { + // we want pre cache existing accounts and their storage + // this only includes changed accounts and storage but is better than nothing + let storage = acc + .storage + .iter() + .map(|(key, slot)| (*key, slot.present_value)) + .collect(); + cached.insert_account(addr, info, storage); + } + } + + self.pre_cached = Some(PrecachedState { + block: committed.tip().hash(), + cached, + }); + } } use std::{ @@ -214,6 +256,11 @@ where pub(crate) cancel: CancellationToken, pub(crate) deadline: Pin>, // Add deadline pub(crate) build_complete: Option>>, + /// Caches all disk reads for the state the new payloads builds on + /// + /// This is used to avoid reading the same state over and over again when new attempts are + /// triggered, because during the building process we'll repeatedly execute the transactions. + pub(crate) cached_reads: Option, } impl PayloadJob for BlockPayloadJob @@ -274,10 +321,10 @@ where let (tx, rx) = oneshot::channel(); self.build_complete = Some(rx); - + let cached_reads = self.cached_reads.take().unwrap_or_default(); self.executor.spawn_blocking(Box::pin(async move { let args = BuildArguments { - cached_reads: Default::default(), + cached_reads, config: payload_config, cancel, };