From 53c99310d9fec7a56461a33893675740e40718d5 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 08:31:18 -0700 Subject: [PATCH] tendermint-rs: Move Address into the crate; node::Id Moves the ValidatorAddr type into the Tendermint crate, as it's generally useful for any remote address. Also moves the `PeerId` type from `secret_connection` into a `node::Id` crate which isn't gated on the rest of the Secret Connection code. --- src/client.rs | 10 +- src/config/{validator/mod.rs => validator.rs} | 8 +- src/config/validator/addr.rs | 90 ------------------ src/session.rs | 3 +- tendermint-rs/src/address.rs | 93 +++++++++++++++++++ tendermint-rs/src/lib.rs | 3 + .../{secret_connection/peer_id.rs => node.rs} | 54 +++++------ tendermint-rs/src/secret_connection.rs | 3 +- .../src/secret_connection/public_key.rs | 7 +- 9 files changed, 137 insertions(+), 134 deletions(-) rename src/config/{validator/mod.rs => validator.rs} (85%) delete mode 100644 src/config/validator/addr.rs create mode 100644 tendermint-rs/src/address.rs rename tendermint-rs/src/{secret_connection/peer_id.rs => node.rs} (60%) diff --git a/src/client.rs b/src/client.rs index 13fba40..e338ade 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,7 +6,7 @@ //! as a "Key Management System". use crate::{ - config::{ValidatorAddr, ValidatorConfig}, + config::ValidatorConfig, error::{KmsError, KmsErrorKind}, keyring::SecretKeyEncoding, session::Session, @@ -21,7 +21,7 @@ use std::{ thread::{self, JoinHandle}, time::Duration, }; -use tendermint::{chain, secret_connection}; +use tendermint::{chain, node, secret_connection, Address}; /// How long to wait after a crash before respawning (in seconds) pub const RESPAWN_DELAY: u64 = 1; @@ -68,7 +68,7 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { } let session_result = match &addr { - ValidatorAddr::Tcp { + Address::Tcp { peer_id, host, port, @@ -82,7 +82,7 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { return; } }, - ValidatorAddr::Unix { socket_path } => unix_session(chain_id, socket_path, should_term), + Address::Unix { path } => unix_session(chain_id, path, should_term), }; if let Err(e) = session_result { @@ -107,7 +107,7 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { /// Create a TCP connection to a validator (encrypted with SecretConnection) fn tcp_session( chain_id: chain::Id, - validator_peer_id: Option, + validator_peer_id: Option, host: &str, port: u16, secret_key_path: &Path, diff --git a/src/config/validator/mod.rs b/src/config/validator.rs similarity index 85% rename from src/config/validator/mod.rs rename to src/config/validator.rs index a398374..e41e1ca 100644 --- a/src/config/validator/mod.rs +++ b/src/config/validator.rs @@ -1,15 +1,11 @@ -mod addr; - use std::path::PathBuf; -use tendermint::chain; - -pub use self::addr::ValidatorAddr; +use tendermint::{chain, Address}; /// Validator configuration #[derive(Clone, Deserialize, Debug)] pub struct ValidatorConfig { /// Address of the validator (`tcp://` or `unix://`) - pub addr: ValidatorAddr, + pub addr: Address, /// Chain ID of the Tendermint network this validator is part of pub chain_id: chain::Id, diff --git a/src/config/validator/addr.rs b/src/config/validator/addr.rs deleted file mode 100644 index b3e5d56..0000000 --- a/src/config/validator/addr.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Validator addresses (`tcp://` or `unix://`) - -use crate::error::{KmsError, KmsErrorKind::*}; -use serde::{de::Error as DeError, Deserialize, Deserializer}; -use std::{ - fmt::{self, Display}, - path::PathBuf, - str::{self, FromStr}, -}; -use tendermint::secret_connection; - -#[derive(Clone, Debug)] -pub enum ValidatorAddr { - /// TCP connections (with SecretConnection transport encryption) - Tcp { - /// Remote peer ID of the validator - // TODO(tarcieri): make this mandatory - peer_id: Option, - - /// Validator hostname or IP address - host: String, - - /// Validator port - port: u16, - }, - - /// UNIX domain sockets - Unix { socket_path: PathBuf }, -} - -impl ValidatorAddr { - /// Get the URI representation of this configuration - pub fn to_uri(&self) -> String { - self.to_string() - } -} - -impl<'de> Deserialize<'de> for ValidatorAddr { - fn deserialize>(deserializer: D) -> Result { - Self::from_str(&String::deserialize(deserializer)?) - .map_err(|e| D::Error::custom(format!("{}", e))) - } -} - -impl Display for ValidatorAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ValidatorAddr::Tcp { host, port, .. } => write!(f, "tcp://{}:{}", host, port), - ValidatorAddr::Unix { socket_path } => write!(f, "unix://{}", socket_path.display()), - } - } -} - -impl FromStr for ValidatorAddr { - type Err = KmsError; - - // TODO: less janky URL parser? (e.g. use `url` crate) - fn from_str(addr: &str) -> Result { - if addr.starts_with("tcp://") { - let authority_parts = addr[6..].split('@').collect::>(); - let (peer_id, authority) = match authority_parts.len() { - 1 => (None, authority_parts[0]), - 2 => (Some(authority_parts[0].parse()?), authority_parts[1]), - _ => fail!(ConfigError, "invalid tcp:// address: {}", addr), - }; - - let host_and_port: Vec<&str> = authority.split(':').collect(); - - if host_and_port.len() != 2 { - fail!(ConfigError, "invalid tcp:// address: {}", addr); - } - - let host = host_and_port[0].to_owned(); - let port = host_and_port[1] - .parse() - .map_err(|_| err!(ConfigError, "invalid tcp:// address (bad port): {}", addr))?; - - Ok(ValidatorAddr::Tcp { - peer_id, - host, - port, - }) - } else if addr.starts_with("unix://") { - let socket_path = PathBuf::from(&addr[7..]); - Ok(ValidatorAddr::Unix { socket_path }) - } else { - fail!(ConfigError, "invalid addr: {}", addr) - } - } -} diff --git a/src/session.rs b/src/session.rs index ae07cb6..0eac723 100644 --- a/src/session.rs +++ b/src/session.rs @@ -25,6 +25,7 @@ use std::{ use subtle::ConstantTimeEq; use tendermint::{ amino_types::{PingRequest, PingResponse, PubKeyRequest, PubKeyResponse}, + node, secret_connection::{self, SecretConnection}, }; @@ -41,7 +42,7 @@ impl Session> { /// Create a new session with the validator at the given address/port pub fn connect_tcp( chain_id: chain::Id, - validator_peer_id: Option, + validator_peer_id: Option, host: &str, port: u16, secret_connection_key: &ed25519::Seed, diff --git a/tendermint-rs/src/address.rs b/tendermint-rs/src/address.rs new file mode 100644 index 0000000..101e67e --- /dev/null +++ b/tendermint-rs/src/address.rs @@ -0,0 +1,93 @@ +//! Remote addresses (`tcp://` or `unix://`) + +use crate::node; +use failure::{bail, Error}; +use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt::{self, Display}, + path::PathBuf, + str::{self, FromStr}, +}; + +/// Remote address (TCP or UNIX socket) +#[derive(Clone, Debug)] +pub enum Address { + /// TCP connections + Tcp { + /// Remote peer ID + peer_id: Option, + + /// Hostname or IP address + host: String, + + /// Port + port: u16, + }, + + /// UNIX domain sockets + Unix { + /// Path to a UNIX domain socket path + path: PathBuf, + }, +} + +impl<'de> Deserialize<'de> for Address { + fn deserialize>(deserializer: D) -> Result { + Self::from_str(&String::deserialize(deserializer)?) + .map_err(|e| D::Error::custom(format!("{}", e))) + } +} + +impl Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Address::Tcp { host, port, .. } => write!(f, "tcp://{}:{}", host, port), + Address::Unix { path } => write!(f, "unix://{}", path.display()), + } + } +} + +impl FromStr for Address { + type Err = Error; + + fn from_str(addr: &str) -> Result { + if addr.starts_with("tcp://") { + let authority_parts = addr[6..].split('@').collect::>(); + + let (peer_id, authority) = match authority_parts.len() { + 1 => (None, authority_parts[0]), + 2 => (Some(authority_parts[0].parse()?), authority_parts[1]), + _ => bail!("invalid tcp:// address: {}", addr), + }; + + let host_and_port: Vec<&str> = authority.split(':').collect(); + + if host_and_port.len() != 2 { + bail!("invalid tcp:// address: {}", addr); + } + + let host = host_and_port[0].to_owned(); + + match host_and_port[1].parse() { + Ok(port) => Ok(Address::Tcp { + peer_id, + host, + port, + }), + Err(_) => bail!("invalid tcp:// address (bad port): {}", addr), + } + } else if addr.starts_with("unix://") { + Ok(Address::Unix { + path: PathBuf::from(&addr[7..]), + }) + } else { + bail!("invalid address: {}", addr) + } + } +} + +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } +} diff --git a/tendermint-rs/src/lib.rs b/tendermint-rs/src/lib.rs index dc74063..e4b3de3 100644 --- a/tendermint-rs/src/lib.rs +++ b/tendermint-rs/src/lib.rs @@ -25,6 +25,7 @@ extern crate prost_amino as prost; #[macro_use] extern crate prost_amino_derive as prost_derive; +pub mod address; pub mod algorithm; #[cfg(feature = "amino-types")] pub mod amino_types; @@ -32,6 +33,7 @@ pub mod block; pub mod chain; pub mod error; pub mod hash; +pub mod node; pub mod public_keys; #[cfg(feature = "secret-connection")] pub mod secret_connection; @@ -40,6 +42,7 @@ pub mod timestamp; #[cfg(feature = "secret-connection")] pub use crate::secret_connection::SecretConnection; pub use crate::{ + address::*, algorithm::*, block::{ParseHeight as ParseBlockHeight, ParseId as ParseBlockId}, chain::ParseId as ParseChainId, diff --git a/tendermint-rs/src/secret_connection/peer_id.rs b/tendermint-rs/src/node.rs similarity index 60% rename from tendermint-rs/src/secret_connection/peer_id.rs rename to tendermint-rs/src/node.rs index c6ee592..28f7b8a 100644 --- a/tendermint-rs/src/secret_connection/peer_id.rs +++ b/tendermint-rs/src/node.rs @@ -1,3 +1,5 @@ +//! Nodes in Tendermint blockchain networks + use crate::error::Error; use serde::de::{self, Deserialize, Deserializer}; use sha2::{Digest, Sha256}; @@ -9,39 +11,39 @@ use std::{ use subtle::{self, ConstantTimeEq}; use subtle_encoding::hex; -/// Size of a PeerId in bytes -pub const SIZE: usize = 20; +/// Size of a Node ID in bytes +pub const ID_LENGTH: usize = 20; -/// SecretConnection Peer IDs +/// Node IDs #[derive(Copy, Clone, Debug, Hash)] -pub struct PeerId([u8; SIZE]); +pub struct Id([u8; ID_LENGTH]); -impl PeerId { - /// Create a new PeerId from raw bytes - pub fn new(bytes: [u8; SIZE]) -> PeerId { - PeerId(bytes) +impl Id { + /// Create a new Node ID from raw bytes + pub fn new(bytes: [u8; ID_LENGTH]) -> Id { + Id(bytes) } - /// Borrow the Peer ID as a byte slice + /// Borrow the node ID as a byte slice pub fn as_bytes(&self) -> &[u8] { - self.0.as_ref() + &self.0[..] } } -impl AsRef<[u8]> for PeerId { +impl AsRef<[u8]> for Id { fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl ConstantTimeEq for PeerId { +impl ConstantTimeEq for Id { #[inline] - fn ct_eq(&self, other: &PeerId) -> subtle::Choice { + fn ct_eq(&self, other: &Id) -> subtle::Choice { self.as_bytes().ct_eq(other.as_bytes()) } } -impl Display for PeerId { +impl Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in &self.0 { write!(f, "{:02X}", byte)?; @@ -50,17 +52,17 @@ impl Display for PeerId { } } -impl From for PeerId { - fn from(pk: ed25519::PublicKey) -> PeerId { +impl From for Id { + fn from(pk: ed25519::PublicKey) -> Id { let digest = Sha256::digest(pk.as_bytes()); - let mut peer_id_bytes = [0u8; SIZE]; - peer_id_bytes.copy_from_slice(&digest[..SIZE]); - PeerId(peer_id_bytes) + let mut bytes = [0u8; ID_LENGTH]; + bytes.copy_from_slice(&digest[..ID_LENGTH]); + Id(bytes) } } -/// Decode PeerId from hex -impl FromStr for PeerId { +/// Decode Node ID from hex +impl FromStr for Id { type Err = Error; fn from_str(s: &str) -> Result { @@ -69,17 +71,17 @@ impl FromStr for PeerId { .or_else(|_| hex::decode(s)) .map_err(|_| Error::Parse)?; - if bytes.len() != SIZE { + if bytes.len() != ID_LENGTH { return Err(Error::Parse); } - let mut peer_id_bytes = [0u8; SIZE]; + let mut peer_id_bytes = [0u8; ID_LENGTH]; peer_id_bytes.copy_from_slice(&bytes); - Ok(PeerId(peer_id_bytes)) + Ok(Id(peer_id_bytes)) } } -impl<'de> Deserialize<'de> for PeerId { +impl<'de> Deserialize<'de> for Id { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -88,7 +90,7 @@ impl<'de> Deserialize<'de> for PeerId { Self::from_str(&s).map_err(|_| { de::Error::custom(format!( "expected {}-character hex string, got {:?}", - SIZE * 2, + ID_LENGTH * 2, s )) }) diff --git a/tendermint-rs/src/secret_connection.rs b/tendermint-rs/src/secret_connection.rs index ce7135b..59e0a07 100644 --- a/tendermint-rs/src/secret_connection.rs +++ b/tendermint-rs/src/secret_connection.rs @@ -2,10 +2,9 @@ mod kdf; mod nonce; -mod peer_id; mod public_key; -pub use self::{kdf::Kdf, nonce::Nonce, peer_id::PeerId, public_key::PublicKey}; +pub use self::{kdf::Kdf, nonce::Nonce, public_key::PublicKey}; use crate::{amino_types::AuthSigMessage, error::Error}; use byteorder::{ByteOrder, LE}; use bytes::BufMut; diff --git a/tendermint-rs/src/secret_connection/public_key.rs b/tendermint-rs/src/secret_connection/public_key.rs index 451c0bf..fce3240 100644 --- a/tendermint-rs/src/secret_connection/public_key.rs +++ b/tendermint-rs/src/secret_connection/public_key.rs @@ -1,7 +1,6 @@ //! Secret Connection peer public keys -use super::peer_id::PeerId; -use crate::error::Error; +use crate::{error::Error, node}; use signatory::ed25519; use std::fmt::{self, Display}; @@ -26,9 +25,9 @@ impl PublicKey { } /// Get the remote Peer ID - pub fn peer_id(self) -> PeerId { + pub fn peer_id(self) -> node::Id { match self { - PublicKey::Ed25519(pk) => PeerId::from(pk), + PublicKey::Ed25519(pk) => node::Id::from(pk), } } }