oasis_core_runtime/consensus/tendermint/verifier/store/
state.rs

1use std::sync::Arc;
2
3use sgx_isa::Keypolicy;
4use tendermint_light_client::{
5    store::LightStore,
6    types::{LightBlock as TMLightBlock, Status},
7};
8
9use crate::{
10    common::{
11        namespace::Namespace,
12        sgx::{seal, EnclaveIdentity},
13        version::Version,
14    },
15    consensus::verifier::{Error, TrustRoot},
16    protocol::ProtocolUntrustedLocalStorage,
17    storage::KeyValue,
18    Protocol, TeeType, BUILD_INFO,
19};
20
21/// Storage key prefix under which the sealed trusted state is stored in
22/// the untrusted local storage.
23///
24/// The actual key includes the MRENCLAVE to support upgrades.
25const TRUSTED_STATE_STORAGE_KEY_PREFIX: &str = "tendermint.verifier.trusted_state";
26
27/// Domain separation context for the trusted state.
28const TRUSTED_STATE_CONTEXT: &[u8] = b"oasis-core/verifier: trusted state";
29
30/// An encoded Tendermint light block.
31#[derive(Debug, Clone)]
32pub struct EncodedLightBlock(TMLightBlock);
33
34impl From<TMLightBlock> for EncodedLightBlock {
35    fn from(value: TMLightBlock) -> Self {
36        Self(value)
37    }
38}
39
40impl From<EncodedLightBlock> for TMLightBlock {
41    fn from(value: EncodedLightBlock) -> Self {
42        value.0
43    }
44}
45
46impl cbor::Encode for EncodedLightBlock {
47    fn into_cbor_value(self) -> cbor::Value {
48        cbor::serde::to_value(&self.0).unwrap()
49    }
50}
51
52impl cbor::Decode for EncodedLightBlock {
53    fn try_from_cbor_value(value: cbor::Value) -> Result<Self, cbor::DecodeError> {
54        cbor::serde::from_value(value)
55            .map_err(|_| cbor::DecodeError::ParsingFailed)
56            .map(Self)
57    }
58}
59
60/// Trusted state containing trust root and trusted light block.
61#[derive(Debug, Clone, Default, cbor::Encode, cbor::Decode)]
62pub struct TrustedState {
63    /// Trust root.
64    pub trust_root: TrustRoot,
65    /// Trusted light blocks, ordered by height from lowest to highest.
66    ///
67    /// Optional as we don't want to force trusted state for embedded trust
68    /// root to have a matching trusted light block.
69    pub trusted_blocks: Vec<EncodedLightBlock>,
70}
71
72/// Untrusted local storage for storing the sealed latest trusted root.
73pub struct TrustedStateStore {
74    runtime_id: Namespace,
75    chain_context: String,
76    untrusted_local_store: ProtocolUntrustedLocalStorage,
77}
78
79impl TrustedStateStore {
80    /// Create a new trusted state local store.
81    pub fn new(runtime_id: Namespace, chain_context: String, protocol: Arc<Protocol>) -> Self {
82        let untrusted_local_store = ProtocolUntrustedLocalStorage::new(protocol);
83
84        Self {
85            runtime_id,
86            chain_context,
87            untrusted_local_store,
88        }
89    }
90
91    /// Persist latest trusted state from the in-memory light store.
92    ///
93    /// # Panics
94    ///
95    /// Panics in case the light store does not have any blocks or if insertion to the underlying
96    /// runtime's untrusted local store fails.
97    pub fn save(&self, runtime_version: Version, store: &Box<dyn LightStore>) {
98        if BUILD_INFO.tee_type == TeeType::Tdx {
99            // TODO: Currently TDX does not have sealing capabilities, so we just do not persist
100            //       anything as we can't seal secrets until we have CPU-bound key derivation.
101            return;
102        }
103
104        let lowest_block = store.lowest(Status::Trusted).unwrap();
105        let highest_block = store.highest(Status::Trusted).unwrap();
106
107        // Generate a new trust root from the highest trusted block.
108        let trust_root = TrustRoot {
109            height: highest_block.height().into(),
110            hash: highest_block.signed_header.header.hash().to_string(),
111            runtime_id: self.runtime_id,
112            chain_context: self.chain_context.clone(),
113        };
114
115        let trusted_state = TrustedState {
116            trust_root,
117            trusted_blocks: vec![lowest_block.into(), highest_block.into()],
118        };
119
120        // Serialize and seal the trusted state.
121        let raw = cbor::to_vec(trusted_state);
122        let sealed = seal::seal(Keypolicy::MRENCLAVE, TRUSTED_STATE_CONTEXT, &raw);
123
124        // Store the trusted state.
125        self.untrusted_local_store
126            .insert(Self::derive_storage_key(runtime_version), sealed)
127            .unwrap();
128    }
129
130    /// Attempts to load previously sealed trusted state.
131    ///
132    /// If no sealed trusted state is available, it returns state based on the passed trust root.
133    pub fn load(
134        &self,
135        runtime_version: Version,
136        trust_root: &TrustRoot,
137    ) -> Result<TrustedState, Error> {
138        if BUILD_INFO.tee_type == TeeType::Tdx {
139            // TODO: Currently TDX does not have sealing capabilities, so we just do not persist
140            //       anything as we can't seal secrets until we have CPU-bound key derivation.
141            return Ok(TrustedState {
142                trust_root: trust_root.clone(),
143                trusted_blocks: vec![],
144            });
145        }
146
147        // Attempt to load the previously sealed trusted state.
148        let untrusted_value = self
149            .untrusted_local_store
150            .get(Self::derive_storage_key(runtime_version))
151            .map_err(|_| Error::TrustedStateLoadingFailed)?;
152        if untrusted_value.is_empty() {
153            return Ok(TrustedState {
154                trust_root: trust_root.clone(),
155                trusted_blocks: vec![],
156            });
157        }
158
159        // Unseal the sealed trusted state.
160        let raw = seal::unseal(
161            Keypolicy::MRENCLAVE,
162            TRUSTED_STATE_CONTEXT,
163            &untrusted_value,
164        )
165        .map_err(|_| Error::TrustedStateLoadingFailed)?
166        .unwrap();
167
168        let trusted_state: TrustedState =
169            cbor::from_slice(&raw).expect("corrupted sealed trusted state");
170
171        Ok(trusted_state)
172    }
173
174    fn derive_storage_key(runtime_version: Version) -> Vec<u8> {
175        // Namespace storage key by MRENCLAVE as we can only unseal our own sealed data and we need
176        // to support upgrades. We assume that an upgrade will include an up-to-date trusted state
177        // anyway.
178        format!(
179            "{}.{}.{:x}",
180            TRUSTED_STATE_STORAGE_KEY_PREFIX,
181            u64::from(runtime_version),
182            EnclaveIdentity::current()
183                .map(|eid| eid.mr_enclave)
184                .unwrap_or_default()
185        )
186        .into_bytes()
187    }
188}