oasis_core_runtime/consensus/state/
beacon.rs1use anyhow::anyhow;
3
4use crate::{
5 common::key_format::{KeyFormat, KeyFormatAtom},
6 consensus::{
7 beacon::{EpochTime, EpochTimeState},
8 state::StateError,
9 },
10 key_format,
11 storage::mkvs::{FallibleMKVS, ImmutableMKVS},
12};
13
14pub struct ImmutableState<'a, T: ImmutableMKVS> {
16 mkvs: &'a T,
17}
18
19impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
20 pub fn new(mkvs: &'a T) -> ImmutableState<'a, T> {
22 ImmutableState { mkvs }
23 }
24}
25
26key_format!(CurrentEpochKeyFmt, 0x40, ());
27key_format!(FutureEpochKeyFmt, 0x41, ());
28
29impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
30 pub fn epoch(&self) -> Result<EpochTime, StateError> {
32 self.epoch_state().map(|es| es.epoch)
33 }
34
35 pub fn epoch_state(&self) -> Result<EpochTimeState, StateError> {
37 match self.mkvs.get(&CurrentEpochKeyFmt(()).encode()) {
38 Ok(Some(b)) => {
39 let state: EpochTimeState =
40 cbor::from_slice(&b).map_err(|err| StateError::Unavailable(anyhow!(err)))?;
41 Ok(state)
42 }
43 Ok(None) => Ok(EpochTimeState::default()),
44 Err(err) => Err(StateError::Unavailable(anyhow!(err))),
45 }
46 }
47
48 pub fn future_epoch(&self) -> Result<EpochTime, StateError> {
50 self.future_epoch_state().map(|es| es.epoch)
51 }
52
53 pub fn future_epoch_state(&self) -> Result<EpochTimeState, StateError> {
55 match self.mkvs.get(&FutureEpochKeyFmt(()).encode()) {
56 Ok(Some(b)) => {
57 let state: EpochTimeState =
58 cbor::from_slice(&b).map_err(|err| StateError::Unavailable(anyhow!(err)))?;
59 Ok(state)
60 }
61 Ok(None) => Ok(EpochTimeState::default()),
62 Err(err) => Err(StateError::Unavailable(anyhow!(err))),
63 }
64 }
65}
66
67pub struct MutableState;
69
70impl MutableState {
71 pub fn set_epoch_state<S: FallibleMKVS>(
73 mkvs: &mut S,
74 epoch_state: EpochTimeState,
75 ) -> Result<(), StateError> {
76 mkvs.insert(&CurrentEpochKeyFmt(()).encode(), &cbor::to_vec(epoch_state))?;
77 Ok(())
78 }
79
80 pub fn set_future_epoch_state<S: FallibleMKVS>(
82 mkvs: &mut S,
83 epoch_state: EpochTimeState,
84 ) -> Result<(), StateError> {
85 mkvs.insert(&FutureEpochKeyFmt(()).encode(), &cbor::to_vec(epoch_state))?;
86 Ok(())
87 }
88}
89
90#[cfg(test)]
91mod test {
92 use crate::{
93 common::crypto::hash::Hash,
94 storage::mkvs::{
95 interop::{Fixture, ProtocolServer},
96 sync::NoopReadSyncer,
97 Root, RootType, Tree,
98 },
99 };
100
101 use super::*;
102
103 #[test]
104 fn test_mutable_state() {
105 let mut mkvs = Tree::builder()
106 .with_root_type(RootType::State)
107 .build(Box::new(NoopReadSyncer));
108
109 MutableState::set_epoch_state(
110 &mut mkvs,
111 EpochTimeState {
112 epoch: 10,
113 height: 100,
114 },
115 )
116 .unwrap();
117
118 MutableState::set_future_epoch_state(
119 &mut mkvs,
120 EpochTimeState {
121 epoch: 11,
122 height: 110,
123 },
124 )
125 .unwrap();
126
127 let beacon_state = ImmutableState::new(&mkvs);
128
129 let epoch_state = beacon_state
131 .epoch_state()
132 .expect("epoch state query should work");
133 assert_eq!(10u64, epoch_state.epoch, "expected epoch should match");
134 assert_eq!(100i64, epoch_state.height, "expected height should match");
135
136 let epoch_state = beacon_state
138 .future_epoch_state()
139 .expect("future epoch state query should work");
140 assert_eq!(11u64, epoch_state.epoch, "expected epoch should match");
141 assert_eq!(110i64, epoch_state.height, "expected height should match");
142 }
143
144 #[test]
145 fn test_beacon_state_interop() {
146 let server = ProtocolServer::new(Fixture::ConsensusMock.into());
154 let mock_consensus_root = Root {
155 version: 1,
156 root_type: RootType::State,
157 hash: Hash::from("8e39bf193f8a954ab8f8d7cb6388c591fd0785ea060bbd8e3752e266b54499d3"),
158 ..Default::default()
159 };
160 let mkvs = Tree::builder()
161 .with_capacity(100_000, 10_000_000)
162 .with_root(mock_consensus_root)
163 .build(server.read_sync());
164 let beacon_state = ImmutableState::new(&mkvs);
165
166 let epoch = beacon_state.epoch().expect("epoch query should work");
168 assert_eq!(42u64, epoch, "expected epoch should match");
169
170 let epoch_state = beacon_state
172 .epoch_state()
173 .expect("epoch state query should work");
174 assert_eq!(42u64, epoch_state.epoch, "expected epoch should match");
175 assert_eq!(13i64, epoch_state.height, "expected height should match");
176
177 let epoch = beacon_state
179 .future_epoch()
180 .expect("future epoch query should work");
181 assert_eq!(43u64, epoch, "expected future epoch should match");
182
183 let epoch_state = beacon_state
185 .future_epoch_state()
186 .expect("future epoch state query should work");
187 assert_eq!(43u64, epoch_state.epoch, "expected epoch should match");
188 assert_eq!(15i64, epoch_state.height, "expected height should match");
189 }
190}