|
1 | 1 | //! Provides an interface and a default implementation of the `Io` component
|
2 | 2 |
|
3 |
| -use std::collections::HashMap; |
4 |
| -use std::time::Duration; |
5 |
| - |
6 |
| -use contracts::{contract_trait, post, pre}; |
| 3 | +use contracts::{contract_trait, post}; |
7 | 4 | use serde::{Deserialize, Serialize};
|
8 | 5 | use thiserror::Error;
|
9 | 6 |
|
10 |
| -use tendermint::{ |
11 |
| - block::signed_header::SignedHeader as TMSignedHeader, validator::Set as TMValidatorSet, |
12 |
| -}; |
13 |
| - |
14 | 7 | use tendermint_rpc as rpc;
|
15 | 8 |
|
16 |
| -use crate::{ |
17 |
| - bail, |
18 |
| - types::{Height, LightBlock, PeerId}, |
19 |
| -}; |
| 9 | +use crate::types::{Height, LightBlock, PeerId}; |
20 | 10 |
|
21 | 11 | /// Type for selecting either a specific height or the latest one
|
22 | 12 | pub enum AtHeight {
|
@@ -81,113 +71,132 @@ where
|
81 | 71 | }
|
82 | 72 | }
|
83 | 73 |
|
84 |
| -/// Production implementation of the Io component, which fetches |
85 |
| -/// light blocks from full nodes via RPC. |
86 |
| -#[derive(Clone, Debug)] |
87 |
| -pub struct ProdIo { |
88 |
| - peer_map: HashMap<PeerId, tendermint::net::Address>, |
89 |
| - timeout: Option<Duration>, |
90 |
| -} |
| 74 | +#[cfg(feature = "rpc")] |
| 75 | +pub use self::prod::ProdIo; |
91 | 76 |
|
92 |
| -#[contract_trait] |
93 |
| -impl Io for ProdIo { |
94 |
| - fn fetch_light_block(&self, peer: PeerId, height: AtHeight) -> Result<LightBlock, IoError> { |
95 |
| - let signed_header = self.fetch_signed_header(peer, height)?; |
96 |
| - let height = signed_header.header.height; |
| 77 | +#[cfg(feature = "rpc")] |
| 78 | +mod prod { |
| 79 | + use super::*; |
97 | 80 |
|
98 |
| - let validator_set = self.fetch_validator_set(peer, height.into())?; |
99 |
| - let next_validator_set = self.fetch_validator_set(peer, height.increment().into())?; |
| 81 | + use std::collections::HashMap; |
| 82 | + use std::time::Duration; |
100 | 83 |
|
101 |
| - let light_block = LightBlock::new(signed_header, validator_set, next_validator_set, peer); |
| 84 | + use crate::bail; |
| 85 | + use contracts::{contract_trait, post, pre}; |
| 86 | + use tendermint::{ |
| 87 | + block::signed_header::SignedHeader as TMSignedHeader, validator::Set as TMValidatorSet, |
| 88 | + }; |
102 | 89 |
|
103 |
| - Ok(light_block) |
104 |
| - } |
105 |
| -} |
106 |
| - |
107 |
| -impl ProdIo { |
108 |
| - /// Constructs a new ProdIo component. |
109 |
| - /// |
110 |
| - /// A peer map which maps peer IDS to their network address must be supplied. |
111 |
| - pub fn new( |
| 90 | + /// Production implementation of the Io component, which fetches |
| 91 | + /// light blocks from full nodes via RPC. |
| 92 | + #[derive(Clone, Debug)] |
| 93 | + pub struct ProdIo { |
112 | 94 | peer_map: HashMap<PeerId, tendermint::net::Address>,
|
113 | 95 | timeout: Option<Duration>,
|
114 |
| - ) -> Self { |
115 |
| - Self { peer_map, timeout } |
116 | 96 | }
|
117 | 97 |
|
118 |
| - #[pre(self.peer_map.contains_key(&peer))] |
119 |
| - fn fetch_signed_header( |
120 |
| - &self, |
121 |
| - peer: PeerId, |
122 |
| - height: AtHeight, |
123 |
| - ) -> Result<TMSignedHeader, IoError> { |
124 |
| - let rpc_client = self.rpc_client_for(peer); |
125 |
| - |
126 |
| - let res = block_on( |
127 |
| - async { |
128 |
| - match height { |
129 |
| - AtHeight::Highest => rpc_client.latest_commit().await, |
130 |
| - AtHeight::At(height) => rpc_client.commit(height).await, |
131 |
| - } |
132 |
| - }, |
133 |
| - peer, |
134 |
| - self.timeout, |
135 |
| - )?; |
136 |
| - |
137 |
| - match res { |
138 |
| - Ok(response) => Ok(response.signed_header), |
139 |
| - Err(err) => Err(IoError::IoError(err)), |
| 98 | + #[contract_trait] |
| 99 | + impl Io for ProdIo { |
| 100 | + fn fetch_light_block(&self, peer: PeerId, height: AtHeight) -> Result<LightBlock, IoError> { |
| 101 | + let signed_header = self.fetch_signed_header(peer, height)?; |
| 102 | + let height = signed_header.header.height; |
| 103 | + |
| 104 | + let validator_set = self.fetch_validator_set(peer, height.into())?; |
| 105 | + let next_validator_set = self.fetch_validator_set(peer, height.increment().into())?; |
| 106 | + |
| 107 | + let light_block = |
| 108 | + LightBlock::new(signed_header, validator_set, next_validator_set, peer); |
| 109 | + |
| 110 | + Ok(light_block) |
140 | 111 | }
|
141 | 112 | }
|
142 | 113 |
|
143 |
| - #[pre(self.peer_map.contains_key(&peer))] |
144 |
| - fn fetch_validator_set( |
145 |
| - &self, |
146 |
| - peer: PeerId, |
147 |
| - height: AtHeight, |
148 |
| - ) -> Result<TMValidatorSet, IoError> { |
149 |
| - let height = match height { |
150 |
| - AtHeight::Highest => bail!(IoError::InvalidHeight( |
151 |
| - "given height must be greater than 0".to_string() |
152 |
| - )), |
153 |
| - AtHeight::At(height) => height, |
154 |
| - }; |
155 |
| - |
156 |
| - let res = block_on( |
157 |
| - self.rpc_client_for(peer).validators(height), |
158 |
| - peer, |
159 |
| - self.timeout, |
160 |
| - )?; |
161 |
| - |
162 |
| - match res { |
163 |
| - Ok(response) => Ok(TMValidatorSet::new(response.validators)), |
164 |
| - Err(err) => Err(IoError::IoError(err)), |
| 114 | + impl ProdIo { |
| 115 | + /// Constructs a new ProdIo component. |
| 116 | + /// |
| 117 | + /// A peer map which maps peer IDS to their network address must be supplied. |
| 118 | + pub fn new( |
| 119 | + peer_map: HashMap<PeerId, tendermint::net::Address>, |
| 120 | + timeout: Option<Duration>, |
| 121 | + ) -> Self { |
| 122 | + Self { peer_map, timeout } |
| 123 | + } |
| 124 | + |
| 125 | + #[pre(self.peer_map.contains_key(&peer))] |
| 126 | + fn fetch_signed_header( |
| 127 | + &self, |
| 128 | + peer: PeerId, |
| 129 | + height: AtHeight, |
| 130 | + ) -> Result<TMSignedHeader, IoError> { |
| 131 | + let rpc_client = self.rpc_client_for(peer); |
| 132 | + |
| 133 | + let res = block_on( |
| 134 | + async { |
| 135 | + match height { |
| 136 | + AtHeight::Highest => rpc_client.latest_commit().await, |
| 137 | + AtHeight::At(height) => rpc_client.commit(height).await, |
| 138 | + } |
| 139 | + }, |
| 140 | + peer, |
| 141 | + self.timeout, |
| 142 | + )?; |
| 143 | + |
| 144 | + match res { |
| 145 | + Ok(response) => Ok(response.signed_header), |
| 146 | + Err(err) => Err(IoError::IoError(err)), |
| 147 | + } |
165 | 148 | }
|
166 |
| - } |
167 | 149 |
|
168 |
| - // FIXME: Cannot enable precondition because of "autoref lifetime" issue |
169 |
| - // #[pre(self.peer_map.contains_key(&peer))] |
170 |
| - fn rpc_client_for(&self, peer: PeerId) -> rpc::Client { |
171 |
| - let peer_addr = self.peer_map.get(&peer).unwrap().to_owned(); |
172 |
| - rpc::Client::new(peer_addr) |
| 150 | + #[pre(self.peer_map.contains_key(&peer))] |
| 151 | + fn fetch_validator_set( |
| 152 | + &self, |
| 153 | + peer: PeerId, |
| 154 | + height: AtHeight, |
| 155 | + ) -> Result<TMValidatorSet, IoError> { |
| 156 | + let height = match height { |
| 157 | + AtHeight::Highest => bail!(IoError::InvalidHeight( |
| 158 | + "given height must be greater than 0".to_string() |
| 159 | + )), |
| 160 | + AtHeight::At(height) => height, |
| 161 | + }; |
| 162 | + |
| 163 | + let res = block_on( |
| 164 | + self.rpc_client_for(peer).validators(height), |
| 165 | + peer, |
| 166 | + self.timeout, |
| 167 | + )?; |
| 168 | + |
| 169 | + match res { |
| 170 | + Ok(response) => Ok(TMValidatorSet::new(response.validators)), |
| 171 | + Err(err) => Err(IoError::IoError(err)), |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + // FIXME: Cannot enable precondition because of "autoref lifetime" issue |
| 176 | + // #[pre(self.peer_map.contains_key(&peer))] |
| 177 | + fn rpc_client_for(&self, peer: PeerId) -> rpc::Client { |
| 178 | + let peer_addr = self.peer_map.get(&peer).unwrap().to_owned(); |
| 179 | + rpc::Client::new(peer_addr) |
| 180 | + } |
173 | 181 | }
|
174 |
| -} |
175 | 182 |
|
176 |
| -fn block_on<F: std::future::Future>( |
177 |
| - f: F, |
178 |
| - peer: PeerId, |
179 |
| - timeout: Option<Duration>, |
180 |
| -) -> Result<F::Output, IoError> { |
181 |
| - let mut rt = tokio::runtime::Builder::new() |
182 |
| - .basic_scheduler() |
183 |
| - .enable_all() |
184 |
| - .build() |
185 |
| - .unwrap(); |
186 |
| - |
187 |
| - if let Some(timeout) = timeout { |
188 |
| - rt.block_on(async { tokio::time::timeout(timeout, f).await }) |
189 |
| - .map_err(|_| IoError::Timeout(peer)) |
190 |
| - } else { |
191 |
| - Ok(rt.block_on(f)) |
| 183 | + fn block_on<F: std::future::Future>( |
| 184 | + f: F, |
| 185 | + peer: PeerId, |
| 186 | + timeout: Option<Duration>, |
| 187 | + ) -> Result<F::Output, IoError> { |
| 188 | + let mut rt = tokio::runtime::Builder::new() |
| 189 | + .basic_scheduler() |
| 190 | + .enable_all() |
| 191 | + .build() |
| 192 | + .unwrap(); |
| 193 | + |
| 194 | + if let Some(timeout) = timeout { |
| 195 | + rt.block_on(async { tokio::time::timeout(timeout, f).await }) |
| 196 | + .map_err(|_| IoError::Timeout(peer)) |
| 197 | + } else { |
| 198 | + Ok(rt.block_on(f)) |
| 199 | + } |
192 | 200 | }
|
193 | 201 | }
|
| 202 | + |
0 commit comments