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

Implement verify_light() and verify_light_trusting() #1226

Merged
merged 17 commits into from
Nov 24, 2022
Merged
Show file tree
Hide file tree
Changes from 11 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `[light-client]` Added `validate`, `validate_against_trusted`, `verify_commit` and `verify_commit_against_trusted` methods to `PredicateVerifier`.
([#1222](https://github.com/informalsystems/tendermint-rs/issues/1222))
3 changes: 1 addition & 2 deletions light-client-verifier/src/operations/voting_power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ pub trait VotingPowerCalculator: Send + Sync {
}
}

/// Check against the given threshold that there is enough signers
/// overlap between an untrusted header and untrusted validator set
/// Check if there is 2/3rd overlap between an untrusted header and untrusted validator set
fn check_signers_overlap(
&self,
untrusted_header: &SignedHeader,
Expand Down
175 changes: 108 additions & 67 deletions light-client-verifier/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,119 +111,160 @@ where
hasher,
}
}
}

impl<P, C, V, H> Verifier for PredicateVerifier<P, C, V, H>
where
P: VerificationPredicates,
C: VotingPowerCalculator,
V: CommitValidator,
H: Hasher,
{
/// Validate the given light block state.
///
/// - Ensure the latest trusted header hasn't expired
/// - Ensure the header validator hashes match the given validators
/// - Ensure the header next validator hashes match the given next validators
/// - Additional implementation specific validation via `commit_validator`
/// - Check that the untrusted block is more recent than the trusted state
/// - If the untrusted block is the very next block after the trusted block, check that their
/// (next) validator sets hashes match.
/// - Otherwise, ensure that the untrusted block has a greater height than the trusted block.
///
/// **NOTE**: If the untrusted state's `next_validators` field is `None`,
/// this will not (and will not be able to) check whether the untrusted
/// state's `next_validators_hash` field is valid.
fn verify(
&self,
untrusted: UntrustedBlockState<'_>,
trusted: TrustedBlockState<'_>,
options: &Options,
now: Time,
) -> Verdict {
// Ensure the latest trusted header hasn't expired
verdict!(self.predicates.is_within_trust_period(
trusted.header_time,
options.trusting_period,
now,
));

// Ensure the header isn't from a future time
verdict!(self.predicates.is_header_from_past(
untrusted.signed_header.header.time,
options.clock_drift,
now,
));

/// Validates an `UntrustedBlockState`.
pub fn validate(&self, untrusted: &UntrustedBlockState<'_>) -> Result<(), VerificationError> {
// Ensure the header validator hashes match the given validators
verdict!(self.predicates.validator_sets_match(
self.predicates.validator_sets_match(
untrusted.validators,
untrusted.signed_header.header.validators_hash,
&self.hasher,
));
)?;

// TODO(thane): Is this check necessary for IBC?
if let Some(untrusted_next_validators) = untrusted.next_validators {
// Ensure the header next validator hashes match the given next validators
hu55a1n1 marked this conversation as resolved.
Show resolved Hide resolved
verdict!(self.predicates.next_validators_match(
self.predicates.next_validators_match(
untrusted_next_validators,
untrusted.signed_header.header.next_validators_hash,
&self.hasher,
));
)?;
}

// Ensure the header matches the commit
verdict!(self.predicates.header_matches_commit(
self.predicates.header_matches_commit(
&untrusted.signed_header.header,
untrusted.signed_header.commit.block_id.hash,
&self.hasher,
));
)?;

// Additional implementation specific validation
verdict!(self.predicates.valid_commit(
self.predicates.valid_commit(
untrusted.signed_header,
untrusted.validators,
&self.commit_validator,
));
)?;

Ok(())
}

/// Verify that more than 2/3 of the validators correctly committed the block.
pub fn verify_commit(
&self,
untrusted: &UntrustedBlockState<'_>,
) -> Result<(), VerificationError> {
self.predicates.has_sufficient_signers_overlap(
untrusted.signed_header,
untrusted.validators,
&self.voting_power_calculator,
)?;

Ok(())
}

/// Validate an `UntrustedBlockState`, based on the given `TrustedBlockState`, `Options` and
/// current time.
pub fn validate_against_trusted(
&self,
untrusted: &UntrustedBlockState<'_>,
trusted: &TrustedBlockState<'_>,
options: &Options,
now: Time,
) -> Result<(), VerificationError> {
// Ensure the latest trusted header hasn't expired
self.predicates.is_within_trust_period(
trusted.header_time,
options.trusting_period,
now,
)?;

// Ensure the header isn't from a future time
self.predicates.is_header_from_past(
untrusted.signed_header.header.time,
options.clock_drift,
now,
)?;

// Check that the untrusted block is more recent than the trusted state
verdict!(self
.predicates
.is_monotonic_bft_time(untrusted.signed_header.header.time, trusted.header_time,));
self.predicates
.is_monotonic_bft_time(untrusted.signed_header.header.time, trusted.header_time)?;

let trusted_next_height = trusted.height.increment();

if untrusted.height() == trusted_next_height {
// If the untrusted block is the very next block after the trusted block,
// check that their (next) validator sets hashes match.
verdict!(self.predicates.valid_next_validator_set(
self.predicates.valid_next_validator_set(
untrusted.signed_header.header.validators_hash,
trusted.next_validators_hash,
));
)?;
} else {
// Otherwise, ensure that the untrusted block has a greater height than
// the trusted block.
verdict!(self
.predicates
.is_monotonic_height(untrusted.signed_header.header.height, trusted.height));
self.predicates
.is_monotonic_height(untrusted.signed_header.header.height, trusted.height)?;
}

Ok(())
}

/// Check there is enough overlap between the validator sets of the trusted and untrusted
/// blocks.
pub fn verify_commit_against_trusted(
&self,
untrusted: &UntrustedBlockState<'_>,
trusted: &TrustedBlockState<'_>,
options: &Options,
) -> Result<(), VerificationError> {
let trusted_next_height = trusted.height.increment();

if untrusted.height() != trusted_next_height {
// Check there is enough overlap between the validator sets of
// the trusted and untrusted blocks.
verdict!(self.predicates.has_sufficient_validators_overlap(
self.predicates.has_sufficient_validators_overlap(
untrusted.signed_header,
trusted.next_validators,
&options.trust_threshold,
&self.voting_power_calculator,
));
)?;
}

// Verify that more than 2/3 of the validators correctly committed the block.
verdict!(self.predicates.has_sufficient_signers_overlap(
untrusted.signed_header,
untrusted.validators,
&self.voting_power_calculator,
));
Ok(())
}
}

impl<P, C, V, H> Verifier for PredicateVerifier<P, C, V, H>
where
P: VerificationPredicates,
C: VotingPowerCalculator,
V: CommitValidator,
H: Hasher,
{
/// Validate the given light block state.
///
/// - Ensure the latest trusted header hasn't expired
/// - Ensure the header validator hashes match the given validators
/// - Ensure the header next validator hashes match the given next validators
/// - Additional implementation specific validation via `commit_validator`
/// - Check that the untrusted block is more recent than the trusted state
/// - If the untrusted block is the very next block after the trusted block, check that their
/// (next) validator sets hashes match.
/// - Otherwise, ensure that the untrusted block has a greater height than the trusted block.
///
/// **NOTE**: If the untrusted state's `next_validators` field is `None`,
/// this will not (and will not be able to) check whether the untrusted
/// state's `next_validators_hash` field is valid.
ancazamfir marked this conversation as resolved.
Show resolved Hide resolved
fn verify(
&self,
untrusted: UntrustedBlockState<'_>,
trusted: TrustedBlockState<'_>,
options: &Options,
now: Time,
) -> Verdict {
verdict!(self.validate(&untrusted));
verdict!(self.validate_against_trusted(&untrusted, &trusted, options, now));
verdict!(self.verify_commit_against_trusted(&untrusted, &trusted, options));
verdict!(self.verify_commit(&untrusted));
Verdict::Success
}
}
Expand Down
1 change: 0 additions & 1 deletion light-client/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ pub fn verify_single(
now: Time,
) -> Result<LightBlock, Verdict> {
let verifier = ProdVerifier::default();

let options = Options {
trust_threshold,
trusting_period,
Expand Down