Skip to content

Commit

Permalink
Implement production header hasher
Browse files Browse the repository at this point in the history
  • Loading branch information
romac committed May 4, 2020
1 parent e8ea778 commit e233d8d
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 52 deletions.
1 change: 1 addition & 0 deletions light-spike/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ thiserror = "1.0.15"
futures = "0.3.4"
color-backtrace = "0.3.0"
tokio = "0.2.20"
prost-amino = "0.5.0"

[dev-dependencies]
serde_json = "1.0.51"
4 changes: 2 additions & 2 deletions light-spike/examples/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn main() {
denominator: 3,
},
trusting_period: Duration::from_secs(36000),
now: SystemTime::now(),
now: Time::now(),
};

let predicates = MockPredicates;
Expand Down Expand Up @@ -138,7 +138,7 @@ impl VerificationPredicates for MockPredicates {
&self,
header: &Header,
trusting_period: Duration,
now: SystemTime,
now: Time,
) -> Result<(), VerificationError> {
ProductionPredicates.is_within_trust_period(header, trusting_period, now)
}
Expand Down
10 changes: 5 additions & 5 deletions light-spike/src/components/demuxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use super::{io::*, scheduler::*, verifier::*};
use crate::prelude::*;

pub trait Clock {
fn now(&self) -> SystemTime;
fn now(&self) -> Time;
}

pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> SystemTime {
SystemTime::now()
fn now(&self) -> Time {
Time::now()
}
}

