From 9c7ceb8048ab7c9d7e51c3e03ad8a3bca3a8a201 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 08:01:22 -0700 Subject: [PATCH 1/9] tendermint-rs: Fixes, cleanups, and updates - Fix --no-default-features build (and test in CI) - Eliminate most usages of (deprecated) `extern crate` - Use the `derive` cargo feature of `serde` feature instead of `serde_derive` - Rename `serializers` cargo feature of tendermint-rs to `serde` - Use 2018 edition module names (`modname.rs` instead of `modname/mod.rs`) - Update `x25519-dalek` to 0.5.2 - Update `zeroize` to 0.6 --- .circleci/config.yml | 1 + Cargo.lock | 33 +++++++++++++++---- Cargo.toml | 2 +- src/error.rs | 9 +++-- src/session.rs | 8 ++--- tendermint-rs/Cargo.toml | 18 ++++------ .../{amino_types/mod.rs => amino_types.rs} | 0 tendermint-rs/src/amino_types/block_id.rs | 2 +- tendermint-rs/src/amino_types/ed25519.rs | 25 +++++++------- tendermint-rs/src/amino_types/validate.rs | 2 ++ tendermint-rs/src/amino_types/vote.rs | 2 +- tendermint-rs/src/{block/mod.rs => block.rs} | 0 tendermint-rs/src/block/height.rs | 4 ++- tendermint-rs/src/block/id.rs | 6 ++-- tendermint-rs/src/{chain/mod.rs => chain.rs} | 0 tendermint-rs/src/chain/id.rs | 6 ++-- tendermint-rs/src/chain/state.rs | 5 ++- tendermint-rs/src/error.rs | 10 ++---- tendermint-rs/src/lib.rs | 12 +++---- tendermint-rs/src/public_keys.rs | 12 +------ .../mod.rs => secret_connection.rs} | 2 +- tendermint-rs/src/timestamp.rs | 4 ++- tendermint-rs/tests/integration.rs | 16 +++++---- 23 files changed, 96 insertions(+), 83 deletions(-) rename tendermint-rs/src/{amino_types/mod.rs => amino_types.rs} (100%) rename tendermint-rs/src/{block/mod.rs => block.rs} (100%) rename tendermint-rs/src/{chain/mod.rs => chain.rs} (100%) rename tendermint-rs/src/{secret_connection/mod.rs => secret_connection.rs} (99%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 12c62e2..a071148 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,6 +26,7 @@ jobs: cargo build --features=yubihsm cargo build --features=ledgertm cargo build --features=yubihsm,ledgertm,softsign + cd tendermint-rs && cargo build --no-default-features - run: name: build --release command: | diff --git a/Cargo.lock b/Cargo.lock index b9ccc9a..972f5f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -916,6 +916,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" @@ -1112,22 +1115,20 @@ dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "hkdf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost-amino 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost-amino-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle-encoding 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tai64 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1271,7 +1272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "x25519-dalek" -version = "0.4.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1325,6 +1326,24 @@ name = "zeroize" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "zeroize" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zeroize_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zeroize_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum abscissa 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "159a56ddefc6675d47b8063aada781ca621be8c7ac3adfc6520647024fd3461b" "checksum abscissa_derive 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ef4cd3ff1140f337a6d49256197c1455e2fb97ed76b2f220606809974f128814" @@ -1469,7 +1488,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum x25519-dalek 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "58e75de7a035f694df91838afa87faa8278bc484fa8d7dc34b5a24535cc2bb41" +"checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" "checksum yubihsm 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b61bfeae6f2ff19327d5ce0c9263afd7f80bda2c3fc5ffd9afd98146d0e72457" "checksum zeroize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ddffec9ddef28ba2d6359bcbf0dc6772e62b112bc103dfb1e6fab46cd47c39" "checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" +"checksum zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e68403b858b6af538b11614e62dfe9ab2facba9f13a0cafb974855cfb495ec95" +"checksum zeroize_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3f07490820219949839d0027b965ffdd659d75be9220c00798762e36c6cd281" diff --git a/Cargo.toml b/Cargo.toml index a0f31c4..97df756 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ signatory-dalek = "0.11" signatory-secp256k1 = "0.11" subtle = "2" subtle-encoding = { version = "0.3", features = ["bech32-preview"] } -tendermint = { version = "0.5.0-alpha1", path = "tendermint-rs" } +tendermint = { version = "0.5", path = "tendermint-rs" } tiny-bip39 = "0.6" wait-timeout = "0.2" yubihsm = { version = "0.22", features = ["setup", "usb"], optional = true } diff --git a/src/error.rs b/src/error.rs index b77d120..0bc1f71 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,8 +9,7 @@ use std::{ fmt::{self, Display}, io, }; -use tendermint; -use tendermint::amino_types::validate::ValidationError as TmValidationError; +use tendermint::amino_types::validate::ValidationError; /// Error type #[derive(Debug)] @@ -142,7 +141,7 @@ impl From for KmsError { } impl From for KmsError { - fn from(other: tendermint::Error) -> Self { + fn from(other: tendermint::error::Error) -> Self { let kind = match other { tendermint::Error::Crypto => KmsErrorKind::CryptoError, tendermint::Error::InvalidKey => KmsErrorKind::InvalidKey, @@ -158,8 +157,8 @@ impl From for KmsError { } } -impl From for KmsError { - fn from(other: TmValidationError) -> Self { +impl From for KmsError { + fn from(other: ValidationError) -> Self { err!(KmsErrorKind::InvalidMessageError, other).into() } } diff --git a/src/session.rs b/src/session.rs index 31ea6ab..ae07cb6 100644 --- a/src/session.rs +++ b/src/session.rs @@ -24,7 +24,7 @@ use std::{ }; use subtle::ConstantTimeEq; use tendermint::{ - amino_types::{PingRequest, PingResponse, PubKeyRequest}, + amino_types::{PingRequest, PingResponse, PubKeyRequest, PubKeyResponse}, secret_connection::{self, SecretConnection}, }; @@ -170,8 +170,8 @@ where /// Get the public key for (the only) public key in the keyring fn get_public_key(&mut self, _request: &PubKeyRequest) -> Result { - Ok(Response::PublicKey( - KeyRing::default_pubkey()?.to_response(), - )) + Ok(Response::PublicKey(PubKeyResponse::from( + *KeyRing::default_pubkey()?, + ))) } } diff --git a/tendermint-rs/Cargo.toml b/tendermint-rs/Cargo.toml index 078e377..2a2069c 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -32,37 +32,33 @@ bytes = "0.4" chrono = { version = "0.4", features = ["serde"] } digest = "0.8" failure = "0.1" -failure_derive = "0.1" hkdf = { version = "0.7", optional = true } prost-amino = { version = "0.4.0", optional = true } prost-amino-derive = { version = "0.4.0", optional = true } rand_os = { version = "0.1", optional = true } ring = { version = "0.14", optional = true } -serde = { version = "1", optional = true } -serde_derive = { version = "1", optional = true } -signatory = { version = "0.11.2", optional = true, features = ["ed25519", "ecdsa"] } +serde = { version = "1", optional = true, features = ["derive"] } +signatory = { version = "0.11.2", features = ["ed25519", "ecdsa"] } signatory-dalek = { version = "0.11", optional = true } sha2 = { version = "0.8", optional = true, default-features = false } subtle = { version = "2", optional = true } subtle-encoding = { version = "0.3", features = ["bech32-preview"] } tai64 = { version = "1", optional = true, features = ["chrono"] } -x25519-dalek = { version = "0.4.4", optional = true, default-features = false, features = ["u64_backend"] } -zeroize = { version = "0.5.2", optional = true } +x25519-dalek = { version = "0.5", optional = true, default-features = false, features = ["u64_backend"] } +zeroize = { version = "0.6", optional = true } [features] -default = ["secret-connection", "serializers", "tai64"] +default = ["secret-connection", "serde", "tai64"] +amino-types = ["prost-amino", "prost-amino-derive"] secret-connection = [ + "amino-types", "byteorder", "hkdf", - "prost-amino", - "prost-amino-derive", "rand_os", "ring", - "signatory", "signatory-dalek", "sha2", "subtle", "x25519-dalek", "zeroize" ] -serializers = ["serde", "serde_derive"] diff --git a/tendermint-rs/src/amino_types/mod.rs b/tendermint-rs/src/amino_types.rs similarity index 100% rename from tendermint-rs/src/amino_types/mod.rs rename to tendermint-rs/src/amino_types.rs diff --git a/tendermint-rs/src/amino_types/block_id.rs b/tendermint-rs/src/amino_types/block_id.rs index fb3ade2..30a5cb3 100644 --- a/tendermint-rs/src/amino_types/block_id.rs +++ b/tendermint-rs/src/amino_types/block_id.rs @@ -29,7 +29,7 @@ impl ConsensusMessage for BlockId { } self.parts_header .as_ref() - .map_or(Ok(()), |psh| psh.validate_basic()) + .map_or(Ok(()), ConsensusMessage::validate_basic) } } diff --git a/tendermint-rs/src/amino_types/ed25519.rs b/tendermint-rs/src/amino_types/ed25519.rs index da567fb..c170217 100644 --- a/tendermint-rs/src/amino_types/ed25519.rs +++ b/tendermint-rs/src/amino_types/ed25519.rs @@ -1,4 +1,5 @@ -use signatory::ed25519::{PublicKey, PUBLIC_KEY_SIZE}; +use crate::public_keys::PublicKey; +use signatory::ed25519::PUBLIC_KEY_SIZE; // Note:On the golang side this is generic in the sense that it could everything that implements // github.com/tendermint/tendermint/crypto.PubKey @@ -19,21 +20,23 @@ pub struct PubKeyResponse { #[amino_name = "tendermint/remotesigner/PubKeyRequest"] pub struct PubKeyRequest {} -impl Into for PubKeyResponse { +impl From for PublicKey { // This does not check if the underlying pub_key_ed25519 has the right size. // The caller needs to make sure that this is actually the case. - fn into(self) -> PublicKey { + fn from(response: PubKeyResponse) -> PublicKey { let mut public_key = [0u8; PUBLIC_KEY_SIZE]; - public_key.copy_from_slice(self.pub_key_ed25519.as_ref()); - PublicKey(public_key) + public_key.copy_from_slice(response.pub_key_ed25519.as_ref()); + PublicKey::Ed25519(signatory::ed25519::PublicKey::new(public_key)) } } -impl Into for PublicKey { - fn into(self) -> PubKeyResponse { - let pk = self.0.to_vec(); - PubKeyResponse { - pub_key_ed25519: pk, +impl From for PubKeyResponse { + fn from(public_key: PublicKey) -> PubKeyResponse { + match public_key { + PublicKey::Ed25519(ref pk) => PubKeyResponse { + pub_key_ed25519: pk.as_bytes().to_vec(), + }, + PublicKey::Secp256k1(_) => panic!("secp256k1 PubKeyResponse unimplemented"), } } } @@ -137,7 +140,7 @@ mod tests { 0xe7, 0xc1, 0xd4, 0x69, 0xc3, 0x44, 0x26, 0xec, 0xef, 0xc0, 0x72, 0xa, 0x52, 0x4d, 0x37, 0x32, 0xef, 0xed, ]; - let want = PublicKey(raw_pk); + let want = PublicKey::Ed25519(signatory::ed25519::PublicKey::new(raw_pk)); let pk = PubKeyResponse { pub_key_ed25519: vec![ 0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, 0xe0, 0x8b, 0x7b, 0xb5, 0x61, 0xbc, diff --git a/tendermint-rs/src/amino_types/validate.rs b/tendermint-rs/src/amino_types/validate.rs index 760231b..26984b1 100644 --- a/tendermint-rs/src/amino_types/validate.rs +++ b/tendermint-rs/src/amino_types/validate.rs @@ -1,3 +1,5 @@ +use failure::*; + pub trait ConsensusMessage { fn validate_basic(&self) -> Result<(), ValidationError>; } diff --git a/tendermint-rs/src/amino_types/vote.rs b/tendermint-rs/src/amino_types/vote.rs index 263ab09..9df60f7 100644 --- a/tendermint-rs/src/amino_types/vote.rs +++ b/tendermint-rs/src/amino_types/vote.rs @@ -198,7 +198,7 @@ impl ConsensusMessage for Vote { self.block_id .as_ref() - .map_or(Ok(()), |bid| bid.validate_basic()) + .map_or(Ok(()), ConsensusMessage::validate_basic) // signature will be missing as the KMS provides it } diff --git a/tendermint-rs/src/block/mod.rs b/tendermint-rs/src/block.rs similarity index 100% rename from tendermint-rs/src/block/mod.rs rename to tendermint-rs/src/block.rs diff --git a/tendermint-rs/src/block/height.rs b/tendermint-rs/src/block/height.rs index 5f308e8..a3e43c2 100644 --- a/tendermint-rs/src/block/height.rs +++ b/tendermint-rs/src/block/height.rs @@ -1,9 +1,11 @@ use crate::error::Error; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::fmt::{self, Debug, Display}; /// Block height for a particular chain (i.e. number of blocks created since /// the chain began) -#[cfg_attr(feature = "serializers", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] pub struct Height(pub u64); diff --git a/tendermint-rs/src/block/id.rs b/tendermint-rs/src/block/id.rs index 6e4331a..1c8b501 100644 --- a/tendermint-rs/src/block/id.rs +++ b/tendermint-rs/src/block/id.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "serializers")] +#[cfg(feature = "serde")] use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{self, Display}, @@ -37,14 +37,14 @@ impl FromStr for Id { } } -#[cfg(feature = "serializers")] +#[cfg(feature = "serde")] impl Serialize for Id { fn serialize(&self, serializer: S) -> Result { self.to_string().serialize(serializer) } } -#[cfg(feature = "serializers")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Id { fn deserialize>(deserializer: De) -> Result { Self::from_str(&String::deserialize(deserializer)?) diff --git a/tendermint-rs/src/chain/mod.rs b/tendermint-rs/src/chain.rs similarity index 100% rename from tendermint-rs/src/chain/mod.rs rename to tendermint-rs/src/chain.rs diff --git a/tendermint-rs/src/chain/id.rs b/tendermint-rs/src/chain/id.rs index 54431d5..30684e0 100644 --- a/tendermint-rs/src/chain/id.rs +++ b/tendermint-rs/src/chain/id.rs @@ -1,7 +1,7 @@ //! Tendermint blockchain identifiers use crate::error::Error; -#[cfg(feature = "serializers")] +#[cfg(feature = "serde")] use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; use std::{ cmp::Ordering, @@ -104,14 +104,14 @@ impl PartialEq for Id { impl Eq for Id {} -#[cfg(feature = "serializers")] +#[cfg(feature = "serde")] impl Serialize for Id { fn serialize(&self, serializer: S) -> Result { self.as_str().serialize(serializer) } } -#[cfg(feature = "serializers")] +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Id { fn deserialize>(deserializer: D) -> Result { Self::from_str(&String::deserialize(deserializer)?) diff --git a/tendermint-rs/src/chain/state.rs b/tendermint-rs/src/chain/state.rs index 814c885..9cd1e04 100644 --- a/tendermint-rs/src/chain/state.rs +++ b/tendermint-rs/src/chain/state.rs @@ -1,9 +1,12 @@ //! State of a particular Tendermint network (a.k.a. chain) use crate::block; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// Tendermint consensus state -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct ConsensusState { /// Current block height pub height: block::Height, diff --git a/tendermint-rs/src/error.rs b/tendermint-rs/src/error.rs index 164320e..2e11db2 100644 --- a/tendermint-rs/src/error.rs +++ b/tendermint-rs/src/error.rs @@ -1,11 +1,9 @@ //! Error types +use failure::*; +use std::io; #[cfg(feature = "secret-connection")] -use { - chrono, prost, signatory, - std::{self, io}, - subtle_encoding, -}; +use {chrono, prost, subtle_encoding}; /// Kinds of errors #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] @@ -72,14 +70,12 @@ impl From for Error { } } -#[cfg(feature = "secret-connection")] impl From for Error { fn from(_: subtle_encoding::Error) -> Error { Error::Parse } } -#[cfg(feature = "secret-connection")] impl From for Error { fn from(other: signatory::Error) -> Self { match other.kind() { diff --git a/tendermint-rs/src/lib.rs b/tendermint-rs/src/lib.rs index cb587cf..dc74063 100644 --- a/tendermint-rs/src/lib.rs +++ b/tendermint-rs/src/lib.rs @@ -19,18 +19,14 @@ html_root_url = "https://docs.rs/tendermint/0.5.0" )] -#[macro_use] -extern crate failure_derive; -#[cfg(feature = "secret-connection")] +#[cfg(feature = "amino-types")] extern crate prost_amino as prost; -#[cfg(feature = "secret-connection")] +#[cfg(feature = "amino-types")] #[macro_use] extern crate prost_amino_derive as prost_derive; -#[cfg(feature = "serializers")] -#[macro_use] -extern crate serde_derive; pub mod algorithm; +#[cfg(feature = "amino-types")] pub mod amino_types; pub mod block; pub mod chain; @@ -47,7 +43,7 @@ pub use crate::{ algorithm::*, block::{ParseHeight as ParseBlockHeight, ParseId as ParseBlockId}, chain::ParseId as ParseChainId, - error::*, + error::Error, hash::*, public_keys::*, timestamp::*, diff --git a/tendermint-rs/src/public_keys.rs b/tendermint-rs/src/public_keys.rs index 7d331a6..e0a9517 100644 --- a/tendermint-rs/src/public_keys.rs +++ b/tendermint-rs/src/public_keys.rs @@ -1,6 +1,6 @@ //! Public keys used in Tendermint networks -use crate::{amino_types::PubKeyResponse, error::Error}; +use crate::error::Error; use signatory::{ecdsa::curve::secp256k1, ed25519}; use std::ops::Deref; use subtle_encoding::{bech32, hex}; @@ -62,16 +62,6 @@ impl PublicKey { pub fn to_hex(self) -> String { String::from_utf8(hex::encode_upper(self.to_amino_bytes())).unwrap() } - - /// Create a response which represents this public key - pub fn to_response(self) -> PubKeyResponse { - match self { - PublicKey::Ed25519(ref pk) => PubKeyResponse { - pub_key_ed25519: pk.as_bytes().to_vec(), - }, - PublicKey::Secp256k1(_) => panic!("secp256k1 PubKeyResponse unimplemented"), - } - } } impl From for PublicKey { diff --git a/tendermint-rs/src/secret_connection/mod.rs b/tendermint-rs/src/secret_connection.rs similarity index 99% rename from tendermint-rs/src/secret_connection/mod.rs rename to tendermint-rs/src/secret_connection.rs index 8180aff..ce7135b 100644 --- a/tendermint-rs/src/secret_connection/mod.rs +++ b/tendermint-rs/src/secret_connection.rs @@ -19,7 +19,7 @@ use std::{ io::{self, Read, Write}, marker::{Send, Sync}, }; -use x25519_dalek::{EphemeralPublic, EphemeralSecret}; +use x25519_dalek::{EphemeralSecret, PublicKey as EphemeralPublic}; /// Size of the MAC tag pub const TAG_SIZE: usize = 16; diff --git a/tendermint-rs/src/timestamp.rs b/tendermint-rs/src/timestamp.rs index 68f470c..9846a6f 100644 --- a/tendermint-rs/src/timestamp.rs +++ b/tendermint-rs/src/timestamp.rs @@ -2,12 +2,14 @@ use crate::error::Error; use chrono::{DateTime, Utc}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; #[cfg(feature = "tai64")] use tai64::TAI64N; /// Chain timestamps (e.g. consensus time) -#[cfg_attr(feature = "serializers", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Timestamp(DateTime); diff --git a/tendermint-rs/tests/integration.rs b/tendermint-rs/tests/integration.rs index 4f71b4f..8dfbaa3 100644 --- a/tendermint-rs/tests/integration.rs +++ b/tendermint-rs/tests/integration.rs @@ -1,15 +1,17 @@ //! `SecretConnection` integration tests -extern crate subtle_encoding; -extern crate tendermint; - +#[cfg(features = "secret-connection")] use self::subtle_encoding::hex; -use std::fs::File; -use std::io::BufRead; -use std::io::BufReader; -use std::str::FromStr; +#[cfg(features = "secret-connection")] +use std::{ + fs::File, + io::{BufRead, BufReader}, + str::FromStr, +}; +#[cfg(features = "secret-connection")] use tendermint::secret_connection::Kdf; +#[cfg(features = "secret-connection")] #[test] fn test_derive_secrets_and_challenge_golden_test_vectors() { let f = File::open("tests/support/TestDeriveSecretsAndChallenge.golden").unwrap(); From f9953ea76219f15f5f1645563c6cb256a8bb620b Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 08:31:18 -0700 Subject: [PATCH 2/9] 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. --- Cargo.toml | 2 +- src/client.rs | 10 +- src/config/{validator/mod.rs => validator.rs} | 8 +- src/config/validator/addr.rs | 90 ----------------- src/session.rs | 3 +- tendermint-rs/Cargo.toml | 11 +-- tendermint-rs/src/address.rs | 96 +++++++++++++++++++ tendermint-rs/src/lib.rs | 3 + .../{secret_connection/peer_id.rs => node.rs} | 56 ++++++----- tendermint-rs/src/secret_connection.rs | 3 +- .../src/secret_connection/public_key.rs | 7 +- 11 files changed, 147 insertions(+), 142 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} (59%) diff --git a/Cargo.toml b/Cargo.toml index 97df756..46c0508 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ signatory-dalek = "0.11" signatory-secp256k1 = "0.11" subtle = "2" subtle-encoding = { version = "0.3", features = ["bech32-preview"] } -tendermint = { version = "0.5", path = "tendermint-rs" } +tendermint = { version = "0.5", path = "tendermint-rs", features = ["amino-types", "secret-connection"] } tiny-bip39 = "0.6" wait-timeout = "0.2" yubihsm = { version = "0.22", features = ["setup", "usb"], optional = true } 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/Cargo.toml b/tendermint-rs/Cargo.toml index 2a2069c..4bd1267 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -40,15 +40,15 @@ ring = { version = "0.14", optional = true } serde = { version = "1", optional = true, features = ["derive"] } signatory = { version = "0.11.2", features = ["ed25519", "ecdsa"] } signatory-dalek = { version = "0.11", optional = true } -sha2 = { version = "0.8", optional = true, default-features = false } -subtle = { version = "2", optional = true } +sha2 = { version = "0.8", default-features = false } +subtle = "2" subtle-encoding = { version = "0.3", features = ["bech32-preview"] } tai64 = { version = "1", optional = true, features = ["chrono"] } x25519-dalek = { version = "0.5", optional = true, default-features = false, features = ["u64_backend"] } zeroize = { version = "0.6", optional = true } [features] -default = ["secret-connection", "serde", "tai64"] +default = ["serde", "tai64"] amino-types = ["prost-amino", "prost-amino-derive"] secret-connection = [ "amino-types", @@ -56,9 +56,6 @@ secret-connection = [ "hkdf", "rand_os", "ring", - "signatory-dalek", - "sha2", - "subtle", - "x25519-dalek", + "signatory-dalek", "x25519-dalek", "zeroize" ] diff --git a/tendermint-rs/src/address.rs b/tendermint-rs/src/address.rs new file mode 100644 index 0000000..020b23d --- /dev/null +++ b/tendermint-rs/src/address.rs @@ -0,0 +1,96 @@ +//! Remote addresses (`tcp://` or `unix://`) + +use crate::node; +use failure::{bail, Error}; +#[cfg(feature = "serde")] +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, + }, +} + +#[cfg(feature = "serde")] +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) + } + } +} + +#[cfg(feature = "serde")] +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 59% rename from tendermint-rs/src/secret_connection/peer_id.rs rename to tendermint-rs/src/node.rs index c6ee592..70b666e 100644 --- a/tendermint-rs/src/secret_connection/peer_id.rs +++ b/tendermint-rs/src/node.rs @@ -1,4 +1,7 @@ +//! Nodes in Tendermint blockchain networks + use crate::error::Error; +#[cfg(feature = "serde")] use serde::de::{self, Deserialize, Deserializer}; use sha2::{Digest, Sha256}; use signatory::ed25519; @@ -9,39 +12,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 +53,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 +72,18 @@ 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 { +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Id { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -88,7 +92,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), } } } From cde8abae86ec6177be1973d40226a3db586ccc23 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 08:54:19 -0700 Subject: [PATCH 3/9] tendermint-rs: Custom Serialize/Deserialize for block::Height ...and a `FromStr` impl. This enables parsing block heights from JSON. Right now these impls are specialized to strings (as they internally use e.g. `String::deserialize` and `String::serialize`) but in the future with the use of a custom visitor they could be extended to support both strings and numbers. Strings are immediately useful for JSONRPC. --- tendermint-rs/src/block/height.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tendermint-rs/src/block/height.rs b/tendermint-rs/src/block/height.rs index a3e43c2..40c4e8a 100644 --- a/tendermint-rs/src/block/height.rs +++ b/tendermint-rs/src/block/height.rs @@ -1,11 +1,13 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use std::fmt::{self, Debug, Display}; +use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt::{self, Debug, Display}, + str::FromStr, +}; /// Block height for a particular chain (i.e. number of blocks created since /// the chain began) -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] pub struct Height(pub u64); @@ -66,6 +68,29 @@ impl From for i64 { } } +impl FromStr for Height { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(Self::from(s.parse::().map_err(|_| Error::Parse)?)) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Height { + fn deserialize>(deserializer: D) -> Result { + Ok(Self::from_str(&String::deserialize(deserializer)?) + .map_err(|e| D::Error::custom(format!("{}", e)))?) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Height { + fn serialize(&self, serializer: S) -> Result { + self.value().to_string().serialize(serializer) + } +} + /// Parse `block::Height` from a type pub trait ParseHeight { /// Parse `block::Height`, or return an `Error` if parsing failed From 934f7d869c1a577add62fa32301a96e7e5b9060e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 09:01:39 -0700 Subject: [PATCH 4/9] tendermint-rs: Initial Moniker type Adds a type for representing monikers --- tendermint-rs/src/lib.rs | 2 ++ tendermint-rs/src/moniker.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tendermint-rs/src/moniker.rs diff --git a/tendermint-rs/src/lib.rs b/tendermint-rs/src/lib.rs index e4b3de3..c411197 100644 --- a/tendermint-rs/src/lib.rs +++ b/tendermint-rs/src/lib.rs @@ -33,6 +33,7 @@ pub mod block; pub mod chain; pub mod error; pub mod hash; +pub mod moniker; pub mod node; pub mod public_keys; #[cfg(feature = "secret-connection")] @@ -48,6 +49,7 @@ pub use crate::{ chain::ParseId as ParseChainId, error::Error, hash::*, + moniker::Moniker, public_keys::*, timestamp::*, }; diff --git a/tendermint-rs/src/moniker.rs b/tendermint-rs/src/moniker.rs new file mode 100644 index 0000000..a6c0170 --- /dev/null +++ b/tendermint-rs/src/moniker.rs @@ -0,0 +1,34 @@ +//! Monikers: names associated with validators + +use crate::error::Error; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; + +/// Validator display names +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct Moniker(String); + +impl FromStr for Moniker { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(Moniker(s.to_owned())) + } +} + +impl AsRef for Moniker { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl Display for Moniker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} From 58e41e9f33898339dadbbdc9acd67eb8c244bbbc Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 09:27:46 -0700 Subject: [PATCH 5/9] tendermint-rs: Impl FromStr, Deserialize, and Serialize for Hash Support for serializing and deserializing hash values from hex strings --- tendermint-rs/src/hash.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tendermint-rs/src/hash.rs b/tendermint-rs/src/hash.rs index 8bebfe2..37fc178 100644 --- a/tendermint-rs/src/hash.rs +++ b/tendermint-rs/src/hash.rs @@ -1,7 +1,12 @@ //! Hash functions and their outputs use crate::{algorithm::HashAlgorithm, error::Error}; -use std::fmt::{self, Display}; +#[cfg(feature = "serde")] +use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; use subtle_encoding::{Encoding, Hex}; /// Output size for the SHA-256 hash function @@ -65,3 +70,26 @@ impl Display for Hash { write!(f, "{}", hex) } } + +impl FromStr for Hash { + type Err = Error; + + fn from_str(s: &str) -> Result { + Self::from_hex_upper(HashAlgorithm::Sha256, s) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Hash { + fn deserialize>(deserializer: D) -> Result { + Ok(Self::from_str(&String::deserialize(deserializer)?) + .map_err(|e| D::Error::custom(format!("{}", e)))?) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Hash { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } +} From 4e2af2cc601172dfaa1ecce8ac47eb6640f8271e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 10:43:36 -0700 Subject: [PATCH 6/9] tendermint-rs: Impl Serialize and Deserialize for PublicKey Adds serde serializers/deserializers for PublicKey types which support the JSON serialization used by the JSONRPC interface. --- Cargo.lock | 1 + tendermint-rs/Cargo.toml | 3 + tendermint-rs/src/public_keys.rs | 96 ++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 972f5f5..a29dbb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1121,6 +1121,7 @@ dependencies = [ "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tendermint-rs/Cargo.toml b/tendermint-rs/Cargo.toml index 4bd1267..b6d2121 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -47,6 +47,9 @@ tai64 = { version = "1", optional = true, features = ["chrono"] } x25519-dalek = { version = "0.5", optional = true, default-features = false, features = ["u64_backend"] } zeroize = { version = "0.6", optional = true } +[dev-dependencies] +serde_json = "1" + [features] default = ["serde", "tai64"] amino-types = ["prost-amino", "prost-amino-derive"] diff --git a/tendermint-rs/src/public_keys.rs b/tendermint-rs/src/public_keys.rs index e0a9517..35bcbee 100644 --- a/tendermint-rs/src/public_keys.rs +++ b/tendermint-rs/src/public_keys.rs @@ -1,17 +1,39 @@ //! Public keys used in Tendermint networks use crate::error::Error; +#[cfg(feature = "serde")] +use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; use signatory::{ecdsa::curve::secp256k1, ed25519}; use std::ops::Deref; +#[cfg(feature = "serde")] +use subtle_encoding::base64; use subtle_encoding::{bech32, hex}; /// Public keys allowed in Tendermint protocols +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pub enum PublicKey { /// Ed25519 keys + #[cfg_attr( + feature = "serde", + serde( + rename = "tendermint/PubKeyEd25519", + serialize_with = "serialize_ed25519_base64", + deserialize_with = "deserialize_ed25519_base64" + ) + )] Ed25519(ed25519::PublicKey), /// Secp256k1 keys + #[cfg_attr( + feature = "serde", + serde( + rename = "tendermint/PubKeySecp256k1", + serialize_with = "serialize_secp256k1_base64", + deserialize_with = "deserialize_secp256k1_base64" + ) + )] Secp256k1(secp256k1::PublicKey), } @@ -64,6 +86,62 @@ impl PublicKey { } } +/// Serialize the bytes of an Ed25519 public key as Base64. Used for serializing JSON +#[cfg(feature = "serde")] +fn serialize_ed25519_base64( + pk: &signatory::ed25519::PublicKey, + serializer: S, +) -> Result +where + S: Serializer, +{ + String::from_utf8(base64::encode(pk.as_bytes())) + .unwrap() + .serialize(serializer) +} + +/// Serialize the bytes of a secp256k1 ECDSA public key as Base64. Used for serializing JSON +#[cfg(feature = "serde")] +fn serialize_secp256k1_base64( + pk: &signatory::ecdsa::curve::secp256k1::PublicKey, + serializer: S, +) -> Result +where + S: Serializer, +{ + String::from_utf8(base64::encode(pk.as_bytes())) + .unwrap() + .serialize(serializer) +} + +#[cfg(feature = "serde")] +fn deserialize_ed25519_base64<'de, D>( + deserializer: D, +) -> Result +where + D: Deserializer<'de>, +{ + let bytes = base64::decode(String::deserialize(deserializer)?.as_bytes()) + .map_err(|e| D::Error::custom(format!("{}", e)))?; + + signatory::ed25519::PublicKey::from_bytes(&bytes) + .map_err(|e| D::Error::custom(format!("{}", e))) +} + +#[cfg(feature = "serde")] +fn deserialize_secp256k1_base64<'de, D>( + deserializer: D, +) -> Result +where + D: Deserializer<'de>, +{ + let bytes = base64::decode(String::deserialize(deserializer)?.as_bytes()) + .map_err(|e| D::Error::custom(format!("{}", e)))?; + + signatory::ecdsa::curve::secp256k1::PublicKey::from_bytes(&bytes) + .map_err(|e| D::Error::custom(format!("{}", e))) +} + impl From for PublicKey { fn from(pk: ed25519::PublicKey) -> PublicKey { PublicKey::Ed25519(pk) @@ -133,4 +211,22 @@ mod tests { "cosmospub1addwnpepq2skx090esq7h7md0r3e76r6ruyet330e904r6k3pgpwuzl92x6actrt4uq" ); } + + #[cfg(feature = "serde")] + #[test] + fn json_parsing() { + let json_string = "{\"type\":\"tendermint/PubKeyEd25519\",\"value\":\"RblzMO4is5L1hZz6wo4kPbptzOyue6LTk4+lPhD1FRk=\"}"; + let pubkey: PublicKey = serde_json::from_str(json_string).unwrap(); + + assert_eq!( + pubkey.ed25519().unwrap().as_ref(), + [ + 69, 185, 115, 48, 238, 34, 179, 146, 245, 133, 156, 250, 194, 142, 36, 61, 186, + 109, 204, 236, 174, 123, 162, 211, 147, 143, 165, 62, 16, 245, 21, 25 + ] + ); + + let reserialized_json = serde_json::to_string(&pubkey).unwrap(); + assert_eq!(reserialized_json.as_str(), json_string); + } } From 611692b6455408c5f01ad50eedde9a13ec47dabc Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 11:05:12 -0700 Subject: [PATCH 7/9] tendermint-rs: account::Id type Adds a type for representing IDs of Tendermint accounts, which are computed as a truncated hash of a secp256k1 public key. --- tendermint-rs/src/account.rs | 100 +++++++++++++++++++++++++++++++++++ tendermint-rs/src/lib.rs | 1 + tendermint-rs/src/node.rs | 6 +-- 3 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 tendermint-rs/src/account.rs diff --git a/tendermint-rs/src/account.rs b/tendermint-rs/src/account.rs new file mode 100644 index 0000000..0f0a2b8 --- /dev/null +++ b/tendermint-rs/src/account.rs @@ -0,0 +1,100 @@ +//! Tendermint accounts + +use crate::error::Error; +#[cfg(feature = "serde")] +use serde::de::{self, Deserialize, Deserializer}; +use sha2::{Digest, Sha256}; +use signatory::ecdsa::curve::secp256k1; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; +use subtle::{self, ConstantTimeEq}; +use subtle_encoding::hex; + +/// Size of an account ID in bytes +pub const ID_LENGTH: usize = 20; + +/// Account IDs +#[derive(Copy, Clone, Debug, Hash)] +pub struct Id([u8; ID_LENGTH]); + +impl Id { + /// Create a new account ID from raw bytes + pub fn new(bytes: [u8; ID_LENGTH]) -> Id { + Id(bytes) + } + + /// Borrow the account ID as a byte slice + pub fn as_bytes(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsRef<[u8]> for Id { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl ConstantTimeEq for Id { + #[inline] + fn ct_eq(&self, other: &Id) -> subtle::Choice { + self.as_bytes().ct_eq(other.as_bytes()) + } +} + +impl Display for Id { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in &self.0 { + write!(f, "{:02X}", byte)?; + } + Ok(()) + } +} + +impl From for Id { + fn from(pk: secp256k1::PublicKey) -> Id { + let digest = Sha256::digest(pk.as_bytes()); + let mut bytes = [0u8; ID_LENGTH]; + bytes.copy_from_slice(&digest[..ID_LENGTH]); + Id(bytes) + } +} + +/// Decode account ID from hex +impl FromStr for Id { + type Err = Error; + + fn from_str(s: &str) -> Result { + // Accept either upper or lower case hex + let bytes = hex::decode_upper(s) + .or_else(|_| hex::decode(s)) + .map_err(|_| Error::Parse)?; + + if bytes.len() != ID_LENGTH { + return Err(Error::Parse); + } + + let mut result_bytes = [0u8; ID_LENGTH]; + result_bytes.copy_from_slice(&bytes); + Ok(Id(result_bytes)) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Id { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_str(&s).map_err(|_| { + de::Error::custom(format!( + "expected {}-character hex string, got {:?}", + ID_LENGTH * 2, + s + )) + }) + } +} diff --git a/tendermint-rs/src/lib.rs b/tendermint-rs/src/lib.rs index c411197..25a7156 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 account; pub mod address; pub mod algorithm; #[cfg(feature = "amino-types")] diff --git a/tendermint-rs/src/node.rs b/tendermint-rs/src/node.rs index 70b666e..1e15338 100644 --- a/tendermint-rs/src/node.rs +++ b/tendermint-rs/src/node.rs @@ -76,9 +76,9 @@ impl FromStr for Id { return Err(Error::Parse); } - let mut peer_id_bytes = [0u8; ID_LENGTH]; - peer_id_bytes.copy_from_slice(&bytes); - Ok(Id(peer_id_bytes)) + let mut result_bytes = [0u8; ID_LENGTH]; + result_bytes.copy_from_slice(&bytes); + Ok(Id(result_bytes)) } } From 248c39d266143a019c252f90a0ab358a3e4f1a42 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 11:35:16 -0700 Subject: [PATCH 8/9] tendermint-rs/Cargo.toml: Document all features on docs.rs --- tendermint-rs/Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tendermint-rs/Cargo.toml b/tendermint-rs/Cargo.toml index b6d2121..b4ddfb8 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -59,6 +59,10 @@ secret-connection = [ "hkdf", "rand_os", "ring", - "signatory-dalek", "x25519-dalek", + "signatory-dalek", + "x25519-dalek", "zeroize" ] + +[package.metadata.docs.rs] +all-features = true From 39cfe2d20923efae2b01d59664e1b80df4b76f77 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 15 Apr 2019 11:48:25 -0700 Subject: [PATCH 9/9] tendermint-rs: impl Serialize for account::Id and node::Id --- tendermint-rs/src/account.rs | 9 ++++++++- tendermint-rs/src/node.rs | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tendermint-rs/src/account.rs b/tendermint-rs/src/account.rs index 0f0a2b8..b3304c9 100644 --- a/tendermint-rs/src/account.rs +++ b/tendermint-rs/src/account.rs @@ -2,7 +2,7 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::de::{self, Deserialize, Deserializer}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Digest, Sha256}; use signatory::ecdsa::curve::secp256k1; use std::{ @@ -98,3 +98,10 @@ impl<'de> Deserialize<'de> for Id { }) } } + +#[cfg(feature = "serde")] +impl Serialize for Id { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } +} diff --git a/tendermint-rs/src/node.rs b/tendermint-rs/src/node.rs index 1e15338..4a15732 100644 --- a/tendermint-rs/src/node.rs +++ b/tendermint-rs/src/node.rs @@ -2,7 +2,7 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::de::{self, Deserialize, Deserializer}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Digest, Sha256}; use signatory::ed25519; use std::{ @@ -98,3 +98,10 @@ impl<'de> Deserialize<'de> for Id { }) } } + +#[cfg(feature = "serde")] +impl Serialize for Id { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } +}