1use crate::trie_cursor::{TrieCursor, TrieCursorFactory, TrieStorageCursor};
22use alloy_primitives::{map::B256Map, B256};
23use reth_storage_errors::db::DatabaseError;
24use reth_trie_common::{
25 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
26 BranchNodeCompact, Nibbles,
27};
28
29pub type ChangesetResult<T> = Result<T, DatabaseError>;
31
32pub fn compute_trie_changesets<Factory>(
47 factory: &Factory,
48 trie_updates: &TrieUpdatesSorted,
49) -> ChangesetResult<TrieUpdatesSorted>
50where
51 Factory: TrieCursorFactory,
52{
53 let account_nodes = compute_account_changesets(factory, trie_updates)?;
55
56 let mut storage_tries = B256Map::default();
58
59 let mut storage_cursor = factory.storage_trie_cursor(B256::default())?;
61
62 for (hashed_address, storage_updates) in trie_updates.storage_tries_ref() {
63 storage_cursor.set_hashed_address(*hashed_address);
64
65 let storage_changesets = compute_storage_changesets(&mut storage_cursor, storage_updates)?;
66
67 if !storage_changesets.is_empty() {
68 storage_tries.insert(
69 *hashed_address,
70 StorageTrieUpdatesSorted { storage_nodes: storage_changesets },
71 );
72 }
73 }
74
75 Ok(TrieUpdatesSorted::new(account_nodes, storage_tries))
77}
78
79fn compute_account_changesets<Factory>(
85 factory: &Factory,
86 trie_updates: &TrieUpdatesSorted,
87) -> ChangesetResult<Vec<(Nibbles, Option<BranchNodeCompact>)>>
88where
89 Factory: TrieCursorFactory,
90{
91 let mut cursor = factory.account_trie_cursor()?;
92 let mut account_changesets = Vec::with_capacity(trie_updates.account_nodes_ref().len());
93
94 for (path, _new_node) in trie_updates.account_nodes_ref() {
97 let old_node = cursor.seek_exact(*path)?.map(|(_path, node)| node);
98 account_changesets.push((*path, old_node));
99 }
100
101 Ok(account_changesets)
102}
103
104fn compute_storage_changesets(
116 cursor: &mut impl TrieStorageCursor,
117 storage_updates: &StorageTrieUpdatesSorted,
118) -> ChangesetResult<Vec<(Nibbles, Option<BranchNodeCompact>)>> {
119 let mut storage_changesets = Vec::with_capacity(storage_updates.storage_nodes.len());
120
121 for (path, _new_node) in &storage_updates.storage_nodes {
124 let old_node = cursor.seek_exact(*path)?.map(|(_path, node)| node);
125 storage_changesets.push((*path, old_node));
126 }
127
128 Ok(storage_changesets)
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::trie_cursor::mock::MockTrieCursorFactory;
135 use alloy_primitives::map::B256Map;
136 use reth_trie_common::updates::StorageTrieUpdatesSorted;
137 use std::collections::BTreeMap;
138
139 #[test]
140 fn test_empty_updates() {
141 let mut storage_tries = B256Map::default();
145 storage_tries.insert(B256::default(), BTreeMap::new());
146 let factory = MockTrieCursorFactory::new(BTreeMap::new(), storage_tries);
147
148 let updates = TrieUpdatesSorted::new(vec![], B256Map::default());
150
151 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
153
154 assert!(changesets.account_nodes_ref().is_empty());
156 assert!(changesets.storage_tries_ref().is_empty());
157 }
158
159 #[test]
160 fn test_account_changesets() {
161 let path1 = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
163 let path2 = Nibbles::from_nibbles([0x4, 0x5, 0x6]);
164 let node1 = BranchNodeCompact::new(0b1111, 0b1010, 0, vec![], None);
166 let node2 = BranchNodeCompact::new(0b1111, 0b1100, 0, vec![], None);
167
168 let mut account_nodes = BTreeMap::new();
169 account_nodes.insert(path1, node1.clone());
170 account_nodes.insert(path2, node2);
171
172 let mut storage_tries = B256Map::default();
174 storage_tries.insert(B256::default(), BTreeMap::new());
175 let factory = MockTrieCursorFactory::new(account_nodes, storage_tries);
176
177 let path3 = Nibbles::from_nibbles([0x7, 0x8, 0x9]);
179 let new_node1 = BranchNodeCompact::new(0b1111, 0b0001, 0, vec![], None);
180 let new_node3 = BranchNodeCompact::new(0b1111, 0b0000, 0, vec![], None);
181
182 let updates = TrieUpdatesSorted::new(
183 vec![(path1, Some(new_node1)), (path3, Some(new_node3))],
184 B256Map::default(),
185 );
186
187 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
189
190 assert_eq!(changesets.account_nodes_ref().len(), 2);
192
193 assert_eq!(changesets.account_nodes_ref()[0].0, path1);
195 assert_eq!(changesets.account_nodes_ref()[0].1, Some(node1));
196
197 assert_eq!(changesets.account_nodes_ref()[1].0, path3);
199 assert_eq!(changesets.account_nodes_ref()[1].1, None);
200 }
201
202 #[test]
203 fn test_storage_changesets() {
204 let hashed_address = B256::from([1u8; 32]);
205
206 let path1 = Nibbles::from_nibbles([0x1, 0x2]);
208 let path2 = Nibbles::from_nibbles([0x3, 0x4]);
209 let node1 = BranchNodeCompact::new(0b1111, 0b0011, 0, vec![], None);
210 let node2 = BranchNodeCompact::new(0b1111, 0b0101, 0, vec![], None);
211
212 let mut storage_nodes = BTreeMap::new();
213 storage_nodes.insert(path1, node1.clone());
214 storage_nodes.insert(path2, node2);
215
216 let mut storage_tries = B256Map::default();
217 storage_tries.insert(B256::default(), BTreeMap::new()); storage_tries.insert(hashed_address, storage_nodes);
219
220 let factory = MockTrieCursorFactory::new(BTreeMap::new(), storage_tries);
221
222 let path3 = Nibbles::from_nibbles([0x5, 0x6]);
224 let new_node1 = BranchNodeCompact::new(0b1111, 0b1000, 0, vec![], None);
225 let new_node3 = BranchNodeCompact::new(0b1111, 0b0000, 0, vec![], None);
226
227 let mut storage_updates = B256Map::default();
228 storage_updates.insert(
229 hashed_address,
230 StorageTrieUpdatesSorted {
231 storage_nodes: vec![(path1, Some(new_node1)), (path3, Some(new_node3))],
232 },
233 );
234
235 let updates = TrieUpdatesSorted::new(vec![], storage_updates);
236
237 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
239
240 assert_eq!(changesets.storage_tries_ref().len(), 1);
242 let storage_changesets = changesets.storage_tries_ref().get(&hashed_address).unwrap();
243 assert_eq!(storage_changesets.storage_nodes.len(), 2);
244
245 assert_eq!(storage_changesets.storage_nodes[0].0, path1);
247 assert_eq!(storage_changesets.storage_nodes[0].1, Some(node1));
248
249 assert_eq!(storage_changesets.storage_nodes[1].0, path3);
251 assert_eq!(storage_changesets.storage_nodes[1].1, None);
252 }
253}