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

blockchain: simplyfied Slot structure

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

+ 16 - 31
bin/darkfid2/src/tests/harness.rs

@@ -28,7 +28,7 @@ use darkfi::{
 };
 use darkfi_contract_test_harness::{vks, Holder, TestHarness};
 use darkfi_sdk::{
-    blockchain::Slot,
+    blockchain::{PidOutput, PreviousSlot, Slot},
     pasta::{group::ff::Field, pallas},
 };
 
@@ -126,42 +126,27 @@ impl Harness {
         // 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,
+        for i in 0..slots_count {
+            let id = previous_slot.id + 1;
+            // First slot in the sequence has (at least) 1 previous slot producer
+            let producers = if i == 0 { 1 } else { 0 };
+            let previous = PreviousSlot::new(
+                producers,
                 vec![previous_hash],
                 vec![previous.header.previous.clone()],
-                f,
-                error,
-                previous_slot.error,
-                previous_slot.total_tokens + previous_slot.reward,
-                0,
-                sigma1,
-                sigma2,
+                pallas::Base::ZERO,
+                previous_slot.pid.error,
             );
+            let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot, producers);
+            let pid = PidOutput::new(f, error, sigma1, sigma2);
+            let total_tokens = previous_slot.total_tokens + previous_slot.reward;
+            // Only last slot in the sequence has a reward
+            let reward = if i == slots_count - 1 { next_block_reward() } else { 0 };
+            let slot = Slot::new(id, previous, pid, total_tokens, reward);
             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);
