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 {
23 type AccountKey: Key + From<Nibbles> + Clone;
25
26 type StorageSubKey: Key + From<Nibbles> + Clone + PartialEq;
29
30 type StorageValue: Value + StorageTrieEntryLike<SubKey = Self::StorageSubKey>;
32
33 fn account_key_to_nibbles(key: &Self::AccountKey) -> Nibbles;
35
36 fn subkey_to_nibbles(subkey: &Self::StorageSubKey) -> Nibbles;
38}
39
40pub 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 {
144 type AccountTrieTable: Table<Key = Self::AccountKey, Value = BranchNodeCompact>;
146 type StorageTrieTable: Table<Key = B256, Value = Self::StorageValue>
148 + DupSort<SubKey = Self::StorageSubKey>;
149}
150
151impl TrieTableAdapter for LegacyKeyAdapter {
152 type AccountTrieTable = tables::AccountsTrie;
153 type StorageTrieTable = tables::StoragesTrie;
154}
155
156impl TrieTableAdapter for PackedKeyAdapter {
157 type AccountTrieTable = PackedAccountsTrie;
158 type StorageTrieTable = PackedStoragesTrie;
159}
160
161#[derive(Debug, Clone)]
163pub struct DatabaseTrieCursorFactory<T, A: TrieKeyAdapter> {
164 tx: T,
165 _adapter: PhantomData<A>,
166}
167
168impl<T, A: TrieKeyAdapter> DatabaseTrieCursorFactory<T, A> {
169 pub const fn new(tx: T) -> Self {
171 Self { tx, _adapter: PhantomData }
172 }
173}
174
175impl<TX, A> TrieCursorFactory for DatabaseTrieCursorFactory<&TX, A>
176where
177 TX: DbTx,
178 A: TrieTableAdapter,
179{
180 type AccountTrieCursor<'a>
181 = DatabaseAccountTrieCursor<<TX as DbTx>::Cursor<A::AccountTrieTable>, A>
182 where
183 Self: 'a;
184
185 type StorageTrieCursor<'a>
186 = DatabaseStorageTrieCursor<<TX as DbTx>::DupCursor<A::StorageTrieTable>, A>
187 where
188 Self: 'a;
189
190 fn account_trie_cursor(&self) -> Result<Self::AccountTrieCursor<'_>, DatabaseError> {
191 Ok(DatabaseAccountTrieCursor::new(self.tx.cursor_read::<A::AccountTrieTable>()?))
192 }
193
194 fn storage_trie_cursor(
195 &self,
196 hashed_address: B256,
197 ) -> Result<Self::StorageTrieCursor<'_>, DatabaseError> {
198 Ok(DatabaseStorageTrieCursor::new(
199 self.tx.cursor_dup_read::<A::StorageTrieTable>()?,
200 hashed_address,
201 ))
202 }
203}
204
205#[derive(Debug)]
207pub struct DatabaseAccountTrieCursor<C, A: TrieKeyAdapter>(C, PhantomData<A>);
208
209impl<C, A: TrieKeyAdapter> DatabaseAccountTrieCursor<C, A> {
210 pub const fn new(cursor: C) -> Self {
212 Self(cursor, PhantomData)
213 }
214}
215
216impl<C, A> TrieCursor for DatabaseAccountTrieCursor<C, A>
217where
218 A: TrieTableAdapter,
219 C: DbCursorRO<A::AccountTrieTable> + Send,
220{
221 fn seek_exact(
222 &mut self,
223 key: Nibbles,
224 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
225 Ok(self
226 .0
227 .seek_exact(A::AccountKey::from(key))?
228 .map(|value| (A::account_key_to_nibbles(&value.0), value.1)))
229 }
230
231 fn seek(
232 &mut self,
233 key: Nibbles,
234 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
235 Ok(self
236 .0
237 .seek(A::AccountKey::from(key))?
238 .map(|value| (A::account_key_to_nibbles(&value.0), value.1)))
239 }
240
241 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
242 Ok(self.0.next()?.map(|value| (A::account_key_to_nibbles(&value.0), value.1)))
243 }
244
245 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
246 Ok(self.0.current()?.map(|(k, _)| A::account_key_to_nibbles(&k)))
247 }
248
249 fn reset(&mut self) {
250 }
252}
253
254#[derive(Debug)]
256pub struct DatabaseStorageTrieCursor<C, A: TrieKeyAdapter> {
257 pub cursor: C,
259 hashed_address: B256,
261 _adapter: PhantomData<A>,
262}
263
264impl<C, A: TrieKeyAdapter> DatabaseStorageTrieCursor<C, A> {
265 pub const fn new(cursor: C, hashed_address: B256) -> Self {
267 Self { cursor, hashed_address, _adapter: PhantomData }
268 }
269}
270
271impl<C, A> DatabaseStorageTrieCursor<C, A>
272where
273 A: TrieTableAdapter,
274 C: DbCursorRO<A::StorageTrieTable>
275 + DbCursorRW<A::StorageTrieTable>
276 + DbDupCursorRO<A::StorageTrieTable>
277 + DbDupCursorRW<A::StorageTrieTable>,
278{
279 pub fn write_storage_trie_updates_sorted(
281 &mut self,
282 updates: &StorageTrieUpdatesSorted,
283 ) -> Result<usize, DatabaseError> {
284 if updates.is_deleted() && self.cursor.seek_exact(self.hashed_address)?.is_some() {
286 self.cursor.delete_current_duplicates()?;
287 }
288
289 let mut num_entries = 0;
290 for (nibbles, maybe_updated) in updates.storage_nodes.iter().filter(|(n, _)| !n.is_empty())
291 {
292 num_entries += 1;
293 let nibbles = A::StorageSubKey::from(*nibbles);
294 if self
296 .cursor
297 .seek_by_key_subkey(self.hashed_address, nibbles.clone())?
298 .as_ref()
299 .is_some_and(|e| *e.nibbles() == nibbles)
300 {
301 self.cursor.delete_current()?;
302 }
303
304 if let Some(node) = maybe_updated {
306 self.cursor
307 .upsert(self.hashed_address, &A::StorageValue::new(nibbles, node.clone()))?;
308 }
309 }
310
311 Ok(num_entries)
312 }
313}
314
315impl<C, A> TrieCursor for DatabaseStorageTrieCursor<C, A>
316where
317 A: TrieTableAdapter,
318 C: DbCursorRO<A::StorageTrieTable> + DbDupCursorRO<A::StorageTrieTable> + Send,
319{
320 fn seek_exact(
321 &mut self,
322 key: Nibbles,
323 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
324 let subkey = A::StorageSubKey::from(key);
325 Ok(self
326 .cursor
327 .seek_by_key_subkey(self.hashed_address, subkey.clone())?
328 .filter(|e| *e.nibbles() == subkey)
329 .map(|value| {
330 let (subkey, node) = value.into_parts();
331 (A::subkey_to_nibbles(&subkey), node)
332 }))
333 }
334
335 fn seek(
336 &mut self,
337 key: Nibbles,
338 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
339 Ok(self.cursor.seek_by_key_subkey(self.hashed_address, A::StorageSubKey::from(key))?.map(
340 |value| {
341 let (subkey, node) = value.into_parts();
342 (A::subkey_to_nibbles(&subkey), node)
343 },
344 ))
345 }
346
347 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
348 Ok(self.cursor.next_dup()?.map(|(_, value)| {
349 let (subkey, node) = value.into_parts();
350 (A::subkey_to_nibbles(&subkey), node)
351 }))
352 }
353
354 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
355 Ok(self.cursor.current()?.map(|(_, v)| A::subkey_to_nibbles(v.nibbles())))
356 }
357
358 fn reset(&mut self) {
359 }
361}
362
363impl<C, A> TrieStorageCursor for DatabaseStorageTrieCursor<C, A>
364where
365 A: TrieTableAdapter,
366 C: DbCursorRO<A::StorageTrieTable> + DbDupCursorRO<A::StorageTrieTable> + Send,
367{
368 fn set_hashed_address(&mut self, hashed_address: B256) {
369 self.hashed_address = hashed_address;
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376 use alloy_primitives::hex_literal::hex;
377 use reth_db_api::{cursor::DbCursorRW, transaction::DbTxMut};
378 use reth_provider::test_utils::create_test_provider_factory;
379
380 #[test]
381 fn test_account_trie_order() {
382 let factory = create_test_provider_factory();
383 let provider = factory.provider_rw().unwrap();
384 let mut cursor = provider.tx_ref().cursor_write::<tables::AccountsTrie>().unwrap();
385
386 let data = vec![
387 hex!("0303040e").to_vec(),
388 hex!("030305").to_vec(),
389 hex!("03030500").to_vec(),
390 hex!("0303050a").to_vec(),
391 ];
392
393 for key in data.clone() {
394 cursor
395 .upsert(
396 key.into(),
397 &BranchNodeCompact::new(
398 0b0000_0010_0000_0001,
399 0b0000_0010_0000_0001,
400 0,
401 Vec::default(),
402 None,
403 ),
404 )
405 .unwrap();
406 }
407
408 let db_data = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
409 assert_eq!(db_data[0].0 .0.to_vec(), data[0]);
410 assert_eq!(db_data[1].0 .0.to_vec(), data[1]);
411 assert_eq!(db_data[2].0 .0.to_vec(), data[2]);
412 assert_eq!(db_data[3].0 .0.to_vec(), data[3]);
413
414 assert_eq!(
415 cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.0.to_vec()),
416 Some(data[1].clone())
417 );
418 }
419
420 #[test]
422 fn test_storage_cursor_abstraction() {
423 use reth_storage_api::StorageSettingsCache;
424 use reth_trie::trie_cursor::{TrieCursor, TrieCursorFactory};
425
426 let factory = create_test_provider_factory();
427 let provider = factory.provider_rw().unwrap();
428 let mut cursor = provider.tx_ref().cursor_dup_write::<tables::StoragesTrie>().unwrap();
429
430 let hashed_address = B256::random();
431 let key = StoredNibblesSubKey::from(vec![0x2, 0x3]);
432 let value = BranchNodeCompact::new(1, 1, 1, vec![B256::random()], None);
433
434 cursor
435 .upsert(hashed_address, &StorageTrieEntry { nibbles: key.clone(), node: value.clone() })
436 .unwrap();
437
438 crate::with_adapter!(provider, |A| {
439 let trie_factory = DatabaseTrieCursorFactory::<_, A>::new(provider.tx_ref());
440 let mut cursor = trie_factory.storage_trie_cursor(hashed_address).unwrap();
441 assert_eq!(cursor.seek(key.into()).unwrap().unwrap().1, value);
442 });
443 }
444}