diff --git a/.changelog/unreleased/breaking-changes/983-tendermint-config.md b/.changelog/unreleased/breaking-changes/983-tendermint-config.md new file mode 100644 index 000000000..efd5e537e --- /dev/null +++ b/.changelog/unreleased/breaking-changes/983-tendermint-config.md @@ -0,0 +1,4 @@ +- `[tendermint, tendermint-config]` The `tendermint::config` + module has now been broken out into its own crate (`tendermint- + config`) to help towards facilitating `no_std` compatibility + ([#983](https://github.com/informalsystems/tendermint-rs/issues/983)) \ No newline at end of file diff --git a/.changelog/unreleased/breaking-changes/983-tendermint-node-info.md b/.changelog/unreleased/breaking-changes/983-tendermint-node-info.md new file mode 100644 index 000000000..3ca391c1f --- /dev/null +++ b/.changelog/unreleased/breaking-changes/983-tendermint-node-info.md @@ -0,0 +1,4 @@ +- `[tendermint]` The `tendermint::node::info::OtherInfo::rpc_address` + field type has been changed from `tendermint::net::Address` + to `String` toward facilitating `no_std` compatibility + ([#983](https://github.com/informalsystems/tendermint-rs/issues/983)) \ No newline at end of file diff --git a/.changelog/unreleased/breaking-changes/983-tendermint-node-listen-addr.md b/.changelog/unreleased/breaking-changes/983-tendermint-node-listen-addr.md new file mode 100644 index 000000000..125bed986 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/983-tendermint-node-listen-addr.md @@ -0,0 +1,4 @@ +- `[tendermint]` The `tendermint::node::info::ListenAddress::to_net_address` + method was replaced with a simple `as_str` method toward facilitating + `no_std` compatibility ([#983](https://github.com/informalsystems/tendermint- + rs/issues/983)) \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 4a3a782c9..6411b720b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "abci", + "config", "light-client", "light-client-js", "p2p", diff --git a/abci/Cargo.toml b/abci/Cargo.toml index a590e9265..ef1524ba7 100644 --- a/abci/Cargo.toml +++ b/abci/Cargo.toml @@ -32,11 +32,10 @@ std = [ ] [dependencies] -bytes = "1.0" -prost = { package = "informalsystems-prost", version = "0.8.1" } -tendermint-proto = { version = "0.22.0", path = "../proto" } -tracing = "0.1" -flex-error = { version = "0.4.1", default-features = false } - -structopt = { version = "0.3", optional = true } -tracing-subscriber = { version = "0.2", optional = true } +bytes = { version = "1.0", default-features = false } +prost = { package = "informalsystems-prost", version = "0.8.1", default-features = false } +tendermint-proto = { version = "0.22.0", default-features = false, path = "../proto" } +tracing = { version = "0.1", default-features = false } +flex-error = { version = "0.4.3", default-features = false } +structopt = { version = "0.3", optional = true, default-features = false } +tracing-subscriber = { version = "0.2", optional = true, default-features = false } diff --git a/config/Cargo.toml b/config/Cargo.toml new file mode 100644 index 000000000..5dbf430ba --- /dev/null +++ b/config/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "tendermint-config" +version = "0.22.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/informalsystems/tendermint-rs/tree/master/tendermint" +readme = "../README.md" +categories = ["cryptography", "cryptography::cryptocurrencies", "database"] +keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] +edition = "2018" + +description = """ + tendermint-config provides functions for loading and validating Tendermint + configuration files. It is moved out of the tendermint crate for no_std support + in the tendermint crate. + """ + +authors = [ + "Informal Systems ", +] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +tendermint = { version = "0.22.0", default-features = false, path = "../tendermint" } +flex-error = { version = "0.4.1", default-features = false } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +toml = { version = "0.5" } +url = { version = "2.2" } + +[dev-dependencies] +pretty_assertions = "0.7.2" + +[features] +default = ["std", "eyre_tracer"] +eyre_tracer = ["flex-error/eyre_tracer"] +std = [ + "flex-error/std" +] diff --git a/tendermint/src/config.rs b/config/src/config.rs similarity index 99% rename from tendermint/src/config.rs rename to config/src/config.rs index bf5e59bb2..d7459b04c 100644 --- a/tendermint/src/config.rs +++ b/config/src/config.rs @@ -6,12 +6,10 @@ //! - `node_key.rs`: `config::node_key::NodeKey` //! - `priv_validator_key.rs`: `config::priv_validator_key::PrivValidatorKey` -mod node_key; -mod priv_validator_key; +use crate::net; +use crate::node_key::NodeKey; +use crate::Error; -pub use self::{node_key::NodeKey, priv_validator_key::PrivValidatorKey}; - -use crate::{error::Error, genesis::Genesis, net, node, Moniker, Timeout}; use serde::{de, de::Error as _, ser, Deserialize, Serialize}; use std::{ collections::BTreeMap, @@ -19,6 +17,7 @@ use std::{ path::{Path, PathBuf}, str::FromStr, }; +use tendermint::{genesis::Genesis, node, Moniker, Timeout}; /// Tendermint `config.toml` file #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] diff --git a/config/src/error.rs b/config/src/error.rs new file mode 100644 index 000000000..87321578b --- /dev/null +++ b/config/src/error.rs @@ -0,0 +1,37 @@ +use flex_error::{define_error, DisplayOnly}; +use std::io::Error as IoError; +use tendermint::Error as TendermintError; + +define_error! { + #[derive(Debug, Clone, PartialEq, Eq)] + Error { + Io + [ DisplayOnly ] + |_| { format_args!("I/O error") }, + + FileIo + { path: String } + [ DisplayOnly ] + |e| { format_args!("failed to open file: {}", e.path) }, + + Parse + { data: String } + | e | { format_args!("error parsing data: {}", e.data) }, + + SerdeJson + [ DisplayOnly ] + |_| { format_args!("serde json error") }, + + Toml + [ DisplayOnly ] + |_| { format_args!("toml de error") }, + + ParseUrl + [ DisplayOnly ] + |_| { format_args!("error parsing url error") }, + + Tendermint + [ TendermintError ] + |_| { format_args!("tendermint error") }, + } +} diff --git a/config/src/lib.rs b/config/src/lib.rs new file mode 100644 index 000000000..06ef0eb50 --- /dev/null +++ b/config/src/lib.rs @@ -0,0 +1,27 @@ +//! Tendermint Configuration Utilities +//! +//! This crate defines the [`TendermintConfig`] type, which is used by +//! crates such as `tendermint-rpc` to perform operations based on +//! a common configuration type. + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny( + warnings, + trivial_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications +)] +#![forbid(unsafe_code)] + +pub mod net; + +mod config; +mod error; +mod node_key; +mod priv_validator_key; + +pub use config::*; +pub use error::*; +pub use node_key::NodeKey; +pub use priv_validator_key::PrivValidatorKey; diff --git a/tendermint/src/net.rs b/config/src/net.rs similarity index 89% rename from tendermint/src/net.rs rename to config/src/net.rs index 6e9c4ceff..fd08d4875 100644 --- a/tendermint/src/net.rs +++ b/config/src/net.rs @@ -1,13 +1,13 @@ //! Remote addresses (`tcp://` or `unix://`) -use crate::{error::Error, node}; +use crate::error::Error; use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{self, Display}, - path::PathBuf, str::{self, FromStr}, }; +use tendermint::node::{self, info::ListenAddress}; use url::Url; /// URI prefix for TCP connections @@ -40,10 +40,23 @@ pub enum Address { /// UNIX domain sockets Unix { /// Path to a UNIX domain socket path - path: PathBuf, + path: String, }, } +impl Address { + /// Convert `ListenAddress` to a `net::Address` + pub fn from_listen_address(address: &ListenAddress) -> Option { + let raw_address = address.as_str(); + // TODO(tarcieri): validate these and handle them better at parse time + if raw_address.starts_with("tcp://") { + raw_address.parse().ok() + } else { + format!("tcp://{}", raw_address).parse().ok() + } + } +} + impl<'de> Deserialize<'de> for Address { fn deserialize>(deserializer: D) -> Result { Self::from_str(&String::deserialize(deserializer)?) @@ -64,7 +77,7 @@ impl Display for Address { host, port, } => write!(f, "{}{}@{}:{}", TCP_PREFIX, peer_id, host, port), - Address::Unix { path } => write!(f, "{}{}", UNIX_PREFIX, path.display()), + Address::Unix { path } => write!(f, "{}{}", UNIX_PREFIX, path), } } } @@ -83,7 +96,8 @@ impl FromStr for Address { match url.scheme() { "tcp" => Ok(Self::Tcp { peer_id: if !url.username().is_empty() { - Some(url.username().parse()?) + let username = url.username().parse().map_err(Error::tendermint)?; + Some(username) } else { None }, @@ -98,7 +112,7 @@ impl FromStr for Address { })?, }), "unix" => Ok(Self::Unix { - path: PathBuf::from(url.path()), + path: url.path().to_string(), }), _ => Err(Error::parse(format!("invalid address scheme: {:?}", addr))), } @@ -114,7 +128,7 @@ impl Serialize for Address { #[cfg(test)] mod tests { use super::*; - use crate::node; + use tendermint::node; const EXAMPLE_TCP_ADDR: &str = "tcp://abd636b766dcefb5322d8ca40011ec2cb35efbc2@35.192.61.41:26656"; @@ -175,7 +189,7 @@ mod tests { let addr = EXAMPLE_UNIX_ADDR.parse::
().unwrap(); match addr { Address::Unix { path } => { - assert_eq!(path.to_str().unwrap(), "/tmp/node.sock"); + assert_eq!(path, "/tmp/node.sock"); } other => panic!("unexpected address type: {:?}", other), } diff --git a/tendermint/src/config/node_key.rs b/config/src/node_key.rs similarity index 88% rename from tendermint/src/config/node_key.rs rename to config/src/node_key.rs index e1abe7e4a..981119c2d 100644 --- a/tendermint/src/config/node_key.rs +++ b/config/src/node_key.rs @@ -1,8 +1,10 @@ //! Node keys -use crate::{error::Error, node, private_key::PrivateKey, public_key::PublicKey}; use serde::{Deserialize, Serialize}; use std::{fs, path::Path}; +use tendermint::{node, private_key::PrivateKey, public_key::PublicKey}; + +use crate::error::Error; /// P2P node private keys #[derive(Serialize, Deserialize)] @@ -31,8 +33,10 @@ impl NodeKey { /// Get the public key for this keypair pub fn public_key(&self) -> PublicKey { + #[allow(unreachable_patterns)] match &self.priv_key { PrivateKey::Ed25519(keypair) => keypair.public.into(), + _ => unreachable!(), } } diff --git a/tendermint/src/config/priv_validator_key.rs b/config/src/priv_validator_key.rs similarity index 87% rename from tendermint/src/config/priv_validator_key.rs rename to config/src/priv_validator_key.rs index f6f3d7bc4..6dcc2508d 100644 --- a/tendermint/src/config/priv_validator_key.rs +++ b/config/src/priv_validator_key.rs @@ -1,9 +1,11 @@ //! Validator private keys -use crate::public_key::TendermintKey; -use crate::{account, error::Error, private_key::PrivateKey, public_key::PublicKey}; use serde::{Deserialize, Serialize}; use std::{fs, path::Path}; +use tendermint::public_key::TendermintKey; +use tendermint::{account, private_key::PrivateKey, public_key::PublicKey}; + +use crate::error::Error; /// Validator private key #[derive(Serialize, Deserialize)] // JSON custom serialization for priv_validator_key.json @@ -25,7 +27,8 @@ impl PrivValidatorKey { serde_json::from_str::(json_string.as_ref()).map_err(Error::serde_json)?; // Validate that the parsed key type is usable as a consensus key - TendermintKey::new_consensus_key(result.priv_key.public_key())?; + TendermintKey::new_consensus_key(result.priv_key.public_key()) + .map_err(Error::tendermint)?; Ok(result) } diff --git a/config/tests/mod.rs b/config/tests/mod.rs new file mode 100644 index 000000000..0db1b5f54 --- /dev/null +++ b/config/tests/mod.rs @@ -0,0 +1,237 @@ +//! Tests for parsing configuration files. +//! +//! Test config files are located in the `tests/support/config` subdirectory. + +#[cfg(test)] +use pretty_assertions::assert_eq; +use std::{fs, path::PathBuf, time::Duration}; +use tendermint::node; +use tendermint_config::*; + +/// Read a fixture file from the `support/config` directory +fn read_fixture(name: &str) -> String { + fs::read_to_string(PathBuf::from("./tests/support/config/").join(name)).unwrap() +} + +/// Parse an example `config.toml` file to a `TendermintConfig` struct +#[allow(clippy::cognitive_complexity)] +#[test] +fn config_toml_parser() { + let config_toml = read_fixture("config.toml"); + let config = TendermintConfig::parse_toml(&config_toml).unwrap(); + + // main base config options + + assert_eq!( + config.proxy_app, + "tcp://127.0.0.1:26658".parse::().unwrap() + ); + assert_eq!(config.moniker.as_ref(), "technodrome"); + assert!(config.fast_sync); + assert_eq!(config.db_backend, DbBackend::GoLevelDb); + assert_eq!(config.db_dir, PathBuf::from("data")); + assert_eq!(config.log_level.global, Some("info".to_string())); + assert_eq!(config.log_level.get("main"), Some("info")); + assert_eq!(config.log_level.get("state"), Some("info")); + assert_eq!(config.log_level.get("*"), Some("info")); + assert_eq!(config.log_format, LogFormat::Plain); + assert_eq!(config.genesis_file, PathBuf::from("config/genesis.json")); + assert_eq!( + config.priv_validator_key_file, + Some(PathBuf::from("config/priv_validator_key.json")) + ); + assert_eq!( + config.priv_validator_state_file, + PathBuf::from("data/priv_validator_state.json") + ); + assert_eq!(config.priv_validator_laddr, None); + assert_eq!(config.node_key_file, PathBuf::from("config/node_key.json")); + assert_eq!(config.abci, AbciMode::Socket); + assert!(!config.filter_peers); + + // rpc server configuration options + + let rpc = &config.rpc; + assert_eq!( + rpc.laddr, + "tcp://0.0.0.0:26657".parse::().unwrap() + ); + assert!(rpc.cors_allowed_origins.is_empty()); + assert_eq!(rpc.cors_allowed_methods.len(), 3); + assert_eq!(rpc.cors_allowed_methods[0].as_ref(), "HEAD"); + assert_eq!(rpc.cors_allowed_methods[1].as_ref(), "GET"); + assert_eq!(rpc.cors_allowed_methods[2].as_ref(), "POST"); + assert_eq!(rpc.cors_allowed_headers.len(), 5); + assert_eq!(rpc.cors_allowed_headers[0].as_ref(), "Origin"); + assert_eq!(rpc.cors_allowed_headers[1].as_ref(), "Accept"); + assert_eq!(rpc.cors_allowed_headers[2].as_ref(), "Content-Type"); + assert_eq!(rpc.cors_allowed_headers[3].as_ref(), "X-Requested-With"); + assert_eq!(rpc.cors_allowed_headers[4].as_ref(), "X-Server-Time"); + assert_eq!(rpc.grpc_laddr, None); + assert_eq!(rpc.grpc_max_open_connections, 900); + assert!(!rpc.unsafe_commands); + assert_eq!(rpc.max_open_connections, 900); + assert_eq!(rpc.max_subscription_clients, 100); + assert_eq!(rpc.max_subscriptions_per_client, 5); + assert_eq!(*rpc.timeout_broadcast_tx_commit, Duration::from_secs(10)); + assert_eq!(rpc.tls_cert_file, None); + assert_eq!(rpc.tls_key_file, None); + + // peer to peer configuration options + + let p2p = &config.p2p; + assert_eq!( + p2p.laddr, + "tcp://0.0.0.0:26656".parse::().unwrap() + ); + assert_eq!(p2p.external_address, None); + assert_eq!(p2p.seeds.len(), 2); + assert_eq!( + p2p.seeds[0], + "tcp://c2e1bde78877975b31e6f06e77da200a38048e2b@seed-1.example.com:26656" + .parse::() + .unwrap() + ); + assert_eq!( + p2p.seeds[1], + "tcp://0eafed3e9e76f626a299e1b8a79454fffe9ca83c@seed-2.example.com:26656" + .parse::() + .unwrap() + ); + assert_eq!(p2p.persistent_peers.len(), 2); + assert_eq!( + p2p.persistent_peers[0], + "tcp://70d834561f91613153e4a873f01a2cbbf1b9678d@1.2.3.4:26656" + .parse::() + .unwrap() + ); + assert_eq!( + p2p.persistent_peers[1], + "tcp://f68ed33a0baa0c734a939a9e60659566adc725cd@peer-2.example.com:26656" + .parse::() + .unwrap() + ); + assert!(!p2p.upnp); + assert_eq!(p2p.addr_book_file, PathBuf::from("config/addrbook.json")); + assert!(p2p.addr_book_strict); + assert_eq!(p2p.max_num_inbound_peers, 40); + assert_eq!(p2p.max_num_outbound_peers, 10); + assert_eq!(*p2p.flush_throttle_timeout, Duration::from_millis(100)); + assert_eq!(p2p.max_packet_msg_payload_size, 1024); + assert_eq!(p2p.send_rate.bytes_per_sec(), 5_120_000); + assert_eq!(p2p.recv_rate.bytes_per_sec(), 5_120_000); + assert!(p2p.pex); + assert!(!p2p.seed_mode); + assert_eq!(p2p.private_peer_ids.len(), 3); + assert_eq!( + p2p.private_peer_ids[0], + "8112E5C5AB6A48ADCC0E875D58A4264A2639F6A8" + .parse::() + .unwrap() + ); + assert_eq!( + p2p.private_peer_ids[1], + "3D1B9086E48C7BDF7F0D766351EED812A75DE500" + .parse::() + .unwrap() + ); + assert_eq!( + p2p.private_peer_ids[2], + "A7306AEE50627E68177A002BADD3BA4A45301AD4" + .parse::() + .unwrap() + ); + assert!(!p2p.allow_duplicate_ip); + assert_eq!(*p2p.handshake_timeout, Duration::from_secs(20)); + assert_eq!(*p2p.dial_timeout, Duration::from_secs(3)); + + // mempool configuration options + + let mempool = &config.mempool; + assert!(mempool.recheck); + assert!(mempool.broadcast); + assert_eq!(mempool.wal_dir, None); + assert_eq!(mempool.size, 5000); + assert_eq!(mempool.max_txs_bytes, 1_073_741_824); + assert_eq!(mempool.cache_size, 10000); + + // consensus configuration options + + let consensus = &config.consensus; + assert_eq!(consensus.wal_file, PathBuf::from("data/cs.wal/wal")); + assert_eq!(*consensus.timeout_propose, Duration::from_secs(3)); + assert_eq!(*consensus.timeout_propose_delta, Duration::from_millis(500)); + assert_eq!(*consensus.timeout_prevote, Duration::from_secs(1)); + assert_eq!(*consensus.timeout_prevote_delta, Duration::from_millis(500)); + assert_eq!(*consensus.timeout_precommit, Duration::from_secs(1)); + assert_eq!( + *consensus.timeout_precommit_delta, + Duration::from_millis(500) + ); + assert_eq!(*consensus.timeout_commit, Duration::from_secs(5)); + assert!(!consensus.skip_timeout_commit); + assert_eq!( + *consensus.create_empty_blocks_interval, + Duration::from_secs(0) + ); + assert_eq!( + *consensus.peer_gossip_sleep_duration, + Duration::from_millis(100) + ); + assert_eq!( + *consensus.peer_query_maj23_sleep_duration, + Duration::from_secs(2) + ); + + // transactions indexer configuration options + + let tx_index = &config.tx_index; + assert_eq!(tx_index.indexer, TxIndexer::Kv); + + // instrumentation configuration options + + let instrumentation = &config.instrumentation; + assert!(!instrumentation.prometheus); + assert_eq!(instrumentation.prometheus_listen_addr, ":26660"); + assert_eq!(instrumentation.max_open_connections, 3); + assert_eq!(instrumentation.namespace, "tendermint"); +} + +/// Parse an example `node_key.json` file to a `NodeKey` struct +#[test] +fn node_key_parser() { + let raw_node_key = read_fixture("node_key.json"); + let node_key = NodeKey::parse_json(&raw_node_key).unwrap(); + assert_eq!( + node_key.node_id().to_string(), + "1A7B6BCF3D6FB055AB3AEBCA415847531B626699" + ); +} + +/// Parse an example `priv_validator_key.json` to a `PrivValidatorKey` struct +#[test] +fn priv_validator_json_parser() { + let raw_priv_validator_key = read_fixture("priv_validator_key.json"); + let priv_validator_key = PrivValidatorKey::parse_json(&raw_priv_validator_key).unwrap(); + assert_eq!( + priv_validator_key.consensus_pubkey().to_hex(), + "F26BF4B2A2E84CEB7A53C3F1AE77408779B20064782FBADBDF0E365959EE4534" + ); +} + +/// Parse an example `config.toml` file to a `TendermintConfig` struct, then +/// serialize it and parse again. +#[test] +fn parsing_roundtrip() { + let config_toml = read_fixture("config.toml"); + let config = TendermintConfig::parse_toml(&config_toml).unwrap(); + + let written_config_toml = toml::to_string(&config).unwrap(); + let written_config = TendermintConfig::parse_toml(&written_config_toml).unwrap(); + + assert_eq!( + config, written_config, + "written config {}", + written_config_toml + ); +} diff --git a/tendermint/tests/support/TestDeriveSecretsAndChallenge.golden b/config/tests/support/TestDeriveSecretsAndChallenge.golden similarity index 100% rename from tendermint/tests/support/TestDeriveSecretsAndChallenge.golden rename to config/tests/support/TestDeriveSecretsAndChallenge.golden diff --git a/tendermint/tests/support/config/config.toml b/config/tests/support/config/config.toml similarity index 100% rename from tendermint/tests/support/config/config.toml rename to config/tests/support/config/config.toml diff --git a/tendermint/tests/support/config/node_key.json b/config/tests/support/config/node_key.json similarity index 100% rename from tendermint/tests/support/config/node_key.json rename to config/tests/support/config/node_key.json diff --git a/tendermint/tests/support/config/priv_validator_key.json b/config/tests/support/config/priv_validator_key.json similarity index 100% rename from tendermint/tests/support/config/priv_validator_key.json rename to config/tests/support/config/priv_validator_key.json diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 3a8bb167c..0a82b44b3 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -31,21 +31,21 @@ std = [ ] [dependencies] -chacha20poly1305 = "0.8" -ed25519-dalek = "1" -eyre = "0.6" -flume = "0.10.7" -hkdf = "0.10.0" -merlin = "2" -prost = { package = "informalsystems-prost", version = "0.8.1" } -rand_core = { version = "0.5", features = ["std"] } -sha2 = "0.9" -subtle = "2" -x25519-dalek = "1.1" -zeroize = "1" -signature = "1.3.0" -aead = "0.4.1" -flex-error = { version = "0.4.1", default-features = false } +chacha20poly1305 = { version = "0.8", default-features = false, features = ["reduced-round"] } +ed25519-dalek = { version = "1", default-features = false } +eyre = { version = "0.6", default-features = false } +flume = { version = "0.10.7", default-features = false } +hkdf = { version = "0.10.0", default-features = false } +merlin = { version = "2", default-features = false } +prost = { package = "informalsystems-prost", version = "0.8.1", default-features = false } +rand_core = { version = "0.5", default-features = false, features = ["std"] } +sha2 = { version = "0.9", default-features = false } +subtle = { version = "2", default-features = false } +x25519-dalek = { version = "1.1", default-features = false } +zeroize = { version = "1", default-features = false } +signature = { version = "1.3.0", default-features = false } +aead = { version = "0.4.1", default-features = false } +flex-error = { version = "0.4.3", default-features = false } # path dependencies tendermint = { path = "../tendermint", version = "0.22.0" } diff --git a/p2p/src/error.rs b/p2p/src/error.rs index 3a33aadbe..a0530c68d 100644 --- a/p2p/src/error.rs +++ b/p2p/src/error.rs @@ -1,6 +1,6 @@ //! Error types -use flex_error::{define_error, DisplayError, TraceError}; +use flex_error::{define_error, DisplayOnly}; use prost::DecodeError; use signature::Error as SignatureError; @@ -22,11 +22,11 @@ define_error! { | _ | { "malformed handshake message (protocol version mismatch?)" }, Io - [ TraceError ] + [ DisplayOnly ] | _ | { "io error" }, Decode - [ TraceError ] + [ DisplayOnly ] | _ | { "malformed handshake message (protocol version mismatch?)" }, MissingSecret @@ -36,14 +36,14 @@ define_error! { | _ | { "public key missing" }, Signature - [ TraceError ] + [ DisplayOnly ] | _ | { "signature error" }, UnsupportedKey | _ | { "secp256k1 is not supported" }, Aead - [ DisplayError ] + [ DisplayOnly ] | _ | { "aead error" }, ShortCiphertext diff --git a/proto/Cargo.toml b/proto/Cargo.toml index d385e1a92..b18c28b42 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -17,16 +17,16 @@ description = """ all-features = true [dependencies] -prost = { package = "informalsystems-prost", version = "0.8.1" } -prost-types = { package = "informalsystems-prost-types", version = "0.8.1" } -bytes = "1.0" -serde = { version = "1.0", features = ["derive"] } -subtle-encoding = "0.5" -serde_bytes = "0.11" -num-traits = "0.2" -num-derive = "0.3" +prost = { package = "informalsystems-prost", version = "0.8.1", default-features = false } +prost-types = { package = "informalsystems-prost-types", version = "0.8.1", default-features = false } +bytes = { version = "1.0", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive"] } +serde_bytes = { version = "0.11", default-features = false, features = ["alloc"] } +subtle-encoding = { version = "0.5", default-features = false, features = ["hex", "base64", "alloc"] } +num-traits = { version = "0.2", default-features = false } +num-derive = { version = "0.3", default-features = false } chrono = { version = "0.4", default-features = false, features = ["serde", "alloc"] } -flex-error = { version = "0.4.1", default-features = false } +flex-error = { version = "0.4.3", default-features = false } [dev-dependencies] serde_json = "1.0" diff --git a/release.sh b/release.sh index 828815aab..8bf0ccc72 100755 --- a/release.sh +++ b/release.sh @@ -36,7 +36,7 @@ set -e # A space-separated list of all the crates we want to publish, in the order in # which they must be published. It's important to respect this order, since # each subsequent crate depends on one or more of the preceding ones. -DEFAULT_CRATES="tendermint-proto tendermint-std-ext tendermint tendermint-abci tendermint-rpc tendermint-p2p tendermint-light-client tendermint-light-client-js tendermint-testgen" +DEFAULT_CRATES="tendermint-proto tendermint-std-ext tendermint tendermint-config tendermint-abci tendermint-rpc tendermint-p2p tendermint-light-client tendermint-light-client-js tendermint-testgen" # Allows us to override the crates we want to publish. CRATES=${*:-${DEFAULT_CRATES}} diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 384208921..611d10b5e 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -73,6 +73,7 @@ pin-project = "1.0.1" serde = { version = "1", features = [ "derive" ] } serde_bytes = "0.11" serde_json = "1" +tendermint-config = { version = "0.22.0", path = "../config" } tendermint = { version = "0.22.0", path = "../tendermint" } tendermint-proto = { version = "0.22.0", path = "../proto" } thiserror = "1" diff --git a/rpc/src/client/transport/http.rs b/rpc/src/client/transport/http.rs index 40abb925c..69a96afcb 100644 --- a/rpc/src/client/transport/http.rs +++ b/rpc/src/client/transport/http.rs @@ -5,7 +5,7 @@ use crate::{Error, Scheme, SimpleRequest, Url}; use async_trait::async_trait; use std::convert::{TryFrom, TryInto}; use std::str::FromStr; -use tendermint::net; +use tendermint_config::net; /// A JSON-RPC/HTTP Tendermint RPC client (implements [`crate::Client`]). /// diff --git a/rpc/src/client/transport/websocket.rs b/rpc/src/client/transport/websocket.rs index 01ef6d715..8d5068e7d 100644 --- a/rpc/src/client/transport/websocket.rs +++ b/rpc/src/client/transport/websocket.rs @@ -24,7 +24,7 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::ops::Add; use std::str::FromStr; -use tendermint::net; +use tendermint_config::net; use tokio::time::{Duration, Instant}; use tracing::{debug, error}; @@ -806,7 +806,7 @@ mod test { use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; - use tendermint::net; + use tendermint_config::net; use tokio::fs; use tokio::net::{TcpListener, TcpStream}; use tokio::task::JoinHandle; diff --git a/rpc/tests/kvstore_fixtures.rs b/rpc/tests/kvstore_fixtures.rs index b7df5db50..a69604e76 100644 --- a/rpc/tests/kvstore_fixtures.rs +++ b/rpc/tests/kvstore_fixtures.rs @@ -6,6 +6,7 @@ use subtle_encoding::{base64, hex}; use tendermint::abci::transaction::Hash; use tendermint::evidence::Duration; use tendermint::public_key; +use tendermint_config::net::Address; use tendermint_rpc::{ endpoint, error::{Error, ErrorDetail}, @@ -793,14 +794,14 @@ fn incoming_fixtures() { "status" => { let result = endpoint::status::Response::from_string(content).unwrap(); assert_eq!( - result.node_info.listen_addr.to_net_address().unwrap(), - tendermint::net::Address::from_str("tcp://0.0.0.0:26656").unwrap() + Address::from_listen_address(&result.node_info.listen_addr).unwrap(), + Address::from_str("tcp://0.0.0.0:26656").unwrap() ); assert_eq!(result.node_info.moniker.to_string(), "dockernode"); assert_eq!(result.node_info.network.to_string(), CHAIN_ID); assert_eq!( result.node_info.other.rpc_address, - tendermint::net::Address::from_str("tcp://0.0.0.0:26657").unwrap() + format!("{}", Address::from_str("tcp://0.0.0.0:26657").unwrap()) ); assert_eq!( result.node_info.other.tx_index, diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 69f6964dd..025f21c05 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -33,33 +33,29 @@ rustdoc-args = ["--cfg", "docsrs"] crate-type = ["cdylib", "rlib"] [dependencies] -async-trait = "0.1" -bytes = "1.0" -chrono = { version = "0.4.19", features = ["serde"] } -ed25519 = "1" -ed25519-dalek = { version = "1", features = ["serde"] } -futures = "0.3" -num-traits = "0.2" -once_cell = "1.3" -prost = { package = "informalsystems-prost", version = "0.8.1" } -prost-types = { package = "informalsystems-prost-types", version = "0.8.1" } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -serde_bytes = "0.11" -serde_repr = "0.1" +async-trait = { version = "0.1", default-features = false } +bytes = { version = "1.0", default-features = false } +chrono = { version = "0.4.19", default-features = false, features = ["serde"] } +ed25519 = { version = "1", default-features = false } +ed25519-dalek = { version = "1", default-features = false, features = ["u64_backend"] } +futures = { version = "0.3", default-features = false } +num-traits = { version = "0.2", default-features = false } +once_cell = { version = "1.3", default-features = false } +prost = { package = "informalsystems-prost", version = "0.8.1", default-features = false } +prost-types = { package = "informalsystems-prost-types", version = "0.8.1", default-features = false } +serde = { version = "1", default-features = false, features = ["derive"] } +serde_json = { version = "1", default-features = false, features = ["alloc"] } +serde_bytes = { version = "0.11", default-features = false } +serde_repr = { version = "0.1", default-features = false } sha2 = { version = "0.9", default-features = false } -signature = "1.2" -subtle = "2" -subtle-encoding = { version = "0.5", features = ["bech32-preview"] } -tendermint-proto = { version = "0.22.0", path = "../proto" } -toml = { version = "0.5" } -url = { version = "2.2" } -zeroize = { version = "1.1", features = ["zeroize_derive"] } -flex-error = { version = "0.4.1", default-features = false } -time = "0.1.40" - -k256 = { version = "0.9", optional = true, features = ["ecdsa"] } -ripemd160 = { version = "0.9", optional = true } +signature = { version = "1.2", default-features = false } +subtle = { version = "2", default-features = false } +subtle-encoding = { version = "0.5", default-features = false, features = ["bech32-preview"] } +tendermint-proto = { version = "0.22.0", default-features = false, path = "../proto" } +zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } +flex-error = { version = "0.4.3", default-features = false } +k256 = { version = "0.9", optional = true, default-features = false, features = ["ecdsa", "sha256"] } +ripemd160 = { version = "0.9", default-features = false, optional = true } [features] default = ["std", "eyre_tracer"] diff --git a/tendermint/src/error.rs b/tendermint/src/error.rs index 635a9207e..ace2b0fb1 100644 --- a/tendermint/src/error.rs +++ b/tendermint/src/error.rs @@ -6,7 +6,6 @@ use alloc::string::String; use core::num::TryFromIntError; use flex_error::{define_error, DisplayOnly}; use serde::{Deserialize, Serialize}; -use std::io::Error as IoError; define_error! { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -18,15 +17,6 @@ define_error! { { detail: String } |e| { format_args!("invalid key: {}", e) }, - Io - [ DisplayOnly ] - |_| { format_args!("I/O error") }, - - FileIo - { path: String } - [ DisplayOnly ] - |e| { format_args!("failed to open file: {}", e.path) }, - Length |_| { format_args!("length error") }, @@ -39,10 +29,6 @@ define_error! { [ DisplayOnly] | e | { format_args!("error parsing int data: {}", e.data) }, - ParseUrl - [ DisplayOnly ] - |_| { format_args!("error parsing url error") }, - Protocol { detail: String } |e| { format_args!("protocol error: {}", e.detail) }, @@ -195,14 +181,6 @@ define_error! { [ DisplayOnly ] |_| { format_args!("subtle encoding error") }, - SerdeJson - [ DisplayOnly ] - |_| { format_args!("serde json error") }, - - Toml - [ DisplayOnly ] - |_| { format_args!("toml de error") }, - Signature [ DisplayOnly ] |_| { format_args!("signature error") }, diff --git a/tendermint/src/lib.rs b/tendermint/src/lib.rs index cfe2badda..28b3f26cb 100644 --- a/tendermint/src/lib.rs +++ b/tendermint/src/lib.rs @@ -28,14 +28,12 @@ pub mod account; pub mod block; pub mod chain; pub mod channel; -pub mod config; pub mod consensus; pub mod evidence; pub mod genesis; pub mod hash; pub mod merkle; mod moniker; -pub mod net; pub mod node; pub mod private_key; pub mod proposal; diff --git a/tendermint/src/node/info.rs b/tendermint/src/node/info.rs index 427813629..3d2c7b8ab 100644 --- a/tendermint/src/node/info.rs +++ b/tendermint/src/node/info.rs @@ -1,6 +1,6 @@ //! Node information (used in RPC responses) -use crate::{chain, channel::Channels, net, node, serializers, Moniker, Version}; +use crate::{chain, channel::Channels, node, serializers, Moniker, Version}; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -58,14 +58,8 @@ impl ListenAddress { ListenAddress(s) } - /// Convert `ListenAddress` to a `net::Address` - pub fn to_net_address(&self) -> Option { - // TODO(tarcieri): validate these and handle them better at parse time - if self.0.starts_with("tcp://") { - self.0.parse().ok() - } else { - format!("tcp://{}", self.0).parse().ok() - } + pub fn as_str(&self) -> &str { + &self.0 } } @@ -82,7 +76,7 @@ pub struct OtherInfo { pub tx_index: TxIndexStatus, /// RPC address - pub rpc_address: net::Address, + pub rpc_address: String, } /// Transaction index status diff --git a/tendermint/tests/config.rs b/tendermint/tests/config.rs deleted file mode 100644 index 60d947a74..000000000 --- a/tendermint/tests/config.rs +++ /dev/null @@ -1,238 +0,0 @@ -//! Tests for parsing configuration files. -//! -//! Test config files are located in the `tests/support/config` subdirectory. - -mod files { - #[cfg(test)] - use pretty_assertions::assert_eq; - use std::{fs, path::PathBuf, time::Duration}; - use tendermint::{config::*, net, node}; - - /// Read a fixture file from the `support/config` directory - fn read_fixture(name: &str) -> String { - fs::read_to_string(PathBuf::from("./tests/support/config/").join(name)).unwrap() - } - - /// Parse an example `config.toml` file to a `TendermintConfig` struct - #[allow(clippy::cognitive_complexity)] - #[test] - fn config_toml_parser() { - let config_toml = read_fixture("config.toml"); - let config = TendermintConfig::parse_toml(&config_toml).unwrap(); - - // main base config options - - assert_eq!( - config.proxy_app, - "tcp://127.0.0.1:26658".parse::().unwrap() - ); - assert_eq!(config.moniker.as_ref(), "technodrome"); - assert!(config.fast_sync); - assert_eq!(config.db_backend, DbBackend::GoLevelDb); - assert_eq!(config.db_dir, PathBuf::from("data")); - assert_eq!(config.log_level.global, Some("info".to_string())); - assert_eq!(config.log_level.get("main"), Some("info")); - assert_eq!(config.log_level.get("state"), Some("info")); - assert_eq!(config.log_level.get("*"), Some("info")); - assert_eq!(config.log_format, LogFormat::Plain); - assert_eq!(config.genesis_file, PathBuf::from("config/genesis.json")); - assert_eq!( - config.priv_validator_key_file, - Some(PathBuf::from("config/priv_validator_key.json")) - ); - assert_eq!( - config.priv_validator_state_file, - PathBuf::from("data/priv_validator_state.json") - ); - assert_eq!(config.priv_validator_laddr, None); - assert_eq!(config.node_key_file, PathBuf::from("config/node_key.json")); - assert_eq!(config.abci, AbciMode::Socket); - assert!(!config.filter_peers); - - // rpc server configuration options - - let rpc = &config.rpc; - assert_eq!( - rpc.laddr, - "tcp://0.0.0.0:26657".parse::().unwrap() - ); - assert!(rpc.cors_allowed_origins.is_empty()); - assert_eq!(rpc.cors_allowed_methods.len(), 3); - assert_eq!(rpc.cors_allowed_methods[0].as_ref(), "HEAD"); - assert_eq!(rpc.cors_allowed_methods[1].as_ref(), "GET"); - assert_eq!(rpc.cors_allowed_methods[2].as_ref(), "POST"); - assert_eq!(rpc.cors_allowed_headers.len(), 5); - assert_eq!(rpc.cors_allowed_headers[0].as_ref(), "Origin"); - assert_eq!(rpc.cors_allowed_headers[1].as_ref(), "Accept"); - assert_eq!(rpc.cors_allowed_headers[2].as_ref(), "Content-Type"); - assert_eq!(rpc.cors_allowed_headers[3].as_ref(), "X-Requested-With"); - assert_eq!(rpc.cors_allowed_headers[4].as_ref(), "X-Server-Time"); - assert_eq!(rpc.grpc_laddr, None); - assert_eq!(rpc.grpc_max_open_connections, 900); - assert!(!rpc.unsafe_commands); - assert_eq!(rpc.max_open_connections, 900); - assert_eq!(rpc.max_subscription_clients, 100); - assert_eq!(rpc.max_subscriptions_per_client, 5); - assert_eq!(*rpc.timeout_broadcast_tx_commit, Duration::from_secs(10)); - assert_eq!(rpc.tls_cert_file, None); - assert_eq!(rpc.tls_key_file, None); - - // peer to peer configuration options - - let p2p = &config.p2p; - assert_eq!( - p2p.laddr, - "tcp://0.0.0.0:26656".parse::().unwrap() - ); - assert_eq!(p2p.external_address, None); - assert_eq!(p2p.seeds.len(), 2); - assert_eq!( - p2p.seeds[0], - "tcp://c2e1bde78877975b31e6f06e77da200a38048e2b@seed-1.example.com:26656" - .parse::() - .unwrap() - ); - assert_eq!( - p2p.seeds[1], - "tcp://0eafed3e9e76f626a299e1b8a79454fffe9ca83c@seed-2.example.com:26656" - .parse::() - .unwrap() - ); - assert_eq!(p2p.persistent_peers.len(), 2); - assert_eq!( - p2p.persistent_peers[0], - "tcp://70d834561f91613153e4a873f01a2cbbf1b9678d@1.2.3.4:26656" - .parse::() - .unwrap() - ); - assert_eq!( - p2p.persistent_peers[1], - "tcp://f68ed33a0baa0c734a939a9e60659566adc725cd@peer-2.example.com:26656" - .parse::() - .unwrap() - ); - assert!(!p2p.upnp); - assert_eq!(p2p.addr_book_file, PathBuf::from("config/addrbook.json")); - assert!(p2p.addr_book_strict); - assert_eq!(p2p.max_num_inbound_peers, 40); - assert_eq!(p2p.max_num_outbound_peers, 10); - assert_eq!(*p2p.flush_throttle_timeout, Duration::from_millis(100)); - assert_eq!(p2p.max_packet_msg_payload_size, 1024); - assert_eq!(p2p.send_rate.bytes_per_sec(), 5_120_000); - assert_eq!(p2p.recv_rate.bytes_per_sec(), 5_120_000); - assert!(p2p.pex); - assert!(!p2p.seed_mode); - assert_eq!(p2p.private_peer_ids.len(), 3); - assert_eq!( - p2p.private_peer_ids[0], - "8112E5C5AB6A48ADCC0E875D58A4264A2639F6A8" - .parse::() - .unwrap() - ); - assert_eq!( - p2p.private_peer_ids[1], - "3D1B9086E48C7BDF7F0D766351EED812A75DE500" - .parse::() - .unwrap() - ); - assert_eq!( - p2p.private_peer_ids[2], - "A7306AEE50627E68177A002BADD3BA4A45301AD4" - .parse::() - .unwrap() - ); - assert!(!p2p.allow_duplicate_ip); - assert_eq!(*p2p.handshake_timeout, Duration::from_secs(20)); - assert_eq!(*p2p.dial_timeout, Duration::from_secs(3)); - - // mempool configuration options - - let mempool = &config.mempool; - assert!(mempool.recheck); - assert!(mempool.broadcast); - assert_eq!(mempool.wal_dir, None); - assert_eq!(mempool.size, 5000); - assert_eq!(mempool.max_txs_bytes, 1_073_741_824); - assert_eq!(mempool.cache_size, 10000); - - // consensus configuration options - - let consensus = &config.consensus; - assert_eq!(consensus.wal_file, PathBuf::from("data/cs.wal/wal")); - assert_eq!(*consensus.timeout_propose, Duration::from_secs(3)); - assert_eq!(*consensus.timeout_propose_delta, Duration::from_millis(500)); - assert_eq!(*consensus.timeout_prevote, Duration::from_secs(1)); - assert_eq!(*consensus.timeout_prevote_delta, Duration::from_millis(500)); - assert_eq!(*consensus.timeout_precommit, Duration::from_secs(1)); - assert_eq!( - *consensus.timeout_precommit_delta, - Duration::from_millis(500) - ); - assert_eq!(*consensus.timeout_commit, Duration::from_secs(5)); - assert!(!consensus.skip_timeout_commit); - assert_eq!( - *consensus.create_empty_blocks_interval, - Duration::from_secs(0) - ); - assert_eq!( - *consensus.peer_gossip_sleep_duration, - Duration::from_millis(100) - ); - assert_eq!( - *consensus.peer_query_maj23_sleep_duration, - Duration::from_secs(2) - ); - - // transactions indexer configuration options - - let tx_index = &config.tx_index; - assert_eq!(tx_index.indexer, TxIndexer::Kv); - - // instrumentation configuration options - - let instrumentation = &config.instrumentation; - assert!(!instrumentation.prometheus); - assert_eq!(instrumentation.prometheus_listen_addr, ":26660"); - assert_eq!(instrumentation.max_open_connections, 3); - assert_eq!(instrumentation.namespace, "tendermint"); - } - - /// Parse an example `node_key.json` file to a `NodeKey` struct - #[test] - fn node_key_parser() { - let raw_node_key = read_fixture("node_key.json"); - let node_key = NodeKey::parse_json(&raw_node_key).unwrap(); - assert_eq!( - node_key.node_id().to_string(), - "1A7B6BCF3D6FB055AB3AEBCA415847531B626699" - ); - } - - /// Parse an example `priv_validator_key.json` to a `PrivValidatorKey` struct - #[test] - fn priv_validator_json_parser() { - let raw_priv_validator_key = read_fixture("priv_validator_key.json"); - let priv_validator_key = PrivValidatorKey::parse_json(&raw_priv_validator_key).unwrap(); - assert_eq!( - priv_validator_key.consensus_pubkey().to_hex(), - "F26BF4B2A2E84CEB7A53C3F1AE77408779B20064782FBADBDF0E365959EE4534" - ); - } - - /// Parse an example `config.toml` file to a `TendermintConfig` struct, then - /// serialize it and parse again. - #[test] - fn parsing_roundtrip() { - let config_toml = read_fixture("config.toml"); - let config = TendermintConfig::parse_toml(&config_toml).unwrap(); - - let written_config_toml = toml::to_string(&config).unwrap(); - let written_config = TendermintConfig::parse_toml(&written_config_toml).unwrap(); - - assert_eq!( - config, written_config, - "written config {}", - written_config_toml - ); - } -} diff --git a/tools/abci-test/Cargo.toml b/tools/abci-test/Cargo.toml index 62f4636f1..a41bcdf3f 100644 --- a/tools/abci-test/Cargo.toml +++ b/tools/abci-test/Cargo.toml @@ -14,6 +14,7 @@ description = """ futures = "0.3" structopt = "0.3" tendermint = { version = "0.22.0", path = "../../tendermint" } +tendermint-config = { version = "0.22.0", path = "../../config" } tendermint-rpc = { version = "0.22.0", path = "../../rpc", features = [ "websocket-client" ] } tracing = "0.1" tracing-subscriber = "0.2" diff --git a/tools/abci-test/src/main.rs b/tools/abci-test/src/main.rs index 6dfd072e1..e69cf48a3 100644 --- a/tools/abci-test/src/main.rs +++ b/tools/abci-test/src/main.rs @@ -3,7 +3,7 @@ use futures::StreamExt; use structopt::StructOpt; use tendermint::abci::Transaction; -use tendermint::net::Address; +use tendermint_config::net::Address; use tendermint_rpc::event::EventData; use tendermint_rpc::query::EventType; use tendermint_rpc::{Client, SubscriptionClient, WebSocketClient};