@@ -170,7 +155,7 @@ impl Harness {
         let header = Header::new(
             previous_hash,
             previous.header.epoch,
-            previous_slot.id + 1,
+            slots.last().unwrap().id,
             timestamp,
             previous.header.root.clone(),
         );

+ 6 - 4
src/blockchain/slot_store.rs

@@ -49,12 +49,12 @@ pub fn validate_slot(
     }
 
     // Check previous block hash (2)
-    if !slot.fork_hashes.contains(previous_block_hash) {
+    if !slot.previous.last_hashes.contains(previous_block_hash) {
         return error
     }
 
     // Check previous block sequence (3)
-    if !slot.fork_previous_hashes.contains(previous_block_sequence) {
+    if !slot.previous.second_to_last_hashes.contains(previous_block_sequence) {
         return error
     }
 
@@ -64,12 +64,14 @@ pub fn validate_slot(
     }
 
     // Check previous slot error (5)
-    if slot.previous_slot_error != previous.error {
+    if slot.previous.error != previous.pid.error {
         return error
     }
 
     // Check PID output for this slot (6)
-    if (slot.f, slot.error, slot.sigma1, slot.sigma2) != slot_pid_output(previous) {
+    if (slot.pid.f, slot.pid.error, slot.pid.sigma1, slot.pid.sigma2) !=
+        slot_pid_output(previous, slot.previous.producers)
+    {
         return error
     }
 

+ 5 - 15
src/consensus/state.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::{
-    blockchain::Slot,
+    blockchain::{PidOutput, PreviousSlot, Slot},
     crypto::MerkleTree,
     pasta::{group::ff::PrimeField, pallas},
 };
@@ -164,20 +164,10 @@ impl ConsensusState {
         sigma2: pallas::Base,
     ) {
         let id = self.time_keeper.current_slot();
-        let previous_eta = self.get_previous_eta();
-        let slot = Slot::new(
-            id,
-            previous_eta,
-            fork_hashes,
-            fork_previous_hashes,
-            0.0,
-            0.0,
-            0.0,
-            0,
-            0,
-            sigma1,
-            sigma2,
-        );
+        let previous =
+            PreviousSlot::new(0, fork_hashes, fork_previous_hashes, self.get_previous_eta(), 0.0);
+        let pid = PidOutput::new(0.0, 0.0, sigma1, sigma2);
+        let slot = Slot::new(id, previous, pid, 0, 0);
         info!(target: "consensus::state", "generate_slot: {:?}", slot);
         self.slots.push(slot);
     }

+ 4 - 4
src/consensus/validator.rs

@@ -609,20 +609,20 @@ impl ValidatorState {
             let slot = self.consensus.get_slot(current)?;
             // sigma1
             let prop_sigma1 = lf.public_inputs[constants::PI_SIGMA1_INDEX];
-            if slot.sigma1 != prop_sigma1 {
+            if slot.pid.sigma1 != prop_sigma1 {
                 error!(
                     target: "consensus::validator",
                     "receive_proposal(): Failed to verify public value sigma1: {:?}, to proposed: {:?}",
-                    slot.sigma1, prop_sigma1
+                    slot.pid.sigma1, prop_sigma1
                 );
             }
             // sigma2
             let prop_sigma2 = lf.public_inputs[constants::PI_SIGMA2_INDEX];
-            if slot.sigma2 != prop_sigma2 {
+            if slot.pid.sigma2 != prop_sigma2 {
                 error!(
                     target: "consensus::validator",
                     "receive_proposal(): Failed to verify public value sigma2: {:?}, to proposed: {:?}",
-                    slot.sigma2, prop_sigma2
+                    slot.pid.sigma2, prop_sigma2
                 );
             }
         }

+ 4 - 4
src/contract/consensus/src/client/proposal_v1.rs

@@ -175,7 +175,7 @@ impl ConsensusProposalCallBuilder {
 
         info!("Building Consensus::ProposalV1 VRF proof");
         let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
-        vrf_input.extend_from_slice(&self.slot.previous_eta.to_repr());
+        vrf_input.extend_from_slice(&self.slot.previous.eta.to_repr());
         vrf_input.extend_from_slice(self.fork_previous_hash.as_bytes());
         vrf_input.extend_from_slice(&pallas::Base::from(self.slot.id).to_repr());
         let vrf_proof = VrfProof::prove(input.secret, &vrf_input, &mut OsRng);
@@ -264,7 +264,7 @@ fn create_proposal_proof(
     // Verify coin is the slot block producer
     let value_pallas = pallas::Base::from(input.note.value);
     let shifted_target =
-        slot.sigma1 * value_pallas + slot.sigma2 * value_pallas * value_pallas + HEADSTART;
+        slot.pid.sigma1 * value_pallas + slot.pid.sigma2 * value_pallas * value_pallas + HEADSTART;
 
     if y >= shifted_target {
         error!("MU_Y: {:?}", mu_y);
@@ -330,8 +330,8 @@ fn create_proposal_proof(
         y,
         mu_rho,
         rho,
-        sigma1: slot.sigma1,
-        sigma2: slot.sigma2,
+        sigma1: slot.pid.sigma1,
+        sigma2: slot.pid.sigma2,
         headstart: HEADSTART,
     };
 

+ 4 - 4
src/contract/consensus/src/entrypoint/proposal_v1.rs

@@ -78,7 +78,7 @@ pub(crate) fn consensus_proposal_get_metadata_v1(
     let slot_fp = pallas::Base::from(slot.id);
 
     // Verify proposal extends a known fork
-    if !slot.fork_hashes.contains(&params.fork_hash) {
+    if !slot.previous.last_hashes.contains(&params.fork_hash) {
         msg!("[ConsensusProposalV1] Error: Proposal extends unknown fork {}", params.fork_hash);
         return Err(ConsensusError::ProposalExtendsUnknownFork.into())
     }
@@ -86,7 +86,7 @@ pub(crate) fn consensus_proposal_get_metadata_v1(
     // TODO: Add fork rank check using params.fork_hash
 
     // Verify sequence is correct
-    if !slot.fork_previous_hashes.contains(&params.fork_previous_hash) {
+    if !slot.previous.second_to_last_hashes.contains(&params.fork_previous_hash) {
         let fork_prev = &params.fork_previous_hash;
         msg!("[ConsensusProposalV1] Error: Proposal extends unknown fork {}", fork_prev);
         return Err(ConsensusError::ProposalExtendsUnknownFork.into())
@@ -94,7 +94,7 @@ pub(crate) fn consensus_proposal_get_metadata_v1(
 
     // Construct VRF input
     let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
-    vrf_input.extend_from_slice(&slot.previous_eta.to_repr());
+    vrf_input.extend_from_slice(&slot.previous.eta.to_repr());
     vrf_input.extend_from_slice(params.fork_previous_hash.as_bytes());
     vrf_input.extend_from_slice(&slot_fp.to_repr());
 
@@ -114,7 +114,7 @@ pub(crate) fn consensus_proposal_get_metadata_v1(
     let mu_rho = poseidon_hash([MU_RHO_PREFIX, eta, slot_fp]);
 
     // Grab sigmas from slot
-    let (sigma1, sigma2) = (slot.sigma1, slot.sigma2);
+    let (sigma1, sigma2) = (slot.pid.sigma1, slot.pid.sigma2);
 
     zk_public_inputs.push((
         CONSENSUS_CONTRACT_ZKAS_PROPOSAL_NS_V1.to_string(),

+ 6 - 10
src/contract/test-harness/src/lib.rs

@@ -37,7 +37,7 @@ use darkfi_money_contract::{
     model::{ConsensusOutput, Output},
 };
 use darkfi_sdk::{
-    blockchain::Slot,
+    blockchain::{PidOutput, PreviousSlot, Slot},
     bridgetree,
     crypto::{
         pasta_prelude::Field, poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey,
@@ -456,19 +456,15 @@ impl TestHarness {
         // using same consensus parameters
         let genesis_block = self.genesis_block;
         let genesis_slot = self.get_slot_by_slot(0).await?;
-        let slot = Slot::new(
-            id,
-            genesis_slot.previous_eta,
+        let previous = PreviousSlot::new(
+            0,
             vec![genesis_block],
             vec![genesis_block],
+            genesis_slot.previous.eta,
             0.0,
-            0.0,
-            0.0,
-            0,
-            0,
-            genesis_slot.sigma1,
-            genesis_slot.sigma2,
         );
+        let pid = PidOutput::new(0.0, 0.0, genesis_slot.pid.sigma1, genesis_slot.pid.sigma2);
+        let slot = Slot::new(id, previous, pid, 0, 0);
 
         // Store generated slot
         for wallet in self.holders.values() {

+ 71 - 53
src/sdk/src/blockchain.rs

@@ -19,81 +19,99 @@
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use pasta_curves::{group::ff::Field, pallas};
 
-/// Auxiliary structure used to keep track of slot validation parameters.
+/// Auxiliary structure used to keep track of slots' previous slot
+/// relevant validation parameters.
 #[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
-pub struct Slot {
-    /// Slot UID
-    pub id: u64,
-    /// Previous slot eta
-    pub previous_eta: pallas::Base,
-    /// Previous slot forks last proposal/block hashes,
+pub struct PreviousSlot {
+    /// Block producers count
+    pub producers: u64,
+    /// Existing forks last proposal/block hashes,
     /// as observed by the validator
-    pub fork_hashes: Vec<blake3::Hash>,
-    /// Previous slot second to last proposal/block hashes,
+    pub last_hashes: Vec<blake3::Hash>,
+    /// Existing forks second to last proposal/block hashes,
     /// as observed by the validator
-    pub fork_previous_hashes: Vec<blake3::Hash>,
-    /// Slot inverse probability `f` of becoming a block producer
+    pub second_to_last_hashes: Vec<blake3::Hash>,
+    /// Slot eta
+    pub eta: pallas::Base,
+    /// Feedback error
+    pub error: f64,
+}
+
+impl PreviousSlot {
+    pub fn new(
+        producers: u64,
+        last_hashes: Vec<blake3::Hash>,
+        second_to_last_hashes: Vec<blake3::Hash>,
+        eta: pallas::Base,
+        error: f64,
+    ) -> Self {
+        Self { producers, last_hashes, second_to_last_hashes, eta, error }
+    }
+}
+
+impl Default for PreviousSlot {
+    /// Represents the genesis slot previous slot on current timestamp
+    fn default() -> Self {
+        Self::new(0, vec![], vec![], pallas::Base::ZERO, 0.0)
+    }
+}
+
+/// Auxiliary structure used to keep track of slot PID output.
+#[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct PidOutput {
+    /// Inverse probability `f` of becoming a block producer
     pub f: f64,
-    /// Slot feedback error
+    /// Feedback error
     pub error: f64,
-    /// Previous slot feedback error
-    pub previous_slot_error: f64,
-    /// Total tokens up until this slot
-    pub total_tokens: u64,
-    /// Slot reward
-    pub reward: u64,
     /// Slot sigma1
     pub sigma1: pallas::Base,
     /// Slot sigma2
     pub sigma2: pallas::Base,
 }
 
+impl PidOutput {
+    pub fn new(f: f64, error: f64, sigma1: pallas::Base, sigma2: pallas::Base) -> Self {
+        Self { f, error, sigma1, sigma2 }
+    }
+}
+
+impl Default for PidOutput {
+    /// Represents the genesis slot PID output on current timestamp
+    fn default() -> Self {
+        Self::new(0.0, 0.0, pallas::Base::ZERO, pallas::Base::ZERO)
+    }
+}
+
+/// Auxiliary structure used to keep track of slot validation parameters.
+#[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct Slot {
+    /// Slot UID
+    pub id: u64,
+    /// Previous slot information
+    pub previous: PreviousSlot,
+    /// Slot PID output
+    pub pid: PidOutput,
+    /// Total tokens up until this slot
+    pub total_tokens: u64,
+    /// Slot reward
+    pub reward: u64,
+}
+
 impl Slot {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         id: u64,
-        previous_eta: pallas::Base,
-        fork_hashes: Vec<blake3::Hash>,
-        fork_previous_hashes: Vec<blake3::Hash>,
-        f: f64,
-        error: f64,
-        previous_slot_error: f64,
+        previous: PreviousSlot,
+        pid: PidOutput,
         total_tokens: u64,
         reward: u64,
-        sigma1: pallas::Base,
-        sigma2: pallas::Base,
     ) -> Self {
-        Self {
-            id,
-            previous_eta,
-            fork_hashes,
-            fork_previous_hashes,
-            f,
-            error,
-            previous_slot_error,
-            total_tokens,
-            reward,
-            sigma1,
-            sigma2,
-        }
+        Self { id, previous, pid, total_tokens, reward }
     }
 }
 
 impl Default for Slot {
     /// Represents the genesis slot on current timestamp
     fn default() -> Self {
-        Self::new(
-            0,
-            pallas::Base::ZERO,
-            vec![],
-            vec![],
-            0.0,
-            0.0,
-            0.0,
-            0,
-            0,
-            pallas::Base::ZERO,
-            pallas::Base::ZERO,
-        )
+        Self::new(0, PreviousSlot::default(), PidOutput::default(), 0, 0)
     }
 }

+ 10 - 9
src/validator/consensus/pid.rs

@@ -39,8 +39,11 @@ lazy_static! {
 /// 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);
+pub fn slot_pid_output(
+    previous_slot: &Slot,
+    previous_producers: u64,
+) -> (f64, f64, pallas::Base, pallas::Base) {
+    let (f, error) = calculate_f(previous_slot, previous_producers);
     let total_tokens =
         Float10::try_from(previous_slot.total_tokens + previous_slot.reward).unwrap();
     let (sigma1, sigma2) = calculate_sigmas(f.clone(), total_tokens);
@@ -52,22 +55,20 @@ pub fn slot_pid_output(previous_slot: &Slot) -> (f64, f64, pallas::Base, pallas:
 
 /// Calculate the inverse probability `f` of becoming a block producer (winning the lottery)
 /// having all the tokens, and the feedback error, represented as Float10.
-fn calculate_f(previous_slot: &Slot) -> (Float10, Float10) {
+fn calculate_f(previous_slot: &Slot, previous_producers: u64) -> (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();
     let k3 = KD.clone();
 
     // Convert slot values to Float10
-    let previous_slot_f = Float10::try_from(previous_slot.f).unwrap();
-    let previous_slot_error = Float10::try_from(previous_slot.error).unwrap();
+    let previous_slot_f = Float10::try_from(previous_slot.pid.f).unwrap();
+    let previous_slot_error = Float10::try_from(previous_slot.pid.error).unwrap();
     let previous_slot_previous_slot_error =
-        Float10::try_from(previous_slot.previous_slot_error).unwrap();
+        Float10::try_from(previous_slot.previous.error).unwrap();
 
     // Calculate feedback error based on previous block producers.
-    // We know how many producers existed in previous slot by
-    // the len of its fork hashes.
-    let feedback = Float10::try_from(previous_slot.fork_hashes.len() as u64).unwrap();
+    let feedback = Float10::try_from(previous_producers).unwrap();
     let error = FLOAT10_ONE.clone() - feedback;
 
     // Calculate f