Skip to content

Commit

Permalink
Add TLS support for the Light Client (#842)
Browse files Browse the repository at this point in the history
* Remove unused file

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Refactor validators RPC endpoint interface

This commit adds pagination to the `validators` method on the `Client`
trait (BREAKING).

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Ensure "total" response field is a string

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add serializer for optional types that need to be converted to/from a string (like page numbers/per page counts)

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Refactor to ensure page numbers and per-page values are converted to/from strings first

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Convert tcp:// scheme to http:// for RPC addresses

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add Light Client support for RPC URLs instead of net::Address

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Revert 14ad69f for now

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Revert f0c26f7

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add CHANGELOG

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Convert tcp:// scheme to http:// for RPC addresses

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add Light Client support for RPC URLs instead of net::Address

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Comment not needed

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Expose rpc::Url type

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Update kvstore integration test to use rpc::Url

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Update CHANGELOG

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove debug output from height log

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Attach serialization directly to tendermint_rpc::Url

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add some happy path tests for tendermint_rpc::Url parsing

Signed-off-by: Thane Thomson <connect@thanethomson.com>
  • Loading branch information
thanethomson committed Mar 30, 2021
1 parent 5b0c9b5 commit 2df4219
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 61 deletions.
10 changes: 10 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 `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
Expand All @@ -27,6 +34,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 Down Expand Up @@ -55,6 +64,7 @@
[#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())
}
}
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
2 changes: 1 addition & 1 deletion light-node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub struct LightNodeConfig {
pub struct LightClientConfig {
/// Address of the Tendermint fullnode to connect to and
/// fetch LightBlock data from.
pub address: tendermint::net::Address,
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
11 changes: 6 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,6 @@ 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 }

[dev-dependencies]
lazy_static = "1.4.0"
2 changes: 2 additions & 0 deletions rpc/src/endpoint/validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub const DEFAULT_VALIDATORS_PER_PAGE: u8 = 30;

/// List validators for a specific block
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[non_exhaustive]
pub struct Request {
/// The height at which to retrieve the validator set. If not specified,
/// defaults to the latest height.
Expand Down Expand Up @@ -65,6 +66,7 @@ impl crate::SimpleRequest for Request {}

/// Validator responses
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct Response {
/// Block height
pub block_height: block::Height,
Expand Down
1 change: 0 additions & 1 deletion rpc/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ impl From<std::io::Error> for Error {
}
}

#[cfg(any(feature = "http-client", feature = "websocket-client"))]
impl From<url::ParseError> for Error {
fn from(e: url::ParseError) -> Self {
Error::invalid_params(&e.to_string())
Expand Down
20 changes: 11 additions & 9 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,10 @@
#[cfg(any(feature = "http-client", feature = "websocket-client"))]
mod client;
#[cfg(any(feature = "http-client", feature = "websocket-client"))]
mod rpc_url;
#[cfg(any(feature = "http-client", feature = "websocket-client"))]
pub use client::{
Client, MockClient, MockRequestMatcher, MockRequestMethodMatcher, Subscription,
SubscriptionClient,
};
#[cfg(any(feature = "http-client", feature = "websocket-client"))]
pub use rpc_url::{Scheme, Url};

#[cfg(feature = "http-client")]
pub use client::{HttpClient, HttpClientUrl};
Expand All @@ -55,11 +51,17 @@ pub mod query;
pub mod request;
pub mod response;
mod result;
mod rpc_url;
mod utils;
mod version;

pub use self::{
error::Error, id::Id, method::Method, order::Order, paging::PageNumber, paging::Paging,
paging::PerPage, request::Request, request::SimpleRequest, response::Response, result::Result,
version::Version,
};
pub use error::Error;
pub use id::Id;
pub use method::Method;
pub use order::Order;
pub use paging::{PageNumber, Paging, PerPage};
pub use request::{Request, SimpleRequest};
pub use response::Response;
pub use result::Result;
pub use rpc_url::{Scheme, Url};
pub use version::Version;
Loading

0 comments on commit 2df4219

Please sign in to comment.