diff --git a/tendermint/src/lite/types.rs b/tendermint/src/lite/types.rs index a8f95bc5f..fc604a85e 100644 --- a/tendermint/src/lite/types.rs +++ b/tendermint/src/lite/types.rs @@ -102,6 +102,19 @@ pub trait Vote { fn signature(&self) -> &[u8]; } +/// TrustLevel defines what fraction of the total voting power of a known +/// and trusted validator set is sufficient for a commit to be +/// accepted going forward. +/// The default implementation returns true, iff at least a third of the trusted +/// voting power signed (in other words at least one "trusted" validator signed). +/// Some clients might require more than +1/3 and can implement their own +/// TrustLevel which can be passed into all relevant methods. +pub trait TrustLevel { + fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool { + signed_voting_power * 3 > total_voting_power + } +} + #[derive(Debug)] pub enum Error { Expired, diff --git a/tendermint/src/lite/verifier.rs b/tendermint/src/lite/verifier.rs index 4579a68cd..0e8056e94 100644 --- a/tendermint/src/lite/verifier.rs +++ b/tendermint/src/lite/verifier.rs @@ -1,5 +1,7 @@ #[allow(clippy::all)] -use crate::lite::{Commit, Error, Header, Validator, ValidatorSet, ValidatorSetLookup, Vote}; +use crate::lite::{ + Commit, Error, Header, TrustLevel, Validator, ValidatorSet, ValidatorSetLookup, Vote, +}; use crate::Time; use std::time::Duration; @@ -71,23 +73,25 @@ where /// Verify the commit is trusted according to the last validators and is valid /// from the current validators for the header. -pub fn verify_trusting( +pub fn verify_trusting( header: H, commit: C, last_validators: V, validators: V, + trust_level: L, ) -> Result<(), Error> where H: Header, V: ValidatorSetLookup, C: Commit, + L: TrustLevel, { // NOTE it might be more prudent to do the cheap validations first // before we even call verify_commit_trusting, but not doing that // makes the code cleaner and allows us to just call verify directly. // ensure that +1/3 of last trusted validators signed correctly - if let Err(e) = verify_commit_trusting(&last_validators, &commit) { + if let Err(e) = verify_commit_trusting(&last_validators, &commit, trust_level) { return Err(e); } @@ -144,14 +148,13 @@ where /// Verify that +1/3 of the given validator set signed this commit. /// NOTE the given validators do not necessarily correspond to the validator set for this commit, -/// but there may be some intersection. -/// TODO: this should take a "trust_level" param to allow clients to require more -/// than +1/3. How should this be defined semantically? Probably shouldn't be a float, maybe -/// and enum of options, eg. 1/3, 1/2, 2/3, 1 ? -fn verify_commit_trusting(validators: &V, commit: &C) -> Result<(), Error> +/// but there may be some intersection. The trust_level parameter allows clients to require more +/// than +1/3 by implementing the TrustLevel trait accordingly. +fn verify_commit_trusting(validators: &V, commit: &C, trust_level: L) -> Result<(), Error> where V: ValidatorSetLookup, C: Commit, + L: TrustLevel, { let total_power = validators.total_power(); let mut signed_power: u64 = 0; @@ -188,7 +191,8 @@ where // check the signers account for +1/3 of the voting power // TODO: incorporate "trust_level" in here to possibly increase // beyond 1/3. - if signed_power * 3 <= total_power { + + if !trust_level.is_enough_power(signed_power, total_power) { return Err(Error::InsufficientVotingPower); } diff --git a/tendermint/tests/lite.rs b/tendermint/tests/lite.rs index 251a8f916..d00ca6d71 100644 --- a/tendermint/tests/lite.rs +++ b/tendermint/tests/lite.rs @@ -17,12 +17,16 @@ fn read_json_fixture(name: &str) -> String { #[test] fn check_verifier_with_mock_data() { + pub struct DefaultTrustLevel {} + impl lite::TrustLevel for DefaultTrustLevel {} + let suite: TestSuite = serde_json::from_str(&read_json_fixture("basic")).unwrap(); lite::verify_trusting( suite.signed_header.header.clone(), suite.signed_header, validator::Set::new(suite.last_validators), validator::Set::new(suite.validators), + DefaultTrustLevel {}, ) .expect("verify_trusting failed"); }