Skip to main content

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#[allow(clippy::double_must_use)]
65#[async_trait]
66impl verifier::Verifier for NopVerifier {
67    async fn sync(&self, height: u64) -> Result<(), Error> {
68        let height = self.fetch_light_block(height).await?.height; // Ensure height is valid.
69
70        let mut inner = self.inner.lock().unwrap();
71        inner.latest_height = Some(height);
72
73        Ok(())
74    }
75
76    async fn verify(
77        &self,
78        consensus_block: LightBlock,
79        _runtime_header: Header,
80        _epoch: EpochTime,
81    ) -> Result<ConsensusState, Error> {
82        self.unverified_state(consensus_block).await
83    }
84
85    async fn verify_for_query(
86        &self,
87        consensus_block: LightBlock,
88        _runtime_header: Header,
89        _epoch: EpochTime,
90    ) -> Result<ConsensusState, Error> {
91        self.unverified_state(consensus_block).await
92    }
93
94    async fn unverified_state(&self, consensus_block: LightBlock) -> Result<ConsensusState, Error> {
95        let untrusted_block =
96            decode_light_block(consensus_block).map_err(Error::VerificationFailed)?;
97        // NOTE: No actual verification is performed.
98        let state_root = untrusted_block.get_state_root();
99
100        let mut inner = self.inner.lock().unwrap();
101        if state_root.version + 1 > inner.latest_height.unwrap_or_default() {
102            inner.latest_height = Some(state_root.version + 1);
103        }
104
105        Ok(ConsensusState::from_protocol(
106            self.protocol.clone(),
107            state_root.version + 1,
108            state_root,
109        ))
110    }
111
112    async fn latest_state(&self) -> Result<ConsensusState, Error> {
113        let height = self.latest_height().await?;
114
115        // When latest state is requested we always perform same-block execution verification.
116        let result = self
117            .protocol
118            .call_host_async(Body::HostFetchBlockMetadataTxRequest { height })
119            .await
120            .map_err(|err| Error::StateRoot(err.into()))?;
121
122        // NOTE: This is a noop verifier so we do not verify the Merkle proof.
123        let signed_tx = match result {
124            Body::HostFetchBlockMetadataTxResponse { signed_tx, .. } => signed_tx,
125            _ => return Err(Error::StateRoot(anyhow!("bad response from host"))),
126        };
127
128        let tx: Transaction = cbor::from_slice(signed_tx.blob.as_slice()).map_err(|err| {
129            Error::TransactionVerificationFailed(anyhow!("failed to decode transaction: {}", err))
130        })?;
131
132        if tx.method != METHOD_META {
133            return Err(Error::StateRoot(anyhow!("invalid method name")));
134        }
135
136        let meta: BlockMetadata = cbor::from_value(tx.body).map_err(|err| {
137            Error::StateRoot(anyhow!(
138                "failed to decode block metadata transaction: {}",
139                err
140            ))
141        })?;
142
143        let state_root = Root {
144            namespace: Namespace::default(),
145            version: height,
146            root_type: RootType::State,
147            hash: meta.state_root,
148        };
149
150        Ok(ConsensusState::from_protocol(
151            self.protocol.clone(),
152            state_root.version,
153            state_root,
154        ))
155    }
156
157    async fn state_at(&self, height: u64) -> Result<ConsensusState, Error> {
158        let block = self.fetch_light_block(height).await?;
159        self.unverified_state(block).await
160    }
161
162    async fn events_at(&self, height: u64, kind: EventKind) -> Result<Vec<Event>, Error> {
163        let result = self
164            .protocol
165            .call_host_async(Body::HostFetchConsensusEventsRequest(
166                HostFetchConsensusEventsRequest { height, kind },
167            ))
168            .await
169            .map_err(|err| Error::VerificationFailed(err.into()))?;
170
171        match result {
172            Body::HostFetchConsensusEventsResponse(HostFetchConsensusEventsResponse { events }) => {
173                Ok(events)
174            }
175            _ => Err(Error::VerificationFailed(anyhow!("bad response from host"))),
176        }
177    }
178
179    async fn latest_height(&self) -> Result<u64, Error> {
180        {
181            let inner = self.inner.lock().unwrap();
182            if let Some(latest_height) = inner.latest_height {
183                return Ok(latest_height);
184            }
185        }
186
187        let latest_height = self.fetch_light_block(HEIGHT_LATEST).await?.height;
188        self.sync(latest_height).await?;
189        Ok(latest_height)
190    }
191}