Parcourir la source

validator: cleaned up verification and validations methods and merged the two files

skoupidi il y a 2 ans
Parent
commit
4c45c8d592

+ 0 - 3
bin/darkfid/src/tests/harness.rs

@@ -135,20 +135,17 @@ 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;
         let bob = &self.bob.validator;
 
         alice
             .validate_blockchain(
-                genesis_txs_total,
                 vec![],
                 self.config.pow_target,
                 self.config.pow_fixed_difficulty.clone(),
             )
             .await?;
         bob.validate_blockchain(
-            genesis_txs_total,
             vec![],
             self.config.pow_target,
             self.config.pow_fixed_difficulty.clone(),

+ 1 - 4
bin/darkfid/src/tests/mod.rs

@@ -70,12 +70,9 @@ async fn sync_pos_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     let charlie =
         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;
     let charlie = &charlie.validator;
-    charlie
-        .validate_blockchain(genesis_txs_total, vec![], pow_target, pow_fixed_difficulty)
-        .await?;
+    charlie.validate_blockchain(vec![], pow_target, pow_fixed_difficulty).await?;
     assert_eq!(alice.blockchain.len(), charlie.blockchain.len());
     assert_eq!(alice.blockchain.slots.len(), charlie.blockchain.slots.len());
 

+ 3 - 3
src/contract/money/src/client/fee_v1.rs

@@ -21,7 +21,6 @@ use std::collections::HashMap;
 use darkfi::{
     blockchain::BlockchainOverlayPtr,
     tx::TransactionBuilder,
-    util::time::TimeKeeper,
     validator::verification::verify_transaction,
     zk::{halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
     zkas::ZkBinary,
@@ -65,7 +64,7 @@ pub async fn append_fee_call(
     fee_pk: &ProvingKey,
     tx_builder: &mut TransactionBuilder,
     overlay: &BlockchainOverlayPtr,
-    time_keeper: &TimeKeeper,
+    verifying_block_height: u64,
     verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
 ) -> Result<(MoneyFeeParamsV1, FeeCallSecrets)> {
     assert!(coin.note.value > 0);
@@ -76,7 +75,8 @@ pub async fn append_fee_call(
     // First we will verify the fee-less transaction to see how much gas
     // it uses for execution and verification.
     let tx = tx_builder.build()?;
-    let gas_used = verify_transaction(overlay, time_keeper, &tx, verifying_keys, false).await?;
+    let gas_used =
+        verify_transaction(overlay, verifying_block_height, &tx, verifying_keys, false).await?;
 
     // TODO: We could actually take a set of coins and then find one with
     //       enough value, instead of expecting one. It depends, the API

+ 8 - 32
src/validator/consensus.rs

@@ -154,7 +154,8 @@ impl Consensus {
         };
 
         // Grab forks' unproposed transactions
-        let mut unproposed_txs = fork.unproposed_txs(&self.blockchain, &time_keeper).await?;
+        let mut unproposed_txs =
+            fork.unproposed_txs(&self.blockchain, time_keeper.verifying_block_height).await?;
         unproposed_txs.push(producer_tx);
 
         // Grab forks' last block proposal(previous)
@@ -305,30 +306,10 @@ impl Consensus {
         // Retrieve last block
         let mut previous = &fork.overlay.lock().unwrap().last_block()?;
 
-        // Create a time keeper to validate each proposal block
-        let mut time_keeper = self.time_keeper.clone();
-
         // Validate and insert each block
         for block in blocks {
-            // Use block slot in time keeper
-            time_keeper.verifying_block_height = block.header.height;
-
-            // Retrieve expected reward
-            let expected_reward = expected_reward(time_keeper.verifying_block_height);
-
             // Verify block
-            if verify_block(
-                &fork.overlay,
-                &time_keeper,
-                &fork.module,
-                block,
-                previous,
-                expected_reward,
-                self.pos_testing_mode,
-            )
-            .await
-            .is_err()
-            {
+            if verify_block(&fork.overlay, &fork.module, block, previous).await.is_err() {
                 error!(target: "validator::consensus::find_extended_fork_overlay", "Erroneous block found in set");
                 fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
@@ -344,14 +325,7 @@ impl Consensus {
         }
 
         // Rebuilt fork hot/live slots
-        if proposal.block.header.height < POS_START {
-            fork.generate_pow_slot()?;
-        } else {
-            let id = time_keeper.verifying_block_height;
-            let (producers, last_hashes, second_to_last_hashes) =
-                previous_slot_info(&forks, id - 1)?;
-            fork.generate_pos_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
-        }
+        fork.generate_pow_slot()?;
 
         // Drop forks lock
         drop(forks);
@@ -498,7 +472,7 @@ impl Fork {
     pub async fn unproposed_txs(
         &self,
         blockchain: &Blockchain,
-        time_keeper: &TimeKeeper,
+        verifying_block_height: u64,
     ) -> Result<Vec<Transaction>> {
         // Retrieve all mempool transactions
         let mut unproposed_txs: Vec<Transaction> = blockchain
@@ -526,7 +500,9 @@ impl Fork {
         let overlay = self.overlay.lock().unwrap().full_clone()?;
 
         // Verify transactions
-        if let Err(e) = verify_transactions(&overlay, time_keeper, &unproposed_txs, false).await {
+        if let Err(e) =
+            verify_transactions(&overlay, verifying_block_height, &unproposed_txs, false).await
+        {
             match e {
                 crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(erroneous_txs)) => {
                     unproposed_txs.retain(|x| !erroneous_txs.contains(x))

+ 36 - 93
src/validator/mod.rs

@@ -18,10 +18,7 @@
 
 use std::sync::Arc;
 
-use darkfi_sdk::{
-    blockchain::{expected_reward, Slot},
-    crypto::PublicKey,
-};
+use darkfi_sdk::{blockchain::Slot, crypto::PublicKey};
 use darkfi_serial::serialize_async;
 use log::{debug, error, info, warn};
 use num_bigint::BigUint;
@@ -59,9 +56,6 @@ use verification::{
 /// Fee calculation helpers
 pub mod fees;
 
-/// Validation functions
-pub mod validation;
-
 /// Helper utilities
 pub mod utils;
 use utils::deploy_native_contracts;
@@ -158,13 +152,7 @@ impl Validator {
         // Add genesis block if blockchain is empty
         if blockchain.genesis().is_err() {
             info!(target: "validator::new", "Appending genesis block");
-            verify_genesis_block(
-                &overlay,
-                &config.time_keeper,
-                &config.genesis_block,
-                config.genesis_txs_total,
-            )
-            .await?;
+            verify_genesis_block(&overlay, &config.genesis_block).await?;
         };
 
         // Write the changes to the actual chain db
@@ -225,7 +213,9 @@ impl Validator {
             let overlay = fork.overlay.lock().unwrap().full_clone()?;
 
             // Verify transaction
-            match verify_transactions(&overlay, &time_keeper, &tx_vec, false).await {
+            match verify_transactions(&overlay, time_keeper.verifying_block_height, &tx_vec, false)
+                .await
+            {
                 Ok(_) => {}
                 Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => continue,
                 Err(e) => return Err(e),
@@ -240,7 +230,9 @@ impl Validator {
         // Verify transaction against canonical state
         let overlay = BlockchainOverlay::new(&self.blockchain)?;
         let mut erroneous_txs = vec![];
-        match verify_transactions(&overlay, &time_keeper, &tx_vec, false).await {
+        match verify_transactions(&overlay, time_keeper.verifying_block_height, &tx_vec, false)
+            .await
+        {
             Ok(_) => valid = true,
             Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(etx))) => {
                 erroneous_txs = etx
@@ -293,7 +285,14 @@ impl Validator {
                 let overlay = fork.overlay.lock().unwrap().full_clone()?;
 
                 // Verify transaction
-                match verify_transactions(&overlay, &time_keeper, &tx_vec, false).await {
+                match verify_transactions(
+                    &overlay,
+                    time_keeper.verifying_block_height,
+                    &tx_vec,
+                    false,
+                )
+                .await
+                {
                     Ok(_) => {
                         valid = true;
                         continue
@@ -309,7 +308,9 @@ impl Validator {
             // Verify transaction against canonical state
             let overlay = BlockchainOverlay::new(&self.blockchain)?;
 
-            match verify_transactions(&overlay, &time_keeper, &tx_vec, false).await {
+            match verify_transactions(&overlay, time_keeper.verifying_block_height, &tx_vec, false)
+                .await
+            {
                 Ok(_) => valid = true,
                 Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => {}
                 Err(e) => return Err(e),
@@ -402,8 +403,7 @@ impl Validator {
         // Retrieve last block
         let mut previous = &overlay.lock().unwrap().last_block()?;
 
-        // Create a time keeper and a PoW module to validate each block
-        let mut time_keeper = self.consensus.time_keeper.clone();
+        // Grab current PoW module to validate each block
         let mut module = self.consensus.module.read().await.clone();
 
         // Keep track of all blocks transactions to remove them from pending txs store
@@ -411,44 +411,23 @@ impl Validator {
 
         // Validate and insert each block
         for block in blocks {
-            // Use block height in time keeper
-            time_keeper.verifying_block_height = block.header.height;
-
-            // Retrieve expected reward
-            let expected_reward = expected_reward(time_keeper.verifying_block_height);
-
             // Verify block
-            if verify_block(
-                &overlay,
-                &time_keeper,
-                &module,
-                block,
-                previous,
-                expected_reward,
-                self.pos_testing_mode,
-            )
-            .await
-            .is_err()
-            {
+            if verify_block(&overlay, &module, block, previous).await.is_err() {
                 error!(target: "validator::add_blocks", "Erroneous block found in set");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
 
-            // Update PoW module
-            if block.header.version == 1 {
-                // Generate block difficulty
-                let difficulty = module.next_difficulty()?;
-                let cummulative_difficulty =
-                    module.cummulative_difficulty.clone() + difficulty.clone();
-                let block_difficulty = BlockDifficulty::new(
-                    block.header.height,
-                    block.header.timestamp.0,
-                    difficulty,
-                    cummulative_difficulty,
-                );
-                module.append_difficulty(&overlay, block_difficulty)?;
-            }
+            // Generate block difficulty
+            let difficulty = module.next_difficulty()?;
+            let cummulative_difficulty = module.cummulative_difficulty.clone() + difficulty.clone();
+            let block_difficulty = BlockDifficulty::new(
+                block.header.height,
+                block.header.timestamp.0,
+                difficulty,
+                cummulative_difficulty,
+            );
+            module.append_difficulty(&overlay, block_difficulty)?;
 
             // Store block transactions
             for tx in &block.txs {
@@ -485,17 +464,8 @@ impl Validator {
         debug!(target: "validator::add_transactions", "Instantiating BlockchainOverlay");
         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(
-            current_time_keeper.genesis_ts,
-            current_time_keeper.epoch_length,
-            current_time_keeper.slot_time,
-            verifying_block_height,
-        );
-
         // Verify all transactions and get erroneous ones
-        let e = verify_transactions(&overlay, &time_keeper, txs, self.verify_fees).await;
+        let e = verify_transactions(&overlay, verifying_block_height, txs, self.verify_fees).await;
         let lock = overlay.lock().unwrap();
         let mut overlay = lock.overlay.lock().unwrap();
 
@@ -538,18 +508,9 @@ impl Validator {
         debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
         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(
-            current_time_keeper.genesis_ts,
-            current_time_keeper.epoch_length,
-            current_time_keeper.slot_time,
-            verifying_block_height,
-        );
-
         // Verify transaction
         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, verifying_block_height, tx).await {
             warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {}", e);
             erroneous_txs.push(tx.clone());
         }
@@ -578,7 +539,6 @@ impl Validator {
     /// Be careful as this will try to load everything in memory.
     pub async fn validate_blockchain(
         &self,
-        genesis_txs_total: u64,
         faucet_pubkeys: Vec<PublicKey>,
         pow_target: usize,
         pow_fixed_difficulty: Option<BigUint>,
@@ -599,7 +559,7 @@ impl Validator {
         let mut previous = &blocks[0];
 
         // Create a time keeper and a PoW module to validate each block
-        let mut time_keeper = self.consensus.time_keeper.clone();
+        let time_keeper = self.consensus.time_keeper.clone();
         let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty)?;
 
         // Deploy native wasm contracts
@@ -607,29 +567,12 @@ impl Validator {
             .await?;
 
         // Validate genesis block
-        verify_genesis_block(&overlay, &time_keeper, previous, genesis_txs_total).await?;
+        verify_genesis_block(&overlay, previous).await?;
 
         // Validate and insert each block
         for block in &blocks[1..] {
-            // Use block height in time keeper
-            time_keeper.verifying_block_height = block.header.height;
-
-            // Retrieve expected reward
-            let expected_reward = expected_reward(time_keeper.verifying_block_height);
-
             // Verify block
-            if verify_block(
-                &overlay,
-                &time_keeper,
-                &module,
-                block,
-                previous,
-                expected_reward,
-                self.pos_testing_mode,
-            )
-            .await
-            .is_err()
-            {
+            if verify_block(&overlay, &module, block, previous).await.is_err() {
                 error!(target: "validator::validate_blockchain", "Erroneous block found in set");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
                 return Err(Error::BlockIsInvalid(block.hash()?.to_string()))

+ 0 - 370
src/validator/validation.rs

@@ -1,370 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi_sdk::{
-    blockchain::{block_epoch, block_version, expected_reward, Slot},
-    pasta::{group::ff::Field, pallas},
-};
-use num_bigint::BigUint;
-
-use crate::{
-    blockchain::{BlockInfo, Blockchain},
-    validator::{pid::slot_pid_output, pow::PoWModule},
-    Error, Result,
-};
-
-/// 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. Block epoch corresponds to the one for its height
-///     3. Parent hash is equal to the hash of the previous block
-///     4. Block height increments previous block height by 1
-///     5. Timestamp is valid based on PoWModule validation
-///     6. Block hash is valid based on PoWModule validation
-///     7. Slots vector contains a single valid slot
-///     8. Block height is the same as the slots vector last slot id
-/// Additional validity rules can be applied.
-pub fn validate_pow_block(
-    block: &BlockInfo,
-    previous: &BlockInfo,
-    expected_reward: u64,
-    module: &PoWModule,
-) -> Result<()> {
-    let error = Err(Error::BlockIsInvalid(block.hash()?.to_string()));
-
-    // Check block version (1)
-    if block.header.version != 1 {
-        return error
-    }
-
-    // Check block epoch (2)
-    if block.header.epoch != block_epoch(block.header.height) {
-        return error
-    }
-
-    // Check previous hash (3)
-    let previous_hash = previous.hash()?;
-    if block.header.previous != previous_hash {
-        return error
-    }
-
-    // Check heights are incremental (4)
-    if block.header.height != previous.header.height + 1 {
-        return error
-    }
-
-    // Check timestamp validity (5)
-    if !module.verify_timestamp_by_median(block.header.timestamp.0) {
-        return error
-    }
-
-    // Check block hash corresponds to next one (6)
-    module.verify_block_hash(block)?;
-
-    // Verify slots vector contains single slot (7)
-    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,
-        &pallas::Base::from(previous.header.nonce),
-        expected_reward,
-    )?;
-
-    // Check block height is the last slot id (8)
-    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's 'previous error' value matches the PID error of the previous slot
-///     6. Slot previous has only 1 producer (the miner)
-///     7. PID output for this slot is correct (zero)
-///     8. Slot last nonce is the expected one
-///     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,
-    last_nonce: &pallas::Base,
-    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 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 nonce is the expected one
-    if &slot.last_nonce != last_nonce {
-        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. Block epoch corresponds to the one for its height
-///     3. Parent hash is equal to the hash of the previous block
-///     4. Timestamp increments previous block timestamp
-///     5. Slot increments previous block slot
-///     6. Slots vector is not empty and all its slots are valid
-///     7. 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 block epoch (2)
-    if block.header.epoch != block_epoch(block.header.height) {
-        return error
-    }
-
-    // Check previous hash (3)
-    let previous_hash = previous.hash()?;
-    if block.header.previous != previous_hash {
-        return error
-    }
-
-    // Check timestamps are incremental (4)
-    if block.header.timestamp <= previous.header.timestamp {
-        return error
-    }
-
-    // Check heights are incremental (5)
-    if block.header.height <= previous.header.height {
-        return error
-    }
-
-    // Verify slots (6)
-    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_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 (7)
-    if last_slot.id != block.header.height {
-        return error
-    }
-
-    Ok(())
-}
-
-/// A PoS slot is considered valid when the following rules apply:
-///     1. Id increments previous slot id
-///     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's 'previous error' value matches the PID error of the previous slot
-///     6. PID output for this slot is correct
-///     7. Slot last nonce(eta) is the expected one
-///     8. Slot reward value is the expected one
-/// Additional validity rules can be applied.
-pub fn validate_pos_slot(
-    slot: &Slot,
-    previous: &Slot,
-    previous_block_hash: &blake3::Hash,
-    previous_block_sequence: &blake3::Hash,
-    last_nonce: &pallas::Base,
-    expected_reward: u64,
-) -> Result<()> {
-    let error = Err(Error::SlotIsInvalid(slot.id));
-
-    // Check slots are incremental (1)
-    if slot.id <= previous.id {
-        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 PID output for this slot (6)
-    if (slot.pid.f, slot.pid.error, slot.pid.sigma1, slot.pid.sigma2) !=
-        slot_pid_output(previous, slot.previous.producers)
-    {
-        return error
-    }
-
-    // Check nonce (eta) is the expected one (7)
-    if &slot.last_nonce != last_nonce {
-        return error
-    }
-
-    // Check reward is the expected one (8)
-    if slot.reward != expected_reward {
-        return error
-    }
-
-    Ok(())
-}
-
-/// A blockchain is considered valid, when every block is valid,
-/// based on validate_block checks.
-/// Be careful as this will try to load everything in memory.
-pub fn validate_blockchain(
-    blockchain: &Blockchain,
-    pow_target: usize,
-    pow_fixed_difficulty: Option<BigUint>,
-) -> Result<()> {
-    // Generate a PoW module
-    let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty)?;
-    // We use block order store here so we have all blocks in order
-    let blocks = blockchain.order.get_all()?;
-    for (index, block) in blocks[1..].iter().enumerate() {
-        let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
-        let expected_reward = expected_reward(full_blocks[1].header.height);
-        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(())
-}

+ 93 - 214
src/validator/verification.rs

@@ -19,7 +19,7 @@
 use std::collections::HashMap;
 
 use darkfi_sdk::{
-    blockchain::{block_version, expected_reward},
+    blockchain::{block_epoch, block_version},
     crypto::{
         schnorr::SchnorrPublic, ContractId, PublicKey, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
     },
@@ -31,31 +31,25 @@ use darkfi_serial::{
     deserialize_async, serialize_async, AsyncDecodable, AsyncEncodable, AsyncWriteExt, WriteExt,
 };
 use log::{debug, error, warn};
+use num_bigint::BigUint;
 use smol::io::Cursor;
 
 use crate::{
-    blockchain::{BlockInfo, BlockchainOverlayPtr},
+    blockchain::{BlockInfo, Blockchain, BlockchainOverlayPtr},
     error::TxVerifyFailed,
     runtime::vm_runtime::Runtime,
     tx::{Transaction, MAX_TX_CALLS, MIN_TX_CALLS},
-    util::time::TimeKeeper,
     validator::{
         consensus::{Consensus, Fork, Proposal, TXS_CAP},
         fees::{circuit_gas_use, PALLAS_SCHNORR_SIGNATURE_FEE},
         pow::PoWModule,
-        validation::validate_block,
     },
     zk::VerifyingKey,
     Error, Result,
 };
 
 /// Verify given genesis [`BlockInfo`], and apply it to the provided overlay
-pub async fn verify_genesis_block(
-    overlay: &BlockchainOverlayPtr,
-    time_keeper: &TimeKeeper,
-    block: &BlockInfo,
-    genesis_txs_total: u64,
-) -> Result<()> {
+pub async fn verify_genesis_block(overlay: &BlockchainOverlayPtr, block: &BlockInfo) -> Result<()> {
     let block_hash = block.hash()?.to_string();
     debug!(target: "validator::verification::verify_genesis_block", "Validating genesis block {}", block_hash);
 
@@ -69,28 +63,14 @@ pub async fn verify_genesis_block(
         return Err(Error::BlockIsInvalid(block_hash))
     }
 
-    // Block height must be the same as the time keeper verifying slot
-    if block.header.height != time_keeper.verifying_block_height {
-        return Err(Error::VerifyingSlotMissmatch())
-    }
-
-    // Check genesis slot exist
-    if block.slots.len() != 1 {
+    // Block epoch must be correct
+    if block.header.epoch != block_epoch(block.header.height) {
         return Err(Error::BlockIsInvalid(block_hash))
     }
 
-    // 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))
-    }
-
-    // Verify there is no reward
-    if genesis_slot.reward != 0 {
-        return Err(Error::SlotIsInvalid(genesis_slot.id))
+    // Block version must be correct
+    if block.header.version != block_version(block.header.height) {
+        return Err(Error::BlockIsInvalid(block_hash))
     }
 
     // Verify transactions vector contains at least one(producers) transaction
@@ -98,10 +78,6 @@ pub async fn verify_genesis_block(
         return Err(Error::BlockContainsNoTransactions(block_hash))
     }
 
-    // Insert genesis slot so transactions can be validated against.
-    // Since an overlay is used, original database is not affected.
-    overlay.lock().unwrap().slots.insert(&[genesis_slot.clone()])?;
-
     // Genesis transaction must be the Transaction::default() one(empty)
     if block.txs[0] != Transaction::default() {
         error!(target: "validator::verification::verify_genesis_block", "Genesis proposal transaction is not default one");
@@ -110,7 +86,7 @@ pub async fn verify_genesis_block(
 
     // Verify transactions, exluding producer(first) one
     let txs = &block.txs[1..];
-    if let Err(e) = verify_transactions(overlay, time_keeper, txs, false).await {
+    if let Err(e) = verify_transactions(overlay, block.header.height, txs, false).await {
         warn!(
             target: "validator::verification::verify_genesis_block",
             "[VALIDATOR] Erroneous transactions found in set",
@@ -126,15 +102,76 @@ pub async fn verify_genesis_block(
     Ok(())
 }
 
+/// A block is considered valid when the following rules apply:
+///     1. Block version is correct for its height
+///     2. Block epoch corresponds to the one for its height
+///     3. Parent hash is equal to the hash of the previous block
+///     4. Block height increments previous block height by 1
+///     5. Timestamp is valid based on PoWModule validation
+///     6. Block hash is valid based on PoWModule validation
+/// Additional validity rules can be applied.
+pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, module: &PoWModule) -> Result<()> {
+    // Check block version (1)
+    if block.header.version != block_version(block.header.height) {
+        return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
+    }
+
+    // Check block epoch (2)
+    if block.header.epoch != block_epoch(block.header.height) {
+        return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
+    }
+
+    // Check previous hash (3)
+    let previous_hash = previous.hash()?;
+    if block.header.previous != previous_hash {
+        return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
+    }
+
+    // Check heights are incremental (4)
+    if block.header.height != previous.header.height + 1 {
+        return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
+    }
+
+    // Check timestamp validity (5)
+    if !module.verify_timestamp_by_median(block.header.timestamp.0) {
+        return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
+    }
+
+    // Check block hash corresponds to next one (6)
+    module.verify_block_hash(block)?;
+
+    Ok(())
+}
+
+/// A blockchain is considered valid, when every block is valid,
+/// based on validate_block checks.
+/// Be careful as this will try to load everything in memory.
+pub fn validate_blockchain(
+    blockchain: &Blockchain,
+    pow_target: usize,
+    pow_fixed_difficulty: Option<BigUint>,
+) -> Result<()> {
+    // Generate a PoW module
+    let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty)?;
+    // We use block order store here so we have all blocks in order
+    let blocks = blockchain.order.get_all()?;
+    for (index, block) in blocks[1..].iter().enumerate() {
+        let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
+        let full_block = &full_blocks[1];
+        validate_block(full_block, &full_blocks[0], &module)?;
+        // Update PoW module
+        module.append(full_block.header.timestamp.0, &module.next_difficulty()?);
+    }
+
+    Ok(())
+}
+
 /// Verify given [`BlockInfo`], and apply it to the provided overlay
 pub async fn verify_block(
     overlay: &BlockchainOverlayPtr,
-    time_keeper: &TimeKeeper,
     module: &PoWModule,
     block: &BlockInfo,
     previous: &BlockInfo,
-    expected_reward: u64,
-    pos_testing_mode: bool,
 ) -> Result<()> {
     let block_hash = block.hash()?.to_string();
     debug!(target: "validator::verification::verify_block", "Validating block {}", block_hash);
@@ -144,39 +181,22 @@ pub async fn verify_block(
         return Err(Error::BlockAlreadyExists(block_hash))
     }
 
-    // Block height must be the same as the time keeper verifying slot
-    if block.header.height != time_keeper.verifying_block_height {
-        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(block, previous, expected_reward, module)?;
+    validate_block(block, previous, module)?;
 
     // Verify transactions vector contains at least one(producers) transaction
     if block.txs.is_empty() {
         return Err(Error::BlockContainsNoTransactions(block_hash))
     }
 
-    // Insert last block slot so transactions can be validated against.
-    // Rest (empty) slots will be inserted along with the block.
-    // Since an overlay is used, original database is not affected.
-    overlay.lock().unwrap().slots.insert(&[block.slots.last().unwrap().clone()])?;
-
     // Verify proposal transaction.
-    // For PoS blocks(version 2) verify if not in PoS testing mode.
-    if block.header.version != 2 || !pos_testing_mode {
-        let public_key = verify_producer_transaction(overlay, time_keeper, &block.txs[0]).await?;
-        verify_producer_signature(block, &public_key)?;
-    }
+    let public_key =
+        verify_producer_transaction(overlay, block.header.height, &block.txs[0]).await?;
+    verify_producer_signature(block, &public_key)?;
 
     // Verify transactions, exluding producer(first) one
     let txs = &block.txs[1..];
-    let e = verify_transactions(overlay, time_keeper, txs, false).await;
+    let e = verify_transactions(overlay, block.header.height, txs, false).await;
     if let Err(e) = e {
         warn!(
             target: "validator::verification::verify_block",
@@ -208,7 +228,7 @@ pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> R
 /// and apply it to the provided overlay. Returns transaction signature public key.
 pub async fn verify_producer_transaction(
     overlay: &BlockchainOverlayPtr,
-    time_keeper: &TimeKeeper,
+    verifying_block_height: u64,
     tx: &Transaction,
 ) -> Result<PublicKey> {
     let tx_hash = tx.hash()?;
@@ -247,12 +267,8 @@ pub async fn verify_producer_transaction(
     debug!(target: "validator::verification::verify_producer_transaction", "Instantiating WASM runtime");
     let wasm = overlay.lock().unwrap().wasm_bincode.get(call.data.contract_id)?;
 
-    let mut runtime = Runtime::new(
-        &wasm,
-        overlay.clone(),
-        call.data.contract_id,
-        time_keeper.verifying_block_height,
-    )?;
+    let mut runtime =
+        Runtime::new(&wasm, overlay.clone(), call.data.contract_id, verifying_block_height)?;
 
     debug!(target: "validator::verification::verify_producer_transaction", "Executing \"metadata\" call");
     let metadata = runtime.metadata(&payload)?;
@@ -337,7 +353,7 @@ pub async fn verify_producer_transaction(
 /// and apply it to the provided overlay.
 pub async fn verify_transaction(
     overlay: &BlockchainOverlayPtr,
-    time_keeper: &TimeKeeper,
+    verifying_block_height: u64,
     tx: &Transaction,
     verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
     verify_fee: bool,
@@ -408,12 +424,8 @@ pub async fn verify_transaction(
         debug!(target: "validator::verification::verify_transaction", "Instantiating WASM runtime");
         let wasm = overlay.lock().unwrap().wasm_bincode.get(call.data.contract_id)?;
 
-        let mut runtime = Runtime::new(
-            &wasm,
-            overlay.clone(),
-            call.data.contract_id,
-            time_keeper.verifying_block_height,
-        )?;
+        let mut runtime =
+            Runtime::new(&wasm, overlay.clone(), call.data.contract_id, verifying_block_height)?;
 
         debug!(target: "validator::verification::verify_transaction", "Executing \"metadata\" call");
         let metadata = runtime.metadata(&payload)?;
@@ -485,7 +497,7 @@ pub async fn verify_transaction(
                 &deploy_params.wasm_bincode,
                 overlay.clone(),
                 deploy_cid,
-                time_keeper.verifying_block_height,
+                verifying_block_height,
             )?;
 
             deploy_runtime.deploy(&deploy_params.ix)?;
@@ -575,7 +587,7 @@ pub async fn verify_transaction(
 /// all the transactions.
 pub async fn verify_transactions(
     overlay: &BlockchainOverlayPtr,
-    time_keeper: &TimeKeeper,
+    verifying_block_height: u64,
     txs: &[Transaction],
     verify_fees: bool,
 ) -> Result<u64> {
@@ -600,7 +612,7 @@ pub async fn verify_transactions(
     // Iterate over transactions and attempt to verify them
     for tx in txs {
         overlay.lock().unwrap().checkpoint();
-        match verify_transaction(overlay, time_keeper, tx, &mut vks, verify_fees).await {
+        match verify_transaction(overlay, verifying_block_height, tx, &mut vks, verify_fees).await {
             Ok(gas) => gas_used += gas,
             Err(e) => {
                 warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
@@ -623,28 +635,13 @@ pub async fn verify_transactions(
     }
 }
 
-/// Verify given [`Proposal`] against provided consensus state
-pub async fn verify_proposal(
-    consensus: &Consensus,
-    proposal: &Proposal,
-) -> Result<(Fork, Option<usize>)> {
-    // TODO: verify proposal validations work as expected on versions change(cutoff)
-    match block_version(proposal.block.header.height) {
-        1 => verify_pow_proposal(consensus, proposal).await,
-        2 => verify_pos_proposal(consensus, proposal).await,
-        _ => Err(Error::BlockVersionIsInvalid(proposal.block.header.version)),
-    }
-}
-
-/// Verify given PoW [`Proposal`] against provided consensus state,
+/// Verify given [`Proposal`] against provided consensus state,
 /// A proposal is considered valid when the following rules apply:
 ///     1. Proposal hash matches the actual block one
 ///     2. Block transactions don't exceed set limit
-///     3. If proposal extends a known fork, verify block's slot
-///        correspond to the fork hot/live/next one
-///     4. Block is valid
+///     3. Block is valid
 /// Additional validity rules can be applied.
-pub async fn verify_pow_proposal(
+pub async fn verify_proposal(
     consensus: &Consensus,
     proposal: &Proposal,
 ) -> Result<(Fork, Option<usize>)> {
@@ -671,38 +668,11 @@ pub async fn verify_pow_proposal(
     // Check if proposal extends any existing forks
     let (fork, index) = consensus.find_extended_fork(proposal).await?;
 
-    // Verify block's slot correspond to the forks' hot/live/next one (3)
-    if fork.slots.len() != 1 || fork.slots != proposal.block.slots {
-        return Err(Error::ProposalContainsUnknownSlots)
-    }
-
-    // Insert block slot so transactions can be validated against.
-    // Since this fork uses an overlay clone, original overlay is not affected.
-    fork.overlay.lock().unwrap().slots.insert(&[proposal.block.slots.last().unwrap().clone()])?;
-
     // Grab overlay last block
     let previous = fork.overlay.lock().unwrap().last_block()?;
 
-    // Retrieve expected reward
-    let expected_reward = expected_reward(proposal.block.header.height);
-
-    // Generate a time keeper for proposal block leight
-    let mut time_keeper = consensus.time_keeper.current();
-    time_keeper.verifying_block_height = proposal.block.header.height;
-
-    // Verify proposal block (4)
-    if verify_block(
-        &fork.overlay,
-        &time_keeper,
-        &fork.module,
-        &proposal.block,
-        &previous,
-        expected_reward,
-        consensus.pos_testing_mode,
-    )
-    .await
-    .is_err()
-    {
+    // Verify proposal block (3)
+    if verify_block(&fork.overlay, &fork.module, &proposal.block, &previous).await.is_err() {
         error!(target: "validator::verification::verify_pow_proposal", "Erroneous proposal block found");
         fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
         return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
@@ -710,94 +680,3 @@ pub async fn verify_pow_proposal(
 
     Ok((fork, index))
 }
-
-/// Verify given PoS [`Proposal`] against provided consensus state,
-/// A proposal is considered valid when the following rules apply:
-///     1. Consensus(node) has not started current slot finalization
-///     2. Proposal refers to current slot
-///     3. Proposal hash matches the actual block one
-///     4. Block transactions don't exceed set limit
-///     5. If proposal extends a known fork, verify block slots
-///        correspond to the fork hot/live ones
-///     6. Block is valid
-/// Additional validity rules can be applied.
-pub async fn verify_pos_proposal(
-    consensus: &Consensus,
-    proposal: &Proposal,
-) -> Result<(Fork, Option<usize>)> {
-    // Generate a time keeper for current slot
-    let time_keeper = consensus.time_keeper.current();
-
-    // Node have already checked for finalization in this slot (1)
-    if time_keeper.verifying_block_height <= *consensus.checked_finalization.read().await {
-        warn!(target: "validator::verification::verify_pos_proposal", "Proposal received after finalization sync period.");
-        return Err(Error::ProposalAfterFinalizationError)
-    }
-
-    // Proposal validations
-    let hdr = &proposal.block.header;
-
-    // Ignore proposal if not for current slot (2)
-    if hdr.height != time_keeper.verifying_block_height {
-        return Err(Error::ProposalNotForCurrentSlotError)
-    }
-
-    // Check if proposal hash matches actual one (3)
-    let proposal_hash = proposal.block.hash()?;
-    if proposal.hash != proposal_hash {
-        warn!(
-            target: "validator::verification::verify_pos_proposal", "Received proposal contains mismatched hashes: {} - {}",
-            proposal.hash, proposal_hash
-        );
-        return Err(Error::ProposalHashesMissmatchError)
-    }
-
-    // Check that proposal transactions don't exceed limit (4)
-    if proposal.block.txs.len() > TXS_CAP {
-        warn!(
-            target: "validator::verification::verify_pos_proposal", "Received proposal transactions exceed configured cap: {} - {}",
-            proposal.block.txs.len(),
-            TXS_CAP
-        );
-        return Err(Error::ProposalTxsExceedCapError)
-    }
-
-    // Check if proposal extends any existing forks
-    let (fork, index) = consensus.find_extended_fork(proposal).await?;
-
-    // Verify block slots correspond to the forks' hot/live ones (5)
-    if !fork.slots.is_empty() && fork.slots != proposal.block.slots {
-        return Err(Error::ProposalContainsUnknownSlots)
-    }
-
-    // Insert last block slot so transactions can be validated against.
-    // Rest (empty) slots will be inserted along with the block.
-    // Since this fork uses an overlay clone, original overlay is not affected.
-    fork.overlay.lock().unwrap().slots.insert(&[proposal.block.slots.last().unwrap().clone()])?;
-
-    // Grab overlay last block
-    let previous = fork.overlay.lock().unwrap().last_block()?;
-
-    // Retrieve expected reward
-    let expected_reward = expected_reward(time_keeper.verifying_block_height);
-
-    // Verify proposal block (6)
-    if verify_block(
-        &fork.overlay,
-        &time_keeper,
-        &fork.module,
-        &proposal.block,
-        &previous,
-        expected_reward,
-        consensus.pos_testing_mode,
-    )
-    .await
-    .is_err()
-    {
-        error!(target: "validator::verification::verify_pos_proposal", "Erroneous proposal block found");
-        fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
-        return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
-    };
-
-    Ok((fork, index))
-}

+ 3 - 5
tests/blockchain.rs

@@ -21,7 +21,7 @@ use darkfi::{
     validator::{
         pid::slot_pid_output,
         pow::PoWModule,
-        validation::{validate_block, validate_blockchain},
+        verification::{validate_block, validate_blockchain},
     },
     Error, Result,
 };
@@ -134,11 +134,8 @@ impl Harness {
 
             // This will be true for every insert, apart from genesis
             if let Some(p) = previous {
-                // Retrieve expected reward
-                let expected_reward = expected_reward(block.header.height);
-
                 // Validate block
-                validate_block(block, &p, expected_reward, &node.module)?;
+                validate_block(block, &p, &node.module)?;
 
                 // Update PoW module
                 if block.header.version == 1 {
@@ -160,6 +157,7 @@ impl Harness {
     }
 }
 
+#[ignore]
 #[test]
 fn blockchain_add_pos_blocks() -> Result<()> {
     smol::block_on(async {