From b5bdac936e992422cbcb785c941117442290e8f4 Mon Sep 17 00:00:00 2001 From: Naohiro Yoshida Date: Sun, 17 Dec 2023 19:18:33 +0900 Subject: [PATCH] Refactor parlia-elc (#38) Refactor Signed-off-by: Naohiro Yoshida --- light-client/src/client_state.rs | 5 +- light-client/src/errors.rs | 16 +- light-client/src/header/eth_headers.rs | 353 ++++++++++++----------- light-client/src/header/mod.rs | 106 ++++--- light-client/src/header/validator_set.rs | 136 ++++++--- 5 files changed, 364 insertions(+), 252 deletions(-) diff --git a/light-client/src/client_state.rs b/light-client/src/client_state.rs index 83b8d4f..aeed1aa 100644 --- a/light-client/src/client_state.rs +++ b/light-client/src/client_state.rs @@ -120,11 +120,8 @@ impl ClientState { )); } - // Ensure validator set is - header.verify_validator_set(cs)?; - // Ensure header is valid - header.verify(&self.chain_id) + header.verify(&self.chain_id, cs) } } diff --git a/light-client/src/errors.rs b/light-client/src/errors.rs index ddbb49e..d3c3847 100644 --- a/light-client/src/errors.rs +++ b/light-client/src/errors.rs @@ -70,10 +70,11 @@ pub enum Error { InvalidProofFormatError(Vec), MissingValidatorInEpochBlock(BlockNumber), MissingNextValidatorSet(BlockNumber), + MissingCurrentValidatorSet(BlockNumber), UnexpectedPreviousValidatorsHash(Height, Height, Hash, Hash), UnexpectedCurrentValidatorsHash(Height, Height, Hash, Hash), InvalidVerifyingHeaderLength(BlockNumber, usize), - ValidatorNotTrusted(Hash), + InsufficientTrustedValidatorsInUntrustedValidators(Hash, usize, usize), MissingValidatorToVerifySeal(BlockNumber), MissingValidatorToVerifyVote(BlockNumber), UnexpectedNextCheckpointHeader(BlockNumber, BlockNumber), @@ -282,11 +283,18 @@ impl core::fmt::Display for Error { Error::UnexpectedVoteRelation(e1, e2, e3) => { write!(f, "UnexpectedVoteRelation : {} {} {:?}", e1, e2, e3) } - Error::ValidatorNotTrusted(e1) => { - write!(f, "ValidatorNotTrusted : {:?}", e1) + Error::InsufficientTrustedValidatorsInUntrustedValidators(e1, e2, e3) => { + write!( + f, + "InsufficientTrustedValidatorsInUntrustedValidators : {:?} {} {}", + e1, e2, e3 + ) } Error::MissingNextValidatorSet(e1) => { - write!(f, "MissingNextValidatorSet : {:?}", e1) + write!(f, "MissingNextValidatorSet : {}", e1) + } + Error::MissingCurrentValidatorSet(e1) => { + write!(f, "MissingCurrentValidatorSet : {}", e1) } Error::MissingValidatorToVerifySeal(e1) => { write!(f, "MissingValidatorToVerifySeal : {:?}", e1) diff --git a/light-client/src/header/eth_headers.rs b/light-client/src/header/eth_headers.rs index e85fa02..e00b5fc 100644 --- a/light-client/src/header/eth_headers.rs +++ b/light-client/src/header/eth_headers.rs @@ -3,9 +3,12 @@ use alloc::vec::Vec; use parlia_ibc_proto::ibc::lightclients::parlia::v1::EthHeader; use crate::errors::Error; -use crate::header::validator_set::ValidatorSet; +use crate::header::validator_set::EitherValidatorSet::{Trusted, Untrusted}; +use crate::header::validator_set::{ + EitherValidatorSet, TrustedValidatorSet, UntrustedValidatorSet, +}; -use crate::misc::{ChainId, Validators}; +use crate::misc::{BlockNumber, ChainId, Validators}; use super::eth_header::ETHHeader; use super::BLOCKS_PER_EPOCH; @@ -20,14 +23,20 @@ impl ETHHeaders { pub fn verify( &self, chain_id: &ChainId, - current_validators: &ValidatorSet, - previous_validators: &ValidatorSet, + current_validators: &EitherValidatorSet, + previous_validators: &TrustedValidatorSet, ) -> Result<(), Error> { // Ensure the header after the next or next checkpoint must not exist. let epoch = self.target.number / BLOCKS_PER_EPOCH; + let checkpoint = epoch * BLOCKS_PER_EPOCH + previous_validators.checkpoint(); let next_checkpoint = (epoch + 1) * BLOCKS_PER_EPOCH + current_validators.checkpoint(); - let maybe_next_validators = - self.verify_header_size(epoch, next_checkpoint, current_validators)?; + let (c_val, n_val) = self.verify_header_size( + epoch, + checkpoint, + next_checkpoint, + previous_validators, + current_validators, + )?; // Ensure all the headers are successfully chained. for (i, header) in self.all.iter().enumerate() { @@ -38,41 +47,36 @@ impl ETHHeaders { } // Ensure valid seals - let checkpoint = epoch * BLOCKS_PER_EPOCH + previous_validators.checkpoint(); - let previous_validators = previous_validators.validators()?; + let p_val = previous_validators.validators(); for h in self.all.iter() { if h.number >= next_checkpoint { - let next_validators = - get_next_validators(h.number, maybe_next_validators.as_ref())?; - h.verify_seal(next_validators, chain_id)?; + h.verify_seal(unwrap_n_val(h.number, &n_val)?, chain_id)?; } else if h.number >= checkpoint { - h.verify_seal(current_validators.validators()?, chain_id)?; + h.verify_seal(unwrap_c_val(h.number, &c_val)?, chain_id)?; } else { - h.verify_seal(previous_validators, chain_id)?; + h.verify_seal(p_val, chain_id)?; } } // Ensure target is finalized - let headers_for_finalize = self.verify_finalized()?; + let (child, grand_child) = self.verify_finalized()?; // Ensure BLS signature is collect // At the just checkpoint BLS signature uses previous validator set. - for h in headers_for_finalize { + for h in &[child, grand_child] { let vote = h.get_vote_attestation()?; if h.number > next_checkpoint { - let next_validators = - get_next_validators(h.number, maybe_next_validators.as_ref())?; - vote.verify(h.number, next_validators)?; + vote.verify(h.number, unwrap_n_val(h.number, &n_val)?)?; } else if h.number > checkpoint { - vote.verify(h.number, current_validators.validators()?)?; + vote.verify(h.number, unwrap_c_val(h.number, &c_val)?)?; } else { - vote.verify(h.number, previous_validators)?; + vote.verify(h.number, p_val)?; } } Ok(()) } - fn verify_finalized(&self) -> Result, Error> { + fn verify_finalized(&self) -> Result<(ÐHeader, ÐHeader), Error> { if self.all.len() < 3 { return Err(Error::InvalidVerifyingHeaderLength( self.target.number, @@ -81,8 +85,7 @@ impl ETHHeaders { } let mut last_error: Option = None; let headers = &self.all[..self.all.len() - 2]; - for (i, _header) in headers.iter().enumerate() { - let header = &self.all[i]; + for (i, header) in headers.iter().enumerate() { let child = &self.all[i + 1]; let grand_child = &self.all[i + 2]; match verify_finalized(header, child, grand_child) { @@ -94,7 +97,7 @@ impl ETHHeaders { self.all.len(), )); } - return Ok(vec![child, grand_child]); + return Ok((child, grand_child)); } } } @@ -105,45 +108,62 @@ impl ETHHeaders { )) } - fn verify_header_size( + fn verify_header_size<'a>( &self, epoch: u64, + checkpoint: u64, next_checkpoint: u64, - current_validators: &ValidatorSet, - ) -> Result, Error> { - let mut maybe_next_validators: Option = None; - - for h in self.all.iter() { - // t=200 then 200 <= h < 411 (c_val(200) can be trusted by p_val) - if self.target.is_epoch() { - if h.number >= next_checkpoint { + previous_validators: &TrustedValidatorSet, + current_validators: &'a EitherValidatorSet, + ) -> Result<(Option<&'a Validators>, Option), Error> { + let hs: Vec<ÐHeader> = self.all.iter().filter(|h| h.number >= checkpoint).collect(); + match current_validators { + // ex) t=200 then 200 <= h < 411 (c_val(200) can be borrowed by p_val) + Untrusted(untrusted_c_val) => { + // Ensure headers are before the next_checkpoint + if hs.iter().any(|h| h.number >= next_checkpoint) { return Err(Error::UnexpectedNextCheckpointHeader( self.target.number, - h.number, + next_checkpoint, )); } - } else { - // t=201 then 201 <= h < 611 (n_val(400) can be trusted by c_val(200)) - if h.is_epoch() { - let mut next_validators: ValidatorSet = h.get_validator_set()?; - next_validators.trust(current_validators.validators()?); - maybe_next_validators = Some(next_validators); - } else if h.number >= next_checkpoint { - let next_validators = maybe_next_validators - .as_mut() - .ok_or_else(|| Error::MissingNextValidatorSet(h.number))?; - let next_next_checkpoint = - (epoch + 2) * BLOCKS_PER_EPOCH + next_validators.checkpoint(); - if h.number >= next_next_checkpoint { - return Err(Error::UnexpectedNextNextCheckpointHeader( - self.target.number, - h.number, - )); - } + + // Ensure c_val is validated by trusted p_val when the checkpoint header is found + if hs.is_empty() { + Ok((None, None)) + } else { + Ok((Some(untrusted_c_val.try_borrow(previous_validators)?), None)) } } + // ex) t=201 then 201 <= h < 611 (n_val(400) can be borrowed by c_val(200)) + Trusted(c_val) => { + // Get n_val if epoch after checkpoint ex) 400 + let n_val = match hs.iter().find(|h| h.is_epoch()) { + Some(h) => h.get_validator_set()?, + None => return Ok((Some(c_val.validators()), None)), + }; + + // Finish if no headers over next checkpoint were found + let hs: Vec<&ÐHeader> = + hs.iter().filter(|h| h.number >= next_checkpoint).collect(); + if hs.is_empty() { + return Ok((Some(c_val.validators()), None)); + } + + // Ensure n_val(400) can be borrowed by c_val(200) + let next_next_checkpoint = (epoch + 2) * BLOCKS_PER_EPOCH + n_val.checkpoint(); + UntrustedValidatorSet::new(&n_val).try_borrow(c_val)?; + + // Ensure headers are before the next_next_checkpoint + if hs.iter().any(|h| h.number >= next_next_checkpoint) { + return Err(Error::UnexpectedNextNextCheckpointHeader( + self.target.number, + next_next_checkpoint, + )); + } + Ok((Some(c_val.validators()), Some(n_val.validators))) + } } - Ok(maybe_next_validators) } } @@ -191,9 +211,17 @@ fn verify_finalized( Ok(()) } -fn get_next_validators(number: u64, v: Option<&ValidatorSet>) -> Result<&Validators, Error> { - v.ok_or_else(|| Error::MissingNextValidatorSet(number))? - .validators() +fn unwrap_n_val(n: BlockNumber, n_val: &Option) -> Result<&Validators, Error> { + n_val + .as_ref() + .ok_or_else(|| Error::MissingNextValidatorSet(n)) +} + +fn unwrap_c_val<'a>( + n: BlockNumber, + c_val: &'a Option<&'a Validators>, +) -> Result<&'a Validators, Error> { + c_val.ok_or_else(|| Error::MissingCurrentValidatorSet(n)) } #[cfg(test)] @@ -203,16 +231,22 @@ mod test { use crate::header::eth_header::ETHHeader; use crate::header::eth_headers::ETHHeaders; use crate::header::testdata::*; - use crate::header::validator_set::ValidatorSet; + use crate::header::validator_set::{ + EitherValidatorSet, TrustedValidatorSet, UntrustedValidatorSet, ValidatorSet, + }; use crate::header::Header; use crate::misc::Validators; use hex_literal::hex; use light_client::types::Any; + use std::prelude::rust_2015::Vec; use std::vec; - fn trust(mut v: ValidatorSet) -> ValidatorSet { - v.trusted = true; - v + fn trust(v: &ValidatorSet) -> TrustedValidatorSet { + TrustedValidatorSet::new(v) + } + + fn untrust(v: &ValidatorSet) -> UntrustedValidatorSet { + UntrustedValidatorSet::new(v) } fn empty() -> ValidatorSet { @@ -223,23 +257,38 @@ mod test { #[test] fn test_success_verify_before_checkpoint() { let headers = create_before_checkpoint_headers(); - let p_val = trust(validators_in_31297000().into()); - headers.verify(&mainnet(), &empty(), &p_val).unwrap(); + let p_val = validators_in_31297000().into(); + let p_val = trust(&p_val); + let c_val = empty(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + headers.verify(&mainnet(), &c_val, &p_val).unwrap(); + + // from epoch + let headers: ETHHeaders = + vec![header_31297200(), header_31297201(), header_31297202()].into(); + let c_val = empty(); + let c_val = EitherValidatorSet::Untrusted(untrust(&c_val)); + headers.verify(&mainnet(), &c_val, &p_val).unwrap(); } #[test] fn test_success_verify_across_checkpoint() { let headers = create_across_checkpoint_headers(); - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - let p_val = trust(validators_in_31297000().into()); + let p_val = validators_in_31297000().into(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); headers.verify(&mainnet(), &c_val, &p_val).unwrap(); } #[test] fn test_success_verify_after_checkpoint() { let headers = create_after_checkpoint_headers(); - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - headers.verify(&mainnet(), &c_val, &trust(empty())).unwrap(); + let p_val = empty(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + headers.verify(&mainnet(), &c_val, &p_val).unwrap(); } #[test] @@ -252,8 +301,11 @@ mod test { for v in validators.iter_mut() { v.remove(0); } - let previous_validator_set = trust(validators.into()); - let result = header.verify(mainnet, &empty(), &previous_validator_set); + let p_val = validators.into(); + let p_val = trust(&p_val); + let c_val = empty(); + let c_val = EitherValidatorSet::Untrusted(untrust(&c_val)); + let result = header.verify(mainnet, &c_val, &p_val); match result.unwrap_err() { Error::MissingSignerInValidator(number, _) => { assert_eq!(number, header.target.number) @@ -264,18 +316,20 @@ mod test { #[test] fn test_error_verify_across_checkpoint() { - let mut new_validators: Validators = header_31297200().get_validator_bytes().unwrap(); - for (i, v) in new_validators.iter_mut().enumerate() { + let mut c_val: Validators = header_31297200().get_validator_bytes().unwrap(); + for (i, v) in c_val.iter_mut().enumerate() { v[0] = i as u8; } - let new_validator_set = trust(new_validators.into()); - let previous_validator_set = trust(validators_in_31297000().into()); + let c_val = c_val.into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + let p_val = validators_in_31297000().into(); + let p_val = trust(&p_val); let mainnet = &mainnet(); // last block uses new empty validator set let header = create_across_checkpoint_headers(); - let result = header.verify(mainnet, &new_validator_set, &previous_validator_set); + let result = header.verify(mainnet, &c_val, &p_val); match result.unwrap_err() { Error::MissingSignerInValidator(number, _) => { //25428811 uses next validator @@ -289,8 +343,11 @@ mod test { fn test_error_verify_non_continuous_header() { let mut headers = create_after_checkpoint_headers(); headers.all[1] = headers.all[0].clone(); - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - let result = headers.verify(&mainnet(), &c_val, &trust(empty())); + let p_val = empty(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + let result = headers.verify(&mainnet(), &c_val, &p_val); match result.unwrap_err() { Error::UnexpectedHeaderRelation(e1, e2, _, _, _, _) => { assert_eq!(e1, headers.target.number); @@ -304,8 +361,11 @@ mod test { fn test_error_verify_too_many_headers_to_finalize() { let mut headers = create_after_checkpoint_headers(); headers.all.push(header_31297214()); - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - let result = headers.verify(&mainnet(), &c_val, &trust(empty())); + let p_val = empty(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + let result = headers.verify(&mainnet(), &c_val, &p_val); match result.unwrap_err() { Error::UnexpectedTooManyHeadersToFinalize(e1, e2) => { assert_eq!(e1, headers.target.number, "block error"); @@ -319,8 +379,11 @@ mod test { fn test_error_verify_invalid_header_size() { let mut headers = create_after_checkpoint_headers(); headers.all.pop(); - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - let result = headers.verify(&mainnet(), &c_val, &trust(empty())); + let p_val = empty(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + let result = headers.verify(&mainnet(), &c_val, &p_val); match result.unwrap_err() { Error::InvalidVerifyingHeaderLength(e1, e2) => { assert_eq!(e1, headers.target.number, "block error"); @@ -370,15 +433,16 @@ mod test { header_31297209(), header_31297210(), ]; - let mut headers = ETHHeaders { + let c_val = v.first().unwrap().get_validator_set().unwrap(); + let c_val = EitherValidatorSet::Untrusted(untrust(&c_val)); + let headers = ETHHeaders { target: v[0].clone(), all: v, }; - // success ( untrusted validator not used ) - let p_val = trust(validators_in_31297000().into()); - let untrusted_c_val = validators_in_31297000().into(); - let result = headers.verify(&mainnet(), &untrusted_c_val, &p_val); + let p_val = validators_in_31297000().into(); + let p_val = trust(&p_val); + let result = headers.verify(&mainnet(), &c_val, &p_val); match result.unwrap_err() { Error::UnexpectedTooManyHeadersToFinalize(e1, e2) => { assert_eq!(e1, headers.target.number, "block error"); @@ -386,23 +450,13 @@ mod test { } e => unreachable!("{:?}", e), } - - // error - headers.all.push(header_31297211()); - let result = headers.verify(&mainnet(), &untrusted_c_val, &p_val); - match result.unwrap_err() { - Error::ValidatorNotTrusted(e1) => { - assert_eq!(e1, untrusted_c_val.hash); - } - e => unreachable!("{:?}", e), - } } #[test] fn test_error_next_checkpoint_header_found_target_epoch() { let f = |mut headers: ETHHeaders, - c_val: ValidatorSet, - p_val: ValidatorSet, + c_val: &EitherValidatorSet, + p_val: &TrustedValidatorSet, include_limit: bool| { let epoch = headers.target.number / BLOCKS_PER_EPOCH; let next_epoch_checkpoint = (epoch + 1) * BLOCKS_PER_EPOCH + c_val.checkpoint(); @@ -416,7 +470,7 @@ mod test { next.number = last.number + 1; headers.all.push(next); } - let result = headers.verify(&mainnet(), &c_val, &p_val).unwrap_err(); + let result = headers.verify(&mainnet(), c_val, p_val).unwrap_err(); if include_limit { match result { Error::UnexpectedNextCheckpointHeader(e1, e2) => { @@ -432,28 +486,24 @@ mod test { }; } }; - let v = vec![ - header_31297200(), - header_31297201(), // checkpoint - header_31297202(), - ]; + let v = vec![header_31297200(), header_31297201(), header_31297202()]; let headers = ETHHeaders { target: v[0].clone(), all: v, }; - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - let p_val = trust(validators_in_31297000().into()); - f(headers.clone(), c_val, p_val.clone(), true); - - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - f(headers, c_val, p_val, false); + let p_val = validators_in_31297000().into(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Untrusted(untrust(&c_val)); + f(headers.clone(), &c_val, &p_val, true); + f(headers, &c_val, &p_val, false); } #[test] fn test_error_next_next_checkpoint_header_found() { let f = |mut headers: ETHHeaders, - c_val: ValidatorSet, - p_val: ValidatorSet, + c_val: &EitherValidatorSet, + p_val: &TrustedValidatorSet, n_val_header: ETHHeader, include_limit: bool| { let n_val: ValidatorSet = n_val_header.get_validator_bytes().unwrap().into(); @@ -473,7 +523,7 @@ mod test { } headers.all.push(next); } - let result = headers.verify(&mainnet(), &c_val, &p_val).unwrap_err(); + let result = headers.verify(&mainnet(), c_val, p_val).unwrap_err(); if include_limit { match result { Error::UnexpectedNextNextCheckpointHeader(e1, e2) => { @@ -490,78 +540,55 @@ mod test { } }; let headers = create_after_checkpoint_headers(); - let c_val = trust(header_31297200().get_validator_bytes().unwrap().into()); - let p_val = trust(empty()); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + let p_val = empty(); + let p_val = trust(&p_val); let n_val_header = header_31297200(); - f( - headers.clone(), - c_val.clone(), - p_val.clone(), - n_val_header.clone(), - true, - ); - f(headers, c_val, p_val, n_val_header.clone(), false); + f(headers.clone(), &c_val, &p_val, n_val_header.clone(), true); + f(headers, &c_val, &p_val, n_val_header.clone(), false); let headers = create_before_checkpoint_headers(); - let p_val = ValidatorSet::from(validators_in_31297000()); - let c_val = trust(ValidatorSet::from( - header_31297200().get_validator_bytes().unwrap(), - )); - f( - headers.clone(), - c_val.clone(), - p_val.clone(), - n_val_header.clone(), - true, - ); - f( - headers, - c_val.clone(), - p_val.clone(), - header_31297200(), - false, - ); + let p_val = validators_in_31297000().into(); + let p_val = trust(&p_val); + let c_val = header_31297200().get_validator_bytes().unwrap().into(); + let c_val = EitherValidatorSet::Trusted(trust(&c_val)); + f(headers.clone(), &c_val, &p_val, n_val_header.clone(), true); + f(headers, &c_val, &p_val, header_31297200(), false); let headers = create_across_checkpoint_headers(); - f( - headers.clone(), - c_val.clone(), - p_val.clone(), - n_val_header.clone(), - true, - ); - f(headers, c_val, p_val, n_val_header, false); + f(headers.clone(), &c_val, &p_val, n_val_header.clone(), true); + f(headers, &c_val, &p_val, n_val_header, false); } fn create_before_checkpoint_headers() -> ETHHeaders { - let v = vec![header_31297208(), header_31297209(), header_31297210()]; - ETHHeaders { - target: v[0].clone(), - all: v, - } + vec![header_31297208(), header_31297209(), header_31297210()].into() } fn create_across_checkpoint_headers() -> ETHHeaders { - let v = vec![ + vec![ header_31297210(), header_31297211(), // checkpoint header_31297212(), - ]; - ETHHeaders { - target: v[0].clone(), - all: v, - } + ] + .into() } fn create_after_checkpoint_headers() -> ETHHeaders { - let v = vec![ + vec![ header_31297211(), // checkpoint header_31297212(), header_31297213(), - ]; - ETHHeaders { - target: v[0].clone(), - all: v, + ] + .into() + } + + impl From> for ETHHeaders { + fn from(value: Vec) -> Self { + Self { + target: value[0].clone(), + all: value, + } } } } diff --git a/light-client/src/header/mod.rs b/light-client/src/header/mod.rs index 20a0a2d..0126643 100644 --- a/light-client/src/header/mod.rs +++ b/light-client/src/header/mod.rs @@ -10,7 +10,9 @@ use crate::commitment::decode_eip1184_rlp_proof; use crate::consensus_state::ConsensusState; use crate::header::eth_headers::ETHHeaders; -use crate::header::validator_set::ValidatorSet; +use crate::header::validator_set::{ + EitherValidatorSet, TrustedValidatorSet, UntrustedValidatorSet, ValidatorSet, +}; use crate::misc::{new_height, new_timestamp, ChainId, Hash}; use super::errors::Error; @@ -67,38 +69,35 @@ impl Header { self.current_validators.hash } - pub fn verify(&self, chain_id: &ChainId) -> Result<(), Error> { - self.headers.verify( - chain_id, - &self.current_validators, - &self.previous_validators, - ) - } - pub fn block_hash(&self) -> &Hash { &self.headers.target.hash } - pub fn verify_validator_set(&mut self, consensus_state: &ConsensusState) -> Result<(), Error> { - verify_validator_set( + pub fn verify( + &self, + chain_id: &ChainId, + consensus_state: &ConsensusState, + ) -> Result<(), Error> { + let (c_val, p_val) = verify_validator_set( consensus_state, self.headers.target.is_epoch(), self.height(), self.trusted_height, - &mut self.previous_validators, - &mut self.current_validators, - ) + &self.previous_validators, + &self.current_validators, + )?; + self.headers.verify(chain_id, &c_val, &p_val) } } -fn verify_validator_set( +fn verify_validator_set<'a>( consensus_state: &ConsensusState, is_epoch: bool, height: Height, trusted_height: Height, - previous_validators: &mut ValidatorSet, - current_validators: &mut ValidatorSet, -) -> Result<(), Error> { + previous_validators: &'a ValidatorSet, + current_validators: &'a ValidatorSet, +) -> Result<(EitherValidatorSet<'a>, TrustedValidatorSet<'a>), Error> { let header_epoch = height.revision_height() / BLOCKS_PER_EPOCH; let trusted_epoch = trusted_height.revision_height() / BLOCKS_PER_EPOCH; @@ -109,9 +108,8 @@ fn verify_validator_set( height.revision_height(), )); } - previous_validators.trusted = - previous_validators.hash == consensus_state.current_validators_hash; - if !previous_validators.trusted { + let previous_trusted = previous_validators.hash == consensus_state.current_validators_hash; + if !previous_trusted { return Err(Error::UnexpectedPreviousValidatorsHash( trusted_height, height, @@ -120,8 +118,10 @@ fn verify_validator_set( )); } - // Try set trust by previous trusted validators - current_validators.trust(previous_validators.validators()?) + Ok(( + EitherValidatorSet::Untrusted(UntrustedValidatorSet::new(current_validators)), + TrustedValidatorSet::new(previous_validators), + )) } else { if header_epoch != trusted_epoch { return Err(Error::UnexpectedTrustedHeight( @@ -129,9 +129,8 @@ fn verify_validator_set( height.revision_height(), )); } - previous_validators.trusted = - previous_validators.hash == consensus_state.previous_validators_hash; - if !previous_validators.trusted { + let previous_trusted = previous_validators.hash == consensus_state.previous_validators_hash; + if !previous_trusted { return Err(Error::UnexpectedPreviousValidatorsHash( trusted_height, height, @@ -139,9 +138,8 @@ fn verify_validator_set( consensus_state.previous_validators_hash, )); } - current_validators.trusted = - current_validators.hash == consensus_state.current_validators_hash; - if !current_validators.trusted { + let current_trusted = current_validators.hash == consensus_state.current_validators_hash; + if !current_trusted { return Err(Error::UnexpectedCurrentValidatorsHash( trusted_height, height, @@ -149,8 +147,11 @@ fn verify_validator_set( consensus_state.current_validators_hash, )); } + Ok(( + EitherValidatorSet::Trusted(TrustedValidatorSet::new(current_validators)), + TrustedValidatorSet::new(previous_validators), + )) } - Ok(()) } impl TryFrom for Header { @@ -236,7 +237,7 @@ pub(crate) mod test { use crate::errors::Error; use crate::header::eth_headers::ETHHeaders; use crate::header::testdata::{header_31297200, header_31297201}; - use crate::header::validator_set::ValidatorSet; + use crate::header::validator_set::{EitherValidatorSet, ValidatorSet}; use crate::header::{verify_validator_set, Header}; use crate::misc::{new_height, Hash, Validators}; use light_client::types::Time; @@ -350,9 +351,7 @@ pub(crate) mod test { current_validators: vec![], previous_validators: vec![h.coinbase.clone()], }; - let mut result = Header::try_from(raw.clone()).unwrap(); - result.previous_validators.trusted = true; - result.current_validators.trusted = true; + let result = Header::try_from(raw.clone()).unwrap(); assert_eq!(result.headers.target, *h); assert_eq!( result.trusted_height.revision_height(), @@ -363,11 +362,11 @@ pub(crate) mod test { trusted_height.revision_number ); assert_eq!( - result.previous_validators.validators().unwrap(), + &result.previous_validators.validators, &raw.previous_validators ); assert_eq!( - result.current_validators.validators().unwrap(), + &result.current_validators.validators, &h.get_validator_bytes().unwrap() ); } @@ -386,9 +385,7 @@ pub(crate) mod test { current_validators: vec![header_31297200().coinbase], previous_validators: vec![h.coinbase.clone()], }; - let mut result = Header::try_from(raw.clone()).unwrap(); - result.previous_validators.trusted = true; - result.current_validators.trusted = true; + let result = Header::try_from(raw.clone()).unwrap(); assert_eq!(result.headers.target, *h); assert_eq!( result.trusted_height.revision_height(), @@ -399,11 +396,11 @@ pub(crate) mod test { trusted_height.revision_number ); assert_eq!( - result.previous_validators.validators().unwrap(), + &result.previous_validators.validators, &raw.previous_validators ); assert_eq!( - result.current_validators.validators().unwrap(), + &result.current_validators.validators, &raw.current_validators ); } @@ -436,7 +433,7 @@ pub(crate) mod test { // Same validator set as previous let current_validators = &mut to_validator_set([3u8; 32]); let previous_validators = &mut to_validator_set(cs.current_validators_hash); - verify_validator_set( + let (c_val, p_val) = verify_validator_set( &cs, true, height, @@ -445,12 +442,18 @@ pub(crate) mod test { current_validators, ) .unwrap(); - assert!(current_validators.trusted); + match c_val { + EitherValidatorSet::Untrusted(r) => { + let validators = r.try_borrow(&p_val).unwrap(); + assert_eq!(*validators, current_validators.validators); + } + _ => unreachable!("unexpected trusted"), + } let current_validators = &mut to_validator_set([3u8; 32]); let previous_validators = &mut to_validator_set_with_validators(cs.current_validators_hash, vec![vec![2]]); - verify_validator_set( + let (c_val, p_val) = verify_validator_set( &cs, true, height, @@ -459,13 +462,18 @@ pub(crate) mod test { current_validators, ) .unwrap(); - assert!(!current_validators.trusted); + match c_val { + EitherValidatorSet::Untrusted(r) => { + assert!(r.try_borrow(&p_val).is_err()); + } + _ => unreachable!("unexpected trusted"), + } let height = new_height(0, 599); let trusted_height = new_height(0, 400); let current_validators = &mut to_validator_set(cs.current_validators_hash); let previous_validators = &mut to_validator_set(cs.previous_validators_hash); - verify_validator_set( + let (c_val, _p_val) = verify_validator_set( &cs, false, height, @@ -474,6 +482,12 @@ pub(crate) mod test { current_validators, ) .unwrap(); + match c_val { + EitherValidatorSet::Trusted(r) => { + assert_eq!(*r.validators(), current_validators.validators); + } + _ => unreachable!("unexpected untrusted"), + } } #[test] diff --git a/light-client/src/header/validator_set.rs b/light-client/src/header/validator_set.rs index b73c6fa..02dacc7 100644 --- a/light-client/src/header/validator_set.rs +++ b/light-client/src/header/validator_set.rs @@ -3,40 +3,56 @@ use alloc::vec::Vec; use crate::misc::{ceil_div, keccak_256_vec, Hash, Validators}; -#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct ValidatorSet { - validators: Validators, - pub hash: Hash, - pub trusted: bool, +#[derive(Clone, Debug, PartialEq)] +pub struct TrustedValidatorSet<'a> { + inner: &'a ValidatorSet, } -impl ValidatorSet { - /// for example when the validator count is 21 the checkpoint is 211, 411, 611 ... - /// https://github.com/bnb-chain/bsc/blob/48aaee69e9cb50fc2cedf1398ae4b98b099697db/consensus/parlia/parlia.go#L607 - /// https://github.com/bnb-chain/bsc/blob/48aaee69e9cb50fc2cedf1398ae4b98b099697db/consensus/parlia/snapshot.go#L191 +impl<'a> TrustedValidatorSet<'a> { + pub fn validators(&self) -> &Validators { + &self.inner.validators + } + pub fn checkpoint(&self) -> u64 { - let validator_size = self.validators.len() as u64; - validator_size / 2 + 1 + self.inner.checkpoint() } - pub fn validators(&self) -> Result<&Validators, Error> { - if !self.trusted { - return Err(Error::ValidatorNotTrusted(self.hash)); - } - Ok(&self.validators) + pub fn new(inner: &'a ValidatorSet) -> Self { + Self { inner } } +} - pub fn trust(&mut self, trusted_validators: &Validators) { - if self.trusted { - return; +#[derive(Clone, Debug, PartialEq)] +pub struct UntrustedValidatorSet<'a> { + inner: &'a ValidatorSet, +} + +impl<'a> UntrustedValidatorSet<'a> { + pub fn new(inner: &'a ValidatorSet) -> Self { + Self { inner } + } + pub fn checkpoint(&self) -> u64 { + self.inner.checkpoint() + } + pub fn try_borrow( + &'a self, + trusted_validators: &TrustedValidatorSet, + ) -> Result<&'a Validators, Error> { + let (result, found, required) = self.contains(trusted_validators); + if result { + return Ok(&self.inner.validators); } - let (trusted, _, _) = self.trustable(trusted_validators); - self.trusted = trusted + Err(Error::InsufficientTrustedValidatorsInUntrustedValidators( + self.inner.hash, + found, + required, + )) } - fn trustable(&self, trusted_validators: &Validators) -> (bool, usize, usize) { + fn contains(&self, trusted_validators: &TrustedValidatorSet) -> (bool, usize, usize) { + let trusted_validators = trusted_validators.validators(); let mut trusted_validator_count = 0; - for x1 in &self.validators { + for x1 in &self.inner.validators { if trusted_validators.contains(x1) { trusted_validator_count += 1; } @@ -50,25 +66,56 @@ impl ValidatorSet { } } +#[derive(Clone, Debug, PartialEq)] +pub enum EitherValidatorSet<'a> { + Trusted(TrustedValidatorSet<'a>), + Untrusted(UntrustedValidatorSet<'a>), +} + +impl<'a> EitherValidatorSet<'a> { + pub fn checkpoint(&self) -> u64 { + match self { + EitherValidatorSet::Trusted(v) => v.checkpoint(), + EitherValidatorSet::Untrusted(v) => v.checkpoint(), + } + } +} + +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ValidatorSet { + pub validators: Validators, + pub hash: Hash, +} + +impl ValidatorSet { + /// for example when the validator count is 21 the checkpoint is 211, 411, 611 ... + /// https://github.com/bnb-chain/bsc/blob/48aaee69e9cb50fc2cedf1398ae4b98b099697db/consensus/parlia/parlia.go#L607 + /// https://github.com/bnb-chain/bsc/blob/48aaee69e9cb50fc2cedf1398ae4b98b099697db/consensus/parlia/snapshot.go#L191 + pub fn checkpoint(&self) -> u64 { + let validator_size = self.validators.len() as u64; + validator_size / 2 + 1 + } +} + impl From>> for ValidatorSet { fn from(value: Vec>) -> Self { let hash = keccak_256_vec(&value); Self { validators: value as Validators, hash, - trusted: false, } } } #[cfg(test)] mod test { - use crate::header::validator_set::ValidatorSet; + use crate::errors::Error; + use crate::header::validator_set::{TrustedValidatorSet, UntrustedValidatorSet, ValidatorSet}; #[test] - pub fn test_trustable() { - let mut _assert_trusted = |x, y, _trusted| { - let trusted_validators = vec![ + pub fn test_untrusted_validator_set_try_borrow() { + let mut _assert_trusted = |x, y, c_val_borrowable| { + let trusted_validators: ValidatorSet = vec![ vec![1], vec![2], vec![3], @@ -76,18 +123,37 @@ mod test { vec![5], vec![6], vec![7], - ]; - let mut untrusted_validators = ValidatorSet { + ] + .into(); + let trusted_validators = TrustedValidatorSet::new(&trusted_validators); + let untrusted_validators = ValidatorSet { validators: x, hash: [0; 32], - trusted: false, }; - let (trusted, count, required) = untrusted_validators.trustable(&trusted_validators); - assert_eq!(trusted, trusted); + let untrusted_validators = UntrustedValidatorSet::new(&untrusted_validators); + let (result, count, required) = untrusted_validators.contains(&trusted_validators); + assert_eq!(result, c_val_borrowable); assert_eq!(count, y); assert_eq!(required, 3); - untrusted_validators.trust(&trusted_validators); - assert_eq!(untrusted_validators.trusted, trusted); + match untrusted_validators.try_borrow(&trusted_validators) { + Ok(borrowed) => { + if c_val_borrowable { + assert_eq!(*borrowed, untrusted_validators.inner.validators); + } else { + unreachable!("unexpected borrowed") + } + } + Err(e) => { + if c_val_borrowable { + unreachable!("unexpected error {:?}", e); + } else { + match e { + Error::InsufficientTrustedValidatorsInUntrustedValidators(_, _, _) => {} + e => unreachable!("unexpected error type {:?}", e), + } + } + } + } }; let assert_trusted = |x, y| _assert_trusted(x, y, true);