diff --git a/.changelog/unreleased/breaking-changes/1027-light-client-verifier.md b/.changelog/unreleased/breaking-changes/1027-light-client-verifier.md new file mode 100644 index 000000000..2af2cf74c --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1027-light-client-verifier.md @@ -0,0 +1,5 @@ +- `[tendermint-light-client]` Split out the verification functionality from the + `tendermint-light-client` crate into its own `no_std`-compatible crate: + `tendermint-light-client-verifier`. This helps move us closer to `no_std` + compliance in both tendermint-rs and ibc-rs + ([#1027](https://github.com/informalsystems/tendermint-rs/issues/1027)) diff --git a/Cargo.toml b/Cargo.toml index 6411b720b..ad3049454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "config", "light-client", "light-client-js", + "light-client-verifier", "p2p", "pbt-gen", "proto", diff --git a/light-client-js/Cargo.toml b/light-client-js/Cargo.toml index 01ce2f570..eacf498be 100644 --- a/light-client-js/Cargo.toml +++ b/light-client-js/Cargo.toml @@ -25,7 +25,7 @@ serde_json = { version = "1.0", default-features = false } # TODO(thane): Remove once https://github.com/rustwasm/wasm-bindgen/issues/2508 is resolved syn = { version = "=1.0.65", default-features = false } tendermint = { version = "0.23.0", default-features = false, path = "../tendermint" } -tendermint-light-client = { version = "0.23.0", default-features = false, path = "../light-client" } +tendermint-light-client-verifier = { version = "0.23.0", default-features = false, path = "../light-client-verifier" } wasm-bindgen = { version = "0.2.63", default-features = false, features = [ "serde-serialize" ] } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/light-client-js/src/lib.rs b/light-client-js/src/lib.rs index 2097e3345..4f95d861c 100644 --- a/light-client-js/src/lib.rs +++ b/light-client-js/src/lib.rs @@ -1,12 +1,12 @@ //! Tendermint Light Client JavaScript/WASM interface. //! -//! This crate exposes some of the [`tendermint-light-client`] crate's +//! This crate exposes some of the [`tendermint-light-client-verifier`] crate's //! functionality to be used from the JavaScript ecosystem. //! //! For a detailed example, please see the [`verifier-web` example] in the //! repository. //! -//! [`tendermint-light-client`]: https://github.com/informalsystems/tendermint-rs/tree/master/light-client +//! [`tendermint-light-client-verifier`]: https://github.com/informalsystems/tendermint-rs/tree/master/light-client-verifier //! [`verifier-web` example]: https://github.com/informalsystems/tendermint-rs/tree/master/light-client-js/examples/verifier-web mod utils; @@ -14,9 +14,9 @@ mod utils; use serde::{Deserialize, Serialize}; use std::time::Duration; use tendermint::Time; -use tendermint_light_client::components::verifier::{ProdVerifier, Verifier}; -use tendermint_light_client::light_client::Options; -use tendermint_light_client::types::{LightBlock, TrustThreshold}; +use tendermint_light_client_verifier::options::Options; +use tendermint_light_client_verifier::types::{LightBlock, TrustThreshold}; +use tendermint_light_client_verifier::{ProdVerifier, Verifier}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; diff --git a/light-client-js/tests/web.rs b/light-client-js/tests/web.rs index 03e36c753..8ffbe9024 100644 --- a/light-client-js/tests/web.rs +++ b/light-client-js/tests/web.rs @@ -4,9 +4,9 @@ extern crate wasm_bindgen_test; use tendermint::Time; -use tendermint_light_client::components::verifier::Verdict; -use tendermint_light_client::types::LightBlock; use tendermint_light_client_js::{verify, Error, JsOptions}; +use tendermint_light_client_verifier::types::LightBlock; +use tendermint_light_client_verifier::Verdict; use wasm_bindgen::JsValue; use wasm_bindgen_test::*; diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml new file mode 100644 index 000000000..fc1542c3e --- /dev/null +++ b/light-client-verifier/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "tendermint-light-client-verifier" +version = "0.23.0" +edition = "2021" +license = "Apache-2.0" +readme = "README.md" +keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] +categories = ["cryptography::cryptocurrencies", "network-programming"] +repository = "https://github.com/informalsystems/tendermint-rs" +authors = [ + "Informal Systems ", +] + +description = """ + Implementation of the Tendermint Light Client Verification Algorithm. +""" + +# docs.rs-specific configuration +[package.metadata.docs.rs] +# document all features +all-features = true +# defines the configuration attribute `docsrs` +rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = ["flex-error/std", "flex-error/eyre_tracer"] + +[dependencies] +tendermint = { version = "0.23.0", path = "../tendermint", default-features = false } +tendermint-rpc = { version = "0.23.0", path = "../rpc", default-features = false } + +derive_more = { version = "0.99.5", default-features = false, features = ["display"] } +serde = { version = "1.0.106", default-features = false } +time = { version = "0.3.5", default-features = false } +flex-error = { version = "0.4.4", default-features = false } + +[dev-dependencies] +tendermint-testgen = { path = "../testgen", default-features = false } diff --git a/light-client/src/predicates/errors.rs b/light-client-verifier/src/errors.rs similarity index 90% rename from light-client/src/predicates/errors.rs rename to light-client-verifier/src/errors.rs index 6e0f1e121..a9f08220f 100644 --- a/light-client/src/predicates/errors.rs +++ b/light-client-verifier/src/errors.rs @@ -1,14 +1,14 @@ //! Errors which may be raised when verifying a `LightBlock` +use core::time::Duration; use flex_error::define_error; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use tendermint::account::Id; +use tendermint::Error as TendermintError; -use crate::errors::ErrorExt; use crate::operations::voting_power::VotingPowerTally; +use crate::prelude::*; use crate::types::{Hash, Height, Time, Validator, ValidatorAddress}; -use tendermint::account::Id; -use tendermint::Error as TendermintError; define_error! { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -27,15 +27,6 @@ define_error! { e.header_time, e.now) }, - ImplementationSpecific - { - detail: String, - } - | e | { - format_args!("implementation specific: {0}", - e.detail) - }, - NotEnoughTrust { tally: VotingPowerTally, @@ -171,6 +162,21 @@ define_error! { } } +/// Extension methods for `ErrorKind` +pub trait ErrorExt { + /// Whether this error means that the light block + /// cannot be trusted w.r.t. the latest trusted state. + fn not_enough_trust(&self) -> Option; + + /// Whether this error means that the light block has expired, + /// ie. it's outside of the trusting period. + fn has_expired(&self) -> bool; + + /// Whether this error means that a timeout occured when + /// querying a node. + fn is_timeout(&self) -> Option; +} + impl ErrorExt for VerificationErrorDetail { fn not_enough_trust(&self) -> Option { match &self { diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs new file mode 100644 index 000000000..ce413cefd --- /dev/null +++ b/light-client-verifier/src/lib.rs @@ -0,0 +1,14 @@ +#![no_std] + +extern crate alloc; + +mod prelude; + +pub mod errors; +pub mod operations; +pub mod options; +pub mod predicates; +pub mod types; +mod verifier; + +pub use verifier::{PredicateVerifier, ProdVerifier, Verdict, Verifier}; diff --git a/light-client/src/operations.rs b/light-client-verifier/src/operations.rs similarity index 100% rename from light-client/src/operations.rs rename to light-client-verifier/src/operations.rs diff --git a/light-client/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs similarity index 98% rename from light-client/src/operations/commit_validator.rs rename to light-client-verifier/src/operations/commit_validator.rs index f58ee12ea..650ce0d4e 100644 --- a/light-client/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -1,8 +1,8 @@ //! Provides an interface and default implementation for the `CommitValidator` operation use crate::{ + errors::VerificationError, operations::{Hasher, ProdHasher}, - predicates::errors::VerificationError, types::{SignedHeader, ValidatorSet}, }; diff --git a/light-client/src/operations/hasher.rs b/light-client-verifier/src/operations/hasher.rs similarity index 100% rename from light-client/src/operations/hasher.rs rename to light-client-verifier/src/operations/hasher.rs diff --git a/light-client/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs similarity index 98% rename from light-client/src/operations/voting_power.rs rename to light-client-verifier/src/operations/voting_power.rs index 826c9d1c5..1caadd0a3 100644 --- a/light-client/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -1,15 +1,16 @@ //! Provides an interface and default implementation for the `VotingPower` operation +use crate::prelude::*; use crate::{ - predicates::errors::VerificationError, + errors::VerificationError, types::{Commit, SignedHeader, TrustThreshold, ValidatorSet}, }; +use alloc::collections::BTreeSet as HashSet; +use core::fmt; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use std::fmt; -use std::convert::TryFrom; +use core::convert::TryFrom; use tendermint::block::CommitSig; use tendermint::trust_threshold::TrustThreshold as _; use tendermint::vote::{SignedVote, ValidatorIndex, Vote}; @@ -220,7 +221,7 @@ fn non_absent_vote( #[cfg(test)] mod tests { use super::*; - use crate::predicates::errors::VerificationErrorDetail; + use crate::errors::VerificationErrorDetail; use crate::types::LightBlock; use tendermint::trust_threshold::TrustThresholdFraction; use tendermint_testgen::light_block::generate_signed_header; diff --git a/light-client-verifier/src/options.rs b/light-client-verifier/src/options.rs new file mode 100644 index 000000000..767627149 --- /dev/null +++ b/light-client-verifier/src/options.rs @@ -0,0 +1,30 @@ +//! Light client implementation as per the [Core Verification specification][1]. +//! +//! [1]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/verification/verification.md + +use core::time::Duration; + +use derive_more::Display; +use serde::{Deserialize, Serialize}; + +use crate::types::TrustThreshold; + +/// Verification parameters +#[derive(Copy, Clone, Debug, PartialEq, Display, Serialize, Deserialize)] +#[display(fmt = "{:?}", self)] +pub struct Options { + /// Defines what fraction of the total voting power of a known + /// and trusted validator set is sufficient for a commit to be + /// accepted going forward. + pub trust_threshold: TrustThreshold, + + /// How long a validator set is trusted for (must be shorter than the chain's + /// unbonding period) + pub trusting_period: Duration, + + /// Correction parameter dealing with only approximately synchronized clocks. + /// The local clock should always be ahead of timestamps from the blockchain; this + /// is the maximum amount that the local clock may drift behind a timestamp from the + /// blockchain. + pub clock_drift: Duration, +} diff --git a/light-client/src/predicates.rs b/light-client-verifier/src/predicates.rs similarity index 98% rename from light-client/src/predicates.rs rename to light-client-verifier/src/predicates.rs index 8b984c4dc..e7d3a1981 100644 --- a/light-client/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -1,16 +1,15 @@ //! Predicates for light block validation and verification. +use crate::errors::VerificationError; +use crate::prelude::*; use crate::{ operations::{CommitValidator, Hasher, VotingPowerCalculator}, types::{Header, SignedHeader, Time, TrustThreshold, ValidatorSet}, }; -use errors::VerificationError; -use std::time::Duration; +use core::time::Duration; use tendermint::{block::Height, hash::Hash}; -pub mod errors; - /// Production predicates, using the default implementation /// of the `VerificationPredicates` trait. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -205,8 +204,8 @@ pub trait VerificationPredicates: Send + Sync { #[cfg(test)] mod tests { - use std::convert::TryInto; - use std::time::Duration; + use core::convert::TryInto; + use core::time::Duration; use tendermint::block::CommitSig; use tendermint::validator::Set; use time::OffsetDateTime; @@ -216,9 +215,10 @@ mod tests { Commit, Generator, Header, Validator, ValidatorSet, }; - use crate::predicates::{ + use crate::prelude::*; + use crate::{ errors::{VerificationError, VerificationErrorDetail}, - ProdPredicates, VerificationPredicates, + predicates::{ProdPredicates, VerificationPredicates}, }; use crate::operations::{ diff --git a/light-client-verifier/src/prelude.rs b/light-client-verifier/src/prelude.rs new file mode 100644 index 000000000..3aee8bcda --- /dev/null +++ b/light-client-verifier/src/prelude.rs @@ -0,0 +1,14 @@ +pub use core::prelude::v1::*; + +// Re-export according to alloc::prelude::v1 because it is not yet stabilized +// https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html +pub use alloc::borrow::ToOwned; +pub use alloc::boxed::Box; +pub use alloc::string::{String, ToString}; +pub use alloc::vec::Vec; + +pub use alloc::format; +pub use alloc::vec; + +// will be included in 2021 edition. +pub use core::convert::{TryFrom, TryInto}; diff --git a/light-client/src/types.rs b/light-client-verifier/src/types.rs similarity index 98% rename from light-client/src/types.rs rename to light-client-verifier/src/types.rs index 11ce8c31c..6e35f96b0 100644 --- a/light-client/src/types.rs +++ b/light-client-verifier/src/types.rs @@ -1,5 +1,7 @@ //! Defines or just re-exports the main datatypes used by the light client. +use crate::prelude::*; + use derive_more::Display; use serde::{Deserialize, Serialize}; @@ -73,7 +75,7 @@ impl Status { /// /// From least to most trusted: `Failed`, `Unverified`, `Verified`, `Trusted`. pub fn most_trusted(a: Self, b: Self) -> Self { - std::cmp::max(a, b) + core::cmp::max(a, b) } } @@ -201,6 +203,7 @@ impl LatestStatus { mod tests { mod status { + use crate::prelude::*; use crate::types::Status; use Status::*; diff --git a/light-client/src/components/verifier.rs b/light-client-verifier/src/verifier.rs similarity index 97% rename from light-client/src/components/verifier.rs rename to light-client-verifier/src/verifier.rs index 5bf03bc54..00b017de7 100644 --- a/light-client/src/components/verifier.rs +++ b/light-client-verifier/src/verifier.rs @@ -1,21 +1,18 @@ //! Provides an interface and default implementation of the `Verifier` component +use crate::errors::{ErrorExt, VerificationError, VerificationErrorDetail}; use crate::operations::voting_power::VotingPowerTally; use crate::predicates as preds; use crate::types::{TrustedBlockState, UntrustedBlockState}; use crate::{ - errors::ErrorExt, - light_client::Options, operations::{ CommitValidator, Hasher, ProdCommitValidator, ProdHasher, ProdVotingPowerCalculator, VotingPowerCalculator, }, + options::Options, types::Time, }; -use preds::{ - errors::{VerificationError, VerificationErrorDetail}, - ProdPredicates, VerificationPredicates, -}; +use preds::{ProdPredicates, VerificationPredicates}; use serde::{Deserialize, Serialize}; /// Represents the result of the verification performed by the diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 1a76e8d8e..400bed100 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -36,6 +36,7 @@ mbt = [] [dependencies] tendermint = { version = "0.23.0", path = "../tendermint", default-features = false } tendermint-rpc = { version = "0.23.0", path = "../rpc", default-features = false } +tendermint-light-client-verifier = { version = "0.23.0", path = "../light-client-verifier", default-features = false } contracts = { version = "0.4.0", default-features = false } crossbeam-channel = { version = "0.4.2", default-features = false } diff --git a/light-client/examples/light_client.rs b/light-client/examples/light_client.rs index 6b6320ade..564171763 100644 --- a/light-client/examples/light_client.rs +++ b/light-client/examples/light_client.rs @@ -3,15 +3,14 @@ use std::{path::PathBuf, time::Duration}; use gumdrop::Options; use tendermint::Hash; -use tendermint_rpc as rpc; - use tendermint_light_client::supervisor::{Handle as _, Instance}; +use tendermint_light_client::verifier::options::Options as LightClientOptions; +use tendermint_light_client::verifier::types::{Height, PeerId, TrustThreshold}; use tendermint_light_client::{ builder::{LightClientBuilder, SupervisorBuilder}, - light_client, store::memory::MemoryStore, - types::{Height, PeerId, TrustThreshold}, }; +use tendermint_rpc as rpc; #[derive(Debug, Options)] struct CliOptions { @@ -82,7 +81,7 @@ fn make_instance( ) -> Result> { let light_store = MemoryStore::new(); let rpc_client = rpc::HttpClient::new(addr).unwrap(); - let options = light_client::Options { + let options = LightClientOptions { trust_threshold: TrustThreshold::default(), trusting_period: Duration::from_secs(36000), clock_drift: Duration::from_secs(1), diff --git a/light-client/src/builder/error.rs b/light-client/src/builder/error.rs index 66ee70fa9..d5db2142a 100644 --- a/light-client/src/builder/error.rs +++ b/light-client/src/builder/error.rs @@ -5,7 +5,7 @@ use tendermint::block::Height; use tendermint::Hash; use crate::components::io::IoError; -use crate::predicates::errors::VerificationError; +use crate::verifier::errors::VerificationError; define_error! { Error { diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index 4702a21ff..e42953711 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -6,20 +6,23 @@ use crate::builder::error::Error; use crate::components::clock::Clock; use crate::components::io::{AtHeight, Io}; use crate::components::scheduler::Scheduler; -use crate::components::verifier::Verifier; -use crate::light_client::{LightClient, Options}; -use crate::operations::Hasher; -use crate::predicates::VerificationPredicates; +use crate::light_client::LightClient; use crate::state::{State, VerificationTrace}; use crate::store::LightStore; use crate::supervisor::Instance; -use crate::types::{LightBlock, PeerId, Status}; +use crate::verifier::operations::Hasher; +use crate::verifier::options::Options; +use crate::verifier::predicates::VerificationPredicates; +use crate::verifier::types::{LightBlock, PeerId, Status}; +use crate::verifier::Verifier; #[cfg(feature = "rpc-client")] use { - crate::components::clock::SystemClock, crate::components::io::ProdIo, - crate::components::scheduler, crate::components::verifier::ProdVerifier, - crate::operations::ProdHasher, crate::predicates::ProdPredicates, std::time::Duration, + crate::components::clock::SystemClock, + crate::components::io::ProdIo, + crate::components::scheduler, + crate::verifier::{operations::ProdHasher, predicates::ProdPredicates, ProdVerifier}, + core::time::Duration, tendermint_rpc as rpc, }; diff --git a/light-client/src/builder/supervisor.rs b/light-client/src/builder/supervisor.rs index 7b7be6e4d..aea70ae0c 100644 --- a/light-client/src/builder/supervisor.rs +++ b/light-client/src/builder/supervisor.rs @@ -1,9 +1,9 @@ -use std::time::Duration; +use core::time::Duration; use crate::builder::error::Error; use crate::peer_list::{PeerList, PeerListBuilder}; use crate::supervisor::Instance; -use crate::types::PeerId; +use crate::verifier::types::PeerId; #[cfg(feature = "rpc-client")] use { diff --git a/light-client/src/components.rs b/light-client/src/components.rs index 50d709c64..2b30995cc 100644 --- a/light-client/src/components.rs +++ b/light-client/src/components.rs @@ -3,4 +3,6 @@ pub mod clock; pub mod io; pub mod scheduler; -pub mod verifier; + +// Re-export for backward compatibility +pub use tendermint_light_client_verifier as verifier; diff --git a/light-client/src/components/clock.rs b/light-client/src/components/clock.rs index b1dd3ff0e..0a0398cdb 100644 --- a/light-client/src/components/clock.rs +++ b/light-client/src/components/clock.rs @@ -1,6 +1,6 @@ //! Provides an interface and a default implementation of the `Clock` component -use crate::types::Time; +use crate::verifier::types::Time; use std::convert::TryInto; use time::OffsetDateTime; diff --git a/light-client/src/components/io.rs b/light-client/src/components/io.rs index 640583e38..3883f835b 100644 --- a/light-client/src/components/io.rs +++ b/light-client/src/components/io.rs @@ -6,10 +6,9 @@ use std::time::Duration; #[cfg(feature = "rpc-client")] use tendermint_rpc::Client; +use crate::verifier::types::{Height, LightBlock}; use tendermint_rpc as rpc; -use crate::types::{Height, LightBlock}; - #[cfg(feature = "tokio")] type TimeoutError = flex_error::DisplayOnly; @@ -99,8 +98,8 @@ mod prod { use std::time::Duration; - use crate::types::PeerId; use crate::utils::block_on; + use crate::verifier::types::PeerId; use tendermint::account::Id as TMAccountId; use tendermint::block::signed_header::SignedHeader as TMSignedHeader; diff --git a/light-client/src/components/scheduler.rs b/light-client/src/components/scheduler.rs index 6210cd67a..1014a6890 100644 --- a/light-client/src/components/scheduler.rs +++ b/light-client/src/components/scheduler.rs @@ -3,8 +3,8 @@ use contracts::*; use crate::store::LightStore; -use crate::types::Height; -use std::convert::TryInto; +use crate::verifier::types::Height; +use core::convert::TryInto; /// The scheduler decides what block to verify next given the current and target heights. /// diff --git a/light-client/src/contracts.rs b/light-client/src/contracts.rs index 28164ea76..31c42be2b 100644 --- a/light-client/src/contracts.rs +++ b/light-client/src/contracts.rs @@ -1,9 +1,7 @@ //! Predicates used in components contracts. -use crate::{ - store::LightStore, - types::{Height, LightBlock, Status, Time}, -}; +use crate::store::LightStore; +use crate::verifier::types::{Height, LightBlock, Status, Time}; use std::time::Duration; diff --git a/light-client/src/errors.rs b/light-client/src/errors.rs index c92286384..3ac4117f3 100644 --- a/light-client/src/errors.rs +++ b/light-client/src/errors.rs @@ -3,17 +3,18 @@ use std::fmt::Debug; use std::time::Duration; -use crate::operations::voting_power::VotingPowerTally; +use crate::verifier::errors::VerificationErrorDetail; +use crate::verifier::operations::voting_power::VotingPowerTally; +use crate::verifier::options::Options; +use crate::verifier::types::{Hash, Height, LightBlock, PeerId, Status}; use crossbeam_channel as crossbeam; -use crate::{ - components::io::IoError, - light_client::Options, - predicates::errors::VerificationErrorDetail, - types::{Hash, Height, LightBlock, PeerId, Status}, -}; +use crate::components::io::IoError; use flex_error::{define_error, DisplayError, TraceError}; +// Re-export for backward compatibility +pub use crate::verifier::errors::ErrorExt; + #[cfg(feature = "sled")] type SledError = TraceError; @@ -117,21 +118,6 @@ define_error! { } } -/// Extension methods for `ErrorKind` -pub trait ErrorExt { - /// Whether this error means that the light block - /// cannot be trusted w.r.t. the latest trusted state. - fn not_enough_trust(&self) -> Option; - - /// Whether this error means that the light block has expired, - /// ie. it's outside of the trusting period. - fn has_expired(&self) -> bool; - - /// Whether this error means that a timeout occured when - /// querying a node. - fn is_timeout(&self) -> Option; -} - impl ErrorExt for ErrorDetail { fn not_enough_trust(&self) -> Option { if let Self::InvalidLightBlock(e) = self { diff --git a/light-client/src/evidence.rs b/light-client/src/evidence.rs index 3da3538d1..1821bfcbd 100644 --- a/light-client/src/evidence.rs +++ b/light-client/src/evidence.rs @@ -1,10 +1,10 @@ //! Fork evidence data structures and interfaces. -use crate::{components::io::IoError, types::PeerId}; - +use contracts::contract_trait; use tendermint_rpc::abci::transaction::Hash; -use contracts::contract_trait; +use crate::components::io::IoError; +use crate::verifier::types::PeerId; pub use tendermint::evidence::Evidence; diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index 0c4ae7df4..730e8279e 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -1,12 +1,14 @@ //! Fork detection data structures and implementation. +use crate::verifier::errors::ErrorExt; +use crate::verifier::operations::{Hasher, ProdHasher}; +use crate::verifier::types::{LightBlock, PeerId, Status}; + use crate::{ - errors::{Error, ErrorDetail, ErrorExt}, - operations::{Hasher, ProdHasher}, + errors::{Error, ErrorDetail}, state::State, store::memory::MemoryStore, supervisor::Instance, - types::{LightBlock, PeerId, Status}, }; /// Result of fork detection diff --git a/light-client/src/lib.rs b/light-client/src/lib.rs index 340d67ff7..968018131 100644 --- a/light-client/src/lib.rs +++ b/light-client/src/lib.rs @@ -23,15 +23,18 @@ pub mod errors; pub mod evidence; pub mod fork_detector; pub mod light_client; -pub mod operations; pub mod peer_list; -pub mod predicates; pub mod state; pub mod store; pub mod supervisor; -pub mod types; pub(crate) mod utils; +// Re-export `tendermint-light-client-verifier` crate. +pub use tendermint_light_client_verifier as verifier; + +// Re-export for backward compatibility +pub use verifier::{operations, predicates, types}; + #[doc(hidden)] pub mod tests; diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index 1f2fe3b0d..e90168c55 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -2,40 +2,23 @@ //! //! [1]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/verification/verification.md -use std::{fmt, time::Duration}; - use contracts::*; -use derive_more::Display; -use serde::{Deserialize, Serialize}; +use core::fmt; use crate::{ - components::{clock::Clock, io::*, scheduler::*, verifier::*}, + components::{clock::Clock, io::*, scheduler::*}, contracts::*, errors::Error, - operations::Hasher, state::State, - types::{Height, LightBlock, PeerId, Status, TrustThreshold}, + verifier::{ + operations::Hasher, + types::{Height, LightBlock, PeerId, Status}, + Verdict, Verifier, + }, }; -/// Verification parameters -#[derive(Copy, Clone, Debug, PartialEq, Display, Serialize, Deserialize)] -#[display(fmt = "{:?}", self)] -pub struct Options { - /// Defines what fraction of the total voting power of a known - /// and trusted validator set is sufficient for a commit to be - /// accepted going forward. - pub trust_threshold: TrustThreshold, - - /// How long a validator set is trusted for (must be shorter than the chain's - /// unbonding period) - pub trusting_period: Duration, - - /// Correction parameter dealing with only approximately synchronized clocks. - /// The local clock should always be ahead of timestamps from the blockchain; this - /// is the maximum amount that the local clock may drift behind a timestamp from the - /// blockchain. - pub clock_drift: Duration, -} +// Re-export for backward compatibility +pub use crate::verifier::options::Options; /// The light client implements a read operation of a header from the blockchain, /// by communicating with full nodes. As full nodes may be faulty, it cannot trust diff --git a/light-client/src/peer_list.rs b/light-client/src/peer_list.rs index a0e408e30..d1f3d58f4 100644 --- a/light-client/src/peer_list.rs +++ b/light-client/src/peer_list.rs @@ -1,10 +1,11 @@ //! Provides a peer list for use within the `Supervisor` -use crate::{errors::Error, types::PeerId}; - use contracts::{post, pre}; use std::collections::{BTreeSet, HashMap}; +use crate::errors::Error; +use crate::verifier::types::PeerId; + /// A generic container mapping `PeerId`s to some type `T`, /// which keeps track of the primary peer, witnesses, full nodes, /// and faulty nodes. Provides lifecycle methods to swap the primary, diff --git a/light-client/src/state.rs b/light-client/src/state.rs index 219570c92..c4ee0a56d 100644 --- a/light-client/src/state.rs +++ b/light-client/src/state.rs @@ -1,9 +1,7 @@ //! State maintained by the light client. -use crate::{ - store::LightStore, - types::{Height, LightBlock, Status}, -}; +use crate::store::LightStore; +use crate::verifier::types::{Height, LightBlock, Status}; use contracts::*; use std::collections::{HashMap, HashSet}; diff --git a/light-client/src/store.rs b/light-client/src/store.rs index d7c99ebf3..5d27380aa 100644 --- a/light-client/src/store.rs +++ b/light-client/src/store.rs @@ -7,8 +7,8 @@ use std::fmt::Debug; -use crate::types::{Height, LightBlock, Status}; use crate::utils::std_ext; +use crate::verifier::types::{Height, LightBlock, Status}; pub mod memory; diff --git a/light-client/src/store/memory.rs b/light-client/src/store/memory.rs index 300adb2ea..a6a6dd328 100644 --- a/light-client/src/store/memory.rs +++ b/light-client/src/store/memory.rs @@ -1,9 +1,7 @@ //! Transient in-memory store -use crate::{ - store::{LightStore, Status}, - types::{Height, LightBlock}, -}; +use crate::store::{LightStore, Status}; +use crate::verifier::types::{Height, LightBlock}; use std::collections::btree_map::Entry::*; use std::collections::BTreeMap; diff --git a/light-client/src/store/sled.rs b/light-client/src/store/sled.rs index 38219e45d..e02afbed1 100644 --- a/light-client/src/store/sled.rs +++ b/light-client/src/store/sled.rs @@ -1,14 +1,11 @@ //! Persistent store backed by an on-disk `sled` database. pub mod utils; +use utils::HeightIndexedDb; +use crate::verifier::types::{Height, LightBlock}; use std::path::Path; -use crate::{ - store::sled::utils::HeightIndexedDb, - types::{Height, LightBlock}, -}; - use super::{LightStore, Status}; const UNVERIFIED: &str = "unverified"; diff --git a/light-client/src/store/sled/utils.rs b/light-client/src/store/sled/utils.rs index aefe3d7d6..caa7ddf3b 100644 --- a/light-client/src/store/sled/utils.rs +++ b/light-client/src/store/sled/utils.rs @@ -7,7 +7,7 @@ use std::marker::PhantomData; use serde::{de::DeserializeOwned, Serialize}; use crate::errors::Error; -use crate::types::Height; +use crate::verifier::types::Height; /// Provides a view over the database for storing key/value pairs at the given prefix. #[derive(Clone, Debug)] diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index 90fa17fe7..14a1358c1 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -10,7 +10,7 @@ use crate::fork_detector::{Fork, ForkDetection, ForkDetector}; use crate::light_client::LightClient; use crate::peer_list::PeerList; use crate::state::State; -use crate::types::{Height, LatestStatus, LightBlock, PeerId, Status}; +use crate::verifier::types::{Height, LatestStatus, LightBlock, PeerId, Status}; /// Provides an interface to the supervisor for use in downstream code. pub trait Handle: Send + Sync { @@ -424,20 +424,23 @@ impl Handle for SupervisorHandle { mod tests { use super::*; use crate::errors::{Error, ErrorDetail}; - use crate::light_client::Options; - use crate::operations::ProdHasher; use crate::{ components::{ io::{self, AtHeight, Io}, scheduler, - verifier::ProdVerifier, }, fork_detector::ProdForkDetector, store::{memory::MemoryStore, LightStore}, tests::{MockClock, MockEvidenceReporter, MockIo, TrustOptions}, - types::Time, }; - use std::{collections::HashMap, convert::TryFrom, time::Duration}; + + use crate::verifier::operations::ProdHasher; + use crate::verifier::options::Options; + use crate::verifier::types::Time; + use crate::verifier::ProdVerifier; + use core::convert::{Into, TryFrom}; + use core::time::Duration; + use std::collections::HashMap; use tendermint::block::Height; use tendermint::evidence::Duration as DurationStr; use tendermint::trust_threshold::TrustThresholdFraction; @@ -446,10 +449,26 @@ mod tests { response_error::{Code, ResponseError}, }; use tendermint_testgen::helpers::get_time; + use tendermint_testgen::light_block::TmLightBlock; use tendermint_testgen::{ Commit, Generator, Header, LightBlock as TestgenLightBlock, LightChain, ValidatorSet, }; + trait IntoLightBlock { + fn into_light_block(self) -> LightBlock; + } + + impl IntoLightBlock for TmLightBlock { + fn into_light_block(self) -> LightBlock { + LightBlock { + signed_header: self.signed_header, + validators: self.validators, + next_validators: self.next_validators, + provider: self.provider, + } + } + } + fn make_instance( peer_id: PeerId, trust_options: TrustOptions, @@ -570,11 +589,11 @@ mod tests { let commit = Commit::new(header.clone(), 1); let mut lb = TestgenLightBlock::new(header, commit).provider(peer_id); - let mut witness: Vec = vec![lb.generate().unwrap().into()]; + let mut witness: Vec = vec![lb.generate().unwrap().into_light_block()]; for _ in 1..length { lb = lb.next(); - let tm_lb = lb.generate().unwrap().into(); + let tm_lb = lb.generate().unwrap().into_light_block(); witness.push(tm_lb); } @@ -587,7 +606,7 @@ mod tests { let primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let witness = change_provider(primary.clone(), None); @@ -612,7 +631,7 @@ mod tests { let primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let peer_list = make_peer_list(Some(primary), None, get_time(11).unwrap()); @@ -631,7 +650,7 @@ mod tests { let primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let mut light_blocks = primary.clone(); @@ -662,7 +681,7 @@ mod tests { let primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let witness = make_conflicting_witness(5, None, None, None); @@ -699,7 +718,7 @@ mod tests { .light_blocks .clone() .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let mut header = chain.light_blocks[4].header.clone().unwrap(); @@ -713,7 +732,7 @@ mod tests { chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(), None, ); @@ -734,7 +753,7 @@ mod tests { let primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let witness1 = change_provider(primary.clone(), None); @@ -782,7 +801,7 @@ mod tests { let primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); let witness = change_provider(primary.clone(), None); @@ -812,7 +831,7 @@ mod tests { let mut primary = chain .light_blocks .into_iter() - .map(|lb| lb.generate().unwrap().into()) + .map(|lb| lb.generate().unwrap().into_light_block()) .collect::>(); primary[9].signed_header.commit.round = primary[9].signed_header.commit.round.increment(); diff --git a/light-client/src/tests.rs b/light-client/src/tests.rs index 093d4fa79..32c5bfc92 100644 --- a/light-client/src/tests.rs +++ b/light-client/src/tests.rs @@ -1,18 +1,20 @@ //! Utilities and datatypes for use in tests. -use crate::types::{Height, LightBlock, PeerId, SignedHeader, Time, TrustThreshold, ValidatorSet}; - use serde::{Deserialize, Serialize}; use tendermint_rpc as rpc; use tendermint_rpc::abci::transaction::Hash; use crate::components::clock::Clock; use crate::components::io::{AtHeight, Io, IoError}; -use crate::components::verifier::{ProdVerifier, Verdict, Verifier}; use crate::errors::Error; use crate::evidence::EvidenceReporter; -use crate::light_client::{LightClient, Options}; +use crate::light_client::LightClient; use crate::state::State; +use crate::verifier::options::Options; +use crate::verifier::types::{ + Height, LightBlock, PeerId, SignedHeader, Time, TrustThreshold, ValidatorSet, +}; +use crate::verifier::{ProdVerifier, Verdict, Verifier}; use contracts::contract_trait; use std::collections::HashMap; use std::time::Duration; diff --git a/light-client/tests/backward.rs b/light-client/tests/backward.rs index c5a68977c..e7b3bd324 100644 --- a/light-client/tests/backward.rs +++ b/light-client/tests/backward.rs @@ -8,15 +8,18 @@ use tendermint_light_client::{ components::{ io::{AtHeight, Io}, scheduler, - verifier::ProdVerifier, }, errors::Error, - light_client::{LightClient, Options}, - operations::ProdHasher, + light_client::LightClient, state::State, store::{memory::MemoryStore, LightStore}, tests::{MockClock, MockIo}, - types::{Height, LightBlock, Status}, + verifier::{ + operations::ProdHasher, + options::Options, + types::{Height, LightBlock, Status}, + ProdVerifier, + }, }; use tendermint_testgen::{ diff --git a/light-client/tests/light_client.rs b/light-client/tests/light_client.rs index 1f2f626d5..6e3f74d14 100644 --- a/light-client/tests/light_client.rs +++ b/light-client/tests/light_client.rs @@ -5,15 +5,18 @@ use tendermint_light_client::{ components::{ io::{AtHeight, Io}, scheduler, - verifier::ProdVerifier, }, errors::Error, - light_client::{LightClient, Options}, - operations::ProdHasher, + light_client::LightClient, state::State, store::{memory::MemoryStore, LightStore}, tests::*, - types::{LightBlock, Status}, + verifier::{ + operations::ProdHasher, + options::Options, + types::{LightBlock, Status}, + ProdVerifier, + }, }; use tendermint_testgen::light_block::default_peer_id; diff --git a/light-client/tests/model_based.rs b/light-client/tests/model_based.rs index 136567d9e..b512e2ef9 100644 --- a/light-client/tests/model_based.rs +++ b/light-client/tests/model_based.rs @@ -8,12 +8,11 @@ mod mbt { use std::str::FromStr; use std::time::Duration; use tendermint::validator::Set; - use tendermint_light_client::components::verifier::Verdict; - use tendermint_light_client::types::ValidatorSet; - use tendermint_light_client::{ - tests::*, - types::{LightBlock, Time, TrustThreshold}, + use tendermint_light_client::tests::*; + use tendermint_light_client::verifier::types::{ + LightBlock, Time, TrustThreshold, ValidatorSet, }; + use tendermint_light_client::verifier::Verdict; use tendermint_testgen::light_block::default_peer_id; use tendermint_testgen::{ apalache::*, jsonatr::*, light_block::TmLightBlock, validator::generate_validators, diff --git a/light-client/tests/supervisor.rs b/light-client/tests/supervisor.rs index 0a7dcfc3f..cb58db316 100644 --- a/light-client/tests/supervisor.rs +++ b/light-client/tests/supervisor.rs @@ -2,16 +2,19 @@ use tendermint_light_client::{ components::{ io::{AtHeight, Io}, scheduler, - verifier::ProdVerifier, }, fork_detector::ProdForkDetector, - light_client::{self, LightClient}, - operations::ProdHasher, + light_client::LightClient, peer_list::PeerList, state::State, store::LightStore, supervisor::{Handle, Instance, Supervisor}, - types::{LightBlock, PeerId, Status, Time}, + verifier::{ + operations::ProdHasher, + options::Options, + types::{LightBlock, PeerId, Status, Time}, + ProdVerifier, + }, }; use std::collections::HashMap; @@ -40,7 +43,7 @@ fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: verification_trace: HashMap::new(), }; - let options = light_client::Options { + let options = Options { trust_threshold: trust_options.trust_level, trusting_period: trust_options.period.into(), clock_drift: Duration::from_secs(10), diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 5b1abc31d..e12128301 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -55,7 +55,9 @@ k256 = { version = "0.10", optional = true, default-features = false, features = ripemd160 = { version = "0.9", default-features = false, optional = true } [features] -default = ["flex-error/std", "flex-error/eyre_tracer"] +default = ["std"] +std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] +clock = ["time/std"] secp256k1 = ["k256", "ripemd160"] [dev-dependencies] diff --git a/tendermint/src/time.rs b/tendermint/src/time.rs index 3920aad37..a57984232 100644 --- a/tendermint/src/time.rs +++ b/tendermint/src/time.rs @@ -63,6 +63,11 @@ impl From