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, ValidatorSet as TMValidatorSet},
9};
10use tendermint_rpc::error::Error as RpcError;
11
12use crate::{
13    consensus::{
14        tendermint::{decode_light_block, decode_validators, 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    fn fetch_validators(&self, height: u64) -> Result<TMValidatorSet, IoError> {
55        let result = self
56            .protocol
57            .call_host(Body::HostFetchConsensusValidatorsRequest { height })
58            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
59
60        // Extract generic validators from response.
61        let validators = match result {
62            Body::HostFetchConsensusValidatorsResponse { validators } => validators,
63            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
64        };
65
66        // Decode validators as Tendermint validators.
67        let validators = decode_validators(validators)
68            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
69
70        Ok(validators)
71    }
72
73    pub fn fetch_genesis_height(&self) -> Result<u64, IoError> {
74        let result = self
75            .protocol
76            .call_host(Body::HostFetchGenesisHeightRequest {})
77            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
78
79        // Extract genesis height from response.
80        let height = match result {
81            Body::HostFetchGenesisHeightResponse { height } => height,
82            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
83        };
84
85        Ok(height)
86    }
87
88    pub fn fetch_freshness_proof(
89        &self,
90        nonce: &Nonce,
91    ) -> Result<SignedTransactionWithProof, IoError> {
92        let result = self
93            .protocol
94            .call_host(Body::HostProveFreshnessRequest {
95                blob: nonce.to_vec(),
96            })
97            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
98
99        // Extract proof from response.
100        let (signed_tx, proof) = match result {
101            Body::HostProveFreshnessResponse { signed_tx, proof } => (signed_tx, proof),
102            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
103        };
104
105        Ok(SignedTransactionWithProof { signed_tx, proof })
106    }
107
108    pub fn fetch_block_metadata(&self, height: u64) -> Result<SignedTransactionWithProof, IoError> {
109        let result = self
110            .protocol
111            .call_host(Body::HostFetchBlockMetadataTxRequest { height })
112            .map_err(|err| IoError::rpc(RpcError::server(err.to_string())))?;
113
114        // Extract proof from response.
115        let (signed_tx, proof) = match result {
116            Body::HostFetchBlockMetadataTxResponse { signed_tx, proof } => (signed_tx, proof),
117            _ => return Err(IoError::rpc(RpcError::server("bad response".to_string()))),
118        };
119
120        Ok(SignedTransactionWithProof { signed_tx, proof })
121    }
122}
123
124impl components::io::Io for Io {
125    fn fetch_light_block(&self, height: AtHeight) -> Result<TMLightBlock, IoError> {
126        let height = match height {
127            AtHeight::At(height) => height.into(),
128            AtHeight::Highest => HEIGHT_LATEST,
129        };
130
131        let block = Io::fetch_light_block(self, height)?;
132        let height: u64 = block
133            .signed_header
134            .as_ref()
135            .ok_or_else(|| IoError::rpc(RpcError::server("missing signed header".to_string())))?
136            .header()
137            .height
138            .into();
139
140        let next_validators = Io::fetch_validators(self, height + 1)?;
141
142        Ok(TMLightBlock {
143            signed_header: block.signed_header.unwrap(), // Checked above.
144            validators: block.validators,
145            next_validators,
146            provider: PeerId::new([0; 20]),
147        })
148    }
149}