Skip to content

Commit

Permalink
Merge branch 'main' into event-attributes-as-vec
Browse files Browse the repository at this point in the history
  • Loading branch information
penso authored Apr 20, 2024
2 parents 0afbd1a + 6399b5b commit 7f60426
Show file tree
Hide file tree
Showing 28 changed files with 802 additions and 486 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `[tendermint-proto]` Remove redundant impl of `num_traits::ToPrimitive` for `BlockIDFlag`
([\#1389](https://github.com/informalsystems/tendermint-rs/pull/1389))
2 changes: 2 additions & 0 deletions .changelog/unreleased/features/1389-missing-serde-derives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `[tendermint-proto]` Add missing `serde` derives on Protobuf definitions
([\#1389](https://github.com/informalsystems/tendermint-rs/pull/1389))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[tendermint]` Allow misformed blocks (eg. with empty `last_commit`
on non-first block) when decoding them from Protobuf or RPC responses
([\#1403](https://github.com/informalsystems/tendermint-rs/issues/1403))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[light-client-verifier]` Optimise validators lookup in
`ProvidedVotingPowerCalculator::voting_power_in` method.
([\#1407](https://github.com/informalsystems/tendermint-rs/pull/1407))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[tendermint]` Check `index ≤ i32::MAX` invariant when converting `usize`
into `ValidatorIndex`.
([\#1411](https://github.com/informalsystems/tendermint-rs/issues/1411))
87 changes: 72 additions & 15 deletions light-client-verifier/src/operations/voting_power.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Provides an interface and default implementation for the `VotingPower` operation

use alloc::collections::BTreeSet as HashSet;
use alloc::vec::Vec;
use core::{fmt, marker::PhantomData};

use serde::{Deserialize, Serialize};
use tendermint::{
account,
block::CommitSig,
crypto::signature,
trust_threshold::TrustThreshold as _,
validator,
vote::{SignedVote, ValidatorIndex, Vote},
};

Expand Down Expand Up @@ -118,6 +120,58 @@ impl<V> Default for ProvidedVotingPowerCalculator<V> {
}
}

/// Dictionary of validators sorted by address.
///
/// The map stores reference to [`validator::Info`] object (typically held by
/// a `ValidatorSet`) and a boolean flag indicating whether the validator has
/// already been seen. The validators are sorted by their address such that
/// lookup by address is a logarithmic operation.
struct ValidatorMap<'a> {
validators: Vec<(&'a validator::Info, bool)>,
}

/// Error during validator lookup.
enum LookupError {
NotFound,
AlreadySeen,
}

impl<'a> ValidatorMap<'a> {
/// Constructs a new map from given list of validators.
///
/// Sorts the validators by address which makes the operation’s time
/// complexity `O(N lng N)`.
///
/// Produces unspecified result if two objects with the same address are
/// found. Unspecified in that it’s not guaranteed which entry will be
/// subsequently returned by [`Self::find_mut`] however it will always be
/// consistently the same entry.
pub fn new(validators: &'a [validator::Info]) -> Self {
let mut validators = validators.iter().map(|v| (v, false)).collect::<Vec<_>>();
validators.sort_unstable_by_key(|item| &item.0.address);
Self { validators }
}

/// Finds entry for validator with given address; returns error if validator
/// has been returned already by previous call to `find`.
///
/// Uses binary search resulting in logarithmic lookup time.
pub fn find(&mut self, address: &account::Id) -> Result<&'a validator::Info, LookupError> {
let index = self
.validators
.binary_search_by_key(&address, |item| &item.0.address)
.map_err(|_| LookupError::NotFound)?;

let (validator, seen) = &mut self.validators[index];
if *seen {
Err(LookupError::AlreadySeen)
} else {
*seen = true;
Ok(validator)
}
}
}

/// Default implementation of a `VotingPowerCalculator`.
#[cfg(feature = "rust-crypto")]
pub type ProdVotingPowerCalculator =
Expand All @@ -134,7 +188,6 @@ impl<V: signature::Verifier> VotingPowerCalculator for ProvidedVotingPowerCalcul
let total_voting_power = self.total_power_of(validator_set);

let mut tallied_voting_power = 0_u64;
let mut seen_validators = HashSet::new();

// Get non-absent votes from the signatures
let non_absent_votes = signatures.iter().enumerate().flat_map(|(idx, signature)| {
Expand All @@ -146,19 +199,23 @@ impl<V: signature::Verifier> VotingPowerCalculator for ProvidedVotingPowerCalcul
.map(|vote| (signature, vote))
});

for (signature, vote) in non_absent_votes {
// Ensure we only count a validator's power once
if seen_validators.contains(&vote.validator_address) {
return Err(VerificationError::duplicate_validator(
vote.validator_address,
));
} else {
seen_validators.insert(vote.validator_address);
}
// Create index of validators sorted by address.
let mut validators = ValidatorMap::new(validator_set.validators());

let validator = match validator_set.validator(vote.validator_address) {
Some(validator) => validator,
None => continue, // Cannot find matching validator, so we skip the vote
for (signature, vote) in non_absent_votes {
// Find the validator by address.
let validator = match validators.find(&vote.validator_address) {
Ok(validator) => validator,
Err(LookupError::NotFound) => {
// Cannot find matching validator, so we skip the vote
continue;
},
Err(LookupError::AlreadySeen) => {
// Ensure we only count a validator's power once
return Err(VerificationError::duplicate_validator(
vote.validator_address,
));
},
};

let signed_vote =
Expand All @@ -173,7 +230,7 @@ impl<V: signature::Verifier> VotingPowerCalculator for ProvidedVotingPowerCalcul
{
return Err(VerificationError::invalid_signature(
signed_vote.signature().as_bytes().to_vec(),
Box::new(validator),
Box::new(validator.clone()),
sign_bytes,
));
}
Expand Down
1 change: 1 addition & 0 deletions light-client-verifier/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ mod tests {
Verdict, Verifier,
};

#[allow(dead_code)]
#[cfg(feature = "rust-crypto")]
#[derive(Clone, Debug, PartialEq, Eq)]
struct ProdVerifierSupportsCommonDerivedTraits {
Expand Down
2 changes: 0 additions & 2 deletions proto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ bytes = { version = "1.0", default-features = false, features = ["serde"]}
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_bytes = { version = "0.11", default-features = false, features = ["alloc"] }
subtle-encoding = { version = "0.5", default-features = false, features = ["hex", "base64", "alloc"] }
num-traits = { version = "0.2", default-features = false }
num-derive = { version = "0.4", default-features = false }
time = { version = "0.3", default-features = false, features = ["macros", "parsing"] }
flex-error = { version = "0.4.4", default-features = false }
tonic = { version = "0.10", optional = true }
Expand Down
25 changes: 11 additions & 14 deletions proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,29 @@

extern crate alloc;

mod prelude;

/// Built-in prost_types with slight customization to enable JSON-encoding
#[allow(warnings)]
pub mod google {
pub mod protobuf {
// custom Timeout and Duration types that have valid doctest documentation texts
include!("protobuf.rs");
}
}

mod error;
#[allow(warnings)]
mod prelude;
mod tendermint;

use bytes::{Buf, BufMut};
use core::fmt::Display;
use prost::Message;

use bytes::{Buf, BufMut};
pub use error::Error;
use prost::Message;
pub use tendermint::*;

pub mod serializers;

use prelude::*;

/// Built-in prost_types with slight customization to enable JSON-encoding
pub mod google {
pub mod protobuf {
// custom Timeout and Duration types that have valid doctest documentation texts
include!("protobuf.rs");
}
}

/// Allows for easy Google Protocol Buffers encoding and decoding of domain
/// types with validation.
///
Expand Down
5 changes: 3 additions & 2 deletions proto/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Re-export according to alloc::prelude::v1 because it is not yet stabilized
// https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html

#[allow(unused_imports)]
pub use alloc::vec;
#![allow(unused_imports)]

pub use alloc::{
format,
string::{String, ToString},
vec,
vec::Vec,
};
pub use core::prelude::v1::*;
Loading

0 comments on commit 7f60426

Please sign in to comment.