Skip to main content

tycho_collator/utils/
vset_cache.rs

1use std::sync::Arc;
2
3use anyhow::Result;
4use tycho_block_util::config::BlockchainConfigExt;
5use tycho_types::cell::HashBytes;
6use tycho_types::models::{BlockchainConfig, ValidatorSet};
7use tycho_util::FastDashMap;
8
9#[derive(Default)]
10pub struct ValidatorSetCache {
11    inner: FastDashMap<VsetType, (HashBytes, Arc<ValidatorSet>)>,
12}
13
14impl ValidatorSetCache {
15    /// Returns cached previous validator set if hash was not changed,
16    /// otherwise parses it from config.
17    pub fn get_prev_validator_set(
18        &self,
19        config: &BlockchainConfig,
20    ) -> Result<Option<(HashBytes, Arc<ValidatorSet>)>> {
21        self.get_validator_set(config, VsetType::Prev)
22    }
23
24    /// Returns cached current validator set if hash was not changed,
25    /// otherwise parses it from config.
26    pub fn get_current_validator_set(
27        &self,
28        config: &BlockchainConfig,
29    ) -> Result<(HashBytes, Arc<ValidatorSet>)> {
30        let (hash, vset) = self.get_validator_set(config, VsetType::Current)?.unwrap();
31        Ok((hash, vset))
32    }
33
34    /// Returns cached next validator set if hash was not changed,
35    /// otherwise parses it from config.
36    pub fn get_next_validator_set(
37        &self,
38        config: &BlockchainConfig,
39    ) -> Result<Option<(HashBytes, Arc<ValidatorSet>)>> {
40        self.get_validator_set(config, VsetType::Next)
41    }
42
43    fn get_validator_set(
44        &self,
45        config: &BlockchainConfig,
46        ty: VsetType,
47    ) -> Result<Option<(HashBytes, Arc<ValidatorSet>)>> {
48        let Some(vset) = match ty {
49            VsetType::Prev => config.get_prev_validator_set_raw(),
50            VsetType::Current => config.get_current_validator_set_raw().map(Some),
51            VsetType::Next => config.get_next_validator_set_raw(),
52        }?
53        else {
54            return Ok(None);
55        };
56
57        let vset_hash = vset.repr_hash();
58
59        // TODO: Use fixed array and ArcSwap with compare_exchange
60        let res = match self.inner.entry(ty) {
61            tycho_util::DashMapEntry::Occupied(mut entry) => {
62                let (existing_hash, existing_vset) = entry.get();
63                if vset_hash != existing_hash {
64                    let vset = Arc::new(vset.parse::<ValidatorSet>()?);
65                    entry.insert((*vset_hash, vset.clone()));
66                    (*vset_hash, vset)
67                } else {
68                    (*existing_hash, existing_vset.clone())
69                }
70            }
71            tycho_util::DashMapEntry::Vacant(entry) => {
72                let vset = Arc::new(vset.parse::<ValidatorSet>()?);
73                entry.insert((*vset_hash, vset.clone()));
74                (*vset_hash, vset)
75            }
76        };
77
78        Ok(Some(res))
79    }
80}
81
82#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
83enum VsetType {
84    Prev,
85    Current,
86    Next,
87}