diff --git a/CHANGES.md b/CHANGES.md index 9aca025d8..dabfc17de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,9 @@ -## Pending +## [0.15.0] (2020-07-17) +This release is mostly about the revamped [light-client] library and the [light-node] command-line interface. +Note that both crates are to be considered experimental software that will still undergo a lot of improvements and iterations. +The goal of releasing an early version of our Light Client is to make it accessible, to get people use it, and to receive feedback. + + ⚠️ ️Deprecation warning ⚠️ : This might be the last release containing the [lite] module. Please take a look at the [light-client] crate. ### Light Client: @@ -21,6 +26,8 @@ - Add `latest_status` to the supervisor `Handle` ([#449]) - Add JSONRPC endpoints to query the light-node ([#363], [#449]) +[0.15.0]: https://github.com/informalsystems/tendermint-rs/pull/454 + [#302]: https://github.com/informalsystems/tendermint-rs/pull/302 [#336]: https://github.com/informalsystems/tendermint-rs/pull/336 [#360]: https://github.com/informalsystems/tendermint-rs/pull/360 @@ -42,11 +49,14 @@ [ADR-007]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/architecture/adr-007-light-client-supervisor-ergonomics.md +[light-node]: ./light-node/README.md + ## [0.14.1] (2020-06-23) - Update `prost-amino`/`prost-amino-derive` to v0.6 ([#367]) [#367]: https://github.com/informalsystems/tendermint-rs/issues/367 +[0.14.1]: https://github.com/informalsystems/tendermint-rs/pull/368 ## [0.14.0] (2020-06-19) diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 7b1b75531..a17d7581d 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -1,24 +1,26 @@ [package] name = "tendermint-light-client" -version = "0.14.0" +version = "0.15.0" authors = ["Romain Ruetschi "] edition = "2018" -publish = false +license = "Apache-2.0" +readme = "README.md" +keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] [dependencies] -tendermint = { path = "../tendermint" } -tendermint-rpc = { version = "0.14.0", path = "../rpc", features = ["client"] } +tendermint = { version = "0.15.0", path = "../tendermint" } +tendermint-rpc = { version = "0.15.0", path = "../rpc", features = ["client"] } anomaly = { version = "0.2.0", features = ["serializer"] } contracts = "0.4.0" crossbeam-channel = "0.4.2" derive_more = "0.99.5" futures = "0.3.4" -prost-amino = "0.5.0" +prost-amino = "0.6.0" serde = "1.0.106" serde_cbor = "0.11.1" serde_derive = "1.0.106" -sled = "0.31.0" +sled = "0.33.0" static_assertions = "1.1.0" thiserror = "1.0.15" tokio = "0.2.20" diff --git a/light-client/src/components/scheduler.rs b/light-client/src/components/scheduler.rs index ddcb3bdc4..5dd3e867b 100644 --- a/light-client/src/components/scheduler.rs +++ b/light-client/src/components/scheduler.rs @@ -85,10 +85,11 @@ pub fn basic_bisecting_schedule( /// ## Note /// /// - Case i. captures the case where the light block at height `current_height` has been verified, -/// and we can choose a height closer to the `target_height`. As we get the `light_store` as parameter, -/// the choice of the next height can depend on the `light_store`, e.g., we can pick a height -/// for which we have already downloaded a light block. -/// - In Case ii. the header at `current_height` could not be verified, and we need to pick a lesser height. +/// and we can choose a height closer to the `target_height`. As we get the `light_store` as +/// parameter, the choice of the next height can depend on the `light_store`, e.g., we can pick a +/// height for which we have already downloaded a light block. +/// - In Case ii. the header at `current_height` could not be verified, and we need to pick a lesser +/// height. /// - In Case iii. is a special case when we have verified the `target_height`. /// /// ## Implements diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index 81413a498..7b7b973ca 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -54,10 +54,8 @@ pub trait ForkDetector: Send { /// the given trusted state, and then: /// /// - If the verification succeeds, we have a real fork -/// - If verification fails because of lack of trust, -/// we have a potential fork. -/// - If verification fails for any other reason, the -/// witness is deemed faulty. +/// - If verification fails because of lack of trust, we have a potential fork. +/// - If verification fails for any other reason, the witness is deemed faulty. pub struct ProdForkDetector { hasher: Box, } diff --git a/light-client/src/lib.rs b/light-client/src/lib.rs index 93ed2e349..0a76aa87c 100644 --- a/light-client/src/lib.rs +++ b/light-client/src/lib.rs @@ -9,6 +9,10 @@ rust_2018_idioms, nonstandard_style, )] +#![doc( + html_root_url = "https://docs.rs/tendermint-light-client/0.15.0", + html_logo_url = "https://raw.githubusercontent.com/informalsystems/tendermint-rs/master/img/logo-tendermint-rs.png" +)] //! See the `light_client` module for the main documentation. diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index afde2c023..eddfc9ea1 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -102,12 +102,12 @@ impl LightClient { /// /// This is the main function and uses the following components: /// - /// - The I/O component is called to fetch the next light block. - /// It is the only component that communicates with other nodes. - /// - The Verifier component checks whether a header is valid and checks if a new - /// light block should be trusted based on a previously verified light block. - /// - The Scheduler component decides which height to try to verify next, in case - /// the current block pass verification but cannot be trusted yet. + /// - The I/O component is called to fetch the next light block. It is the only component that + /// communicates with other nodes. + /// - The Verifier component checks whether a header is valid and checks if a new light block + /// should be trusted based on a previously verified light block. + /// - The Scheduler component decides which height to try to verify next, in case the current + /// block pass verification but cannot be trusted yet. /// /// ## Implements /// - [LCV-DIST-SAFE.1] @@ -120,8 +120,8 @@ impl LightClient { /// - The light store contains a light block within the trusting period [LCV-PRE-TP.1] /// /// ## Postcondition - /// - The light store contains a light block that corresponds - /// to a block of the blockchain of height `target_height` [LCV-POST-LS.1] + /// - The light store contains a light block that corresponds to a block of the blockchain of + /// height `target_height` [LCV-POST-LS.1] /// /// ## Error conditions /// - If the precondition is violated [LVC-PRE-TP.1] @@ -146,7 +146,8 @@ impl LightClient { target_height: Height, state: &mut State, ) -> Result { - // Let's first look in the store to see whether we have already successfully verified this block + // Let's first look in the store to see whether we have already successfully verified this + // block. if let Some(light_block) = state.light_store.get_trusted_or_verified(target_height) { return Ok(light_block); } @@ -180,7 +181,8 @@ impl LightClient { // Log the current height as a dependency of the block at the target height state.trace_block(target_height, current_height); - // If the trusted state is now at a height equal to the target height, we are done. [LCV-DIST-LIFE.1] + // If the trusted state is now at a height equal to the target height, we are done. + // [LCV-DIST-LIFE.1] if target_height == trusted_state.height() { return Ok(trusted_state); } @@ -202,16 +204,18 @@ impl LightClient { state.light_store.update(¤t_block, new_status); } Verdict::Invalid(e) => { - // Verification failed, add the block to the light store with `Failed` status, and abort. + // Verification failed, add the block to the light store with `Failed` status, + // and abort. state.light_store.update(¤t_block, Status::Failed); bail!(ErrorKind::InvalidLightBlock(e)) } Verdict::NotEnoughTrust(_) => { - // The current block cannot be trusted because of missing overlap in the validator sets. - // Add the block to the light store with `Unverified` status. - // This will engage bisection in an attempt to raise the height of the highest - // trusted state until there is enough overlap. + // The current block cannot be trusted because of a missing overlap in the + // validator sets. Add the block to the light store with + // the `Unverified` status. This will engage bisection in an + // attempt to raise the height of the highest trusted state + // until there is enough overlap. state.light_store.update(¤t_block, Status::Unverified); } } diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index 8b29fb1de..8ea1bb078 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -199,7 +199,8 @@ impl Supervisor { self.verify(Some(height)) } - /// Verify either to the latest block (if `height == None`) or to a given block (if `height == Some(height)`). + /// Verify either to the latest block (if `height == None`) or to a given block (if `height == + /// Some(height)`). fn verify(&mut self, height: Option) -> Result { let primary = self.peers.primary_mut(); diff --git a/light-node/Cargo.toml b/light-node/Cargo.toml index 99e03a3e5..f8a9088d4 100644 --- a/light-node/Cargo.toml +++ b/light-node/Cargo.toml @@ -1,9 +1,24 @@ [package] name = "tendermint-light-node" authors = ["Ethan Buchman ", "Ismail Khoffi "] -version = "0.14.0" +version = "0.15.0" edition = "2018" -publish = false +license = "Apache-2.0" +repository = "https://github.com/informalsystems/tendermint-rs" +readme = "README.md" +keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] + +description = """ + The Tendermint light-node wraps the light-client crate into a command-line + interface tool. + It can be used to initialize and start a standalone light client daemon and + exposes a JSONRPC endpoint from which you can query the current state of the + light node. + """ + +[[bin]] +name = "tendermint-light-node" +path = "src/bin/tendermint-light-node/main.rs" [dependencies] abscissa_tokio = "0.5" @@ -16,10 +31,10 @@ jsonrpc-http-server = "14.2" jsonrpc-derive = "14.2" serde = { version = "1", features = ["serde_derive"] } serde_json = "1.0" -sled = "0.31.0" -tendermint = { version = "0.14.0", path = "../tendermint" } -tendermint-light-client = { version = "0.14.0", path = "../light-client" } -tendermint-rpc = { version = "0.14.0", path = "../rpc", features = [ "client" ] } +sled = "0.33.0" +tendermint = { version = "0.15.0", path = "../tendermint" } +tendermint-light-client = { version = "0.15.0", path = "../light-client" } +tendermint-rpc = { version = "0.15.0", path = "../rpc", features = [ "client" ] } thiserror = "1.0" tokio = { version = "0.2", features = ["full"] } diff --git a/light-node/src/bin/light-node/main.rs b/light-node/src/bin/tendermint-light-node/main.rs similarity index 81% rename from light-node/src/bin/light-node/main.rs rename to light-node/src/bin/tendermint-light-node/main.rs index 6a16e918e..123b736de 100644 --- a/light-node/src/bin/light-node/main.rs +++ b/light-node/src/bin/tendermint-light-node/main.rs @@ -1,4 +1,4 @@ -//! Main entry point for LightNode +//! Main entry point for the Tendermint light-node #![deny(warnings, missing_docs, trivial_casts, unused_qualifications)] #![forbid(unsafe_code)] diff --git a/light-node/src/commands/start.rs b/light-node/src/commands/start.rs index 434bca908..bfc653863 100644 --- a/light-node/src/commands/start.rs +++ b/light-node/src/commands/start.rs @@ -37,7 +37,6 @@ use tendermint_light_client::supervisor::Handle; use tendermint_light_client::supervisor::{Instance, Supervisor}; /// `start` subcommand -/// #[derive(Command, Debug, Options)] pub struct StartCmd { /// Path to configuration file @@ -93,7 +92,8 @@ impl config::Override for StartCmd { &self, mut config: LightNodeConfig, ) -> Result { - // TODO(liamsi): figure out if other options would be reasonable to overwrite via CLI arguments. + // TODO(liamsi): figure out if other options would be reasonable to overwrite via CLI + // arguments. if let Some(addr) = self.listen_addr { config.rpc_config.listen_addr = addr; } diff --git a/light-node/src/lib.rs b/light-node/src/lib.rs index 6545cb66f..74dc92346 100644 --- a/light-node/src/lib.rs +++ b/light-node/src/lib.rs @@ -1,8 +1,9 @@ -//! LightNode +//! light-node //! //! The Tendermint light-node wraps the light-client crate into a command-line interface tool. -//! It can be used as a standalone light client daemon and exposes a JSONRPC endpoint from which -//! you can query the current state of the light node. +//! +//! It can be used to initialize and start a standalone light client daemon and exposes a JSONRPC +//! endpoint from which you can query the current state of the light node. // Tip: Deny warnings with `RUSTFLAGS="-D warnings"` environment variable in CI @@ -13,11 +14,14 @@ unused_lifetimes, unused_qualifications )] +#![doc( + html_root_url = "https://docs.rs/tendermint-light-node/0.15.0", + html_logo_url = "https://raw.githubusercontent.com/informalsystems/tendermint-rs/master/img/logo-tendermint-rs.png" +)] pub mod application; pub mod commands; pub mod config; pub mod error; pub mod prelude; -pub mod requester; pub mod rpc; diff --git a/light-node/src/requester.rs b/light-node/src/requester.rs deleted file mode 100644 index 3cbaea4ad..000000000 --- a/light-node/src/requester.rs +++ /dev/null @@ -1,72 +0,0 @@ -use async_trait::async_trait; - -use tendermint::block::signed_header::SignedHeader as TMCommit; -use tendermint::block::Header as TMHeader; -use tendermint::lite::{error, Height, SignedHeader}; -use tendermint::validator; -use tendermint::validator::Set; -use tendermint::{block, lite}; - -use tendermint_rpc as rpc; - -/// RPCRequester wraps the Tendermint rpc::Client. -pub struct RPCRequester { - client: rpc::Client, -} - -impl RPCRequester { - pub fn new(client: rpc::Client) -> Self { - RPCRequester { client } - } -} - -type TMSignedHeader = SignedHeader; - -#[async_trait] -impl lite::types::Requester for RPCRequester { - /// Request the signed header at height h. - /// If h==0, request the latest signed header. - /// TODO: use an enum instead of h==0. - async fn signed_header(&self, h: Height) -> Result { - let height: block::Height = h.into(); - let r = match height.value() { - 0 => self.client.latest_commit().await, - _ => self.client.commit(height).await, - }; - match r { - Ok(response) => Ok(response.signed_header.into()), - Err(error) => Err(error::Kind::RequestFailed.context(error).into()), - } - } - - /// Request the validator set at height h. - async fn validator_set(&self, h: Height) -> Result { - let r = self.client.validators(h).await; - match r { - Ok(response) => Ok(validator::Set::new(response.validators)), - Err(error) => Err(error::Kind::RequestFailed.context(error).into()), - } - } -} - -#[cfg(test)] -mod tests { - use tendermint::lite::types::Header as LiteHeader; - use tendermint::lite::types::Requester as LiteRequester; - use tendermint::lite::types::ValidatorSet as LiteValSet; - - use tendermint_rpc as rpc; - - use super::*; - - // TODO: integration test - #[tokio::test] - #[ignore] - async fn test_val_set() { - let client = rpc::Client::new("localhost:26657".parse().unwrap()); - let req = RPCRequester::new(client); - let r1 = req.validator_set(5).await.unwrap(); - let r2 = req.signed_header(5).await.unwrap(); - assert_eq!(r1.hash(), r2.header().validators_hash()); - } -} diff --git a/light-node/src/rpc.rs b/light-node/src/rpc.rs index 5315c2429..b9f113cce 100644 --- a/light-node/src/rpc.rs +++ b/light-node/src/rpc.rs @@ -1,3 +1,4 @@ +//! JSONRPC Server and Client for the light-node RPC endpoint. use jsonrpc_core::IoHandler; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; @@ -9,10 +10,10 @@ pub use sealed::{Client, Rpc, Server}; /// Run the given [`Server`] on the given address and blocks until closed. /// -/// n.b. The underlying server has semantics to close on drop. Also does it does not offer any way to -/// get the underlying Future to await, so we are left with this rather rudimentary way to control -/// the lifecycle. Should we be interested in a more controlled way to close the server we can -/// expose a handle in the future. +/// n.b. The underlying server has semantics to close on drop. Also it does not offer any way +/// to get the underlying Future to await, so we are left with this rather rudimentary way to +/// control the lifecycle. Should we be interested in a more controlled way to close the server we +/// can expose a handle in the future. pub fn run(server: Server, addr: &str) -> Result<(), error::Error> where H: Handle + Send + Sync + 'static, diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 0ce22b669..9fcf15205 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tendermint-rpc" -version = "0.14.0" +version = "0.15.0" authors = ["Alexander Simmerl "] edition = "2018" license = "Apache-2.0" @@ -28,7 +28,7 @@ getrandom = "0.1" serde = { version = "1", features = [ "derive" ] } serde_bytes = "0.11" serde_json = "1" -tendermint = { version = "0.14.0", path = "../tendermint" } +tendermint = { version = "0.15.0", path = "../tendermint" } thiserror = "1" uuid = { version = "0.8", default-features = false } diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 1e9ef0599..4bda89e09 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "tendermint" -version = "0.14.1" # Also update `html_root_url` in lib.rs and +version = "0.15.0" # Also update `html_root_url` in lib.rs and # depending crates (rpc, light-node, ..) when bumping this license = "Apache-2.0" homepage = "https://www.tendermint.com/" -repository = "https://github.com/interchainio/tendermint-rs/tree/master/tendermint" +repository = "https://github.com/informalsystems/tendermint-rs/tree/master/tendermint" readme = "../README.md" categories = ["cryptography", "cryptography::cryptocurrencies", "database"] keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] diff --git a/tendermint/src/lib.rs b/tendermint/src/lib.rs index 22ff436b4..8ad78b574 100644 --- a/tendermint/src/lib.rs +++ b/tendermint/src/lib.rs @@ -14,7 +14,7 @@ )] #![forbid(unsafe_code)] #![doc( - html_root_url = "https://docs.rs/tendermint/0.14.1", + html_root_url = "https://docs.rs/tendermint/0.15.0", html_logo_url = "https://raw.githubusercontent.com/informalsystems/tendermint-rs/master/img/logo-tendermint-rs.png" )] diff --git a/tendermint/src/lite/types.rs b/tendermint/src/lite/types.rs index 14a4509c9..911eead5b 100644 --- a/tendermint/src/lite/types.rs +++ b/tendermint/src/lite/types.rs @@ -107,7 +107,7 @@ impl TrustThresholdFraction { /// numerator are valid. /// /// The parameters are valid iff `1/3 <= numerator/denominator <= 1`. - /// In any other case we return [`Error::InvalidTrustThreshold`]. + /// In any other case we return [`Kind::InvalidTrustThreshold`]. pub fn new(numerator: u64, denominator: u64) -> Result { if numerator <= denominator && denominator > 0 && 3 * numerator >= denominator { return Ok(Self { diff --git a/tendermint/src/lite_impl/header.rs b/tendermint/src/lite_impl/header.rs index 03315bd56..fc385b756 100644 --- a/tendermint/src/lite_impl/header.rs +++ b/tendermint/src/lite_impl/header.rs @@ -90,7 +90,7 @@ mod test { fn test_hash_height_1() { // JSON extracted from https://github.com/tendermint/tendermint/tree/v0.33 // more precisely `curl`ed from locally build docker image of: - // git log --pretty=format:"%H" -1 15:35:44 + // git log --pretty=format:"%H" -1 // 606d0a89ccabbd3e59cff521f9f4d875cc366ac9 // via // curl -X GET "http://localhost:26657/commit?height=1" -H "accept: application/json" | jq .result.signed_header.header @@ -135,7 +135,7 @@ mod test { fn test_hash_height_2() { // JSON test-vector extracted from https://github.com/tendermint/tendermint/tree/v0.33 // more precisely `curl`ed from locally build docker image of: - // git log --pretty=format:"%H" -1 15:35:44 + // git log --pretty=format:"%H" -1 // 606d0a89ccabbd3e59cff521f9f4d875cc366ac9 // via // curl -X GET "http://localhost:26657/commit?height=2" -H "accept: application/json" | jq .result.signed_header.header