Skip to content

Commit

Permalink
Support non neighboring epoch (#40)
Browse files Browse the repository at this point in the history
support non_neighbouring_epoch

Signed-off-by: Naohiro Yoshida <[email protected]>
  • Loading branch information
yoshidan authored Feb 8, 2024
1 parent 2e1daa7 commit a9f7da3
Show file tree
Hide file tree
Showing 6 changed files with 481 additions and 689 deletions.
14 changes: 7 additions & 7 deletions light-client/src/client.rs

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions light-client/src/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ mod test {
revision_height: h.number - 1,
}),
account_proof: vec![],
current_validators: vec![],
current_validators: if h.is_epoch() {
h.get_validator_set().unwrap().validators
} else {
vec![]
},
previous_validators: validators_in_31297000(),
};
let now = new_timestamp(h.timestamp + 1).unwrap();
Expand Down Expand Up @@ -340,7 +344,7 @@ mod test {
revision_height: h.number - 1,
}),
account_proof: vec![1],
current_validators: vec![],
current_validators: header_31297200().get_validator_bytes().unwrap(),
previous_validators: validators_in_31297000(),
};
let valid_header: Header = raw.try_into().unwrap();
Expand All @@ -366,7 +370,11 @@ mod test {
headers: h.iter().map(|e| e.try_into().unwrap()).collect(),
trusted_height: Some(trusted_height),
account_proof: vec![],
current_validators: vec![h[0].coinbase.clone()],
current_validators: if h[0].is_epoch() {
h[0].get_validator_set().unwrap().validators
} else {
vec![h[0].coinbase.clone()]
},
previous_validators: vec![h[0].coinbase.clone()],
};
raw.try_into().unwrap()
Expand Down
4 changes: 4 additions & 0 deletions light-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub enum Error {
MissingValidatorToVerifyVote(BlockNumber),
UnexpectedNextCheckpointHeader(BlockNumber, BlockNumber),
UnexpectedNextNextCheckpointHeader(BlockNumber, BlockNumber),
MissingTrustedCurrentValidators(BlockNumber),

// Vote attestation
UnexpectedTooManyHeadersToFinalize(BlockNumber, usize),
Expand Down Expand Up @@ -308,6 +309,9 @@ impl core::fmt::Display for Error {
Error::UnexpectedNextNextCheckpointHeader(e1, e2) => {
write!(f, "UnexpectedNextNextCheckpointHeader : {} {}", e1, e2)
}
Error::MissingTrustedCurrentValidators(e1) => {
write!(f, "MissingTrustedCurrentValidators : {}", e1)
}
}
}
}
Expand Down
193 changes: 187 additions & 6 deletions light-client/src/header/eth_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,46 @@ pub struct ETHHeaders {
}

