1use alloy_primitives::B256;
2use reth_db_api::{
3 cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
4 tables,
5 transaction::DbTx,
6 DatabaseError,
7};
8use reth_trie::{
9 trie_cursor::{TrieCursor, TrieCursorFactory, TrieStorageCursor},
10 updates::StorageTrieUpdatesSorted,
11 BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey,
12};
13
14#[derive(Debug, Clone)]
16pub struct DatabaseTrieCursorFactory<T>(T);
17
18impl<T> DatabaseTrieCursorFactory<T> {
19 pub const fn new(tx: T) -> Self {
21 Self(tx)
22 }
23}
24
25impl<TX> TrieCursorFactory for DatabaseTrieCursorFactory<&TX>
26where
27 TX: DbTx,
28{
29 type AccountTrieCursor<'a>
30 = DatabaseAccountTrieCursor<<TX as DbTx>::Cursor<tables::AccountsTrie>>
31 where
32 Self: 'a;
33
34 type StorageTrieCursor<'a>
35 = DatabaseStorageTrieCursor<<TX as DbTx>::DupCursor<tables::StoragesTrie>>
36 where
37 Self: 'a;
38
39 fn account_trie_cursor(&self) -> Result<Self::AccountTrieCursor<'_>, DatabaseError> {
40 Ok(DatabaseAccountTrieCursor::new(self.0.cursor_read::<tables::AccountsTrie>()?))
41 }
42
43 fn storage_trie_cursor(
44 &self,
45 hashed_address: B256,
46 ) -> Result<Self::StorageTrieCursor<'_>, DatabaseError> {
47 Ok(DatabaseStorageTrieCursor::new(
48 self.0.cursor_dup_read::<tables::StoragesTrie>()?,
49 hashed_address,
50 ))
51 }
52}
53
54#[derive(Debug)]
56pub struct DatabaseAccountTrieCursor<C>(pub(crate) C);
57
58impl<C> DatabaseAccountTrieCursor<C> {
59 pub const fn new(cursor: C) -> Self {
61 Self(cursor)
62 }
63}
64
65impl<C> TrieCursor for DatabaseAccountTrieCursor<C>
66where
67 C: DbCursorRO<tables::AccountsTrie> + Send + Sync,
68{
69 fn seek_exact(
71 &mut self,
72 key: Nibbles,
73 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
74 Ok(self.0.seek_exact(StoredNibbles(key))?.map(|value| (value.0 .0, value.1)))
75 }
76
77 fn seek(
79 &mut self,
80 key: Nibbles,
81 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
82 Ok(self.0.seek(StoredNibbles(key))?.map(|value| (value.0 .0, value.1)))
83 }
84
85 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
87 Ok(self.0.next()?.map(|value| (value.0 .0, value.1)))
88 }
89
90 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
92 Ok(self.0.current()?.map(|(k, _)| k.0))
93 }
94
95 fn reset(&mut self) {
96 }
98}
99
100#[derive(Debug)]
102pub struct DatabaseStorageTrieCursor<C> {
103 pub cursor: C,
105 hashed_address: B256,
107}
108
109impl<C> DatabaseStorageTrieCursor<C> {
110 pub const fn new(cursor: C, hashed_address: B256) -> Self {
112 Self { cursor, hashed_address }
113 }
114}
115
116impl<C> DatabaseStorageTrieCursor<C>
117where
118 C: DbCursorRO<tables::StoragesTrie>
119 + DbCursorRW<tables::StoragesTrie>
120 + DbDupCursorRO<tables::StoragesTrie>
121 + DbDupCursorRW<tables::StoragesTrie>,
122{
123 pub fn write_storage_trie_updates_sorted(
125 &mut self,
126 updates: &StorageTrieUpdatesSorted,
127 ) -> Result<usize, DatabaseError> {
128 if updates.is_deleted() && self.cursor.seek_exact(self.hashed_address)?.is_some() {
130 self.cursor.delete_current_duplicates()?;
131 }
132
133 let mut num_entries = 0;
134 for (nibbles, maybe_updated) in updates.storage_nodes.iter().filter(|(n, _)| !n.is_empty())
135 {
136 num_entries += 1;
137 let nibbles = StoredNibblesSubKey(*nibbles);
138 if self
140 .cursor
141 .seek_by_key_subkey(self.hashed_address, nibbles.clone())?
142 .filter(|e| e.nibbles == nibbles)
143 .is_some()
144 {
145 self.cursor.delete_current()?;
146 }
147
148 if let Some(node) = maybe_updated {
150 self.cursor.upsert(
151 self.hashed_address,
152 &StorageTrieEntry { nibbles, node: node.clone() },
153 )?;
154 }
155 }
156
157 Ok(num_entries)
158 }
159}
160
161impl<C> TrieCursor for DatabaseStorageTrieCursor<C>
162where
163 C: DbCursorRO<tables::StoragesTrie> + DbDupCursorRO<tables::StoragesTrie> + Send + Sync,
164{
165 fn seek_exact(
167 &mut self,
168 key: Nibbles,
169 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
170 Ok(self
171 .cursor
172 .seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(key))?
173 .filter(|e| e.nibbles == StoredNibblesSubKey(key))
174 .map(|value| (value.nibbles.0, value.node)))
175 }
176
177 fn seek(
179 &mut self,
180 key: Nibbles,
181 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
182 Ok(self
183 .cursor
184 .seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(key))?
185 .map(|value| (value.nibbles.0, value.node)))
186 }
187
188 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
190 Ok(self.cursor.next_dup()?.map(|(_, v)| (v.nibbles.0, v.node)))
191 }
192
193 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
195 Ok(self.cursor.current()?.map(|(_, v)| v.nibbles.0))
196 }
197
198 fn reset(&mut self) {
199 }
201}
202
203impl<C> TrieStorageCursor for DatabaseStorageTrieCursor<C>
204where
205 C: DbCursorRO<tables::StoragesTrie> + DbDupCursorRO<tables::StoragesTrie> + Send + Sync,
206{
207 fn set_hashed_address(&mut self, hashed_address: B256) {
208 self.hashed_address = hashed_address;
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use alloy_primitives::hex_literal::hex;
216 use reth_db_api::{cursor::DbCursorRW, transaction::DbTxMut};
217 use reth_provider::test_utils::create_test_provider_factory;
218
219 #[test]
220 fn test_account_trie_order() {
221 let factory = create_test_provider_factory();
222 let provider = factory.provider_rw().unwrap();
223 let mut cursor = provider.tx_ref().cursor_write::<tables::AccountsTrie>().unwrap();
224
225 let data = vec![
226 hex!("0303040e").to_vec(),
227 hex!("030305").to_vec(),
228 hex!("03030500").to_vec(),
229 hex!("0303050a").to_vec(),
230 ];
231
232 for key in data.clone() {
233 cursor
234 .upsert(
235 key.into(),
236 &BranchNodeCompact::new(
237 0b0000_0010_0000_0001,
238 0b0000_0010_0000_0001,
239 0,
240 Vec::default(),
241 None,
242 ),
243 )
244 .unwrap();
245 }
246
247 let db_data = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
248 assert_eq!(db_data[0].0 .0.to_vec(), data[0]);
249 assert_eq!(db_data[1].0 .0.to_vec(), data[1]);
250 assert_eq!(db_data[2].0 .0.to_vec(), data[2]);
251 assert_eq!(db_data[3].0 .0.to_vec(), data[3]);
252
253 assert_eq!(
254 cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.0.to_vec()),
255 Some(data[1].clone())
256 );
257 }
258
259 #[test]
261 fn test_storage_cursor_abstraction() {
262 let factory = create_test_provider_factory();
263 let provider = factory.provider_rw().unwrap();
264 let mut cursor = provider.tx_ref().cursor_dup_write::<tables::StoragesTrie>().unwrap();
265
266 let hashed_address = B256::random();
267 let key = StoredNibblesSubKey::from(vec![0x2, 0x3]);
268 let value = BranchNodeCompact::new(1, 1, 1, vec![B256::random()], None);
269
270 cursor
271 .upsert(hashed_address, &StorageTrieEntry { nibbles: key.clone(), node: value.clone() })
272 .unwrap();
273
274 let mut cursor = DatabaseStorageTrieCursor::new(cursor, hashed_address);
275 assert_eq!(cursor.seek(key.into()).unwrap().unwrap().1, value);
276 }
277}