|
@@ -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()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|