Skip to content

Commit

Permalink
3966 add_epoch_root and sync_l1 on Membership
Browse files Browse the repository at this point in the history
  • Loading branch information
pls148 committed Jan 2, 2025
1 parent 3d705c6 commit 2cb5789
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
67 changes: 66 additions & 1 deletion crates/task-impls/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,51 @@ pub(crate) async fn fetch_proposal<TYPES: NodeType, V: Versions>(
Ok((leaf, view))
}

/// Handles calling add_epoch_root and sync_l1 on Membership if necessary.
async fn decide_from_proposal_add_epoch_root<TYPES: NodeType>(
proposal: &QuorumProposal2<TYPES>,
leaf_views: &[LeafInfo<TYPES>],
epoch_height: u64,
membership: &Arc<RwLock<TYPES::Membership>>,
) {
let decided_block_number = leaf_views
.last()
.unwrap()
.leaf
.block_header()
.block_number();

// Skip if this is not the expected block.
if epoch_height != 0 && (decided_block_number + 3) % epoch_height == 0 {
let next_epoch_number =
TYPES::Epoch::new(epoch_from_block_number(decided_block_number, epoch_height) + 1);

let write_callback = {
let membership_reader = membership.read().await;
membership_reader
.add_epoch_root(next_epoch_number, proposal.block_header.clone())
.await
};

if let Some(write_callback) = write_callback {
let mut membership_writer = membership.write().await;
write_callback(&mut *membership_writer);
}

if TYPES::Membership::uses_sync_l1() {
let write_callback = {
let membership_reader = membership.read().await;
membership_reader.sync_l1().await
};

if let Some(write_callback) = write_callback {
let mut membership_writer = membership.write().await;
write_callback(&mut *membership_writer);
}
}
}
}

/// Helper type to give names and to the output values of the leaf chain traversal operation.
#[derive(Debug)]
pub struct LeafChainTraversalOutcome<TYPES: NodeType> {
Expand Down Expand Up @@ -202,7 +247,7 @@ impl<TYPES: NodeType + Default> Default for LeafChainTraversalOutcome<TYPES> {
}
}

/// calculate the new decided leaf chain based on the rules of hostuff 2
/// calculate the new decided leaf chain based on the rules of HotStuff 2
///
/// # Panics
/// Can't actually panic
Expand All @@ -211,6 +256,8 @@ pub async fn decide_from_proposal_2<TYPES: NodeType>(
consensus: OuterConsensus<TYPES>,
existing_upgrade_cert: Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
public_key: &TYPES::SignatureKey,
with_epochs: bool,
membership: &Arc<RwLock<TYPES::Membership>>,
) -> LeafChainTraversalOutcome<TYPES> {
let mut res = LeafChainTraversalOutcome::default();
let consensus_reader = consensus.read().await;
Expand Down Expand Up @@ -282,6 +329,14 @@ pub async fn decide_from_proposal_2<TYPES: NodeType>(
res.included_txns = Some(txns);
}

if with_epochs {
let epoch_height = consensus_reader.epoch_height;
drop(consensus_reader);

decide_from_proposal_add_epoch_root(proposal, &res.leaf_views, epoch_height, membership)
.await;
}

res
}

Expand Down Expand Up @@ -317,6 +372,8 @@ pub async fn decide_from_proposal<TYPES: NodeType>(
consensus: OuterConsensus<TYPES>,
existing_upgrade_cert: Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
public_key: &TYPES::SignatureKey,
with_epochs: bool,
membership: &Arc<RwLock<TYPES::Membership>>,
) -> LeafChainTraversalOutcome<TYPES> {
let consensus_reader = consensus.read().await;
let existing_upgrade_cert_reader = existing_upgrade_cert.read().await;
Expand Down Expand Up @@ -428,6 +485,14 @@ pub async fn decide_from_proposal<TYPES: NodeType>(
tracing::debug!("Leaf ascension failed; error={e}");
}

if with_epochs {
let epoch_height = consensus_reader.epoch_height;
drop(consensus_reader);

decide_from_proposal_add_epoch_root(proposal, &res.leaf_views, epoch_height, membership)
.await;
}

res
}

Expand Down
4 changes: 4 additions & 0 deletions crates/task-impls/src/quorum_vote/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ pub(crate) async fn handle_quorum_proposal_validated<
OuterConsensus::new(Arc::clone(&task_state.consensus.inner_consensus)),
Arc::clone(&task_state.upgrade_lock.decided_upgrade_certificate),
&task_state.public_key,
version >= V::Epochs::VERSION,
&task_state.membership,
)
.await
} else {
Expand All @@ -175,6 +177,8 @@ pub(crate) async fn handle_quorum_proposal_validated<
OuterConsensus::new(Arc::clone(&task_state.consensus.inner_consensus)),
Arc::clone(&task_state.upgrade_lock.decided_upgrade_certificate),
&task_state.public_key,
version >= V::Epochs::VERSION,
&task_state.membership,
)
.await
};
Expand Down
30 changes: 30 additions & 0 deletions crates/types/src/traits/election.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
//! The election trait, used to decide which node is the leader and determine if a vote is valid.
use std::{collections::BTreeSet, fmt::Debug, num::NonZeroU64};

use async_trait::async_trait;
use utils::anytrace::Result;

use super::node_implementation::NodeType;
use crate::{traits::signature_key::SignatureKey, PeerConfig};

#[async_trait]
/// A protocol for determining membership in and participating in a committee.
pub trait Membership<TYPES: NodeType>: Debug + Send + Sync {
/// The error type returned by methods like `lookup_leader`.
Expand Down Expand Up @@ -125,4 +127,32 @@ pub trait Membership<TYPES: NodeType>: Debug + Send + Sync {

/// Returns the threshold required to upgrade the network protocol
fn upgrade_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64;

#[allow(clippy::type_complexity)]
/// Handles notifications that a new epoch root has been created
/// Is called under a read lock to the Membership. Return a callback
/// with Some to have that callback invoked under a write lock.
async fn add_epoch_root(
&self,
_epoch: TYPES::Epoch,
_block_header: TYPES::BlockHeader,
) -> Option<Box<dyn FnOnce(&mut Self) + Send>> {
None
}

#[must_use]
/// Used to determine if sync_l1 should be called after add_epoch_root.
/// Returning false avoids a redundant read lock for cases where sync_l1
/// is a no-op.
fn uses_sync_l1() -> bool {
false
}

#[allow(clippy::type_complexity)]
/// Called after add_epoch_root runs and any callback has been invoked.
/// Causes a read lock to be reacquired for this functionality. If it is
/// not needed, then have uses_sync_l1 return false.
async fn sync_l1(&self) -> Option<Box<dyn FnOnce(&mut Self) + Send>> {
None
}
}

0 comments on commit 2cb5789

Please sign in to comment.