diff --git a/Cargo.lock b/Cargo.lock index c654c3cd..03953f8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1581,6 +1581,16 @@ dependencies = [ "syn", ] +[[package]] +name = "prost-types" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" +dependencies = [ + "bytes", + "prost", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2269,6 +2279,7 @@ dependencies = [ "prost-amino", "prost-amino-derive", "prost-derive", + "prost-types", "rand", "rand_core", "rpassword", diff --git a/Cargo.toml b/Cargo.toml index 9631b75f..78e1cd60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,10 @@ ledger = { version = "0.2", optional = true } merlin = "2" once_cell = "1.4" prost = "0.6" -prost-derive = "0.6" prost-amino = "0.6" prost-amino-derive = "0.6" +prost-derive = "0.6" +prost-types = "0.6" rand_core = { version = "0.5", features = ["std"] } rpassword = { version = "5", optional = true } serde = { version = "1", features = ["serde_derive"] } diff --git a/src/connection/secret_connection.rs b/src/connection/secret_connection.rs index 6c4540db..a95b10c3 100644 --- a/src/connection/secret_connection.rs +++ b/src/connection/secret_connection.rs @@ -3,7 +3,7 @@ mod amino_types; mod kdf; mod nonce; -mod proto_types; +pub(crate) mod proto_types; mod protocol; mod public_key; @@ -36,9 +36,11 @@ use zeroize::Zeroizing; /// Size of the MAC tag pub const TAG_SIZE: usize = 16; +/// Maximum size of a message +pub const DATA_MAX_SIZE: usize = 1024; + /// 4 + 1024 == 1028 total frame size const DATA_LEN_SIZE: usize = 4; -const DATA_MAX_SIZE: usize = 1024; const TOTAL_FRAME_SIZE: usize = DATA_MAX_SIZE + DATA_LEN_SIZE; /// Generate a Secret Connection key at the given path diff --git a/src/connection/secret_connection/proto_types.rs b/src/connection/secret_connection/proto_types.rs index b56066a2..ba6b49d2 100644 --- a/src/connection/secret_connection/proto_types.rs +++ b/src/connection/secret_connection/proto_types.rs @@ -1,4 +1,4 @@ -//! Secret Connection Protobuf Types +//! Secret Connection Protobuf Types (tendermint.p2p.conn) //! //! Generated from: //! diff --git a/src/connection/secret_connection/protocol.rs b/src/connection/secret_connection/protocol.rs index ea9828bd..26d1c5f6 100644 --- a/src/connection/secret_connection/protocol.rs +++ b/src/connection/secret_connection/protocol.rs @@ -34,12 +34,12 @@ pub enum Version { impl Version { /// Does this version of Secret Connection use a transcript hash - pub(super) fn has_transcript(self) -> bool { + pub fn has_transcript(self) -> bool { self != Version::Legacy } /// Are messages encoded using Protocol Buffers? - pub(super) fn is_protobuf(self) -> bool { + pub fn is_protobuf(self) -> bool { match self { Version::V0_34 => true, Version::V0_33 | Version::Legacy => false, @@ -47,7 +47,7 @@ impl Version { } /// Encode the initial handshake message (i.e. first one sent by both peers) - pub(super) fn encode_initial_handshake(self, eph_pubkey: &EphemeralPublic) -> Vec { + pub fn encode_initial_handshake(self, eph_pubkey: &EphemeralPublic) -> Vec { if self.is_protobuf() { // Equivalent Go implementation: // https://github.com/tendermint/tendermint/blob/9e98c74/p2p/conn/secret_connection.go#L307-L312 @@ -72,7 +72,7 @@ impl Version { } /// Decode the initial handshake message - pub(super) fn decode_initial_handshake(self, bytes: &[u8]) -> Result { + pub fn decode_initial_handshake(self, bytes: &[u8]) -> Result { let eph_pubkey = if self.is_protobuf() { // Equivalent Go implementation: // https://github.com/tendermint/tendermint/blob/9e98c74/p2p/conn/secret_connection.go#L315-L323 @@ -111,7 +111,7 @@ impl Version { } /// Encode signature which authenticates the handshake - pub(super) fn encode_auth_signature( + pub fn encode_auth_signature( self, pub_key: &ed25519::PublicKey, signature: &ed25519::Signature, @@ -149,7 +149,7 @@ impl Version { } /// Get the length of the auth message response for this protocol version - pub(super) fn auth_sig_msg_response_len(self) -> usize { + pub fn auth_sig_msg_response_len(self) -> usize { if self.is_protobuf() { // 32 + 64 + (proto overhead = 1 prefix + 2 fields + 2 lengths + total length) 103 @@ -160,10 +160,7 @@ impl Version { } /// Decode signature message which authenticates the handshake - pub(super) fn decode_auth_signature( - self, - bytes: &[u8], - ) -> Result { + pub fn decode_auth_signature(self, bytes: &[u8]) -> Result { if self.is_protobuf() { // Parse Protobuf-encoded `AuthSigMessage` proto_types::AuthSigMessage::decode_length_delimited(bytes).map_err(|e| { diff --git a/src/error.rs b/src/error.rs index e3eb7099..6ad5a5da 100644 --- a/src/error.rs +++ b/src/error.rs @@ -185,6 +185,18 @@ impl std::error::Error for Error { } } +impl From for Error { + fn from(other: prost::DecodeError) -> Self { + ErrorKind::ProtocolError.context(other).into() + } +} + +impl From for Error { + fn from(other: prost::EncodeError) -> Self { + ErrorKind::ProtocolError.context(other).into() + } +} + impl From for Error { fn from(other: prost_amino::DecodeError) -> Self { ErrorKind::ProtocolError.context(other).into() diff --git a/src/lib.rs b/src/lib.rs index 8515cd32..9fc93f52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ pub mod error; pub mod key_utils; pub mod keyring; pub mod prelude; +pub mod proto_types; pub mod rpc; pub mod session; diff --git a/src/proto_types.rs b/src/proto_types.rs new file mode 100644 index 00000000..c922d36e --- /dev/null +++ b/src/proto_types.rs @@ -0,0 +1,249 @@ +//! PrivVal Protobuf Types (tendermint.privval) +//! +//! Generated from: +//! + +#![allow(missing_docs)] + +pub use prost_types::Timestamp; + +/// PublicKey defines the keys available for use with Tendermint Validators +#[derive(Clone, PartialEq, prost::Message)] +pub struct PublicKey { + /// Sum + #[prost(oneof = "public_key::Sum", tags = "1, 2")] + pub sum: Option, +} + +/// Public key types +pub mod public_key { + /// Public key enum + #[derive(Clone, PartialEq, prost::Oneof)] + pub enum Sum { + /// Ed25519 + #[prost(bytes, tag = "1")] + Ed25519(Vec), + + /// Secp256k1 + #[prost(bytes, tag = "2")] + Secp256k1(Vec), + } +} + +/// PartsetHeader +#[derive(Clone, PartialEq, prost::Message)] +pub struct PartSetHeader { + /// Total + #[prost(uint32, tag = "1")] + pub total: u32, + + /// Hash + #[prost(bytes, tag = "2")] + pub hash: Vec, +} + +/// BlockID +#[derive(Clone, PartialEq, prost::Message)] +pub struct BlockId { + /// Hash + #[prost(bytes, tag = "1")] + pub hash: Vec, + + /// PartSetHeader + #[prost(message, optional, tag = "2")] + pub part_set_header: Option, +} + +/// Vote represents a prevote, precommit, or commit vote from validators for +/// consensus. +#[derive(Clone, PartialEq, prost::Message)] +pub struct Vote { + #[prost(enumeration = "SignedMsgType", tag = "1")] + pub msg_type: i32, + #[prost(int64, tag = "2")] + pub height: i64, + #[prost(int32, tag = "3")] + pub round: i32, + #[prost(message, optional, tag = "4")] + pub block_id: Option, + #[prost(message, optional, tag = "5")] + pub timestamp: Option, + #[prost(bytes, tag = "6")] + pub validator_address: Vec, + #[prost(int32, tag = "7")] + pub validator_index: i32, + #[prost(bytes, tag = "8")] + pub signature: Vec, +} + +/// Proposal +#[derive(Clone, PartialEq, prost::Message)] +pub struct Proposal { + #[prost(enumeration = "SignedMsgType", tag = "1")] + pub msg_type: i32, + #[prost(int64, tag = "2")] + pub height: i64, + #[prost(int32, tag = "3")] + pub round: i32, + #[prost(int32, tag = "4")] + pub pol_round: i32, + #[prost(message, optional, tag = "5")] + pub block_id: Option, + #[prost(message, optional, tag = "6")] + pub timestamp: Option, + #[prost(bytes, tag = "7")] + pub signature: Vec, +} + +/// Remote Signer Error +#[derive(Clone, PartialEq, prost::Message)] +pub struct RemoteSignerError { + #[prost(int32, tag = "1")] + pub code: i32, + #[prost(string, tag = "2")] + pub description: String, +} + +/// 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, +} + +/// PubKeyResponse is a response message containing the public key. +#[derive(Clone, PartialEq, prost::Message)] +pub struct PubKeyResponse { + #[prost(message, optional, tag = "1")] + pub pub_key: Option, + #[prost(message, optional, tag = "2")] + pub error: Option, +} + +/// SignVoteRequest is a request to sign a vote +#[derive(Clone, PartialEq, prost::Message)] +pub struct SignVoteRequest { + #[prost(message, optional, tag = "1")] + pub vote: Option, + #[prost(string, tag = "2")] + pub chain_id: String, +} + +/// SignedVoteResponse is a response containing a signed vote or an error +#[derive(Clone, PartialEq, prost::Message)] +pub struct SignedVoteResponse { + #[prost(message, optional, tag = "1")] + pub vote: Option, + #[prost(message, optional, tag = "2")] + pub error: Option, +} + +/// SignProposalRequest is a request to sign a proposal +#[derive(Clone, PartialEq, prost::Message)] +pub struct SignProposalRequest { + #[prost(message, optional, tag = "1")] + pub proposal: Option, + #[prost(string, tag = "2")] + pub chain_id: String, +} + +/// SignedProposalResponse is response containing a signed proposal or an error +#[derive(Clone, PartialEq, prost::Message)] +pub struct SignedProposalResponse { + #[prost(message, optional, tag = "1")] + pub proposal: Option, + #[prost(message, optional, tag = "2")] + pub error: Option, +} + +/// PingRequest is a request to confirm that the connection is alive. +#[derive(Clone, PartialEq, prost::Message)] +pub struct PingRequest {} + +/// PingResponse is a response to confirm that the connection is alive. +#[derive(Clone, PartialEq, prost::Message)] +pub struct PingResponse {} + +/// Message +#[derive(Clone, PartialEq, prost::Message)] +pub struct Message { + #[prost(oneof = "message::Sum", tags = "1, 2, 3, 4, 5, 6, 7, 8")] + pub sum: Option, +} + +/// Messages +pub mod message { + #[derive(Clone, PartialEq, prost::Oneof)] + pub enum Sum { + /// PubKeyRequest + #[prost(message, tag = "1")] + PubKeyRequest(super::PubKeyRequest), + + /// PubKeyResponse + #[prost(message, tag = "2")] + PubKeyResponse(super::PubKeyResponse), + + /// SignVoteRequest + #[prost(message, tag = "3")] + SignVoteRequest(super::SignVoteRequest), + + /// SignedVoteResponse + #[prost(message, tag = "4")] + SignedVoteResponse(super::SignedVoteResponse), + + /// SignProposalRequest + #[prost(message, tag = "5")] + SignProposalRequest(super::SignProposalRequest), + + /// SignedProposalResponse + #[prost(message, tag = "6")] + SignedProposalResponse(super::SignedProposalResponse), + + /// PingRequest + #[prost(message, tag = "7")] + PingRequest(super::PingRequest), + + /// PingResponse + #[prost(message, tag = "8")] + PingResponse(super::PingResponse), + } +} + +/// Errors +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)] +#[repr(i32)] +pub enum Errors { + /// Unknown + Unknown = 0, + + /// Unexpected response + UnexpectedResponse = 1, + + /// No connection + NoConnection = 2, + + /// Connection timeout + ConnectionTimeout = 3, + + /// Read timeout + ReadTimeout = 4, + + /// Write timeout + WriteTimeout = 5, +} + +/// 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 + Unknown = 0, + + /// Votes + Prevote = 1, + + /// Precommit + Precommit = 2, + /// Proposals + Proposal = 32, +} diff --git a/src/rpc.rs b/src/rpc.rs index 1f45e33d..62706160 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -3,122 +3,221 @@ // TODO: docs for everything #![allow(missing_docs)] +use crate::{ + connection::secret_connection::{Version, DATA_MAX_SIZE}, + error::{Error, ErrorKind}, + prelude::*, + proto_types, +}; use bytes::Bytes; use once_cell::sync::Lazy; -use prost_amino::{ - encoding::{decode_varint, encoded_len_varint}, - Message, -}; +use prost::Message as _; +use prost_amino::{encoding::decode_varint, Message as _}; use sha2::{Digest, Sha256}; -use std::io::{self, Error, ErrorKind, Read}; -use tendermint::amino_types::*; +use std::io::Read; +use tendermint::amino_types::{ + self, PING_AMINO_NAME, PROPOSAL_AMINO_NAME, PUBKEY_AMINO_NAME, VOTE_AMINO_NAME, +}; -/// Maximum size of an RPC message -pub const MAX_MSG_LEN: usize = 1024; +// pre-compute registered types prefix (this is probably sth. our amino library should +// provide instead) -/// Requests to the KMS +static VOTE_PREFIX: Lazy> = Lazy::new(|| compute_prefix(VOTE_AMINO_NAME)); +static PROPOSAL_PREFIX: Lazy> = Lazy::new(|| compute_prefix(PROPOSAL_AMINO_NAME)); +static PUBKEY_PREFIX: Lazy> = Lazy::new(|| compute_prefix(PUBKEY_AMINO_NAME)); +static PING_PREFIX: Lazy> = Lazy::new(|| compute_prefix(PING_AMINO_NAME)); + +/// RPC requests to the KMS #[derive(Debug)] pub enum Request { /// Sign the given message - SignProposal(SignProposalRequest), - SignVote(SignVoteRequest), - ShowPublicKey(PubKeyRequest), + SignProposal(amino_types::SignProposalRequest), + SignVote(amino_types::SignVoteRequest), + ShowPublicKey(amino_types::PubKeyRequest), // PingRequest is a PrivValidatorSocket message to keep the connection alive. - ReplyPing(PingRequest), + ReplyPing(amino_types::PingRequest), } -/// Responses from the KMS -#[derive(Debug)] -pub enum Response { - /// Signature response - SignedVote(SignedVoteResponse), - SignedProposal(SignedProposalResponse), - Ping(PingResponse), - PublicKey(PubKeyResponse), -} +impl Request { + /// Read a request from the given readable + pub fn read(conn: &mut impl Read, protocol_version: Version) -> Result { + let msg = read_msg(conn)?; -pub trait TendermintRequest: SignableMsg { - fn build_response(self, error: Option) -> Response; -} + if protocol_version.is_protobuf() { + // Parse Protobuf-encoded request message + let msg = proto_types::Message::decode_length_delimited(msg.as_ref()) + .map_err(|e| { + format_err!(ErrorKind::ProtocolError, "malformed message packet: {}", e) + })? + .sum; -fn compute_prefix(name: &str) -> Vec { - let mut sh = Sha256::default(); - sh.update(name.as_bytes()); - let output = sh.finalize(); + // TODO(tarcieri): transition natively to protobuf types + match msg { + Some(proto_types::message::Sum::SignVoteRequest(req)) => { + Ok(Request::SignVote(amino_types::SignVoteRequest { + vote: req.vote.map(|vote| amino_types::vote::Vote { + vote_type: vote.msg_type as u32, + height: vote.height, + round: vote.round as i64, + block_id: vote.block_id.map(Into::into), + timestamp: vote.timestamp.map(|ts| amino_types::TimeMsg { + seconds: ts.seconds, + nanos: ts.nanos, + }), + validator_address: vote.validator_address, + validator_index: vote.validator_index as i64, + signature: vote.signature, + }), + })) + } + Some(proto_types::message::Sum::SignProposalRequest(req)) => { + Ok(Request::SignProposal(amino_types::SignProposalRequest { + proposal: req + .proposal + .map(|proposal| amino_types::proposal::Proposal { + msg_type: proposal.msg_type as u32, + height: proposal.height, + round: proposal.round as i64, + pol_round: proposal.pol_round as i64, + block_id: proposal.block_id.map(Into::into), + timestamp: proposal.timestamp.map(|ts| amino_types::TimeMsg { + seconds: ts.seconds, + nanos: ts.nanos, + }), + signature: proposal.signature, + }), + })) + } + Some(proto_types::message::Sum::PubKeyRequest(_)) => Ok(Request::ShowPublicKey( + amino_types::ed25519::PubKeyRequest {}, + )), + Some(proto_types::message::Sum::PingRequest(_)) => { + Ok(Request::ReplyPing(amino_types::ping::PingRequest {})) + } + _ => fail!(ErrorKind::ProtocolError, "invalid RPC message: {:?}", msg), + } + } else { + let amino_prefix = parse_amino_prefix(&msg)?; - output - .iter() - .filter(|&x| *x != 0x00) - .skip(3) - .filter(|&x| *x != 0x00) - .cloned() - .take(4) - .collect() + if amino_prefix == *VOTE_PREFIX { + let req = amino_types::SignVoteRequest::decode(msg.as_ref())?; + Ok(Request::SignVote(req)) + } else if amino_prefix == *PROPOSAL_PREFIX { + let req = amino_types::SignProposalRequest::decode(msg.as_ref())?; + Ok(Request::SignProposal(req)) + } else if amino_prefix == *PUBKEY_PREFIX { + let req = amino_types::PubKeyRequest::decode(msg.as_ref())?; + Ok(Request::ShowPublicKey(req)) + } else if amino_prefix == *PING_PREFIX { + let req = amino_types::PingRequest::decode(msg.as_ref())?; + Ok(Request::ReplyPing(req)) + } else { + fail!(ErrorKind::ProtocolError, "received unknown RPC message"); + } + } + } } -// pre-compute registered types prefix (this is probably sth. our amino library should -// provide instead) +/// RPC responses from the KMS +#[derive(Debug)] +pub enum Response { + /// Signature response + SignedVote(amino_types::SignedVoteResponse), + SignedProposal(amino_types::SignedProposalResponse), + Ping(amino_types::PingResponse), + PublicKey(amino_types::PubKeyResponse), +} -static VOTE_PREFIX: Lazy> = Lazy::new(|| compute_prefix(VOTE_AMINO_NAME)); -static PROPOSAL_PREFIX: Lazy> = Lazy::new(|| compute_prefix(PROPOSAL_AMINO_NAME)); -static PUBKEY_PREFIX: Lazy> = Lazy::new(|| compute_prefix(PUBKEY_AMINO_NAME)); -static PING_PREFIX: Lazy> = Lazy::new(|| compute_prefix(PING_AMINO_NAME)); +impl Response { + /// Encode response to bytes + pub fn encode(self, protocol_version: Version) -> Result, Error> { + if protocol_version.is_protobuf() { + let mut buf = Vec::new(); -impl Request { - /// Read a request from the given readable - pub fn read(r: &mut R) -> io::Result { - // this buffer contains the overall length and the amino prefix (for the registered types) - let mut buf = vec![0; MAX_MSG_LEN]; - let bytes_read = r.read(&mut buf)?; - if bytes_read < 4 { - return Err(Error::new( - ErrorKind::InvalidData, - "Did not read enough bytes to continue.", - )); - } + let msg = match self { + Response::SignedVote(resp) => { + proto_types::message::Sum::SignedVoteResponse(proto_types::SignedVoteResponse { + vote: resp.vote.map(|vote| proto_types::Vote { + msg_type: vote.vote_type as i32, + height: vote.height, + round: vote.round as i32, + block_id: vote.block_id.map(Into::into), + timestamp: vote.timestamp.map(|ts| proto_types::Timestamp { + seconds: ts.seconds, + nanos: ts.nanos, + }), + validator_address: vote.validator_address, + validator_index: vote.validator_index as i32, + signature: vote.signature, + }), + error: None, + }) + } + Response::SignedProposal(resp) => { + proto_types::message::Sum::SignedProposalResponse( + proto_types::SignedProposalResponse { + proposal: resp.proposal.map(|proposal| proto_types::Proposal { + msg_type: proposal.msg_type as i32, + height: proposal.height, + round: proposal.round as i32, + pol_round: proposal.pol_round as i32, + block_id: proposal.block_id.map(Into::into), + timestamp: proposal.timestamp.map(|ts| proto_types::Timestamp { + seconds: ts.seconds, + nanos: ts.nanos, + }), + signature: proposal.signature, + }), + error: None, + }, + ) + } + Response::Ping(_) => { + proto_types::message::Sum::PingResponse(proto_types::PingResponse {}) + } + Response::PublicKey(pk) => { + let pk = proto_types::PublicKey { + sum: Some(proto_types::public_key::Sum::Ed25519(pk.pub_key_ed25519)), + }; - let mut buf_amino: Bytes = Bytes::from(buf.clone()); - let len = decode_varint(&mut buf_amino).unwrap(); - if len > MAX_MSG_LEN as u64 { - return Err(Error::new(ErrorKind::InvalidData, "RPC message too large.")); - } - let amino_pre = buf_amino.slice(0..4); + proto_types::message::Sum::PubKeyResponse(proto_types::PubKeyResponse { + pub_key: Some(pk), + error: None, + }) + } + }; - let buf: Bytes = Bytes::from(buf); + proto_types::Message { sum: Some(msg) }.encode_length_delimited(&mut buf)?; + Ok(buf) + } else { + let mut buf = Vec::new(); - let total_len = encoded_len_varint(len).checked_add(len as usize).unwrap(); - let rem = buf.as_ref()[..total_len].to_vec(); - match amino_pre { - ref vt if *vt == *VOTE_PREFIX => { - Ok(Request::SignVote(SignVoteRequest::decode(rem.as_ref())?)) - } - ref pr if *pr == *PROPOSAL_PREFIX => Ok(Request::SignProposal( - SignProposalRequest::decode(rem.as_ref())?, - )), - ref pubk if *pubk == *PUBKEY_PREFIX => { - Ok(Request::ShowPublicKey(PubKeyRequest::decode(rem.as_ref())?)) - } - ref ping if *ping == *PING_PREFIX => { - Ok(Request::ReplyPing(PingRequest::decode(rem.as_ref())?)) + match self { + Response::SignedProposal(sp) => sp.encode(&mut buf)?, + Response::SignedVote(sv) => sv.encode(&mut buf)?, + Response::Ping(ping) => ping.encode(&mut buf)?, + Response::PublicKey(pk) => pk.encode(&mut buf)?, } - _ => Err(Error::new( - ErrorKind::InvalidData, - "Received unknown RPC message.", - )), + + Ok(buf) } } } -impl TendermintRequest for SignVoteRequest { - fn build_response(self, error: Option) -> Response { +pub trait TendermintRequest: amino_types::SignableMsg { + fn build_response(self, error: Option) -> Response; +} + +impl TendermintRequest for amino_types::SignVoteRequest { + fn build_response(self, error: Option) -> Response { let response = if let Some(e) = error { - SignedVoteResponse { + amino_types::SignedVoteResponse { vote: None, err: Some(e), } } else { - SignedVoteResponse { + amino_types::SignedVoteResponse { vote: self.vote, err: None, } @@ -128,15 +227,15 @@ impl TendermintRequest for SignVoteRequest { } } -impl TendermintRequest for SignProposalRequest { - fn build_response(self, error: Option) -> Response { +impl TendermintRequest for amino_types::SignProposalRequest { + fn build_response(self, error: Option) -> Response { let response = if let Some(e) = error { - SignedProposalResponse { + amino_types::SignedProposalResponse { proposal: None, err: Some(e), } } else { - SignedProposalResponse { + amino_types::SignedProposalResponse { proposal: self.proposal, err: None, } @@ -145,3 +244,69 @@ impl TendermintRequest for SignProposalRequest { Response::SignedProposal(response) } } + +impl From for amino_types::BlockId { + fn from(block_id: proto_types::BlockId) -> amino_types::BlockId { + amino_types::BlockId::new( + block_id.hash, + block_id + .part_set_header + .map(|psh| amino_types::PartsSetHeader { + total: psh.total as i64, + hash: psh.hash, + }), + ) + } +} + +impl From for proto_types::BlockId { + fn from(block_id: amino_types::BlockId) -> proto_types::BlockId { + proto_types::BlockId { + hash: block_id.hash, + part_set_header: block_id.parts_header.map(|psh| proto_types::PartSetHeader { + total: psh.total as u32, + hash: psh.hash, + }), + } + } +} + +/// Read a message from a Secret Connection +// TODO(tarcieri): extract this into Secret Connection +fn read_msg(conn: &mut impl Read) -> Result, Error> { + let mut buf = vec![0; DATA_MAX_SIZE]; + let buf_read = conn.read(&mut buf)?; + buf.truncate(buf_read); + Ok(buf) +} + +/// Parse the Amino prefix from a message +fn parse_amino_prefix(packet: &[u8]) -> Result, Error> { + let mut amino_buf = Bytes::from(packet.to_vec()); + decode_varint(&mut amino_buf)?; + + if amino_buf.len() < 4 { + fail!( + ErrorKind::ProtocolError, + "message too short to contain Amino header" + ); + } + + Ok(amino_buf[..4].into()) +} + +/// Compute Amino prefix +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/src/session.rs b/src/session.rs index bbbe0f14..366d0843 100644 --- a/src/session.rs +++ b/src/session.rs @@ -8,7 +8,6 @@ use crate::{ prelude::*, rpc::{Request, Response, TendermintRequest}, }; -use prost_amino::Message; use std::{fmt::Debug, os::unix::net::UnixStream, time::Instant}; use tendermint::{ amino_types::{PingResponse, PubKeyRequest, PubKeyResponse, RemoteError, SignedMsgType}, @@ -97,7 +96,7 @@ impl Session { /// Handle an incoming request from the validator fn handle_request(&mut self) -> Result { - let request = Request::read(&mut self.connection)?; + let request = Request::read(&mut self.connection, self.config.protocol_version)?; debug!( "[{}@{}] received request: {:?}", &self.config.chain_id, &self.config.addr, &request @@ -116,16 +115,8 @@ impl Session { &self.config.chain_id, &self.config.addr, &response ); - let mut buf = vec![]; - - match response { - Response::SignedProposal(sp) => sp.encode(&mut buf)?, - Response::SignedVote(sv) => sv.encode(&mut buf)?, - Response::Ping(ping) => ping.encode(&mut buf)?, - Response::PublicKey(pk) => pk.encode(&mut buf)?, - } - - self.connection.write_all(&buf)?; + let response_bytes = response.encode(self.config.protocol_version)?; + self.connection.write_all(&response_bytes)?; Ok(true) }