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

BEP-341 4.2.1 Priority Allocation #53

Merged
merged 4 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions light-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub enum Error {
UnexpectedNextCheckpointHeader(BlockNumber, BlockNumber),
UnexpectedNextNextCheckpointHeader(BlockNumber, BlockNumber),
MissingTrustedCurrentValidators(BlockNumber),
UnexpectedDifficultyInTurn(BlockNumber, u64, usize),
UnexpectedDifficultyNoTurn(BlockNumber, u64, usize),
UnexpectedUntrustedValidatorsHashInEpoch(Height, Height, Hash, Hash),
UnexpectedCurrentValidatorsHashInEpoch(Height, Height, Hash, Hash),

Expand Down Expand Up @@ -346,6 +348,12 @@ impl core::fmt::Display for Error {
Error::LCPError(e1) => {
write!(f, "LCPError: {}", e1)
}
Error::UnexpectedDifficultyInTurn(e1, e2, e3) => {
write!(f, "UnexpectedDifficultyInTurn : {} {} {}", e1, e2, e3)
}
Error::UnexpectedDifficultyNoTurn(e1, e2, e3) => {
write!(f, "UnexpectedDifficultyNoTurn : {} {} {}", e1, e2, e3)
}
Error::UnexpectedTurnLength(e1) => {
write!(f, "UnexpectedTurnLength : {}", e1)
}
Expand Down
10 changes: 7 additions & 3 deletions light-client/src/header/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ impl<'a> TrustedEpoch<'a> {
self.inner.checkpoint()
}

pub fn epoch(&self) -> &'a Epoch {
self.inner
}

pub fn new(inner: &'a Epoch) -> Self {
Self { inner }
}
Expand All @@ -67,10 +71,10 @@ impl<'a> UntrustedEpoch<'a> {
pub fn checkpoint(&self) -> u64 {
self.inner.checkpoint()
}
pub fn try_borrow(&'a self, trusted_epoch: &TrustedEpoch) -> Result<&'a Validators, Error> {
pub fn try_borrow(&'a self, trusted_epoch: &TrustedEpoch) -> Result<&'a Epoch, Error> {
let (result, found, required) = self.contains(trusted_epoch);
if result {
return Ok(self.inner.validators());
return Ok(self.inner);
}
Err(Error::InsufficientTrustedValidatorsInUntrustedValidators(
self.inner.hash,
Expand Down Expand Up @@ -146,7 +150,7 @@ mod test {
match untrusted_epoch.try_borrow(&trusted_epoch) {
Ok(borrowed) => {
if c_val_borrowable {
assert_eq!(borrowed, untrusted_epoch.inner.validators());
assert_eq!(borrowed, untrusted_epoch.inner);
} else {
unreachable!("unexpected borrowed")
}
Expand Down
82 changes: 72 additions & 10 deletions light-client/src/header/eth_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl ETHHeader {
/// https://github.com/bnb-chain/bsc/blob/7a19cd27b61b342d24a1584efc7fa00de4a5b4f5/consensus/parlia/parlia.go#L755
pub fn verify_seal(
&self,
validator_set: &Validators,
validating_epoch: &Epoch,
chain_id: &ChainId,
) -> Result<Address, Error> {
// Resolve the authorization key and check against validators
Expand All @@ -205,7 +205,7 @@ impl ETHHeader {
}

let mut valid_signer = false;
for validator in validator_set.iter() {
for validator in validating_epoch.validators().iter() {
if validator[0..VALIDATOR_BYTES_LENGTH_BEFORE_LUBAN] == signer {
valid_signer = true;
break;
Expand All @@ -215,11 +215,33 @@ impl ETHHeader {
return Err(Error::MissingSignerInValidator(self.number, signer));
}

// Don't check that the difficulty corresponds to the turn-ness of the signer
// Ensure that the difficulty corresponds to the turn-ness of the signer
self.verify_validator_rotation(validating_epoch)?;

Ok(signer)
}

fn verify_validator_rotation(&self, epoch: &Epoch) -> Result<(), Error> {
let offset = (self.number / epoch.turn_length() as u64) as usize % epoch.validators().len();
let inturn_validator = &epoch.validators()[offset][0..VALIDATOR_BYTES_LENGTH_BEFORE_LUBAN];
if inturn_validator == self.coinbase {
if self.difficulty != DIFFICULTY_INTURN {
return Err(Error::UnexpectedDifficultyInTurn(
self.number,
self.difficulty,
offset,
));
}
} else if self.difficulty != DIFFICULTY_NOTURN {
return Err(Error::UnexpectedDifficultyNoTurn(
self.number,
self.difficulty,
offset,
));
}
Ok(())
}

pub fn verify_target_attestation(&self, parent: &ETHHeader) -> Result<VoteAttestation, Error> {
let target_vote_attestation = self.get_vote_attestation()?;
let target_data = &target_vote_attestation.data;
Expand Down Expand Up @@ -286,7 +308,6 @@ impl ETHHeader {
}
}

// https://github.com/bnb-chain/bsc/blob/33e6f840d25edb95385d23d284846955327b0fcd/consensus/parlia/parlia.go#L342
pub fn get_validator_bytes_and_tern_term(extra_data: &[u8]) -> Result<(Validators, u8), Error> {
if extra_data.len() <= EXTRA_VANITY + EXTRA_SEAL {
return Err(Error::UnexpectedExtraDataLength(extra_data.len()));
Expand Down Expand Up @@ -482,13 +503,15 @@ impl TryFrom<RawETHHeader> for ETHHeader {
pub(crate) mod test {
use crate::errors::Error;
use crate::header::eth_header::{
ETHHeader, EXTRA_SEAL, EXTRA_VANITY, PARAMS_GAS_LIMIT_BOUND_DIVISOR,
ETHHeader, DIFFICULTY_INTURN, DIFFICULTY_NOTURN, EXTRA_SEAL, EXTRA_VANITY,
PARAMS_GAS_LIMIT_BOUND_DIVISOR, VALIDATOR_BYTES_LENGTH_BEFORE_LUBAN,
};

use rlp::RlpStream;
use rstest::*;

use crate::fixture::{localnet, Network};
use crate::header::epoch::Epoch;
use alloc::boxed::Box;
use parlia_ibc_proto::ibc::lightclients::parlia::v1::EthHeader as RawETHHeader;

Expand Down Expand Up @@ -614,14 +637,14 @@ pub(crate) mod test {
#[rstest]
#[case::localnet(localnet())]
fn test_success_verify_seal(#[case] hp: Box<dyn Network>) {
let validators = hp.previous_validators();
let prev_epoch = hp.previous_epoch_header().epoch.unwrap();
let blocks = vec![
hp.epoch_header(),
hp.epoch_header_plus_1(),
hp.epoch_header_plus_2(),
];
for block in blocks {
if let Err(e) = block.verify_seal(&validators, &hp.network()) {
if let Err(e) = block.verify_seal(&prev_epoch, &hp.network()) {
unreachable!("{} {:?}", block.number, e);
}
}
Expand All @@ -630,11 +653,11 @@ pub(crate) mod test {
#[rstest]
#[case::localnet(localnet())]
fn test_error_verify_seal(#[case] hp: Box<dyn Network>) {
let validators = hp.previous_validators();
let prev_epoch = hp.previous_epoch_header().epoch.unwrap();
let mut blocks = vec![hp.epoch_header_plus_1(), hp.epoch_header_plus_2()];

for block in blocks.iter_mut() {
let result = block.verify_seal(&vec![], &hp.network());
let result = block.verify_seal(&Epoch::new(vec![].into(), 1), &hp.network());
match result.unwrap_err() {
Error::MissingSignerInValidator(number, address) => {
assert_eq!(block.number, number);
Expand All @@ -646,7 +669,7 @@ pub(crate) mod test {

for mut block in blocks.iter_mut() {
block.coinbase = vec![];
let result = block.verify_seal(&validators, &hp.network());
let result = block.verify_seal(&prev_epoch, &hp.network());
match result.unwrap_err() {
Error::UnexpectedCoinbase(number) => assert_eq!(block.number, number),
e => unreachable!("{:?}", e),
Expand Down Expand Up @@ -792,4 +815,43 @@ pub(crate) mod test {
err => unreachable!("{:?}", err),
}
}

#[rstest]
#[case::localnet(localnet())]
fn test_error_verify_validator_rotation_inturn(#[case] hp: Box<dyn Network>) {
let mut header = hp.epoch_header();
header.difficulty = DIFFICULTY_NOTURN;
let prev = hp.previous_epoch_header();
match header
.verify_validator_rotation(&prev.epoch.unwrap())
.unwrap_err()
{
Error::UnexpectedDifficultyInTurn(e1, e2, _e3) => {
assert_eq!(e1, header.number);
assert_eq!(e2, header.difficulty);
}
err => unreachable!("{:?}", err),
}
}

#[rstest]
#[case::localnet(localnet())]
fn test_error_verify_validator_rotation_noturn(#[case] hp: Box<dyn Network>) {
let mut header = hp.epoch_header();
header.difficulty = DIFFICULTY_INTURN;
let prev = hp.previous_epoch_header();
header.coinbase = prev.epoch.clone().unwrap().validators()[1]
[0..VALIDATOR_BYTES_LENGTH_BEFORE_LUBAN]
.to_vec();
match header
.verify_validator_rotation(&prev.epoch.unwrap())
.unwrap_err()
{
Error::UnexpectedDifficultyNoTurn(e1, e2, _e3) => {
assert_eq!(e1, header.number);
assert_eq!(e2, header.difficulty);
}
err => unreachable!("{:?}", err),
}
}
}
28 changes: 11 additions & 17 deletions light-client/src/header/eth_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use parlia_ibc_proto::ibc::lightclients::parlia::v1::EthHeader;
use crate::errors::Error;
use crate::errors::Error::MissingEpochInfoInEpochBlock;
use crate::header::epoch::EitherEpoch::{Trusted, Untrusted};
use crate::header::epoch::{EitherEpoch, TrustedEpoch, UntrustedEpoch};
use crate::header::epoch::{EitherEpoch, Epoch, TrustedEpoch, UntrustedEpoch};

use crate::misc::{BlockNumber, ChainId, Validators};
use crate::misc::{BlockNumber, ChainId};

use super::eth_header::ETHHeader;
use super::BLOCKS_PER_EPOCH;
Expand Down Expand Up @@ -48,7 +48,7 @@ impl ETHHeaders {
} else if h.number >= checkpoint {
h.verify_seal(unwrap_c_val(h.number, &c_val)?, chain_id)?;
} else {
h.verify_seal(p_val, chain_id)?;
h.verify_seal(previous_epoch.epoch(), chain_id)?;
}
}

Expand All @@ -60,9 +60,9 @@ impl ETHHeaders {
for h in &[child, grand_child] {
let vote = h.get_vote_attestation()?;
if h.number > next_checkpoint {
vote.verify(h.number, unwrap_n_val(h.number, &n_val)?)?;
vote.verify(h.number, unwrap_n_val(h.number, &n_val)?.validators())?;
} else if h.number > checkpoint {
vote.verify(h.number, unwrap_c_val(h.number, &c_val)?)?;
vote.verify(h.number, unwrap_c_val(h.number, &c_val)?.validators())?;
} else {
vote.verify(h.number, p_val)?;
}
Expand Down Expand Up @@ -119,7 +119,7 @@ impl ETHHeaders {
next_checkpoint: u64,
previous_epoch: &TrustedEpoch,
current_epoch: &'a EitherEpoch,
) -> Result<(Option<&'a Validators>, Option<&'b Validators>), Error> {
) -> Result<(Option<&'a Epoch>, Option<&'b Epoch>), Error> {
let hs: Vec<&ETHHeader> = self.all.iter().filter(|h| h.number >= checkpoint).collect();
match current_epoch {
// ex) t=200 then 200 <= h < 411 (c_val(200) can be borrowed by p_val)
Expand Down Expand Up @@ -147,14 +147,14 @@ impl ETHHeaders {
.epoch
.as_ref()
.ok_or_else(|| MissingEpochInfoInEpochBlock(h.number))?,
None => return Ok((Some(trusted.validators()), None)),
None => return Ok((Some(trusted.epoch()), None)),
};

// Finish if no headers over next checkpoint were found
let hs: Vec<&&ETHHeader> =
hs.iter().filter(|h| h.number >= next_checkpoint).collect();
if hs.is_empty() {
return Ok((Some(trusted.validators()), None));
return Ok((Some(trusted.epoch()), None));
}

// Ensure n_val(400) can be borrowed by c_val(200)
Expand All @@ -168,7 +168,7 @@ impl ETHHeaders {
next_next_checkpoint,
));
}
Ok((Some(trusted.validators()), Some(next_epoch.validators())))
Ok((Some(trusted.epoch()), Some(next_epoch)))
}
}
}
Expand Down Expand Up @@ -218,17 +218,11 @@ fn verify_finalized(
Ok(())
}

fn unwrap_n_val<'a>(
n: BlockNumber,
n_val: &'a Option<&'a Validators>,
) -> Result<&'a Validators, Error> {
fn unwrap_n_val<'a>(n: BlockNumber, n_val: &'a Option<&'a Epoch>) -> Result<&'a Epoch, Error> {
n_val.ok_or_else(|| Error::MissingNextValidatorSet(n))
}

fn unwrap_c_val<'a>(
n: BlockNumber,
c_val: &'a Option<&'a Validators>,
) -> Result<&'a Validators, Error> {
fn unwrap_c_val<'a>(n: BlockNumber, c_val: &'a Option<&'a Epoch>) -> Result<&'a Epoch, Error> {
c_val.ok_or_else(|| Error::MissingCurrentValidatorSet(n))
}

Expand Down
Loading