Skip to content

Commit 539b4e4

Browse files
authored
feat: rm HashedStateChanges, introduce StorageWriter::write_hashed_state (paradigmxyz#9561)
1 parent fb6ea8b commit 539b4e4

File tree

6 files changed

+121
-136
lines changed

6 files changed

+121
-136
lines changed

crates/engine/tree/src/database.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use reth_db::database::Database;
88
use reth_errors::ProviderResult;
99
use reth_primitives::B256;
1010
use reth_provider::{
11-
bundle_state::HashedStateChanges, BlockExecutionWriter, BlockNumReader, BlockWriter,
12-
HistoryWriter, OriginalValuesKnown, ProviderFactory, StageCheckpointWriter, StateWriter,
11+
writer::StorageWriter, BlockExecutionWriter, BlockNumReader, BlockWriter, HistoryWriter,
12+
OriginalValuesKnown, ProviderFactory, StageCheckpointWriter, StateWriter,
1313
};
1414
use reth_prune::{Pruner, PrunerOutput};
1515
use reth_stages_types::{StageCheckpoint, StageId};
@@ -82,14 +82,16 @@ impl<DB: Database> DatabaseService<DB> {
8282
// Write state and changesets to the database.
8383
// Must be written after blocks because of the receipt lookup.
8484
let execution_outcome = block.execution_outcome().clone();
85+
// TODO: use single storage writer in task when sf / db tasks are combined
8586
execution_outcome.write_to_storage(&provider_rw, None, OriginalValuesKnown::No)?;
8687

8788
// insert hashes and intermediate merkle nodes
8889
{
8990
let trie_updates = block.trie_updates().clone();
9091
let hashed_state = block.hashed_state();
91-
HashedStateChanges(&hashed_state.clone().into_sorted())
92-
.write_to_db(&provider_rw)?;
92+
// TODO: use single storage writer in task when sf / db tasks are combined
93+
let storage_writer = StorageWriter::new(Some(&provider_rw), None);
94+
storage_writer.write_hashed_state(&hashed_state.clone().into_sorted())?;
9395
trie_updates.write_to_database(provider_rw.tx_ref())?;
9496
}
9597

crates/storage/provider/src/bundle_state/hashed_state_changes.rs

Lines changed: 0 additions & 123 deletions
This file was deleted.

crates/storage/provider/src/bundle_state/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
//! This module contains all the logic related to bundle state.
33
44
mod execution_outcome;
5-
mod hashed_state_changes;
65
mod state_changes;
76
mod state_reverts;
87

98
pub use execution_outcome::{AccountRevertInit, BundleStateInit, OriginalValuesKnown, RevertsInit};
10-
pub use hashed_state_changes::HashedStateChanges;
119
pub use state_changes::StateChanges;
1210
pub use state_reverts::{StateReverts, StorageRevertsIter};

crates/storage/provider/src/providers/database/provider.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::{
2-
bundle_state::{BundleStateInit, HashedStateChanges, RevertsInit},
2+
bundle_state::{BundleStateInit, RevertsInit},
33
providers::{database::metrics, static_file::StaticFileWriter, StaticFileProvider},
44
to_range,
55
traits::{
66
AccountExtReader, BlockSource, ChangeSetReader, ReceiptProvider, StageCheckpointWriter,
77
},
8+
writer::StorageWriter,
89
AccountReader, BlockExecutionReader, BlockExecutionWriter, BlockHashReader, BlockNumReader,
910
BlockReader, BlockWriter, EvmEnvProvider, FinalizedBlockReader, FinalizedBlockWriter,
1011
HashingWriter, HeaderProvider, HeaderSyncGap, HeaderSyncGapProvider, HistoricalStateProvider,
@@ -3307,7 +3308,8 @@ impl<DB: Database> BlockWriter for DatabaseProviderRW<DB> {
33073308

33083309
// insert hashes and intermediate merkle nodes
33093310
{
3310-
HashedStateChanges(&hashed_state).write_to_db(self)?;
3311+
let storage_writer = StorageWriter::new(Some(self), None);
3312+
storage_writer.write_hashed_state(&hashed_state)?;
33113313
trie_updates.write_to_database(&self.tx)?;
33123314
}
33133315
durations_recorder.record_relative(metrics::Action::InsertHashes);

crates/storage/provider/src/writer/mod.rs

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use crate::{providers::StaticFileProviderRWRefMut, DatabaseProviderRW};
2+
use itertools::Itertools;
23
use reth_db::{
3-
cursor::DbCursorRO,
4+
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
45
tables,
56
transaction::{DbTx, DbTxMut},
67
Database,
78
};
89
use reth_errors::{ProviderError, ProviderResult};
9-
use reth_primitives::BlockNumber;
10+
use reth_primitives::{BlockNumber, StorageEntry, U256};
1011
use reth_storage_api::ReceiptWriter;
1112
use reth_storage_errors::writer::StorageWriterError;
13+
use reth_trie::HashedPostStateSorted;
1214
use static_file::StaticFileWriter;
1315

1416
mod database;
@@ -93,6 +95,49 @@ impl<'a, 'b, DB: Database> StorageWriter<'a, 'b, DB> {
9395
Ok(())
9496
}
9597

98+
/// Writes the hashed state changes to the database
99+
pub fn write_hashed_state(&self, hashed_state: &HashedPostStateSorted) -> ProviderResult<()> {
100+
self.ensure_database_writer()?;
101+
102+
// Write hashed account updates.
103+
let mut hashed_accounts_cursor =
104+
self.database_writer().tx_ref().cursor_write::<tables::HashedAccounts>()?;
105+
for (hashed_address, account) in hashed_state.accounts().accounts_sorted() {
106+
if let Some(account) = account {
107+
hashed_accounts_cursor.upsert(hashed_address, account)?;
108+
} else if hashed_accounts_cursor.seek_exact(hashed_address)?.is_some() {
109+
hashed_accounts_cursor.delete_current()?;
110+
}
111+
}
112+
113+
// Write hashed storage changes.
114+
let sorted_storages = hashed_state.account_storages().iter().sorted_by_key(|(key, _)| *key);
115+
let mut hashed_storage_cursor =
116+
self.database_writer().tx_ref().cursor_dup_write::<tables::HashedStorages>()?;
117+
for (hashed_address, storage) in sorted_storages {
118+
if storage.is_wiped() && hashed_storage_cursor.seek_exact(*hashed_address)?.is_some() {
119+
hashed_storage_cursor.delete_current_duplicates()?;
120+
}
121+
122+
for (hashed_slot, value) in storage.storage_slots_sorted() {
123+
let entry = StorageEntry { key: hashed_slot, value };
124+
if let Some(db_entry) =
125+
hashed_storage_cursor.seek_by_key_subkey(*hashed_address, entry.key)?
126+
{
127+
if db_entry.key == entry.key {
128+
hashed_storage_cursor.delete_current()?;
129+
}
130+
}
131+
132+
if entry.value != U256::ZERO {
133+
hashed_storage_cursor.upsert(*hashed_address, entry)?;
134+
}
135+
}
136+
}
137+
138+
Ok(())
139+
}
140+
96141
/// Appends receipts block by block.
97142
///
98143
/// ATTENTION: If called from [`StorageWriter`] without a static file producer, it will always
@@ -155,3 +200,64 @@ impl<'a, 'b, DB: Database> StorageWriter<'a, 'b, DB> {
155200
Ok(())
156201
}
157202
}
203+
204+
#[cfg(test)]
205+
mod tests {
206+
use super::*;
207+
use crate::test_utils::create_test_provider_factory;
208+
use reth_db_api::transaction::DbTx;
209+
use reth_primitives::{keccak256, Account, Address, B256};
210+
use reth_trie::{HashedPostState, HashedStorage};
211+
212+
#[test]
213+
fn wiped_entries_are_removed() {
214+
let provider_factory = create_test_provider_factory();
215+
216+
let addresses = (0..10).map(|_| Address::random()).collect::<Vec<_>>();
217+
let destroyed_address = *addresses.first().unwrap();
218+
let destroyed_address_hashed = keccak256(destroyed_address);
219+
let slot = B256::with_last_byte(1);
220+
let hashed_slot = keccak256(slot);
221+
{
222+
let provider_rw = provider_factory.provider_rw().unwrap();
223+
let mut accounts_cursor =
224+
provider_rw.tx_ref().cursor_write::<tables::HashedAccounts>().unwrap();
225+
let mut storage_cursor =
226+
provider_rw.tx_ref().cursor_write::<tables::HashedStorages>().unwrap();
227+
228+
for address in addresses {
229+
let hashed_address = keccak256(address);
230+
accounts_cursor
231+
.insert(hashed_address, Account { nonce: 1, ..Default::default() })
232+
.unwrap();
233+
storage_cursor
234+
.insert(hashed_address, StorageEntry { key: hashed_slot, value: U256::from(1) })
235+
.unwrap();
236+
}
237+
provider_rw.commit().unwrap();
238+
}
239+
240+
let mut hashed_state = HashedPostState::default();
241+
hashed_state.accounts.insert(destroyed_address_hashed, None);
242+
hashed_state.storages.insert(destroyed_address_hashed, HashedStorage::new(true));
243+
244+
let provider_rw = provider_factory.provider_rw().unwrap();
245+
let storage_writer = StorageWriter::new(Some(&provider_rw), None);
246+
assert_eq!(storage_writer.write_hashed_state(&hashed_state.into_sorted()), Ok(()));
247+
provider_rw.commit().unwrap();
248+
249+
let provider = provider_factory.provider().unwrap();
250+
assert_eq!(
251+
provider.tx_ref().get::<tables::HashedAccounts>(destroyed_address_hashed),
252+
Ok(None)
253+
);
254+
assert_eq!(
255+
provider
256+
.tx_ref()
257+
.cursor_read::<tables::HashedStorages>()
258+
.unwrap()
259+
.seek_by_key_subkey(destroyed_address_hashed, hashed_slot),
260+
Ok(None)
261+
);
262+
}
263+
}

crates/trie/parallel/benches/root.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use proptest_arbitrary_interop::arb;
55
use rayon::ThreadPoolBuilder;
66
use reth_primitives::{Account, B256, U256};
77
use reth_provider::{
8-
bundle_state::HashedStateChanges, providers::ConsistentDbView,
9-
test_utils::create_test_provider_factory,
8+
providers::ConsistentDbView, test_utils::create_test_provider_factory, writer::StorageWriter,
109
};
1110
use reth_tasks::pool::BlockingTaskPool;
1211
use reth_trie::{
@@ -27,7 +26,8 @@ pub fn calculate_state_root(c: &mut Criterion) {
2726
let provider_factory = create_test_provider_factory();
2827
{
2928
let provider_rw = provider_factory.provider_rw().unwrap();
30-
HashedStateChanges(&db_state.into_sorted()).write_to_db(&provider_rw).unwrap();
29+
let storage_writer = StorageWriter::new(Some(&provider_rw), None);
30+
storage_writer.write_hashed_state(&db_state.into_sorted()).unwrap();
3131
let (_, updates) =
3232
StateRoot::from_tx(provider_rw.tx_ref()).root_with_updates().unwrap();
3333
updates.write_to_database(provider_rw.tx_ref()).unwrap();

0 commit comments

Comments
 (0)