Ver Fonte

validator: removed RwLock over whole struct ptr and moved to inner stuff

aggstam há 2 anos atrás
pai
commit
bb99e816f2
31 ficheiros alterados com 251 adições e 215 exclusões
  1. 2 2
      bin/darkfid2/src/main.rs
  2. 3 3
      bin/darkfid2/src/proto/protocol_block.rs
  3. 3 3
      bin/darkfid2/src/proto/protocol_proposal.rs
  4. 2 2
      bin/darkfid2/src/proto/protocol_sync.rs
  5. 2 2
      bin/darkfid2/src/proto/protocol_tx.rs
  6. 4 4
      bin/darkfid2/src/rpc_blockchain.rs
  7. 12 14
      bin/darkfid2/src/rpc_tx.rs
  8. 17 12
      bin/darkfid2/src/task/miner.rs
  9. 4 4
      bin/darkfid2/src/task/sync.rs
  10. 6 6
      bin/darkfid2/src/tests/harness.rs
  11. 3 3
      bin/darkfid2/src/tests/mod.rs
  12. 1 1
      src/contract/test-harness/src/consensus_genesis_stake.rs
  13. 2 8
      src/contract/test-harness/src/consensus_proposal.rs
  14. 2 2
      src/contract/test-harness/src/consensus_stake.rs
  15. 1 1
      src/contract/test-harness/src/consensus_unstake.rs
  16. 2 2
      src/contract/test-harness/src/consensus_unstake_request.rs
  17. 1 1
      src/contract/test-harness/src/dao_exec.rs
  18. 1 1
      src/contract/test-harness/src/dao_mint.rs
  19. 1 1
      src/contract/test-harness/src/dao_propose.rs
  20. 1 1
      src/contract/test-harness/src/dao_vote.rs
  21. 2 5
      src/contract/test-harness/src/lib.rs
  22. 1 1
      src/contract/test-harness/src/money_airdrop.rs
  23. 1 1
      src/contract/test-harness/src/money_genesis_mint.rs
  24. 1 1
      src/contract/test-harness/src/money_otc_swap.rs
  25. 1 8
      src/contract/test-harness/src/money_pow_reward.rs
  26. 2 2
      src/contract/test-harness/src/money_token.rs
  27. 3 3
      src/contract/test-harness/src/money_transfer.rs
  28. 64 99
      src/validator/consensus.rs
  29. 35 21
      src/validator/mod.rs
  30. 70 0
      src/validator/utils.rs
  31. 1 1
      src/validator/verification.rs

+ 2 - 2
bin/darkfid2/src/main.rs

@@ -331,11 +331,11 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     if !blockchain_config.skip_sync {
         sync_task(&darkfid).await?;
     } else {
-        darkfid.validator.write().await.synced = true;
+        *darkfid.validator.synced.write().await = true;
     }
 
     // Clean node pending transactions
