reth_transaction_pool/blobstore/
mod.rs

1//! Storage for blob data of EIP4844 transactions.
2
3use alloy_eips::eip4844::{BlobAndProofV1, BlobTransactionSidecar};
4use alloy_primitives::B256;
5pub use disk::{DiskFileBlobStore, DiskFileBlobStoreConfig, OpenDiskFileBlobStore};
6pub use mem::InMemoryBlobStore;
7pub use noop::NoopBlobStore;
8use std::{
9    fmt,
10    sync::{
11        atomic::{AtomicUsize, Ordering},
12        Arc,
13    },
14};
15pub use tracker::{BlobStoreCanonTracker, BlobStoreUpdates};
16
17pub mod disk;
18mod mem;
19mod noop;
20mod tracker;
21
22/// A blob store that can be used to store blob data of EIP4844 transactions.
23///
24/// This type is responsible for keeping track of blob data until it is no longer needed (after
25/// finalization).
26///
27/// Note: this is Clone because it is expected to be wrapped in an Arc.
28pub trait BlobStore: fmt::Debug + Send + Sync + 'static {
29    /// Inserts the blob sidecar into the store
30    fn insert(&self, tx: B256, data: BlobTransactionSidecar) -> Result<(), BlobStoreError>;
31
32    /// Inserts multiple blob sidecars into the store
33    fn insert_all(&self, txs: Vec<(B256, BlobTransactionSidecar)>) -> Result<(), BlobStoreError>;
34
35    /// Deletes the blob sidecar from the store
36    fn delete(&self, tx: B256) -> Result<(), BlobStoreError>;
37
38    /// Deletes multiple blob sidecars from the store
39    fn delete_all(&self, txs: Vec<B256>) -> Result<(), BlobStoreError>;
40
41    /// A maintenance function that can be called periodically to clean up the blob store, returns
42    /// the number of successfully deleted blobs and the number of failed deletions.
43    ///
44    /// This is intended to be called in the background to clean up any old or unused data, in case
45    /// the store uses deferred cleanup: [`DiskFileBlobStore`]
46    fn cleanup(&self) -> BlobStoreCleanupStat;
47
48    /// Retrieves the decoded blob data for the given transaction hash.
49    fn get(&self, tx: B256) -> Result<Option<Arc<BlobTransactionSidecar>>, BlobStoreError>;
50
51    /// Checks if the given transaction hash is in the blob store.
52    fn contains(&self, tx: B256) -> Result<bool, BlobStoreError>;
53
54    /// Retrieves all decoded blob data for the given transaction hashes.
55    ///
56    /// This only returns the blobs that were found in the store.
57    /// If there's no blob it will not be returned.
58    ///
59    /// Note: this is not guaranteed to return the blobs in the same order as the input.
60    fn get_all(
61        &self,
62        txs: Vec<B256>,
63    ) -> Result<Vec<(B256, Arc<BlobTransactionSidecar>)>, BlobStoreError>;
64
65    /// Returns the exact [`BlobTransactionSidecar`] for the given transaction hashes in the exact
66    /// order they were requested.
67    ///
68    /// Returns an error if any of the blobs are not found in the blob store.
69    fn get_exact(&self, txs: Vec<B256>)
70        -> Result<Vec<Arc<BlobTransactionSidecar>>, BlobStoreError>;
71
72    /// Return the [`BlobTransactionSidecar`]s for a list of blob versioned hashes.
73    fn get_by_versioned_hashes(
74        &self,
75        versioned_hashes: &[B256],
76    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError>;
77
78    /// Data size of all transactions in the blob store.
79    fn data_size_hint(&self) -> Option<usize>;
80
81    /// How many blobs are in the blob store.
82    fn blobs_len(&self) -> usize;
83}
84
85/// Error variants that can occur when interacting with a blob store.
86#[derive(Debug, thiserror::Error)]
87pub enum BlobStoreError {
88    /// Thrown if the blob sidecar is not found for a given transaction hash but was required.
89    #[error("blob sidecar not found for transaction {0:?}")]
90    MissingSidecar(B256),
91    /// Failed to decode the stored blob data.
92    #[error("failed to decode blob data: {0}")]
93    DecodeError(#[from] alloy_rlp::Error),
94    /// Other implementation specific error.
95    #[error(transparent)]
96    Other(Box<dyn core::error::Error + Send + Sync>),
97}
98
99/// Keeps track of the size of the blob store.
100#[derive(Debug, Default)]
101pub(crate) struct BlobStoreSize {
102    data_size: AtomicUsize,
103    num_blobs: AtomicUsize,
104}
105
106impl BlobStoreSize {
107    #[inline]
108    pub(crate) fn add_size(&self, add: usize) {
109        self.data_size.fetch_add(add, Ordering::Relaxed);
110    }
111
112    #[inline]
113    pub(crate) fn sub_size(&self, sub: usize) {
114        let _ = self.data_size.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
115            Some(current.saturating_sub(sub))
116        });
117    }
118
119    #[inline]
120    pub(crate) fn update_len(&self, len: usize) {
121        self.num_blobs.store(len, Ordering::Relaxed);
122    }
123
124    #[inline]
125    pub(crate) fn inc_len(&self, add: usize) {
126        self.num_blobs.fetch_add(add, Ordering::Relaxed);
127    }
128
129    #[inline]
130    pub(crate) fn sub_len(&self, sub: usize) {
131        let _ = self.num_blobs.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
132            Some(current.saturating_sub(sub))
133        });
134    }
135
136    #[inline]
137    pub(crate) fn data_size(&self) -> usize {
138        self.data_size.load(Ordering::Relaxed)
139    }
140
141    #[inline]
142    pub(crate) fn blobs_len(&self) -> usize {
143        self.num_blobs.load(Ordering::Relaxed)
144    }
145}
146
147impl PartialEq for BlobStoreSize {
148    fn eq(&self, other: &Self) -> bool {
149        self.data_size.load(Ordering::Relaxed) == other.data_size.load(Ordering::Relaxed) &&
150            self.num_blobs.load(Ordering::Relaxed) == other.num_blobs.load(Ordering::Relaxed)
151    }
152}
153
154/// Statistics for the cleanup operation.
155#[derive(Debug, Clone, Default, PartialEq, Eq)]
156pub struct BlobStoreCleanupStat {
157    /// the number of successfully deleted blobs
158    pub delete_succeed: usize,
159    /// the number of failed deletions
160    pub delete_failed: usize,
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[expect(dead_code)]
168    struct DynStore {
169        store: Box<dyn BlobStore>,
170    }
171}