|
|
@@ -48,6 +48,8 @@ pub struct Consensus {
|
|
|
pub forks: RwLock<Vec<Fork>>,
|
|
|
/// Canonical blockchain PoW module state
|
|
|
pub module: RwLock<PoWModule>,
|
|
|
+ /// Lock to restrict when proposals appends can happen
|
|
|
+ pub append_lock: RwLock<()>,
|
|
|
}
|
|
|
|
|
|
impl Consensus {
|
|
|
@@ -58,66 +60,18 @@ impl Consensus {
|
|
|
pow_target: usize,
|
|
|
pow_fixed_difficulty: Option<BigUint>,
|
|
|
) -> Result<Self> {
|
|
|
+ let forks = RwLock::new(vec![]);
|
|
|
let module =
|
|
|
RwLock::new(PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty)?);
|
|
|
- Ok(Self { blockchain, finalization_threshold, forks: RwLock::new(vec![]), module })
|
|
|
- }
|
|
|
-
|
|
|
- /// Generate an unsigned block for provided fork, containing all
|
|
|
- /// pending transactions.
|
|
|
- pub async fn generate_unsigned_block(
|
|
|
- &self,
|
|
|
- fork: &Fork,
|
|
|
- producer_tx: Transaction,
|
|
|
- ) -> Result<BlockInfo> {
|
|
|
- // Grab forks' next block height
|
|
|
- let next_block_height = fork.get_next_block_height()?;
|
|
|
-
|
|
|
- // Grab forks' unproposed transactions
|
|
|
- let mut unproposed_txs = fork.unproposed_txs(&self.blockchain, next_block_height).await?;
|
|
|
- unproposed_txs.push(producer_tx);
|
|
|
-
|
|
|
- // Grab forks' last block proposal(previous)
|
|
|
- let previous = fork.last_proposal()?;
|
|
|
-
|
|
|
- // Generate the new header
|
|
|
- let header =
|
|
|
- Header::new(previous.block.hash()?, next_block_height, Timestamp::current_time(), 0);
|
|
|
-
|
|
|
- // Generate the block
|
|
|
- let mut block = BlockInfo::new_empty(header);
|
|
|
-
|
|
|
- // Add transactions to the block
|
|
|
- block.append_txs(unproposed_txs)?;
|
|
|
-
|
|
|
- Ok(block)
|
|
|
- }
|
|
|
-
|
|
|
- /// Generate a block proposal for provided fork, containing all
|
|
|
- /// pending transactions. Proposal is signed using provided secret key,
|
|
|
- /// which must also have signed the provided proposal transaction.
|
|
|
- pub async fn generate_signed_proposal(
|
|
|
- &self,
|
|
|
- fork: &Fork,
|
|
|
- producer_tx: Transaction,
|
|
|
- secret_key: &SecretKey,
|
|
|
- ) -> Result<Proposal> {
|
|
|
- let mut block = self.generate_unsigned_block(fork, producer_tx).await?;
|
|
|
-
|
|
|
- // Sign block
|
|
|
- block.sign(secret_key)?;
|
|
|
-
|
|
|
- // Generate the block proposal from the block
|
|
|
- let proposal = Proposal::new(block)?;
|
|
|
-
|
|
|
- Ok(proposal)
|
|
|
+ let append_lock = RwLock::new(());
|
|
|
+ Ok(Self { blockchain, finalization_threshold, forks, module, append_lock })
|
|
|
}
|
|
|
|
|
|
/// Generate a new empty fork.
|
|
|
pub async fn generate_empty_fork(&self) -> Result<()> {
|
|
|
debug!(target: "validator::consensus::generate_empty_fork", "Generating new empty fork...");
|
|
|
let mut lock = self.forks.write().await;
|
|
|
- let fork = Fork::new(&self.blockchain, self.module.read().await.clone()).await?;
|
|
|
+ let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
|
|
|
lock.push(fork);
|
|
|
drop(lock);
|
|
|
debug!(target: "validator::consensus::generate_empty_fork", "Fork generated!");
|
|
|
@@ -129,6 +83,18 @@ impl Consensus {
|
|
|
pub async fn append_proposal(&self, proposal: &Proposal) -> Result<()> {
|
|
|
debug!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
|
|
|
|
|
|
+ // Check if proposal already exists
|
|
|
+ let lock = self.forks.read().await;
|
|
|
+ for fork in lock.iter() {
|
|
|
+ for p in fork.proposals.iter().rev() {
|
|
|
+ if p == &proposal.hash {
|
|
|
+ drop(lock);
|
|
|
+ return Err(Error::ProposalAlreadyExists)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ drop(lock);
|
|
|
+
|
|
|
// Verify proposal and grab corresponding fork
|
|
|
let (mut fork, index) = verify_proposal(self, proposal).await?;
|
|
|
|
|
|
@@ -141,13 +107,6 @@ impl Consensus {
|
|
|
// If a fork index was found, replace forks with the mutated one,
|
|
|
// otherwise push the new fork.
|
|
|
let mut lock = self.forks.write().await;
|
|
|
- // Check if fork already exists
|
|
|
- for f in lock.iter() {
|
|
|
- if f.proposals == fork.proposals {
|
|
|
- drop(lock);
|
|
|
- return Err(Error::ProposalAlreadyExists)
|
|
|
- }
|
|
|
- }
|
|
|
match index {
|
|
|
Some(i) => {
|
|
|
if i < lock.len() && lock[i].proposals == fork.proposals[..fork.proposals.len() - 1]
|
|
|
@@ -200,7 +159,7 @@ impl Consensus {
|
|
|
}
|
|
|
|
|
|
// Generate a new fork extending canonical
|
|
|
- let fork = Fork::new(&self.blockchain, self.module.read().await.clone()).await?;
|
|
|
+ let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
|
|
|
return Ok((fork, None))
|
|
|
}
|
|
|
|
|
|
@@ -212,7 +171,7 @@ impl Consensus {
|
|
|
}
|
|
|
|
|
|
// Rebuild fork
|
|
|
- let mut fork = Fork::new(&self.blockchain, self.module.read().await.clone()).await?;
|
|
|
+ let mut fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
|
|
|
fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
|
|
|
|
|
|
// Retrieve proposals blocks from original fork
|
|
|
@@ -382,6 +341,8 @@ impl From<Proposal> for BlockInfo {
|
|
|
/// the proposals hashes sequence, for validations.
|
|
|
#[derive(Clone)]
|
|
|
pub struct Fork {
|
|
|
+ /// Canonical (finalized) blockchain
|
|
|
+ pub blockchain: Blockchain,
|
|
|
/// Overlay cache over canonical Blockchain
|
|
|
pub overlay: BlockchainOverlayPtr,
|
|
|
/// Current PoW module state,
|
|
|
@@ -395,11 +356,62 @@ pub struct Fork {
|
|
|
}
|
|
|
|
|
|
impl Fork {
|
|
|
- pub async fn new(blockchain: &Blockchain, module: PoWModule) -> Result<Self> {
|
|
|
+ pub async fn new(blockchain: Blockchain, module: PoWModule) -> Result<Self> {
|
|
|
let mempool =
|
|
|
blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
|
|
|
- let overlay = BlockchainOverlay::new(blockchain)?;
|
|
|
- Ok(Self { overlay, module, proposals: vec![], mempool, rank: BigUint::from(0u64) })
|
|
|
+ let overlay = BlockchainOverlay::new(&blockchain)?;
|
|
|
+ Ok(Self {
|
|
|
+ blockchain,
|
|
|
+ overlay,
|
|
|
+ module,
|
|
|
+ proposals: vec![],
|
|
|
+ mempool,
|
|
|
+ rank: BigUint::from(0u64),
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Generate an unsigned block containing all pending transactions.
|
|
|
+ pub async fn generate_unsigned_block(&self, producer_tx: Transaction) -> Result<BlockInfo> {
|
|
|
+ // Grab forks' last block proposal(previous)
|
|
|
+ let previous = self.last_proposal()?;
|
|
|
+
|
|
|
+ // Grab forks' next block height
|
|
|
+ let next_block_height = previous.block.header.height + 1;
|
|
|
+
|
|
|
+ // Grab forks' unproposed transactions
|
|
|
+ let mut unproposed_txs = self.unproposed_txs(&self.blockchain, next_block_height).await?;
|
|
|
+ unproposed_txs.push(producer_tx);
|
|
|
+
|
|
|
+ // Generate the new header
|
|
|
+ let header =
|
|
|
+ Header::new(previous.block.hash()?, next_block_height, Timestamp::current_time(), 0);
|
|
|
+
|
|
|
+ // Generate the block
|
|
|
+ let mut block = BlockInfo::new_empty(header);
|
|
|
+
|
|
|
+ // Add transactions to the block
|
|
|
+ block.append_txs(unproposed_txs)?;
|
|
|
+
|
|
|
+ Ok(block)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Generate a block proposal containing all pending transactions.
|
|
|
+ /// Proposal is signed using provided secret key, which must also
|
|
|
+ /// have signed the provided proposal transaction.
|
|
|
+ pub async fn generate_signed_proposal(
|
|
|
+ &self,
|
|
|
+ producer_tx: Transaction,
|
|
|
+ secret_key: &SecretKey,
|
|
|
+ ) -> Result<Proposal> {
|
|
|
+ let mut block = self.generate_unsigned_block(producer_tx).await?;
|
|
|
+
|
|
|
+ // Sign block
|
|
|
+ block.sign(secret_key)?;
|
|
|
+
|
|
|
+ // Generate the block proposal from the block
|
|
|
+ let proposal = Proposal::new(block)?;
|
|
|
+
|
|
|
+ Ok(proposal)
|
|
|
}
|
|
|
|
|
|
/// Auxiliary function to append a proposal and recalculate current fork rank
|
|
|
@@ -532,12 +544,13 @@ impl Fork {
|
|
|
/// Changes to this copy don't affect original fork overlay records, since underlying
|
|
|
/// overlay pointer have been updated to the cloned one.
|
|
|
pub fn full_clone(&self) -> Result<Self> {
|
|
|
+ let blockchain = self.blockchain.clone();
|
|
|
let overlay = self.overlay.lock().unwrap().full_clone()?;
|
|
|
let module = self.module.clone();
|
|
|
let proposals = self.proposals.clone();
|
|
|
let mempool = self.mempool.clone();
|
|
|
let rank = self.rank.clone();
|
|
|
|
|
|
- Ok(Self { overlay, module, proposals, mempool, rank })
|
|
|
+ Ok(Self { blockchain, overlay, module, proposals, mempool, rank })
|
|
|
}
|
|
|
}
|