Просмотр исходного кода

consensus: leaders count history logic implemented, minor fixes in lottery calculations

aggstam 3 лет назад
Родитель
Сommit
4d0a9b1b7c
2 измененных файлов с 100 добавлено и 61 удалено
  1. 6 9
      src/consensus/metadata.rs
  2. 94 52
      src/consensus/state.rs

+ 6 - 9
src/consensus/metadata.rs

@@ -43,6 +43,8 @@ pub struct Metadata {
     pub proof: LeadProof,
     /// Slot offset block owner used
     pub offset: u64,
+    /// Block owner leaders count
+    pub leaders: u64,
 }
 
 impl Default for Metadata {
@@ -54,7 +56,8 @@ impl Default for Metadata {
         let eta: [u8; 32] = *blake3::hash(b"let there be dark!").as_bytes();
         let proof = LeadProof::default();
         let offset = 0;
-        Self { signature, public_key: keypair.public, public_inputs, eta, proof, offset }
+        let leaders = 0;
+        Self { signature, public_key: keypair.public, public_inputs, eta, proof, offset, leaders }
     }
 }
 
@@ -66,8 +69,9 @@ impl Metadata {
         eta: [u8; 32],
         proof: LeadProof,
         offset: u64,
+        leaders: u64,
     ) -> Self {
-        Self { signature, public_key, public_inputs, eta, proof, offset }
+        Self { signature, public_key, public_inputs, eta, proof, offset, leaders }
     }
 }
 
