1use alloc::{sync::Arc, vec::Vec};
2use alloy_primitives::{BlockHash, BlockNumber, Bytes};
3use reth_storage_errors::provider::ProviderResult;
4
5#[auto_impl::auto_impl(&, Arc, Box)]
11pub trait BalStore: Send + Sync + 'static {
12 fn insert(
14 &self,
15 block_hash: BlockHash,
16 block_number: BlockNumber,
17 bal: Bytes,
18 ) -> ProviderResult<()>;
19
20 fn get_by_hashes(&self, block_hashes: &[BlockHash]) -> ProviderResult<Vec<Option<Bytes>>>;
24
25 fn get_by_hashes_with_limit(
31 &self,
32 block_hashes: &[BlockHash],
33 limit: GetBlockAccessListLimit,
34 ) -> ProviderResult<Vec<Bytes>> {
35 let mut out = Vec::new();
36 self.append_by_hashes_with_limit(block_hashes, limit, &mut out)?;
37 out.shrink_to_fit();
38 Ok(out)
39 }
40
41 fn append_by_hashes_with_limit(
45 &self,
46 block_hashes: &[BlockHash],
47 limit: GetBlockAccessListLimit,
48 out: &mut Vec<Bytes>,
49 ) -> ProviderResult<()> {
50 let mut size = 0;
51 for bal in self.get_by_hashes(block_hashes)? {
52 let bal = bal.unwrap_or_else(|| Bytes::from_static(&[0xc0]));
53 size += bal.len();
54 out.push(bal);
55
56 if limit.exceeds(size) {
57 break
58 }
59 }
60 Ok(())
61 }
62
63 fn get_by_range(&self, start: BlockNumber, count: u64) -> ProviderResult<Vec<Bytes>>;
67}
68
69#[derive(Debug, Clone, Copy, Eq, PartialEq)]
71pub enum GetBlockAccessListLimit {
72 None,
74 ResponseSizeSoftLimit(usize),
76}
77
78impl GetBlockAccessListLimit {
79 #[inline]
81 pub const fn exceeds(&self, size: usize) -> bool {
82 match self {
83 Self::None => false,
84 Self::ResponseSizeSoftLimit(limit) => size > *limit,
85 }
86 }
87}
88
89#[derive(Clone)]
91pub struct BalStoreHandle {
92 inner: Arc<dyn BalStore>,
93}
94
95impl BalStoreHandle {
96 pub fn new(inner: impl BalStore) -> Self {
98 Self { inner: Arc::new(inner) }
99 }
100
101 pub fn noop() -> Self {
103 Self::new(NoopBalStore)
104 }
105
106 #[inline]
108 pub fn insert(
109 &self,
110 block_hash: BlockHash,
111 block_number: BlockNumber,
112 bal: Bytes,
113 ) -> ProviderResult<()> {
114 self.inner.insert(block_hash, block_number, bal)
115 }
116
117 #[inline]
119 pub fn get_by_hashes(&self, block_hashes: &[BlockHash]) -> ProviderResult<Vec<Option<Bytes>>> {
120 self.inner.get_by_hashes(block_hashes)
121 }
122
123 #[inline]
126 pub fn get_by_hashes_with_limit(
127 &self,
128 block_hashes: &[BlockHash],
129 limit: GetBlockAccessListLimit,
130 ) -> ProviderResult<Vec<Bytes>> {
131 self.inner.get_by_hashes_with_limit(block_hashes, limit)
132 }
133
134 #[inline]
136 pub fn append_by_hashes_with_limit(
137 &self,
138 block_hashes: &[BlockHash],
139 limit: GetBlockAccessListLimit,
140 out: &mut Vec<Bytes>,
141 ) -> ProviderResult<()> {
142 self.inner.append_by_hashes_with_limit(block_hashes, limit, out)
143 }
144
145 #[inline]
147 pub fn get_by_range(&self, start: BlockNumber, count: u64) -> ProviderResult<Vec<Bytes>> {
148 self.inner.get_by_range(start, count)
149 }
150}
151
152impl Default for BalStoreHandle {
153 fn default() -> Self {
154 Self::noop()
155 }
156}
157
158impl core::fmt::Debug for BalStoreHandle {
159 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
160 f.debug_struct("BalStoreHandle").finish_non_exhaustive()
161 }
162}
163
164#[auto_impl::auto_impl(&, Arc)]
166pub trait BalProvider {
167 fn bal_store(&self) -> &BalStoreHandle;
169}
170
171#[derive(Debug, Default, Clone, Copy)]
173pub struct NoopBalStore;
174
175impl BalStore for NoopBalStore {
176 fn insert(
177 &self,
178 _block_hash: BlockHash,
179 _block_number: BlockNumber,
180 _bal: Bytes,
181 ) -> ProviderResult<()> {
182 Ok(())
183 }
184
185 fn get_by_hashes(&self, block_hashes: &[BlockHash]) -> ProviderResult<Vec<Option<Bytes>>> {
186 Ok(block_hashes.iter().map(|_| None).collect())
187 }
188
189 fn append_by_hashes_with_limit(
190 &self,
191 block_hashes: &[BlockHash],
192 limit: GetBlockAccessListLimit,
193 out: &mut Vec<Bytes>,
194 ) -> ProviderResult<()> {
195 let mut size = 0;
196 for _ in block_hashes {
197 let bal = Bytes::from_static(&[0xc0]);
198 size += bal.len();
199 out.push(bal);
200
201 if limit.exceeds(size) {
202 break
203 }
204 }
205 Ok(())
206 }
207
208 fn get_by_range(&self, _start: BlockNumber, _count: u64) -> ProviderResult<Vec<Bytes>> {
209 Ok(Vec::new())
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use alloy_primitives::B256;
217
218 #[test]
219 fn noop_store_returns_empty_results() {
220 let store = BalStoreHandle::default();
221 let hashes = [B256::random(), B256::random()];
222
223 let by_hash = store.get_by_hashes(&hashes).unwrap();
224 let by_range = store.get_by_range(1, 10).unwrap();
225
226 assert_eq!(by_hash, vec![None, None]);
227 assert!(by_range.is_empty());
228 }
229
230 #[test]
231 fn noop_store_limited_lookup_returns_prefix() {
232 let store = BalStoreHandle::default();
233 let hashes = [B256::random(), B256::random(), B256::random()];
234
235 let limited = store
236 .get_by_hashes_with_limit(&hashes, GetBlockAccessListLimit::ResponseSizeSoftLimit(1))
237 .unwrap();
238
239 assert_eq!(limited, vec![Bytes::from_static(&[0xc0]), Bytes::from_static(&[0xc0])]);
240 }
241
242 #[test]
243 fn block_access_list_limit() {
244 let limit_none = GetBlockAccessListLimit::None;
245 assert!(!limit_none.exceeds(usize::MAX));
246
247 let size_limit_2mb = GetBlockAccessListLimit::ResponseSizeSoftLimit(2 * 1024 * 1024);
248 assert!(!size_limit_2mb.exceeds(1024 * 1024));
249 assert!(!size_limit_2mb.exceeds(2 * 1024 * 1024));
250 assert!(size_limit_2mb.exceeds(3 * 1024 * 1024));
251 }
252}