aggstam 3 лет назад
Родитель
Сommit
17415a3d21

+ 50 - 21
bin/darkfid2/src/tests/harness.rs

@@ -19,7 +19,10 @@
 use darkfi::{
     blockchain::{BlockInfo, Header},
     util::time::TimeKeeper,
-    validator::{Validator, ValidatorConfig},
+    validator::{
+        consensus::{next_block_reward, pid::slot_pid_output},
+        Validator, ValidatorConfig,
+    },
     Result,
 };
 use darkfi_contract_test_harness::vks;
@@ -81,11 +84,52 @@ impl Harness {
         Ok(())
     }
 
-    pub async fn generate_next_block(&self) -> Result<BlockInfo> {
-        // Retrieve last block
-        let previous = self.alice._validator.read().await.blockchain.last_block()?;
+    pub async fn generate_next_block(
+        &self,
+        previous: &BlockInfo,
+        slots_count: usize,
+    ) -> Result<BlockInfo> {
         let previous_hash = previous.blockhash();
 
+        // Generate empty slots
+        let mut slots = Vec::with_capacity(slots_count);
+        let mut previous_slot = previous.slots.last().unwrap().clone();
+        for _ in 0..slots_count - 1 {
+            let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot);
+            let slot = Slot::new(
+                previous_slot.id + 1,
+                pallas::Base::ZERO,
+                vec![previous_hash],
+                vec![previous.header.previous.clone()],
+                f,
+                error,
+                previous_slot.error,
+                previous_slot.total_tokens + previous_slot.reward,
+                0,
+                sigma1,
+                sigma2,
+            );
+            slots.push(slot.clone());
+            previous_slot = slot;
+        }
+
+        // Generate slot
+        let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot);
+        let slot = Slot::new(
+            previous_slot.id + 1,
+            pallas::Base::ZERO,
+            vec![previous_hash],
+            vec![previous.header.previous.clone()],
+            f,
+            error,
+            previous_slot.error,
+            previous_slot.total_tokens + previous_slot.reward,
+            next_block_reward(),
+            sigma1,
+            sigma2,
+        );
+        slots.push(slot);
+
         // We increment timestamp so we don't have to use sleep
         let mut timestamp = previous.header.timestamp;
         timestamp.add(1);
@@ -94,28 +138,13 @@ impl Harness {
         let header = Header::new(
             previous_hash,
             previous.header.epoch,
-            previous.header.slot + 1,
+            previous_slot.id + 1,
             timestamp,
             previous.header.root.clone(),
         );
 
-        // Generate slot
-        let slot = Slot::new(
-            previous.header.slot + 1,
-            pallas::Base::ZERO,
-            vec![previous_hash],
-            vec![previous.header.previous.clone()],
-            0.0,
-            0.0,
-            0.0,
-            0,
-            0,
-            pallas::Base::ZERO,
-            pallas::Base::ZERO,
-        );
-
         // Generate block
-        let block = BlockInfo::new(header, vec![], previous.producer.clone(), vec![slot]);
+        let block = BlockInfo::new(header, vec![], previous.producer.clone(), slots);
 
         Ok(block)
     }

+ 8 - 2
bin/darkfid2/src/tests/mod.rs

