oasis_core_runtime/consensus/tendermint/verifier/
io.rs

1use std::sync::Arc;
2
3use tendermint_light_client::{
4    components::{
5        self,
6        io::{AtHeight, IoError},
7    },
8    types::{LightBlock as TMLightBlock, PeerId},
9};
10use tendermint_rpc::error::Error as RpcError;
11
12use crate::{
13    consensus::{
14        tendermint::{decode_light_block, LightBlockMeta},
15        transaction::SignedTransactionWithProof,
16        HEIGHT_LATEST,
17    },
18    protocol::Protocol,
19    types::Body,
20};
21
22use super::types::Nonce;
23
24pub struct Io {
25    protocol: Arc<Protocol>,
26}
27
28impl Io {
29    pub fn new(protocol: &Arc<Protocol>) -> Self {
30        Self {
31            protocol: protocol.clone(),
32        }
33    }
34
35    fn fetch_light_block(&self, height: u64) -> Result<LightBlockMeta, IoError> {
36        let result = self
37            .protocol
38            .call_host(Body::HostFetchConsensusBlockRequest { height })
39            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
40
41        // Extract generic light block from response.
42        let block = match result {
43            Body::HostFetchConsensusBlockResponse { block } => block,
44            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
45        };
46
47        // Decode block as a Tendermint light block.
48        let block = decode_light_block(block)
49            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
50
51        Ok(block)
52    }
53
54    pub fn fetch_genesis_height(&self) -> Result<u64, IoError> {
55        let result = self
56            .protocol
57            .call_host(Body::HostFetchGenesisHeightRequest {})
58            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
59
60        // Extract genesis height from response.
61        let height = match result {
62            Body::HostFetchGenesisHeightResponse { height } => height,
63            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
64        };
65
66        Ok(height)
67    }
68
69    pub fn fetch_freshness_proof(
70        &self,
71        nonce: &Nonce,
72    ) -> Result<SignedTransactionWithProof, IoError> {
73        let result = self
74            .protocol
75            .call_host(Body::HostProveFreshnessRequest {
76                blob: nonce.to_vec(),
77            })
78            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
79
80        // Extract proof from response.
81        let (signed_tx, proof) = match result {
82            Body::HostProveFreshnessResponse { signed_tx, proof } => (signed_tx, proof),
83            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
84        };
85
86        Ok(SignedTransactionWithProof { signed_tx, proof })
87    }
88
89    pub fn fetch_block_metadata(&self, height: u64) -> Result<SignedTransactionWithProof, IoError> {
90        let result = self
91            .protocol
92            .call_host(Body::HostFetchBlockMetadataTxRequest { height })
93            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
94
95        // Extract proof from response.
96        let (signed_tx, proof) = match result {
97            Body::HostFetchBlockMetadataTxResponse { signed_tx, proof } => (signed_tx, proof),
98            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
99        };
100
101        Ok(SignedTransactionWithProof { signed_tx, proof })
102    }
103}
104
105impl components::io::Io for Io {
106    fn fetch_light_block(&self, height: AtHeight) -> Result<TMLightBlock, IoError> {
107        let height = match height {
108            AtHeight::At(height) => height.into(),
109            AtHeight::Highest => HEIGHT_LATEST,
110        };
111
112        // Fetch light block at height and height+1.
113        let block = Io::fetch_light_block(self, height)?;
114        let height: u64 = block
115            .signed_header
116            .as_ref()
117            .ok_or_else(|| IoError::rpc(RpcError::server("missing signed header".to_string())))?
118            .header()
119            .height
120            .into();
121        // NOTE: It seems that the requirement to fetch the next validator set is redundant and it
122        //       should be handled at a higher layer of the light client.
123        let next_block = Io::fetch_light_block(self, height + 1)?;
124
125        Ok(TMLightBlock {
126            signed_header: block.signed_header.unwrap(), // Checked above.
127            validators: block.validators,
128            next_validators: next_block.validators,
129            provider: PeerId::new([0; 20]),
130        })
131    }
132}