reth_trie_common/
target_v2.rs1use crate::Nibbles;
4use alloc::vec::Vec;
5use alloy_primitives::{keccak256, map::B256Map, B256};
6use revm::state::EvmState;
7
8#[derive(Debug, Copy, Clone)]
11pub struct ProofV2Target {
12 pub key_nibbles: Nibbles,
14 pub parent: ProofV2TargetParent,
16}
17
18impl ProofV2Target {
19 pub fn new(key: B256) -> Self {
22 let key_nibbles = unsafe { Nibbles::unpack_unchecked(key.as_slice()) };
24 Self { key_nibbles, parent: ProofV2TargetParent::NONE }
25 }
26
27 pub fn key(&self) -> B256 {
29 B256::from_slice(&self.key_nibbles.pack())
30 }
31
32 pub const fn with_parent(mut self, parent: ProofV2TargetParent) -> Self {
34 self.parent = parent;
35 self
36 }
37}
38
39impl From<B256> for ProofV2Target {
40 fn from(key: B256) -> Self {
41 Self::new(key)
42 }
43}
44
45#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
56pub struct ProofV2TargetParent(Option<u8>);
57
58impl ProofV2TargetParent {
59 pub const NONE: Self = Self(None);
61
62 pub const fn new(path_len: usize) -> Self {
68 assert!(path_len < 64, "parent path length must be less than 64");
69 Self(Some(path_len as u8))
70 }
71
72 pub const fn is_known(self) -> bool {
74 self.0.is_some()
75 }
76
77 pub const fn path_len(self) -> Option<usize> {
79 match self.0 {
80 Some(path_len) => Some(path_len as usize),
81 None => None,
82 }
83 }
84
85 pub fn path(self, mut target_path: Nibbles) -> Option<Nibbles> {
87 target_path.truncate(self.path_len()?);
88 Some(target_path)
89 }
90}
91
92#[derive(Debug, Default)]
95pub struct MultiProofTargetsV2 {
96 pub account_targets: Vec<ProofV2Target>,
98 pub storage_targets: B256Map<Vec<ProofV2Target>>,
100}
101
102impl MultiProofTargetsV2 {
103 pub fn is_empty(&self) -> bool {
105 self.account_targets.is_empty() && self.storage_targets.is_empty()
106 }
107
108 pub fn chunking_length(&self) -> usize {
110 self.account_targets.len() +
111 self.storage_targets.values().map(|slots| slots.len()).sum::<usize>()
112 }
113
114 pub fn chunks(self, chunk_size: usize) -> impl Iterator<Item = Self> {
120 ChunkedMultiProofTargetsV2::new(self, chunk_size)
121 }
122
123 pub fn from_state(state: &EvmState) -> (Self, usize) {
126 let mut targets = Self::default();
127 targets.account_targets.reserve(state.len());
128 targets.storage_targets.reserve(state.len());
129 let mut storage_target_count = 0;
130 for (addr, account) in state {
131 if !account.is_touched() || account.is_selfdestructed() {
139 continue
140 }
141
142 let hashed_address = keccak256(addr);
143
144 if account.info != account.original_info() {
145 targets.account_targets.push(hashed_address.into());
146 }
147
148 let mut storage_slots = Vec::with_capacity(account.storage.len());
149 for (key, slot) in &account.storage {
150 if !slot.is_changed() {
152 continue
153 }
154
155 let hashed_slot = keccak256(B256::from(*key));
156 storage_slots.push(ProofV2Target::from(hashed_slot));
157 }
158
159 storage_target_count += storage_slots.len();
160 if !storage_slots.is_empty() {
161 targets.storage_targets.insert(hashed_address, storage_slots);
162 }
163 }
164
165 (targets, storage_target_count)
166 }
167}
168
169#[derive(Debug)]
177pub struct ChunkedMultiProofTargetsV2 {
178 account_targets: alloc::vec::IntoIter<ProofV2Target>,
180 storage_targets: B256Map<Vec<ProofV2Target>>,
182 current_account_storage: Option<(B256, alloc::vec::IntoIter<ProofV2Target>)>,
184 size: usize,
186}
187
188impl ChunkedMultiProofTargetsV2 {
189 pub fn new(targets: MultiProofTargetsV2, size: usize) -> Self {
195 assert!(size > 0, "chunk size must be non-zero");
196 Self {
197 account_targets: targets.account_targets.into_iter(),
198 storage_targets: targets.storage_targets,
199 current_account_storage: None,
200 size,
201 }
202 }
203}
204
205impl Iterator for ChunkedMultiProofTargetsV2 {
206 type Item = MultiProofTargetsV2;
207
208 fn next(&mut self) -> Option<Self::Item> {
209 let mut chunk = MultiProofTargetsV2::default();
210 let mut count = 0;
211
212 if let Some((account_addr, ref mut storage_iter)) = self.current_account_storage {
214 let remaining_capacity = self.size - count;
215 let slots: Vec<_> = storage_iter.by_ref().take(remaining_capacity).collect();
216
217 count += slots.len();
218 chunk.storage_targets.insert(account_addr, slots);
219
220 if storage_iter.len() == 0 {
222 self.current_account_storage = None;
223 }
224 }
225
226 while count < self.size {
228 let Some(account_target) = self.account_targets.next() else {
229 break;
230 };
231
232 chunk.account_targets.push(account_target);
234 count += 1;
235
236 let account_addr = account_target.key();
238 if let Some(storage_slots) = self.storage_targets.remove(&account_addr) {
239 let remaining_capacity = self.size - count;
240
241 if storage_slots.len() <= remaining_capacity {
242 count += storage_slots.len();
244 chunk.storage_targets.insert(account_addr, storage_slots);
245 } else {
246 let mut storage_iter = storage_slots.into_iter();
248 let slots_in_chunk: Vec<_> =
249 storage_iter.by_ref().take(remaining_capacity).collect();
250 count += slots_in_chunk.len();
251
252 chunk.storage_targets.insert(account_addr, slots_in_chunk);
253
254 self.current_account_storage = Some((account_addr, storage_iter));
256 break;
257 }
258 }
259 }
260
261 while let Some((account_addr, storage_slots)) = self.storage_targets.iter_mut().next() &&
263 count < self.size
264 {
265 let account_addr = *account_addr;
266 let storage_slots = core::mem::take(storage_slots);
267 let remaining_capacity = self.size - count;
268
269 self.storage_targets.remove(&account_addr);
272
273 if storage_slots.len() <= remaining_capacity {
274 count += storage_slots.len();
276 chunk.storage_targets.insert(account_addr, storage_slots);
277 } else {
278 let mut storage_iter = storage_slots.into_iter();
280 let slots_in_chunk: Vec<_> =
281 storage_iter.by_ref().take(remaining_capacity).collect();
282
283 chunk.storage_targets.insert(account_addr, slots_in_chunk);
284
285 if storage_iter.len() > 0 {
287 self.current_account_storage = Some((account_addr, storage_iter));
288 }
289 break;
290 }
291 }
292
293 if chunk.account_targets.is_empty() && chunk.storage_targets.is_empty() {
294 None
295 } else {
296 Some(chunk)
297 }
298 }
299}