Skip to content

Commit

Permalink
add randomized overlap committee generator
Browse files Browse the repository at this point in the history
  • Loading branch information
pls148 committed Nov 13, 2024
1 parent f7bd253 commit 8693058
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 27 deletions.
23 changes: 13 additions & 10 deletions crates/example-types/src/node_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
// You should have received a copy of the MIT License
// along with the HotShot repository. If not, see <https://mit-license.org/>.

use std::marker::PhantomData;

pub use hotshot::traits::election::helpers::{
RandomOverlapQuorumFilterConfig, StableQuorumFilterConfig,
};
use hotshot::traits::{
election::{
randomized_committee::RandomizedCommittee,
helpers::QuorumFilterConfig, randomized_committee::RandomizedCommittee,
randomized_committee_members::RandomizedCommitteeMembers,
static_committee::StaticCommittee,
static_committee_leader_two_views::StaticCommitteeLeaderForTwoViews,
Expand Down Expand Up @@ -104,10 +109,11 @@ impl NodeType for TestTypesRandomizedLeader {
)]
/// filler struct to implement node type and allow us
/// to select our traits
pub struct TestTypesRandomizedCommitteeMembers<const SEED: u64, const OVERLAP: u64>;
impl<const SEED: u64, const OVERLAP: u64> NodeType
for TestTypesRandomizedCommitteeMembers<SEED, OVERLAP>
{
pub struct TestTypesRandomizedCommitteeMembers<CONFIG: QuorumFilterConfig> {
_pd: PhantomData<CONFIG>,
}

impl<CONFIG: QuorumFilterConfig> NodeType for TestTypesRandomizedCommitteeMembers<CONFIG> {
type AuctionResult = TestAuctionResult;
type View = ViewNumber;
type Epoch = EpochNumber;
Expand All @@ -117,11 +123,8 @@ impl<const SEED: u64, const OVERLAP: u64> NodeType
type Transaction = TestTransaction;
type ValidatedState = TestValidatedState;
type InstanceState = TestInstanceState;
type Membership = RandomizedCommitteeMembers<
TestTypesRandomizedCommitteeMembers<SEED, OVERLAP>,
SEED,
OVERLAP,
>;
type Membership =
RandomizedCommitteeMembers<TestTypesRandomizedCommitteeMembers<CONFIG>, CONFIG>;
type BuilderSignatureKey = BuilderKey;
}

Expand Down
110 changes: 109 additions & 1 deletion crates/hotshot/src/traits/election/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// You should have received a copy of the MIT License
// along with the HotShot repository. If not, see <https://mit-license.org/>.

use std::collections::BTreeSet;
use std::{collections::BTreeSet, hash::Hash};

use rand::{rngs::StdRng, Rng, SeedableRng};

Expand Down Expand Up @@ -102,7 +102,12 @@ fn calc_num_slots(count: u64, odd: bool) -> u64 {
}

impl StableQuorumIterator {
#[must_use]
/// Create a new StableQuorumIterator
///
/// # Panics
///
/// panics if overlap is greater than half of count
pub fn new(seed: u64, round: u64, count: u64, overlap: u64) -> Self {
assert!(
count / 2 > overlap,
Expand Down Expand Up @@ -142,7 +147,12 @@ impl Iterator for StableQuorumIterator {
}
}

#[must_use]
/// Helper function to convert the arguments to a StableQuorumIterator into an ordered set of values.
///
/// # Panics
///
/// panics if the arguments are invalid for StableQuorumIterator::new
pub fn stable_quorum_filter(seed: u64, round: u64, count: usize, overlap: u64) -> BTreeSet<usize> {
StableQuorumIterator::new(seed, round, count as u64, overlap)
// We should never have more than u32_max members in a test
Expand Down Expand Up @@ -175,7 +185,12 @@ pub struct RandomOverlapQuorumIterator {
}

impl RandomOverlapQuorumIterator {
#[must_use]
/// Create a new RandomOverlapQuorumIterator
///
/// # Panics
///
/// panics if overlap and members can produce invalid results or if ranges are invalid
pub fn new(
seed: u64,
round: u64,
Expand Down Expand Up @@ -241,6 +256,99 @@ impl Iterator for RandomOverlapQuorumIterator {
}
}

#[must_use]
/// Helper function to convert the arguments to a StableQuorumIterator into an ordered set of values.
///
/// # Panics
///
/// panics if the arguments are invalid for RandomOverlapQuorumIterator::new
pub fn random_overlap_quorum_filter(
seed: u64,
round: u64,
count: usize,
members_min: u64,
members_max: u64,
overlap_min: u64,
overlap_max: u64,
) -> BTreeSet<usize> {
RandomOverlapQuorumIterator::new(
seed,
round,
count as u64,
members_min,
members_max,
overlap_min,
overlap_max,
)
// We should never have more than u32_max members in a test
.map(|x| usize::try_from(x).unwrap())
.collect()
}

/// Trait wrapping a config for quorum filters. This allows selection between either the StableQuorumIterator or the
/// RandomOverlapQuorumIterator functionality from above
pub trait QuorumFilterConfig:
Copy
+ Clone
+ std::fmt::Debug
+ Default
+ Send
+ Sync
+ Ord
+ PartialOrd
+ Eq
+ PartialEq
+ Hash
+ 'static
{
/// Called to run the filter and return a set of indices
fn execute(epoch: u64, count: usize) -> BTreeSet<usize>;
}

#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
/// Provides parameters to use the StableQuorumIterator
pub struct StableQuorumFilterConfig<const SEED: u64, const OVERLAP: u64> {}

impl<const SEED: u64, const OVERLAP: u64> QuorumFilterConfig
for StableQuorumFilterConfig<SEED, OVERLAP>
{
fn execute(epoch: u64, count: usize) -> BTreeSet<usize> {
stable_quorum_filter(SEED, epoch, count, OVERLAP)
}
}

#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
/// Provides parameters to use the RandomOverlapQuorumIterator
pub struct RandomOverlapQuorumFilterConfig<
const SEED: u64,
const MEMBERS_MIN: u64,
const MEMBERS_MAX: u64,
const OVERLAP_MIN: u64,
const OVERLAP_MAX: u64,
> {}

impl<
const SEED: u64,
const MEMBERS_MIN: u64,
const MEMBERS_MAX: u64,
const OVERLAP_MIN: u64,
const OVERLAP_MAX: u64,
> QuorumFilterConfig
for RandomOverlapQuorumFilterConfig<SEED, MEMBERS_MIN, MEMBERS_MAX, OVERLAP_MIN, OVERLAP_MAX>
{
fn execute(epoch: u64, count: usize) -> BTreeSet<usize> {
random_overlap_quorum_filter(
SEED,
epoch,
count,
MEMBERS_MIN,
MEMBERS_MAX,
OVERLAP_MIN,
OVERLAP_MAX,
)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/hotshot/src/traits/election/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ pub mod static_committee;
pub mod static_committee_leader_two_views;

/// general helpers
mod helpers;
pub mod helpers;
21 changes: 11 additions & 10 deletions crates/hotshot/src/traits/election/randomized_committee_members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::{
cmp::max,
collections::{BTreeMap, BTreeSet},
marker::PhantomData,
num::NonZeroU64,
};

Expand All @@ -23,13 +24,11 @@ use hotshot_types::{
use rand::{rngs::StdRng, Rng};
use utils::anytrace::Result;

use super::helpers::stable_quorum_filter;
use crate::traits::election::helpers::QuorumFilterConfig;

#[derive(Clone, Debug, Eq, PartialEq, Hash)]

/// The static committee election
pub struct RandomizedCommitteeMembers<T: NodeType, const SEED: u64, const OVERLAP: u64> {
pub struct RandomizedCommitteeMembers<T: NodeType, C: QuorumFilterConfig> {
/// The nodes eligible for leadership.
/// NOTE: This is currently a hack because the DA leader needs to be the quorum
/// leader but without voting rights.
Expand All @@ -44,19 +43,20 @@ pub struct RandomizedCommitteeMembers<T: NodeType, const SEED: u64, const OVERLA

/// The network topic of the committee
committee_topic: Topic,

/// Phantom
_pd: PhantomData<C>,
}

impl<TYPES: NodeType, const SEED: u64, const OVERLAP: u64>
RandomizedCommitteeMembers<TYPES, SEED, OVERLAP>
{
impl<TYPES: NodeType, CONFIG: QuorumFilterConfig> RandomizedCommitteeMembers<TYPES, CONFIG> {
/// Creates a set of indices into the stake_table which reference the nodes selected for this epoch's committee
fn make_quorum_filter(&self, epoch: <TYPES as NodeType>::Epoch) -> BTreeSet<usize> {
stable_quorum_filter(SEED, epoch.u64(), self.stake_table.len(), OVERLAP)
CONFIG::execute(epoch.u64(), self.stake_table.len())
}
}

impl<TYPES: NodeType, const SEED: u64, const OVERLAP: u64> Membership<TYPES>
for RandomizedCommitteeMembers<TYPES, SEED, OVERLAP>
impl<TYPES: NodeType, CONFIG: QuorumFilterConfig> Membership<TYPES>
for RandomizedCommitteeMembers<TYPES, CONFIG>
{
type Error = utils::anytrace::Error;

Expand Down Expand Up @@ -102,6 +102,7 @@ impl<TYPES: NodeType, const SEED: u64, const OVERLAP: u64> Membership<TYPES>
stake_table: members,
indexed_stake_table,
committee_topic,
_pd: PhantomData,
}
}

Expand Down
40 changes: 39 additions & 1 deletion crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use syn::{
parse::{Parse, ParseStream, Result},
parse_macro_input,
punctuated::Punctuated,
Expr, ExprArray, ExprPath, ExprTuple, Ident, LitBool, Token, TypePath,
Expr, ExprArray, ExprPath, ExprTuple, Ident, LitBool, PathArguments, Token, TypePath,
};

/// Bracketed types, e.g. [A, B, C<D>]
Expand Down Expand Up @@ -108,13 +108,51 @@ impl ToLowerSnakeStr for ExprPath {
}
}

impl ToLowerSnakeStr for syn::GenericArgument {
/// allow panic because this is a compiler error
#[allow(clippy::panic)]
fn to_lower_snake_str(&self) -> String {
match self {
syn::GenericArgument::Lifetime(l) => l.ident.to_string().to_lowercase(),
syn::GenericArgument::Type(t) => match t {
syn::Type::Path(p) => p.to_lower_snake_str(),
_ => {
panic!("Unexpected type for GenericArgument::Type: {t:?}");
}
},
syn::GenericArgument::Const(c) => match c {
syn::Expr::Lit(l) => match &l.lit {
syn::Lit::Str(v) => format!("{}_", v.value().to_lowercase()),
syn::Lit::Int(v) => format!("{}_", v.base10_digits()),
_ => {
panic!("Unexpected type for GenericArgument::Const::Lit: {l:?}");
}
},
_ => {
panic!("Unexpected type for GenericArgument::Const: {c:?}");
}
},
_ => {
panic!("Unexpected type for GenericArgument: {self:?}");
}
}
}
}

impl ToLowerSnakeStr for TypePath {
fn to_lower_snake_str(&self) -> String {
self.path
.segments
.iter()
.fold(String::new(), |mut acc, s| {
acc.push_str(&s.ident.to_string().to_lowercase());
if let PathArguments::AngleBracketed(a) = &s.arguments {
acc.push('_');
for arg in &a.args {
acc.push_str(&arg.to_lower_snake_str());
}
}

acc.push('_');
acc
})
Expand Down
2 changes: 1 addition & 1 deletion crates/testing/tests/tests_1/block_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::{
use hotshot_builder_api::v0_1::block_info::AvailableBlockData;
use hotshot_example_types::{
block_types::{TestBlockPayload, TestMetadata, TestTransaction},
node_types::{TestTypes, TestVersions},
node_types::TestTypes,
};
use hotshot_task_impls::builder::{BuilderClient, BuilderClientError};
use hotshot_testing::block_builder::{
Expand Down
7 changes: 4 additions & 3 deletions crates/testing/tests/tests_1/test_success.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use std::time::Duration;

use hotshot_example_types::{
node_types::{
EpochsTestVersions, Libp2pImpl, MemoryImpl, PushCdnImpl, TestConsecutiveLeaderTypes,
TestTypes, TestTypesRandomizedCommitteeMembers, TestTypesRandomizedLeader, TestVersions,
EpochsTestVersions, Libp2pImpl, MemoryImpl, PushCdnImpl, StableQuorumFilterConfig,
TestConsecutiveLeaderTypes, TestTypes, TestTypesRandomizedCommitteeMembers,
TestTypesRandomizedLeader, TestVersions,
},
testable_delay::{DelayConfig, DelayOptions, DelaySettings, SupportedTraitTypesForAsyncDelay},
};
Expand Down Expand Up @@ -43,7 +44,7 @@ cross_tests!(
cross_tests!(
TestName: test_epoch_success,
Impls: [MemoryImpl, Libp2pImpl, PushCdnImpl],
Types: [TestTypes, TestTypesRandomizedLeader, TestTypesRandomizedCommitteeMembers<123, 2>],
Types: [TestTypes, TestTypesRandomizedLeader, TestTypesRandomizedCommitteeMembers<StableQuorumFilterConfig<123, 2>>],
Versions: [EpochsTestVersions],
Ignore: false,
Metadata: {
Expand Down

0 comments on commit 8693058

Please sign in to comment.