1use std::{collections::BTreeMap, sync::Arc};
2
3use async_trait::async_trait;
4use bytes::Bytes;
5
6use melstructs::{BlockHeight, CoinID, CoinValue, Header, NetID, TxHash};
7use mini_moka::sync::Cache;
8use once_cell::sync::Lazy;
9use parking_lot::RwLock;
10
11use stdcode::StdcodeSerializeExt;
12use tmelcrypt::{Ed25519PK, HashVal};
13
14use crate::Substate;
15
16pub(crate) static GLOBAL_CACHE: Lazy<RwLock<Arc<dyn StateCache>>> =
18 Lazy::new(|| RwLock::new(Arc::new(InMemoryStateCache::new(100_000_000))));
19
20pub fn set_global_cache(cache: impl StateCache) {
22 *GLOBAL_CACHE.write() = Arc::new(cache);
23}
24
25pub struct InMemoryStateCache {
27 inner: Cache<Bytes, Bytes>,
28}
29
30impl InMemoryStateCache {
31 pub fn new(max_bytes: usize) -> Self {
33 Self {
34 inner: Cache::builder()
35 .max_capacity(max_bytes as u64)
36 .weigher(|k: &Bytes, v: &Bytes| (k.len() + v.len() + 10) as u32)
37 .build(),
38 }
39 }
40}
41
42#[async_trait]
43impl StateCache for InMemoryStateCache {
44 async fn get_blob(&self, key: &[u8]) -> Option<Bytes> {
45 let key: Bytes = key.to_vec().into();
46 let res = self.inner.get(&key);
47 log::debug!("memcache: {:?} hit? {}", key, res.is_some());
48 res
49 }
50
51 async fn insert_blob(&self, key: &[u8], value: &[u8]) {
52 self.inner
53 .insert(Bytes::copy_from_slice(key), Bytes::copy_from_slice(value));
54 }
55}
56
57#[async_trait]
59pub trait StateCache: Send + Sync + 'static {
60 async fn get_blob(&self, key: &[u8]) -> Option<Bytes>;
62
63 async fn insert_blob(&self, key: &[u8], value: &[u8]);
65
66 async fn get_header(&self, network: NetID, height: BlockHeight) -> Option<Header> {
68 stdcode::deserialize(
69 &self
70 .get_blob(&("header", network, height).stdcode())
71 .await?,
72 )
73 .ok()
74 }
75
76 async fn get_staker_votes(&self, epoch: u64) -> Option<BTreeMap<Ed25519PK, CoinValue>> {
78 stdcode::deserialize(&self.get_blob(&("staker_votes", epoch).stdcode()).await?).ok()
79 }
80
81 async fn insert_staker_votes(&self, epoch: u64, votes: BTreeMap<Ed25519PK, CoinValue>) {
83 self.insert_blob(&("staker_votes", epoch).stdcode(), &votes.stdcode())
84 .await;
85 }
86
87 async fn get_spend_location(&self, coin: CoinID) -> Option<(TxHash, BlockHeight)> {
89 stdcode::deserialize(&self.get_blob(&("spend_location", coin).stdcode()).await?).ok()
90 }
91
92 async fn insert_spend_location(&self, coin: CoinID, txhash: TxHash, height: BlockHeight) {
94 self.insert_blob(
95 &("spend_location", coin).stdcode(),
96 &(txhash, height).stdcode(),
97 )
98 .await;
99 }
100
101 async fn insert_header(&self, network: NetID, height: BlockHeight, header: Header) {
103 self.insert_blob(&("header", network, height).stdcode(), &header.stdcode())
104 .await;
105 }
106
107 async fn get_smt_branch(
109 &self,
110 header_hash: HashVal,
111 tree: Substate,
112 branch: HashVal,
113 ) -> Option<Bytes> {
114 self.get_blob(&("smt", header_hash, tree, branch).stdcode())
115 .await
116 }
117
118 async fn insert_smt_branch(
120 &self,
121 header_hash: HashVal,
122 tree: Substate,
123 branch: HashVal,
124 value: &[u8],
125 ) {
126 self.insert_blob(&("smt", header_hash, tree, branch).stdcode(), value)
127 .await
128 }
129}