@@ -79,13 +83,6 @@ pub struct LeadProof {
 }
 
 impl LeadProof {
-    /*
-        pub fn new(pk: &ProvingKey, coin: LeadCoin) -> Self {
-            let proof = coin.create_lead_proof(pk).unwrap();
-            Self { proof }
-    }
-        */
-
     pub fn verify(&self, vk: &VerifyingKey, public_inputs: &[pallas::Base]) -> Result<()> {
         if let Err(e) = self.proof.verify(vk, public_inputs) {
             error!("Verification of consensus lead proof failed: {}", e);

+ 94 - 52
src/consensus/state.rs

@@ -33,11 +33,7 @@ use darkfi_serial::{
 };
 use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
 use log::{debug, error, info, warn};
-use pasta_curves::{
-    arithmetic::CurveAffine,
-    group::{ff::PrimeField, Curve},
-    pallas,
-};
+use pasta_curves::{group::ff::PrimeField, pallas};
 use rand::{rngs::OsRng, thread_rng, Rng};
 
 use super::{
@@ -148,14 +144,13 @@ pub struct ValidatorState {
     pub participating: Option<u64>,
     /// Wallet interface
     pub wallet: WalletPtr,
-    /// nullifiers
-    pub nullifiers: Vec<pallas::Base>,
-    /// spent coins
-    pub spent: Vec<(pallas::Base, pallas::Base)>,
-    /// lead coins
-    pub lead: Vec<(pallas::Base, pallas::Base)>,
-    /// f history
-    pub f_history: Vec<Float10>,
+    // TODO: Aren't these already in db after finalization?
+    /// Seen nullifiers from proposals
+    pub leaders_nullifiers: Vec<pallas::Base>,
+    /// Seen spent coins from proposals
+    pub leaders_spent_coins: Vec<(pallas::Base, pallas::Base)>,
+    /// Leaders count history
+    pub leaders_history: Vec<u64>,
     /// Kp
     pub Kp: Float10,
     /// Ti
@@ -267,7 +262,6 @@ impl ValidatorState {
         info!("Finished deployment of native wasm contracts");
         // -----NATIVE WASM CONTRACTS-----
 
-        let zero = Float10::from_str_native("0").unwrap().with_precision(RADIX_BITS).value();
         let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
         let ten = Float10::from_str_native("10").unwrap().with_precision(RADIX_BITS).value();
         let three = Float10::from_str_native("3").unwrap().with_precision(RADIX_BITS).value();
@@ -281,10 +275,9 @@ impl ValidatorState {
             verifying_keys: Arc::new(RwLock::new(verifying_keys)),
             participating,
             wallet,
-            nullifiers: vec![],
-            spent: vec![],
-            lead: vec![],
-            f_history: vec![zero],
+            leaders_nullifiers: vec![],
+            leaders_spent_coins: vec![],
+            leaders_history: vec![0],
             Kp: three / nine,
             Ti: one.clone() / ten.clone(),
             Td: one / ten,
@@ -516,7 +509,8 @@ impl ValidatorState {
     /// Auxillary function to calculate overall empty slots.
     /// We keep an offset from genesis indicating when the first slot actually started.
     /// This offset is shared between nodes.
-    fn overall_empty_slots(&mut self, slot: u64) -> u64 {
+    fn overall_empty_slots(&mut self) -> u64 {
+        let slot = self.current_slot();
         // Retrieve existing blocks excluding genesis
         let blocks = (self.blockchain.len() as u64) - 1;
         // Setup offset if only have genesis and havent received offset from other nodes
@@ -534,34 +528,49 @@ impl ValidatorState {
     /// total stake plus one.
     /// assuming constant Reward.
     fn total_stake_plus(&mut self, epoch: u64, slot: u64) -> u64 {
-        (epoch * EPOCH_LENGTH as u64 + slot + 1 - self.overall_empty_slots(slot)) * Self::reward()
+        (epoch * EPOCH_LENGTH as u64 + slot + 1 - self.overall_empty_slots()) * Self::reward()
     }
 
-    /// get number of leaders in last epoch.
-    /// in ideal-world all nodes will end up with identical POV
-    /// of blockchain, but in real-world:
-    /// TODO: this parameter need to be published in the block header,
-    /// and only read from last block header.
-    fn leads_per_block(&mut self) -> Float10 {
-        //TODO: complete this
-        let fi64: i64 = 1;
-        self.f_history.push(Float10::try_from(fi64).unwrap().with_precision(RADIX_BITS).value());
-        Float10::try_from(fi64).unwrap().with_precision(RADIX_BITS).value()
+    /// Calculate how many leaders existed in previous slot and appends
+    /// it to history, to report it if win. On finalization sync period,
+    /// node replaces its leaders history with the sequence extracted by
+    /// the longest fork.
+    fn extend_leaders_history(&mut self) -> Float10 {
+        let slot = self.current_slot();
+        let previous_slot = slot - 1;
+        let mut count = 0;
+        for chain in &self.consensus.proposals {
+            // Previous slot proposals exist at end of each fork
+            if chain.proposals.last().unwrap().block.header.slot == previous_slot {
+                count += 1;
+            }
+        }
+        self.leaders_history.push(count);
+        debug!("extend_leaders_history(): Current leaders history: {:?}", self.leaders_history);
+        Float10::try_from(count).unwrap().with_precision(RADIX_BITS).value()
     }
 
     fn f_dif(&mut self) -> Float10 {
         let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
-        one - self.leads_per_block()
+        one - self.extend_leaders_history()
     }
 
     fn f_der(&self) -> Float10 {
-        let len = self.f_history.len();
-        (self.f_history[len - 1].clone() - self.f_history[len - 2].clone()) / self.Td.clone()
+        let len = self.leaders_history.len();
+        let last = Float10::try_from(self.leaders_history[len - 1])
+            .unwrap()
+            .with_precision(RADIX_BITS)
+            .value();
+        let second_to_last = Float10::try_from(self.leaders_history[len - 2])
+            .unwrap()
+            .with_precision(RADIX_BITS)
+            .value();
+        (last - second_to_last) / self.Td.clone()
     }
 
     fn f_int(&self) -> Float10 {
         let mut sum = Float10::from_str_native("0").unwrap().with_precision(RADIX_BITS).value();
-        for f in &self.f_history {
+        for f in &self.leaders_history {
             sum += f.clone() * self.Td.clone();
         }
         sum
@@ -570,16 +579,14 @@ impl ValidatorState {
     /// the probability of winnig lottery having all the stake
     /// returns f
     fn win_prob_with_full_stake(&mut self) -> Float10 {
-        let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
         let zero = Float10::from_str_native("0").unwrap().with_precision(RADIX_BITS).value();
+        let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
         let mut f = zero.clone();
         let step = Float10::from_str_native("0.1").unwrap().with_precision(RADIX_BITS).value();
         let p = self.f_dif();
         let i = self.f_int();
         let d = self.f_der();
-        while f <= Float10::from_str_native("0").unwrap().with_precision(RADIX_BITS).value() &&
-            f >= Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value()
-        {
+        while f <= zero || f >= one {
             f = self.Kp.clone() *
                 (p.clone() +
                     one.clone() / self.Ti.clone() * i.clone() +
@@ -590,7 +597,7 @@ impl ValidatorState {
                 self.Kp += step.clone();
             }
         }
-        Float10::try_from(f).unwrap().with_precision(RADIX_BITS).value()
+        f
     }
 
     /// Check that the provided participant/stakeholder coins win the slot lottery.
@@ -602,11 +609,11 @@ impl ValidatorState {
     pub fn is_slot_leader(&mut self) -> (bool, usize, pallas::Base, pallas::Base) {
         // Slot relative index
         let slot = self.relative_slot(self.current_slot());
-        let (sigma1, sigma2) = self.sigmas(slot, self.consensus.epoch);
+        let (sigma1, sigma2) = self.sigmas(self.consensus.epoch, slot);
         // Stakeholder's epoch coins
         let coins = &self.consensus.coins;
 
-        info!("Consensus::is_leader(): slot: {}, coins len: {}", slot, coins.len());
+        info!("consensus::is_leader(): slot: {}, coins len: {}", slot, coins.len());
         assert!((slot as usize) < coins.len());
 
         let competing_coins = &coins[slot as usize];
@@ -678,18 +685,13 @@ impl ValidatorState {
             eta.to_repr(),
             LeadProof::from(proof),
             self.get_current_offset(),
+            self.leaders_history.last().unwrap().clone(),
         );
         // Replacing old coin with the derived coin
         // TODO: do we need that? on next epoch we replace everything
         // how is this going to get reused?
         self.consensus.coins[relative_slot][idx] = coin.derive_coin(eta, relative_slot as u64);
 
-        // lead,spend,nullifiers
-        self.nullifiers.push(coin.sn());
-        let cm = coin.coin1_commitment.to_affine().coordinates().unwrap();
-        self.spent.push((*cm.x(), *cm.y()));
-        self.lead.push((*cm.x(), *cm.y()));
-
         Ok(Some(BlockProposal::new(header, unproposed_txs, metadata)))
     }
 
@@ -805,18 +807,19 @@ impl ValidatorState {
         info!("receive_proposal(): Leader proof verified successfully!");
 
         // Verify proposal public inputs
-        //TODO: validate sn/cm
         let prop_sn = md.public_inputs[PI_NULLIFIER_INDEX];
-        for sn in &self.nullifiers {
+        for sn in &self.leaders_nullifiers {
             if *sn == prop_sn {
+                error!("receive_proposal(): Proposal nullifiers exist.");
                 return Err(Error::ProposalIsSpent)
             }
         }
         let prop_cm_x: pallas::Base = md.public_inputs[PI_COMMITMENT_X_INDEX];
         let prop_cm_y: pallas::Base = md.public_inputs[PI_COMMITMENT_Y_INDEX];
 
-        for cm in &self.lead {
+        for cm in &self.leaders_spent_coins {
             if *cm == (prop_cm_x, prop_cm_y) {
+                error!("receive_proposal(): Proposal coin already spent.");
                 return Err(Error::ProposalIsSpent)
             }
         }
@@ -848,6 +851,10 @@ impl ValidatorState {
             }
         };
 
+        // Store proposal coin info
+        self.leaders_nullifiers.push(prop_sn);
+        self.leaders_spent_coins.push((prop_cm_x, prop_cm_y));
+
         Ok(())
     }
 
@@ -923,6 +930,30 @@ impl ValidatorState {
         Ok(())
     }
 
+    /// Auxillary function to set nodes leaders count history to the largest fork sequence
+    /// of leaders, by using provided index.
+    fn set_leader_history(&mut self, index: i64) {
+        // Check if we found longest fork to extract sequence from
+        match index {
+            -1 => {
+                debug!("set_leader_history(): No fork exists.");
+            }
+            _ => {
+                debug!("set_leader_history(): Checking last proposal of fork: {}", index);
+                let last_proposal =
+                    self.consensus.proposals[index as usize].proposals.last().unwrap();
+                if last_proposal.block.header.slot == self.current_slot() {
+                    // Replacing our last history element with the leaders one
+                    self.leaders_history.pop();
+                    self.leaders_history.push(last_proposal.block.metadata.leaders);
+                    debug!("set_leader_history(): New leaders history: {:?}", self.leaders_history);
+                    return
+                }
+            }
+        }
+        self.leaders_history.push(0);
+    }
+
     /// Node checks if any of the fork chains can be finalized.
     /// Consensus finalization logic:
     /// - If the node has observed the creation of 3 proposals in a fork chain and no other
@@ -932,9 +963,15 @@ impl ValidatorState {
     pub async fn chain_finalization(&mut self) -> Result<Vec<BlockInfo>> {
         // First we find longest chain without any other forks at same height
         let mut chain_index = -1;
+        // Use this index to extract leaders count sequence from longest fork
+        let mut index_for_history = -1;
         let mut max_length = 0;
         for (index, chain) in self.consensus.proposals.iter().enumerate() {
             let length = chain.proposals.len();
+            // Check if greater than max to retain index for history
+            if length > max_length {
+                index_for_history = index as i64;
+            }
             // Ignore forks with less that 3 blocks
             if length < 3 {
                 continue
@@ -958,11 +995,13 @@ impl ValidatorState {
         // Check if we found any fork to finalize
         match chain_index {
             -2 => {
-                debug!("chain_finalization(): Eligible forks with same heigh exist, nothing to finalize");
+                debug!("chain_finalization(): Eligible forks with same heigh exist, nothing to finalize.");
+                self.set_leader_history(index_for_history);
                 return Ok(vec![])
             }
             -1 => {
-                debug!("chain_finalization(): All chains have less than 3 proposals, nothing to finalize");
+                debug!("chain_finalization(): All chains have less than 3 proposals, nothing to finalize.");
+                self.set_leader_history(index_for_history);
                 return Ok(vec![])
             }
             _ => debug!("chain_finalization(): Chain {} can be finalized!", chain_index),
@@ -1004,6 +1043,9 @@ impl ValidatorState {
             }
         }
 
+        // Setting leaders history to last proposal leaders count
+        self.leaders_history = vec![chain.proposals.last().unwrap().block.metadata.leaders];
+
         // Removing rest forks
         self.consensus.proposals = vec![];
         self.consensus.proposals.push(chain);