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