diff --git a/src/error.rs b/src/error.rs index 1c2e0dc37..bcb576587 100644 --- a/src/error.rs +++ b/src/error.rs @@ -336,6 +336,9 @@ pub enum Error { #[error("Proposal task stopped")] ProposalTaskStopped, + #[error("Proposal already exists")] + ProposalAlreadyExists, + #[error("Miner task stopped")] MinerTaskStopped, diff --git a/src/validator/consensus.rs b/src/validator/consensus.rs index 8007785b6..e7934cb2d 100644 --- a/src/validator/consensus.rs +++ b/src/validator/consensus.rs @@ -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 || diff --git a/src/validator/utils.rs b/src/validator/utils.rs index 996aa5160..abbd21e28 100644 --- a/src/validator/utils.rs +++ b/src/validator/utils.rs @@ -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) }