Kaynağa Gözat

validator: validate blocks based on their version

aggstam 2 yıl önce
ebeveyn
işleme
6ad2a19bac

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

@@ -159,8 +159,10 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     let genesis_block = BlockInfo::default();
     let genesis_block = BlockInfo::default();
     let genesis_txs_total = genesis_txs_total(&genesis_block.txs)?;
     let genesis_txs_total = genesis_txs_total(&genesis_block.txs)?;
     let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
     let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
+    // TODO: add miner threads arg
     let config = ValidatorConfig::new(
     let config = ValidatorConfig::new(
         time_keeper,
         time_keeper,
+        Some(40),
         genesis_block,
         genesis_block,
         genesis_txs_total,
         genesis_txs_total,
         vec![],
         vec![],

+ 33 - 16
bin/darkfid2/src/task/miner.rs

@@ -16,8 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
-use darkfi::{
-    blockchain::BlockInfo, system::sleep, util::time::Timestamp, validator::pow::PoWModule, Result,
+use darkfi::{blockchain::BlockInfo, system::sleep, util::time::Timestamp, Result};
+use darkfi_sdk::{
+    blockchain::{expected_reward, PidOutput, PreviousSlot, Slot},
+    pasta::{group::ff::Field, pallas},
 };
 };
 use log::info;
 use log::info;
 use smol::channel::Receiver;
 use smol::channel::Receiver;
@@ -57,40 +59,55 @@ pub async fn miner_task(node: &Darkfid, stop_signal: &Receiver<()>) -> Result<()
 
 
 /// Miner loop
 /// Miner loop
 async fn miner_loop(node: &Darkfid, stop_signal: &Receiver<()>) -> Result<()> {
 async fn miner_loop(node: &Darkfid, stop_signal: &Receiver<()>) -> Result<()> {
-    // TODO: add miner threads arg
-    // Generate a PoW module
-    let mut module = PoWModule::new(node.validator.read().await.blockchain.clone(), None, Some(90));
-
     // Miner loop
     // Miner loop
+    let timekeeper = node.validator.read().await.consensus.time_keeper.clone();
     loop {
     loop {
         // TODO: consensus should generate next block, along with its difficulty,
         // TODO: consensus should generate next block, along with its difficulty,
         //       derived from best fork
         //       derived from best fork
         // Retrieve last block
         // Retrieve last block
         let last = node.validator.read().await.blockchain.last_block()?;
         let last = node.validator.read().await.blockchain.last_block()?;
+        let last_hash = last.hash()?;
+
+        // Generate next PoW slot
+        let last_slot = last.slots.last().unwrap().clone();
+        let id = last_slot.id + 1;
+        let producers = 1;
+        let previous = PreviousSlot::new(
+            producers,
+            vec![last_hash],
+            vec![last.header.previous],
+            last_slot.pid.error,
+        );
+        let pid = PidOutput::default();
+        let total_tokens = last_slot.total_tokens + last_slot.reward;
+        let reward = expected_reward(id);
+        let slot = Slot::new(id, previous, pid, pallas::Base::ZERO, total_tokens, reward);
 
 
         // Mine next block
         // Mine next block
-        let difficulty = module.next_difficulty();
         let mut next_block = BlockInfo::default();
         let mut next_block = BlockInfo::default();
-        next_block.header.version = 0;
-        next_block.header.previous = last.hash()?;
-        next_block.header.height = last.header.height + 1;
+        next_block.header.previous = last_hash;
+        next_block.header.height = slot.id;
+        next_block.header.epoch = timekeeper.slot_epoch(next_block.header.height);
         next_block.header.timestamp = Timestamp::current_time();
         next_block.header.timestamp = Timestamp::current_time();
-        module.mine_block(&mut next_block, stop_signal)?;
+        next_block.slots = vec![slot];
+        node.validator.read().await.consensus.module.mine_block(&mut next_block, stop_signal)?;
 
 
         // Verify it
         // Verify it
-        module.verify_block(&next_block)?;
+        node.validator.read().await.consensus.module.verify_current_block(&next_block)?;
 
 
         // Generate stuff before pushing block to blockchain
         // Generate stuff before pushing block to blockchain
         let timestamp = next_block.header.timestamp.0;
         let timestamp = next_block.header.timestamp.0;
         let message = BlockInfoMessage::from(&next_block);
         let message = BlockInfoMessage::from(&next_block);
 
 
         // Append block to blockchain
         // Append block to blockchain
-        node.validator.write().await.add_blocks(&[next_block]).await?;
+        let mut lock = node.validator.write().await;
+        lock.add_blocks(&[next_block]).await?;
+
+        // Update PoW module
+        let difficulty = lock.consensus.module.next_difficulty();
+        lock.consensus.module.append(timestamp, &difficulty);
 
 
         // Broadcast block
         // Broadcast block
         node.sync_p2p.broadcast(&message).await;
         node.sync_p2p.broadcast(&message).await;
-
-        // Update PoW module
-        module.append(timestamp, &difficulty);
     }
     }
 }
 }

+ 1 - 1
bin/darkfid2/src/tests/forks.rs

@@ -28,7 +28,7 @@ fn forks() -> Result<()> {
     let blockchain = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
     let blockchain = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
 
 
     // Create a fork
     // Create a fork
-    let fork = Fork::new(&blockchain)?;
+    let fork = Fork::new(&blockchain, Some(90))?;
 
 
     // Add a dummy record to fork
     // Add a dummy record to fork
     fork.overlay.lock().unwrap().order.insert(&[0], &[record0])?;
     fork.overlay.lock().unwrap().order.insert(&[0], &[record0])?;

+ 10 - 7
bin/darkfid2/src/tests/harness.rs

@@ -29,7 +29,7 @@ use darkfi::{
 };
 };
 use darkfi_contract_test_harness::{vks, Holder, TestHarness};
 use darkfi_contract_test_harness::{vks, Holder, TestHarness};
 use darkfi_sdk::{
 use darkfi_sdk::{
-    blockchain::{expected_reward, PidOutput, PreviousSlot, Slot},
+    blockchain::{expected_reward, PidOutput, PreviousSlot, Slot, POS_START},
     pasta::{group::ff::Field, pallas},
     pasta::{group::ff::Field, pallas},
 };
 };
 use url::Url;
 use url::Url;
@@ -42,6 +42,7 @@ use crate::{
 };
 };
 
 
 pub struct HarnessConfig {
 pub struct HarnessConfig {
+    pub pow_target: Option<usize>,
     pub testing_node: bool,
     pub testing_node: bool,
     pub alice_initial: u64,
     pub alice_initial: u64,
     pub bob_initial: u64,
     pub bob_initial: u64,
@@ -81,6 +82,7 @@ impl Harness {
         let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
         let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
         let validator_config = ValidatorConfig::new(
         let validator_config = ValidatorConfig::new(
             time_keeper,
             time_keeper,
+            config.pow_target,
             genesis_block,
             genesis_block,
             genesis_txs_total,
             genesis_txs_total,
             vec![],
             vec![],
@@ -134,8 +136,8 @@ impl Harness {
         let alice = &self.alice.validator.read().await;
         let alice = &self.alice.validator.read().await;
         let bob = &self.bob.validator.read().await;
         let bob = &self.bob.validator.read().await;
 
 
-        alice.validate_blockchain(genesis_txs_total, vec![]).await?;
-        bob.validate_blockchain(genesis_txs_total, vec![]).await?;
+        alice.validate_blockchain(genesis_txs_total, vec![], self.config.pow_target).await?;
+        bob.validate_blockchain(genesis_txs_total, vec![], self.config.pow_target).await?;
 
 
         let alice_blockchain_len = alice.blockchain.len();
         let alice_blockchain_len = alice.blockchain.len();
         assert_eq!(alice_blockchain_len, bob.blockchain.len());
         assert_eq!(alice_blockchain_len, bob.blockchain.len());
@@ -160,7 +162,7 @@ impl Harness {
         Ok(())
         Ok(())
     }
     }
 
 
-    pub async fn generate_next_block(
+    pub async fn generate_next_pos_block(
         &self,
         &self,
         previous: &BlockInfo,
         previous: &BlockInfo,
         slots_count: usize,
         slots_count: usize,
@@ -171,7 +173,7 @@ impl Harness {
         let mut slots = Vec::with_capacity(slots_count);
         let mut slots = Vec::with_capacity(slots_count);
         let mut previous_slot = previous.slots.last().unwrap().clone();
         let mut previous_slot = previous.slots.last().unwrap().clone();
         for i in 0..slots_count {
         for i in 0..slots_count {
-            let id = previous_slot.id + 1;
+            let id = if previous_slot.id < POS_START { POS_START } else { previous_slot.id + 1 };
             // First slot in the sequence has (at least) 1 previous slot producer
             // First slot in the sequence has (at least) 1 previous slot producer
             let producers = if i == 0 { 1 } else { 0 };
             let producers = if i == 0 { 1 } else { 0 };
             let previous = PreviousSlot::new(
             let previous = PreviousSlot::new(
@@ -195,10 +197,11 @@ impl Harness {
         timestamp.add(1);
         timestamp.add(1);
 
 
         // Generate header
         // Generate header
+        let height = slots.last().unwrap().id;
         let header = Header::new(
         let header = Header::new(
             previous_hash,
             previous_hash,
-            previous.header.epoch,
-            slots.last().unwrap().id,
+            self.alice.validator.read().await.consensus.time_keeper.slot_epoch(height),
+            height,
             timestamp,
             timestamp,
             previous.header.nonce,
             previous.header.nonce,
         );
         );

+ 9 - 7
bin/darkfid2/src/tests/mod.rs

@@ -28,21 +28,23 @@ use harness::{generate_node, Harness, HarnessConfig};
 
 
 mod forks;
 mod forks;
 
 
-async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
+async fn sync_pos_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     init_logger();
     init_logger();
 
 
     // Initialize harness in testing mode
     // Initialize harness in testing mode
-    let config = HarnessConfig { testing_node: true, alice_initial: 1000, bob_initial: 500 };
+    let pow_target = Some(90);
+    let config =
+        HarnessConfig { testing_node: true, pow_target, alice_initial: 1000, bob_initial: 500 };
     let th = Harness::new(config, &ex).await?;
     let th = Harness::new(config, &ex).await?;
 
 
     // Retrieve genesis block
     // Retrieve genesis block
     let previous = th.alice.validator.read().await.blockchain.last_block()?;
     let previous = th.alice.validator.read().await.blockchain.last_block()?;
 
 
     // Generate next block
     // Generate next block
-    let block1 = th.generate_next_block(&previous, 1).await?;
+    let block1 = th.generate_next_pos_block(&previous, 1).await?;
 
 
     // Generate next block, with 4 empty slots inbetween
     // Generate next block, with 4 empty slots inbetween
-    let block2 = th.generate_next_block(&block1, 5).await?;
+    let block2 = th.generate_next_pos_block(&block1, 5).await?;
 
 
     // Add it to nodes
     // Add it to nodes
     th.add_blocks(&vec![block1, block2]).await?;
     th.add_blocks(&vec![block1, block2]).await?;
@@ -65,7 +67,7 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     let genesis_txs_total = th.config.alice_initial + th.config.bob_initial;
     let genesis_txs_total = th.config.alice_initial + th.config.bob_initial;
     let alice = &th.alice.validator.read().await;
     let alice = &th.alice.validator.read().await;
     let charlie = &charlie.validator.read().await;
     let charlie = &charlie.validator.read().await;
-    charlie.validate_blockchain(genesis_txs_total, vec![]).await?;
+    charlie.validate_blockchain(genesis_txs_total, vec![], pow_target).await?;
     assert_eq!(alice.blockchain.len(), charlie.blockchain.len());
     assert_eq!(alice.blockchain.len(), charlie.blockchain.len());
     assert_eq!(alice.blockchain.slots.len(), charlie.blockchain.slots.len());
     assert_eq!(alice.blockchain.slots.len(), charlie.blockchain.slots.len());
 
 
@@ -74,14 +76,14 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
 }
 }
 
 
 #[test]
 #[test]
-fn sync_blocks() -> Result<()> {
+fn sync_pos_blocks() -> Result<()> {
     let ex = Arc::new(Executor::new());
     let ex = Arc::new(Executor::new());
     let (signal, shutdown) = smol::channel::unbounded::<()>();
     let (signal, shutdown) = smol::channel::unbounded::<()>();
 
 
     easy_parallel::Parallel::new().each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))).finish(
     easy_parallel::Parallel::new().each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))).finish(
         || {
         || {
             smol::block_on(async {
             smol::block_on(async {
-                sync_blocks_real(ex.clone()).await.unwrap();
+                sync_pos_blocks_real(ex.clone()).await.unwrap();
                 drop(signal);
                 drop(signal);
             })
             })
         },
         },

+ 47 - 67
src/blockchain/mod.rs

@@ -139,19 +139,16 @@ impl Blockchain {
         trees.push(self.order.0.clone());
         trees.push(self.order.0.clone());
         batches.push(blocks_order_batch);
         batches.push(blocks_order_batch);
 
 
-        // Store extra stuff based on block version
-        if block.header.version > 0 {
-            // Store block slots uids vector
-            let slots = block.slots.iter().map(|x| x.id).collect();
-            let blocks_slots_bactch = self.blocks_slots.insert_batch(&block_hash_vec, &[&slots])?;
-            trees.push(self.blocks_slots.0.clone());
-            batches.push(blocks_slots_bactch);
-
-            // Store block slots
-            let slots_batch = self.slots.insert_batch(&block.slots)?;
-            trees.push(self.slots.0.clone());
-            batches.push(slots_batch);
-        }
+        // Store block slots uids vector
+        let slots = block.slots.iter().map(|x| x.id).collect();
+        let blocks_slots_bactch = self.blocks_slots.insert_batch(&block_hash_vec, &[&slots])?;
+        trees.push(self.blocks_slots.0.clone());
+        batches.push(blocks_slots_bactch);
+
+        // Store block slots
+        let slots_batch = self.slots.insert_batch(&block.slots)?;
+        trees.push(self.slots.0.clone());
+        batches.push(slots_batch);
 
 
         // Perform an atomic transaction over the trees and apply the batches.
         // Perform an atomic transaction over the trees and apply the batches.
         self.atomic_write(&trees, &batches)?;
         self.atomic_write(&trees, &batches)?;
@@ -173,22 +170,19 @@ impl Blockchain {
             return Ok(false)
             return Ok(false)
         }
         }
 
 
-        // Check extra stuff based on block version
-        if block.header.version > 0 {
-            // Check if we have block slots uids vector
-            let slots = match self.blocks_slots.get(&[blockhash], true) {
-                Ok(v) => v[0].clone().unwrap(),
-                Err(_) => return Ok(false),
-            };
-            let provided_block_slots: Vec<u64> = block.slots.iter().map(|x| x.id).collect();
-            if slots != provided_block_slots {
-                return Ok(false)
-            }
+        // Check if we have block slots uids vector
+        let slots = match self.blocks_slots.get(&[blockhash], true) {
+            Ok(v) => v[0].clone().unwrap(),
+            Err(_) => return Ok(false),
+        };
+        let provided_block_slots: Vec<u64> = block.slots.iter().map(|x| x.id).collect();
+        if slots != provided_block_slots {
+            return Ok(false)
+        }
 
 
-            // Check if we have all slots
-            if self.slots.get(&slots, true).is_err() {
-                return Ok(false)
-            }
+        // Check if we have all slots
+        if self.slots.get(&slots, true).is_err() {
+            return Ok(false)
         }
         }
 
 
         // Check provided info produces the same hash
         // Check provided info produces the same hash
@@ -216,14 +210,10 @@ impl Blockchain {
             let txs = self.transactions.get(&block.txs, true)?;
             let txs = self.transactions.get(&block.txs, true)?;
             let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
             let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
 
 
-            // Retrieve extra stuff based on block version
-            let mut block_slots = vec![];
-            if header.version > 0 {
-                let slots = self.blocks_slots.get(&[block.hash()], true)?;
-                let slots = slots[0].clone().unwrap();
-                let slots = self.slots.get(&slots, true)?;
-                block_slots = slots.iter().map(|x| x.clone().unwrap()).collect();
-            }
+            let slots = self.blocks_slots.get(&[block.hash()], true)?;
+            let slots = slots[0].clone().unwrap();
+            let slots = self.slots.get(&slots, true)?;
+            let block_slots = slots.iter().map(|x| x.clone().unwrap()).collect();
 
 
             let info = BlockInfo::new(header, txs, block.signature, block_slots);
             let info = BlockInfo::new(header, txs, block.signature, block_slots);
             ret.push(info);
             ret.push(info);
@@ -491,15 +481,12 @@ impl BlockchainOverlay {
         // Store block order
         // Store block order
         self.order.insert(&[block.header.height], &block_hash_vec)?;
         self.order.insert(&[block.header.height], &block_hash_vec)?;
 
 
-        // Store extra stuff based on block version
-        if block.header.version > 0 {
-            // Store block slots uids vector
-            let slots = block.slots.iter().map(|x| x.id).collect();
-            self.blocks_slots.insert(&block_hash_vec, &[&slots])?;
+        // Store block slots uids vector
+        let slots = block.slots.iter().map(|x| x.id).collect();
+        self.blocks_slots.insert(&block_hash_vec, &[&slots])?;
 
 
-            // Store block slots
-            self.slots.insert(&block.slots)?;
-        }
+        // Store block slots
+        self.slots.insert(&block.slots)?;
 
 
         Ok(block_hash)
         Ok(block_hash)
     }
     }
@@ -518,22 +505,19 @@ impl BlockchainOverlay {
             return Ok(false)
             return Ok(false)
         }
         }
 
 
-        // Check extra stuff based on block version
-        if block.header.version > 0 {
-            // Check if we have block slots uids vector
-            let slots = match self.blocks_slots.get(&[blockhash], true) {
-                Ok(v) => v[0].clone().unwrap(),
-                Err(_) => return Ok(false),
-            };
-            let provided_block_slots: Vec<u64> = block.slots.iter().map(|x| x.id).collect();
-            if slots != provided_block_slots {
-                return Ok(false)
-            }
+        // Check if we have block slots uids vector
+        let slots = match self.blocks_slots.get(&[blockhash], true) {
+            Ok(v) => v[0].clone().unwrap(),
+            Err(_) => return Ok(false),
+        };
+        let provided_block_slots: Vec<u64> = block.slots.iter().map(|x| x.id).collect();
+        if slots != provided_block_slots {
+            return Ok(false)
+        }
 
 
-            // Check if we have all slots
-            if self.slots.get(&slots, true).is_err() {
-                return Ok(false)
-            }
+        // Check if we have all slots
+        if self.slots.get(&slots, true).is_err() {
+            return Ok(false)
         }
         }
 
 
         // Check provided info produces the same hash
         // Check provided info produces the same hash
@@ -561,14 +545,10 @@ impl BlockchainOverlay {
             let txs = self.transactions.get(&block.txs, true)?;
             let txs = self.transactions.get(&block.txs, true)?;
             let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
             let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
 
 
-            // Retrieve extra stuff based on block version
-            let mut block_slots = vec![];
-            if header.version > 0 {
-                let slots = self.blocks_slots.get(&[block.hash()], true)?;
-                let slots = slots[0].clone().unwrap();
-                let slots = self.slots.get(&slots, true)?;
-                block_slots = slots.iter().map(|x| x.clone().unwrap()).collect();
-            }
+            let slots = self.blocks_slots.get(&[block.hash()], true)?;
+            let slots = slots[0].clone().unwrap();
+            let slots = self.slots.get(&slots, true)?;
+            let block_slots = slots.iter().map(|x| x.clone().unwrap()).collect();
 
 
             let info = BlockInfo::new(header, txs, block.signature, block_slots);
             let info = BlockInfo::new(header, txs, block.signature, block_slots);
             ret.push(info);
             ret.push(info);

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

@@ -111,7 +111,7 @@ impl TestHarness {
 
 
         let timer = Instant::now();
         let timer = Instant::now();
 
 
-        wallet.validator.read().await.add_test_producer_transaction(tx, slot, true).await?;
+        wallet.validator.read().await.add_test_producer_transaction(tx, slot, 2, true).await?;
         wallet.consensus_staked_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         wallet.consensus_staked_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
         tx_action_benchmark.verify_times.push(timer.elapsed());
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
 
@@ -174,7 +174,7 @@ impl TestHarness {
             .validator
             .validator
             .read()
             .read()
             .await
             .await
-            .add_test_producer_transaction(tx, slot, true)
+            .add_test_producer_transaction(tx, slot, 2, true)
             .await
             .await
             .is_err());
             .is_err());
         tx_action_benchmark.verify_times.push(timer.elapsed());
         tx_action_benchmark.verify_times.push(timer.elapsed());

+ 1 - 0
src/contract/test-harness/src/lib.rs

@@ -157,6 +157,7 @@ impl Wallet {
         let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
         let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
         let config = ValidatorConfig::new(
         let config = ValidatorConfig::new(
             time_keeper,
             time_keeper,
+            Some(90),
             genesis_block.clone(),
             genesis_block.clone(),
             0,
             0,
             faucet_pubkeys.to_vec(),
             faucet_pubkeys.to_vec(),

+ 7 - 2
src/contract/test-harness/src/money_pow_reward.rs

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

+ 3 - 0
src/error.rs

@@ -354,6 +354,9 @@ pub enum Error {
     #[error("Block {0} is invalid")]
     #[error("Block {0} is invalid")]
     BlockIsInvalid(String),
     BlockIsInvalid(String),
 
 
+    #[error("Block version {0} is invalid")]
+    BlockVersionIsInvalid(u8),
+
     #[error("Block {0} already in database")]
     #[error("Block {0} already in database")]
     BlockAlreadyExists(String),
     BlockAlreadyExists(String),
 
 

+ 37 - 8
src/validator/consensus.rs

@@ -29,7 +29,7 @@ use crate::{
     blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header},
     blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header},
     tx::Transaction,
     tx::Transaction,
     util::time::{TimeKeeper, Timestamp},
     util::time::{TimeKeeper, Timestamp},
-    validator::{pid::slot_pid_output, verify_block, verify_transactions},
+    validator::{pid::slot_pid_output, pow::PoWModule, verify_block, verify_transactions},
     Error, Result,
     Error, Result,
 };
 };
 
 
@@ -42,25 +42,38 @@ pub struct Consensus {
     pub blockchain: Blockchain,
     pub blockchain: Blockchain,
     /// Helper structure to calculate time related operations
     /// Helper structure to calculate time related operations
     pub time_keeper: TimeKeeper,
     pub time_keeper: TimeKeeper,
+    /// Currently configured PoW target
+    pub pow_target: Option<usize>,
     /// Node is participating to consensus
     /// Node is participating to consensus
     pub participating: bool,
     pub participating: bool,
     /// Last slot node check for finalization
     /// Last slot node check for finalization
     pub checked_finalization: u64,
     pub checked_finalization: u64,
     /// Fork chains containing block proposals
     /// Fork chains containing block proposals
     pub forks: Vec<Fork>,
     pub forks: Vec<Fork>,
+    // TODO: remove this once everything goes through forks
+    /// Canonical blockchain PoW module state
+    pub module: PoWModule,
     /// Flag to enable testing mode
     /// Flag to enable testing mode
     pub testing_mode: bool,
     pub testing_mode: bool,
 }
 }
 
 
 impl Consensus {
 impl Consensus {
     /// Generate a new Consensus state.
     /// Generate a new Consensus state.
-    pub fn new(blockchain: Blockchain, time_keeper: TimeKeeper, testing_mode: bool) -> Self {
+    pub fn new(
+        blockchain: Blockchain,
+        time_keeper: TimeKeeper,
+        pow_target: Option<usize>,
+        testing_mode: bool,
+    ) -> Self {
+        let module = PoWModule::new(blockchain.clone(), None, pow_target);
         Self {
         Self {
             blockchain,
             blockchain,
             time_keeper,
             time_keeper,
+            pow_target,
             participating: false,
             participating: false,
             checked_finalization: 0,
             checked_finalization: 0,
             forks: vec![],
             forks: vec![],
+            module,
             testing_mode,
             testing_mode,
         }
         }
     }
     }
@@ -72,7 +85,7 @@ impl Consensus {
 
 
         // If no forks exist, create a new one as a basis to extend
         // If no forks exist, create a new one as a basis to extend
         if self.forks.is_empty() {
         if self.forks.is_empty() {
-            self.forks.push(Fork::new(&self.blockchain)?);
+            self.forks.push(Fork::new(&self.blockchain, self.pow_target)?);
         }
         }
 
 
         // Grab previous slot information
         // Grab previous slot information
@@ -238,6 +251,7 @@ impl Consensus {
         if verify_block(
         if verify_block(
             &fork.overlay,
             &fork.overlay,
             &time_keeper,
             &time_keeper,
+            &fork.module,
             &proposal.block,
             &proposal.block,
             &previous,
             &previous,
             expected_reward,
             expected_reward,
@@ -251,6 +265,11 @@ impl Consensus {
             return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
             return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
         };
         };
 
 
+        // Update PoW module
+        if proposal.block.header.version == 1 {
+            fork.module.append(proposal.block.header.timestamp.0, &fork.module.next_difficulty());
+        }
+
         // If a fork index was found, replace forks with the mutated one,
         // If a fork index was found, replace forks with the mutated one,
         // otherwise push the new fork.
         // otherwise push the new fork.
         fork.proposals.push(proposal.hash);
         fork.proposals.push(proposal.hash);
@@ -299,7 +318,7 @@ impl Consensus {
                 return Err(Error::ExtendedChainIndexNotFound)
                 return Err(Error::ExtendedChainIndexNotFound)
             }
             }
 
 
-            return Ok((Fork::new(&self.blockchain)?, None))
+            return Ok((Fork::new(&self.blockchain, self.pow_target)?, None))
         }
         }
 
 
         let (f_index, p_index) = found.unwrap();
         let (f_index, p_index) = found.unwrap();
@@ -310,7 +329,7 @@ impl Consensus {
         }
         }
 
 
         // Rebuild fork
         // Rebuild fork
-        let mut fork = Fork::new(&self.blockchain)?;
+        let mut fork = Fork::new(&self.blockchain, self.pow_target)?;
         fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
         fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
 
 
         // Retrieve proposals blocks from original fork
         // Retrieve proposals blocks from original fork
@@ -334,6 +353,7 @@ impl Consensus {
             if verify_block(
             if verify_block(
                 &fork.overlay,
                 &fork.overlay,
                 &time_keeper,
                 &time_keeper,
+                &fork.module,
                 block,
                 block,
                 previous,
                 previous,
                 expected_reward,
                 expected_reward,
@@ -347,6 +367,11 @@ impl Consensus {
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
             };
 
 
+            // Update PoW module
+            if block.header.version == 1 {
+                fork.module.append(block.header.timestamp.0, &fork.module.next_difficulty());
+            }
+
             // Use last inserted block as next iteration previous
             // Use last inserted block as next iteration previous
             previous = block;
             previous = block;
         }
         }
@@ -444,6 +469,8 @@ impl From<Proposal> for BlockInfo {
 pub struct Fork {
 pub struct Fork {
     /// Overlay cache over canonical Blockchain
     /// Overlay cache over canonical Blockchain
     pub overlay: BlockchainOverlayPtr,
     pub overlay: BlockchainOverlayPtr,
+    /// Current PoW module state,
+    pub module: PoWModule,
     /// Fork proposal hashes sequence
     /// Fork proposal hashes sequence
     pub proposals: Vec<blake3::Hash>,
     pub proposals: Vec<blake3::Hash>,
     /// Hot/live slots
     /// Hot/live slots
@@ -453,11 +480,12 @@ pub struct Fork {
 }
 }
 
 
 impl Fork {
 impl Fork {
-    pub fn new(blockchain: &Blockchain) -> Result<Self> {
+    pub fn new(blockchain: &Blockchain, pow_target: Option<usize>) -> Result<Self> {
+        let module = PoWModule::new(blockchain.clone(), None, pow_target);
         let mempool =
         let mempool =
             blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
             blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
         let overlay = BlockchainOverlay::new(blockchain)?;
         let overlay = BlockchainOverlay::new(blockchain)?;
-        Ok(Self { overlay, proposals: vec![], slots: vec![], mempool })
+        Ok(Self { overlay, module, proposals: vec![], slots: vec![], mempool })
     }
     }
 
 
     /// Auxiliary function to retrieve last proposal
     /// Auxiliary function to retrieve last proposal
@@ -573,10 +601,11 @@ impl Fork {
     /// overlay pointer have been updated to the cloned one.
     /// overlay pointer have been updated to the cloned one.
     pub fn full_clone(&self) -> Result<Self> {
     pub fn full_clone(&self) -> Result<Self> {
         let overlay = self.overlay.lock().unwrap().full_clone()?;
         let overlay = self.overlay.lock().unwrap().full_clone()?;
+        let module = self.module.clone();
         let proposals = self.proposals.clone();
         let proposals = self.proposals.clone();
         let slots = self.slots.clone();
         let slots = self.slots.clone();
         let mempool = self.mempool.clone();
         let mempool = self.mempool.clone();
 
 
-        Ok(Self { overlay, proposals, slots, mempool })
+        Ok(Self { overlay, module, proposals, slots, mempool })
     }
     }
 }
 }

+ 37 - 5
src/validator/mod.rs

@@ -40,6 +40,7 @@ use consensus::Consensus;
 
 
 /// DarkFi PoW module
 /// DarkFi PoW module
 pub mod pow;
 pub mod pow;
+use pow::PoWModule;
 
 
 /// DarkFi consensus PID controller
 /// DarkFi consensus PID controller
 pub mod pid;
 pub mod pid;
@@ -65,6 +66,8 @@ pub mod float_10;
 pub struct ValidatorConfig {
 pub struct ValidatorConfig {
     /// Helper structure to calculate time related operations
     /// Helper structure to calculate time related operations
     pub time_keeper: TimeKeeper,
     pub time_keeper: TimeKeeper,
+    /// Currently configured PoW target
+    pub pow_target: Option<usize>,
     /// Genesis block
     /// Genesis block
     pub genesis_block: BlockInfo,
     pub genesis_block: BlockInfo,
     /// Total amount of minted tokens in genesis block
     /// Total amount of minted tokens in genesis block
@@ -78,12 +81,20 @@ pub struct ValidatorConfig {
 impl ValidatorConfig {
 impl ValidatorConfig {
     pub fn new(
     pub fn new(
         time_keeper: TimeKeeper,
         time_keeper: TimeKeeper,
+        pow_target: Option<usize>,
         genesis_block: BlockInfo,
         genesis_block: BlockInfo,
         genesis_txs_total: u64,
         genesis_txs_total: u64,
         faucet_pubkeys: Vec<PublicKey>,
         faucet_pubkeys: Vec<PublicKey>,
         testing_mode: bool,
         testing_mode: bool,
     ) -> Self {
     ) -> Self {
-        Self { time_keeper, genesis_block, genesis_txs_total, faucet_pubkeys, testing_mode }
+        Self {
+            time_keeper,
+            pow_target,
+            genesis_block,
+            genesis_txs_total,
+            faucet_pubkeys,
+            testing_mode,
+        }
     }
     }
 }
 }
 
 
@@ -132,7 +143,8 @@ impl Validator {
         overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
         overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
 
 
         info!(target: "validator::new", "Initializing Consensus");
         info!(target: "validator::new", "Initializing Consensus");
-        let consensus = Consensus::new(blockchain.clone(), config.time_keeper, testing_mode);
+        let consensus =
+            Consensus::new(blockchain.clone(), config.time_keeper, config.pow_target, testing_mode);
 
 
         // Create the actual state
         // Create the actual state
         let state =
         let state =
@@ -296,8 +308,9 @@ impl Validator {
         // Retrieve last block
         // Retrieve last block
         let mut previous = &overlay.lock().unwrap().last_block()?;
         let mut previous = &overlay.lock().unwrap().last_block()?;
 
 
-        // Create a time keeper to validate each block
+        // Create a time keeper and a PoW module to validate each block
         let mut time_keeper = self.consensus.time_keeper.clone();
         let mut time_keeper = self.consensus.time_keeper.clone();
+        let mut module = self.consensus.module.clone();
 
 
         // Keep track of all blocks transactions to remove them from pending txs store
         // Keep track of all blocks transactions to remove them from pending txs store
         let mut removed_txs = vec![];
         let mut removed_txs = vec![];
@@ -314,6 +327,7 @@ impl Validator {
             if verify_block(
             if verify_block(
                 &overlay,
                 &overlay,
                 &time_keeper,
                 &time_keeper,
+                &module,
                 block,
                 block,
                 previous,
                 previous,
                 expected_reward,
                 expected_reward,
@@ -327,6 +341,11 @@ impl Validator {
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
             };
 
 
+            // Update PoW module
+            if block.header.version == 1 {
+                module.append(block.header.timestamp.0, &module.next_difficulty());
+            }
+
             // Store block transactions
             // Store block transactions
             for tx in &block.txs {
             for tx in &block.txs {
                 removed_txs.push(tx.clone());
                 removed_txs.push(tx.clone());
@@ -343,6 +362,9 @@ impl Validator {
         self.blockchain.remove_pending_txs(&removed_txs)?;
         self.blockchain.remove_pending_txs(&removed_txs)?;
         self.purge_pending_txs().await?;
         self.purge_pending_txs().await?;
 
 
+        // Update PoW module
+        self.consensus.module = module;
+
         Ok(())
         Ok(())
     }
     }
 
 
@@ -407,6 +429,7 @@ impl Validator {
         &self,
         &self,
         tx: &Transaction,
         tx: &Transaction,
         verifying_slot: u64,
         verifying_slot: u64,
+        block_version: u8,
         write: bool,
         write: bool,
     ) -> Result<()> {
     ) -> Result<()> {
         debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
         debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
@@ -422,7 +445,8 @@ impl Validator {
 
 
         // Verify transaction
         // Verify transaction
         let mut erroneous_txs = vec![];
         let mut erroneous_txs = vec![];
-        if let Err(e) = verify_producer_transaction(&overlay, &time_keeper, tx).await {
+        if let Err(e) = verify_producer_transaction(&overlay, &time_keeper, tx, block_version).await
+        {
             warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {}", e);
             warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {}", e);
             erroneous_txs.push(tx.clone());
             erroneous_txs.push(tx.clone());
         }
         }
@@ -453,6 +477,7 @@ impl Validator {
         &self,
         &self,
         genesis_txs_total: u64,
         genesis_txs_total: u64,
         faucet_pubkeys: Vec<PublicKey>,
         faucet_pubkeys: Vec<PublicKey>,
+        pow_target: Option<usize>,
     ) -> Result<()> {
     ) -> Result<()> {
         let blocks = self.blockchain.get_all()?;
         let blocks = self.blockchain.get_all()?;
 
 
@@ -469,8 +494,9 @@ impl Validator {
         // Set previous
         // Set previous
         let mut previous = &blocks[0];
         let mut previous = &blocks[0];
 
 
-        // Create a time keeper to validate each block
+        // Create a time keeper and a PoW module to validate each block
         let mut time_keeper = self.consensus.time_keeper.clone();
         let mut time_keeper = self.consensus.time_keeper.clone();
+        let mut module = PoWModule::new(blockchain.clone(), None, pow_target);
 
 
         // Deploy native wasm contracts
         // Deploy native wasm contracts
         deploy_native_contracts(&overlay, &time_keeper, &faucet_pubkeys)?;
         deploy_native_contracts(&overlay, &time_keeper, &faucet_pubkeys)?;
@@ -490,6 +516,7 @@ impl Validator {
             if verify_block(
             if verify_block(
                 &overlay,
                 &overlay,
                 &time_keeper,
                 &time_keeper,
+                &module,
                 block,
                 block,
                 previous,
                 previous,
                 expected_reward,
                 expected_reward,
@@ -503,6 +530,11 @@ impl Validator {
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
             };
 
 
+            // Update PoW module
+            if block.header.version == 1 {
+                module.append(block.header.timestamp.0, &module.next_difficulty());
+            }
+
             // Use last inserted block as next iteration previous
             // Use last inserted block as next iteration previous
             previous = block;
             previous = block;
         }
         }

+ 18 - 11
src/validator/pow.rs

@@ -73,6 +73,7 @@ const CUT_BEGIN: usize = 60;
 const CUT_END: usize = 660;
 const CUT_END: usize = 660;
 /// Default target block time, in seconds
 /// Default target block time, in seconds
 const DIFFICULTY_TARGET: usize = 20;
 const DIFFICULTY_TARGET: usize = 20;
+// TODO: maybe add more difficulty targets (testnet, mainnet, etc)
 /// How many most recent blocks to use to verify new blocks' timestamp
 /// How many most recent blocks to use to verify new blocks' timestamp
 const BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW: usize = 60;
 const BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW: usize = 60;
 /// Time limit in the future of what blocks can be
 /// Time limit in the future of what blocks can be
@@ -175,12 +176,18 @@ impl PoWModule {
         difficulty == &self.next_difficulty()
         difficulty == &self.next_difficulty()
     }
     }
 
 
-    /// Verify provided block timestamp is valid and matches certain criteria
-    pub fn verify_timestamp(&self, timestamp: u64) -> bool {
+    /// Verify provided block timestamp is not far in the future and
+    /// check its valid acorrding to current timestamps median
+    pub fn verify_current_timestamp(&self, timestamp: u64) -> bool {
         if timestamp > Timestamp::current_time().0 + BLOCK_FUTURE_TIME_LIMIT {
         if timestamp > Timestamp::current_time().0 + BLOCK_FUTURE_TIME_LIMIT {
             return false
             return false
         }
         }
 
 
+        self.verify_timestamp_by_median(timestamp)
+    }
+
+    /// Verify provided block timestamp is valid and matches certain criteria
+    pub fn verify_timestamp_by_median(&self, timestamp: u64) -> bool {
         // If not enough blocks, no proper median yet, return true
         // If not enough blocks, no proper median yet, return true
         if self.timestamps.len() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW {
         if self.timestamps.len() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW {
             return true
             return true
@@ -192,17 +199,17 @@ impl PoWModule {
         timestamp >= median(timestamps)
         timestamp >= median(timestamps)
     }
     }
 
 
-    /// Verify provided block timestamp and difficulty pair
-    pub fn verify_pair(&self, timestamp: u64, difficulty: &BigUint) {
-        assert!(self.verify_timestamp(timestamp));
-        assert!(self.verify_difficulty(difficulty));
+    /// Verify provided block timestamp and hash
+    pub fn verify_current_block(&self, block: &BlockInfo) -> Result<()> {
+        // First we verify the block's timestamp
+        assert!(self.verify_current_timestamp(block.header.timestamp.0));
+
+        // Then we verify the block's hash
+        self.verify_block_hash(block)
     }
     }
 
 
     /// Verify provided block corresponds to next mine target
     /// Verify provided block corresponds to next mine target
-    pub fn verify_block(&self, block: &BlockInfo) -> Result<()> {
-        // First we verify the block's timestamp
-        assert!(self.verify_timestamp(block.header.timestamp.0));
-
+    pub fn verify_block_hash(&self, block: &BlockInfo) -> Result<()> {
         // Then we verify the proof of work:
         // Then we verify the proof of work:
         let verifier_setup = Instant::now();
         let verifier_setup = Instant::now();
 
 
@@ -421,7 +428,7 @@ mod tests {
         module.mine_block(&mut next_block, &recvr)?;
         module.mine_block(&mut next_block, &recvr)?;
 
 
         // Verify it
         // Verify it
-        module.verify_block(&next_block)?;
+        module.verify_current_block(&next_block)?;
 
 
         Ok(())
         Ok(())
     }
     }

+ 226 - 55
src/validator/validation.rs

@@ -17,88 +17,252 @@
  */
  */
 
 
 use darkfi_sdk::{
 use darkfi_sdk::{
-    blockchain::{expected_reward, Slot},
-    pasta::pallas,
+    blockchain::{block_version, expected_reward, Slot},
+    pasta::{group::ff::Field, pallas},
 };
 };
 
 
 use crate::{
 use crate::{
     blockchain::{BlockInfo, Blockchain},
     blockchain::{BlockInfo, Blockchain},
-    validator::pid::slot_pid_output,
+    validator::{pid::slot_pid_output, pow::PoWModule},
     Error, Result,
     Error, Result,
 };
 };
 
 
-/// A block is considered valid when the following rules apply:
-///     1. Parent hash is equal to the hash of the previous block
-///     2. Timestamp increments previous block timestamp
-///     3. Slot increments previous block slot
-///     4. Slots vector is not empty and all its slots are valid
-///     5. Slot is the same as the slots vector last slot id
+/// Validate provided block, using its previous, based on its version.
+pub fn validate_block(
+    block: &BlockInfo,
+    previous: &BlockInfo,
+    expected_reward: u64,
+    module: &PoWModule,
+) -> Result<()> {
+    // TODO: verify block validations work as expected on versions change(cutoff)
+    match block_version(block.header.height) {
+        1 => validate_pow_block(block, previous, expected_reward, module)?,
+        2 => validate_pos_block(block, previous, expected_reward)?,
+        _ => return Err(Error::BlockVersionIsInvalid(block.header.version)),
+    }
+
+    Ok(())
+}
+
+/// A PoW block is considered valid when the following rules apply:
+///     1. Block version is equal to 1
+///     2. Parent hash is equal to the hash of the previous block
+///     3. Block heigh increments previous block height by 1
+///     4. Timestamp is valid based on PoWModule validation
+///     5. Block hash is valid based on PoWModule validation
+///     6. Slots vector contains a single valid slot
+///     7. Block height is the same as the slots vector last slot id
 /// Additional validity rules can be applied.
 /// Additional validity rules can be applied.
-pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, expected_reward: u64) -> Result<()> {
+pub fn validate_pow_block(
+    block: &BlockInfo,
+    previous: &BlockInfo,
+    expected_reward: u64,
+    module: &PoWModule,
+) -> Result<()> {
     let error = Err(Error::BlockIsInvalid(block.hash()?.to_string()));
     let error = Err(Error::BlockIsInvalid(block.hash()?.to_string()));
+
+    // Check block version (1)
+    if block.header.version != 1 {
+        return error
+    }
+
+    // Check previous hash (2)
     let previous_hash = previous.hash()?;
     let previous_hash = previous.hash()?;
+    if block.header.previous != previous_hash {
+        return error
+    }
+
+    // Check heights are incremental (3)
+    if block.header.height != previous.header.height + 1 {
+        return error
+    }
+
+    // Check timestamp validity (4)
+    if !module.verify_timestamp_by_median(block.header.timestamp.0) {
+        return error
+    }
+
+    // Check block hash corresponds to next one (5)
+    module.verify_block_hash(block)?;
+
+    // Verify slots vector is empty (6)
+    if block.slots.len() != 1 {
+        return error
+    }
+
+    // Retrieve previous block last slot
+    let previous_slot = previous.slots.last().unwrap();
+
+    // Validate last slot
+    let last_slot = block.slots.last().unwrap();
+    validate_pow_slot(
+        last_slot,
+        previous_slot,
+        &previous_hash,
+        &previous.header.previous,
+        expected_reward,
+    )?;
+
+    // Check block height is the last slot id (7)
+    if last_slot.id != block.header.height {
+        return error
+    }
+
+    Ok(())
+}
+
+/// A PoW slot is considered valid when the following rules apply:
+///     1. Id increments previous slot id by 1
+///     2. Forks extend previous block hash
+///     3. Forks follow previous block sequence
+///     4. Slot total tokens represent the total network tokens
+///        up until this slot
+///     5. Slot previous error value correspond to previous slot one
+///     6. Slot previous has only 1 producer(the miner)
+///     7. PID output for this slot is correct(zero)
+///     8. Slot last eta is the expected one(zero)
+///     9. Slot reward value is the expected one
+/// Additional validity rules can be applied.
+pub fn validate_pow_slot(
+    slot: &Slot,
+    previous: &Slot,
+    previous_block_hash: &blake3::Hash,
+    previous_block_sequence: &blake3::Hash,
+    expected_reward: u64,
+) -> Result<()> {
+    let error = Err(Error::SlotIsInvalid(slot.id));
+
+    // Check slots are incremental (1)
+    if slot.id != previous.id + 1 {
+        return error
+    }
+
+    // Check previous block hash (2)
+    if !slot.previous.last_hashes.contains(previous_block_hash) {
+        return error
+    }
+
+    // Check previous block sequence (3)
+    if !slot.previous.second_to_last_hashes.contains(previous_block_sequence) {
+        return error
+    }
+
+    // Check total tokens (4)
+    if slot.total_tokens != previous.total_tokens + previous.reward {
+        return error
+    }
+
+    // Check previous slot error (5)
+    if slot.previous.error != previous.pid.error {
+        return error
+    }
+
+    // Check previous slot producers (6)
+    if slot.previous.producers != 1 {
+        return error
+    }
 
 
-    // Check previous hash (1)
+    // Check PID output for this slot (7)
+    if (slot.pid.f, slot.pid.error, slot.pid.sigma1, slot.pid.sigma2) !=
+        (0.0, 0.0, pallas::Base::ZERO, pallas::Base::ZERO)
+    {
+        return error
+    }
+
+    // Check eta is the expected one (8)
+    if slot.last_eta != pallas::Base::ZERO {
+        return error
+    }
+
+    // Check reward is the expected one (9)
+    if slot.reward != expected_reward {
+        return error
+    }
+
+    Ok(())
+}
+
+/// A PoS block is considered valid when the following rules apply:
+///     1. Block version is equal to 2
+///     2. Parent hash is equal to the hash of the previous block
+///     3. Timestamp increments previous block timestamp
+///     4. Slot increments previous block slot
+///     5. Slots vector is not empty and all its slots are valid
+///     6. Slot is the same as the slots vector last slot id
+/// Additional validity rules can be applied.
+pub fn validate_pos_block(
+    block: &BlockInfo,
+    previous: &BlockInfo,
+    expected_reward: u64,
+) -> Result<()> {
+    let error = Err(Error::BlockIsInvalid(block.hash()?.to_string()));
+
+    // Check block version (1)
+    if block.header.version != 2 {
+        return error
+    }
+
+    // Check previous hash (2)
+    let previous_hash = previous.hash()?;
     if block.header.previous != previous_hash {
     if block.header.previous != previous_hash {
         return error
         return error
     }
     }
 
 
-    // Check timestamps are incremental (2)
+    // Check timestamps are incremental (3)
     if block.header.timestamp <= previous.header.timestamp {
     if block.header.timestamp <= previous.header.timestamp {
         return error
         return error
     }
     }
 
 
-    // Check heights are incremental (3)
+    // Check heights are incremental (4)
     if block.header.height <= previous.header.height {
     if block.header.height <= previous.header.height {
         return error
         return error
     }
     }
 
 
-    // Check extra stuff based on block version
-    if block.header.version > 0 {
-        // Verify slots (4)
-        if block.slots.is_empty() {
-            return error
-        }
+    // Verify slots (5)
+    if block.slots.is_empty() {
+        return error
+    }
 
 
-        // Retrieve previous block last slot
-        let mut previous_slot = previous.slots.last().unwrap();
-
-        // Check if empty slots existed
-        if block.slots.len() > 1 {
-            // All slots exluding the last one must have reward value set to 0.
-            // Slots must already be in correct order (sorted by id).
-            for slot in &block.slots[..block.slots.len() - 1] {
-                validate_slot(
-                    slot,
-                    previous_slot,
-                    &previous_hash,
-                    &previous.header.previous,
-                    &previous.header.nonce,
-                    0,
-                )?;
-                previous_slot = slot;
-            }
-        }
+    // Retrieve previous block last slot
+    let mut previous_slot = previous.slots.last().unwrap();
 
 
-        validate_slot(
-            block.slots.last().unwrap(),
-            previous_slot,
-            &previous_hash,
-            &previous.header.previous,
-            &previous.header.nonce,
-            expected_reward,
-        )?;
-
-        // Check block height is the last slot id (5)
-        if block.slots.last().unwrap().id != block.header.height {
-            return error
+    // Check if empty slots existed
+    if block.slots.len() > 1 {
+        // All slots exluding the last one must have reward value set to 0.
+        // Slots must already be in correct order (sorted by id).
+        for slot in &block.slots[..block.slots.len() - 1] {
+            validate_pos_slot(
+                slot,
+                previous_slot,
+                &previous_hash,
+                &previous.header.previous,
+                &previous.header.nonce,
+                0,
+            )?;
+            previous_slot = slot;
         }
         }
     }
     }
 
 
+    // Validate last slot
+    let last_slot = block.slots.last().unwrap();
+    validate_pos_slot(
+        last_slot,
+        previous_slot,
+        &previous_hash,
+        &previous.header.previous,
+        &previous.header.nonce,
+        expected_reward,
+    )?;
+
+    // Check block height is the last slot id (6)
+    if last_slot.id != block.header.height {
+        return error
+    }
+
     Ok(())
     Ok(())
 }
 }
 
 
-/// A slot is considered valid when the following rules apply:
+/// A PoS slot is considered valid when the following rules apply:
 ///     1. Id increments previous slot id
 ///     1. Id increments previous slot id
 ///     2. Forks extend previous block hash
 ///     2. Forks extend previous block hash
 ///     3. Forks follow previous block sequence
 ///     3. Forks follow previous block sequence
@@ -109,7 +273,7 @@ pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, expected_reward:
 ///     7. Slot last eta is the expected one
 ///     7. Slot last eta is the expected one
 ///     8. Slot reward value is the expected one
 ///     8. Slot reward value is the expected one
 /// Additional validity rules can be applied.
 /// Additional validity rules can be applied.
-pub fn validate_slot(
+pub fn validate_pos_slot(
     slot: &Slot,
     slot: &Slot,
     previous: &Slot,
     previous: &Slot,
     previous_block_hash: &blake3::Hash,
     previous_block_hash: &blake3::Hash,
@@ -151,7 +315,7 @@ pub fn validate_slot(
         return error
         return error
     }
     }
 
 
-    // Check reward is the expected one (7)
+    // Check eta is the expected one (7)
     if &slot.last_eta != last_eta {
     if &slot.last_eta != last_eta {
         return error
         return error
     }
     }
@@ -167,13 +331,20 @@ pub fn validate_slot(
 /// A blockchain is considered valid, when every block is valid,
 /// A blockchain is considered valid, when every block is valid,
 /// based on validate_block checks.
 /// based on validate_block checks.
 /// Be careful as this will try to load everything in memory.
 /// Be careful as this will try to load everything in memory.
-pub fn validate_blockchain(blockchain: &Blockchain) -> Result<()> {
+pub fn validate_blockchain(blockchain: &Blockchain, pow_target: Option<usize>) -> Result<()> {
+    // Generate a PoW module
+    let mut module = PoWModule::new(blockchain.clone(), None, pow_target);
     // We use block order store here so we have all blocks in order
     // We use block order store here so we have all blocks in order
     let blocks = blockchain.order.get_all()?;
     let blocks = blockchain.order.get_all()?;
     for (index, block) in blocks[1..].iter().enumerate() {
     for (index, block) in blocks[1..].iter().enumerate() {
         let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
         let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
         let expected_reward = expected_reward(full_blocks[1].header.height);
         let expected_reward = expected_reward(full_blocks[1].header.height);
-        validate_block(&full_blocks[1], &full_blocks[0], expected_reward)?;
+        let full_block = &full_blocks[1];
+        validate_block(full_block, &full_blocks[0], expected_reward, &module)?;
+        // Update PoW module
+        if full_block.header.version == 1 {
+            module.append(full_block.header.timestamp.0, &module.next_difficulty());
+        }
     }
     }
 
 
     Ok(())
     Ok(())

+ 48 - 28
src/validator/verification.rs

@@ -31,7 +31,7 @@ use crate::{
     runtime::vm_runtime::Runtime,
     runtime::vm_runtime::Runtime,
     tx::Transaction,
     tx::Transaction,
     util::time::TimeKeeper,
     util::time::TimeKeeper,
-    validator::validation::validate_block,
+    validator::{pow::PoWModule, validation::validate_block},
     zk::VerifyingKey,
     zk::VerifyingKey,
     Error, Result,
     Error, Result,
 };
 };
@@ -51,31 +51,33 @@ pub async fn verify_genesis_block(
         return Err(Error::BlockAlreadyExists(block_hash))
         return Err(Error::BlockAlreadyExists(block_hash))
     }
     }
 
 
+    // Block height must be 0
+    if block.header.height != 0 {
+        return Err(Error::BlockIsInvalid(block_hash))
+    }
+
     // Block height must be the same as the time keeper verifying slot
     // Block height must be the same as the time keeper verifying slot
     if block.header.height != time_keeper.verifying_slot {
     if block.header.height != time_keeper.verifying_slot {
         return Err(Error::VerifyingSlotMissmatch())
         return Err(Error::VerifyingSlotMissmatch())
     }
     }
 
 
-    // Check extra stuff based on block version
-    if block.header.version > 0 {
-        // Check genesis slot exist
-        if block.slots.len() != 1 {
-            return Err(Error::BlockIsInvalid(block_hash))
-        }
+    // Check genesis slot exist
+    if block.slots.len() != 1 {
+        return Err(Error::BlockIsInvalid(block_hash))
+    }
 
 
-        // Retrieve genesis slot
-        let genesis_slot = block.slots.last().unwrap();
+    // Retrieve genesis slot
+    let genesis_slot = block.slots.last().unwrap();
 
 
-        // Genesis block slot total token must correspond to the total
-        // of all genesis transactions public inputs (genesis distribution).
-        if genesis_slot.total_tokens != genesis_txs_total {
-            return Err(Error::SlotIsInvalid(genesis_slot.id))
-        }
+    // Genesis block slot total token must correspond to the total
+    // of all genesis transactions public inputs (genesis distribution).
+    if genesis_slot.total_tokens != genesis_txs_total {
+        return Err(Error::SlotIsInvalid(genesis_slot.id))
+    }
 
 
-        // Verify there is no reward
-        if genesis_slot.reward != 0 {
-            return Err(Error::SlotIsInvalid(genesis_slot.id))
-        }
+    // Verify there is no reward
+    if genesis_slot.reward != 0 {
+        return Err(Error::SlotIsInvalid(genesis_slot.id))
     }
     }
 
 
     // Verify transactions vector contains at least one(producers) transaction
     // Verify transactions vector contains at least one(producers) transaction
@@ -110,6 +112,7 @@ pub async fn verify_genesis_block(
 pub async fn verify_block(
 pub async fn verify_block(
     overlay: &BlockchainOverlayPtr,
     overlay: &BlockchainOverlayPtr,
     time_keeper: &TimeKeeper,
     time_keeper: &TimeKeeper,
+    module: &PoWModule,
     block: &BlockInfo,
     block: &BlockInfo,
     previous: &BlockInfo,
     previous: &BlockInfo,
     expected_reward: u64,
     expected_reward: u64,
@@ -128,8 +131,13 @@ pub async fn verify_block(
         return Err(Error::VerifyingSlotMissmatch())
         return Err(Error::VerifyingSlotMissmatch())
     }
     }
 
 
+    // Block epoch must be the correct one, calculated by the time keeper configuration
+    if block.header.epoch != time_keeper.slot_epoch(block.header.height) {
+        return Err(Error::VerifyingSlotMissmatch())
+    }
+
     // Validate block, using its previous
     // Validate block, using its previous
-    validate_block(block, previous, expected_reward)?;
+    validate_block(block, previous, expected_reward, module)?;
 
 
     // Verify transactions vector contains at least one(producers) transaction
     // Verify transactions vector contains at least one(producers) transaction
     if block.txs.is_empty() {
     if block.txs.is_empty() {
@@ -139,7 +147,8 @@ pub async fn verify_block(
     // Validate proposal transaction if not in testing mode
     // Validate proposal transaction if not in testing mode
     if !testing_mode {
     if !testing_mode {
         let tx = block.txs.last().unwrap();
         let tx = block.txs.last().unwrap();
-        let public_key = verify_producer_transaction(overlay, time_keeper, tx).await?;
+        let public_key =
+            verify_producer_transaction(overlay, time_keeper, tx, block.header.version).await?;
         verify_producer_signature(block, &public_key)?;
         verify_producer_signature(block, &public_key)?;
     }
     }
 
 
@@ -176,22 +185,33 @@ pub async fn verify_producer_transaction(
     overlay: &BlockchainOverlayPtr,
     overlay: &BlockchainOverlayPtr,
     time_keeper: &TimeKeeper,
     time_keeper: &TimeKeeper,
     tx: &Transaction,
     tx: &Transaction,
+    block_version: u8,
 ) -> Result<PublicKey> {
 ) -> Result<PublicKey> {
     let tx_hash = tx.hash()?;
     let tx_hash = tx.hash()?;
     debug!(target: "validator::verification::verify_producer_transaction", "Validating proposal transaction {}", tx_hash);
     debug!(target: "validator::verification::verify_producer_transaction", "Validating proposal transaction {}", tx_hash);
 
 
-    // Transaction must contain a single call, either Money::PoWReward(0x08) or Consensus::Proposal(0x02)
-    if tx.calls.len() != 1 ||
-        (tx.calls[0].contract_id != *MONEY_CONTRACT_ID &&
-            tx.calls[0].contract_id != *CONSENSUS_CONTRACT_ID) ||
-        (tx.calls[0].contract_id == *MONEY_CONTRACT_ID && tx.calls[0].data[0] != 0x08) ||
-        (tx.calls[0].contract_id == *CONSENSUS_CONTRACT_ID && tx.calls[0].data[0] != 0x02)
-    {
-        error!(target: "validator::verification::verify_producer_transaction", "Proposal transaction is malformed");
+    // Transaction must contain a single call
+    if tx.calls.len() != 1 {
         return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
         return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
     }
     }
 
 
+    // Verify call based on version
     let call = &tx.calls[0];
     let call = &tx.calls[0];
+    match block_version {
+        1 => {
+            // Version 1 blocks must contain a Money::PoWReward(0x08) call
+            if call.contract_id != *MONEY_CONTRACT_ID || call.data[0] != 0x08 {
+                return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
+            }
+        }
+        2 => {
+            // Version 1 blocks must contain a Consensus::Proposal(0x02) call
+            if call.contract_id != *CONSENSUS_CONTRACT_ID || call.data[0] != 0x02 {
+                return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
+            }
+        }
+        _ => return Err(Error::BlockVersionIsInvalid(block_version)),
+    }
 
 
     // Map of ZK proof verifying keys for the current transaction
     // Map of ZK proof verifying keys for the current transaction
     let mut verifying_keys: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
     let mut verifying_keys: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();

+ 46 - 25
tests/blockchain.rs

@@ -20,47 +20,63 @@ use darkfi::{
     blockchain::{BlockInfo, Blockchain, BlockchainOverlay, Header},
     blockchain::{BlockInfo, Blockchain, BlockchainOverlay, Header},
     validator::{
     validator::{
         pid::slot_pid_output,
         pid::slot_pid_output,
+        pow::PoWModule,
         validation::{validate_block, validate_blockchain},
         validation::{validate_block, validate_blockchain},
     },
     },
     Error, Result,
     Error, Result,
 };
 };
 use darkfi_sdk::{
 use darkfi_sdk::{
-    blockchain::{expected_reward, PidOutput, PreviousSlot, Slot},
+    blockchain::{expected_reward, PidOutput, PreviousSlot, Slot, POS_START},
     pasta::{group::ff::Field, pallas},
     pasta::{group::ff::Field, pallas},
 };
 };
 
 
+const POW_TARGET: Option<usize> = Some(10);
+
+struct Node {
+    blockchain: Blockchain,
+    module: PoWModule,
+}
+
+impl Node {
+    fn new() -> Result<Self> {
+        let blockchain = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
+        let module = PoWModule::new(blockchain.clone(), None, POW_TARGET);
+        Ok(Self { blockchain, module })
+    }
+}
+
 struct Harness {
 struct Harness {
-    pub alice: Blockchain,
-    pub bob: Blockchain,
+    pub alice: Node,
+    pub bob: Node,
 }
 }
 
 
 impl Harness {
 impl Harness {
     fn new() -> Result<Self> {
     fn new() -> Result<Self> {
-        let alice = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
-        let bob = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
+        let alice = Node::new()?;
+        let bob = Node::new()?;
         Ok(Self { alice, bob })
         Ok(Self { alice, bob })
     }
     }
 
 
     fn is_empty(&self) {
     fn is_empty(&self) {
-        assert!(self.alice.is_empty());
-        assert!(self.bob.is_empty());
+        assert!(self.alice.blockchain.is_empty());
+        assert!(self.bob.blockchain.is_empty());
     }
     }
 
 
     fn validate_chains(&self) -> Result<()> {
     fn validate_chains(&self) -> Result<()> {
-        validate_blockchain(&self.alice)?;
-        validate_blockchain(&self.bob)?;
+        validate_blockchain(&self.alice.blockchain, POW_TARGET)?;
+        validate_blockchain(&self.bob.blockchain, POW_TARGET)?;
 
 
-        assert_eq!(self.alice.len(), self.bob.len());
+        assert_eq!(self.alice.blockchain.len(), self.bob.blockchain.len());
 
 
         Ok(())
         Ok(())
     }
     }
 
 
-    fn generate_next_block(&self, previous: &BlockInfo) -> Result<BlockInfo> {
+    fn generate_next_pos_block(&self, previous: &BlockInfo) -> Result<BlockInfo> {
         let previous_hash = previous.hash()?;
         let previous_hash = previous.hash()?;
 
 
         // Generate slot
         // Generate slot
         let previous_slot = previous.slots.last().unwrap();
         let previous_slot = previous.slots.last().unwrap();
-        let id = previous_slot.id + 1;
+        let id = if previous_slot.id < POS_START { POS_START } else { previous_slot.id + 1 };
         let producers = 1;
         let producers = 1;
         let previous_slot_info = PreviousSlot::new(
         let previous_slot_info = PreviousSlot::new(
             producers,
             producers,
@@ -94,17 +110,17 @@ impl Harness {
         Ok(block)
         Ok(block)
     }
     }
 
 
-    fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
-        self.add_blocks_to_chain(&self.alice, blocks)?;
-        self.add_blocks_to_chain(&self.bob, blocks)?;
+    fn add_pos_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
+        Self::add_pos_blocks_to_chain(&mut self.alice, blocks)?;
+        Self::add_pos_blocks_to_chain(&mut self.bob, blocks)?;
 
 
         Ok(())
         Ok(())
     }
     }
 
 
     // This is what the validator will execute when it receives a block.
     // This is what the validator will execute when it receives a block.
-    fn add_blocks_to_chain(&self, blockchain: &Blockchain, blocks: &[BlockInfo]) -> Result<()> {
+    fn add_pos_blocks_to_chain(node: &mut Node, blocks: &[BlockInfo]) -> Result<()> {
         // Create overlay
         // Create overlay
-        let blockchain_overlay = BlockchainOverlay::new(blockchain)?;
+        let blockchain_overlay = BlockchainOverlay::new(&node.blockchain)?;
         let lock = blockchain_overlay.lock().unwrap();
         let lock = blockchain_overlay.lock().unwrap();
 
 
         // When we insert genesis, chain is empty
         // When we insert genesis, chain is empty
@@ -123,7 +139,12 @@ impl Harness {
                 let expected_reward = expected_reward(block.header.height);
                 let expected_reward = expected_reward(block.header.height);
 
 
                 // Validate block
                 // Validate block
-                validate_block(block, &p, expected_reward)?;
+                validate_block(block, &p, expected_reward, &node.module)?;
+
+                // Update PoW module
+                if block.header.version == 1 {
+                    node.module.append(block.header.timestamp.0, &node.module.next_difficulty());
+                }
             }
             }
 
 
             // Insert block
             // Insert block
@@ -141,30 +162,30 @@ impl Harness {
 }
 }
 
 
 #[test]
 #[test]
-fn blockchain_add_blocks() -> Result<()> {
+fn blockchain_add_pos_blocks() -> Result<()> {
     smol::block_on(async {
     smol::block_on(async {
         // Initialize harness
         // Initialize harness
-        let th = Harness::new()?;
+        let mut th = Harness::new()?;
 
 
         // Check that nothing exists
         // Check that nothing exists
         th.is_empty();
         th.is_empty();
 
 
-        // We generate some blocks
+        // We generate some pos blocks
         let mut blocks = vec![];
         let mut blocks = vec![];
 
 
         let genesis_block = BlockInfo::default();
         let genesis_block = BlockInfo::default();
         blocks.push(genesis_block.clone());
         blocks.push(genesis_block.clone());
 
 
-        let block = th.generate_next_block(&genesis_block)?;
+        let block = th.generate_next_pos_block(&genesis_block)?;
         blocks.push(block.clone());
         blocks.push(block.clone());
 
 
-        let block = th.generate_next_block(&block)?;
+        let block = th.generate_next_pos_block(&block)?;
         blocks.push(block.clone());
         blocks.push(block.clone());
 
 
-        let block = th.generate_next_block(&block)?;
+        let block = th.generate_next_pos_block(&block)?;
         blocks.push(block.clone());
         blocks.push(block.clone());
 
 
-        th.add_blocks(&blocks)?;
+        th.add_pos_blocks(&blocks)?;
 
 
         // Validate chains
         // Validate chains
         th.validate_chains()?;
         th.validate_chains()?;