impl ETHHeaders {
pub fn verify_non_neighboring_epoch(
&self,
chain_id: &ChainId,
checkpoint: u64,
trusted_validators: TrustedValidatorSet,
next_validators: UntrustedValidatorSet,
) -> Result<(), Error> {
let n_val = next_validators.try_borrow(&trusted_validators)?;

// Ensure all the headers are successfully chained.
self.verify_cascading_fields()?;

// Ensure valid seals
let hs: Vec<ETHHeader> = self
.all
.iter()
.filter(|h| h.number >= checkpoint)
.cloned()
.collect();
for h in hs.iter() {
h.verify_seal(n_val, chain_id)?;
}

let hs = ETHHeaders {
target: self.target.clone(),
all: hs,
};

// Ensure target is finalized
let (child, grand_child) = hs.verify_finalized()?;

// Ensure BLS signature is collect
// At the just checkpoint BLS signature uses previous validator set.
for h in &[child, grand_child] {
let vote = h.get_vote_attestation()?;
vote.verify(h.number, n_val)?;
}
Ok(())
}

pub fn verify(
&self,
chain_id: &ChainId,
Expand All @@ -39,12 +79,7 @@ impl ETHHeaders {
)?;

// Ensure all the headers are successfully chained.
for (i, header) in self.all.iter().enumerate() {
if i < self.all.len() - 1 {
let child = &self.all[i + 1];
child.verify_cascading_fields(header)?;
}
}
self.verify_cascading_fields()?;

// Ensure valid seals
let p_val = previous_validators.validators();
Expand Down Expand Up @@ -76,6 +111,16 @@ impl ETHHeaders {
Ok(())
}

fn verify_cascading_fields(&self) -> Result<(), Error> {
for (i, header) in self.all.iter().enumerate() {
if i < self.all.len() - 1 {
let child = &self.all[i + 1];
child.verify_cascading_fields(header)?;
}
}
Ok(())
}

fn verify_finalized(&self) -> Result<(&ETHHeader, &ETHHeader), Error> {
if self.all.len() < 3 {
return Err(Error::InvalidVerifyingHeaderLength(
Expand Down Expand Up @@ -561,6 +606,142 @@ mod test {
f(headers, &c_val, &p_val, n_val_header, false);
}

#[test]
fn test_success_verify_non_neighboring_epoch() {
let headers: ETHHeaders = vec![
header_31297200(),
header_31297201(),
header_31297202(),
header_31297203(),
header_31297204(),
header_31297205(),
header_31297206(),
header_31297207(),
header_31297208(),
header_31297209(),
header_31297210(),
header_31297211(),
header_31297212(),
header_31297213(),
]
.into();

let checkpoint =
headers.target.number + ValidatorSet::from(validators_in_31297000()).checkpoint();
let n_val_raw = header_31297200().get_validator_bytes().unwrap().into();
let n_val = UntrustedValidatorSet::new(&n_val_raw);
let t_val_raw = validators_in_31297000().into();
let t_val = TrustedValidatorSet::new(&t_val_raw);
headers
.verify_non_neighboring_epoch(&mainnet(), checkpoint, t_val, n_val)
.unwrap();
}

#[test]
fn test_error_verify_non_neighboring_epoch() {
let headers: ETHHeaders = vec![header_31297200()].into();

// Insufficient trustedValidators
let checkpoint =
headers.target.number + ValidatorSet::from(validators_in_31297000()).checkpoint();
let n_val_raw = header_31297200().get_validator_bytes().unwrap().into();
let n_val = UntrustedValidatorSet::new(&n_val_raw);
let t_val_raw = vec![vec![0], vec![1], vec![2]].into();
let t_val = TrustedValidatorSet::new(&t_val_raw);
let err = headers
.verify_non_neighboring_epoch(&mainnet(), checkpoint, t_val, n_val)
.unwrap_err();
match err {
Error::InsufficientTrustedValidatorsInUntrustedValidators(_, found, required) => {
assert_eq!(found, 0);
assert_eq!(required, 1);
}
err => unreachable!("unexpected error type {:?}", err),
}

// illegal sequence
let headers: ETHHeaders = vec![header_31297200(), header_31297202()].into();
let n_val_raw = header_31297200().get_validator_bytes().unwrap().into();
let n_val = UntrustedValidatorSet::new(&n_val_raw);
let t_val_raw = validators_in_31297000().into();
let t_val = TrustedValidatorSet::new(&t_val_raw);
let err = headers
.verify_non_neighboring_epoch(&mainnet(), checkpoint, t_val, n_val)
.unwrap_err();
match err {
Error::UnexpectedHeaderRelation(h1, h2, _, _, _, _) => {
assert_eq!(h1, 31297200);
assert_eq!(h2, 31297202);
}
err => unreachable!("unexpected error type {:?}", err),
}

// illegal sealer after checkpoint
let mut headers: ETHHeaders = vec![
header_31297200(),
header_31297201(),
header_31297202(),
header_31297203(),
header_31297204(),
header_31297205(),
header_31297206(),
header_31297207(),
header_31297208(),
header_31297209(),
header_31297210(),
header_31297211(),
header_31297212(),
header_31297213(),
]
.into();
headers.all.last_mut().unwrap().coinbase = vec![];
let n_val_raw = header_31297200().get_validator_bytes().unwrap().into();
let n_val = UntrustedValidatorSet::new(&n_val_raw);
let t_val_raw = validators_in_31297000().into();
let t_val = TrustedValidatorSet::new(&t_val_raw);
let err = headers
.verify_non_neighboring_epoch(&mainnet(), checkpoint, t_val, n_val)
.unwrap_err();
match err {
Error::UnexpectedCoinbase(h1) => {
assert_eq!(h1, 31297213);
}
err => unreachable!("unexpected error type {:?}", err),
}

// invalid header size
let headers: ETHHeaders = vec![
header_31297200(),
header_31297201(),
header_31297202(),
header_31297203(),
header_31297204(),
header_31297205(),
header_31297206(),
header_31297207(),
header_31297208(),
header_31297209(),
header_31297210(),
header_31297211(),
header_31297212(),
]
.into();
let n_val_raw = header_31297200().get_validator_bytes().unwrap().into();
let n_val = UntrustedValidatorSet::new(&n_val_raw);
let t_val_raw = validators_in_31297000().into();
let t_val = TrustedValidatorSet::new(&t_val_raw);
let err = headers
.verify_non_neighboring_epoch(&mainnet(), checkpoint, t_val, n_val)
.unwrap_err();
match err {
Error::InvalidVerifyingHeaderLength(h1, size) => {
assert_eq!(h1, 31297200);
assert_eq!(size, 2);
}
err => unreachable!("unexpected error type {:?}", err),
}
}

fn create_before_checkpoint_headers() -> ETHHeaders {
vec![header_31297208(), header_31297209(), header_31297210()].into()
}
Expand Down
Loading

0 comments on commit a9f7da3

Please sign in to comment.