diff --git a/.circleci/config.yml b/.circleci/config.yml index a071148..2ae1938 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,6 +40,7 @@ jobs: rustc --version cargo --version cargo test --all --all-features -- --test-threads 1 + cd tendermint-rs && cargo test --release --all-features - run: name: audit command: | diff --git a/src/chain/state/mod.rs b/src/chain/state/mod.rs index aee1278..b19718f 100644 --- a/src/chain/state/mod.rs +++ b/src/chain/state/mod.rs @@ -15,11 +15,11 @@ use std::{ io::{self, prelude::*}, path::{Path, PathBuf}, }; -use tendermint::chain::ConsensusState; +use tendermint::consensus; /// State tracking for double signing prevention pub struct State { - consensus_state: ConsensusState, + consensus_state: consensus::State, state_file_path: PathBuf, } @@ -30,7 +30,7 @@ impl State { P: AsRef, { let mut lst = State { - consensus_state: ConsensusState::default(), + consensus_state: consensus::State::default(), state_file_path: path.as_ref().to_owned(), }; @@ -58,8 +58,11 @@ impl State { } /// Check and update the chain's height, round, and step - pub fn update_consensus_state(&mut self, new_state: ConsensusState) -> Result<(), StateError> { - // TODO(tarcieri): impl `PartialOrd` on `ConsensusState` to simplify this logic? + pub fn update_consensus_state( + &mut self, + new_state: consensus::State, + ) -> Result<(), StateError> { + // TODO(tarcieri): rewrite this using `Ord` impl on `consensus::State` if new_state.height < self.consensus_state.height { fail!( StateErrorKind::HeightRegression, @@ -98,7 +101,7 @@ impl State { new_state.height, new_state.round, new_state.step, - self.consensus_state.block_id.unwrap(), + self.consensus_state.block_id.as_ref().unwrap(), new_state.block_id.unwrap() ) } @@ -127,7 +130,7 @@ impl State { let delta = hook_height - last_height; if delta < hook::BLOCK_HEIGHT_SANITY_LIMIT { - let mut new_state = ConsensusState::default(); + let mut new_state = consensus::State::default(); new_state.height = output.latest_block_height; self.consensus_state = new_state; @@ -178,7 +181,7 @@ mod tests { #[test] fn hrs_test() { let mut last_sign_state = State { - consensus_state: ConsensusState { + consensus_state: consensus::State { height: 1i64.into(), round: 1, step: 0, @@ -189,7 +192,7 @@ mod tests { assert_eq!( last_sign_state - .update_consensus_state(ConsensusState { + .update_consensus_state(consensus::State { height: 2i64.into(), round: 0, step: 0, @@ -203,7 +206,7 @@ mod tests { #[test] fn hrs_test_double_sign() { let mut last_sign_state = State { - consensus_state: ConsensusState { + consensus_state: consensus::State { height: 1i64.into(), round: 1, step: 0, @@ -212,7 +215,7 @@ mod tests { state_file_path: EXAMPLE_PATH.into(), }; let double_sign_block = block::Id::from_str(EXAMPLE_DOUBLE_SIGN_BLOCK_ID).unwrap(); - let err = last_sign_state.update_consensus_state(ConsensusState { + let err = last_sign_state.update_consensus_state(consensus::State { height: 1i64.into(), round: 1, step: 1, diff --git a/src/client.rs b/src/client.rs index 7fef988..5092470 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,15 +13,17 @@ use crate::{ }; use signatory::{ed25519, Decode, Encode, PublicKeyed}; use signatory_dalek::Ed25519Signer; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; use std::{ panic, path::Path, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, thread::{self, JoinHandle}, time::Duration, }; -use tendermint::{chain, node, secret_connection, Address}; +use tendermint::{chain, net, node, secret_connection}; /// How long to wait after a crash before respawning (in seconds) pub const RESPAWN_DELAY: u64 = 1; @@ -68,21 +70,15 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { return; } - let session_result = match &addr { - Address::Tcp { + let session_result = match addr { + net::Address::Tcp { peer_id, - host, + ref host, port, } => match &secret_key { - Some(path) => tcp_session( - chain_id, - max_height, - *peer_id, - host, - *port, - path, - should_term, - ), + Some(path) => { + tcp_session(chain_id, max_height, peer_id, host, port, path, should_term) + } None => { error!( "config error: missing field `secret_key` for validator {}", @@ -91,7 +87,9 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { return; } }, - Address::Unix { path } => unix_session(chain_id, max_height, path, should_term), + net::Address::Unix { ref path } => { + unix_session(chain_id, max_height, path, should_term) + } }; if let Err(e) = session_result { diff --git a/src/config/validator.rs b/src/config/validator.rs index 0811b6f..0bd369a 100644 --- a/src/config/validator.rs +++ b/src/config/validator.rs @@ -1,11 +1,11 @@ use std::path::PathBuf; -use tendermint::{chain, Address}; +use tendermint::{chain, net}; /// Validator configuration #[derive(Clone, Deserialize, Debug)] pub struct ValidatorConfig { /// Address of the validator (`tcp://` or `unix://`) - pub addr: Address, + pub addr: net::Address, /// Chain ID of the Tendermint network this validator is part of pub chain_id: chain::Id, diff --git a/tendermint-rs/Cargo.toml b/tendermint-rs/Cargo.toml index e32bff5..6e936a1 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -12,8 +12,9 @@ edition = "2018" description = """ Tendermint is a high-performance blockchain consensus engine that powers Byzantine fault tolerant applications written in any programming language. - This crate provides types for representing information about Tendermint - blockchain networks, including chain IDs, block IDs, and block heights. + This crate provides core types for representing information about Tendermint + blockchain networks, including chain information types, secret connections, + and remote procedure calls (JSONRPC). """ authors = [ @@ -38,6 +39,7 @@ 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, features = ["derive"] } +serde_json = { version = "1", optional = true } signatory = { version = "0.11.2", features = ["ed25519", "ecdsa"] } signatory-dalek = { version = "0.11", optional = true } sha2 = { version = "0.8", default-features = false } @@ -53,6 +55,7 @@ serde_json = "1" [features] default = ["serde", "tai64"] amino-types = ["prost-amino", "prost-amino-derive"] +rpc = ["serde", "serde_json"] secret-connection = [ "amino-types", "byteorder", diff --git a/tendermint-rs/src/account.rs b/tendermint-rs/src/account.rs index b3304c9..5efbd5e 100644 --- a/tendermint-rs/src/account.rs +++ b/tendermint-rs/src/account.rs @@ -6,7 +6,7 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Digest, Sha256}; use signatory::ecdsa::curve::secp256k1; use std::{ - fmt::{self, Display}, + fmt::{self, Debug, Display}, str::FromStr, }; use subtle::{self, ConstantTimeEq}; @@ -16,7 +16,7 @@ use subtle_encoding::hex; pub const ID_LENGTH: usize = 20; /// Account IDs -#[derive(Copy, Clone, Debug, Hash)] +#[derive(Copy, Clone, Hash)] pub struct Id([u8; ID_LENGTH]); impl Id { @@ -53,6 +53,12 @@ impl Display for Id { } } +impl Debug for Id { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "account::Id({})", self) + } +} + impl From for Id { fn from(pk: secp256k1::PublicKey) -> Id { let digest = Sha256::digest(pk.as_bytes()); diff --git a/tendermint-rs/src/algorithm.rs b/tendermint-rs/src/algorithm.rs deleted file mode 100644 index 899e68f..0000000 --- a/tendermint-rs/src/algorithm.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Algorithm registry - -/// Hash algorithms -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum HashAlgorithm { - /// SHA-256 - Sha256, -} - -/// Digital signature algorithms -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum SignatureAlgorithm { - /// ECDSA over secp256k1 - EcdsaSecp256k1, - - /// EdDSA over Curve25519 - Ed25519, -} diff --git a/tendermint-rs/src/amino_types/block_id.rs b/tendermint-rs/src/amino_types/block_id.rs index 30a5cb3..4306496 100644 --- a/tendermint-rs/src/amino_types/block_id.rs +++ b/tendermint-rs/src/amino_types/block_id.rs @@ -1,8 +1,8 @@ use super::validate::{ConsensusMessage, ValidationError, ValidationErrorKind::*}; use crate::{ - algorithm::HashAlgorithm, block, error::Error, + hash, hash::{Hash, SHA256_HASH_SIZE}, }; @@ -16,8 +16,12 @@ pub struct BlockId { impl block::ParseId for BlockId { fn parse_block_id(&self) -> Result { - let hash = Hash::new(HashAlgorithm::Sha256, &self.hash)?; - Ok(block::Id::new(hash)) + let hash = Hash::new(hash::Algorithm::Sha256, &self.hash)?; + let parts_header = self + .parts_header + .as_ref() + .and_then(PartsSetHeader::parse_parts_header); + Ok(block::Id::new(hash, parts_header)) } } @@ -43,8 +47,12 @@ pub struct CanonicalBlockId { impl block::ParseId for CanonicalBlockId { fn parse_block_id(&self) -> Result { - let hash = Hash::new(HashAlgorithm::Sha256, &self.hash)?; - Ok(block::Id::new(hash)) + let hash = Hash::new(hash::Algorithm::Sha256, &self.hash)?; + let parts_header = self + .parts_header + .as_ref() + .and_then(CanonicalPartSetHeader::parse_parts_header); + Ok(block::Id::new(hash, parts_header)) } } @@ -56,6 +64,14 @@ pub struct PartsSetHeader { pub hash: Vec, } +impl PartsSetHeader { + fn parse_parts_header(&self) -> Option { + Hash::new(hash::Algorithm::Sha256, &self.hash) + .map(|hash| block::parts::Header::new(self.total as u64, hash)) + .ok() + } +} + impl ConsensusMessage for PartsSetHeader { fn validate_basic(&self) -> Result<(), ValidationError> { if self.total < 0 { @@ -76,3 +92,11 @@ pub struct CanonicalPartSetHeader { #[prost(int64, tag = "2")] pub total: i64, } + +impl CanonicalPartSetHeader { + fn parse_parts_header(&self) -> Option { + Hash::new(hash::Algorithm::Sha256, &self.hash) + .map(|hash| block::parts::Header::new(self.total as u64, hash)) + .ok() + } +} diff --git a/tendermint-rs/src/amino_types/ed25519.rs b/tendermint-rs/src/amino_types/ed25519.rs index c170217..1de61e4 100644 --- a/tendermint-rs/src/amino_types/ed25519.rs +++ b/tendermint-rs/src/amino_types/ed25519.rs @@ -1,4 +1,4 @@ -use crate::public_keys::PublicKey; +use crate::public_key::PublicKey; use signatory::ed25519::PUBLIC_KEY_SIZE; // Note:On the golang side this is generic in the sense that it could everything that implements diff --git a/tendermint-rs/src/amino_types/proposal.rs b/tendermint-rs/src/amino_types/proposal.rs index c930dac..a77f239 100644 --- a/tendermint-rs/src/amino_types/proposal.rs +++ b/tendermint-rs/src/amino_types/proposal.rs @@ -7,7 +7,7 @@ use super::{ }; use crate::{ block::{self, ParseId}, - chain::{self, ConsensusState}, + chain, consensus, error::Error, }; use bytes::BufMut; @@ -133,9 +133,9 @@ impl SignableMsg for SignProposalRequest { None => Err(MissingConsensusMessage.into()), } } - fn consensus_state(&self) -> Option { + fn consensus_state(&self) -> Option { match self.proposal { - Some(ref p) => Some(ConsensusState { + Some(ref p) => Some(consensus::State { height: match block::Height::try_from_i64(p.height) { Ok(h) => h, Err(_err) => return None, // TODO(tarcieri): return an error? diff --git a/tendermint-rs/src/amino_types/signature.rs b/tendermint-rs/src/amino_types/signature.rs index 6b458b1..08dd289 100644 --- a/tendermint-rs/src/amino_types/signature.rs +++ b/tendermint-rs/src/amino_types/signature.rs @@ -1,5 +1,5 @@ use super::validate::ValidationError; -use crate::chain::{self, ConsensusState}; +use crate::{chain, consensus}; use bytes::BufMut; use prost::{DecodeError, EncodeError}; use signatory::ed25519; @@ -16,7 +16,7 @@ pub trait SignableMsg { /// Set the Ed25519 signature on the underlying message fn set_signature(&mut self, sig: &ed25519::Signature); fn validate(&self) -> Result<(), ValidationError>; - fn consensus_state(&self) -> Option; + fn consensus_state(&self) -> Option; fn height(&self) -> Option; } diff --git a/tendermint-rs/src/amino_types/time.rs b/tendermint-rs/src/amino_types/time.rs index e80817f..af942c1 100644 --- a/tendermint-rs/src/amino_types/time.rs +++ b/tendermint-rs/src/amino_types/time.rs @@ -2,7 +2,7 @@ use crate::{ error::Error, - timestamp::{ParseTimestamp, Timestamp}, + time::{ParseTimestamp, Time}, }; use chrono::{TimeZone, Utc}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -19,15 +19,15 @@ pub struct TimeMsg { } impl ParseTimestamp for TimeMsg { - fn parse_timestamp(&self) -> Result { + fn parse_timestamp(&self) -> Result { Ok(Utc.timestamp(self.seconds, self.nanos as u32).into()) } } -impl From for TimeMsg { - fn from(ts: Timestamp) -> TimeMsg { +impl From