1use super::*;
5
6impl Archive {
7 pub(crate) fn put_bootstrap(
15 &self,
16 addr: Address,
17 start: u64,
18 proof_block: u64,
19 proof_block_hash: B256,
20 values: &[(B256, B256)],
21 ) -> Result<usize> {
22 let mut written = 0;
23 let txn = self.db.begin_write()?;
24 {
25 let watch = txn.open_table(WATCH)?;
29 if watch.get(addr.as_slice())?.map(|v| v.value()) != Some(start) {
30 return Ok(0);
31 }
32 let hashes = txn.open_table(HASHES)?;
33 let same_block = hashes
34 .get(proof_block)?
35 .map(|v| v.value().len() == 64 && v.value()[..32] == proof_block_hash[..])
36 .unwrap_or(false);
37 if !same_block {
38 return Ok(0);
39 }
40 let mut slots = txn.open_table(SLOTS)?;
41 let mut boot = txn.open_table(BOOT)?;
42 let mut pending = txn.open_table(PENDING)?;
43 for (slot, value) in values {
44 let key = slot_prefix(addr, *slot);
45 let ok = match boot
46 .get(key.as_slice())?
47 .and_then(|v| decode_boot(v.value()))
48 {
49 None => true,
50 Some(BootState::Pending { first_seen })
51 | Some(BootState::Lost { first_seen }) => first_seen > proof_block,
52 Some(BootState::Done) => false,
53 };
54 if !ok {
55 continue;
56 }
57 slots.insert(
58 slot_key(addr, *slot, start - 1, u32::MAX).as_slice(),
59 encode_value(Provenance::Proof, *value).as_slice(),
60 )?;
61 boot.insert(key.as_slice(), encode_boot(BootState::Done).as_slice())?;
62 pending.remove(key.as_slice())?;
63 written += 1;
64 }
65 }
66 txn.commit()?;
67 Ok(written)
68 }
69
70 pub(crate) fn mark_lost(&self, addr: Address, slots: &[B256], first_seen: u64) -> Result<()> {
71 let txn = self.db.begin_write()?;
72 {
73 if txn.open_table(WATCH)?.get(addr.as_slice())?.is_none() {
74 return Ok(()); }
76 let mut boot = txn.open_table(BOOT)?;
77 let mut pending = txn.open_table(PENDING)?;
78 for slot in slots {
79 let key = slot_prefix(addr, *slot);
80 boot.insert(
81 key.as_slice(),
82 encode_boot(BootState::Lost { first_seen }).as_slice(),
83 )?;
84 pending.remove(key.as_slice())?;
85 }
86 }
87 txn.commit()?;
88 Ok(())
89 }
90
91 pub(crate) fn pending_bootstraps(&self) -> Result<Vec<(Address, B256, u64)>> {
94 let rtx = self.db.begin_read()?;
95 let t = rtx.open_table(PENDING)?;
96 let mut out = Vec::new();
97 for item in t.iter()? {
98 let (k, v) = item?;
99 let k = k.value();
100 if k.len() != SLOT_PREFIX_LEN {
101 return Err(ArchiveError::Corrupt("pending key"));
102 }
103 out.push((
104 Address::from_slice(&k[..20]),
105 B256::from_slice(&k[20..]),
106 v.value(),
107 ));
108 }
109 Ok(out)
110 }
111
112 pub fn rollback_to(&self, block: u64) -> Result<()> {
121 let (hash, _) = self
122 .header_at(block)?
123 .ok_or(ArchiveError::ReorgBeyondHorizon(block))?;
124 let watches = self.watchlist()?;
125 let txn = self.db.begin_write()?;
126 {
127 let mut slots = txn.open_table(SLOTS)?;
128 let mut idx = txn.open_table(BLOCKIDX)?;
129 let mut boot = txn.open_table(BOOT)?;
130 let mut pending = txn.open_table(PENDING)?;
131 for (addr, start) in &watches {
132 if block + 1 < *start {
133 for k in collect_prefix_keys(&slots, addr.as_slice())? {
135 slots.remove(k.as_slice())?;
136 }
137 for k in collect_prefix_keys(&idx, addr.as_slice())? {
138 idx.remove(k.as_slice())?;
139 }
140 for k in collect_prefix_keys(&boot, addr.as_slice())? {
141 boot.remove(k.as_slice())?;
142 }
143 for k in collect_prefix_keys(&pending, addr.as_slice())? {
144 pending.remove(k.as_slice())?;
145 }
146 continue;
147 }
148 let lo = blockidx_key(*addr, block + 1);
149 let hi = prefix_end(addr.as_slice());
150 let mut victims = Vec::new();
151 for item in idx.range::<&[u8]>(bounds(&lo, hi.as_deref()))? {
152 let (k, v) = item?;
153 victims.push((k.value().to_vec(), v.value().to_vec()));
154 }
155 for (k, v) in victims {
156 let (_, b) = parse_blockidx_key(&k).ok_or(ArchiveError::Corrupt("blockidx"))?;
157 let written =
158 decode_slots(&v).ok_or(ArchiveError::Corrupt("blockidx value"))?;
159 for slot in written {
160 let sl = slot_key(*addr, slot, b, 0);
161 let sh = slot_key(*addr, slot, b, u32::MAX);
162 let ks: Vec<Vec<u8>> = slots
163 .range::<&[u8]>(sl.as_slice()..=sh.as_slice())?
164 .map(|r| r.map(|(k, _)| k.value().to_vec()))
165 .collect::<std::result::Result<_, _>>()?;
166 for sk in ks {
167 slots.remove(sk.as_slice())?;
168 }
169 let bk = slot_prefix(*addr, slot);
172 let forget = match boot
173 .get(bk.as_slice())?
174 .and_then(|v| decode_boot(v.value()))
175 {
176 Some(BootState::Pending { first_seen })
177 | Some(BootState::Lost { first_seen }) => first_seen > block,
178 _ => false,
179 };
180 if forget {
181 boot.remove(bk.as_slice())?;
182 pending.remove(bk.as_slice())?;
183 }
184 }
185 idx.remove(k.as_slice())?;
186 }
187 }
188 let mut hashes = txn.open_table(HASHES)?;
189 let above: Vec<u64> = hashes
190 .range(block + 1..)?
191 .map(|r| r.map(|(k, _)| k.value()))
192 .collect::<std::result::Result<_, _>>()?;
193 for b in above {
194 hashes.remove(b)?;
195 }
196 let mut created = txn.open_table(CREATED)?;
198 let stale: Vec<Vec<u8>> = created
199 .iter()?
200 .filter_map(|r| r.ok())
201 .filter(|(_, v)| v.value() > block)
202 .map(|(k, _)| k.value().to_vec())
203 .collect();
204 for k in stale {
205 created.remove(k.as_slice())?;
206 }
207 let mut meta = txn.open_table(META)?;
208 meta.insert(META_HEAD, head_bytes(block, hash).as_slice())?;
209 }
210 txn.commit()?;
211 Ok(())
212 }
213
214 pub(crate) fn apply_block(
219 &self,
220 header: &bal_source::Header,
221 bal: &bal_codec::BlockAccessList,
222 watches: &[(Address, u64)],
223 verified: bool,
224 prune_hashes_below: Option<u64>,
225 ) -> Result<(FreshSlots, usize)> {
226 let n = header.number;
227 let provenance = if verified {
228 Provenance::Bal
229 } else {
230 Provenance::Unverified
231 };
232 let mut fresh = Vec::new();
233 let mut written = 0usize;
234 let txn = self.db.begin_write()?;
235 {
236 let mut slots = txn.open_table(SLOTS)?;
237 let mut idx = txn.open_table(BLOCKIDX)?;
238 let mut boot = txn.open_table(BOOT)?;
239 let mut pending = txn.open_table(PENDING)?;
240 let mut created = txn.open_table(CREATED)?;
241 let watch_now = txn.open_table(WATCH)?;
242 for (addr, start) in watches {
243 if n < *start {
244 continue;
245 }
246 if watch_now.get(addr.as_slice())?.is_none() {
249 continue;
250 }
251 let Some(acc) = bal.account(addr) else {
252 continue;
253 };
254 let mut is_created = created.get(addr.as_slice())?.is_some();
257 if !is_created && verified && creation_in(acc) {
258 created.insert(addr.as_slice(), n)?;
259 settle_created(&mut boot, &mut pending, *addr)?;
260 is_created = true;
261 }
262 let mut fresh_here = Vec::new();
263 let mut changed = Vec::with_capacity(acc.storage_changes.len());
264 for sc in &acc.storage_changes {
265 let slot = sc.slot_b256();
266 changed.push(slot);
267 let prefix = slot_prefix(*addr, slot);
268 let seen_before = boot.get(prefix.as_slice())?.is_some();
269 if !seen_before && is_created {
270 boot.insert(prefix.as_slice(), encode_boot(BootState::Done).as_slice())?;
271 } else if !seen_before {
272 boot.insert(
273 prefix.as_slice(),
274 encode_boot(BootState::Pending { first_seen: n }).as_slice(),
275 )?;
276 pending.insert(prefix.as_slice(), n)?;
277 fresh_here.push(slot);
278 }
279 if self.config.full_detail {
280 for ch in &sc.changes {
281 slots.insert(
282 slot_key(*addr, slot, n, ch.block_access_index).as_slice(),
283 encode_value(provenance, ch.value_b256()).as_slice(),
284 )?;
285 written += 1;
286 }
287 } else {
288 let ch = sc.final_change();
289 slots.insert(
290 slot_key(*addr, slot, n, ch.block_access_index).as_slice(),
291 encode_value(provenance, ch.value_b256()).as_slice(),
292 )?;
293 written += 1;
294 }
295 }
296 if !changed.is_empty() {
297 idx.insert(
298 blockidx_key(*addr, n).as_slice(),
299 encode_slots(&changed).as_slice(),
300 )?;
301 }
302 if !fresh_here.is_empty() {
303 fresh.push((*addr, *start, fresh_here));
304 }
305 }
306 let mut hashes = txn.open_table(HASHES)?;
307 hashes.insert(n, header_bytes(header.hash, header.state_root).as_slice())?;
308 if let Some(below) = prune_hashes_below {
309 let old: Vec<u64> = hashes
310 .range(..below)?
311 .map(|r| r.map(|(k, _)| k.value()))
312 .collect::<std::result::Result<_, _>>()?;
313 for b in old {
314 hashes.remove(b)?;
315 }
316 }
317 let mut meta = txn.open_table(META)?;
318 meta.insert(META_HEAD, head_bytes(n, header.hash).as_slice())?;
319 }
320 txn.commit()?;
321 Ok((fresh, written))
322 }
323}