Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TLS support for the Light Client #842

Merged
merged 24 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5f57f97
Remove unused file
thanethomson Mar 24, 2021
e02b109
Refactor validators RPC endpoint interface
thanethomson Mar 24, 2021
c8e62b2
Merge latest changes from master
thanethomson Mar 24, 2021
2baf2d6
Ensure "total" response field is a string
thanethomson Mar 24, 2021
88e2d9c
Add serializer for optional types that need to be converted to/from a…
thanethomson Mar 25, 2021
45b9acd
Refactor to ensure page numbers and per-page values are converted to/…
thanethomson Mar 25, 2021
f0c26f7
Convert tcp:// scheme to http:// for RPC addresses
thanethomson Mar 25, 2021
14ad69f
Add Light Client support for RPC URLs instead of net::Address
thanethomson Mar 25, 2021
fa07173
Revert 14ad69f for now
thanethomson Mar 25, 2021
d0ba5bc
Revert f0c26f7
thanethomson Mar 25, 2021
f21f3ac
Merge latest changes from master
thanethomson Mar 25, 2021
4c251c5
Add CHANGELOG
thanethomson Mar 25, 2021
856ff0b
Convert tcp:// scheme to http:// for RPC addresses
thanethomson Mar 25, 2021
067e06a
Add Light Client support for RPC URLs instead of net::Address
thanethomson Mar 25, 2021
05c53e8
Comment not needed
thanethomson Mar 25, 2021
3196081
Expose rpc::Url type
thanethomson Mar 25, 2021
48aced5
Update kvstore integration test to use rpc::Url
thanethomson Mar 26, 2021
a106505
Update CHANGELOG
thanethomson Mar 26, 2021
35f9bff
Remove debug output from height log
thanethomson Mar 26, 2021
ff81071
Merge CI changes from master
thanethomson Mar 26, 2021
62830cc
Merge latest CI changes from master
thanethomson Mar 26, 2021
9fe5e75
Resolve conflicts with master
thanethomson Mar 29, 2021
90fb573
Attach serialization directly to tendermint_rpc::Url
thanethomson Mar 29, 2021
499100a
Add some happy path tests for tendermint_rpc::Url parsing
thanethomson Mar 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
* `[tendermint]` The `tendermint::block::CommitSig` enum's members have been
renamed to be consistent with Rust's naming conventions. For example,
`BlockIDFlagAbsent` is now renamed to `BlockIdFlagAbsent` ([#839])
* `[tendermint-light-client]` The Light Client no longer uses
`tendermint::net::Address` to refer to peers, and instead uses the
`tendermint_rpc::Url` type ([#835])
* `[tendermint-rpc]` The `Client::validators` method now requires a `Paging`
parameter. Previously, this wasn't possible and, if the network had more than
30 validators (the default for the RPC endpoint), it only returned a subset
of the validators ([#831])
* `[tendermint-rpc]` The `SubscriptionClient` trait now requires a `close`
method, since it assumes that subscription clients will, in general, use
long-running connections. This should not, however, break any downstream
Expand All @@ -23,6 +30,8 @@

* `[tendermint-abci]` Release minimal framework for building ABCI applications
in Rust ([#794])
* `[tendermint-light-client]` The Light Client now provides support for secure
(HTTPS) connections to nodes ([#835])
* `[tendermint-light-client-js]` First release of the
`tendermint-light-client-js` crate to provide access to Tendermint Light
Client functionality from WASM. This only provides access to the `verify`
Expand All @@ -35,9 +44,18 @@
* `[tendermint-rpc]` A `tendermint-rpc` CLI has been added to simplify
interaction with RPC endpoints from the command line ([#820])

### BUG FIXES

* `[tendermint-light-client]` Due to the RPC client's `validators` method
sometimes only returning a subset of validators (for networks larger than 30
validators), validator set hash calculations were failing. Now we are at
least obtaining a full validator set ([#831])

[#794]: https://github.com/informalsystems/tendermint-rs/pull/794
[#812]: https://github.com/informalsystems/tendermint-rs/pull/812
[#820]: https://github.com/informalsystems/tendermint-rs/pull/820
[#831]: https://github.com/informalsystems/tendermint-rs/issues/831
[#835]: https://github.com/informalsystems/tendermint-rs/issues/835
[#839]: https://github.com/informalsystems/tendermint-rs/pull/839

## v0.18.1
Expand Down
4 changes: 2 additions & 2 deletions light-client/examples/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct SyncOpts {
meta = "ADDR",
default = "tcp://127.0.0.1:26657"
)]
address: tendermint::net::Address,
address: tendermint_rpc::Url,
#[options(
help = "height of the initial trusted state (optional if store already initialized)",
meta = "HEIGHT"
Expand Down Expand Up @@ -81,7 +81,7 @@ fn main() {

fn make_instance(
peer_id: PeerId,
addr: tendermint::net::Address,
addr: tendermint_rpc::Url,
db_path: impl AsRef<Path>,
opts: &SyncOpts,
) -> Result<Instance, BoxError> {
Expand Down
12 changes: 5 additions & 7 deletions light-client/src/builder/supervisor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::time::Duration;

use tendermint::net;

use crate::builder::error::{self, Error};
use crate::peer_list::{PeerList, PeerListBuilder};
use crate::supervisor::Instance;
Expand All @@ -21,7 +19,7 @@ pub struct Done;
#[must_use]
pub struct SupervisorBuilder<State> {
instances: PeerListBuilder<Instance>,
addresses: PeerListBuilder<net::Address>,
addresses: PeerListBuilder<tendermint_rpc::Url>,
evidence_reporting_timeout: Option<Duration>,
#[allow(dead_code)]
state: State,
Expand Down Expand Up @@ -66,7 +64,7 @@ impl SupervisorBuilder<Init> {
pub fn primary(
mut self,
peer_id: PeerId,
address: net::Address,
address: tendermint_rpc::Url,
instance: Instance,
) -> SupervisorBuilder<HasPrimary> {
self.instances.primary(peer_id, instance);
Expand All @@ -81,7 +79,7 @@ impl SupervisorBuilder<HasPrimary> {
pub fn witness(
mut self,
peer_id: PeerId,
address: net::Address,
address: tendermint_rpc::Url,
instance: Instance,
) -> SupervisorBuilder<Done> {
self.instances.witness(peer_id, instance);
Expand All @@ -93,7 +91,7 @@ impl SupervisorBuilder<HasPrimary> {
/// Add multiple witnesses at once.
pub fn witnesses(
mut self,
witnesses: impl IntoIterator<Item = (PeerId, net::Address, Instance)>,
witnesses: impl IntoIterator<Item = (PeerId, tendermint_rpc::Url, Instance)>,
) -> Result<SupervisorBuilder<Done>, Error> {
let mut iter = witnesses.into_iter().peekable();
if iter.peek().is_none() {
Expand Down Expand Up @@ -126,7 +124,7 @@ impl SupervisorBuilder<Done> {

/// Get the underlying list of instances and addresses.
#[must_use]
pub fn inner(self) -> (PeerList<Instance>, PeerList<net::Address>) {
pub fn inner(self) -> (PeerList<Instance>, PeerList<tendermint_rpc::Url>) {
(self.instances.build(), self.addresses.build())
}
}
7 changes: 5 additions & 2 deletions light-client/src/components/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ mod prod {
use tendermint::account::Id as TMAccountId;
use tendermint::block::signed_header::SignedHeader as TMSignedHeader;
use tendermint::validator::Set as TMValidatorSet;
use tendermint_rpc::Paging;

/// Production implementation of the Io component, which fetches
/// light blocks from full nodes via RPC.
Expand Down Expand Up @@ -166,8 +167,10 @@ mod prod {
};

let client = self.rpc_client.clone();
let response = block_on(self.timeout, async move { client.validators(height).await })?
.map_err(IoError::RpcError)?;
let response = block_on(self.timeout, async move {
client.validators(height, Paging::All).await
})?
.map_err(IoError::RpcError)?;

let validator_set = match proposer_address {
Some(proposer_address) => {
Expand Down
4 changes: 2 additions & 2 deletions light-client/src/evidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod prod {
/// nodes via RPC.
#[derive(Clone, Debug)]
pub struct ProdEvidenceReporter {
peer_map: HashMap<PeerId, tendermint::net::Address>,
peer_map: HashMap<PeerId, tendermint_rpc::Url>,
timeout: Option<Duration>,
}

Expand All @@ -61,7 +61,7 @@ mod prod {
///
/// A peer map which maps peer IDS to their network address must be supplied.
pub fn new(
peer_map: HashMap<PeerId, tendermint::net::Address>,
peer_map: HashMap<PeerId, tendermint_rpc::Url>,
timeout: Option<Duration>,
) -> Self {
Self { peer_map, timeout }
Expand Down
2 changes: 1 addition & 1 deletion light-client/src/supervisor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl std::fmt::Debug for Supervisor {
static_assertions::assert_impl_all!(Supervisor: Send);

impl Supervisor {
/// Constructs a new supevisor from the given list of peers and fork detector instance.
/// Constructs a new supervisor from the given list of peers and fork detector instance.
pub fn new(
peers: PeerList<Instance>,
fork_detector: impl ForkDetector + 'static,
Expand Down
1 change: 1 addition & 0 deletions light-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ thiserror = "1.0"

tendermint = { version = "0.18.1", path = "../tendermint" }
tendermint-light-client = { version = "0.18.1", path = "../light-client", features = ["lightstore-sled"] }
tendermint-proto = { version = "0.18.1", path = "../proto" }
tendermint-rpc = { version = "0.18.1", path = "../rpc", features = ["http-client"] }

[dependencies.abscissa_core]
Expand Down
30 changes: 23 additions & 7 deletions light-node/src/commands/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ use std::ops::Deref;
use std::time::Duration;

use crate::application::app_config;
use crate::config::LightClientConfig;
use crate::config::LightNodeConfig;
use crate::config::{LightClientConfig, LightNodeConfig};

use abscissa_core::status_err;
use abscissa_core::status_warn;
use abscissa_core::Command;
use abscissa_core::Options;
use abscissa_core::Runnable;
use abscissa_core::{status_err, status_info, status_warn, Command, Options, Runnable};

use tendermint::{hash, Hash};

Expand Down Expand Up @@ -57,6 +52,8 @@ impl Runnable for InitCmd {
) {
status_err!("failed to initialize light client: {}", e);
// TODO: Set exit code to 1
} else {
status_info!("init", "done");
}
}
}
Expand All @@ -68,6 +65,20 @@ fn initialize_subjectively(
config: &LightClientConfig,
timeout: Option<Duration>,
) -> Result<Instance, String> {
status_info!(
"init",
"starting subjective initialization for height: {}",
height,
);
status_info!("init", "subjective header hash: {}", subjective_header_hash,);
status_info!(
"init",
"using sled store located at: {}",
config
.db_path
.to_str()
.ok_or("unable to obtain sled db path")?
);
let light_store =
SledStore::open(&config.db_path).map_err(|e| format!("could not open database: {}", e))?;

Expand All @@ -79,6 +90,11 @@ fn initialize_subjectively(
);
}

status_info!(
"init",
"Tendermint RPC address: {}",
config.address.to_string()
);
let rpc_client = rpc::HttpClient::new(config.address.clone()).map_err(|e| e.to_string())?;

let builder = LightClientBuilder::prod(
Expand Down
30 changes: 23 additions & 7 deletions light-node/src/commands/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ use crate::config::{LightClientConfig, LightNodeConfig};
use crate::rpc;
use crate::rpc::Server;

use abscissa_core::config;
use abscissa_core::path::PathBuf;
use abscissa_core::status_err;
use abscissa_core::status_info;
use abscissa_core::Command;
use abscissa_core::FrameworkError;
use abscissa_core::Options;
use abscissa_core::Runnable;
use abscissa_core::{config, status_err, status_info, Command, FrameworkError, Options, Runnable};

use std::net::SocketAddr;
use std::ops::Deref;
Expand Down Expand Up @@ -125,12 +119,27 @@ impl StartCmd {
options: light_client::Options,
timeout: Option<Duration>,
) -> Result<Instance, String> {
status_info!(
"start",
"constructing Light Client for peer {}",
light_config.peer_id.to_string()
);
status_info!("start", "RPC address: {}", light_config.address.to_string());
let rpc_client = tendermint_rpc::HttpClient::new(light_config.address.clone())
.map_err(|e| format!("failed to create HTTP client: {}", e))?;

let light_store = SledStore::open(&light_config.db_path)
.map_err(|e| format!("could not open database: {}", e))?;

status_info!(
"start",
"highest trusted or verified height: {}",
light_store
.highest_trusted_or_verified()
.map(|b| b.signed_header.header.height.to_string())
.unwrap_or_else(|| "(none)".to_owned()),
);

let builder = LightClientBuilder::prod(
light_config.peer_id,
rpc_client,
Expand Down Expand Up @@ -161,6 +170,12 @@ impl StartCmd {

let builder = SupervisorBuilder::new();

status_info!(
"start",
"primary: {} @ {}",
primary_conf.peer_id,
primary_conf.address
);
let primary_instance = self.make_instance(primary_conf, options, Some(timeout))?;
let builder = builder.primary(
primary_conf.peer_id,
Expand All @@ -173,6 +188,7 @@ impl StartCmd {
let instance = self.make_instance(witness_conf, options, Some(timeout))?;
witnesses.push((witness_conf.peer_id, witness_conf.address.clone(), instance));
}
status_info!("start", "{} witness(es)", witnesses.len());

let builder = builder
.witnesses(witnesses)
Expand Down
3 changes: 2 additions & 1 deletion light-node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ pub struct LightNodeConfig {
pub struct LightClientConfig {
/// Address of the Tendermint fullnode to connect to and
/// fetch LightBlock data from.
pub address: tendermint::net::Address,
#[serde(with = "tendermint_proto::serializers::from_str")]
thanethomson marked this conversation as resolved.
Show resolved Hide resolved
pub address: tendermint_rpc::Url,
/// PeerID of the same Tendermint fullnode.
pub peer_id: PeerId,
/// The data base folder for this instance's store.
Expand Down
1 change: 1 addition & 0 deletions proto/src/serializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub mod evidence;
pub mod from_str;
pub mod nullable;
pub mod optional;
pub mod optional_from_str;
pub mod part_set_header_total;
pub mod time_duration;
pub mod timestamp;
Expand Down
31 changes: 31 additions & 0 deletions proto/src/serializers/optional_from_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! De/serialize an optional type that must be converted from/to a string.

use serde::de::Error;
use serde::{Deserialize, Deserializer, Serializer};
use std::str::FromStr;

pub fn serialize<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: ToString,
{
match value {
Some(t) => serializer.serialize_some(&t.to_string()),
None => serializer.serialize_none(),
}
}

pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: FromStr,
T::Err: std::error::Error,
{
let s = match Option::<String>::deserialize(deserializer)? {
Some(s) => s,
None => return Ok(None),
};
Ok(Some(s.parse().map_err(|e: <T as FromStr>::Err| {
D::Error::custom(e.to_string())
})?))
}
8 changes: 3 additions & 5 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ http-client = [
"hyper-rustls",
"tokio/fs",
"tokio/macros",
"tracing",
"url"
"tracing"
]
secp256k1 = [ "tendermint/secp256k1" ]
websocket-client = [
Expand All @@ -58,8 +57,7 @@ websocket-client = [
"tokio/macros",
"tokio/sync",
"tokio/time",
"tracing",
"url"
"tracing"
]

[dependencies]
Expand All @@ -78,6 +76,7 @@ tendermint-proto = { version = "0.18.1", path = "../proto" }
thiserror = "1"
uuid = { version = "0.8", default-features = false }
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
url = "2.2"
walkdir = "2.3"

async-trait = { version = "0.1", optional = true }
Expand All @@ -91,4 +90,3 @@ structopt = { version = "0.3", optional = true }
tokio = { version = "1.0", optional = true }
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.2", optional = true }
url = { version = "2.2", optional = true }
Loading