Skip to main content

oasis_core_runtime/consensus/
verifier.rs

1//! Trait for consensus layer verification.
2use std::sync::Arc;
3
4use anyhow::anyhow;
5use async_trait::async_trait;
6use thiserror::Error;
7
8use super::{
9    beacon::EpochTime,
10    roothash::Header,
11    state::{registry::ImmutableState as RegistryState, ConsensusState},
12    Event, LightBlock,
13};
14use crate::{
15    common::{crypto::signature::PublicKey, namespace::Namespace, version::Version},
16    identity::Identity,
17    types::{self, EventKind},
18};
19
20#[derive(Debug, Error)]
21pub enum Error {
22    #[error("builder: {0}")]
23    Builder(#[source] anyhow::Error),
24
25    #[error("verification: {0}")]
26    VerificationFailed(#[source] anyhow::Error),
27
28    #[error("trusted state loading failed")]
29    TrustedStateLoadingFailed,
30
31    #[error("consensus chain context transition failed: {0}")]
32    ChainContextTransitionFailed(#[source] anyhow::Error),
33
34    #[error("freshness verification: {0}")]
35    FreshnessVerificationFailed(#[source] anyhow::Error),
36
37    #[error("transaction verification: {0}")]
38    TransactionVerificationFailed(#[source] anyhow::Error),
39
40    #[error("state root: {0}")]
41    StateRoot(#[source] anyhow::Error),
42
43    #[error("internal consensus verifier error")]
44    Internal,
45}
46
47impl Error {
48    fn code(&self) -> u32 {
49        match self {
50            Error::Builder(_) => 1,
51            Error::VerificationFailed(_) => 2,
52            Error::TrustedStateLoadingFailed => 3,
53            Error::ChainContextTransitionFailed(_) => 4,
54            Error::FreshnessVerificationFailed(_) => 5,
55            Error::TransactionVerificationFailed(_) => 6,
56            Error::StateRoot(_) => 7,
57            Error::Internal => 8,
58        }
59    }
60}
61
62impl From<Error> for types::Error {
63    fn from(e: Error) -> Self {
64        Self {
65            module: "verifier".to_string(),
66            code: e.code(),
67            message: e.to_string(),
68        }
69    }
70}
71
72/// Verifier is the consensus layer state verifier trait.
73#[allow(clippy::double_must_use)]
74#[async_trait]
75pub trait Verifier: Send + Sync {
76    /// Synchronize the verifier state up to including the passed consensus height.
77    async fn sync(&self, height: u64) -> Result<(), Error>;
78
79    /// Verify that the given runtime header is valid at the given consensus layer block and return
80    /// the consensus layer state accessor for that block.
81    ///
82    /// This also verifies that the state is fresh.
83    async fn verify(
84        &self,
85        consensus_block: LightBlock,
86        runtime_header: Header,
87        epoch: EpochTime,
88    ) -> Result<ConsensusState, Error>;
89
90    /// Verify that the given runtime header is valid at the given consensus layer block and return
91    /// the consensus layer state accessor for that block.
92    ///
93    /// This is a relaxed version of the `verify` function that should be used for verifying state
94    /// in queries.
95    async fn verify_for_query(
96        &self,
97        consensus_block: LightBlock,
98        runtime_header: Header,
99        epoch: EpochTime,
100    ) -> Result<ConsensusState, Error>;
101
102    /// Return the consensus layer state accessor for the given consensus layer block WITHOUT
103    /// performing any verification. This method should only be used for operations that do not
104    /// require integrity guarantees.
105    async fn unverified_state(&self, consensus_block: LightBlock) -> Result<ConsensusState, Error>;
106
107    /// Return the latest verified consensus layer state.
108    ///
109    /// # Warning
110    ///
111    /// The state is not verified to be fresh. Use `verify_state_freshness` to perform this
112    /// verification manually if needed.
113    async fn latest_state(&self) -> Result<ConsensusState, Error>;
114
115    /// Return the verified consensus layer state for a given height.
116    ///
117    /// # Warning
118    ///
119    /// The state is not verified to be fresh. Use `verify_state_freshness` to perform this
120    /// verification manually if needed.
121    async fn state_at(&self, height: u64) -> Result<ConsensusState, Error>;
122
123    /// Return the consensus layer events at the given height.
124    ///
125    /// # Warning
126    ///
127    /// Event integrity is currently not verified and it thus relies on replicated computation even
128    /// when using a TEE-enabled runtime.
129    async fn events_at(&self, height: u64, kind: EventKind) -> Result<Vec<Event>, Error>;
130
131    /// Return the latest known consensus layer height.
132    async fn latest_height(&self) -> Result<u64, Error>;
133}
134
135#[allow(clippy::double_must_use)]
136#[async_trait]
137impl<T: ?Sized + Verifier> Verifier for Arc<T> {
138    async fn sync(&self, height: u64) -> Result<(), Error> {
139        Verifier::sync(&**self, height).await
140    }
141
142    async fn verify(
143        &self,
144        consensus_block: LightBlock,
145        runtime_header: Header,
146        epoch: EpochTime,
147    ) -> Result<ConsensusState, Error> {
148        Verifier::verify(&**self, consensus_block, runtime_header, epoch).await
149    }
150
151    async fn verify_for_query(
152        &self,
153        consensus_block: LightBlock,
154        runtime_header: Header,
155        epoch: EpochTime,
156    ) -> Result<ConsensusState, Error> {
157        Verifier::verify_for_query(&**self, consensus_block, runtime_header, epoch).await
158    }
159
160    async fn unverified_state(&self, consensus_block: LightBlock) -> Result<ConsensusState, Error> {
161        Verifier::unverified_state(&**self, consensus_block).await
162    }
163
164    async fn latest_state(&self) -> Result<ConsensusState, Error> {
165        Verifier::latest_state(&**self).await
166    }
167
168    async fn state_at(&self, height: u64) -> Result<ConsensusState, Error> {
169        Verifier::state_at(&**self, height).await
170    }
171
172    async fn events_at(&self, height: u64, kind: EventKind) -> Result<Vec<Event>, Error> {
173        Verifier::events_at(&**self, height, kind).await
174    }
175
176    async fn latest_height(&self) -> Result<u64, Error> {
177        Verifier::latest_height(&**self).await
178    }
179}
180
181/// Consensus layer trust root.
182#[derive(Debug, Clone, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
183pub struct TrustRoot {
184    /// Known trusted height.
185    pub height: u64,
186    /// Known hex-encoded trusted consensus layer header hash.
187    pub hash: String,
188    /// Known runtime identifier.
189    pub runtime_id: Namespace,
190    /// Known consensus chain context.
191    pub chain_context: String,
192}
193
194/// Verify consensus layer state freshness based on our internal state.
195pub fn verify_state_freshness(
196    state: &ConsensusState,
197    identity: &Identity,
198    runtime_id: &Namespace,
199    version: &Version,
200    host_node_id: &PublicKey,
201) -> Result<(), Error> {
202    let registry_state = RegistryState::new(&state);
203
204    let node = registry_state.node(host_node_id).map_err(|err| {
205        Error::VerificationFailed(anyhow!(
206            "failed to retrieve node from the registry: {}",
207            err
208        ))
209    })?;
210    let node = node.ok_or_else(|| {
211        Error::VerificationFailed(anyhow!(
212            "own node ID '{}' not found in registry state",
213            host_node_id,
214        ))
215    })?;
216
217    if !node.has_tee(identity, runtime_id, version) {
218        return Err(Error::VerificationFailed(anyhow!(
219            "own identity not found in registry state"
220        )));
221    }
222
223    Ok(())
224}