reth_transaction_pool/blobstore/mod.rs
1//! Storage for blob data of EIP4844 transactions.
2
3use alloy_eips::{
4 eip4844::{BlobAndProofV1, BlobAndProofV2, BlobCellsAndProofsV1},
5 eip7594::{BlobTransactionSidecarVariant, Cell},
6};
7use alloy_primitives::{TxHash, B128, B256};
8pub use converter::BlobSidecarConverter;
9pub use disk::{DiskFileBlobStore, DiskFileBlobStoreConfig, OpenDiskFileBlobStore};
10pub use mem::InMemoryBlobStore;
11pub use noop::NoopBlobStore;
12use std::{
13 fmt,
14 sync::{
15 atomic::{AtomicUsize, Ordering},
16 Arc,
17 },
18};
19pub use tracker::{BlobStoreCanonTracker, BlobStoreUpdates};
20
21mod converter;
22pub mod disk;
23mod mem;
24mod noop;
25mod tracker;
26
27/// A blob store that can be used to store blob data of EIP4844 transactions.
28///
29/// This type is responsible for keeping track of blob data until it is no longer needed (after
30/// finalization).
31///
32/// Note: this is Clone because it is expected to be wrapped in an Arc.
33pub trait BlobStore: fmt::Debug + Send + Sync + 'static {
34 /// Inserts the blob sidecar into the store
35 fn insert(&self, tx: B256, data: BlobTransactionSidecarVariant) -> Result<(), BlobStoreError>;
36
37 /// Inserts multiple blob sidecars into the store
38 fn insert_all(
39 &self,
40 txs: Vec<(B256, BlobTransactionSidecarVariant)>,
41 ) -> Result<(), BlobStoreError>;
42
43 /// Deletes the blob sidecar from the store
44 fn delete(&self, tx: B256) -> Result<(), BlobStoreError>;
45
46 /// Deletes multiple blob sidecars from the store
47 fn delete_all(&self, txs: Vec<B256>) -> Result<(), BlobStoreError>;
48
49 /// A maintenance function that can be called periodically to clean up the blob store, returns
50 /// the number of successfully deleted blobs and the number of failed deletions.
51 ///
52 /// This is intended to be called in the background to clean up any old or unused data, in case
53 /// the store uses deferred cleanup: [`DiskFileBlobStore`]
54 fn cleanup(&self) -> BlobStoreCleanupStat;
55
56 /// Retrieves the decoded blob data for the given transaction hash.
57 fn get(&self, tx: B256) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError>;
58
59 /// Checks if the given transaction hash is in the blob store.
60 fn contains(&self, tx: B256) -> Result<bool, BlobStoreError>;
61
62 /// Retrieves all decoded blob data for the given transaction hashes.
63 ///
64 /// This only returns the blobs that were found in the store.
65 /// If there's no blob it will not be returned.
66 ///
67 /// Note: this is not guaranteed to return the blobs in the same order as the input.
68 fn get_all(
69 &self,
70 txs: Vec<B256>,
71 ) -> Result<Vec<(B256, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError>;
72
73 /// Returns the exact [`BlobTransactionSidecarVariant`] for the given transaction hashes in the
74 /// exact order they were requested.
75 ///
76 /// Returns an error if any of the blobs are not found in the blob store.
77 fn get_exact(
78 &self,
79 txs: Vec<B256>,
80 ) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError>;
81
82 /// Return the [`BlobAndProofV1`]s for a list of blob versioned hashes.
83 fn get_by_versioned_hashes_v1(
84 &self,
85 versioned_hashes: &[B256],
86 ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError>;
87
88 /// Return the [`BlobAndProofV2`]s for a list of blob versioned hashes.
89 /// Blobs and proofs are returned only if they are present for _all_ requested
90 /// versioned hashes.
91 ///
92 /// This differs from [`BlobStore::get_by_versioned_hashes_v1`] in that it also returns all the
93 /// cell proofs in [`BlobAndProofV2`] supported by the EIP-7594 blob sidecar variant.
94 ///
95 /// The response also differs from [`BlobStore::get_by_versioned_hashes_v1`] in that this
96 /// returns `None` if any of the requested versioned hashes are not present in the blob store:
97 /// e.g. where v1 would return `[A, None, C]` v2 would return `None`. See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/osaka.md#engine_getblobsv2>
98 fn get_by_versioned_hashes_v2(
99 &self,
100 versioned_hashes: &[B256],
101 ) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError>;
102
103 /// Return the [`BlobAndProofV2`]s for a list of blob versioned hashes.
104 ///
105 /// The response is always the same length as the request. Missing or older-version blobs are
106 /// returned as `None` elements.
107 fn get_by_versioned_hashes_v3(
108 &self,
109 versioned_hashes: &[B256],
110 ) -> Result<Vec<Option<BlobAndProofV2>>, BlobStoreError>;
111
112 /// Return the [`BlobCellsAndProofsV1`]s for a list of blob versioned hashes and requested cell
113 /// indices.
114 ///
115 /// The response is always the same length as the request. Missing or older-version blobs are
116 /// returned as `None` elements.
117 fn get_by_versioned_hashes_v4(
118 &self,
119 versioned_hashes: &[B256],
120 indices_bitarray: B128,
121 ) -> Result<Vec<Option<BlobCellsAndProofsV1>>, BlobStoreError>;
122
123 /// Return whether each requested blob versioned hash is available.
124 ///
125 /// The response is always the same length and order as the request.
126 fn has_versioned_hashes(&self, versioned_hashes: &[B256]) -> Result<Vec<bool>, BlobStoreError>;
127
128 /// Returns all requested cells for all blobs belonging to the transaction.
129 ///
130 /// The `indices_bitarray` is applied independently to every blob in the tx.
131 ///
132 /// Returned cells are flattened in blob order, then cell-index order.
133 ///
134 /// Example:
135 /// If the tx contains blobs `[blob0, blob1]` and the requested indices are
136 /// `[2, 5, 9]`, the returned vector is:
137 ///
138 /// ```text
139 /// [
140 /// blob0_cell2,
141 /// blob0_cell5,
142 /// blob0_cell9,
143 /// blob1_cell2,
144 /// blob1_cell5,
145 /// blob1_cell9,
146 /// ]
147 /// ```
148 fn get_cells(
149 &self,
150 tx_hash: TxHash,
151 indices_bitarray: B128,
152 ) -> Result<Option<Vec<Cell>>, BlobStoreError>;
153
154 /// Data size of all transactions in the blob store.
155 fn data_size_hint(&self) -> Option<usize>;
156
157 /// How many blobs are in the blob store.
158 fn blobs_len(&self) -> usize;
159}
160
161/// Error variants that can occur when interacting with a blob store.
162#[derive(Debug, thiserror::Error)]
163pub enum BlobStoreError {
164 /// Thrown if the blob sidecar is not found for a given transaction hash but was required.
165 #[error("blob sidecar not found for transaction {0:?}")]
166 MissingSidecar(B256),
167 /// Failed to decode the stored blob data.
168 #[error("failed to decode blob data: {0}")]
169 DecodeError(#[from] alloy_rlp::Error),
170 /// Other implementation specific error.
171 #[error(transparent)]
172 Other(Box<dyn core::error::Error + Send + Sync>),
173}
174
175/// Keeps track of the size of the blob store.
176#[derive(Debug, Default)]
177pub(crate) struct BlobStoreSize {
178 data_size: AtomicUsize,
179 num_blobs: AtomicUsize,
180}
181
182impl BlobStoreSize {
183 #[inline]
184 pub(crate) fn add_size(&self, add: usize) {
185 self.data_size.fetch_add(add, Ordering::Relaxed);
186 }
187
188 #[inline]
189 pub(crate) fn sub_size(&self, sub: usize) {
190 let _ = self.data_size.try_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
191 Some(current.saturating_sub(sub))
192 });
193 }
194
195 #[inline]
196 pub(crate) fn update_len(&self, len: usize) {
197 self.num_blobs.store(len, Ordering::Relaxed);
198 }
199
200 #[inline]
201 pub(crate) fn inc_len(&self, add: usize) {
202 self.num_blobs.fetch_add(add, Ordering::Relaxed);
203 }
204
205 #[inline]
206 pub(crate) fn sub_len(&self, sub: usize) {
207 let _ = self.num_blobs.try_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
208 Some(current.saturating_sub(sub))
209 });
210 }
211
212 #[inline]
213 pub(crate) fn data_size(&self) -> usize {
214 self.data_size.load(Ordering::Relaxed)
215 }
216
217 #[inline]
218 pub(crate) fn blobs_len(&self) -> usize {
219 self.num_blobs.load(Ordering::Relaxed)
220 }
221}
222
223impl PartialEq for BlobStoreSize {
224 fn eq(&self, other: &Self) -> bool {
225 self.data_size.load(Ordering::Relaxed) == other.data_size.load(Ordering::Relaxed) &&
226 self.num_blobs.load(Ordering::Relaxed) == other.num_blobs.load(Ordering::Relaxed)
227 }
228}
229
230/// Statistics for the cleanup operation.
231#[derive(Debug, Clone, Default, PartialEq, Eq)]
232pub struct BlobStoreCleanupStat {
233 /// the number of successfully deleted blobs
234 pub delete_succeed: usize,
235 /// the number of failed deletions
236 pub delete_failed: usize,
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[expect(dead_code)]
244 struct DynStore {
245 store: Box<dyn BlobStore>,
246 }
247}