oasis_core_runtime/consensus/tendermint/verifier/
noop.rs

1use std::sync::{Arc, Mutex};
2
3use anyhow::anyhow;
4use async_trait::async_trait;
5use slog::info;
6
7use crate::{
8    common::{logger::get_logger, namespace::Namespace},
9    consensus::{
10        beacon::EpochTime,
11        roothash::Header,
12        state::ConsensusState,
13        tendermint::decode_light_block,
14        transaction::Transaction,
15        verifier::{self, Error},
16        BlockMetadata, Event, LightBlock, HEIGHT_LATEST, METHOD_META,
17    },
18    protocol::Protocol,
19    storage::mkvs::{Root, RootType},
20    types::{Body, EventKind, HostFetchConsensusEventsRequest, HostFetchConsensusEventsResponse},
21};
22
23struct Inner {
24    latest_height: Option<u64>,
25}
26
27/// A verifier which performs no verification.
28pub struct NopVerifier {
29    protocol: Arc<Protocol>,
30    inner: Arc<Mutex<Inner>>,
31}
32
33impl NopVerifier {
34    /// Create a new non-verifying verifier.
35    pub fn new(protocol: Arc<Protocol>) -> Self {
36        Self {
37            protocol,
38            inner: Arc::new(Mutex::new(Inner {
39                latest_height: None,
40            })),
41        }
42    }
43
44    /// Start the non-verifying verifier.
45    pub fn start(&self) {
46        let logger = get_logger("consensus/cometbft/verifier");
47        info!(logger, "Starting consensus noop verifier");
48    }
49
50    async fn fetch_light_block(&self, height: u64) -> Result<LightBlock, Error> {
51        let result = self
52            .protocol
53            .call_host_async(Body::HostFetchConsensusBlockRequest { height })
54            .await
55            .map_err(|err| Error::VerificationFailed(err.into()))?;
56
57        match result {
58            Body::HostFetchConsensusBlockResponse { block } => Ok(block),
59            _ => Err(Error::VerificationFailed(anyhow!("bad response from host"))),
60        }
61    }
62}
63
64#[async_trait]
65impl verifier::Verifier for NopVerifier {
66    async fn sync(&self, height: u64) -> Result<(), Error> {
67        let mut inner = self.inner.lock().unwrap();
68        inner.latest_height = Some(height);
69
70        Ok(())
71    }
72
73    async fn verify(
74        &self,
75        consensus_block: LightBlock,
76        _runtime_header: Header,
77        _epoch: EpochTime,
78    ) -> Result<ConsensusState, Error> {
79        self.unverified_state(consensus_block).await
80    }
81
82    async fn verify_for_query(
83        &self,
84        consensus_block: LightBlock,
85        _runtime_header: Header,
86        _epoch: EpochTime,
87    ) -> Result<ConsensusState, Error> {
88        self.unverified_state(consensus_block).await
89    }
90
91    async fn unverified_state(&self, consensus_block: LightBlock) -> Result<ConsensusState, Error> {
92        let untrusted_block =
93            decode_light_block(consensus_block).map_err(Error::VerificationFailed)?;
94        // NOTE: No actual verification is performed.
95        let state_root = untrusted_block.get_state_root();
96
97        let mut inner = self.inner.lock().unwrap();
98        if state_root.version + 1 > inner.latest_height.unwrap_or_default() {
99            inner.latest_height = Some(state_root.version + 1);
100        }
101
102        Ok(ConsensusState::from_protocol(
103            self.protocol.clone(),
104            state_root.version + 1,
105            state_root,
106        ))
107    }
108
109    async fn latest_state(&self) -> Result<ConsensusState, Error> {
110        let height = self.latest_height().await?;
111
112        // When latest state is requested we always perform same-block execution verification.
113        let result = self
114            .protocol
115            .call_host_async(Body::HostFetchBlockMetadataTxRequest { height })
116            .await
117            .map_err(|err| Error::StateRoot(err.into()))?;
118
119        // NOTE: This is a noop verifier so we do not verify the Merkle proof.
120        let signed_tx = match result {
121            Body::HostFetchBlockMetadataTxResponse { signed_tx, .. } => signed_tx,
122            _ => return Err(Error::StateRoot(anyhow!("bad response from host"))),
123        };
124
125        let tx: Transaction = cbor::from_slice(signed_tx.blob.as_slice()).map_err(|err| {
126            Error::TransactionVerificationFailed(anyhow!("failed to decode transaction: {}", err))
127        })?;
128
129        if tx.method != METHOD_META {
130            return Err(Error::StateRoot(anyhow!("invalid method name")));
131        }
132
133        let meta: BlockMetadata = cbor::from_value(tx.body).map_err(|err| {
134            Error::StateRoot(anyhow!(
135                "failed to decode block metadata transaction: {}",
136                err
137            ))
138        })?;
139
140        let state_root = Root {
141            namespace: Namespace::default(),
142            version: height,
143            root_type: RootType::State,
144            hash: meta.state_root,
145        };
146
147        Ok(ConsensusState::from_protocol(
148            self.protocol.clone(),
149            state_root.version,
150            state_root,
151        ))
152    }
153
154    async fn state_at(&self, height: u64) -> Result<ConsensusState, Error> {
155        let block = self.fetch_light_block(height).await?;
156        self.unverified_state(block).await
157    }
158
159    async fn events_at(&self, height: u64, kind: EventKind) -> Result<Vec<Event>, Error> {
160        let result = self
161            .protocol
162            .call_host_async(Body::HostFetchConsensusEventsRequest(
163                HostFetchConsensusEventsRequest { height, kind },
164            ))
165            .await
166            .map_err(|err| Error::VerificationFailed(err.into()))?;
167
168        match result {
169            Body::HostFetchConsensusEventsResponse(HostFetchConsensusEventsResponse { events }) => {
170                Ok(events)
171            }
172            _ => Err(Error::VerificationFailed(anyhow!("bad response from host"))),
173        }
174    }
175
176    async fn latest_height(&self) -> Result<u64, Error> {
177        {
178            let inner = self.inner.lock().unwrap();
179            if let Some(latest_height) = inner.latest_height {
180                return Ok(latest_height);
181            }
182        }
183
184        let latest_height = self.fetch_light_block(HEIGHT_LATEST).await?.height;
185        self.sync(latest_height).await?;
186        Ok(latest_height)
187    }
188}