From dfb60c9e2b340da0d6fa81c4c8f32904c36d249d Mon Sep 17 00:00:00 2001 From: Greg Szabo Date: Thu, 20 Aug 2020 11:05:12 -0400 Subject: [PATCH 1/7] Ripped out amino types and replaced them with protobuf types. --- tendermint/Cargo.toml | 6 +- tendermint/src/amino_types.rs | 41 +--- tendermint/src/amino_types/block_id.rs | 92 +++---- tendermint/src/amino_types/ed25519.rs | 177 ++++++++------ tendermint/src/amino_types/message.rs | 14 +- tendermint/src/amino_types/ping.rs | 14 -- tendermint/src/amino_types/proposal.rs | 242 +++++++++++-------- tendermint/src/amino_types/remote_error.rs | 32 --- tendermint/src/amino_types/signature.rs | 38 ++- tendermint/src/amino_types/time.rs | 48 ---- tendermint/src/amino_types/version.rs | 15 +- tendermint/src/amino_types/vote.rs | 266 ++++++++++++--------- tendermint/src/block/header.rs | 11 +- tendermint/src/validator.rs | 36 +-- 14 files changed, 509 insertions(+), 523 deletions(-) delete mode 100644 tendermint/src/amino_types/ping.rs delete mode 100644 tendermint/src/amino_types/remote_error.rs delete mode 100644 tendermint/src/amino_types/time.rs diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index aa2217af8..589858927 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -39,8 +39,9 @@ ed25519-dalek = { version = "= 1.0.0-pre.4", features = ["serde"] } futures = "0.3" k256 = { version = "0.4", optional = true, features = ["ecdsa"] } once_cell = "1.3" -prost-amino = "0.6" -prost-amino-derive = "0.6" +prost = "0.6" +prost-derive = "0.6" +prost-types = "0.6" serde = { version = "1", features = ["derive"] } serde_json = "1" serde_bytes = "0.11" @@ -51,6 +52,7 @@ subtle = "2" subtle-encoding = { version = "0.5", features = ["bech32-preview"] } tai64 = { version = "3", features = ["chrono"] } thiserror = "1" +tendermint-proto = { path = "../proto" } toml = { version = "0.5" } zeroize = { version = "1.1", features = ["zeroize_derive"] } ripemd160 = { version = "0.9", optional = true } diff --git a/tendermint/src/amino_types.rs b/tendermint/src/amino_types.rs index bad6c89e8..e9256327a 100644 --- a/tendermint/src/amino_types.rs +++ b/tendermint/src/amino_types.rs @@ -6,51 +6,18 @@ pub mod block_id; pub mod ed25519; pub mod message; -pub mod ping; pub mod proposal; -pub mod remote_error; pub mod signature; -pub mod time; pub mod validate; pub mod version; pub mod vote; pub use self::{ - block_id::{BlockId, CanonicalBlockId, CanonicalPartSetHeader, PartsSetHeader}, - ed25519::{ - PubKeyRequest, PubKeyResponse, AMINO_NAME as PUBKEY_AMINO_NAME, - AMINO_PREFIX as PUBKEY_PREFIX, - }, - ping::{PingRequest, PingResponse, AMINO_NAME as PING_AMINO_NAME, AMINO_PREFIX as PING_PREFIX}, - proposal::{ - SignProposalRequest, SignedProposalResponse, AMINO_NAME as PROPOSAL_AMINO_NAME, - AMINO_PREFIX as PROPOSAL_PREFIX, - }, - remote_error::RemoteError, + block_id::{BlockId, CanonicalBlockId, CanonicalPartSetHeader, PartSetHeader}, + ed25519::{PubKeyRequest, PubKeyResponse}, + proposal::{SignProposalRequest, SignedProposalResponse}, signature::{SignableMsg, SignedMsgType}, - time::TimeMsg, validate::ConsensusMessage, version::ConsensusVersion, - vote::{ - SignVoteRequest, SignedVoteResponse, AMINO_NAME as VOTE_AMINO_NAME, - AMINO_PREFIX as VOTE_PREFIX, - }, + vote::{SignVoteRequest, SignedVoteResponse}, }; - -use sha2::{Digest, Sha256}; - -/// Compute the Amino prefix for the given registered type name -pub fn compute_prefix(name: &str) -> Vec { - let mut sh = Sha256::default(); - sh.update(name.as_bytes()); - let output = sh.finalize(); - - output - .iter() - .filter(|&x| *x != 0x00) - .skip(3) - .filter(|&x| *x != 0x00) - .cloned() - .take(4) - .collect() -} diff --git a/tendermint/src/amino_types/block_id.rs b/tendermint/src/amino_types/block_id.rs index f7ec00b81..3c76aed1a 100644 --- a/tendermint/src/amino_types/block_id.rs +++ b/tendermint/src/amino_types/block_id.rs @@ -1,4 +1,4 @@ -use super::validate::{ConsensusMessage, Kind::InvalidHashSize, Kind::NegativeTotal}; +use super::validate::{ConsensusMessage, Kind::InvalidHashSize}; use crate::block::parts; use crate::{ block, @@ -6,30 +6,34 @@ use crate::{ hash, hash::{Hash, SHA256_HASH_SIZE}, }; -use prost_amino_derive::Message; -#[derive(Clone, PartialEq, Message)] +// Copied from tendermint_proto::types::BlockId +/// BlockID +#[derive(Clone, PartialEq, ::prost::Message)] pub struct BlockId { - #[prost_amino(bytes, tag = "1")] + #[prost(bytes, tag = "1")] pub hash: Vec, - #[prost_amino(message, tag = "2")] - pub parts_header: Option, + #[prost(message, optional, tag = "2")] + pub part_set_header: ::std::option::Option, } impl BlockId { - pub fn new(hash: Vec, parts_header: Option) -> Self { - BlockId { hash, parts_header } + pub fn new(hash: Vec, part_set_header: Option) -> Self { + BlockId { + hash, + part_set_header, + } } } impl block::ParseId for BlockId { fn parse_block_id(&self) -> Result { let hash = Hash::new(hash::Algorithm::Sha256, &self.hash)?; - let parts_header = self - .parts_header + let part_set_header = self + .part_set_header .as_ref() - .and_then(PartsSetHeader::parse_parts_header); - Ok(block::Id::new(hash, parts_header)) + .and_then(PartSetHeader::parse_part_set_header); + Ok(block::Id::new(hash, part_set_header)) } } @@ -38,7 +42,7 @@ impl From<&block::Id> for BlockId { let bid_hash = bid.hash.as_bytes(); BlockId::new( bid_hash.to_vec(), - bid.parts.as_ref().map(PartsSetHeader::from), + bid.parts.as_ref().map(PartSetHeader::from), ) } } @@ -49,64 +53,67 @@ impl ConsensusMessage for BlockId { if !self.hash.is_empty() && self.hash.len() != SHA256_HASH_SIZE { return Err(InvalidHashSize.into()); } - self.parts_header + self.part_set_header .as_ref() .map_or(Ok(()), ConsensusMessage::validate_basic) } } -#[derive(Clone, PartialEq, Message)] +// Copied from tendermint_proto::types::CanonicalBlockId +#[derive(Clone, PartialEq, ::prost::Message)] pub struct CanonicalBlockId { - #[prost_amino(bytes, tag = "1")] + #[prost(bytes, tag = "1")] pub hash: Vec, - #[prost_amino(message, tag = "2")] - pub parts_header: Option, + #[prost(message, optional, tag = "2")] + pub part_set_header: ::std::option::Option, } impl block::ParseId for CanonicalBlockId { fn parse_block_id(&self) -> Result { let hash = Hash::new(hash::Algorithm::Sha256, &self.hash)?; - let parts_header = self - .parts_header + let part_set_header = self + .part_set_header .as_ref() - .and_then(CanonicalPartSetHeader::parse_parts_header); - Ok(block::Id::new(hash, parts_header)) + .and_then(CanonicalPartSetHeader::parse_part_set_header); + Ok(block::Id::new(hash, part_set_header)) } } -#[derive(Clone, PartialEq, Message)] -pub struct PartsSetHeader { - #[prost_amino(int64, tag = "1")] - pub total: i64, - #[prost_amino(bytes, tag = "2")] +// Copied from tendermint_proto::types::PartSetHeader +/// PartsetHeader +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartSetHeader { + #[prost(uint32, tag = "1")] + pub total: u32, + #[prost(bytes, tag = "2")] pub hash: Vec, } -impl PartsSetHeader { +impl PartSetHeader { pub fn new(total: i64, hash: Vec) -> Self { - PartsSetHeader { total, hash } + PartSetHeader { + total: total as u32, + hash, + } } } -impl From<&parts::Header> for PartsSetHeader { +impl From<&parts::Header> for PartSetHeader { fn from(parts: &parts::Header) -> Self { - PartsSetHeader::new(parts.total as i64, parts.hash.as_bytes().to_vec()) + PartSetHeader::new(parts.total as i64, parts.hash.as_bytes().to_vec()) } } -impl PartsSetHeader { - fn parse_parts_header(&self) -> Option { +impl PartSetHeader { + fn parse_part_set_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 { +impl ConsensusMessage for PartSetHeader { fn validate_basic(&self) -> Result<(), Error> { - if self.total < 0 { - return Err(NegativeTotal.into()); - } // Hash can be empty in case of POLBlockID.PartsHeader in Proposal. if !self.hash.is_empty() && self.hash.len() != SHA256_HASH_SIZE { return Err(InvalidHashSize.into()); @@ -115,16 +122,17 @@ impl ConsensusMessage for PartsSetHeader { } } -#[derive(Clone, PartialEq, Message)] +// Copied from tendermint_proto::types::CanonicalPartSetHeader +#[derive(Clone, PartialEq, ::prost::Message)] pub struct CanonicalPartSetHeader { - #[prost_amino(bytes, tag = "1")] + #[prost(uint32, tag = "1")] + pub total: u32, + #[prost(bytes, tag = "2")] pub hash: Vec, - #[prost_amino(int64, tag = "2")] - pub total: i64, } impl CanonicalPartSetHeader { - fn parse_parts_header(&self) -> Option { + fn parse_part_set_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/src/amino_types/ed25519.rs b/tendermint/src/amino_types/ed25519.rs index 345b3059a..9bd5b9aae 100644 --- a/tendermint/src/amino_types/ed25519.rs +++ b/tendermint/src/amino_types/ed25519.rs @@ -1,13 +1,11 @@ -use super::compute_prefix; use crate::{ error, public_key::{Ed25519, PublicKey}, Error, }; use anomaly::format_err; -use once_cell::sync::Lazy; -use prost_amino_derive::Message; use std::convert::TryFrom; +use tendermint_proto::crypto::public_key::Sum; // Note:On the golang side this is generic in the sense that it could everything that implements // github.com/tendermint/tendermint/crypto.PubKey @@ -15,19 +13,23 @@ use std::convert::TryFrom; // version. // TODO(ismail): make this more generic (by modifying prost and adding a trait for PubKey) -pub const AMINO_NAME: &str = "tendermint/remotesigner/PubKeyRequest"; -pub static AMINO_PREFIX: Lazy> = Lazy::new(|| compute_prefix(AMINO_NAME)); - -#[derive(Clone, PartialEq, Message)] -#[amino_name = "tendermint/remotesigner/PubKeyResponse"] +// Copied from tendermint_proto::privval::PubKeyResponse; +/// PubKeyResponse is a response message containing the public key. +#[derive(Clone, PartialEq, ::prost::Message)] pub struct PubKeyResponse { - #[prost_amino(bytes, tag = "1", amino_name = "tendermint/PubKeyEd25519")] - pub pub_key_ed25519: Vec, + #[prost(message, optional, tag = "1")] + pub pub_key: ::std::option::Option, + #[prost(message, optional, tag = "2")] + pub error: ::std::option::Option, } -#[derive(Clone, PartialEq, Message)] -#[amino_name = "tendermint/remotesigner/PubKeyRequest"] -pub struct PubKeyRequest {} +// Copied from tendermint_proto::privval::PubKeyRequest; +/// PubKeyRequest requests the consensus public key from the remote signer. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PubKeyRequest { + #[prost(string, tag = "1")] + pub chain_id: String, +} impl TryFrom for PublicKey { type Error = Error; @@ -35,9 +37,16 @@ impl TryFrom 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 try_from(response: PubKeyResponse) -> Result { - Ed25519::from_bytes(&response.pub_key_ed25519) - .map(Into::into) - .map_err(|_| format_err!(error::Kind::InvalidKey, "malformed Ed25519 key").into()) + match &response + .pub_key + .ok_or_else(|| format_err!(error::Kind::InvalidKey, "empty pubkey"))? + .sum + .ok_or_else(|| format_err!(error::Kind::InvalidKey, "empty sum"))? + { + Sum::Ed25519(b) => Ed25519::from_bytes(b), + } + .map(Into::into) + .map_err(|_| format_err!(error::Kind::InvalidKey, "malformed key").into()) } } @@ -45,7 +54,12 @@ 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(), + pub_key: Some(tendermint_proto::crypto::PublicKey { + sum: Some(tendermint_proto::crypto::public_key::Sum::Ed25519( + pk.as_bytes().to_vec(), + )), + }), + error: None, }, #[cfg(feature = "secp256k1")] PublicKey::Secp256k1(_) => panic!("secp256k1 PubKeyResponse unimplemented"), @@ -57,43 +71,31 @@ impl From for PubKeyResponse { mod tests { use super::*; use ed25519_dalek::PUBLIC_KEY_LENGTH; - use prost_amino::Message; + use prost::Message; use std::convert::TryInto; #[test] fn test_empty_pubkey_msg() { // test-vector generated via the following go code: - // - // -------------------------------------------------------------------- - //package main - // - //import ( - // "fmt" - // - // "github.com/tendermint/go-amino" - // "github.com/tendermint/tendermint/crypto" - // "github.com/tendermint/tendermint/privval" - //) - // - //func main() { - // cdc := amino.NewCodec() - // - // cdc.RegisterInterface((*crypto.PubKey)(nil), nil) - // cdc.RegisterConcrete(crypto.PubKeyEd25519{}, - // "tendermint/PubKeyEd25519", nil) - // cdc.RegisterConcrete(&privval.PubKeyRequest{}, - // "tendermint/remotesigner/PubKeyRequest", nil) - // b, _ := cdc.MarshalBinary(&privval.PubKeyRequest{}) - // fmt.Printf("%#v\n\n", b) - //} - // -------------------------------------------------------------------- - // Output: - // []byte{0x4, 0xcb, 0x94, 0xd6, 0x20} - // - // - - let want = vec![0x4, 0xcb, 0x94, 0xd6, 0x20]; - let msg = PubKeyRequest {}; + /* + import ( + "fmt" + "github.com/tendermint/tendermint/proto/tendermint/privval" + ) + func ed25519_empty() { + pkr := &privval.PubKeyRequest{ + ChainId: "", + } + pbpk, _ := pkr.Marshal() + fmt.Printf("%#v\n", pbpk) + + } + */ + + let want: Vec = vec![]; + let msg = PubKeyRequest { + chain_id: "".to_string(), + }; let mut got = vec![]; let _have = msg.encode(&mut got); @@ -107,34 +109,47 @@ mod tests { #[test] fn test_ed25519_pubkey_msg() { - // test-vector generated exactly as for test_empty_pubkey_msg - // but with the following modifications: - // cdc.RegisterConcrete(&privval.PubKeyResponse{}, - // "tendermint/remotesigner/PubKeyResponse", nil) - // - // var pubKey [32]byte - // copy(pubKey[:],[]byte{0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, 0xe0, 0x8b, 0x7b, - // 0xb5, 0x61, 0xbc, 0xe7, 0xc1, - // 0xd4, 0x69, 0xc3, 0x44, 0x26, 0xec, 0xef, 0xc0, 0x72, 0xa, 0x52, 0x4d, 0x37, 0x32, 0xef, - // 0xed}) - // - // b, _ = cdc.MarshalBinary(&privval.PubKeyResponse{PubKey: crypto.PubKeyEd25519(pubKey)}) - // fmt.Printf("%#v\n\n", b) - // + // test-vector generated from Go + /* + import ( + "fmt" + "github.com/tendermint/tendermint/proto/tendermint/crypto" + "github.com/tendermint/tendermint/proto/tendermint/privval" + ) + + func ed25519_key() { + pkr := &privval.PubKeyResponse{ + PubKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{Ed25519: []byte{ + 0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, 0xe0, + 0x8b, 0x7b, 0xb5, 0x61, 0xbc, 0xe7, 0xc1, 0xd4, 0x69, + 0xc3, 0x44, 0x26, 0xec, 0xef, 0xc0, 0x72, 0xa, 0x52, + 0x4d, 0x37, 0x32, 0xef, 0xed, + }, + }, + }, + Error: nil, + } + pbpk, _ := pkr.Marshal() + fmt.Printf("%#v\n", pbpk) + + } + */ let encoded = vec![ - 0x2b, // length - 0x17, 0xe, 0xd5, 0x7c, // prefix - 0xa, 0x25, 0x16, 0x24, 0xde, 0x64, 0x20, 0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, - 0xe0, 0x8b, 0x7b, 0xb5, 0x61, 0xbc, 0xe7, 0xc1, 0xd4, 0x69, 0xc3, 0x44, 0x26, 0xec, - 0xef, 0xc0, 0x72, 0xa, 0x52, 0x4d, 0x37, 0x32, 0xef, 0xed, + 0xa, 0x22, 0xa, 0x20, 0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, 0xe0, 0x8b, 0x7b, + 0xb5, 0x61, 0xbc, 0xe7, 0xc1, 0xd4, 0x69, 0xc3, 0x44, 0x26, 0xec, 0xef, 0xc0, 0x72, + 0xa, 0x52, 0x4d, 0x37, 0x32, 0xef, 0xed, ]; let msg = PubKeyResponse { - pub_key_ed25519: vec![ - 0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, 0xe0, 0x8b, 0x7b, 0xb5, 0x61, 0xbc, - 0xe7, 0xc1, 0xd4, 0x69, 0xc3, 0x44, 0x26, 0xec, 0xef, 0xc0, 0x72, 0xa, 0x52, 0x4d, - 0x37, 0x32, 0xef, 0xed, - ], + pub_key: Some(tendermint_proto::crypto::PublicKey { + sum: Some(tendermint_proto::crypto::public_key::Sum::Ed25519(vec![ + 0x79, 0xce, 0xd, 0xe0, 0x43, 0x33, 0x4a, 0xec, 0xe0, 0x8b, 0x7b, 0xb5, 0x61, + 0xbc, 0xe7, 0xc1, 0xd4, 0x69, 0xc3, 0x44, 0x26, 0xec, 0xef, 0xc0, 0x72, 0xa, + 0x52, 0x4d, 0x37, 0x32, 0xef, 0xed, + ])), + }), + error: None, }; let mut got = vec![]; let _have = msg.encode(&mut got); @@ -156,11 +171,14 @@ mod tests { ]; let want = PublicKey::Ed25519(Ed25519::from_bytes(&raw_pk).unwrap()); let pk = PubKeyResponse { - pub_key_ed25519: vec![ - 0xaf, 0xf3, 0x94, 0xc5, 0xb7, 0x5c, 0xfb, 0xd, 0xd9, 0x28, 0xe5, 0x8a, 0x92, 0xdd, - 0x76, 0x55, 0x2b, 0x2e, 0x8d, 0x19, 0x6f, 0xe9, 0x12, 0x14, 0x50, 0x80, 0x6b, 0xd0, - 0xd9, 0x3f, 0xd0, 0xcb, - ], + pub_key: Some(tendermint_proto::crypto::PublicKey { + sum: Some(tendermint_proto::crypto::public_key::Sum::Ed25519(vec![ + 0xaf, 0xf3, 0x94, 0xc5, 0xb7, 0x5c, 0xfb, 0xd, 0xd9, 0x28, 0xe5, 0x8a, 0x92, + 0xdd, 0x76, 0x55, 0x2b, 0x2e, 0x8d, 0x19, 0x6f, 0xe9, 0x12, 0x14, 0x50, 0x80, + 0x6b, 0xd0, 0xd9, 0x3f, 0xd0, 0xcb, + ])), + }), + error: None, }; let orig = pk.clone(); let got: PublicKey = pk.try_into().unwrap(); @@ -176,7 +194,8 @@ mod tests { #[should_panic] fn test_empty_into() { let empty_msg = PubKeyResponse { - pub_key_ed25519: vec![], + pub_key: None, + error: None, }; // we expect this to panic: let _got: PublicKey = empty_msg.try_into().unwrap(); diff --git a/tendermint/src/amino_types/message.rs b/tendermint/src/amino_types/message.rs index c10e012fe..283a3ae88 100644 --- a/tendermint/src/amino_types/message.rs +++ b/tendermint/src/amino_types/message.rs @@ -1,10 +1,10 @@ -use prost_amino::encoding::encoded_len_varint; +use prost::encoding::encoded_len_varint; use std::convert::TryInto; -/// Extend the original prost_amino::Message trait with a few helper functions in order to -/// reduce boiler-plate code (and without modifying the prost-amino dependency). -pub trait AminoMessage: prost_amino::Message { - /// Directly amino encode a prost-amino message into a freshly created Vec. +/// Extend the original prost::Message trait with a few helper functions in order to +/// reduce boiler-plate code (and without modifying the prost dependency). +pub trait AminoMessage: prost::Message { + /// Directly encode a prost message into a freshly created Vec. /// This can be useful when passing those bytes directly to a hasher, or, /// to reduce boiler plate code when working with the encoded bytes. /// @@ -19,7 +19,7 @@ pub trait AminoMessage: prost_amino::Message { res } - /// Encode prost-amino message as length delimited. + /// Encode prost message as length delimited. /// /// Warning: Only use this method, if you are in control what will be encoded. /// If there is an encoding error, this method will panic. @@ -34,6 +34,6 @@ pub trait AminoMessage: prost_amino::Message { res } } -impl AminoMessage for M { +impl AminoMessage for M { // blanket impl } diff --git a/tendermint/src/amino_types/ping.rs b/tendermint/src/amino_types/ping.rs deleted file mode 100644 index 983288b4f..000000000 --- a/tendermint/src/amino_types/ping.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::compute_prefix; -use once_cell::sync::Lazy; -use prost_amino_derive::Message; - -pub const AMINO_NAME: &str = "tendermint/remotesigner/PingRequest"; -pub static AMINO_PREFIX: Lazy> = Lazy::new(|| compute_prefix(AMINO_NAME)); - -#[derive(Clone, PartialEq, Message)] -#[amino_name = "tendermint/remotesigner/PingRequest"] -pub struct PingRequest {} - -#[derive(Clone, PartialEq, Message)] -#[amino_name = "tendermint/remotesigner/PingResponse"] -pub struct PingResponse {} diff --git a/tendermint/src/amino_types/proposal.rs b/tendermint/src/amino_types/proposal.rs index 03529d621..a0a1365d2 100644 --- a/tendermint/src/amino_types/proposal.rs +++ b/tendermint/src/amino_types/proposal.rs @@ -1,9 +1,6 @@ use super::{ block_id::{BlockId, CanonicalBlockId, CanonicalPartSetHeader}, - compute_prefix, - remote_error::RemoteError, signature::{SignableMsg, SignedMsgType}, - time::TimeMsg, validate::{ self, ConsensusMessage, Kind::InvalidMessageType, Kind::MissingConsensusMessage, Kind::NegativeHeight, Kind::NegativePOLRound, Kind::NegativeRound, @@ -14,26 +11,26 @@ use crate::{ chain, consensus, error, }; use bytes::BufMut; -use once_cell::sync::Lazy; -use prost_amino::{EncodeError, Message}; -use prost_amino_derive::Message; +use prost::{EncodeError, Message}; use std::convert::TryFrom; +use tendermint_proto::privval::RemoteSignerError; -#[derive(Clone, PartialEq, Message)] +// Copied from tendermint_proto::types::Proposal +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Proposal { - #[prost_amino(uint32, tag = "1")] - pub msg_type: u32, - #[prost_amino(int64)] + #[prost(enumeration = "SignedMsgType", tag = "1")] + pub r#type: i32, + #[prost(int64, tag = "2")] pub height: i64, - #[prost_amino(int64)] - pub round: i64, - #[prost_amino(int64)] - pub pol_round: i64, - #[prost_amino(message)] - pub block_id: Option, - #[prost_amino(message)] - pub timestamp: Option, - #[prost_amino(bytes)] + #[prost(int32, tag = "3")] + pub round: i32, + #[prost(int32, tag = "4")] + pub pol_round: i32, + #[prost(message, optional, tag = "5")] + pub block_id: ::std::option::Option, + #[prost(message, optional, tag = "6")] + pub timestamp: ::std::option::Option<::prost_types::Timestamp>, + #[prost(bytes, tag = "7")] pub signature: Vec, } @@ -44,32 +41,35 @@ impl block::ParseHeight for Proposal { } } -pub const AMINO_NAME: &str = "tendermint/remotesigner/SignProposalRequest"; -pub static AMINO_PREFIX: Lazy> = Lazy::new(|| compute_prefix(AMINO_NAME)); - -#[derive(Clone, PartialEq, Message)] -#[amino_name = "tendermint/remotesigner/SignProposalRequest"] +// Copied from tendermint_proto::privval::SignProposalRequest +/// SignProposalRequest is a request to sign a proposal +#[derive(Clone, PartialEq, ::prost::Message)] pub struct SignProposalRequest { - #[prost_amino(message, tag = "1")] - pub proposal: Option, + #[prost(message, optional, tag = "1")] + pub proposal: ::std::option::Option, + #[prost(string, tag = "2")] + pub chain_id: String, } -#[derive(Clone, PartialEq, Message)] -struct CanonicalProposal { - #[prost_amino(uint32, tag = "1")] - msg_type: u32, /* this is a byte in golang, which is a varint encoded UInt8 (using amino's - * EncodeUvarint) */ - #[prost_amino(sfixed64)] - height: i64, - #[prost_amino(sfixed64)] - round: i64, - #[prost_amino(sfixed64)] - pol_round: i64, - #[prost_amino(message)] - block_id: Option, - #[prost_amino(message)] - timestamp: Option, - #[prost_amino(string)] +// Copied from tendermint_proto::types::CanonicalProposal +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CanonicalProposal { + /// type alias for byte + #[prost(enumeration = "SignedMsgType", tag = "1")] + pub r#type: i32, + /// canonicalization requires fixed size encoding here + #[prost(sfixed64, tag = "2")] + pub height: i64, + /// canonicalization requires fixed size encoding here + #[prost(sfixed64, tag = "3")] + pub round: i64, + #[prost(int64, tag = "4")] + pub pol_round: i64, + #[prost(message, optional, tag = "5")] + pub block_id: ::std::option::Option, + #[prost(message, optional, tag = "6")] + pub timestamp: ::std::option::Option<::prost_types::Timestamp>, + #[prost(string, tag = "7")] pub chain_id: String, } @@ -85,13 +85,14 @@ impl block::ParseHeight for CanonicalProposal { } } -#[derive(Clone, PartialEq, Message)] -#[amino_name = "tendermint/remotesigner/SignedProposalResponse"] +// Copied from tendermint_proto::privval::SignedProposalResponse +/// SignedProposalResponse is response containing a signed proposal or an error +#[derive(Clone, PartialEq, ::prost::Message)] pub struct SignedProposalResponse { - #[prost_amino(message, tag = "1")] - pub proposal: Option, - #[prost_amino(message, tag = "2")] - pub err: Option, + #[prost(message, optional, tag = "1")] + pub proposal: ::std::option::Option, + #[prost(message, optional, tag = "2")] + pub error: ::std::option::Option, } impl SignableMsg for SignProposalRequest { @@ -106,12 +107,12 @@ impl SignableMsg for SignProposalRequest { let proposal = spr.proposal.unwrap(); let cp = CanonicalProposal { chain_id: chain_id.to_string(), - msg_type: SignedMsgType::Proposal.to_u32(), + r#type: SignedMsgType::Proposal as i32, height: proposal.height, block_id: match proposal.block_id { Some(bid) => Some(CanonicalBlockId { hash: bid.hash, - parts_header: match bid.parts_header { + part_set_header: match bid.part_set_header { Some(psh) => Some(CanonicalPartSetHeader { hash: psh.hash, total: psh.total, @@ -121,8 +122,8 @@ impl SignableMsg for SignProposalRequest { }), None => None, }, - pol_round: proposal.pol_round, - round: proposal.round, + pol_round: proposal.pol_round as i64, + round: proposal.round as i64, timestamp: proposal.timestamp, }; @@ -147,7 +148,7 @@ impl SignableMsg for SignProposalRequest { Ok(h) => h, Err(_err) => return None, // TODO(tarcieri): return an error? }, - round: p.round, + round: p.round as i64, step: 3, block_id: { match p.block_id { @@ -174,7 +175,7 @@ impl SignableMsg for SignProposalRequest { impl ConsensusMessage for Proposal { fn validate_basic(&self) -> Result<(), validate::Error> { - if self.msg_type != SignedMsgType::Proposal.to_u32() { + if self.r#type != SignedMsgType::Proposal { return Err(InvalidMessageType.into()); } if self.height < 0 { @@ -197,27 +198,28 @@ impl ConsensusMessage for Proposal { #[cfg(test)] mod tests { use super::*; - use crate::amino_types::block_id::PartsSetHeader; + use crate::amino_types::block_id::{BlockId, PartSetHeader}; use chrono::{DateTime, Utc}; - use prost_amino::Message; + use prost::Message; + use prost_types::Timestamp; #[test] fn test_serialization() { let dt = "2018-02-11T07:09:22.765Z".parse::>().unwrap(); - let t = TimeMsg { + let t = Timestamp { seconds: dt.timestamp(), nanos: dt.timestamp_subsec_nanos() as i32, }; let proposal = Proposal { - msg_type: SignedMsgType::Proposal.to_u32(), + r#type: SignedMsgType::Proposal as i32, height: 12345, round: 23456, pol_round: -1, block_id: Some(BlockId { - hash: b"hash".to_vec(), - parts_header: Some(PartsSetHeader { - total: 1_000_000, - hash: b"parts_hash".to_vec(), + hash: b"DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA".to_vec(), + part_set_header: Some(PartSetHeader { + total: 65535, + hash: b"0022446688AACCEE1133557799BBDDFF".to_vec(), }), }), timestamp: Some(t), @@ -225,36 +227,68 @@ mod tests { }; let mut got = vec![]; - let _have = SignProposalRequest { - proposal: Some(proposal), - } - .encode(&mut got); - // test-vector generated via: - // cdc := amino.NewCodec() - // privval.RegisterRemoteSignerMsg(cdc) - // stamp, _ := time.Parse(time.RFC3339Nano, "2018-02-11T07:09:22.765Z") - // data, _ := cdc.MarshalBinaryLengthPrefixed(privval.SignProposalRequest{Proposal: - // &types.Proposal{ Type: types.ProposalType, // 0x20 - // Height: 12345, - // Round: 23456, - // POLRound: -1, - // BlockID: types.BlockID{ - // Hash: []byte("hash"), - // PartsHeader: types.PartSetHeader{ - // Hash: []byte("parts_hash"), - // Total: 1000000, - // }, - // }, - // Timestamp: stamp, - // }}) - // fmt.Println(strings.Join(strings.Split(fmt.Sprintf("%v", data), " "), ", ")) + // Simulating Go's ProposalSignBytes function. Shall we make this into a function too? + let canonical = CanonicalProposal { + r#type: proposal.r#type, + height: proposal.height, + round: proposal.round as i64, + pol_round: proposal.pol_round as i64, + block_id: Some(CanonicalBlockId { + hash: proposal.block_id.clone().unwrap().hash, + part_set_header: Some(CanonicalPartSetHeader { + total: proposal + .block_id + .clone() + .unwrap() + .part_set_header + .unwrap() + .total, + hash: proposal.block_id.unwrap().part_set_header.unwrap().hash, + }), + }), + timestamp: proposal.timestamp, + chain_id: "test_chain_id".to_string(), + }; + canonical.encode_length_delimited(&mut got).unwrap(); + + // the following vector is generated via: + /* + import ( + "fmt" + prototypes "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/types" + "strings" + "time" + ) + func proposalSerialize() { + stamp, _ := time.Parse(time.RFC3339Nano, "2018-02-11T07:09:22.765Z") + proposal := &types.Proposal{ + Type: prototypes.SignedMsgType(prototypes.ProposalType), + Height: 12345, + Round: 23456, + POLRound: -1, + BlockID: types.BlockID{ + Hash: []byte("DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA"), + PartSetHeader: types.PartSetHeader{ + Hash: []byte("0022446688AACCEE1133557799BBDDFF"), + Total: 65535, + }, + }, + Timestamp: stamp, + } + signBytes := types.ProposalSignBytes("test_chain_id",proposal.ToProto()) + fmt.Println(strings.Join(strings.Split(fmt.Sprintf("%v", signBytes), " "), ", ")) + } + */ + let want = vec![ - 66, // len - 189, 228, 152, 226, // prefix - 10, 60, 8, 32, 16, 185, 96, 24, 160, 183, 1, 32, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 1, 42, 24, 10, 4, 104, 97, 115, 104, 18, 16, 8, 192, 132, 61, 18, 10, 112, - 97, 114, 116, 115, 95, 104, 97, 115, 104, 50, 12, 8, 162, 216, 255, 211, 5, 16, 192, - 242, 227, 236, 2, + 136, 1, 8, 32, 17, 57, 48, 0, 0, 0, 0, 0, 0, 25, 160, 91, 0, 0, 0, 0, 0, 0, 32, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 1, 42, 74, 10, 32, 68, 69, 65, 68, 66, 69, 69, + 70, 68, 69, 65, 68, 66, 69, 69, 70, 66, 65, 70, 66, 65, 70, 66, 65, 70, 66, 65, 70, 66, + 65, 70, 65, 18, 38, 8, 255, 255, 3, 18, 32, 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 65, + 65, 67, 67, 69, 69, 49, 49, 51, 51, 53, 53, 55, 55, 57, 57, 66, 66, 68, 68, 70, 70, 50, + 12, 8, 162, 216, 255, 211, 5, 16, 192, 242, 227, 236, 2, 58, 13, 116, 101, 115, 116, + 95, 99, 104, 97, 105, 110, 95, 105, 100, ]; assert_eq!(got, want) @@ -263,37 +297,39 @@ mod tests { #[test] fn test_deserialization() { let dt = "2018-02-11T07:09:22.765Z".parse::>().unwrap(); - let t = TimeMsg { + let t = Timestamp { seconds: dt.timestamp(), nanos: dt.timestamp_subsec_nanos() as i32, }; let proposal = Proposal { - msg_type: SignedMsgType::Proposal.to_u32(), + r#type: SignedMsgType::Proposal as i32, height: 12345, round: 23456, timestamp: Some(t), pol_round: -1, block_id: Some(BlockId { - hash: b"hash".to_vec(), - parts_header: Some(PartsSetHeader { - total: 1_000_000, - hash: b"parts_hash".to_vec(), + hash: b"DEADBEEFDEADBEEFBAFBAFBAFBAFBAFA".to_vec(), + part_set_header: Some(PartSetHeader { + total: 65535, + hash: b"0022446688AACCEE1133557799BBDDFF".to_vec(), }), }), signature: vec![], }; let want = SignProposalRequest { proposal: Some(proposal), + chain_id: "test_chain_id".to_string(), }; let data = vec![ - 66, // len - 189, 228, 152, 226, // prefix - 10, 60, 8, 32, 16, 185, 96, 24, 160, 183, 1, 32, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 1, 42, 24, 10, 4, 104, 97, 115, 104, 18, 16, 8, 192, 132, 61, 18, 10, 112, - 97, 114, 116, 115, 95, 104, 97, 115, 104, 50, 12, 8, 162, 216, 255, 211, 5, 16, 192, - 242, 227, 236, 2, + 10, 110, 8, 32, 16, 185, 96, 24, 160, 183, 1, 32, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 1, 42, 74, 10, 32, 68, 69, 65, 68, 66, 69, 69, 70, 68, 69, 65, 68, 66, 69, + 69, 70, 66, 65, 70, 66, 65, 70, 66, 65, 70, 66, 65, 70, 66, 65, 70, 65, 18, 38, 8, 255, + 255, 3, 18, 32, 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 65, 65, 67, 67, 69, 69, 49, 49, + 51, 51, 53, 53, 55, 55, 57, 57, 66, 66, 68, 68, 70, 70, 50, 12, 8, 162, 216, 255, 211, + 5, 16, 192, 242, 227, 236, 2, 18, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110, + 95, 105, 100, ]; match SignProposalRequest::decode(data.as_ref()) { diff --git a/tendermint/src/amino_types/remote_error.rs b/tendermint/src/amino_types/remote_error.rs deleted file mode 100644 index 1561e9cff..000000000 --- a/tendermint/src/amino_types/remote_error.rs +++ /dev/null @@ -1,32 +0,0 @@ -use prost_amino_derive::Message; - -#[derive(Clone, PartialEq, Message)] -pub struct RemoteError { - #[prost_amino(sint32, tag = "1")] - pub code: i32, - #[prost_amino(string, tag = "2")] - pub description: String, -} - -/// Error codes for remote signer failures -// TODO(tarcieri): add these to Tendermint. See corresponding TODO here: -// -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[repr(i32)] -pub enum RemoteErrorCode { - /// Generic error code useful when the others don't apply - RemoteSignerError = 1, - - /// Double signing detected - DoubleSignError = 2, -} - -impl RemoteError { - /// Create a new double signing error with the given message - pub fn double_sign(height: i64) -> Self { - RemoteError { - code: RemoteErrorCode::DoubleSignError as i32, - description: format!("double signing requested at height: {}", height), - } - } -} diff --git a/tendermint/src/amino_types/signature.rs b/tendermint/src/amino_types/signature.rs index 3844381e1..5a8f820ae 100644 --- a/tendermint/src/amino_types/signature.rs +++ b/tendermint/src/amino_types/signature.rs @@ -1,7 +1,7 @@ use super::validate; use crate::{chain, consensus}; use bytes::BufMut; -use prost_amino::{DecodeError, EncodeError}; +use prost::{DecodeError, EncodeError}; /// Amino messages which are signable within a Tendermint network pub trait SignableMsg { @@ -20,36 +20,32 @@ pub trait SignableMsg { fn msg_type(&self) -> Option; } -/// Signed message types. This follows: -/// -#[derive(Copy, Clone, Debug)] +// Copied from use tendermint_proto::types::SignedMsgType +/// SignedMsgType is a type of signed message in the consensus. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] pub enum SignedMsgType { + Unknown = 0, /// Votes - PreVote, - - /// Commits - PreCommit, - + Prevote = 1, + Precommit = 2, /// Proposals - Proposal, + Proposal = 32, } -impl SignedMsgType { - pub fn to_u32(self) -> u32 { - match self { - // Votes - SignedMsgType::PreVote => 0x01, - SignedMsgType::PreCommit => 0x02, - // Proposals - SignedMsgType::Proposal => 0x20, - } +impl PartialEq for i32 { + fn eq(&self, other: &SignedMsgType) -> bool { + *self == *other as i32 } +} +impl SignedMsgType { #[allow(dead_code)] fn from(data: u32) -> Result { match data { - 0x01 => Ok(SignedMsgType::PreVote), - 0x02 => Ok(SignedMsgType::PreCommit), + 0x00 => Ok(SignedMsgType::Unknown), + 0x01 => Ok(SignedMsgType::Prevote), + 0x02 => Ok(SignedMsgType::Precommit), 0x20 => Ok(SignedMsgType::Proposal), _ => Err(DecodeError::new("Invalid vote type")), } diff --git a/tendermint/src/amino_types/time.rs b/tendermint/src/amino_types/time.rs deleted file mode 100644 index 16f3edce2..000000000 --- a/tendermint/src/amino_types/time.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Timestamps - -use crate::{ - error::Error, - time::{ParseTimestamp, Time}, -}; -use chrono::{TimeZone, Utc}; -use prost_amino_derive::Message; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -#[derive(Clone, PartialEq, Message)] -pub struct TimeMsg { - // TODO(ismail): switch to protobuf's well known type as soon as - // https://github.com/tendermint/go-amino/pull/224 was merged - // and tendermint caught up on the latest amino release. - #[prost_amino(int64, tag = "1")] - pub seconds: i64, - #[prost_amino(int32, tag = "2")] - pub nanos: i32, -} - -impl ParseTimestamp for TimeMsg { - fn parse_timestamp(&self) -> Result { - Ok(Utc.timestamp(self.seconds, self.nanos as u32).into()) - } -} - -impl From