1use super::*;
4
5impl Archive {
6 fn check_range(&self, addr: Address, block: u64) -> std::result::Result<u64, NotAvailable> {
7 let start = self.start_of(addr)?.ok_or(NotAvailable::NotWatched(addr))?;
8 if block < start {
9 return Err(NotAvailable::BeforeStart {
10 requested: block,
11 start,
12 });
13 }
14 let (head, _) = self.head()?.ok_or(NotAvailable::NotSynced)?;
15 if block > head {
16 return Err(NotAvailable::AfterHead {
17 requested: block,
18 head,
19 });
20 }
21 Ok(start)
22 }
23
24 pub fn storage_at(
26 &self,
27 addr: Address,
28 slot: B256,
29 block: u64,
30 ) -> std::result::Result<StorageValue, NotAvailable> {
31 self.check_range(addr, block)?;
32 let rtx = self.db.begin_read().map_err(ArchiveError::from)?;
33 let t = rtx.open_table(SLOTS).map_err(ArchiveError::from)?;
34 let lo = slot_prefix(addr, slot);
35 let hi = slot_key(addr, slot, block, u32::MAX);
36 let mut it = t
37 .range::<&[u8]>(lo.as_slice()..=hi.as_slice())
38 .map_err(ArchiveError::from)?;
39 if let Some(item) = it.next_back() {
40 let (k, v) = item.map_err(ArchiveError::from)?;
41 let (_, _, set_at, index) =
42 parse_slot_key(k.value()).ok_or(NotAvailable::Internal("bad slot key".into()))?;
43 let (provenance, value) =
44 decode_value(v.value()).ok_or(NotAvailable::Internal("bad slot value".into()))?;
45 return Ok(StorageValue {
46 value,
47 provenance,
48 set_at,
49 index,
50 });
51 }
52 drop(it);
53 let created = rtx.open_table(CREATED).map_err(ArchiveError::from)?;
58 if let Some(c) = created
59 .get(addr.as_slice())
60 .map_err(ArchiveError::from)?
61 .map(|v| v.value())
62 {
63 return Ok(StorageValue {
64 value: B256::ZERO,
65 provenance: Provenance::Bal,
66 set_at: c,
67 index: u32::MAX,
68 });
69 }
70 let boot = rtx.open_table(BOOT).map_err(ArchiveError::from)?;
73 match boot.get(lo.as_slice()).map_err(ArchiveError::from)? {
74 Some(v) => match decode_boot(v.value()) {
75 Some(BootState::Pending { first_seen }) | Some(BootState::Lost { first_seen }) => {
76 Err(NotAvailable::UnknownBefore { first_seen })
77 }
78 Some(BootState::Done) => Err(NotAvailable::Internal(
79 "bootstrap marked done but no record".into(),
80 )),
81 None => Err(NotAvailable::Internal("bad bootstrap record".into())),
82 },
83 None => Err(NotAvailable::NeverRecorded),
84 }
85 }
86
87 pub fn history(
91 &self,
92 addr: Address,
93 slot: B256,
94 range: Range<u64>,
95 ) -> std::result::Result<Vec<HistoryEntry>, NotAvailable> {
96 if range.start >= range.end {
97 return Err(NotAvailable::InvalidRange {
98 start: range.start,
99 end: range.end,
100 });
101 }
102 self.check_range(addr, range.start)?;
103 self.check_range(addr, range.end - 1)?;
104 let rtx = self.db.begin_read().map_err(ArchiveError::from)?;
105 let t = rtx.open_table(SLOTS).map_err(ArchiveError::from)?;
106 let lo = slot_key(addr, slot, range.start, 0);
107 let hi = slot_key(addr, slot, range.end, 0);
108 let mut out = Vec::new();
109 for item in t
110 .range::<&[u8]>(lo.as_slice()..hi.as_slice())
111 .map_err(ArchiveError::from)?
112 {
113 let (k, v) = item.map_err(ArchiveError::from)?;
114 let (_, _, block, index) =
115 parse_slot_key(k.value()).ok_or(NotAvailable::Internal("bad slot key".into()))?;
116 let (provenance, value) =
117 decode_value(v.value()).ok_or(NotAvailable::Internal("bad slot value".into()))?;
118 out.push(HistoryEntry {
119 block,
120 index,
121 value,
122 provenance,
123 });
124 }
125 Ok(out)
126 }
127
128 pub fn changed_slots(
130 &self,
131 addr: Address,
132 block: u64,
133 ) -> std::result::Result<Vec<B256>, NotAvailable> {
134 self.check_range(addr, block)?;
135 let rtx = self.db.begin_read().map_err(ArchiveError::from)?;
136 let t = rtx.open_table(BLOCKIDX).map_err(ArchiveError::from)?;
137 match t
138 .get(blockidx_key(addr, block).as_slice())
139 .map_err(ArchiveError::from)?
140 {
141 Some(v) => {
142 decode_slots(v.value()).ok_or(NotAvailable::Internal("bad blockidx value".into()))
143 }
144 None => Ok(Vec::new()),
145 }
146 }
147
148 pub fn boot_state(&self, addr: Address, slot: B256) -> Result<Option<BootState>> {
150 let rtx = self.db.begin_read()?;
151 let t = rtx.open_table(BOOT)?;
152 Ok(t.get(slot_prefix(addr, slot).as_slice())?
153 .and_then(|v| decode_boot(v.value())))
154 }
155}