validator: check if proposal already exists when looking ofr its fork index

This also solves the bug where when appending a dublicated proposal, a new duplicate fork being created, resulting in never finalizing, as forks of same height existed
This commit is contained in:
aggstam
2024-01-09 18:36:09 +02:00
parent 4cc5cf6217
commit e5660ce75d
3 changed files with 34 additions and 3 deletions

View File

@@ -336,6 +336,9 @@ pub enum Error {
#[error("Proposal task stopped")]
ProposalTaskStopped,
#[error("Proposal already exists")]
ProposalAlreadyExists,
#[error("Miner task stopped")]
MinerTaskStopped,

View File

@@ -208,7 +208,7 @@ impl Consensus {
/// Given a proposal, the node verifys it and finds which fork it extends.
/// If the proposal extends the canonical blockchain, a new fork chain is created.
pub async fn append_proposal(&self, proposal: &Proposal) -> Result<()> {
info!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
debug!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
// Verify proposal and grab corresponding fork
let (mut fork, index) = verify_proposal(self, proposal).await?;
@@ -244,6 +244,8 @@ impl Consensus {
}
drop(lock);
info!(target: "validator::consensus::append_proposal", "Appended proposal {}", proposal.hash);
Ok(())
}
@@ -259,6 +261,10 @@ impl Consensus {
// Check if proposal extends any fork
let found = find_extended_fork_index(&forks, proposal);
if found.is_err() {
if let Err(Error::ProposalAlreadyExists) = found {
return Err(Error::ProposalAlreadyExists)
}
// Check if proposal extends canonical
let (last_slot, last_block) = self.blockchain.last()?;
if proposal.block.header.previous != last_block ||

View File

@@ -254,17 +254,39 @@ pub fn previous_slot_info(
}
/// Given a proposal, find the index of the fork chain it extends, along with the specific
/// extended proposal index.
/// extended proposal index. Additionally, check that proposal doesn't already exists in any
/// fork chain.
pub fn find_extended_fork_index(forks: &[Fork], proposal: &Proposal) -> Result<(usize, usize)> {
// Grab provided proposal hash
let proposal_hash = proposal.block.hash()?;
// Keep track of fork and proposal indexes
let (mut fork_index, mut proposal_index) = (None, None);
// Loop through all the forks
for (f_index, fork) in forks.iter().enumerate() {
// Traverse fork proposals sequence in reverse
for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
// Check we haven't already seen that proposal
if &proposal_hash == p_hash {
return Err(Error::ProposalAlreadyExists)
}
// Check if proposal extends this fork
if &proposal.block.header.previous == p_hash {
return Ok((f_index, p_index))
// Proposal must only extend a single fork
if fork_index.is_some() {
return Err(Error::ProposalAlreadyExists)
}
(fork_index, proposal_index) = (Some(f_index), Some(p_index));
}
}
}
if let (Some(f_index), Some(p_index)) = (fork_index, proposal_index) {
return Ok((f_index, p_index))
}
Err(Error::ExtendedChainIndexNotFound)
}