Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feb 10 Backport] Theoretical Hotfix for testnet3 branch #2408

Merged
merged 9 commits into from Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions ledger/block/src/lib.rs
Expand Up @@ -125,9 +125,6 @@ impl<N: Network> Block<N> {
transactions: Transactions<N>,
aborted_transaction_ids: Vec<N::TransactionID>,
) -> Result<Self> {
// Ensure the block contains transactions.
ensure!(!transactions.is_empty(), "Cannot create a block with zero transactions");

// Ensure the number of transactions is within the allowed range.
if transactions.len() > Transactions::<N>::MAX_TRANSACTIONS {
bail!(
Expand Down
25 changes: 12 additions & 13 deletions ledger/block/src/verify.rs
Expand Up @@ -30,7 +30,7 @@ impl<N: Network> Block<N> {
&self,
previous_block: &Block<N>,
current_state_root: N::StateRoot,
current_committee: &Committee<N>,
current_committee_lookback: &Committee<N>,
current_puzzle: &CoinbasePuzzle<N>,
current_epoch_challenge: &EpochChallenge<N>,
current_timestamp: i64,
Expand All @@ -41,7 +41,7 @@ impl<N: Network> Block<N> {

// Ensure the block authority is correct.
let (expected_round, expected_height, expected_timestamp, expected_existing_transaction_ids) =
self.verify_authority(previous_block.round(), previous_block.height(), current_committee)?;
self.verify_authority(previous_block.round(), previous_block.height(), current_committee_lookback)?;

// Ensure the block solutions are correct.
let (
Expand Down Expand Up @@ -138,8 +138,9 @@ impl<N: Network> Block<N> {
&self,
previous_round: u64,
previous_height: u32,
current_committee: &Committee<N>,
current_committee_lookback: &Committee<N>,
) -> Result<(u64, u32, i64, Vec<N::TransactionID>)> {
// Note: Do not remove this. This ensures that all blocks after genesis are quorum blocks.
#[cfg(not(any(test, feature = "test")))]
ensure!(self.authority.is_quorum(), "The next block must be a quorum block");

Expand All @@ -164,12 +165,13 @@ impl<N: Network> Block<N> {
subdag.anchor_round()
}
};
// Ensure the block round is at least the starting round of the committee.
// Ensure the block round minus the committee lookback range is at least the starting round of the committee lookback.
ensure!(
expected_round >= current_committee.starting_round(),
"Block {} has an invalid round (found '{expected_round}', expected at least '{}')",
expected_height,
current_committee.starting_round()
expected_round.saturating_sub(Committee::<N>::COMMITTEE_LOOKBACK_RANGE)
>= current_committee_lookback.starting_round(),
"Block {expected_height} has an invalid round (found '{}', expected at least '{}')",
expected_round.saturating_sub(Committee::<N>::COMMITTEE_LOOKBACK_RANGE),
current_committee_lookback.starting_round()
);

// Ensure the block authority is correct.
Expand All @@ -180,7 +182,7 @@ impl<N: Network> Block<N> {
let signer = signature.to_address();
// Ensure the block is signed by a committee member.
ensure!(
current_committee.members().contains_key(&signer),
current_committee_lookback.members().contains_key(&signer),
"Beacon block {expected_height} has a signer not in the committee (found '{signer}')",
);
// Ensure the signature is valid.
Expand All @@ -193,7 +195,7 @@ impl<N: Network> Block<N> {
}
Authority::Quorum(subdag) => {
// Compute the expected leader.
let expected_leader = current_committee.get_leader(expected_round)?;
let expected_leader = current_committee_lookback.get_leader(expected_round)?;
// Ensure the block is authored by the expected leader.
ensure!(
subdag.leader_address() == expected_leader,
Expand Down Expand Up @@ -387,9 +389,6 @@ impl<N: Network> Block<N> {
fn verify_transactions(&self) -> Result<()> {
let height = self.height();

// Ensure there are transactions.
ensure!(!self.transactions.is_empty(), "Block {height} must contain at least 1 transaction");

// Ensure the number of transactions is within the allowed range.
if self.transactions.len() > Transactions::<N>::MAX_TRANSACTIONS {
bail!(
Expand Down
2 changes: 2 additions & 0 deletions ledger/committee/src/lib.rs
Expand Up @@ -47,6 +47,8 @@ pub struct Committee<N: Network> {
}

impl<N: Network> Committee<N> {
/// The committee lookback range.
pub const COMMITTEE_LOOKBACK_RANGE: u64 = 50;
/// The maximum number of members that may be in a committee.
pub const MAX_COMMITTEE_SIZE: u16 = 200;

Expand Down
15 changes: 14 additions & 1 deletion ledger/src/check_next_block.rs
Expand Up @@ -84,11 +84,24 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
let ratified_finalize_operations =
self.vm.check_speculate(state, block.ratifications(), block.solutions(), block.transactions())?;

// 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 block.round() % 2 == 0 {
true => block.round().saturating_sub(1),
false => block.round().saturating_sub(2),
};
// Get the committee lookback round.
let committee_lookback_round = previous_round.saturating_sub(Committee::<N>::COMMITTEE_LOOKBACK_RANGE);
// Retrieve the committee lookback.
let committee_lookback = self
.get_committee_for_round(committee_lookback_round)?
.ok_or(anyhow!("Failed to fetch committee for round {committee_lookback_round}"))?;

// Ensure the block is correct.
let expected_existing_transaction_ids = block.verify(
&self.latest_block(),
self.latest_state_root(),
&self.latest_committee()?,
&committee_lookback,
self.coinbase_puzzle(),
&self.latest_epoch_challenge()?,
OffsetDateTime::now_utc().unix_timestamp(),
Expand Down