1use alloy_primitives::B256;
2use reth_db_api::{
3 cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
4 table::{DupSort, Key, Table, Value},
5 tables::{self, PackedAccountsTrie, PackedStoragesTrie},
6 transaction::DbTx,
7 DatabaseError,
8};
9use reth_trie::{
10 trie_cursor::{TrieCursor, TrieCursorFactory, TrieStorageCursor},
11 updates::StorageTrieUpdatesSorted,
12 BranchNodeCompact, Nibbles, PackedStorageTrieEntry, PackedStoredNibbles,
13 PackedStoredNibblesSubKey, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey,
14};
15use std::marker::PhantomData;
16
17pub trait TrieKeyAdapter: Clone + Send + Sync + 'static {
24 type AccountKey: Key + From<Nibbles>;
26
27 type StorageSubKey: Key + From<Nibbles>;
30
31 type StorageValue: Value + StorageTrieEntryLike<SubKey = Self::StorageSubKey>;
33
34 fn account_key_to_nibbles(key: &Self::AccountKey) -> Nibbles;
36
37 fn subkey_to_nibbles(subkey: &Self::StorageSubKey) -> Nibbles;
39}
40
41pub trait StorageTrieEntryLike: Sized {
46 type SubKey: Clone;
48
49 fn nibbles(&self) -> &Self::SubKey;
51
52 fn node(&self) -> &BranchNodeCompact;
54
55 fn into_parts(self) -> (Self::SubKey, BranchNodeCompact);
57
58 fn new(nibbles: Self::SubKey, node: BranchNodeCompact) -> Self;
60}
61
62impl StorageTrieEntryLike for StorageTrieEntry {
63 type SubKey = StoredNibblesSubKey;
64
65 fn nibbles(&self) -> &Self::SubKey {
66 &self.nibbles
67 }
68
69 fn node(&self) -> &BranchNodeCompact {
70 &self.node
71 }
72
73 fn into_parts(self) -> (Self::SubKey, BranchNodeCompact) {
74 (self.nibbles, self.node)
75 }
76
77 fn new(nibbles: Self::SubKey, node: BranchNodeCompact) -> Self {
78 Self { nibbles, node }
79 }
80}
81
82#[derive(Debug, Clone)]
84pub struct LegacyKeyAdapter;
85
86impl TrieKeyAdapter for LegacyKeyAdapter {
87 type AccountKey = StoredNibbles;
88 type StorageSubKey = StoredNibblesSubKey;
89 type StorageValue = StorageTrieEntry;
90
91 fn account_key_to_nibbles(key: &Self::AccountKey) -> Nibbles {
92 key.0
93 }
94
95 fn subkey_to_nibbles(subkey: &Self::StorageSubKey) -> Nibbles {
96 subkey.0
97 }
98}
99
100impl StorageTrieEntryLike for PackedStorageTrieEntry {
101 type SubKey = PackedStoredNibblesSubKey;
102
103 fn nibbles(&self) -> &Self::SubKey {
104 &self.nibbles
105 }
106
107 fn node(&self) -> &BranchNodeCompact {
108 &self.node
109 }
110
111 fn into_parts(self) -> (Self::SubKey, BranchNodeCompact) {
112 (self.nibbles, self.node)
113 }
114
115 fn new(nibbles: Self::SubKey, node: BranchNodeCompact) -> Self {
116 Self { nibbles, node }
117 }
118}
119
120#[derive(Debug, Clone)]
122pub struct PackedKeyAdapter;
123
124impl TrieKeyAdapter for PackedKeyAdapter {
125 type AccountKey = PackedStoredNibbles;
126 type StorageSubKey = PackedStoredNibblesSubKey;
127 type StorageValue = PackedStorageTrieEntry;
128
129 fn account_key_to_nibbles(key: &Self::AccountKey) -> Nibbles {
130 key.0
131 }
132
133 fn subkey_to_nibbles(subkey: &Self::StorageSubKey) -> Nibbles {
134 subkey.0
135 }
136}
137
138pub trait TrieTableAdapter: TrieKeyAdapter {
142 type AccountTrieTable: Table<Key = Self::AccountKey, Value = BranchNodeCompact>;
144 type StorageTrieTable: Table<Key = B256, Value = Self::StorageValue>
146 + DupSort<SubKey = Self::StorageSubKey>;
147}
148
149impl TrieTableAdapter for LegacyKeyAdapter {
150 type AccountTrieTable = tables::AccountsTrie;
151 type StorageTrieTable = tables::StoragesTrie;
152}
153
154impl TrieTableAdapter for PackedKeyAdapter {
155 type AccountTrieTable = PackedAccountsTrie;
156 type StorageTrieTable = PackedStoragesTrie;
157}
158
159#[derive(Debug, Clone)]
161pub struct DatabaseTrieCursorFactory<T, A: TrieKeyAdapter> {
162 tx: T,
163 _adapter: PhantomData<A>,
164}
165
166impl<T, A: TrieKeyAdapter> DatabaseTrieCursorFactory<T, A> {
167 pub const fn new(tx: T) -> Self {
169 Self { tx, _adapter: PhantomData }
170 }
171}
172
173impl<TX, A> TrieCursorFactory for DatabaseTrieCursorFactory<&TX, A>
174where
175 TX: DbTx,
176 A: TrieTableAdapter,
177{
178 type AccountTrieCursor<'a>
179 = DatabaseAccountTrieCursor<<TX as DbTx>::Cursor<A::AccountTrieTable>, A>
180 where
181 Self: 'a;
182
183 type StorageTrieCursor<'a>
184 = DatabaseStorageTrieCursor<<TX as DbTx>::DupCursor<A::StorageTrieTable>, A>
185 where
186 Self: 'a;
187
188 fn account_trie_cursor(&self) -> Result<Self::AccountTrieCursor<'_>, DatabaseError> {
189 Ok(DatabaseAccountTrieCursor::new(self.tx.cursor_read::<A::AccountTrieTable>()?))
190 }
191
192 fn storage_trie_cursor(
193 &self,
194 hashed_address: B256,
195 ) -> Result<Self::StorageTrieCursor<'_>, DatabaseError> {
196 Ok(DatabaseStorageTrieCursor::new(
197 self.tx.cursor_dup_read::<A::StorageTrieTable>()?,
198 hashed_address,
199 ))
200 }
201}
202
203#[derive(Debug)]
205pub struct DatabaseAccountTrieCursor<C, A: TrieKeyAdapter>(C, PhantomData<A>);
206
207impl<C, A: TrieKeyAdapter> DatabaseAccountTrieCursor<C, A> {
208 pub const fn new(cursor: C) -> Self {
210 Self(cursor, PhantomData)
211 }
212}
213
214impl<C, A> TrieCursor for DatabaseAccountTrieCursor<C, A>
215where
216 A: TrieTableAdapter,
217 C: DbCursorRO<A::AccountTrieTable> + Send,
218{
219 fn seek_exact(
220 &mut self,
221 key: Nibbles,
222 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
223 Ok(self
224 .0
225 .seek_exact(A::AccountKey::from(key))?
226 .map(|value| (A::account_key_to_nibbles(&value.0), value.1)))
227 }
228
229 fn seek(
230 &mut self,
231 key: Nibbles,
232 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
233 Ok(self
234 .0
235 .seek(A::AccountKey::from(key))?
236 .map(|value| (A::account_key_to_nibbles(&value.0), value.1)))
237 }
238
239 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
240 Ok(self.0.next()?.map(|value| (A::account_key_to_nibbles(&value.0), value.1)))
241 }
242
243 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
244 Ok(self.0.current()?.map(|(k, _)| A::account_key_to_nibbles(&k)))
245 }
246
247 fn reset(&mut self) {
248 }
250}
251
252#[derive(Debug)]
254pub struct DatabaseStorageTrieCursor<C, A: TrieKeyAdapter> {
255 pub cursor: C,
257 hashed_address: B256,
259 _adapter: PhantomData<A>,
260}
261
262impl<C, A: TrieKeyAdapter> DatabaseStorageTrieCursor<C, A> {
263 pub const fn new(cursor: C, hashed_address: B256) -> Self {
265 Self { cursor, hashed_address, _adapter: PhantomData }
266 }
267}
268
269impl<C, A> DatabaseStorageTrieCursor<C, A>
270where
271 A: TrieTableAdapter,
272 C: DbCursorRO<A::StorageTrieTable>
273 + DbCursorRW<A::StorageTrieTable>
274 + DbDupCursorRO<A::StorageTrieTable>
275 + DbDupCursorRW<A::StorageTrieTable>,
276{
277 pub fn write_storage_trie_updates_sorted(
279 &mut self,
280 updates: &StorageTrieUpdatesSorted,
281 ) -> Result<usize, DatabaseError> {
282 let mut num_entries = 0;
283 for (nibbles, maybe_updated) in updates.storage_nodes.iter().filter(|(n, _)| !n.is_empty())
284 {
285 num_entries += 1;
286 let nibbles = A::StorageSubKey::from(*nibbles);
287 if self
289 .cursor
290 .seek_by_key_subkey(self.hashed_address, nibbles.clone())?
291 .as_ref()
292 .is_some_and(|e| *e.nibbles() == nibbles)
293 {
294 self.cursor.delete_current()?;
295 }
296
297 if let Some(node) = maybe_updated {
299 self.cursor
300 .upsert(self.hashed_address, &A::StorageValue::new(nibbles, node.clone()))?;
301 }
302 }
303
304 Ok(num_entries)
305 }
306}
307
308impl<C, A> TrieCursor for DatabaseStorageTrieCursor<C, A>
309where
310 A: TrieTableAdapter,
311 C: DbCursorRO<A::StorageTrieTable> + DbDupCursorRO<A::StorageTrieTable> + Send,
312{
313 fn seek_exact(
314 &mut self,
315 key: Nibbles,
316 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
317 let subkey = A::StorageSubKey::from(key);
318 Ok(self
319 .cursor
320 .seek_by_key_subkey(self.hashed_address, subkey.clone())?
321 .filter(|e| *e.nibbles() == subkey)
322 .map(|value| {
323 let (subkey, node) = value.into_parts();
324 (A::subkey_to_nibbles(&subkey), node)
325 }))
326 }
327
328 fn seek(
329 &mut self,
330 key: Nibbles,
331 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
332 Ok(self.cursor.seek_by_key_subkey(self.hashed_address, A::StorageSubKey::from(key))?.map(
333 |value| {
334 let (subkey, node) = value.into_parts();
335 (A::subkey_to_nibbles(&subkey), node)
336 },
337 ))
338 }
339
340 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
341 Ok(self.cursor.next_dup()?.map(|(_, value)| {
342 let (subkey, node) = value.into_parts();
343 (A::subkey_to_nibbles(&subkey), node)
344 }))
345 }
346
347 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
348 Ok(self.cursor.current()?.map(|(_, v)| A::subkey_to_nibbles(v.nibbles())))
349 }
350
351 fn reset(&mut self) {
352 }
354}
355
356impl<C, A> TrieStorageCursor for DatabaseStorageTrieCursor<C, A>
357where
358 A: TrieTableAdapter,
359 C: DbCursorRO<A::StorageTrieTable> + DbDupCursorRO<A::StorageTrieTable> + Send,
360{
361 fn set_hashed_address(&mut self, hashed_address: B256) {
362 self.hashed_address = hashed_address;
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369 use alloy_primitives::hex_literal::hex;
370 use reth_db_api::{cursor::DbCursorRW, transaction::DbTxMut};
371 use reth_provider::test_utils::create_test_provider_factory;
372
373 #[test]
374 fn test_account_trie_order() {
375 let factory = create_test_provider_factory();
376 let provider = factory.provider_rw().unwrap();
377 let mut cursor = provider.tx_ref().cursor_write::<tables::AccountsTrie>().unwrap();
378
379 let data = vec![
380 hex!("0303040e").to_vec(),
381 hex!("030305").to_vec(),
382 hex!("03030500").to_vec(),
383 hex!("0303050a").to_vec(),
384 ];
385
386 for key in data.clone() {
387 cursor
388 .upsert(
389 key.into(),
390 &BranchNodeCompact::new(
391 0b0000_0010_0000_0001,
392 0b0000_0010_0000_0001,
393 0,
394 Vec::default(),
395 None,
396 ),
397 )
398 .unwrap();
399 }
400
401 let db_data = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
402 assert_eq!(db_data[0].0 .0.to_vec(), data[0]);
403 assert_eq!(db_data[1].0 .0.to_vec(), data[1]);
404 assert_eq!(db_data[2].0 .0.to_vec(), data[2]);
405 assert_eq!(db_data[3].0 .0.to_vec(), data[3]);
406
407 assert_eq!(
408 cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.0.to_vec()),
409 Some(data[1].clone())
410 );
411 }
412
413 #[test]
415 fn test_storage_cursor_abstraction() {
416 use reth_storage_api::StorageSettingsCache;
417 use reth_trie::trie_cursor::{TrieCursor, TrieCursorFactory};
418
419 let factory = create_test_provider_factory();
420 let provider = factory.provider_rw().unwrap();
421 let mut cursor = provider.tx_ref().cursor_dup_write::<tables::StoragesTrie>().unwrap();
422
423 let hashed_address = B256::random();
424 let key = StoredNibblesSubKey::from(vec![0x2, 0x3]);
425 let value = BranchNodeCompact::new(1, 1, 1, vec![B256::random()], None);
426
427 cursor
428 .upsert(hashed_address, &StorageTrieEntry { nibbles: key.clone(), node: value.clone() })
429 .unwrap();
430
431 crate::with_adapter!(provider, |A| {
432 let trie_factory = DatabaseTrieCursorFactory::<_, A>::new(provider.tx_ref());
433 let mut cursor = trie_factory.storage_trie_cursor(hashed_address).unwrap();
434 assert_eq!(cursor.seek(key.into()).unwrap().unwrap().1, value);
435 });
436 }
437}