Skip to main content

reth_storage_api/
metadata.rs

1//! Metadata provider trait for reading and writing node metadata.
2
3use alloc::vec::Vec;
4use reth_db_api::models::{SnapAttempt, StorageSettings, SNAP_ATTEMPT_VERSION};
5use reth_storage_errors::provider::{ProviderError, ProviderResult};
6
7/// Metadata keys.
8pub mod keys {
9    /// Storage configuration settings for this node.
10    pub const STORAGE_SETTINGS: &str = "storage_settings";
11
12    /// The snap synchronization attempt that owns downloaded state.
13    pub const SNAP_ATTEMPT: &str = "snap_attempt";
14}
15
16/// Client trait for reading node metadata from the database.
17#[auto_impl::auto_impl(&, Arc)]
18pub trait MetadataProvider: Send {
19    /// Get a metadata value by key
20    fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>>;
21
22    /// Get storage settings for this node.
23    ///
24    /// If the stored metadata can't be deserialized (e.g. the format changed),
25    /// this returns `None` instead of an error so commands like `db clear` can
26    /// still operate without requiring a compatible metadata schema.
27    fn storage_settings(&self) -> ProviderResult<Option<StorageSettings>> {
28        Ok(self
29            .get_metadata(keys::STORAGE_SETTINGS)?
30            .and_then(|bytes| serde_json::from_slice(&bytes).ok()))
31    }
32
33    /// Returns the snap synchronization attempt that owns the downloaded state.
34    ///
35    /// Unlike [`Self::storage_settings`], an unreadable record is an error: its state is already
36    /// in the canonical tables, so reporting it absent would let the node adopt it.
37    fn snap_attempt(&self) -> ProviderResult<Option<SnapAttempt>> {
38        let Some(bytes) = self.get_metadata(keys::SNAP_ATTEMPT)? else { return Ok(None) };
39
40        // Read the version first, so a future build's record is reported as unsupported rather
41        // than as a decode failure.
42        let value: serde_json::Value =
43            serde_json::from_slice(&bytes).map_err(ProviderError::other)?;
44        let found = value.get("version").and_then(serde_json::Value::as_u64);
45        if found != Some(SNAP_ATTEMPT_VERSION as u64) {
46            return Err(ProviderError::UnsupportedSnapAttemptVersion {
47                found,
48                supported: SNAP_ATTEMPT_VERSION,
49            })
50        }
51
52        serde_json::from_slice(&bytes).map(Some).map_err(ProviderError::other)
53    }
54}
55
56/// Client trait for writing node metadata to the database.
57pub trait MetadataWriter: Send {
58    /// Write a metadata value
59    fn write_metadata(&self, key: &str, value: Vec<u8>) -> ProviderResult<()>;
60
61    /// Delete a metadata value.
62    fn delete_metadata(&self, _key: &str) -> ProviderResult<()> {
63        Err(ProviderError::UnsupportedProvider)
64    }
65
66    /// Write storage settings for this node
67    ///
68    /// Be sure to update provider factory cache with
69    /// [`StorageSettingsCache::set_storage_settings_cache`].
70    fn write_storage_settings(&self, settings: StorageSettings) -> ProviderResult<()> {
71        self.write_metadata(
72            keys::STORAGE_SETTINGS,
73            serde_json::to_vec(&settings).map_err(ProviderError::other)?,
74        )
75    }
76
77    /// Writes the snap synchronization attempt that owns the downloaded state.
78    fn write_snap_attempt(&self, attempt: &SnapAttempt) -> ProviderResult<()> {
79        self.write_metadata(
80            keys::SNAP_ATTEMPT,
81            serde_json::to_vec(attempt).map_err(ProviderError::other)?,
82        )
83    }
84}
85
86/// Trait for caching storage settings on a provider factory.
87pub trait StorageSettingsCache: Send {
88    /// Gets the cached storage settings.
89    fn cached_storage_settings(&self) -> StorageSettings;
90
91    /// Sets the storage settings of this `ProviderFactory`.
92    ///
93    /// IMPORTANT: It does not save settings in storage, that should be done by
94    /// [`MetadataWriter::write_storage_settings`]
95    fn set_storage_settings_cache(&self, settings: StorageSettings);
96}
97
98/// Trait for accessing the database directory path.
99#[cfg(feature = "std")]
100pub trait StoragePath: Send {
101    /// Returns the path to the database directory (e.g. `<datadir>/db`).
102    fn storage_path(&self) -> std::path::PathBuf;
103}