Expand Down Expand Up @@ -225,7 +225,7 @@ pub mod contracts {
pub fn trusted_state_contains_block_within_trusting_period(
trusted_store: &StoreReader<Trusted>,
trusting_period: Duration,
now: SystemTime,
now: Time,
) -> bool {
trusted_store
.all()
Expand Down Expand Up @@ -253,7 +253,7 @@ pub mod contracts {
fn is_within_trust_period(
light_block: &LightBlock,
trusting_period: Duration,
now: SystemTime,
now: Time,
) -> bool {
let header_time = light_block.header().bft_time;
let expires_at = header_time + trusting_period;
Expand Down
19 changes: 3 additions & 16 deletions light-spike/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
use crate::prelude::*;
use anomaly::BoxError;

pub mod header_hasher;
pub use self::header_hasher::*;

pub trait VotingPowerCalculator {
fn total_power_of(&self, validators: &ValidatorSet) -> u64;
fn voting_power_in(&self, commit: &Commit, validators: &ValidatorSet) -> u64;
Expand Down Expand Up @@ -43,19 +46,3 @@ impl CommitValidator for Box<dyn CommitValidator> {
self.as_ref().validate(commit, validators)
}
}

pub trait HeaderHasher {
fn hash(&self, header: &Header) -> Hash; // Or Error?
}

impl<T: HeaderHasher> HeaderHasher for &T {
fn hash(&self, header: &Header) -> Hash {
(*self).hash(header)
}
}

impl HeaderHasher for Box<dyn HeaderHasher> {
fn hash(&self, header: &Header) -> Hash {
self.as_ref().hash(header)
}
}
83 changes: 83 additions & 0 deletions light-spike/src/operations/header_hasher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use crate::prelude::*;

use tendermint::amino_types::{message::AminoMessage, BlockId, ConsensusVersion, TimeMsg};
use tendermint::merkle::simple_hash_from_byte_vectors;
use tendermint::Hash;

pub trait HeaderHasher {
fn hash(&self, header: &Header) -> Hash; // Or Error?
}

impl<T: HeaderHasher> HeaderHasher for &T {
fn hash(&self, header: &Header) -> Hash {
(*self).hash(header)
}
}

impl HeaderHasher for Box<dyn HeaderHasher> {
fn hash(&self, header: &Header) -> Hash {
self.as_ref().hash(header)
}
}

pub struct ProdHeaderHasher;

impl HeaderHasher for ProdHeaderHasher {
fn hash(&self, header: &Header) -> Hash {
amino_hash(header)
}
}

fn amino_hash(header: &Header) -> Hash {
// 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

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

Hash::Sha256(simple_hash_from_byte_vectors(fields_bytes))
}

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

fn encode_hash(hash: &Hash) -> Vec<u8> {
bytes_enc(hash.as_bytes())
}

fn encode_varint(val: u64) -> Vec<u8> {
let mut val_enc = vec![];
prost_amino::encoding::encode_varint(val, &mut val_enc);
val_enc
}
4 changes: 1 addition & 3 deletions light-spike/src/predicates.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::time::{Duration, SystemTime};

use crate::prelude::*;

pub mod errors;
Expand Down Expand Up @@ -36,7 +34,7 @@ pub trait VerificationPredicates {
&self,
header: &Header,
trusting_period: Duration,
now: SystemTime,
now: Time,
) -> Result<(), VerificationError>;

fn is_monotonic_bft_time(
Expand Down
19 changes: 7 additions & 12 deletions light-spike/src/predicates/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::time::SystemTime;

use anomaly::{BoxError, Context};
use serde::{Deserialize, Serialize};
use thiserror::Error;
Expand All @@ -8,11 +6,8 @@ use crate::prelude::*;

#[derive(Debug, Clone, Error, PartialEq, Serialize, Deserialize)]
pub enum VerificationError {
#[error("header from the future: header_time={header_time:?} now={now:?}")]
HeaderFromTheFuture {
header_time: SystemTime,
now: SystemTime,
},
#[error("header from the future: header_time={header_time} now={now}")]
HeaderFromTheFuture { header_time: Time, now: Time },
#[error("implementation specific")]
ImplementationSpecific,
#[error(
Expand Down Expand Up @@ -42,13 +37,13 @@ pub enum VerificationError {
},
#[error("non increasing height: got={got} expected={expected}")]
NonIncreasingHeight { got: Height, expected: Height },
#[error("non monotonic BFT time: header_bft_time={header_bft_time:?} trusted_header_bft_time={trusted_header_bft_time:?}")]
#[error("non monotonic BFT time: header_bft_time={header_bft_time} trusted_header_bft_time={trusted_header_bft_time}")]
NonMonotonicBftTime {
header_bft_time: SystemTime,
trusted_header_bft_time: SystemTime,
header_bft_time: Time,
trusted_header_bft_time: Time,
},
#[error("not withing trust period: at={at:?} now={now:?}")]
NotWithinTrustPeriod { at: SystemTime, now: SystemTime },
#[error("not withing trust period: at={at} now={now}")]
NotWithinTrustPeriod { at: Time, now: Time },
}

impl VerificationError {
Expand Down
2 changes: 1 addition & 1 deletion light-spike/src/predicates/production.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl VerificationPredicates for ProductionPredicates {
&self,
header: &Header,
trusting_period: Duration,
now: SystemTime,
now: Time,
) -> Result<(), VerificationError> {
let header_time = header.bft_time;
let expires_at = header_time + trusting_period;
Expand Down
4 changes: 3 additions & 1 deletion light-spike/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub use std::time::{Duration, SystemTime};
pub use std::time::Duration;

pub use tendermint::time::Time;

pub use crate::components::demuxer::*;
pub use crate::components::fork_detector::*;
Expand Down
44 changes: 32 additions & 12 deletions light-spike/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
use derive_more::Display;
use serde::{Deserialize, Serialize};
use std::time::SystemTime;

pub use tendermint::hash::Hash;
pub use tendermint::lite::Height;

use tendermint::{block::signed_header::SignedHeader as TMSignedHeader, lite::Header as _};
use tendermint::{
account::Id as AccountId, block::header::Version as HeaderVersion,
block::signed_header::SignedHeader as TMSignedHeader, block::Id as BlockId,
chain::Id as ChainId, lite::Header as _, Time,
};

use crate::prelude::*;

pub use tendermint::{hash::Hash, lite::Height};

#[derive(Clone, Debug, PartialEq, Display, Serialize, Deserialize)]
#[display(fmt = "{:?}", self)]
pub struct VerificationOptions {
pub trust_threshold: TrustThreshold,
pub trusting_period: Duration,
pub now: SystemTime,
pub now: Time,
}

impl VerificationOptions {
pub fn set_now(&self, now: SystemTime) -> Self {
pub fn set_now(&self, now: Time) -> Self {
Self {
now,
..self.clone()
Expand All @@ -29,11 +31,20 @@ impl VerificationOptions {
#[derive(Clone, Debug, PartialEq, Display, Serialize, Deserialize)]
#[display(fmt = "{:?}", self)]
pub struct Header {
pub version: HeaderVersion,
pub chain_id: ChainId,
pub height: Height,
pub bft_time: SystemTime,
pub bft_time: Time,
pub validators_hash: Hash,
pub next_validators_hash: Hash,
pub hash: Hash, // TODO: What if we don't have this
pub proposer_address: AccountId,
pub evidence_hash: Option<Hash>,
pub last_results_hash: Option<Hash>,
pub last_block_id: Option<BlockId>,
pub last_commit_hash: Option<Hash>,
pub data_hash: Option<Hash>,
pub consensus_hash: Hash,
pub app_hash: Vec<u8>,
}

#[derive(Clone, Debug, PartialEq, Display, Serialize, Deserialize)]
Expand Down Expand Up @@ -80,13 +91,22 @@ impl From<TMSignedHeader> for SignedHeader {
Self {
header: Header {
height: sh.header.height().into(),
bft_time: sh.header.bft_time().to_system_time().unwrap(),
bft_time: sh.header.bft_time(),
validators_hash: sh.header.validators_hash(),
next_validators_hash: sh.header.next_validators_hash(),
hash: sh.header.hash(),
version: sh.header.version,
chain_id: sh.header.chain_id,
proposer_address: sh.header.proposer_address,
evidence_hash: sh.header.evidence_hash,
last_results_hash: sh.header.last_results_hash,
last_block_id: sh.header.last_block_id,
last_commit_hash: sh.header.last_commit_hash,
data_hash: sh.header.data_hash,
consensus_hash: sh.header.consensus_hash,
app_hash: sh.header.app_hash,
},
commit: Commit {
header_hash: sh.header.hash(),
header_hash: sh.commit.block_id.hash,
commit: sh.commit,
},
validators: validators.clone(),
Expand Down
10 changes: 10 additions & 0 deletions tendermint/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::error::{Error, Kind};
use chrono::{DateTime, SecondsFormat, Utc};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Add;
use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tai64::TAI64N;
Expand Down Expand Up @@ -100,6 +101,15 @@ impl From<Time> for TAI64N {
}
}

impl Add<Duration> for Time {
type Output = Self;

fn add(self, rhs: Duration) -> Self::Output {
let st: SystemTime = self.into();
(st + rhs).into()
}
}

/// Parse `Timestamp` from a type
pub trait ParseTimestamp {
/// Parse `Timestamp`, or return an `Error` if parsing failed
Expand Down

0 comments on commit e233d8d

Please sign in to comment.