drive/cache/
protocol_version.rs

1use crate::drive::Drive;
2use crate::error::cache::CacheError;
3use crate::error::Error;
4use dpp::util::deserializer::ProtocolVersion;
5use grovedb::TransactionArg;
6use nohash_hasher::IntMap;
7use platform_version::version::drive_versions::DriveVersion;
8
9/// ProtocolVersion cache that handles both global and block data
10#[derive(Default)]
11pub struct ProtocolVersionsCache {
12    /// The current global cache for protocol versions
13    // TODO: If we persist this in the state and it should be loaded for correct
14    //  use then it's not actually the cache. Move out of cache because it's confusing
15    pub global_cache: IntMap<ProtocolVersion, u64>,
16    block_cache: IntMap<ProtocolVersion, u64>,
17    loaded: bool,
18    is_global_cache_blocked: bool,
19}
20
21#[cfg(feature = "server")]
22impl ProtocolVersionsCache {
23    /// Create a new ProtocolVersionsCache instance
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Load the protocol versions cache from disk if needed
29    pub fn load_if_needed(
30        &mut self,
31        drive: &Drive,
32        transaction: TransactionArg,
33        drive_version: &DriveVersion,
34    ) -> Result<(), Error> {
35        if !self.loaded {
36            self.global_cache = drive.fetch_versions_with_counter(transaction, drive_version)?;
37            self.loaded = true;
38        };
39        Ok(())
40    }
41
42    /// Sets the protocol version to the block cache
43    pub fn set_block_cache_version_count(&mut self, version: ProtocolVersion, count: u64) {
44        self.block_cache.insert(version, count);
45    }
46
47    /// Tries to get a version from block cache if present
48    /// if block cache doesn't have the version set
49    /// then it tries get the version from global cache
50    pub fn get(&self, version: &ProtocolVersion) -> Result<Option<&u64>, Error> {
51        if self.is_global_cache_blocked {
52            return Err(Error::Cache(CacheError::GlobalCacheIsBlocked));
53        }
54
55        let counter = if let Some(count) = self.block_cache.get(version) {
56            Some(count)
57        } else {
58            self.global_cache.get(version)
59        };
60
61        Ok(counter)
62    }
63
64    /// Disable the global cache to do not allow get counters
65    /// If global cache is blocked then [get] will return an error
66    pub fn block_global_cache(&mut self) {
67        self.is_global_cache_blocked = true;
68    }
69
70    /// Unblock the global cache
71    /// This function enables the normal behaviour of [get] function
72    pub fn unblock_global_cache(&mut self) {
73        self.is_global_cache_blocked = false;
74    }
75
76    /// Merge block cache to global cache
77    pub fn merge_block_cache(&mut self) {
78        self.global_cache.extend(self.block_cache.drain());
79    }
80
81    /// Clears the global cache
82    pub fn clear_global_cache(&mut self) {
83        self.global_cache.clear();
84    }
85
86    /// Clear block cache
87    pub fn clear_block_cache(&mut self) {
88        self.block_cache.clear()
89    }
90
91    /// Collect versions passing threshold
92    pub fn versions_passing_threshold(&self, required_upgraded_hpmns: u64) -> Vec<ProtocolVersion> {
93        let mut cache = self.global_cache.clone();
94
95        cache.extend(self.block_cache.iter());
96        cache
97            .into_iter()
98            .filter_map(|(protocol_version, count)| {
99                if count >= required_upgraded_hpmns {
100                    Some(protocol_version)
101                } else {
102                    None
103                }
104            })
105            .collect::<Vec<ProtocolVersion>>()
106    }
107}