Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Light client: further trait impls, improvements, and tests #63

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
4fef6a1
Use concrete basic types for time, hash, bytes, validator id
liamsi Sep 18, 2019
7a10257
cargo fmt
liamsi Sep 18, 2019
d9455af
into_iter unnecessarily moves the Vec (clippy)
liamsi Sep 19, 2019
1a232e5
Resolve a bunch of clippy warnings about:
liamsi Sep 22, 2019
d3ce237
make the lite client a module instead of a separate crate:
liamsi Sep 25, 2019
4b44d6a
add amino type for header Version
liamsi Sep 25, 2019
360a37a
working towards hashing the header: conversion between fields and their
liamsi Sep 25, 2019
3217eae
Header::hash works now (tested against JSON fixture only and yields:
liamsi Sep 25, 2019
4f53c44
remove todo
liamsi Sep 25, 2019
123adcb
implement lite::Validator trait
liamsi Sep 26, 2019
dba1303
implement lite::ValidatorSet trait
liamsi Sep 26, 2019
b7e9e46
implement lite::Vote trait
liamsi Sep 26, 2019
a0588a0
Merge branch 'bucky/lite' into lite_impl
ebuchman Nov 1, 2019
cc66c2b
bring back BufMut; fix clippy warnings
ebuchman Nov 1, 2019
37f9353
Address some review comments:
liamsi Nov 5, 2019
372cb4d
Consistency: From<&vote::Vote> same order as the fields in the struct
liamsi Nov 5, 2019
4140bbb
deduplicate encoding a hash: add a helper
liamsi Nov 5, 2019
9131c4e
deduplicate encoding hashes and varints: add a helper for each
liamsi Nov 5, 2019
15f8b02
simplify sign_bytes to return Vec<u8> instead of passing in a BufMut
liamsi Nov 5, 2019
e686c45
Add test for lite::Header.hash() in rpc tests
liamsi Nov 6, 2019
c941c95
wrap the CanonicalVote in a struct that contains the validator ID
liamsi Nov 6, 2019
6665e8e
simple_hash_from_byte_slices takes a Vec instead of slice
liamsi Nov 8, 2019
a6fcfae
cargo fmt
liamsi Nov 8, 2019
e34f1b6
WIP: pass in Vec<Vec<u8> instead of &Vec<Vec<u8>
liamsi Nov 9, 2019
d0e5e27
use Vec<Vec<u8>> with inner recursion method
liamsi Nov 9, 2019
459cceb
Merge remote-tracking branch 'remotes/origin/ismail/vector-simple_has…
liamsi Nov 9, 2019
3758cf4
merge in changes of #59 and simplify Header::hash
liamsi Nov 9, 2019
be377fa
simplify tests:
liamsi Nov 9, 2019
b4478b8
remove obsolete comment
liamsi Nov 9, 2019
8bbe23f
Further reduce boilerplate code by leveraging generics
liamsi Nov 9, 2019
23ed2dc
Merge branch 'ismail/vector-simple_hash_from_byte_slices' into lite_i…
liamsi Nov 11, 2019
5071ede
Create a trait for with a blanket impl instead of new type
liamsi Nov 11, 2019
3e48382
impl lite::Commit for commit::SignedHeader
yihuang Nov 13, 2019
3e987fe
Merge pull request #67 from yihuang/impl-lite-commit
liamsi Nov 13, 2019
e1a7560
Modify lite::Commit impl for SignedHeader to not include amino dep:
liamsi Nov 13, 2019
b156709
impl lite::ValidatorSetLookup
yihuang Nov 13, 2019
c8d17a9
Merge pull request #68 from yihuang/impl_validator_set_lookup
liamsi Nov 13, 2019
23042bb
Add a few methods to tests and deduplicate code for fn hash of
liamsi Nov 13, 2019
6ca48b5
Signing bytes need to encoded with encode_length_delimited
yihuang Nov 18, 2019
ef7410c
Merge pull request #69 from yihuang/fix-vote-signing-byte
liamsi Nov 19, 2019
8b1d3dc
Represent optional hash and block id as Option
yihuang Nov 13, 2019
9e00e99
add mock testing for lite client
yihuang Nov 19, 2019
39f295e
Merge pull request #66 from yihuang/lite_impl_simple_merkle_merged
liamsi Nov 27, 2019
2c6a36b
Merge pull request #72 from yihuang/mock_testing_lite_client
liamsi Nov 27, 2019
62159ca
parse empty commit
yihuang Nov 25, 2019
22e4b6e
Merge pull request #79 from yihuang/optional-last-commit
liamsi Nov 28, 2019
517068b
utilities
yihuang Nov 13, 2019
63e0928
Merge pull request #70 from yihuang/impl-utility-traits
liamsi Nov 28, 2019
bd26e74
Add TrustLevel as a trait; default behaviour is as before:
liamsi Dec 8, 2019
93089b3
minor clarifications: updated comments and clarified test-name
liamsi Dec 8, 2019
017c1ba
Add a verify method which is closer to the spec / the current golang …
liamsi Dec 9, 2019
2a2fc37
remove unused unit return type
liamsi Dec 9, 2019
b6834fd
add `/validators` to rpc client
liamsi Dec 9, 2019
a53098c
remove associated type (Vote) from SignedHeader
liamsi Dec 9, 2019
24c6e77
Merge remote-tracking branch 'remotes/origin/master' into lite_impl_s…
liamsi Dec 10, 2019
492c453
Merge in master
liamsi Dec 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tendermint/src/amino_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

pub mod block_id;
pub mod ed25519;
pub mod message;
pub mod ping;
pub mod proposal;
pub mod remote_error;
Expand Down
39 changes: 39 additions & 0 deletions tendermint/src/amino_types/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use prost_amino::encoding::encoded_len_varint;
use std::convert::TryInto;

/// Extend the original prost::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<u8>.
/// This can be useful when passing those bytes directly to a hasher, or,
/// to reduce boiler plate code when working with the encoded bytes.
///
/// Warning: Only use this method, if you are in control what will be encoded.
/// If there is an encoding error, this method will panic.
fn bytes_vec(&self) -> Vec<u8>
where
Self: Sized,
{
let mut res = Vec::with_capacity(self.encoded_len());
self.encode(&mut res).unwrap();
res
}

/// Encode prost-amino 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.
fn bytes_vec_length_delimited(&self) -> Vec<u8>
where
Self: Sized,
{
let len = self.encoded_len();
let mut res =
Vec::with_capacity(len + encoded_len_varint(len.try_into().expect("length overflow")));
self.encode_length_delimited(&mut res).unwrap();
res
}
}
impl<M: prost_amino::Message> AminoMessage for M {
// blanket impl
}
2 changes: 1 addition & 1 deletion tendermint/src/amino_types/proposal.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::convert::TryFrom;
use super::{
block_id::{BlockId, CanonicalBlockId, CanonicalPartSetHeader},
remote_error::RemoteError,
Expand All @@ -14,6 +13,7 @@ use crate::{
use bytes::BufMut;
use prost::{EncodeError, Message};
use signatory::{ed25519, Signature};
use std::convert::TryFrom;

#[derive(Clone, PartialEq, Message)]
pub struct Proposal {
Expand Down
22 changes: 9 additions & 13 deletions tendermint/src/amino_types/vote.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::convert::TryFrom;
use super::{
block_id::{BlockId, CanonicalBlockId, CanonicalPartSetHeader},
remote_error::RemoteError,
Expand All @@ -17,6 +16,7 @@ use crate::{
use bytes::BufMut;
use prost::{error::EncodeError, Message};
use signatory::{ed25519, Signature};
use std::convert::TryFrom;

const VALIDATOR_ADDR_SIZE: usize = 20;

Expand Down Expand Up @@ -78,7 +78,7 @@ impl From<&vote::Vote> for Vote {

impl block::ParseHeight for Vote {
fn parse_block_height(&self) -> Result<block::Height, Error> {
block::Height::try_from(self.height)
block::Height::try_from_i64(self.height)
}
}

Expand Down Expand Up @@ -124,7 +124,7 @@ impl chain::ParseId for CanonicalVote {

impl block::ParseHeight for CanonicalVote {
fn parse_block_height(&self) -> Result<block::Height, Error> {
block::Height::try_from(self.height)
block::Height::try_from_i64(self.height)
}
}

Expand Down Expand Up @@ -189,7 +189,7 @@ impl SignableMsg for SignVoteRequest {
fn consensus_state(&self) -> Option<consensus::State> {
match self.vote {
Some(ref v) => Some(consensus::State {
height: match block::Height::try_from(v.height) {
height: match block::Height::try_from_i64(v.height) {
Ok(h) => h,
Err(_err) => return None, // TODO(tarcieri): return an error?
},
Expand Down Expand Up @@ -246,9 +246,9 @@ impl ConsensusMessage for Vote {
mod tests {
use super::super::PartsSetHeader;
use super::*;
use crate::amino_types::message::AminoMessage;
use crate::amino_types::SignedMsgType;
use chrono::{DateTime, Utc};
use prost::Message;

#[test]
fn test_vote_serialization() {
Expand Down Expand Up @@ -334,8 +334,7 @@ mod tests {
vt_precommit.vote_type = SignedMsgType::PreCommit.to_u32(); // precommit
println!("{:?}", vt_precommit);
let cv_precommit = CanonicalVote::new(vt_precommit, "");
got = vec![];
cv_precommit.encode(&mut got).unwrap();
let got = AminoMessage::bytes_vec(&cv_precommit);
let want = vec![
0x8, // (field_number << 3) | wire_type
0x2, // PrecommitType
Expand All @@ -356,10 +355,9 @@ mod tests {
vt_prevote.round = 1;
vt_prevote.vote_type = SignedMsgType::PreVote.to_u32();

got = vec![];
let cv_prevote = CanonicalVote::new(vt_prevote, "");

cv_prevote.encode(&mut got).unwrap();
let got = AminoMessage::bytes_vec(&cv_prevote);

let want = vec![
0x8, // (field_number << 3) | wire_type
Expand All @@ -380,9 +378,8 @@ mod tests {
vt_no_type.height = 1;
vt_no_type.round = 1;

got = vec![];
let cv = CanonicalVote::new(vt_no_type, "");
cv.encode(&mut got).unwrap();
let got = AminoMessage::bytes_vec(&cv);

let want = vec![
0x11, // (field_number << 3) | wire_type
Expand All @@ -401,8 +398,7 @@ mod tests {
no_vote_type2.round = 1;

let with_chain_id = CanonicalVote::new(no_vote_type2, "test_chain_id");
got = vec![];
with_chain_id.encode(&mut got).unwrap();
got = AminoMessage::bytes_vec(&with_chain_id);
let want = vec![
0x11, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
Expand Down
31 changes: 27 additions & 4 deletions tendermint/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ pub mod parts;
mod size;

pub use self::{
commit::Commit,
header::Header,
commit::*,
header::{parse_non_empty_block_id, Header},
height::*,
id::{Id, ParseId},
meta::Meta,
size::Size,
};
use crate::{abci::transaction, evidence};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};

/// Blocks consist of a header, transactions, votes (the commit), and a list of
/// evidence of malfeasance (i.e. signing conflicting votes).
Expand All @@ -35,5 +35,28 @@ pub struct Block {
pub evidence: evidence::Data,

/// Last commit
pub last_commit: Commit,
#[serde(deserialize_with = "parse_non_empty_commit")]
pub last_commit: Option<Commit>,
}

pub(crate) fn parse_non_empty_commit<'de, D>(deserializer: D) -> Result<Option<Commit>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct TmpCommit {
#[serde(deserialize_with = "parse_non_empty_block_id")]
block_id: Option<Id>,
precommits: Option<Precommits>,
}

let commit = TmpCommit::deserialize(deserializer)?;
if commit.block_id.is_none() || commit.precommits.is_none() {
Ok(None)
} else {
Ok(Some(Commit {
block_id: commit.block_id.unwrap(),
precommits: commit.precommits.unwrap(),
}))
}
}
107 changes: 47 additions & 60 deletions tendermint/src/block/header.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Block headers
use amino_types::{message::AminoMessage, BlockId, ConsensusVersion, TimeMsg};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize};
use std::str::FromStr;

use crate::merkle::simple_hash_from_byte_slices;
use crate::{account, amino_types, block, chain, lite, Hash, Time};
use prost::Message;
use crate::{account, amino_types, block, chain, lite, serializers, Hash, Time};
use {
crate::serializers,
serde::{Deserialize, Serialize},
Expand Down Expand Up @@ -41,13 +44,16 @@ pub struct Header {
pub total_txs: u64,

/// Previous block info
pub last_block_id: block::Id,
#[serde(deserialize_with = "parse_non_empty_block_id")]
pub last_block_id: Option<block::Id>,

/// Commit from validators from the last block
pub last_commit_hash: Hash,
#[serde(deserialize_with = "serializers::parse_non_empty_hash")]
pub last_commit_hash: Option<Hash>,

/// Merkle root of transaction hashes
pub data_hash: Hash,
#[serde(deserialize_with = "serializers::parse_non_empty_hash")]
pub data_hash: Option<Hash>,

/// Validators for the current block
pub validators_hash: Hash,
Expand All @@ -59,13 +65,16 @@ pub struct Header {
pub consensus_hash: Hash,

/// State after txs from the previous block
pub app_hash: Hash,
#[serde(deserialize_with = "serializers::parse_non_empty_hash")]
pub app_hash: Option<Hash>,

/// Root hash of all results from the txs from the previous block
pub last_results_hash: Hash,
#[serde(deserialize_with = "serializers::parse_non_empty_hash")]
pub last_results_hash: Option<Hash>,

/// Hash of evidence included in the block
pub evidence_hash: Hash,
#[serde(deserialize_with = "serializers::parse_non_empty_hash")]
pub evidence_hash: Option<Hash>,

/// Original proposer of the block
pub proposer_address: account::Id,
Expand All @@ -89,58 +98,36 @@ impl lite::Header for Header {
}

fn hash(&self) -> Hash {
let mut version_enc = vec![];
// TODO: if there is an encoding problem this will
// Note that if there is an encoding problem this will
// panic (as the golang code would):
// https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/block.go#L393
// https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/encoding_helper.go#L9:6
// Instead, handle errors gracefully here.
amino_types::ConsensusVersion::from(&self.version)
.encode(&mut version_enc)
.unwrap();
let height_enc = encode_varint(self.height.value());
let mut time_enc = vec![];
amino_types::TimeMsg::from(self.time)
.encode(&mut time_enc)
.unwrap();
let chain_id_bytes = self.chain_id.as_bytes();
let chain_id_enc = bytes_enc(&chain_id_bytes);
let num_tx_enc = encode_varint(self.num_txs);
let total_tx_enc = encode_varint(self.total_txs);
let mut last_block_id_enc = vec![];
amino_types::BlockId::from(&self.last_block_id)
.encode(&mut last_block_id_enc)
.unwrap();
let last_commit_hash_enc = encode_hash(self.last_commit_hash);
let data_hash_enc = encode_hash(self.data_hash);
let validator_hash_enc = encode_hash(self.validators_hash);
let next_validator_hash_enc = encode_hash(self.next_validators_hash);
let consensus_hash_enc = encode_hash(self.consensus_hash);
let app_hash_enc = encode_hash(self.app_hash);
let last_result_hash_enc = encode_hash(self.last_results_hash);
let evidence_hash_enc = encode_hash(self.evidence_hash);
let proposer_address_bytes = self.proposer_address.as_bytes();
let proposer_address_enc = bytes_enc(&proposer_address_bytes);

let mut byteslices: Vec<&[u8]> = vec![];
byteslices.push(version_enc.as_slice());
byteslices.push(chain_id_enc.as_slice());
byteslices.push(height_enc.as_slice());
byteslices.push(time_enc.as_slice());
byteslices.push(num_tx_enc.as_slice());
byteslices.push(total_tx_enc.as_slice());
byteslices.push(last_block_id_enc.as_slice());
byteslices.push(last_commit_hash_enc.as_slice());
byteslices.push(data_hash_enc.as_slice());
byteslices.push(validator_hash_enc.as_slice());
byteslices.push(next_validator_hash_enc.as_slice());
byteslices.push(consensus_hash_enc.as_slice());
byteslices.push(app_hash_enc.as_slice());
byteslices.push(last_result_hash_enc.as_slice());
byteslices.push(evidence_hash_enc.as_slice());
byteslices.push(proposer_address_enc.as_slice());

Hash::Sha256(simple_hash_from_byte_slices(byteslices.as_slice()))

let mut byteslices: Vec<Vec<u8>> = Vec::with_capacity(16);
byteslices.push(AminoMessage::bytes_vec(&ConsensusVersion::from(
&self.version,
)));
byteslices.push(bytes_enc(self.chain_id.as_bytes()));
byteslices.push(encode_varint(self.height.value()));
byteslices.push(AminoMessage::bytes_vec(&TimeMsg::from(self.time)));
byteslices.push(encode_varint(self.num_txs));
byteslices.push(encode_varint(self.total_txs));
byteslices.push(
self.last_block_id
.as_ref()
.map_or(vec![], |id| AminoMessage::bytes_vec(&BlockId::from(id))),
);
byteslices.push(self.last_commit_hash.as_ref().map_or(vec![], encode_hash));
byteslices.push(self.data_hash.as_ref().map_or(vec![], encode_hash));
byteslices.push(encode_hash(&self.validators_hash));
byteslices.push(encode_hash(&self.next_validators_hash));
byteslices.push(encode_hash(&self.consensus_hash));
byteslices.push(self.app_hash.as_ref().map_or(vec![], encode_hash));
byteslices.push(self.last_results_hash.as_ref().map_or(vec![], encode_hash));
byteslices.push(self.evidence_hash.as_ref().map_or(vec![], encode_hash));
byteslices.push(bytes_enc(self.proposer_address.as_bytes()));

Hash::Sha256(simple_hash_from_byte_slices(byteslices))
}
}

Expand All @@ -166,10 +153,10 @@ pub struct Version {
}

fn bytes_enc(bytes: &[u8]) -> Vec<u8> {
let mut chain_id_enc = vec![];
let mut res = vec![];
prost_amino::encode_length_delimiter(bytes.len(), &mut chain_id_enc).unwrap();
chain_id_enc.append(&mut bytes.to_vec());
chain_id_enc
res.append(&mut bytes.to_vec());
res
}

fn encode_hash(hash: Hash) -> Vec<u8> {
Expand Down
2 changes: 1 addition & 1 deletion tendermint/src/block/height.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{

/// Block height for a particular chain (i.e. number of blocks created since
/// the chain began)
///
///
/// A height of 0 represents a chain which has not yet produced a block.
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Height(pub u64);
Expand Down
2 changes: 1 addition & 1 deletion tendermint/src/block/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ mod tests {
fn parses_hex_strings() {
let id = Id::from_str(EXAMPLE_SHA256_ID).unwrap();
assert_eq!(
id.hash.as_bytes().unwrap(),
id.hash.as_bytes(),
b"\x26\xC0\xA4\x1F\x32\x43\xC6\xBC\xD7\xAD\x2D\xFF\x8A\x8D\x83\xA7\
\x1D\x29\xD3\x07\xB5\x32\x6C\x22\x7F\x73\x4A\x1A\x51\x2F\xE4\x7D"
.as_ref()
Expand Down
5 changes: 3 additions & 2 deletions tendermint/src/genesis.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Genesis data

use crate::{chain, consensus, validator, Hash, Time};
use crate::{chain, consensus, serializers, validator, Hash, Time};
use serde::{Deserialize, Serialize};

/// Genesis data
Expand All @@ -19,7 +19,8 @@ pub struct Genesis<AppState = serde_json::Value> {
pub validators: Vec<validator::Info>,

/// App hash
pub app_hash: Hash,
#[serde(deserialize_with = "serializers::parse_non_empty_hash")]
pub app_hash: Option<Hash>,

/// App state
pub app_state: AppState,
Expand Down
Loading