Skip to content

Commit

Permalink
Merge pull request #3065 from AleoHQ/feat/committee-round-lag
Browse files Browse the repository at this point in the history
Use committee lookback in consensus
  • Loading branch information
howardwu authored Feb 10, 2024
2 parents 52b6a01 + afa2597 commit b0a7d18
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 130 deletions.
116 changes: 58 additions & 58 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ default-features = false

[workspace.dependencies.snarkvm]
git = "https://github.com/AleoHQ/snarkVM.git"
rev = "dbe4c95"
rev = "2d062ce"
#version = "=0.16.18"
features = [ "circuit", "console", "rocks" ]

Expand Down
13 changes: 8 additions & 5 deletions node/bft/ledger-service/src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,21 @@ impl<N: Network, C: ConsensusStorage<N>> LedgerService<N> for CoreLedgerService<
}
}

/// Returns the previous committee for the given round.
/// If the previous round is in the future, then the current committee is returned.
fn get_previous_committee_for_round(&self, round: u64) -> Result<Committee<N>> {
/// Returns the committee lookback for the given round.
/// If the committee lookback round is in the future, then the current committee is returned.
fn get_committee_lookback_for_round(&self, round: u64) -> Result<Committee<N>> {
// Get the round number for the previous committee. Note, we subtract 2 from odd rounds,
// because committees are updated in even rounds.
let previous_round = match round % 2 == 0 {
true => round.saturating_sub(1),
false => round.saturating_sub(2),
};

// Retrieve the committee for the previous round.
self.get_committee_for_round(previous_round)
// Get the committee lookback round.
let committee_lookback_round = previous_round.saturating_sub(Committee::<N>::COMMITTEE_LOOKBACK_RANGE);

// Retrieve the committee for the committee lookback round.
self.get_committee_for_round(committee_lookback_round)
}

/// Returns `true` if the ledger contains the given certificate ID in block history.
Expand Down
4 changes: 2 additions & 2 deletions node/bft/ledger-service/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ impl<N: Network> LedgerService<N> for MockLedgerService<N> {
Ok(self.committee.clone())
}

/// Returns the previous committee for the given round.
fn get_previous_committee_for_round(&self, _round: u64) -> Result<Committee<N>> {
/// Returns the committee lookback for the given round.
fn get_committee_lookback_for_round(&self, _round: u64) -> Result<Committee<N>> {
Ok(self.committee.clone())
}

Expand Down
6 changes: 3 additions & 3 deletions node/bft/ledger-service/src/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ impl<N: Network> LedgerService<N> for ProverLedgerService<N> {
bail!("Committee for round {round} does not exist in prover")
}

/// Returns the previous committee for the given round.
/// If the previous round is in the future, then the current committee is returned.
fn get_previous_committee_for_round(&self, round: u64) -> Result<Committee<N>> {
/// Returns the committee lookback for the given round.
/// If the committee lookback round is in the future, then the current committee is returned.
fn get_committee_lookback_for_round(&self, round: u64) -> Result<Committee<N>> {
bail!("Previous committee for round {round} does not exist in prover")
}

Expand Down
6 changes: 3 additions & 3 deletions node/bft/ledger-service/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ pub trait LedgerService<N: Network>: Debug + Send + Sync {
/// If the given round is in the future, then the current committee is returned.
fn get_committee_for_round(&self, round: u64) -> Result<Committee<N>>;

/// Returns the previous committee for the given round.
/// If the previous round is in the future, then the current committee is returned.
fn get_previous_committee_for_round(&self, round: u64) -> Result<Committee<N>>;
/// Returns the committee lookback for the given round.
/// If the committee lookback round is in the future, then the current committee is returned.
fn get_committee_lookback_for_round(&self, round: u64) -> Result<Committee<N>>;

/// Returns `true` if the ledger contains the given certificate ID.
fn contains_certificate(&self, certificate_id: &Field<N>) -> Result<bool>;
Expand Down
5 changes: 3 additions & 2 deletions node/bft/ledger-service/src/translucent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ impl<N: Network, C: ConsensusStorage<N>> LedgerService<N> for TranslucentLedgerS
self.inner.get_committee_for_round(round)
}

fn get_previous_committee_for_round(&self, round: u64) -> Result<Committee<N>> {
self.inner.get_previous_committee_for_round(round)
/// Returns the committee lookback for the given round.
fn get_committee_lookback_for_round(&self, round: u64) -> Result<Committee<N>> {
self.inner.get_committee_lookback_for_round(round)
}

/// Returns `true` if the ledger contains the given certificate ID in block history.
Expand Down
38 changes: 19 additions & 19 deletions node/bft/src/bft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,16 +291,16 @@ impl<N: Network> BFT<N> {
return false;
}

// Retrieve the previous committee of the current round.
let previous_committee = match self.ledger().get_previous_committee_for_round(current_round) {
// Retrieve the committee lookback of the current round.
let committee_lookback = match self.ledger().get_committee_lookback_for_round(current_round) {
Ok(committee) => committee,
Err(e) => {
error!("BFT failed to retrieve the previous committee for the even round {current_round} - {e}");
error!("BFT failed to retrieve the committee lookback for the even round {current_round} - {e}");
return false;
}
};
// Determine the leader of the current round.
let leader = match previous_committee.get_leader(current_round) {
let leader = match committee_lookback.get_leader(current_round) {
Ok(leader) => leader,
Err(e) => {
error!("BFT failed to compute the leader for the even round {current_round} - {e}");
Expand All @@ -311,7 +311,7 @@ impl<N: Network> BFT<N> {
let leader_certificate = current_certificates.iter().find(|certificate| certificate.author() == leader);
*self.leader_certificate.write() = leader_certificate.cloned();

self.is_even_round_ready_for_next_round(current_certificates, previous_committee, current_round)
self.is_even_round_ready_for_next_round(current_certificates, committee_lookback, current_round)
}

/// Returns 'true' under one of the following conditions:
Expand Down Expand Up @@ -375,21 +375,21 @@ impl<N: Network> BFT<N> {
let leader_certificate_id = leader_certificate.id();
// Retrieve the certificates for the current round.
let current_certificates = self.storage().get_certificates_for_round(current_round);
// Retrieve the previous committee of the current round.
let previous_committee = match self.ledger().get_previous_committee_for_round(current_round) {
// Retrieve the committee lookback for the current round.
let committee_lookback = match self.ledger().get_committee_lookback_for_round(current_round) {
Ok(committee) => committee,
Err(e) => {
error!("BFT failed to retrieve the previous committee for the odd round {current_round} - {e}");
error!("BFT failed to retrieve the committee lookback for the odd round {current_round} - {e}");
return false;
}
};

// Compute the stake for the leader certificate.
let (stake_with_leader, stake_without_leader) =
self.compute_stake_for_leader_certificate(leader_certificate_id, current_certificates, &previous_committee);
self.compute_stake_for_leader_certificate(leader_certificate_id, current_certificates, &committee_lookback);
// Return 'true' if any of the following conditions hold:
stake_with_leader >= previous_committee.availability_threshold()
|| stake_without_leader >= previous_committee.quorum_threshold()
stake_with_leader >= committee_lookback.availability_threshold()
|| stake_without_leader >= committee_lookback.quorum_threshold()
|| self.is_timer_expired()
}

Expand Down Expand Up @@ -448,12 +448,12 @@ impl<N: Network> BFT<N> {
return Ok(());
}

// Retrieve the previous committee for the commit round.
let Ok(previous_committee) = self.ledger().get_previous_committee_for_round(commit_round) else {
bail!("BFT failed to retrieve the committee for commit round {commit_round}");
// Retrieve the committee lookback for the commit round.
let Ok(committee_lookback) = self.ledger().get_committee_lookback_for_round(commit_round) else {
bail!("BFT failed to retrieve the committee with lag for commit round {commit_round}");
};
// Compute the leader for the commit round.
let Ok(leader) = previous_committee.get_leader(commit_round) else {
let Ok(leader) = committee_lookback.get_leader(commit_round) else {
bail!("BFT failed to compute the leader for commit round {commit_round}");
};
// Retrieve the leader certificate for the commit round.
Expand All @@ -476,7 +476,7 @@ impl<N: Network> BFT<N> {
})
.collect();
// Check if the leader is ready to be committed.
if !previous_committee.is_availability_threshold_reached(&authors) {
if !committee_lookback.is_availability_threshold_reached(&authors) {
// If the leader is not ready to be committed, return early.
trace!("BFT is not ready to commit {commit_round}");
return Ok(());
Expand Down Expand Up @@ -508,14 +508,14 @@ impl<N: Network> BFT<N> {
for round in (self.dag.read().last_committed_round() + 2..=leader_round.saturating_sub(2)).rev().step_by(2)
{
// Retrieve the previous committee for the leader round.
let previous_committee = match self.ledger().get_previous_committee_for_round(round) {
let previous_committee_lookback = match self.ledger().get_committee_lookback_for_round(round) {
Ok(committee) => committee,
Err(e) => {
bail!("BFT failed to retrieve the previous committee for the even round {round} - {e}");
bail!("BFT failed to retrieve a previous committee lookback for the even round {round} - {e}");
}
};
// Compute the leader address for the leader round.
let leader = match previous_committee.get_leader(round) {
let leader = match previous_committee_lookback.get_leader(round) {
Ok(leader) => leader,
Err(e) => {
bail!("BFT failed to compute the leader for the even round {round} - {e}");
Expand Down
4 changes: 2 additions & 2 deletions node/bft/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,13 @@ impl<N: Network> Gateway<N> {

/// Returns `true` if the given address is an authorized validator.
pub fn is_authorized_validator_address(&self, validator_address: Address<N>) -> bool {
// Determine if the validator address is a member of the previous or current committee.
// Determine if the validator address is a member of the committee lookback or the current committee.
// We allow leniency in this validation check in order to accommodate these two scenarios:
// 1. New validators should be able to connect immediately once bonded as a committee member.
// 2. Existing validators must remain connected until they are no longer bonded as a committee member.
// (i.e. meaning they must stay online until the next block has been produced)
self.ledger
.get_previous_committee_for_round(self.ledger.latest_round())
.get_committee_lookback_for_round(self.ledger.latest_round())
.map_or(false, |committee| committee.is_committee_member(validator_address))
|| self
.ledger
Expand Down
24 changes: 12 additions & 12 deletions node/bft/src/helpers/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,12 @@ impl<N: Network> Storage<N> {
bail!("Batch for round {round} already exists in storage {gc_log}")
}

// Retrieve the previous committee for the batch round.
let Ok(previous_committee) = self.ledger.get_previous_committee_for_round(round) else {
bail!("Storage failed to retrieve the committee for round {round} {gc_log}")
// Retrieve the committee lookback for the batch round.
let Ok(committee_lookback) = self.ledger.get_committee_lookback_for_round(round) else {
bail!("Storage failed to retrieve the committee lookback for round {round} {gc_log}")
};
// Ensure the author is in the committee.
if !previous_committee.is_committee_member(batch_header.author()) {
if !committee_lookback.is_committee_member(batch_header.author()) {
bail!("Author {} is not in the committee for round {round} {gc_log}", batch_header.author())
}

Expand All @@ -362,16 +362,16 @@ impl<N: Network> Storage<N> {
let previous_round = round.saturating_sub(1);
// Check if the previous round is within range of the GC round.
if previous_round > gc_round {
// Retrieve the committee for the previous round.
let Ok(previous_committee) = self.ledger.get_previous_committee_for_round(previous_round) else {
// Retrieve the committee lookback for the previous round.
let Ok(previous_committee_lookback) = self.ledger.get_committee_lookback_for_round(previous_round) else {
bail!("Missing committee for the previous round {previous_round} in storage {gc_log}")
};
// Ensure the previous round certificates exists in storage.
if !self.contains_certificates_for_round(previous_round) {
bail!("Missing certificates for the previous round {previous_round} in storage {gc_log}")
}
// Ensure the number of previous certificate IDs is at or below the number of committee members.
if batch_header.previous_certificate_ids().len() > previous_committee.num_members() {
if batch_header.previous_certificate_ids().len() > previous_committee_lookback.num_members() {
bail!("Too many previous certificates for round {round} {gc_log}")
}
// Initialize a set of the previous authors.
Expand All @@ -397,7 +397,7 @@ impl<N: Network> Storage<N> {
previous_authors.insert(previous_certificate.author());
}
// Ensure the previous certificates have reached the quorum threshold.
if !previous_committee.is_quorum_threshold_reached(&previous_authors) {
if !previous_committee_lookback.is_quorum_threshold_reached(&previous_authors) {
bail!("Previous certificates for a batch in round {round} did not reach quorum threshold {gc_log}")
}
}
Expand Down Expand Up @@ -447,8 +447,8 @@ impl<N: Network> Storage<N> {
// Check the timestamp for liveness.
check_timestamp_for_liveness(certificate.timestamp())?;

// Retrieve the previous committee for the batch round.
let Ok(previous_committee) = self.ledger.get_previous_committee_for_round(round) else {
// Retrieve the committee lookback for the batch round.
let Ok(committee_lookback) = self.ledger.get_committee_lookback_for_round(round) else {
bail!("Storage failed to retrieve the committee for round {round} {gc_log}")
};

Expand All @@ -462,15 +462,15 @@ impl<N: Network> Storage<N> {
// Retrieve the signer.
let signer = signature.to_address();
// Ensure the signer is in the committee.
if !previous_committee.is_committee_member(signer) {
if !committee_lookback.is_committee_member(signer) {
bail!("Signer {signer} is not in the committee for round {round} {gc_log}")
}
// Append the signer.
signers.insert(signer);
}

// Ensure the signatures have reached the quorum threshold.
if !previous_committee.is_quorum_threshold_reached(&signers) {
if !committee_lookback.is_quorum_threshold_reached(&signers) {
bail!("Signatures for a batch in round {round} did not reach quorum threshold {gc_log}")
}
Ok(missing_transmissions)
Expand Down
2 changes: 0 additions & 2 deletions node/bft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ pub const MEMORY_POOL_PORT: u16 = 5000; // port

/// The maximum number of milliseconds to wait before proposing a batch.
pub const MAX_BATCH_DELAY_IN_MS: u64 = 2500; // ms
/// The maximum number of rounds to store before garbage collecting.
pub const MAX_GC_ROUNDS: u64 = 50; // rounds
/// The maximum number of seconds allowed for the leader to send their certificate.
pub const MAX_LEADER_CERTIFICATE_DELAY_IN_SECS: i64 = 2 * MAX_BATCH_DELAY_IN_MS as i64 / 1000; // seconds
/// The maximum number of seconds before the timestamp is considered expired.
Expand Down
Loading

0 comments on commit b0a7d18

Please sign in to comment.