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

blockchain: Implement initial blockstore API.

parazyd 4 лет назад
Родитель
Сommit
4a4b40d7be
3 измененных файлов с 127 добавлено и 1 удалено
  1. 3 0
      Cargo.toml
  2. 99 1
      src/blockchain2/blockstore.rs
  3. 25 0
      src/blockchain2/mod.rs

+ 3 - 0
Cargo.toml

@@ -201,7 +201,10 @@ blockchain = [
 ]
 
 blockchain2 = [
+    "blake3",
     "sled",
+
+    "util",
 ]
 
 system = [

+ 99 - 1
src/blockchain2/blockstore.rs

@@ -1,12 +1,110 @@
-use crate::Result;
+use crate::{
+    util::serial::{deserialize, serialize, SerialDecodable, SerialEncodable},
+    Result,
+};
 
 const SLED_BLOCK_TREE: &[u8] = b"_blocks";
 
+// TODO: The block structure should be as follows
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct Block {
+    /// Previous block hash (blake3)
+    pub st: blake3::Hash,
+    /// Slot UID, generated by the beacon
+    pub sl: u64,
+    /// Transaction hashes (blake3)
+    /// The actual transactions are in [`TxStore`]
+    pub txs: Vec<blake3::Hash>,
+    /// Additional block information
+    pub metadata: String,
+}
+
 pub struct BlockStore(sled::Tree);
 
 impl BlockStore {
+    /// Opens a new or existing blockstore tree given a sled database.
     pub fn new(db: &sled::Db) -> Result<Self> {
         let tree = db.open_tree(SLED_BLOCK_TREE)?;
         Ok(Self(tree))
     }
+
+    /// Insert a vector of [`Block`] into the blockstore.
+    /// The blocks are hashed with blake3 and this blockhash is used as
+    // the key, where value is the serialized block itself.
+    pub fn insert(&self, blocks: Vec<Block>) -> Result<()> {
+        for i in &blocks {
+            let serialized = serialize(i);
+            let blockhash = blake3::hash(&serialized);
+            self.0.insert(blockhash.as_bytes(), serialized)?;
+        }
+
+        Ok(())
+    }
+
+    /// Fetch given blockhashes from the blockstore.
+    /// The resulting vector contains `Option` which is `Some` if the block
+    /// was found in the blockstore, and `None`, if it has not.
+    pub fn get(&self, blockhashes: Vec<blake3::Hash>) -> Result<Vec<Option<Block>>> {
+        let mut ret: Vec<Option<Block>> = Vec::with_capacity(blockhashes.len());
+
+        for i in &blockhashes {
+            if let Some(found) = self.0.get(i.as_bytes())? {
+                let block = deserialize(&found)?;
+                ret.push(Some(block));
+            } else {
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Check if the blockstore contains a given blockhash.
+    pub fn contains(&self, blockhash: blake3::Hash) -> Result<bool> {
+        Ok(self.0.contains_key(blockhash.as_bytes())?)
+    }
+
+    /// Fetch the first (oldest) block in the tree.
+    pub fn get_first(&self) -> Result<Option<(blake3::Hash, Block)>> {
+        if let Some(found) = self.0.first()? {
+            let hash_bytes: [u8; 32] = found.0.as_ref().try_into().unwrap();
+            let block = deserialize(&found.1)?;
+            return Ok(Some((hash_bytes.into(), block)))
+        }
+
+        Ok(None)
+    }
+
+    /// Fetch the last (newest) block in the tree.
+    pub fn get_last(&self) -> Result<Option<(blake3::Hash, Block)>> {
+        if let Some(found) = self.0.last()? {
+            let hash_bytes: [u8; 32] = found.0.as_ref().try_into().unwrap();
+            let block = deserialize(&found.1)?;
+            return Ok(Some((hash_bytes.into(), block)))
+        }
+
+        Ok(None)
+    }
+
+    /// Fetch the block and its hash before the provided blockhash, if one exists.
+    pub fn get_lt(&self, blockhash: blake3::Hash) -> Result<Option<(blake3::Hash, Block)>> {
+        if let Some(found) = self.0.get_lt(blockhash.as_bytes())? {
+            let hash_bytes: [u8; 32] = found.0.as_ref().try_into().unwrap();
+            let block = deserialize(&found.1)?;
+            return Ok(Some((hash_bytes.into(), block)))
+        }
+
+        Ok(None)
+    }
+
+    /// Fetch the block and its hash after the provided blockhash, if one exists.
+    pub fn get_gt(&self, blockhash: blake3::Hash) -> Result<Option<(blake3::Hash, Block)>> {
+        if let Some(found) = self.0.get_gt(blockhash.as_bytes())? {
+            let hash_bytes: [u8; 32] = found.0.as_ref().try_into().unwrap();
+            let block = deserialize(&found.1)?;
+            return Ok(Some((hash_bytes.into(), block)))
+        }
+
+        Ok(None)
+    }
 }

+ 25 - 0
src/blockchain2/mod.rs

@@ -1,4 +1,29 @@
+use std::io;
+
+use crate::{
+    impl_vec,
+    util::serial::{Decodable, Encodable, ReadExt, VarInt, WriteExt},
+    Result,
+};
+
 pub mod blockstore;
 pub mod nfstore;
 pub mod rootstore;
 pub mod txstore;
+
+impl Encodable for blake3::Hash {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        s.write_slice(self.as_bytes())?;
+        Ok(32)
+    }
+}
+
+impl Decodable for blake3::Hash {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let mut bytes = [0u8; 32];
+        d.read_slice(&mut bytes)?;
+        Ok(bytes.into())
+    }
+}
+
+impl_vec!(blake3::Hash);