From 4a4b40d7be4a9820f9955eee4278fc774e360879 Mon Sep 17 00:00:00 2001 From: parazyd Date: Sun, 3 Apr 2022 15:24:56 +0200 Subject: [PATCH] blockchain: Implement initial blockstore API. --- Cargo.toml | 3 + src/blockchain2/blockstore.rs | 100 +++++++++++++++++++++++++++++++++- src/blockchain2/mod.rs | 25 +++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 72064bab4..5679edcd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,7 +201,10 @@ blockchain = [ ] blockchain2 = [ + "blake3", "sled", + + "util", ] system = [ diff --git a/src/blockchain2/blockstore.rs b/src/blockchain2/blockstore.rs index 16cde628d..2a6cac262 100644 --- a/src/blockchain2/blockstore.rs +++ b/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, + /// 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 { 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) -> 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) -> Result>> { + let mut ret: Vec> = 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 { + Ok(self.0.contains_key(blockhash.as_bytes())?) + } + + /// Fetch the first (oldest) block in the tree. + pub fn get_first(&self) -> Result> { + 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> { + 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> { + 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> { + 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) + } } diff --git a/src/blockchain2/mod.rs b/src/blockchain2/mod.rs index 8900a0620..48e41521f 100644 --- a/src/blockchain2/mod.rs +++ b/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(&self, mut s: S) -> Result { + s.write_slice(self.as_bytes())?; + Ok(32) + } +} + +impl Decodable for blake3::Hash { + fn decode(mut d: D) -> Result { + let mut bytes = [0u8; 32]; + d.read_slice(&mut bytes)?; + Ok(bytes.into()) + } +} + +impl_vec!(blake3::Hash);