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

consensus: SlotCheckpoint struct created, blockchain: SlotCheckpointStore created

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

+ 1 - 1
src/blockchain/blockstore.rs

@@ -40,8 +40,8 @@ impl HeaderStore {
         let store = Self(tree);
 
         // In case the store is empty, initialize it with the genesis header.
-        let genesis_header = Header::genesis_header(genesis_ts, genesis_data);
         if store.0.is_empty() {
+            let genesis_header = Header::genesis_header(genesis_ts, genesis_data);
             store.insert(&[genesis_header])?;
         }
 

+ 14 - 1
src/blockchain/mod.rs

@@ -20,7 +20,7 @@ use darkfi_serial::serialize;
 use log::debug;
 
 use crate::{
-    consensus::{Block, BlockInfo},
+    consensus::{Block, BlockInfo, SlotCheckpoint},
     util::time::Timestamp,
     Error, Result,
 };
@@ -28,6 +28,9 @@ use crate::{
 pub mod blockstore;
 pub use blockstore::{BlockOrderStore, BlockStore, HeaderStore};
 
+pub mod slotcheckpointstore;
+pub use slotcheckpointstore::SlotCheckpointStore;
+
 pub mod nfstore;
 pub use nfstore::NullifierStore;
 
@@ -51,6 +54,8 @@ pub struct Blockchain {
     pub blocks: BlockStore,
     /// Block order sled tree
     pub order: BlockOrderStore,
+    /// Slot checkpoints sled tree
+    pub slot_checkpoints: SlotCheckpointStore,
     /// Transactions sled tree
     pub transactions: TxStore,
     /// Nullifiers sled tree
@@ -69,6 +74,7 @@ impl Blockchain {
         let headers = HeaderStore::new(db, genesis_ts, genesis_data)?;
         let blocks = BlockStore::new(db, genesis_ts, genesis_data)?;
         let order = BlockOrderStore::new(db, genesis_ts, genesis_data)?;
+        let slot_checkpoints = SlotCheckpointStore::new(db)?;
         let transactions = TxStore::new(db)?;
         let nullifiers = NullifierStore::new(db)?;
         let merkle_roots = RootStore::new(db)?;
@@ -80,6 +86,7 @@ impl Blockchain {
             headers,
             blocks,
             order,
+            slot_checkpoints,
             transactions,
             nullifiers,
             merkle_roots,
@@ -211,4 +218,10 @@ impl Blockchain {
         let block = blocks[0].clone().unwrap();
         Ok((slot, block.lead_info.offset))
     }
+
+    /// Retrieve n checkpoints after given start slot.
+    pub fn get_slot_checkpoints_after(&self, slot: u64, n: u64) -> Result<Vec<SlotCheckpoint>> {
+        debug!("get_slot_checkpoints_after(): {} -> {}", slot, n);
+        self.slot_checkpoints.get_after(slot, n)
+    }
 }

+ 130 - 0
src/blockchain/slotcheckpointstore.rs

@@ -0,0 +1,130 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2022 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_serial::{deserialize, serialize};
+
+use crate::{consensus::SlotCheckpoint, Error, Result};
+
+const SLED_SLOT_CHECKPOINT_TREE: &[u8] = b"_slot_checkpoints";
+
+/// The `SlotCheckpointStore` is a `sled` tree storing the checkpoints of the
+/// blockchain's slots, where the key is the slot uid, and the value is
+/// is the serialized checkpoint.
+#[derive(Clone)]
+pub struct SlotCheckpointStore(sled::Tree);
+
+impl SlotCheckpointStore {
+    /// Opens a new or existing `SlotCheckpointStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_SLOT_CHECKPOINT_TREE)?;
+        let store = Self(tree);
+
+        // In case the store is empty, initialize it with the genesis checkpoint.
+        if store.0.is_empty() {
+            let genesis_checkpoint = SlotCheckpoint::genesis_slot_checkpoint();
+            store.insert(&[genesis_checkpoint])?;
+        }
+
+        Ok(store)
+    }
+
+    /// Insert a slice of [`SlotCheckpoint`] into the slotcheckpointstore.
+    /// With sled, the operation is done as a batch.
+    /// The block slot is used as the key, while value is the serialized [`SlotCheckpoint`] itself.
+    pub fn insert(&self, checkpoints: &[SlotCheckpoint]) -> Result<()> {
+        let mut batch = sled::Batch::default();
+
+        for checkpoint in checkpoints {
+            let serialized = serialize(checkpoint);
+            batch.insert(&checkpoint.slot.to_be_bytes(), serialized);
+        }
+
+        self.0.apply_batch(batch)?;
+        Ok(())
+    }
+
+    /// Check if the slotcheckpointstore contains a given slot.
+    pub fn contains(&self, slot: u64) -> Result<bool> {
+        Ok(self.0.contains_key(slot.to_be_bytes())?)
+    }
+
+    /// Fetch given slots from the slotcheckpointstore.
+    /// The resulting vector contains `Option`, which is `Some` if the slot
+    /// was found in the slotcheckpointstore, and otherwise it is `None`, if it has not.
+    /// The second parameter is a boolean which tells the function to fail in
+    /// case at least one slot was not found.
+    pub fn get(&self, slots: &[u64], strict: bool) -> Result<Vec<Option<SlotCheckpoint>>> {
+        let mut ret = Vec::with_capacity(slots.len());
+
+        for slot in slots {
+            if let Some(found) = self.0.get(slot.to_be_bytes())? {
+                let checkpoint = deserialize(&found)?;
+                ret.push(Some(checkpoint));
+            } else {
+                if strict {
+                    return Err(Error::SlotNotFound(*slot))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve all slot checkpointss from the slotcheckpointstore.
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<SlotCheckpoint>> {
+        let mut slots = vec![];
+
+        for slot in self.0.iter() {
+            let (_, value) = slot.unwrap();
+            let checkpoint = deserialize(&value)?;
+            slots.push(checkpoint);
+        }
+
+        Ok(slots)
+    }
+
+    /// Fetch n slot checkpoints after given slot. In the iteration, if a slot is not
+    /// found, the iteration stops and the function returns what it has found
+    /// so far in the `SlotCheckpointStore`.
+    pub fn get_after(&self, slot: u64, n: u64) -> Result<Vec<SlotCheckpoint>> {
+        let mut ret = vec![];
+
+        let mut key = slot;
+        let mut counter = 0;
+        while counter <= n {
+            if let Some(found) = self.0.get_gt(key.to_be_bytes())? {
+                let key_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
+                key = u64::from_be_bytes(key_bytes);
+                let checkpoint = deserialize(&found.1)?;
+                ret.push(checkpoint);
+                counter += 1;
+                continue
+            }
+            break
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve records count
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+}

+ 1 - 0
src/consensus/mod.rs

@@ -29,6 +29,7 @@ pub use lead_info::{LeadInfo, LeadProof};
 
 /// Consensus state
 pub mod state;
+pub use state::SlotCheckpoint;
 
 /// Consensus validator state
 pub mod validator;

+ 28 - 0
src/consensus/state.rs

@@ -120,3 +120,31 @@ impl net::Message for ConsensusResponse {
         "consensusresponse"
     }
 }
+
+/// Auxiliary structure used to keep track of slot validation parameters.
+#[derive(Debug, SerialEncodable, SerialDecodable)]
+pub struct SlotCheckpoint {
+    /// Slot UID
+    pub slot: u64,
+    /// Slot eta
+    pub eta: pallas::Base,
+    /// Slot sigma1
+    pub sigma1: pallas::Base,
+    /// Slot sigma2
+    pub sigma2: pallas::Base,
+}
+
+impl SlotCheckpoint {
+    pub fn new(slot: u64, eta: pallas::Base, sigma1: pallas::Base, sigma2: pallas::Base) -> Self {
+        Self { slot, eta, sigma1, sigma2 }
+    }
+
+    /// Generate the genesis slot checkpoint.
+    pub fn genesis_slot_checkpoint() -> Self {
+        let eta = pallas::Base::zero();
+        let sigma1 = pallas::Base::zero();
+        let sigma2 = pallas::Base::zero();
+
+        Self::new(0, eta, sigma1, sigma2)
+    }
+}