From 1de37708f8a8174a8ade0ddb9f9ae7cb788e90b3 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Sun, 29 Dec 2019 19:45:15 +0100 Subject: [PATCH] Light client: bisection, store, requester (#100) * Add bisection to light client module: - implement `can_trust` according to the latest spec CanTrustBisection (wip) - add a `Requester` and a `Store` trait - sue refs in parameters (as they are used in several places and we don't want to Copy) * Group params h1 & ha_next_vals into a type `TrustedState` - reduce params to clippy threshold (7); still a lot ... - rename vars for better readability * Add VerifyHeader logic from spec: - rename expired to is_within_trust_period to be closer to the spec - use TrusteState trait in check_support params - use TrusteState in lite tests * Review: doc improvements and minor improvements on naming * Review: doc improvements * More doc improvements * Minor doc improvement * MockHeader and test_is_within_trust_period (#104) * MockHeader and test_is_within_trust_period * remove Validator trait; make ValidatorSet associated to Commit (#105) * remove Validator trait; make ValidatorSet associated to Commit * remove stale comment * Fix clippy errors (#108) * Review comments: (#107) * Review comments: - update some comments / documentation - verify vals (not next vals) - store header and vals in trusted state * One offs & renaming related to trusted state * Rename & remove redundant store of trusted state: c - an_trust -> can_trust_bisection - remove a redundant check for trust period - remove redundant adding state to the store (added TODO) * Bucky/some light follow up (#109) * use ? instead of return Err(err) * remove unused errors * move a validation and add TODOs about them * check_support returns early for sequential case * drop let _ = * add comment to voting_power_in about signers Co-authored-by: Ethan Buchman --- tendermint/src/block/commit.rs | 2 +- tendermint/src/block/signed_header.rs | 21 +- tendermint/src/consensus/state.rs | 20 +- tendermint/src/lite/types.rs | 102 +++++---- tendermint/src/lite/verifier.rs | 316 ++++++++++++++++++++++---- tendermint/src/validator.rs | 28 ++- tendermint/tests/lite.rs | 48 +++- 7 files changed, 407 insertions(+), 130 deletions(-) diff --git a/tendermint/src/block/commit.rs b/tendermint/src/block/commit.rs index 9ecd4a0e7..b61f3608e 100644 --- a/tendermint/src/block/commit.rs +++ b/tendermint/src/block/commit.rs @@ -32,7 +32,7 @@ impl Precommits { /// Convert this collection of precommits into a vector pub fn into_vec(self) -> Vec> { - self.0.clone() + self.0 } /// Iterate over the precommits in the collection diff --git a/tendermint/src/block/signed_header.rs b/tendermint/src/block/signed_header.rs index 42689f9ee..1d0fb717e 100644 --- a/tendermint/src/block/signed_header.rs +++ b/tendermint/src/block/signed_header.rs @@ -1,12 +1,7 @@ //! SignedHeader contains commit and and block header. //! It is what the rpc endpoint /commit returns and hence can be used by a //! light client. -use crate::{ - block, hash, lite, - lite::types::Validator, - lite::{Error, ValidatorSet}, - vote::SignedVote, -}; +use crate::{block, hash, lite, lite::Error, validator::Set, vote::SignedVote}; use serde::{Deserialize, Serialize}; /// Signed block headers @@ -54,14 +49,16 @@ impl SignedHeader { } impl lite::Commit for SignedHeader { + type ValidatorSet = Set; + fn header_hash(&self) -> hash::Hash { self.commit.block_id.hash } + fn votes_len(&self) -> usize { + self.commit.precommits.len() + } - fn voting_power_in(&self, validators: &V) -> Result - where - V: ValidatorSet, - { + fn voting_power_in(&self, validators: &Set) -> Result { // NOTE we don't know the validators that committed this block, // so we have to check for each vote if its validator is already known. let mut signed_power = 0u64; @@ -93,8 +90,4 @@ impl lite::Commit for SignedHeader { Ok(signed_power) } - - fn votes_len(&self) -> usize { - self.commit.precommits.len() - } } diff --git a/tendermint/src/consensus/state.rs b/tendermint/src/consensus/state.rs index 00fe29795..37f18d499 100644 --- a/tendermint/src/consensus/state.rs +++ b/tendermint/src/consensus/state.rs @@ -49,18 +49,14 @@ impl fmt::Display for State { impl Ord for State { fn cmp(&self, other: &State) -> Ordering { - if self.height < other.height { - Ordering::Less - } else if self.height == other.height { - if self.round < other.round { - Ordering::Less - } else if self.round == other.round { - self.step.cmp(&other.step) - } else { - Ordering::Greater - } - } else { - Ordering::Greater + match self.height.cmp(&other.height) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => match self.round.cmp(&other.round) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => self.step.cmp(&other.step), + }, } } } diff --git a/tendermint/src/lite/types.rs b/tendermint/src/lite/types.rs index e55860d6b..9d0e74e26 100644 --- a/tendermint/src/lite/types.rs +++ b/tendermint/src/lite/types.rs @@ -1,14 +1,6 @@ //! All traits that are necessary and need to be implemented to use the main //! verification logic in `super::verifier` for a light client. -// TODO can we abstract this away and use a generic identifier instead ? -// Ie. something that just implements Eq ? -// (Ismail): a really easy solution would be have a trait that expects an -// as_bytes(&self) -> &[u8] method. It's unlikely that a hash won't be -// representable as bytes, or an Id (that is basically also a hash) -// but this feels a a bit like cheating -use crate::account::Id; - use crate::block::Height; use crate::Hash; @@ -16,15 +8,19 @@ use failure::_core::fmt::Debug; use std::time::SystemTime; /// TrustedState stores the latest state trusted by a lite client, -/// including the last header and the validator set to use to verify -/// the next header. -pub struct TrustedState -where - H: Header, - V: ValidatorSet, -{ - pub last_header: H, // height H-1 - pub validators: V, // height H +/// including the last header (at height h-1) and the validator set +/// (at height h) to use to verify the next header. +pub trait TrustedState { + type LastHeader: SignedHeader; + type ValidatorSet: ValidatorSet; + + /// Initialize the TrustedState with the given signed header and validator set. + /// Note that if the height of the passed in header is h-1, the passed in validator set + /// must have been requested for height h. + fn new(last_header: &Self::LastHeader, vals: &Self::ValidatorSet) -> Self; + + fn last_header(&self) -> &Self::LastHeader; // height H-1 + fn validators(&self) -> &Self::ValidatorSet; // height H } /// SignedHeader bundles a Header and a Commit for convenience. @@ -61,17 +57,12 @@ pub trait Header: Debug { /// It also provides a lookup method to fetch a validator by /// its identifier. pub trait ValidatorSet { - type Validator: Validator; - /// Hash of the validator set. fn hash(&self) -> Hash; /// Total voting power of the set fn total_power(&self) -> u64; - /// Fetch validator via their ID (ie. their address). - fn validator(&self, val_id: Id) -> Option; - /// Return the number of validators in this validator set. fn len(&self) -> usize; @@ -79,30 +70,25 @@ pub trait ValidatorSet { fn is_empty(&self) -> bool; } -/// Validator has a voting power and can verify -/// its own signatures. Note it must have implicit access -/// to its public key material to verify signatures. -pub trait Validator { - fn power(&self) -> u64; - fn verify_signature(&self, sign_bytes: &[u8], signature: &[u8]) -> bool; -} - /// Commit is proof a Header is valid. /// It has an underlying Vote type with the relevant vote data /// for verification. pub trait Commit { + type ValidatorSet: ValidatorSet; + /// Hash of the header this commit is for. fn header_hash(&self) -> Hash; /// Compute the voting power of the validators that correctly signed the commit, - /// have according to their voting power in the passed in validator set. + /// according to their voting power in the passed in validator set. /// Will return an error in case an invalid signature was included. /// - /// This method corresponds to the (pure) auxiliary function int the spec: + /// This method corresponds to the (pure) auxiliary function in the spec: /// `votingpower_in(signers(h.Commit),h.Header.V)`. - fn voting_power_in(&self, vals: &V) -> Result - where - V: ValidatorSet; + /// Note this expects the Commit to be able to compute `signers(h.Commit)`, + /// ie. the identity of the validators that signed it, so they + /// can be cross-referenced with the given `vals`. + fn voting_power_in(&self, vals: &Self::ValidatorSet) -> Result; /// Return the number of votes included in this commit /// (including nil/empty votes). @@ -122,18 +108,54 @@ pub trait TrustThreshold { } } -#[derive(Debug)] +/// Requester can be used to request `SignedHeaders` and `ValidatorSet`s for a +/// given height, e.g., by talking to a tendermint fullnode through RPC. +pub trait Requester { + // TODO(Liamsi): consider putting this trait and the Store into a separate module / file... + type SignedHeader: SignedHeader; + type ValidatorSet: ValidatorSet; + + /// Request the signed header at height h. + fn signed_header(&self, h: H) -> Result + where + H: Into; + + /// Request the validator set at height h. + fn validator_set(&self, h: H) -> Result + where + H: Into; +} + +/// This store can be used to store all the headers that have passed basic verification +/// and that are within the light client's trust period. +pub trait Store { + type TrustedState: TrustedState; + + /// Add this state (header at height h, validators at height h+1) as trusted to the store. + fn add(&mut self, trusted: &Self::TrustedState) -> Result<(), Error>; + + /// Retrieve the trusted state at height h if it exists. + /// If it does not exist return an error. + fn get(&self, h: Height) -> Result<&Self::TrustedState, Error>; + + /// Retrieve the trusted signed header with the largest height h' with h' <= h, if it exists. + /// If it does not exist return an error. + fn get_smaller_or_equal(&self, h: Height) -> Result; +} + +#[derive(Debug, PartialEq)] pub enum Error { Expired, DurationOutOfRange, - NonSequentialHeight, - NonIncreasingHeight, + + InvalidSignature, // TODO: deduplicate with ErrorKind::SignatureInvalid InvalidValidatorSet, InvalidNextValidatorSet, InvalidCommitValue, // commit is not for the header we expected InvalidCommitLength, - InvalidSignature, - InsufficientVotingPower, + InsufficientVotingPower, // TODO(Liamsi): change to same name as spec if this changes (curently ErrTooMuchChange) + + RequestFailed, } diff --git a/tendermint/src/lite/verifier.rs b/tendermint/src/lite/verifier.rs index 436edf6fd..46d36015d 100644 --- a/tendermint/src/lite/verifier.rs +++ b/tendermint/src/lite/verifier.rs @@ -8,18 +8,26 @@ //! // looks using the types and methods in this crate/module. //!``` -use crate::lite::{Commit, Error, Header, SignedHeader, TrustThreshold, ValidatorSet}; +use crate::block::Height; +use crate::lite::{ + Commit, Error, Header, Requester, SignedHeader, Store, TrustThreshold, TrustedState, + ValidatorSet, +}; use std::time::{Duration, SystemTime}; /// Returns an error if the header has expired according to the given /// trusting_period and current time. If so, the verifier must be reset subjectively. -pub fn expired(last_header: &H, trusting_period: Duration, now: SystemTime) -> Result<(), Error> +pub fn is_within_trust_period( + last_header: &H, + trusting_period: &Duration, + now: &SystemTime, +) -> Result<(), Error> where H: Header, { match now.duration_since(last_header.bft_time().into()) { Ok(passed) => { - if passed > trusting_period { + if passed > *trusting_period { return Err(Error::Expired); } Ok(()) @@ -42,66 +50,93 @@ where } /// Captures the skipping condition, i.e., it defines when we can trust the header -/// h2 based on header h1. -/// Note that h1 and h2 have already passed basic validation by calling `verify`. +/// h2 based on a known trusted state. +/// Note that the trusted header included in the trusted state and h2 have already +/// passed basic validation by calling `verify`. /// `Error::InsufficientVotingPower`is returned when there is not enough intersection /// between validator sets to have skipping condition true. -pub fn check_support( - h1: &S, - h1_next_vals: &V, - h2: &S, - trust_threshold: L, - trusting_period: Duration, - now: SystemTime, +pub fn check_support( + trusted_state: &TS, + h2: &SH, + trust_threshold: &L, + trusting_period: &Duration, + now: &SystemTime, ) -> Result<(), Error> where - S: SignedHeader, - V: ValidatorSet, + TS: TrustedState, + SH: SignedHeader, L: TrustThreshold, + C: Commit, { - if let Err(err) = expired(h1.header(), trusting_period, now) { - return Err(err); - } + let h1 = trusted_state.last_header(); + let h1_next_vals = trusted_state.validators(); - if h2.header().height() == h1.header().height().increment() - && h2.header().validators_hash() != h1_next_vals.hash() - { - return Err(Error::InvalidNextValidatorSet); + // TODO(EB): can we move all these checks? it seems the + // is_within doesn't need to happen here and the sequential check + // for vals hash should be part of validate_vals_and_commit. then this function + // is basically just verify_commit_trusting. Would need to update spec as well. + // In sequential case, would still need to return early or not call check_support at all. + is_within_trust_period(h1.header(), trusting_period, now)?; + + // check the sequential case + if h2.header().height() == h1.header().height().increment() { + if h2.header().validators_hash() == h1_next_vals.hash() { + // It's sequential, so verify_commit_trusting would be + // redundant with verify since the validators are the same + // when we're not skipping. + return Ok(()); + } else { + return Err(Error::InvalidNextValidatorSet); + } } + + // check if enough trusted validators signed to skip to the new height. verify_commit_trusting(h1_next_vals, h2.commit(), trust_threshold) } /// Validate the validators and commit against the header. +// TODO(EB): consider making this a method on Commit so the details are hidden, +// and so we can remove the votes_len() method (that check would be part of the +// methods implementation). These checks aren't reflected +// explicitly in the spec yet, only in the sentence "Additional checks should +// be done in the implementation to ensure header is well formed". fn validate_vals_and_commit(header: &H, commit: &C, vals: &V) -> Result<(), Error> where H: Header, V: ValidatorSet, C: Commit, { - // ensure the validators in the header matches what we expect from our state. + // ensure the header validators match these validators if header.validators_hash() != vals.hash() { return Err(Error::InvalidValidatorSet); } - // ensure the commit matches the header. + // ensure the header matches the commit if header.hash() != commit.header_hash() { return Err(Error::InvalidCommitValue); } + // ensure the validator size matches the commit size + // NOTE: this is commit structure specifc and should be + // hidden from the light client ... + if vals.len() != commit.votes_len() { + return Err(Error::InvalidCommitLength); + } + Ok(()) } /// Verify the commit is valid from the given validators for the header. -pub fn verify(signed_header: &SH, validators: &V) -> Result<(), Error> +pub fn verify(signed_header: &SH, validators: &C::ValidatorSet) -> Result<(), Error> where - SH: SignedHeader, - V: ValidatorSet, + SH: SignedHeader, + C: Commit, { let header = signed_header.header(); let commit = signed_header.commit(); - if let Err(e) = validate_vals_and_commit(header, commit, validators) { - return Err(e); - } + + // basic validatity checks that header, commit, and vals match up + validate_vals_and_commit(header, commit, validators)?; // ensure that +2/3 validators signed correctly verify_commit_full(validators, commit) @@ -109,16 +144,11 @@ where /// Verify that +2/3 of the correct validator set signed this commit. /// NOTE: these validators are expected to be the correct validators for the commit. -fn verify_commit_full(vals: &V, commit: &C) -> Result<(), Error> +fn verify_commit_full(vals: &C::ValidatorSet, commit: &C) -> Result<(), Error> where - V: ValidatorSet, C: Commit, { let total_power = vals.total_power(); - if vals.len() != commit.votes_len() { - return Err(Error::InvalidCommitLength); - } - let signed_power = commit.voting_power_in(vals)?; // check the signers account for +2/3 of the voting power @@ -133,13 +163,12 @@ where /// NOTE the given validators do not necessarily correspond to the validator set for this commit, /// but there may be some intersection. The trust_level parameter allows clients to require more /// than +1/3 by implementing the TrustLevel trait accordingly. -pub fn verify_commit_trusting( - validators: &V, +pub fn verify_commit_trusting( + validators: &C::ValidatorSet, commit: &C, - trust_level: L, + trust_level: &L, ) -> Result<(), Error> where - V: ValidatorSet, C: Commit, L: TrustThreshold, { @@ -154,3 +183,212 @@ where Ok(()) } + +/// Returns Ok if we can trust the passed in (untrusted) header +/// based on the given trusted state, otherwise returns an Error. +fn can_trust_bisection( + trusted_state: &TS, // h1 in spec + untrusted_header: &SH, // h2 in spec + trust_threshold: &L, + trusting_period: &Duration, + now: &SystemTime, + req: &R, + store: &mut S, +) -> Result<(), Error> +where + TS: TrustedState, + SH: SignedHeader, + C: Commit, + L: TrustThreshold, + S: Store, + R: Requester, +{ + // can we trust the still untrusted header based on the given trusted state? + match check_support( + trusted_state, + untrusted_header, + trust_threshold, + trusting_period, + now, + ) { + Ok(_) => { + let untrusted_next_vals = + req.validator_set(untrusted_header.header().height().increment())?; + let untrusted_state = TS::new(untrusted_header, &untrusted_next_vals); + store.add(&untrusted_state)?; + return Ok(()); + } + Err(e) => { + if e != Error::InsufficientVotingPower { + return Err(e); + } + } + } + + // Here we can't trust the passed in untrusted header based on the known trusted state. + // Run bisection: try again with a pivot header whose height is in the + // middle of the trusted height and the desired height (h2.height). + + let trusted_header = trusted_state.last_header(); + let trusted_height: u64 = trusted_header.header().height().value(); + let untrusted_height: u64 = untrusted_header.header().height().value(); + let pivot: u64 = (trusted_height + untrusted_height) / 2; + let pivot_header = req.signed_header(pivot)?; + let pivot_vals = req.validator_set(pivot)?; + + verify(&pivot_header, &pivot_vals)?; + + // Can we trust pivot header based on trusted_state? + can_trust_bisection( + trusted_state, + &pivot_header, + trust_threshold, + trusting_period, + now, + req, + store, + )?; + // Trust the header in between the trusted and (still) untrusted height: + let pivot_next_vals = req.validator_set(pivot + 1)?; + let pivot_trusted = TS::new(&pivot_header, &pivot_next_vals); + store.add(&pivot_trusted)?; + + // Can we trust the (still) untrusted header based on the (now trusted) "pivot header"? + can_trust_bisection( + &pivot_trusted, + untrusted_header, + trust_threshold, + trusting_period, + now, + req, + store, + )?; + // Add header (and corresponding next validators) to trusted state store: + let untrusted_next_vals = req.validator_set(untrusted_header.header().height().increment())?; + let untrusted_state = TS::new(untrusted_header, &untrusted_next_vals); + store.add(&untrusted_state)?; + + Ok(()) +} + +/// This function captures the high level logic of the light client verification, i.e., +/// an application call to the light client module to (optionally download) and +/// verify a header for some height. +pub fn verify_header( + height: Height, + trust_threshold: &L, + trusting_period: &Duration, + now: &SystemTime, + req: &R, + store: &mut S, +) -> Result<(), Error> +where + TS: TrustedState, + L: TrustThreshold, + S: Store, + R: Requester, + C: Commit, + SH: SignedHeader, +{ + // Check if we already trusted a header at the given height and it didn't expire: + if let Ok(ts2) = store.get(height) { + is_within_trust_period(ts2.last_header().header(), trusting_period, now)? + } + + // We haven't trusted a header at given height yet. Request it: + let sh2 = req.signed_header(height)?; + let sh2_vals = req.validator_set(height)?; + verify(&sh2, &sh2_vals)?; + is_within_trust_period(sh2.header(), trusting_period, now)?; + + // Get the highest trusted header with height lower than sh2's. + let sh1_trusted = store.get_smaller_or_equal(height)?; + can_trust_bisection( + &sh1_trusted, + &sh2, + trust_threshold, + trusting_period, + now, + req, + store, + )?; + + // TODO(Liamsi): The spec re-checks if we are still in trust period here + // and stores the now trusted header again. + // Figure out if we want to store it here or in can_trust_bisection! + // My understanding is: with the current impl, we either bubbled up an + // error, or, successfully added sh2 (and its nex vals) to the trusted store here. + + Ok(()) +} + +mod tests { + use super::*; + use crate::{hash::Algorithm, Hash}; + use serde::Serialize; + use sha2::{Digest, Sha256}; + + #[derive(Debug, Serialize)] + struct MockHeader { + height: u64, + time: SystemTime, + vals: Hash, + next_vals: Hash, + } + + impl MockHeader { + fn new(height: u64, time: SystemTime, vals: Hash, next_vals: Hash) -> MockHeader { + MockHeader { + height, + time, + vals, + next_vals, + } + } + } + + impl Header for MockHeader { + type Time = SystemTime; + + fn height(&self) -> Height { + Height::from(self.height) + } + fn bft_time(&self) -> Self::Time { + self.time + } + fn validators_hash(&self) -> Hash { + self.vals + } + fn next_validators_hash(&self) -> Hash { + self.next_vals + } + fn hash(&self) -> Hash { + let encoded = serde_json::to_vec(self).unwrap(); + let hashed = Sha256::digest(&encoded); + Hash::new(Algorithm::Sha256, &hashed).unwrap() + } + } + + fn fixed_hash() -> Hash { + Hash::new(Algorithm::Sha256, &Sha256::digest(&[5])).unwrap() + } + + #[test] + fn test_is_within_trust_period() { + let header_time = SystemTime::UNIX_EPOCH; + let period = Duration::new(100, 0); + let now = header_time + Duration::new(10, 0); + + // less than the period, OK + let header = MockHeader::new(4, header_time, fixed_hash(), fixed_hash()); + assert!(is_within_trust_period(&header, &period, &now).is_ok()); + + // equal to the period, OK + let now = header_time + period; + assert!(is_within_trust_period(&header, &period, &now).is_ok()); + + // greater than the period, not OK + let now = header_time + period + Duration::new(1, 0); + assert!(is_within_trust_period(&header, &period, &now).is_err()); + } +} diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index d2583aa28..dc2090ec6 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -26,9 +26,17 @@ impl Set { } } -impl lite::ValidatorSet for Set { - type Validator = Info; +impl Set { + /// Returns the validator with the given Id if its in the Set. + pub fn validator(&self, val_id: account::Id) -> Option { + self.validators + .iter() + .find(|val| val.address == val_id) + .cloned() + } +} +impl lite::ValidatorSet for Set { /// Compute the Merkle root of the validator set fn hash(&self) -> Hash { let validator_bytes: Vec> = self @@ -45,13 +53,6 @@ impl lite::ValidatorSet for Set { }) } - fn validator(&self, val_id: account::Id) -> Option { - self.validators - .iter() - .find(|val| val.address == val_id) - .cloned() - } - fn len(&self) -> usize { self.validators.len() } @@ -87,12 +88,15 @@ pub struct Info { pub proposer_priority: Option, } -impl lite::Validator for Info { - fn power(&self) -> u64 { +impl Info { + /// Return the voting power of the validator. + pub fn power(&self) -> u64 { self.voting_power.value() } - fn verify_signature(&self, sign_bytes: &[u8], signature: &[u8]) -> bool { + /// Verify the given signature against the given sign_bytes using the validators + /// public key. + pub fn verify_signature(&self, sign_bytes: &[u8], signature: &[u8]) -> bool { if let Some(pk) = &self.pub_key.ed25519() { let verifier = Ed25519Verifier::from(pk); if let Ok(sig) = ed25519::Signature::from_bytes(signature) { diff --git a/tendermint/tests/lite.rs b/tendermint/tests/lite.rs index 4aba58546..a799990c9 100644 --- a/tendermint/tests/lite.rs +++ b/tendermint/tests/lite.rs @@ -1,6 +1,7 @@ use serde::{de::Error as _, Deserialize, Deserializer, Serialize}; use serde_json; use std::{fs, path::PathBuf}; +use tendermint::lite::TrustedState; use tendermint::{block::signed_header::SignedHeader, lite, validator, validator::Set, Time}; #[derive(Serialize, Deserialize, Clone, Debug)] @@ -68,13 +69,38 @@ fn header_tests_verify() { run_test_cases(cases); } +struct Trusted { + last_signed_header: SignedHeader, + validators: Set, +} + +impl lite::TrustedState for Trusted { + type LastHeader = SignedHeader; + type ValidatorSet = Set; + + fn new(last_header: &Self::LastHeader, vals: &Self::ValidatorSet) -> Self { + Self { + last_signed_header: last_header.clone(), + validators: vals.clone(), + } + } + + fn last_header(&self) -> &Self::LastHeader { + &self.last_signed_header + } + + fn validators(&self) -> &Self::ValidatorSet { + &self.validators + } +} + fn run_test_cases(cases: TestCases) { for (_, tc) in cases.test_cases.iter().enumerate() { - let mut trusted_signed_header = &tc.initial.signed_header; - let mut trusted_next_vals = tc.initial.clone().next_validator_set; + let trusted_next_vals = tc.initial.clone().next_validator_set; + let mut trusted_state = Trusted::new(&tc.initial.signed_header.clone(), &trusted_next_vals); let trusting_period: std::time::Duration = tc.initial.clone().trusting_period.into(); let now = tc.initial.now; - let expexts_err = match &tc.expected_output { + let expects_err = match &tc.expected_output { Some(eo) => eo.eq("error"), None => false, }; @@ -91,21 +117,19 @@ fn run_test_cases(cases: TestCases) { let mut check_support_res: Result<(), lite::Error> = Ok(()); if h2_verif_res.is_ok() { check_support_res = lite::check_support( - trusted_signed_header, - &trusted_next_vals, + &trusted_state, &new_signed_header, - DefaultTrustLevel {}, - trusting_period, - now.into(), + &DefaultTrustLevel {}, + &trusting_period, + &now.into(), ); - assert_eq!(check_support_res.is_err(), expexts_err); + assert_eq!(check_support_res.is_err(), expects_err); if check_support_res.is_ok() { - trusted_signed_header = new_signed_header; - trusted_next_vals = input.next_validator_set.clone(); + trusted_state = Trusted::new(&new_signed_header, &input.next_validator_set); } } let got_err = check_support_res.is_err() || h2_verif_res.is_err(); - assert_eq!(expexts_err, got_err); + assert_eq!(expects_err, got_err); } } }