oasis_core_runtime/consensus/tendermint/verifier/
predicates.rs

1use anyhow::anyhow;
2
3use crate::{
4    common::namespace::Namespace,
5    consensus::{
6        beacon::EpochTime,
7        roothash::Header,
8        state::{
9            beacon::ImmutableState as BeaconState, roothash::ImmutableState as RoothashState,
10            ConsensusState,
11        },
12        tendermint::{verifier::Cache, LightBlockMeta},
13        verifier::Error,
14        LightBlock,
15    },
16};
17
18/// Verifies that the namespace in the runtime header matches the trusted namespace.
19pub fn verify_namespace(trusted: Namespace, runtime_header: &Header) -> Result<(), Error> {
20    if trusted != runtime_header.namespace {
21        return Err(Error::VerificationFailed(anyhow!(
22            "header namespace does not match trusted runtime id"
23        )));
24    }
25
26    Ok(())
27}
28
29/// Verifies that consensus height has correctly advanced since the last update.
30pub fn verify_consensus_advance(cache: &Cache, consensus_block: &LightBlock) -> Result<(), Error> {
31    if consensus_block.height < cache.last_verified_height {
32        // Reject requests for earlier heights.
33        return Err(Error::VerificationFailed(anyhow!(
34            "height seems to have moved backwards"
35        )));
36    }
37
38    Ok(())
39}
40
41/// Verifies that the round has correctly advanced since the last update.
42pub fn verify_round_advance(
43    cache: &Cache,
44    runtime_header: &Header,
45    consensus_block: &LightBlock,
46    epoch: EpochTime,
47) -> Result<(), Error> {
48    if runtime_header.round < cache.last_verified_round {
49        // Reject requests for earlier rounds.
50        return Err(Error::VerificationFailed(anyhow!(
51            "round seems to have moved backwards"
52        )));
53    }
54    if epoch < cache.last_verified_epoch {
55        // Reject requests for earlier epochs.
56        return Err(Error::VerificationFailed(anyhow!(
57            "epoch seems to have moved backwards"
58        )));
59    }
60
61    // If round has advanced make sure that consensus height has also advanced as a round can
62    // only be finalized in a subsequent consensus block. This is to avoid a situation where
63    // one would keep feeding the same consensus block for subsequent rounds.
64    if runtime_header.round > cache.last_verified_round
65        && consensus_block.height <= cache.last_verified_height
66    {
67        return Err(Error::VerificationFailed(anyhow!(
68            "consensus height did not advance but runtime round did"
69        )));
70    }
71
72    Ok(())
73}
74
75/// Verifies that the runtime header has time consistent with the consensus header.
76pub fn verify_time(runtime_header: &Header, consensus_block: &LightBlockMeta) -> Result<(), Error> {
77    let consensus_header = &consensus_block
78        .signed_header
79        .as_ref()
80        .ok_or_else(|| Error::VerificationFailed(anyhow!("missing signed header")))?
81        .header;
82    if runtime_header.timestamp != consensus_header.time.unix_timestamp() as u64 {
83        return Err(Error::VerificationFailed(anyhow!(
84            "runtime block timestamp inconsistent with consensus time"
85        )));
86    }
87
88    Ok(())
89}
90
91/// Verifies that the runtime header has state root consistent with consensus state.
92///
93/// Assumes the namespace in the runtime header has already been verified via `verify_namespace`.
94pub fn verify_state_root(state: &ConsensusState, runtime_header: &Header) -> Result<(), Error> {
95    let roothash_state = RoothashState::new(&state);
96    let state_root = roothash_state
97        .state_root(runtime_header.namespace)
98        .map_err(|err| {
99            Error::VerificationFailed(anyhow!("failed to retrieve trusted state root: {}", err))
100        })?;
101
102    if runtime_header.state_root != state_root {
103        return Err(Error::VerificationFailed(anyhow!(
104            "state root mismatch (expected: {} got: {})",
105            state_root,
106            runtime_header.state_root
107        )));
108    }
109
110    Ok(())
111}
112
113/// Verifies that the epoch is consistent with consensus state.
114pub fn verify_epoch(state: &ConsensusState, epoch: EpochTime) -> Result<(), Error> {
115    let beacon_state = BeaconState::new(&state);
116    let current_epoch = beacon_state
117        .epoch()
118        .map_err(|err| Error::VerificationFailed(anyhow!("failed to retrieve epoch: {}", err)))?;
119
120    if current_epoch != epoch {
121        return Err(Error::VerificationFailed(anyhow!(
122            "epoch number mismatch (expected: {} got: {})",
123            current_epoch,
124            epoch,
125        )));
126    }
127
128    Ok(())
129}