-    darkfid.validator.write().await.purge_pending_txs().await?;
+    darkfid.validator.purge_pending_txs().await?;
 
     // Consensus protocol
     let (consensus_task, consensus_sender) = if blockchain_config.consensus {

+ 3 - 3
bin/darkfid2/src/proto/protocol_block.rs

@@ -102,7 +102,7 @@ impl ProtocolBlock {
             };
 
             // Check if node has finished syncing its blockchain
-            if !self.validator.read().await.synced {
+            if !*self.validator.synced.read().await {
                 debug!(
                     target: "validator::protocol_block::handle_receive_block",
                     "Node still syncing blockchain, skipping..."
@@ -114,7 +114,7 @@ impl ProtocolBlock {
             // Consensus-mode enabled nodes have already performed these steps,
             // during proposal finalization. They still listen to this sub,
             // in case they go out of sync and become a none-consensus node.
-            if self.validator.read().await.consensus.participating {
+            if self.validator.consensus.participating {
                 debug!(
                     target: "validator::protocol_block::handle_receive_block",
                     "Node is participating in consensus, skipping..."
@@ -124,7 +124,7 @@ impl ProtocolBlock {
 
             let block_copy = (*block).clone();
 
-            match self.validator.write().await.append_block(&block_copy.0).await {
+            match self.validator.append_block(&block_copy.0).await {
                 Ok(()) => {
                     self.p2p.broadcast_with_exclude(&block_copy, &exclude_list).await;
                     let encoded_block = JsonValue::String(base64::encode(&serialize(&block_copy)));

+ 3 - 3
bin/darkfid2/src/proto/protocol_proposal.rs

@@ -95,7 +95,7 @@ impl ProtocolProposal {
             };
 
             // Check if node has finished syncing its blockchain
-            if !self.validator.read().await.synced {
+            if !*self.validator.synced.read().await {
                 debug!(
                     target: "validator::protocol_proposal::handle_receive_proposal",
                     "Node still syncing blockchain, skipping..."
@@ -104,7 +104,7 @@ impl ProtocolProposal {
             }
 
             // Check if node started participating in consensus.
-            if !self.validator.read().await.consensus.participating {
+            if !self.validator.consensus.participating {
                 debug!(
                     target: "validator::protocol_proposal::handle_receive_proposal",
                     "Node is not participating in consensus, skipping..."
@@ -114,7 +114,7 @@ impl ProtocolProposal {
 
             let proposal_copy = (*proposal).clone();
 
-            match self.validator.write().await.consensus.append_proposal(&proposal_copy.0).await {
+            match self.validator.consensus.append_proposal(&proposal_copy.0).await {
                 Ok(()) => {
                     self.p2p.broadcast_with_exclude(&proposal_copy, &exclude_list).await;
                     let enc_prop = JsonValue::String(base64::encode(&serialize(&proposal_copy)));

+ 2 - 2
bin/darkfid2/src/proto/protocol_sync.rs

@@ -99,7 +99,7 @@ impl ProtocolSync {
             };
 
             // Check if node has finished syncing its blockchain
-            if !self.validator.read().await.synced {
+            if !*self.validator.synced.read().await {
                 debug!(
                     target: "validator::protocol_sync::handle_receive_request",
                     "Node still syncing blockchain, skipping..."
@@ -108,7 +108,7 @@ impl ProtocolSync {
             }
 
             let key = request.slot;
-            let blocks = match self.validator.read().await.blockchain.get_blocks_after(key, BATCH) {
+            let blocks = match self.validator.blockchain.get_blocks_after(key, BATCH) {
                 Ok(v) => v,
                 Err(e) => {
                     error!(

+ 2 - 2
bin/darkfid2/src/proto/protocol_tx.rs

@@ -92,7 +92,7 @@ impl ProtocolTx {
             };
 
             // Check if node has finished syncing its blockchain
-            if !self.validator.read().await.synced {
+            if !*self.validator.synced.read().await {
                 debug!(
                     target: "validator::protocol_tx::handle_receive_tx",
                     "Node still syncing blockchain, skipping..."
@@ -103,7 +103,7 @@ impl ProtocolTx {
             let tx_copy = (*tx).clone();
 
             // Nodes use unconfirmed_txs vector as seen_txs pool.
-            match self.validator.write().await.append_tx(&tx_copy).await {
+            match self.validator.append_tx(&tx_copy).await {
                 Ok(()) => {
                     self.p2p.broadcast_with_exclude(&tx_copy, &exclude_list).await;
                     let encoded_tx = JsonValue::String(base64::encode(&serialize(&tx_copy)));

+ 4 - 4
bin/darkfid2/src/rpc_blockchain.rs

@@ -59,7 +59,7 @@ impl Darkfid {
             Err(_) => return JsonError::new(ParseError, None, id).into(),
         };
 
-        let blocks = match self.validator.read().await.blockchain.get_blocks_by_slot(&[slot]) {
+        let blocks = match self.validator.blockchain.get_blocks_by_slot(&[slot]) {
             Ok(v) => v,
             Err(e) => {
                 error!(target: "darkfid::rpc::blockchain_get_slot", "Failed fetching block by slot: {}", e);
@@ -100,7 +100,7 @@ impl Darkfid {
             Err(_) => return JsonError::new(ParseError, None, id).into(),
         };
 
-        let txs = match self.validator.read().await.blockchain.transactions.get(&[tx_hash], true) {
+        let txs = match self.validator.blockchain.transactions.get(&[tx_hash], true) {
             Ok(txs) => txs,
             Err(e) => {
                 error!(target: "darkfid::rpc::blockchain_get_tx", "Failed fetching tx by hash: {}", e);
@@ -133,7 +133,7 @@ impl Darkfid {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        let blockchain = { self.validator.read().await.blockchain.clone() };
+        let blockchain = self.validator.blockchain.clone();
         let Ok(last_slot) = blockchain.last() else {
             return JsonError::new(InternalError, None, id).into()
         };
@@ -226,7 +226,7 @@ impl Darkfid {
             }
         };
 
-        let blockchain = { self.validator.read().await.blockchain.clone() };
+        let blockchain = self.validator.blockchain.clone();
 
         let Ok(zkas_db) = blockchain.contracts.lookup(
             &blockchain.sled_db,

+ 12 - 14
bin/darkfid2/src/rpc_tx.rs

@@ -46,7 +46,7 @@ impl Darkfid {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        if !self.validator.read().await.synced {
+        if !*self.validator.synced.read().await {
             error!(target: "darkfid::rpc::tx_simulate", "Blockchain is not synced");
             return server_error(RpcError::NotSynced, id, None)
         }
@@ -70,9 +70,8 @@ impl Darkfid {
         };
 
         // Simulate state transition
-        let lock = self.validator.read().await;
-        let current_slot = lock.consensus.time_keeper.current_slot();
-        let result = lock.add_transactions(&[tx], current_slot, false).await;
+        let current_slot = self.validator.consensus.time_keeper.current_slot();
+        let result = self.validator.add_transactions(&[tx], current_slot, false).await;
         if result.is_err() {
             error!(
                 target: "darkfid::rpc::tx_simulate", "Failed to validate state transition: {}",
@@ -98,7 +97,7 @@ impl Darkfid {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        if !self.validator.read().await.synced {
+        if !*self.validator.synced.read().await {
             error!(target: "darkfid::rpc::tx_broadcast", "Blockchain is not synced");
             return server_error(RpcError::NotSynced, id, None)
         }
@@ -125,15 +124,14 @@ impl Darkfid {
             // Consensus participants can directly perform
             // the state transition check and append to their
             // pending transactions store.
-            if self.validator.write().await.append_tx(&tx).await.is_err() {
+            if self.validator.append_tx(&tx).await.is_err() {
                 error!(target: "darkfid::rpc::tx_broadcast", "Failed to append transaction to mempool");
                 return server_error(RpcError::TxSimulationFail, id, None)
             }
         } else {
             // We'll perform the state transition check here.
-            let lock = self.validator.read().await;
-            let current_slot = lock.consensus.time_keeper.current_slot();
-            let result = lock.add_transactions(&[tx.clone()], current_slot, false).await;
+            let current_slot = self.validator.consensus.time_keeper.current_slot();
+            let result = self.validator.add_transactions(&[tx.clone()], current_slot, false).await;
             if result.is_err() {
                 error!(
                     target: "darkfid::rpc::tx_broadcast", "Failed to validate state transition: {}",
@@ -165,12 +163,12 @@ impl Darkfid {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        if !self.validator.read().await.synced {
+        if !*self.validator.synced.read().await {
             error!(target: "darkfid::rpc::tx_pending", "Blockchain is not synced");
             return server_error(RpcError::NotSynced, id, None)
         }
 
-        let pending_txs = match self.validator.read().await.blockchain.get_pending_txs() {
+        let pending_txs = match self.validator.blockchain.get_pending_txs() {
             Ok(v) => v,
             Err(e) => {
                 error!(target: "darkfid::rpc::tx_pending", "Failed fetching pending txs: {}", e);
@@ -196,12 +194,12 @@ impl Darkfid {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        if !self.validator.read().await.synced {
+        if !*self.validator.synced.read().await {
             error!(target: "darkfid::rpc::tx_clean_pending", "Blockchain is not synced");
             return server_error(RpcError::NotSynced, id, None)
         }
 
-        let pending_txs = match self.validator.read().await.blockchain.get_pending_txs() {
+        let pending_txs = match self.validator.blockchain.get_pending_txs() {
             Ok(v) => v,
             Err(e) => {
                 error!(target: "darkfid::rpc::tx_clean_pending", "Failed fetching pending txs: {}", e);
@@ -209,7 +207,7 @@ impl Darkfid {
             }
         };
 
-        if let Err(e) = self.validator.read().await.blockchain.remove_pending_txs(&pending_txs) {
+        if let Err(e) = self.validator.blockchain.remove_pending_txs(&pending_txs) {
             error!(target: "darkfid::rpc::tx_clean_pending", "Failed fetching pending txs: {}", e);
             return JsonError::new(InternalError, None, id).into()
         };

+ 17 - 12
bin/darkfid2/src/task/miner.rs

@@ -22,6 +22,7 @@ use darkfi::{
     validator::{
         consensus::{Fork, Proposal},
         pow::PoWModule,
+        utils::best_forks_indexes,
     },
     zk::{empty_witnesses, ProvingKey, ZkCircuit},
     zkas::ZkBinary,
@@ -84,7 +85,7 @@ async fn miner_loop(
 ) -> Result<()> {
     // Grab zkas proving keys and bin for PoWReward transaction
     info!(target: "darkfid::task::miner_task", "Generating zkas bin and proving keys...");
-    let blockchain = node.validator.read().await.blockchain.clone();
+    let blockchain = node.validator.blockchain.clone();
     let (zkbin, _) = blockchain.contracts.get_zkas(
         &blockchain.sled_db,
         &MONEY_CONTRACT_ID,
@@ -102,7 +103,7 @@ async fn miner_loop(
 
     // Generate a new fork to be able to extend
     info!(target: "darkfid::task::miner_task", "Generating new empty fork...");
-    node.validator.write().await.consensus.generate_pow_slot()?;
+    node.validator.consensus.generate_pow_slot().await?;
 
     info!(target: "darkfid::task::miner_task", "Miner loop starts!");
     // Miner loop
@@ -116,15 +117,14 @@ async fn miner_loop(
         next_block.sign(&secret)?;
 
         // Verify it
-        node.validator.read().await.consensus.module.verify_current_block(&next_block)?;
+        node.validator.consensus.module.read().await.verify_current_block(&next_block)?;
 
         // Append the mined block as a proposal
         let proposal = Proposal::new(next_block)?;
-        let mut lock = node.validator.write().await;
-        lock.consensus.append_proposal(&proposal).await?;
+        node.validator.consensus.append_proposal(&proposal).await?;
 
         // Check if we can finalize anything and broadcast them
-        let finalized = lock.finalization().await?;
+        let finalized = node.validator.finalization().await?;
         if !finalized.is_empty() {
             for block in finalized {
                 let message = BlockInfoMessage::from(&block);
@@ -142,11 +142,12 @@ async fn generate_next_block(
     zkbin: &ZkBinary,
     pk: &ProvingKey,
 ) -> Result<(BlockInfo, PoWModule)> {
-    let lock = node.validator.read().await;
+    // Grab a lock over nodes' current forks
+    let forks = node.validator.consensus.forks.read().await;
 
     // Grab best current fork
-    let fork_index = lock.consensus.best_forks_indexes()?[0];
-    let fork = &lock.consensus.forks[fork_index];
+    let fork_index = best_forks_indexes(&forks)?[0];
+    let fork = &forks[fork_index];
 
     // Generate new signing key for next block
     let height = fork.slots.last().unwrap().id;
@@ -159,9 +160,13 @@ async fn generate_next_block(
     // Generate reward transaction
     let tx = generate_pow_transaction(fork, secret, recipient, zkbin, pk)?;
 
-    // Mine next block proposal
-    let next_block = lock.consensus.generate_unsigned_block(fork, tx).await?;
-    let module = lock.consensus.forks[fork_index].module.clone();
+    // Generate next block proposal
+    let next_block = node.validator.consensus.generate_unsigned_block(fork, tx).await?;
+    let module = fork.module.clone();
+
+    // Drop forks lock
+    drop(forks);
+
     Ok((next_block, module))
 }
 

+ 4 - 4
bin/darkfid2/src/task/sync.rs

@@ -55,7 +55,7 @@ pub async fn sync_task(node: &Darkfid) -> Result<()> {
     // Node sends the last known block hash of the canonical blockchain
     // and loops until the response is the same block (used to utilize
     // batch requests).
-    let mut last = node.validator.read().await.blockchain.last()?;
+    let mut last = node.validator.blockchain.last()?;
     info!(target: "darkfid::task::sync_task", "Last known block: {:?} - {:?}", last.0, last.1);
     loop {
         // Node creates a `SyncRequest` and sends it
@@ -68,7 +68,7 @@ pub async fn sync_task(node: &Darkfid) -> Result<()> {
 
         // Verify and store retrieved blocks
         debug!(target: "darkfid::task::sync_task", "Processing received blocks");
-        node.validator.write().await.add_blocks(&response.blocks).await?;
+        node.validator.add_blocks(&response.blocks).await?;
 
         // Notify subscriber
         for block in &response.blocks {
@@ -76,7 +76,7 @@ pub async fn sync_task(node: &Darkfid) -> Result<()> {
             notif_sub.notify(vec![encoded_block].into()).await;
         }
 
-        let last_received = node.validator.read().await.blockchain.last()?;
+        let last_received = node.validator.blockchain.last()?;
         info!(target: "darkfid::task::sync_task", "Last received block: {:?} - {:?}", last_received.0, last_received.1);
 
         if last == last_received {
@@ -86,7 +86,7 @@ pub async fn sync_task(node: &Darkfid) -> Result<()> {
         last = last_received;
     }
 
-    node.validator.write().await.synced = true;
+    *node.validator.synced.write().await = true;
     info!(target: "darkfid::task::sync_task", "Blockchain synced!");
     Ok(())
 }

+ 6 - 6
bin/darkfid2/src/tests/harness.rs

@@ -139,8 +139,8 @@ impl Harness {
 
     pub async fn validate_chains(&self, total_blocks: usize, total_slots: usize) -> Result<()> {
         let genesis_txs_total = self.config.alice_initial + self.config.bob_initial;
-        let alice = &self.alice.validator.read().await;
-        let bob = &self.bob.validator.read().await;
+        let alice = &self.alice.validator;
+        let bob = &self.bob.validator;
 
         alice
             .validate_blockchain(
@@ -178,7 +178,7 @@ impl Harness {
         }
 
         // and then add it to her chain
-        self.alice.validator.write().await.add_blocks(blocks).await?;
+        self.alice.validator.add_blocks(blocks).await?;
 
         Ok(())
     }
@@ -221,7 +221,7 @@ impl Harness {
         let height = slots.last().unwrap().id;
         let header = Header::new(
             previous_hash,
-            self.alice.validator.read().await.consensus.time_keeper.slot_epoch(height),
+            self.alice.validator.consensus.time_keeper.slot_epoch(height),
             height,
             timestamp,
             previous.header.nonce,
@@ -279,10 +279,10 @@ pub async fn generate_node(
     if !skip_sync {
         sync_task(&node).await?;
     } else {
-        node.validator.write().await.synced = true;
+        *node.validator.synced.write().await = true;
     }
 
-    node.validator.write().await.purge_pending_txs().await?;
+    node.validator.purge_pending_txs().await?;
 
     Ok(node)
 }

+ 3 - 3
bin/darkfid2/src/tests/mod.rs

@@ -46,7 +46,7 @@ async fn sync_pos_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     let th = Harness::new(config, &ex).await?;
 
     // Retrieve genesis block
-    let previous = th.alice.validator.read().await.blockchain.last_block()?;
+    let previous = th.alice.validator.blockchain.last_block()?;
 
     // Generate next block
     let block1 = th.generate_next_pos_block(&previous, 1).await?;
@@ -73,8 +73,8 @@ async fn sync_pos_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
         generate_node(&th.vks, &th.validator_config, &sync_settings, None, &ex, false).await?;
     // Verify node synced
     let genesis_txs_total = th.config.alice_initial + th.config.bob_initial;
-    let alice = &th.alice.validator.read().await;
-    let charlie = &charlie.validator.read().await;
+    let alice = &th.alice.validator;
+    let charlie = &charlie.validator;
     charlie
         .validate_blockchain(
             genesis_txs_total,

+ 1 - 1
src/contract/test-harness/src/consensus_genesis_stake.rs

@@ -104,7 +104,7 @@ impl TestHarness {
 
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.consensus_staked_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 2 - 8
src/contract/test-harness/src/consensus_proposal.rs

@@ -111,7 +111,7 @@ impl TestHarness {
 
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_test_producer_transaction(tx, slot, 2, true).await?;
+        wallet.validator.add_test_producer_transaction(tx, slot, 2, true).await?;
         wallet.consensus_staked_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
@@ -170,13 +170,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::ConsensusProposal).unwrap();
         let timer = Instant::now();
 
-        assert!(wallet
-            .validator
-            .read()
-            .await
-            .add_test_producer_transaction(tx, slot, 2, true)
-            .await
-            .is_err());
+        assert!(wallet.validator.add_test_producer_transaction(tx, slot, 2, true).await.is_err());
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
         Ok(())

+ 2 - 2
src/contract/test-harness/src/consensus_stake.rs

@@ -55,7 +55,7 @@ impl TestHarness {
         let tx_action_benchmark =
             self.tx_action_benchmarks.get_mut(&TxAction::ConsensusStake).unwrap();
 
-        let epoch = wallet.validator.read().await.consensus.time_keeper.slot_epoch(slot);
+        let epoch = wallet.validator.consensus.time_keeper.slot_epoch(slot);
         let timer = Instant::now();
 
         // Building Money::Stake params
@@ -138,7 +138,7 @@ impl TestHarness {
 
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.consensus_staked_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 1 - 1
src/contract/test-harness/src/consensus_unstake.rs

@@ -130,7 +130,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::ConsensusUnstake).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 2 - 2
src/contract/test-harness/src/consensus_unstake_request.rs

@@ -53,7 +53,7 @@ impl TestHarness {
 
         let tx_action_benchmark =
             self.tx_action_benchmarks.get_mut(&TxAction::ConsensusUnstakeRequest).unwrap();
-        let epoch = wallet.validator.read().await.consensus.time_keeper.slot_epoch(slot);
+        let epoch = wallet.validator.consensus.time_keeper.slot_epoch(slot);
         let timer = Instant::now();
 
         // Building Consensus::Unstake params
@@ -122,7 +122,7 @@ impl TestHarness {
 
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.consensus_unstaked_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 1 - 1
src/contract/test-harness/src/dao_exec.rs

@@ -194,7 +194,7 @@ impl TestHarness {
         let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::DaoExec).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
 
         for output in &xfer_params.outputs {
             wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));

+ 1 - 1
src/contract/test-harness/src/dao_mint.rs

@@ -77,7 +77,7 @@ impl TestHarness {
         let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::DaoMint).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.dao_merkle_tree.append(MerkleNode::from(params.dao_bulla.inner()));
         let leaf_pos = wallet.dao_merkle_tree.mark().unwrap();
         wallet.dao_leafs.insert(params.dao_bulla, leaf_pos);

+ 1 - 1
src/contract/test-harness/src/dao_propose.rs

@@ -131,7 +131,7 @@ impl TestHarness {
         let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::DaoPropose).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.dao_proposals_tree.append(MerkleNode::from(params.proposal_bulla.inner()));
 
         let prop_leaf_pos = wallet.dao_proposals_tree.mark().unwrap();

+ 1 - 1
src/contract/test-harness/src/dao_vote.rs

@@ -122,7 +122,7 @@ impl TestHarness {
         let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::DaoVote).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
 
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 2 - 5
src/contract/test-harness/src/lib.rs

@@ -290,8 +290,6 @@ impl TestHarness {
 
         let erroneous_txs = wallet
             .validator
-            .read()
-            .await
             .add_transactions(txs, slot, false)
             .await
             .err()
@@ -447,8 +445,7 @@ impl TestHarness {
 
     pub async fn get_slot_by_slot(&self, slot: u64) -> Result<Slot> {
         let faucet = self.holders.get(&Holder::Faucet).unwrap();
-        let slot =
-            faucet.validator.read().await.blockchain.get_slots_by_id(&[slot])?[0].clone().unwrap();
+        let slot = faucet.validator.blockchain.get_slots_by_id(&[slot])?[0].clone().unwrap();
 
         Ok(slot)
     }
@@ -464,7 +461,7 @@ impl TestHarness {
 
         // Store generated slot
         for wallet in self.holders.values() {
-            wallet.validator.write().await.receive_test_slot(&slot).await?;
+            wallet.validator.receive_test_slot(&slot).await?;
         }
 
         Ok(slot)

+ 1 - 1
src/contract/test-harness/src/money_airdrop.rs

@@ -111,7 +111,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyAirdrop).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.money_merkle_tree.append(MerkleNode::from(params.outputs[0].coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 1 - 1
src/contract/test-harness/src/money_genesis_mint.rs

@@ -96,7 +96,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyGenesisMint).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 

+ 1 - 1
src/contract/test-harness/src/money_otc_swap.rs

@@ -171,7 +171,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyOtcSwap).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         if append {
             for output in &params.outputs {
                 wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));

+ 1 - 8
src/contract/test-harness/src/money_pow_reward.rs

@@ -116,12 +116,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyPoWReward).unwrap();
         let timer = Instant::now();
 
-        wallet
-            .validator
-            .read()
-            .await
-            .add_test_producer_transaction(tx, block_height, 1, true)
-            .await?;
+        wallet.validator.add_test_producer_transaction(tx, block_height, 1, true).await?;
         wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
@@ -141,8 +136,6 @@ impl TestHarness {
 
         assert!(wallet
             .validator
-            .read()
-            .await
             .add_test_producer_transaction(tx, block_height, 1, true)
             .await
             .is_err());

+ 2 - 2
src/contract/test-harness/src/money_token.rs

@@ -100,7 +100,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyTokenMint).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
@@ -162,7 +162,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyTokenFreeze).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
         Ok(())

+ 3 - 3
src/contract/test-harness/src/money_transfer.rs

@@ -102,7 +102,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyTransfer).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, true).await?;
         if append {
             for output in &params.outputs {
                 wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
@@ -126,7 +126,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyTransfer).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(txs, slot, true).await?;
+        wallet.validator.add_transactions(txs, slot, true).await?;
         if append {
             for params in txs_params {
                 for output in &params.outputs {
@@ -150,7 +150,7 @@ impl TestHarness {
             self.tx_action_benchmarks.get_mut(&TxAction::MoneyTransfer).unwrap();
         let timer = Instant::now();
 
-        wallet.validator.read().await.add_transactions(&[tx.clone()], slot, false).await?;
+        wallet.validator.add_transactions(&[tx.clone()], slot, false).await?;
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
         Ok(())

+ 64 - 99
src/validator/consensus.rs

@@ -24,14 +24,17 @@ use darkfi_sdk::{
 use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
 use log::{debug, error, info};
 use num_bigint::BigUint;
+use smol::lock::RwLock;
 
 use crate::{
     blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header},
     tx::Transaction,
     util::time::{TimeKeeper, Timestamp},
     validator::{
-        pid::slot_pid_output, pow::PoWModule, utils::block_rank, verify_block, verify_proposal,
-        verify_transactions,
+        pid::slot_pid_output,
+        pow::PoWModule,
+        utils::{best_forks_indexes, block_rank, find_extended_fork_index, previous_slot_info},
+        verify_block, verify_proposal, verify_transactions,
     },
     Error, Result,
 };
@@ -51,11 +54,11 @@ pub struct Consensus {
     /// Node is participating to consensus
     pub participating: bool,
     /// Last slot node check for finalization
-    pub checked_finalization: u64,
+    pub checked_finalization: RwLock<u64>,
     /// Fork chains containing block proposals
-    pub forks: Vec<Fork>,
+    pub forks: RwLock<Vec<Fork>>,
     /// Canonical blockchain PoW module state
-    pub module: PoWModule,
+    pub module: RwLock<PoWModule>,
     /// Flag to enable PoS testing mode
     pub pos_testing_mode: bool,
 }
@@ -71,71 +74,68 @@ impl Consensus {
         pow_fixed_difficulty: Option<BigUint>,
         pos_testing_mode: bool,
     ) -> Result<Self> {
-        let module =
-            PoWModule::new(blockchain.clone(), pow_threads, pow_target, pow_fixed_difficulty)?;
+        let module = RwLock::new(PoWModule::new(
+            blockchain.clone(),
+            pow_threads,
+            pow_target,
+            pow_fixed_difficulty,
+        )?);
         Ok(Self {
             blockchain,
             time_keeper,
             finalization_threshold,
             participating: false,
-            checked_finalization: 0,
-            forks: vec![],
+            checked_finalization: RwLock::new(0),
+            forks: RwLock::new(vec![]),
             module,
             pos_testing_mode,
         })
     }
 
     /// Generate next hot/live PoW slot for all current forks.
-    pub fn generate_pow_slot(&mut self) -> Result<()> {
+    pub async fn generate_pow_slot(&self) -> Result<()> {
+        // Grab a lock over current forks
+        let mut forks = self.forks.write().await;
+
         // If no forks exist, create a new one as a basis to extend
-        if self.forks.is_empty() {
-            self.forks.push(Fork::new(&self.blockchain, self.module.clone())?);
+        if forks.is_empty() {
+            forks.push(Fork::new(&self.blockchain, self.module.read().await.clone())?);
         }
 
-        for fork in self.forks.iter_mut() {
+        for fork in forks.iter_mut() {
             fork.generate_pow_slot()?;
         }
 
+        // Drop forks lock
+        drop(forks);
+
         Ok(())
     }
 
     /// Generate current hot/live PoS slot for all current forks.
-    pub fn generate_pos_slot(&mut self) -> Result<()> {
+    pub async fn generate_pos_slot(&self) -> Result<()> {
+        // Grab a lock over current forks
+        let mut forks = self.forks.write().await;
+
         // Grab current slot id
         let id = self.time_keeper.current_slot();
 
         // If no forks exist, create a new one as a basis to extend
-        if self.forks.is_empty() {
-            self.forks.push(Fork::new(&self.blockchain, self.module.clone())?);
+        if forks.is_empty() {
+            forks.push(Fork::new(&self.blockchain, self.module.read().await.clone())?);
         }
 
         // Grab previous slot information
-        let (producers, last_hashes, second_to_last_hashes) = self.previous_slot_info(id - 1)?;
+        let (producers, last_hashes, second_to_last_hashes) = previous_slot_info(&forks, id - 1)?;
 
-        for fork in self.forks.iter_mut() {
+        for fork in forks.iter_mut() {
             fork.generate_pos_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
         }
 
-        Ok(())
-    }
-
-    /// Retrieve previous slot producers, last proposal hashes,
-    /// and their second to last hashes, from all current forks.
-    fn previous_slot_info(&self, slot: u64) -> Result<(u64, Vec<blake3::Hash>, Vec<blake3::Hash>)> {
-        let mut producers = 0;
-        let mut last_hashes = vec![];
-        let mut second_to_last_hashes = vec![];
-
-        for fork in &self.forks {
-            let last_proposal = fork.last_proposal()?;
-            if last_proposal.block.header.height == slot {
-                producers += 1;
-            }
-            last_hashes.push(last_proposal.hash);
-            second_to_last_hashes.push(last_proposal.block.header.previous);
-        }
+        // Drop forks lock
+        drop(forks);
 
-        Ok((producers, last_hashes, second_to_last_hashes))
+        Ok(())
     }
 
     /// Generate an unsigned block for provided fork, containing all
@@ -207,7 +207,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(&mut self, proposal: &Proposal) -> Result<()> {
+    pub async fn append_proposal(&self, proposal: &Proposal) -> Result<()> {
         info!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
 
         // Verify proposal and grab corresponding fork
@@ -233,73 +233,31 @@ 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;
         match index {
             Some(i) => {
-                self.forks[i] = fork;
+                lock[i] = fork;
             }
             None => {
-                self.forks.push(fork);
+                lock.push(fork);
             }
         }
+        drop(lock);
 
         Ok(())
     }
 
-    /// Auxiliary function to find current best ranked forks indexes.
-    pub fn best_forks_indexes(&self) -> Result<Vec<usize>> {
-        // Check if node has any forks
-        if self.forks.is_empty() {
-            return Err(Error::ForksNotFound)
-        }
-
-        // Find the best ranked forks
-        let mut best = 0;
-        let mut indexes = vec![];
-        for (f_index, fork) in self.forks.iter().enumerate() {
-            let rank = fork.rank;
-
-            // Fork ranks lower that current best
-            if rank < best {
-                continue
-            }
-
-            // Fork has same rank as current best
-            if rank == best {
-                indexes.push(f_index);
-                continue
-            }
-
-            // Fork ranks higher that current best
-            best = rank;
-            indexes = vec![f_index];
-        }
-
-        Ok(indexes)
-    }
-
-    /// Given a proposal, find the index of the fork chain it extends, along with the specific
-    /// extended proposal index.
-    fn find_extended_fork_index(&self, proposal: &Proposal) -> Result<(usize, usize)> {
-        for (f_index, fork) in self.forks.iter().enumerate() {
-            // Traverse fork proposals sequence in reverse
-            for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
-                if &proposal.block.header.previous == p_hash {
-                    return Ok((f_index, p_index))
-                }
-            }
-        }
-
-        Err(Error::ExtendedChainIndexNotFound)
-    }
-
     /// Given a proposal, find the fork chain it extends, and return its full clone.
     /// If the proposal extends the fork not on its tail, a new fork is created and
     /// we re-apply the proposals up to the extending one. If proposal extends canonical,
     /// a new fork is created. Additionally, we return the fork index if a new fork
     /// was not created, so caller can replace the fork.
     pub async fn find_extended_fork(&self, proposal: &Proposal) -> Result<(Fork, Option<usize>)> {
+        // Grab a lock over current forks
+        let forks = self.forks.read().await;
+
         // Check if proposal extends any fork
-        let found = self.find_extended_fork_index(proposal);
+        let found = find_extended_fork_index(&forks, proposal);
         if found.is_err() {
             // Check if proposal extends canonical
             let (last_slot, last_block) = self.blockchain.last()?;
@@ -310,20 +268,20 @@ impl Consensus {
             }
 
             // Check if we have an empty fork to use
-            for (f_index, fork) in self.forks.iter().enumerate() {
+            for (f_index, fork) in forks.iter().enumerate() {
                 if fork.proposals.is_empty() {
-                    return Ok((self.forks[f_index].full_clone()?, Some(f_index)))
+                    return Ok((forks[f_index].full_clone()?, Some(f_index)))
                 }
             }
 
             // Generate a new fork extending canonical
-            let mut fork = Fork::new(&self.blockchain, self.module.clone())?;
+            let mut fork = Fork::new(&self.blockchain, self.module.read().await.clone())?;
             if proposal.block.header.height < POS_START {
                 fork.generate_pow_slot()?;
             } else {
                 let id = self.time_keeper.current_slot();
                 let (producers, last_hashes, second_to_last_hashes) =
-                    self.previous_slot_info(id - 1)?;
+                    previous_slot_info(&forks, id - 1)?;
                 fork.generate_pos_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
             }
 
@@ -331,14 +289,14 @@ impl Consensus {
         }
 
         let (f_index, p_index) = found.unwrap();
-        let original_fork = &self.forks[f_index];
+        let original_fork = &forks[f_index];
         // Check if proposal extends fork at last proposal
         if p_index == (original_fork.proposals.len() - 1) {
             return Ok((original_fork.full_clone()?, Some(f_index)))
         }
 
         // Rebuild fork
-        let mut fork = Fork::new(&self.blockchain, self.module.clone())?;
+        let mut fork = Fork::new(&self.blockchain, self.module.read().await.clone())?;
         fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
 
         // Retrieve proposals blocks from original fork
@@ -391,10 +349,13 @@ impl Consensus {
         } else {
             let id = time_keeper.verifying_slot;
             let (producers, last_hashes, second_to_last_hashes) =
-                self.previous_slot_info(id - 1)?;
+                previous_slot_info(&forks, id - 1)?;
             fork.generate_pos_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
         }
 
+        // Drop forks lock
+        drop(forks);
+
         Ok((fork, None))
     }
 
@@ -404,14 +365,15 @@ impl Consensus {
     //    can be finalized (append to canonical blockchain).
     /// When best fork can be finalized, blocks(proposals) should be appended to canonical, excluding the
     /// last one, and fork should be rebuilt.
-    pub async fn finalization(&mut self) -> Result<Vec<BlockInfo>> {
+    pub async fn finalization(&self) -> Result<Vec<BlockInfo>> {
         // Set last slot finalization check occured to current slot
         let slot = self.time_keeper.current_slot();
         debug!(target: "validator::consensus::finalization", "Started finalization check for slot: {}", slot);
-        self.checked_finalization = slot;
+        *self.checked_finalization.write().await = slot;
 
         // Grab best forks
-        let forks_indexes = self.best_forks_indexes()?;
+        let forks = self.forks.read().await;
+        let forks_indexes = best_forks_indexes(&forks)?;
         // Check if multiple forks with same rank were found
         if forks_indexes.len() > 1 {
             debug!(target: "validator::consensus::finalization", "Multiple best ranked forks were found");
@@ -419,7 +381,7 @@ impl Consensus {
         }
 
         // Grag the actual best fork
-        let fork = &self.forks[forks_indexes[0]];
+        let fork = &forks[forks_indexes[0]];
 
         // Check its length
         let length = fork.proposals.len();
@@ -431,6 +393,9 @@ impl Consensus {
         // Grab finalized blocks
         let finalized = fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
 
+        // Drop forks lock
+        drop(forks);
+
         Ok(finalized)
     }
 }

+ 35 - 21
src/validator/mod.rs

@@ -117,7 +117,7 @@ impl ValidatorConfig {
 }
 
 /// Atomic pointer to validator.
-pub type ValidatorPtr = Arc<RwLock<Validator>>;
+pub type ValidatorPtr = Arc<Validator>;
 
 /// This struct represents a DarkFi validator node.
 pub struct Validator {
@@ -126,7 +126,7 @@ pub struct Validator {
     /// Hot/Live data used by the consensus algorithm
     pub consensus: Consensus,
     /// Flag signalling node has finished initial sync
-    pub synced: bool,
+    pub synced: RwLock<bool>,
     /// Flag to enable PoS testing mode
     pub pos_testing_mode: bool,
 }
@@ -173,7 +173,7 @@ impl Validator {
 
         // Create the actual state
         let state =
-            Arc::new(RwLock::new(Self { blockchain, consensus, synced: false, pos_testing_mode }));
+            Arc::new(Self { blockchain, consensus, synced: RwLock::new(false), pos_testing_mode });
         info!(target: "validator::new", "Finished initializing validator");
 
         Ok(state)
@@ -181,7 +181,7 @@ impl Validator {
 
     /// The node retrieves a transaction, validates its state transition,
     /// and appends it to the pending txs store.
-    pub async fn append_tx(&mut self, tx: &Transaction) -> Result<()> {
+    pub async fn append_tx(&self, tx: &Transaction) -> Result<()> {
         let tx_hash = blake3::hash(&serialize(tx));
 
         // Check if we have already seen this tx
@@ -198,12 +198,15 @@ impl Validator {
         let tx_vec = [tx.clone()];
         let mut valid = false;
 
+        // Grab a lock over current consensus forks state
+        let mut forks = self.consensus.forks.write().await;
+
         // Generate a time keeper for current slot
         let time_keeper = self.consensus.time_keeper.current();
 
         // If node participates in consensus and holds any forks, iterate over them
         // to verify transaction validity in their overlays
-        for fork in self.consensus.forks.iter_mut() {
+        for fork in forks.iter_mut() {
             // Clone forks' overlay
             let overlay = fork.overlay.lock().unwrap().full_clone()?;
 
@@ -225,6 +228,9 @@ impl Validator {
             valid = true
         }
 
+        // Drop forks lock
+        drop(forks);
+
         // Return error if transaction is not valid for canonical or any fork
         if !valid {
             return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
@@ -238,7 +244,7 @@ impl Validator {
     }
 
     /// The node removes invalid transactions from the pending txs store.
-    pub async fn purge_pending_txs(&mut self) -> Result<()> {
+    pub async fn purge_pending_txs(&self) -> Result<()> {
         info!(target: "validator::purge_pending_txs", "Removing invalid transactions from pending transactions store...");
 
         // Check if any pending transactions exist
@@ -248,6 +254,9 @@ impl Validator {
             return Ok(())
         }
 
+        // Grab a lock over current consensus forks state
+        let mut forks = self.consensus.forks.write().await;
+
         // Generate a time keeper for current slot
         let time_keeper = self.consensus.time_keeper.current();
 
@@ -259,7 +268,7 @@ impl Validator {
 
             // If node participates in consensus and holds any forks, iterate over them
             // to verify transaction validity in their overlays
-            for fork in self.consensus.forks.iter_mut() {
+            for fork in forks.iter_mut() {
                 // Clone forks' overlay
                 let overlay = fork.overlay.lock().unwrap().full_clone()?;
 
@@ -287,6 +296,9 @@ impl Validator {
             }
         }
 
+        // Drop forks lock
+        drop(forks);
+
         if removed_txs.is_empty() {
             info!(target: "validator::purge_pending_txs", "No erroneous transactions found");
             return Ok(())
@@ -299,7 +311,7 @@ impl Validator {
 
     /// The node retrieves a block and tries to add it if it doesn't
     /// already exists.
-    pub async fn append_block(&mut self, block: &BlockInfo) -> Result<()> {
+    pub async fn append_block(&self, block: &BlockInfo) -> Result<()> {
         let block_hash = block.hash()?.to_string();
 
         // Check if block already exists
@@ -316,7 +328,7 @@ impl Validator {
     /// The node checks if proposals can be finalized.
     /// If proposals are found, node appends them to canonical, excluding the
     /// last one, and rebuild the finalized fork to contain the last one.
-    pub async fn finalization(&mut self) -> Result<Vec<BlockInfo>> {
+    pub async fn finalization(&self) -> Result<Vec<BlockInfo>> {
         info!(target: "validator::finalization", "Performing finalization check");
 
         // Grab blocks that can be finalized
@@ -337,8 +349,8 @@ impl Validator {
         self.add_blocks(&finalized).await?;
 
         // Rebuild best fork using last proposal
-        self.consensus.forks = vec![];
-        self.consensus.generate_pow_slot()?;
+        *self.consensus.forks.write().await = vec![];
+        self.consensus.generate_pow_slot().await?;
         self.consensus.append_proposal(&Proposal::new(last)?).await?;
         info!(target: "validator::finalization", "Finalization completed!");
 
@@ -358,7 +370,7 @@ impl Validator {
     // ==========================
 
     /// Validate a set of [`BlockInfo`] in sequence and apply them if all are valid.
-    pub async fn add_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
+    pub async fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
         debug!(target: "validator::add_blocks", "Instantiating BlockchainOverlay");
         let overlay = BlockchainOverlay::new(&self.blockchain)?;
 
@@ -367,7 +379,7 @@ impl Validator {
 
         // Create a time keeper and a PoW module to validate each block
         let mut time_keeper = self.consensus.time_keeper.clone();
-        let mut module = self.consensus.module.clone();
+        let mut module = self.consensus.module.read().await.clone();
 
         // Keep track of all blocks transactions to remove them from pending txs store
         let mut removed_txs = vec![];
@@ -430,7 +442,7 @@ impl Validator {
         self.purge_pending_txs().await?;
 
         // Update PoW module
-        self.consensus.module = module;
+        *self.consensus.module.write().await = module;
 
         Ok(())
     }
@@ -449,10 +461,11 @@ impl Validator {
         let overlay = BlockchainOverlay::new(&self.blockchain)?;
 
         // Generate a time keeper using transaction verifying slot
+        let current_time_keeper = &self.consensus.time_keeper;
         let time_keeper = TimeKeeper::new(
-            self.consensus.time_keeper.genesis_ts,
-            self.consensus.time_keeper.epoch_length,
-            self.consensus.time_keeper.slot_time,
+            current_time_keeper.genesis_ts,
+            current_time_keeper.epoch_length,
+            current_time_keeper.slot_time,
             verifying_slot,
         );
 
@@ -480,7 +493,7 @@ impl Validator {
 
     /// Append to canonical state received slot.
     /// This should be only used for test purposes.
-    pub async fn receive_test_slot(&mut self, slot: &Slot) -> Result<()> {
+    pub async fn receive_test_slot(&self, slot: &Slot) -> Result<()> {
         debug!(target: "validator::receive_test_slot", "Appending slot to ledger");
         self.blockchain.slots.insert(&[slot.clone()])?;
 
@@ -503,10 +516,11 @@ impl Validator {
         let overlay = BlockchainOverlay::new(&self.blockchain)?;
 
         // Generate a time keeper using transaction verifying slot
+        let current_time_keeper = &self.consensus.time_keeper;
         let time_keeper = TimeKeeper::new(
-            self.consensus.time_keeper.genesis_ts,
-            self.consensus.time_keeper.epoch_length,
-            self.consensus.time_keeper.slot_time,
+            current_time_keeper.genesis_ts,
+            current_time_keeper.epoch_length,
+            current_time_keeper.slot_time,
             verifying_slot,
         );
 

+ 70 - 0
src/validator/utils.rs

@@ -34,6 +34,7 @@ use crate::{
     runtime::vm_runtime::Runtime,
     tx::Transaction,
     util::time::TimeKeeper,
+    validator::consensus::{Fork, Proposal},
     Error, Result,
 };
 
@@ -221,3 +222,72 @@ pub fn genesis_txs_total(txs: &[Transaction]) -> Result<u64> {
 
     Ok(total)
 }
+
+/// Retrieve previous slot producers, last proposal hashes,
+/// and their second to last hashes, from all provided forks.
+pub fn previous_slot_info(
+    forks: &Vec<Fork>,
+    slot: u64,
+) -> Result<(u64, Vec<blake3::Hash>, Vec<blake3::Hash>)> {
+    let mut producers = 0;
+    let mut last_hashes = vec![];
+    let mut second_to_last_hashes = vec![];
+
+    for fork in forks {
+        let last_proposal = fork.last_proposal()?;
+        if last_proposal.block.header.height == slot {
+            producers += 1;
+        }
+        last_hashes.push(last_proposal.hash);
+        second_to_last_hashes.push(last_proposal.block.header.previous);
+    }
+
+    Ok((producers, last_hashes, second_to_last_hashes))
+}
+
+/// Given a proposal, find the index of the fork chain it extends, along with the specific
+/// extended proposal index.
+pub fn find_extended_fork_index(forks: &[Fork], proposal: &Proposal) -> Result<(usize, usize)> {
+    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() {
+            if &proposal.block.header.previous == p_hash {
+                return Ok((f_index, p_index))
+            }
+        }
+    }
+
+    Err(Error::ExtendedChainIndexNotFound)
+}
+
+/// Auxiliary function to find best ranked forks indexes.
+pub fn best_forks_indexes(forks: &[Fork]) -> Result<Vec<usize>> {
+    // Check if node has any forks
+    if forks.is_empty() {
+        return Err(Error::ForksNotFound)
+    }
+
+    // Find the best ranked forks
+    let mut best = 0;
+    let mut indexes = vec![];
+    for (f_index, fork) in forks.iter().enumerate() {
+        let rank = fork.rank;
+
+        // Fork ranks lower that current best
+        if rank < best {
+            continue
+        }
+
+        // Fork has same rank as current best
+        if rank == best {
+            indexes.push(f_index);
+            continue
+        }
+
+        // Fork ranks higher that current best
+        best = rank;
+        indexes = vec![f_index];
+    }
+
+    Ok(indexes)
+}

+ 1 - 1
src/validator/verification.rs

@@ -585,7 +585,7 @@ pub async fn verify_pos_proposal(
     let time_keeper = consensus.time_keeper.current();
 
     // Node have already checked for finalization in this slot (1)
-    if time_keeper.verifying_slot <= consensus.checked_finalization {
+    if time_keeper.verifying_slot <= *consensus.checked_finalization.read().await {
         warn!(target: "validator::verification::verify_pos_proposal", "Proposal received after finalization sync period.");
         return Err(Error::ProposalAfterFinalizationError)
     }