@@ -29,11 +29,17 @@ async fn add_blocks() -> Result<()> {
     // Initialize harness in testing mode
     let th = Harness::new(true).await?;
 
+    // Retrieve genesis block
+    let previous = th.alice._validator.read().await.blockchain.last_block()?;
+
     // Generate next block
-    let block = th.generate_next_block().await?;
+    let block1 = th.generate_next_block(&previous, 1).await?;
+
+    // Generate next block, with 4 empty slots inbetween
+    let block2 = th.generate_next_block(&block1, 5).await?;
 
     // Add it to nodes
-    th.add_blocks(&vec![block]).await?;
+    th.add_blocks(&vec![block1, block2]).await?;
 
     // Validate chains
     th.validate_chains().await?;

+ 17 - 5
src/blockchain/block_store.rs

@@ -116,7 +116,7 @@ impl BlockInfo {
     ///     4. Slots vector is not empty and all its slots are valid
     ///     5. Slot is the same as the slots vector last slot id
     /// Additional validity rules can be applied.
-    pub fn validate(&self, previous: &Self) -> Result<()> {
+    pub fn validate(&self, previous: &Self, expected_reward: u64) -> Result<()> {
         let error = Err(Error::BlockIsInvalid(self.blockhash().to_string()));
         let previous_hash = previous.blockhash();
 
@@ -143,12 +143,24 @@ impl BlockInfo {
         // Retrieve previous block last slot
         let mut previous_slot = previous.slots.last().unwrap();
 
-        // Slots must already be in correct order (sorted by id)
-        for slot in &self.slots {
-            validate_slot(slot, previous_slot, &previous_hash, &previous.header.previous)?;
-            previous_slot = slot;
+        // Check if empty slots existed
+        if self.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 &self.slots[..self.slots.len() - 1] {
+                validate_slot(slot, previous_slot, &previous_hash, &previous.header.previous, 0)?;
+                previous_slot = slot;
+            }
         }
 
+        validate_slot(
+            self.slots.last().unwrap(),
+            previous_slot,
+            &previous_hash,
+            &previous.header.previous,
+            expected_reward,
+        )?;
+
         // Check block slot is the last slot id (5)
         if self.slots.last().unwrap().id != self.header.slot {
             return error

+ 3 - 2
src/blockchain/mod.rs

@@ -24,7 +24,7 @@ use sled::Transactional;
 use darkfi_sdk::blockchain::Slot;
 use darkfi_serial::{deserialize, serialize, Decodable};
 
-use crate::{tx::Transaction, Error, Result};
+use crate::{tx::Transaction, validator::consensus::next_block_reward, Error, Result};
 
 /// Block related definitions and storage implementations
 pub mod block_store;
@@ -110,7 +110,8 @@ impl Blockchain {
         let blocks = self.order.get_all()?;
         for (index, block) in blocks[1..].iter().enumerate() {
             let full_blocks = self.get_blocks_by_hash(&[blocks[index].1, block.1])?;
-            full_blocks[1].validate(&full_blocks[0])?;
+            let expected_reward = next_block_reward();
+            full_blocks[1].validate(&full_blocks[0], expected_reward)?;
         }
 
         Ok(())

+ 24 - 8
src/blockchain/slot_store.rs

@@ -20,7 +20,7 @@
 use darkfi_sdk::blockchain::Slot;
 use darkfi_serial::{deserialize, serialize};
 
-use crate::{validator::consensus::pid::slot_sigmas, Error, Result};
+use crate::{validator::consensus::pid::slot_pid_output, Error, Result};
 
 use super::{parse_record, SledDbOverlayPtr};
 
@@ -28,20 +28,21 @@ use super::{parse_record, SledDbOverlayPtr};
 ///     1. Id increments previous slot id
 ///     2. Forks extend previous block hash
 ///     3. Forks follow previous block sequence
-///     4. Sigmas are the expected ones, based on consensus PID
+///     4. Slot total tokens represent the total network tokens
+///        up until this slot
+///     5. Slot previous error value correspond to previous slot one
+///     6. PID output for this slot is correct
+///     7. Slot reward value is the expected one
 /// Additional validity rules can be applied.
 pub fn validate_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));
 
-    // TODO: Validate previous slot stuff
-    // slot.total_tokens = previous.total_tokens + previous.reward
-    // slot.previous_slot_err = previous.err;
-
     // Check slots are incremental (1)
     if slot.id <= previous.id {
         return error
@@ -57,8 +58,23 @@ pub fn validate_slot(
         return error
     }
 
-    // Check sigmas (4)
-    if (slot.sigma1, slot.sigma2) != slot_sigmas() {
+    // Check total tokens (4)
+    if slot.total_tokens != previous.total_tokens + previous.reward {
+        return error
+    }
+
+    // Check previous slot error (5)
+    if slot.previous_slot_error != previous.error {
+        return error
+    }
+
+    // Check PID output for this slot (6)
+    if (slot.f, slot.error, slot.sigma1, slot.sigma2) != slot_pid_output(previous) {
+        return error
+    }
+
+    // Check reward is the expected one (7)
+    if slot.reward != expected_reward {
         return error
     }
 

+ 5 - 1
src/validator/consensus/float_10.rs

@@ -50,6 +50,10 @@ impl Float10 {
     pub fn ln(&self) -> Self {
         Self(self.0.ln())
     }
+
+    pub fn to_f64(&self) -> f64 {
+        self.0.to_f64().value()
+    }
 }
 
 impl Add for Float10 {
@@ -124,7 +128,7 @@ impl TryFrom<f64> for Float10 {
     type Error = crate::Error;
 
     fn try_from(value: f64) -> Result<Self, Self::Error> {
-        Ok(Self(FBig::try_from(value)?.with_base().value()))
+        Ok(Self(FBig::try_from(value)?.with_precision(RADIX_BITS).value().with_base().value()))
     }
 }
 

+ 8 - 0
src/validator/consensus/mod.rs

@@ -38,3 +38,11 @@ impl Consensus {
         Self { blockchain, time_keeper }
     }
 }
+
+/// Block producer reward.
+/// TODO (res) implement reward mechanism with accord to DRK, DARK token-economics.
+pub fn next_block_reward() -> u64 {
+    // Configured block reward (1 DRK == 1 * 10^8)
+    let reward: u64 = 100_000_000;
+    reward
+}

+ 14 - 19
src/validator/consensus/pid.rs

@@ -16,11 +16,6 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-//! TODO: this is just the foundation layout, so we can complete
-//! the basic validator. We will use pallas::Base::zero() everywhere,
-//! since we just want to simulate its functionality. After layout is
-//! complete, the proper pid functionality will be implemented.
-
 use darkfi_sdk::{blockchain::Slot, pasta::pallas};
 use lazy_static::lazy_static;
 
@@ -41,23 +36,23 @@ lazy_static! {
 }
 
 /// Return 2-term target approximation sigma coefficients,
-/// corresponding to provided slot consensus state.
-pub fn slot_sigmas() -> (pallas::Base, pallas::Base) {
-    (pallas::Base::zero(), pallas::Base::zero())
-}
-
-/// Return 2-term target approximation sigma coefficients,
-/// corresponding to provided slot consensus state.
-pub fn sigmass(previous_slot: &Slot) -> (pallas::Base, pallas::Base) {
-    let f = calculate_f(previous_slot);
+/// alogn with the inverse probability `f` of becoming a
+/// block producer and the feedback error, corresponding
+/// to provided slot consensus state,
+pub fn slot_pid_output(previous_slot: &Slot) -> (f64, f64, pallas::Base, pallas::Base) {
+    let (f, error) = calculate_f(previous_slot);
     let total_tokens =
         Float10::try_from(previous_slot.total_tokens + previous_slot.reward).unwrap();
-    calculate_sigmas(f, total_tokens)
+    let (sigma1, sigma2) = calculate_sigmas(f.clone(), total_tokens);
+
+    // TODO: log values
+
+    (f.to_f64(), error.to_f64(), sigma1, sigma2)
 }
 
 /// Calculate the inverse probability `f` of becoming a block producer (winning the lottery)
-/// having all the tokens, represented as Float10.
-fn calculate_f(previous_slot: &Slot) -> Float10 {
+/// having all the tokens, and the feedback error, represented as Float10.
+fn calculate_f(previous_slot: &Slot) -> (Float10, Float10) {
     // PID controller K values based on constants
     let k1 = KP.clone() + KI.clone() + KD.clone();
     let k2 = FLOAT10_NEG_ONE.clone() * KP.clone() + FLOAT10_NEG_TWO.clone() * KD.clone();
@@ -77,7 +72,7 @@ fn calculate_f(previous_slot: &Slot) -> Float10 {
 
     // Calculate f
     let mut f = previous_slot_f +
-        k1 * error +
+        k1 * error.clone() +
         k2 * previous_slot_error +
         k3 * previous_slot_previous_slot_error;
 
@@ -88,7 +83,7 @@ fn calculate_f(previous_slot: &Slot) -> Float10 {
         f = MAX_F.clone()
     }
 
-    f
+    (f, error)
 }
 
 /// Return 2-term target approximation sigma coefficients,

+ 38 - 9
src/validator/mod.rs

@@ -30,7 +30,7 @@ use crate::{
 
 /// DarkFi consensus module
 pub mod consensus;
-use consensus::Consensus;
+use consensus::{next_block_reward, Consensus};
 
 /// Verification functions
 pub mod verification;
@@ -91,8 +91,15 @@ impl Validator {
         // Add genesis block if blockchain is empty
         if blockchain.genesis().is_err() {
             info!(target: "validator", "Appending genesis block");
-            verify_block(&overlay, &config.time_keeper, &config.genesis_block, None, testing_mode)
-                .await?;
+            verify_block(
+                &overlay,
+                &config.time_keeper,
+                &config.genesis_block,
+                None,
+                0,
+                testing_mode,
+            )
+            .await?;
         };
 
         // Deploy native wasm contracts
@@ -145,9 +152,20 @@ impl Validator {
             // Use block slot in time keeper
             time_keeper.verifying_slot = block.header.slot;
 
-            if verify_block(&overlay, &time_keeper, block, previous, self.testing_mode)
-                .await
-                .is_err()
+            // Retrieve expected reward
+            let expected_reward = next_block_reward();
+
+            // Verify block
+            if verify_block(
+                &overlay,
+                &time_keeper,
+                block,
+                previous,
+                expected_reward,
+                self.testing_mode,
+            )
+            .await
+            .is_err()
             {
                 error!(target: "validator", "Erroneous block found in set");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
@@ -242,9 +260,20 @@ impl Validator {
             // Use block slot in time keeper
             time_keeper.verifying_slot = block.header.slot;
 
-            if verify_block(&overlay, &time_keeper, block, previous, self.testing_mode)
-                .await
-                .is_err()
+            // Retrieve expected reward
+            let expected_reward = next_block_reward();
+
+            // Verify block
+            if verify_block(
+                &overlay,
+                &time_keeper,
+                block,
+                previous,
+                expected_reward,
+                self.testing_mode,
+            )
+            .await
+            .is_err()
             {
                 error!(target: "validator", "Erroneous block found in set");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;

+ 5 - 1
src/validator/verification.rs

@@ -41,6 +41,7 @@ pub async fn verify_block(
     time_keeper: &TimeKeeper,
     block: &BlockInfo,
     previous: Option<&BlockInfo>,
+    expected_reward: u64,
     testing_mode: bool,
 ) -> Result<()> {
     let block_hash = block.blockhash();
@@ -56,12 +57,15 @@ pub async fn verify_block(
         return Err(Error::VerifyingSlotMissmatch())
     }
 
+    // TODO: on genesis block, verify slot.total_tokens = txs.total,
+    // thats our genesis distribution, and reward is 0
+
     // Validate block using its previous, excluding genesis
     if block.header.slot != 0 {
         if previous.is_none() {
             return Err(Error::BlockPreviousMissing())
         }
-        block.validate(previous.unwrap())?;
+        block.validate(previous.unwrap(), expected_reward)?;
     }
 
     // Validate proposal transaction if not in testing mode

+ 27 - 14
tests/blockchain.rs

@@ -18,6 +18,7 @@
 
 use darkfi::{
     blockchain::{BlockInfo, Blockchain, BlockchainOverlay, Header},
+    validator::consensus::{next_block_reward, pid::slot_pid_output},
     Error, Result,
 };
 use darkfi_sdk::{
@@ -53,9 +54,29 @@ impl Harness {
 
     fn generate_next_block(&self, previous: &BlockInfo) -> BlockInfo {
         let previous_hash = previous.blockhash();
+
+        // Generate slot
+        let previous_slot = previous.slots.last().unwrap();
+        let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot);
+        let slot = Slot::new(
+            previous_slot.id + 1,
+            pallas::Base::ZERO,
+            vec![previous_hash],
+            vec![previous.header.previous.clone()],
+            f,
+            error,
+            previous_slot.error,
+            previous_slot.total_tokens + previous_slot.reward,
+            next_block_reward(),
+            sigma1,
+            sigma2,
+        );
+
         // We increment timestamp so we don't have to use sleep
         let mut timestamp = previous.header.timestamp;
         timestamp.add(1);
+
+        // Generate header
         let header = Header::new(
             previous_hash,
             previous.header.epoch,
@@ -63,19 +84,7 @@ impl Harness {
             timestamp,
             previous.header.root.clone(),
         );
-        let slot = Slot::new(
-            previous.header.slot + 1,
-            pallas::Base::ZERO,
-            vec![previous_hash],
-            vec![previous.header.previous.clone()],
-            0.0,
-            0.0,
-            0.0,
-            0,
-            0,
-            pallas::Base::ZERO,
-            pallas::Base::ZERO,
-        );
+
         BlockInfo::new(header, vec![], previous.producer.clone(), vec![slot])
     }
 
@@ -104,7 +113,11 @@ impl Harness {
 
             // This will be true for every insert, apart from genesis
             if let Some(p) = previous {
-                block.validate(&p)?;
+                // Retrieve expected reward
+                let expected_reward = next_block_reward();
+
+                // Validate block
+                block.validate(&p, expected_reward)?;
             }
 
             // Insert block