Skip to content
This repository was archived by the owner on Jan 12, 2026. It is now read-only.

Commit 859ca5a

Browse files
committed
fix: lazy database should be first interface
1 parent 2359b6a commit 859ca5a

3 files changed

Lines changed: 36 additions & 43 deletions

File tree

crates/op-rbuilder/src/block_stm/db_adapter.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -382,19 +382,13 @@ where
382382
did_exist = true;
383383
}
384384
ReadResult::Value {
385-
value: EvmStateValue::BalanceIncrement(increment),
385+
value: EvmStateValue::BalanceIncrement(_),
386386
version,
387387
} => {
388-
// BalanceIncrement is a delta that should be added to the base balance.
389-
// We read the base balance and add the increment.
390-
// Track dependency on the increment write for conflict detection.
391-
self.add_to_reads(
392-
balance_key.clone(),
393-
EvmStateValue::BalanceIncrement(increment),
394-
Some(version),
395-
);
396-
base_info.balance = base_info.balance.saturating_add(increment);
397-
did_exist = true;
388+
// Reading a balance increment counts as an aborted read since we can't know all previous increments
389+
return Err(VersionedDbError::ReadAborted {
390+
aborted_txn_idx: version.txn_idx,
391+
});
398392
}
399393
ReadResult::Value { value, version } => {
400394
return Err(VersionedDbError::InvalidValue {

crates/op-rbuilder/src/block_stm/executor.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,21 @@ impl StateWithIncrements {
5454
pub fn resolve_state<DB: Database>(self, db: &mut DB) -> Result<EvmState, DB::Error> {
5555
let mut state = self.loaded_state;
5656
for (addr, delta) in self.pending_balance_increments.iter() {
57-
match state.entry(*addr) {
58-
Entry::Occupied(mut entry) => {
59-
entry.get_mut().info.balance = entry.get().info.balance.saturating_add(*delta);
60-
}
61-
Entry::Vacant(entry) => {
62-
let mut account = db.basic(*addr)?.unwrap_or_default();
63-
account.balance = account.balance.saturating_add(*delta);
64-
let mut account: Account = account.into();
65-
account.mark_touch();
66-
entry.insert(account);
67-
}
57+
let mut account = db.basic(*addr)?.unwrap_or_default();
58+
// if loaded_state also has an entry and it doesn't match the account from the database, we have a problem
59+
// Addresses in pending_balance_increments must not be in loaded_state
60+
if let Some(loaded_account) = state.get(addr) {
61+
debug_assert_eq!(
62+
loaded_account.info, account,
63+
"Address {} in pending_balance_increments must not be in loaded_state",
64+
addr
65+
);
6866
}
67+
68+
account.balance = account.balance.saturating_add(*delta);
69+
let mut account: Account = account.into();
70+
account.mark_touch();
71+
state.insert(*addr, account);
6972
}
7073
Ok(state)
7174
}
@@ -105,7 +108,7 @@ impl<
105108
pub fn execute_single_tx<
106109
ExecuteTxFn: (Fn(
107110
&Recovered<op_alloy_consensus::OpTxEnvelope>,
108-
&mut State<LazyDatabaseWrapper<VersionedDatabase<'_, DB>>>,
111+
&mut LazyDatabaseWrapper<State<VersionedDatabase<'_, DB>>>,
109112
&HashSet<EvmStateKey>,
110113
Option<&TxExecutionResult>,
111114
u64,
@@ -140,11 +143,11 @@ impl<
140143
Arc::clone(&self.shared_code_cache),
141144
);
142145

143-
// Wrap with LazyDatabaseWrapper to track balance increments (fee payments)
144-
let lazy_db = LazyDatabaseWrapper::new(versioned_db);
145-
146146
// Create State wrapper for EVM execution
147-
let mut tx_state = State::builder().with_database(lazy_db).build();
147+
let tx_state = State::builder().with_database(versioned_db).build();
148+
149+
// Wrap with LazyDatabaseWrapper to track balance increments (fee payments)
150+
let mut tx_state = LazyDatabaseWrapper::new(tx_state);
148151

149152
// Detect conflicting keys for re-executions (incarnation > 0)
150153
// Conflicting keys are reads whose versions changed since the previous execution
@@ -204,10 +207,11 @@ impl<
204207
let mut write_set = WriteSet::new();
205208

206209
// Extract pending balance increments from LazyDatabaseWrapper
207-
let pending_balance_increments = tx_state.database.pending_increments();
210+
// These are balances that were incremented only and never read otherwise.
211+
let pending_balance_increments = tx_state.pending_increments();
208212

209213
// Get read set and captured reads from inner VersionedDatabase
210-
let versioned_db = tx_state.database.inner_mut();
214+
let versioned_db = &mut tx_state.inner_mut().database;
211215
let read_set = versioned_db.take_read_set();
212216
let captured_reads = versioned_db.take_captured_reads();
213217

@@ -335,7 +339,7 @@ impl<
335339
"Read aborted for transaction"
336340
);
337341

338-
let read_set = tx_state.database.inner_mut().take_read_set();
342+
let read_set = tx_state.inner_mut().database.take_read_set();
339343
return (
340344
read_set,
341345
Default::default(),
@@ -367,7 +371,7 @@ impl<
367371
"Error executing transaction"
368372
);
369373

370-
let read_set = tx_state.database.inner_mut().take_read_set();
374+
let read_set = tx_state.inner_mut().database.take_read_set();
371375
return (
372376
read_set,
373377
Default::default(),
@@ -391,7 +395,7 @@ impl<
391395
pub fn execute_transactions_parallel<
392396
ExecuteTxFn: (Fn(
393397
&Recovered<op_alloy_consensus::OpTxEnvelope>,
394-
&mut State<LazyDatabaseWrapper<VersionedDatabase<'_, DB>>>,
398+
&mut LazyDatabaseWrapper<State<VersionedDatabase<'_, DB>>>,
395399
&HashSet<EvmStateKey>,
396400
Option<&TxExecutionResult>,
397401
u64,

crates/op-rbuilder/src/builders/context.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -959,13 +959,13 @@ impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx, OpEvmFactory> {
959959

960960
// 1. Read BlockResourceUsed increments from other txs BEFORE execution to detect conflicts earlier
961961
// These are increments from other transactions within this flashblock (not the base from sequencer)
962-
let gas_increment = state.database.inner_mut()
962+
let gas_increment = state.inner_mut().database
963963
.read_block_resource(BlockResourceType::Gas)?;
964-
let da_increment = state.database.inner_mut()
964+
let da_increment = state.inner_mut().database
965965
.read_block_resource(BlockResourceType::DABytes)?;
966966

967967
// Read AddressGasUsed for this address to check rate limiting
968-
let address_gas_used = state.database.inner_mut()
968+
let address_gas_used = state.inner_mut().database
969969
.read_address_gas_used(tx.signer())?;
970970

971971
trace!(
@@ -1103,15 +1103,15 @@ impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx, OpEvmFactory> {
11031103
new_gas_cumulative, new_da_cumulative, new_address_gas_cumulative, gas_increment, tx_gas_used, da_increment, tx_da_size, address_gas_used, tx_gas_used
11041104
);
11051105

1106-
state.database.inner_mut().write_block_resource(
1106+
state.inner_mut().database.write_block_resource(
11071107
BlockResourceType::Gas,
11081108
new_gas_cumulative
11091109
)?;
1110-
state.database.inner_mut().write_block_resource(
1110+
state.inner_mut().database.write_block_resource(
11111111
BlockResourceType::DABytes,
11121112
new_da_cumulative
11131113
)?;
1114-
state.database.inner_mut().write_address_gas_used(
1114+
state.inner_mut().database.write_address_gas_used(
11151115
tx.signer(),
11161116
new_address_gas_cumulative
11171117
)?;
@@ -1178,11 +1178,6 @@ impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx, OpEvmFactory> {
11781178
for (address, _) in resolved_state.iter() {
11791179
let _ = db.load_cache_account(*address);
11801180
}
1181-
for address in pending_balance_addrs {
1182-
if !resolved_state.contains_key(&address) {
1183-
let _ = db.load_cache_account(address);
1184-
}
1185-
}
11861181

11871182
let mut mock_db = MockDB;
11881183
let evm = self

0 commit comments

Comments
 (0)