Explorar o código

WIP: validator: blockchain rewrite foundation added

aggstam %!s(int64=3) %!d(string=hai) anos
pai
achega
7a2f07502c

+ 10 - 1
src/error.rs

@@ -294,12 +294,21 @@ pub enum Error {
     #[error("Transaction {0} not found in database")]
     #[error("Transaction {0} not found in database")]
     TransactionNotFound(String),
     TransactionNotFound(String),
 
 
-    #[error("Transactioon already seen")]
+    #[error("Transaction already seen")]
     TransactionAlreadySeen,
     TransactionAlreadySeen,
 
 
+    #[error("Input vectors have different length")]
+    InvalidInputLengths,
+
     #[error("Header {0} not found in database")]
     #[error("Header {0} not found in database")]
     HeaderNotFound(String),
     HeaderNotFound(String),
 
 
+    #[error("Block {0} is invalid")]
+    BlockIsInvalid(String),
+
+    #[error("Block {0} already in database")]
+    BlockAlreadyExists(String),
+
     #[error("Block {0} not found in database")]
     #[error("Block {0} not found in database")]
     BlockNotFound(String),
     BlockNotFound(String),
 
 

+ 0 - 1
src/sdk/src/lib.rs

@@ -23,7 +23,6 @@ pub use pasta_curves as pasta;
 
 
 /// Blockchain structures
 /// Blockchain structures
 pub mod blockchain;
 pub mod blockchain;
-pub use blockchain::Slot;
 
 
 /// Database functions
 /// Database functions
 pub mod db;
 pub mod db;

+ 1 - 1
src/tx/mod.rs

@@ -46,7 +46,7 @@ macro_rules! zip {
 // ANCHOR: transaction
 // ANCHOR: transaction
 /// A Transaction contains an arbitrary number of `ContractCall` objects,
 /// A Transaction contains an arbitrary number of `ContractCall` objects,
 /// along with corresponding ZK proofs and Schnorr signatures.
 /// along with corresponding ZK proofs and Schnorr signatures.
-#[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, Default, Eq, PartialEq, SerialEncodable, SerialDecodable)]
 pub struct Transaction {
 pub struct Transaction {
     /// Calls executed in this transaction
     /// Calls executed in this transaction
     pub calls: Vec<ContractCall>,
     pub calls: Vec<ContractCall>,

+ 387 - 0
src/validator/blockchain/block_store.rs

@@ -0,0 +1,387 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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_sdk::{blockchain::Slot, crypto::schnorr::Signature};
+use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
+
+use crate::{tx::Transaction, util::time::Timestamp, Error, Result};
+
+use super::Header;
+
+/// Block version number
+pub const BLOCK_VERSION: u8 = 1;
+
+/// Block magic bytes
+const BLOCK_MAGIC_BYTES: [u8; 4] = [0x11, 0x6d, 0x75, 0x1f];
+
+/// This struct represents a tuple of the form (`magic`, `header`, `txs`, `producer`, `slots`).
+/// The header and transactions are stored as hashes, while slots are stored as integers,
+/// serving as pointers to the actual data in the sled database.
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct Block {
+    /// Block magic bytes
+    pub magic: [u8; 4],
+    /// Block header
+    pub header: blake3::Hash,
+    /// Trasaction hashes
+    pub txs: Vec<blake3::Hash>,
+    /// Block producer info
+    pub producer: BlockProducer,
+    /// Slots up until this block
+    pub slots: Vec<u64>,
+}
+
+impl Block {
+    pub fn new(
+        header: blake3::Hash,
+        txs: Vec<blake3::Hash>,
+        producer: BlockProducer,
+        slots: Vec<u64>,
+    ) -> Self {
+        let magic = BLOCK_MAGIC_BYTES;
+        Self { magic, header, txs, producer, slots }
+    }
+
+    /// Generate the genesis block.
+    pub fn genesis_block(genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Self {
+        let magic = BLOCK_MAGIC_BYTES;
+        let header = Header::genesis_header(genesis_ts, genesis_data);
+        let header = header.headerhash();
+        let producer = BlockProducer::default();
+        Self { magic, header, txs: vec![], producer, slots: vec![] }
+    }
+
+    /// Calculate the block hash
+    pub fn blockhash(&self) -> blake3::Hash {
+        blake3::hash(&serialize(self))
+    }
+}
+
+/// Structure representing full block data.
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct BlockInfo {
+    /// BlockInfo magic bytes
+    pub magic: [u8; 4],
+    /// Block header data
+    pub header: Header,
+    /// Transactions payload
+    pub txs: Vec<Transaction>,
+    /// Block producer info
+    pub producer: BlockProducer,
+    /// Slots payload
+    pub slots: Vec<Slot>,
+}
+
+impl Default for BlockInfo {
+    /// Represents the genesis block on current timestamp
+    fn default() -> Self {
+        let magic = BLOCK_MAGIC_BYTES;
+        Self {
+            magic,
+            header: Header::default(),
+            txs: vec![],
+            producer: BlockProducer::default(),
+            slots: vec![],
+        }
+    }
+}
+
+impl BlockInfo {
+    pub fn new(
+        header: Header,
+        txs: Vec<Transaction>,
+        producer: BlockProducer,
+        slots: Vec<Slot>,
+    ) -> Self {
+        let magic = BLOCK_MAGIC_BYTES;
+        Self { magic, header, txs, producer, slots }
+    }
+
+    /// Calculate the block hash
+    pub fn blockhash(&self) -> blake3::Hash {
+        let block: Block = self.clone().into();
+        block.blockhash()
+    }
+}
+
+impl From<BlockInfo> for Block {
+    fn from(block_info: BlockInfo) -> Self {
+        let txs = block_info.txs.iter().map(|x| blake3::hash(&serialize(x))).collect();
+        let slots = block_info.slots.iter().map(|x| x.id).collect();
+        Self {
+            magic: block_info.magic,
+            header: block_info.header.headerhash(),
+            txs,
+            producer: block_info.producer,
+            slots,
+        }
+    }
+}
+
+/// [`Block`] sled tree
+const SLED_BLOCK_TREE: &[u8] = b"_blocks";
+
+/// The `BlockStore` is a `sled` tree storing all the blockchain's blocks
+/// where the key is the blocks' hash, and value is the serialized block.
+#[derive(Clone)]
+pub struct BlockStore(pub sled::Tree);
+
+impl BlockStore {
+    /// Opens a new or existing `BlockStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_BLOCK_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of [`Block`] into the store.
+    pub fn insert(&self, blocks: &[Block]) -> Result<Vec<blake3::Hash>> {
+        let (batch, ret) = self.insert_batch(blocks)?;
+        self.0.apply_batch(batch)?;
+        Ok(ret)
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    /// The blocks are hashed with BLAKE3 and this blockhash is used as
+    /// the key, while value is the serialized [`Block`] itself.
+    /// On success, the function returns the block hashes in the same order.
+    pub fn insert_batch(&self, blocks: &[Block]) -> Result<(sled::Batch, Vec<blake3::Hash>)> {
+        let mut ret = Vec::with_capacity(blocks.len());
+        let mut batch = sled::Batch::default();
+
+        for block in blocks {
+            let serialized = serialize(block);
+            let blockhash = blake3::hash(&serialized);
+            batch.insert(blockhash.as_bytes(), serialized);
+            ret.push(blockhash);
+        }
+
+        Ok((batch, 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 given block hashes from the blockstore.
+    /// The resulting vector contains `Option`, which is `Some` if the block
+    /// was found in the blockstore, 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 block was not found.
+    pub fn get(&self, block_hashes: &[blake3::Hash], strict: bool) -> Result<Vec<Option<Block>>> {
+        let mut ret = Vec::with_capacity(block_hashes.len());
+
+        for hash in block_hashes {
+            if let Some(found) = self.0.get(hash.as_bytes())? {
+                let block = deserialize(&found)?;
+                ret.push(Some(block));
+            } else {
+                if strict {
+                    let s = hash.to_hex().as_str().to_string();
+                    return Err(Error::BlockNotFound(s))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve all blocks from the blockstore in the form of a tuple
+    /// (`blockhash`, `block`).
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<(blake3::Hash, Block)>> {
+        let mut blocks = vec![];
+
+        for block in self.0.iter() {
+            let (key, value) = block.unwrap();
+            let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
+            let block = deserialize(&value)?;
+            blocks.push((hash_bytes.into(), block));
+        }
+
+        Ok(blocks)
+    }
+}
+
+/// Auxiliary structure used to keep track of blocks order.
+#[derive(Debug, SerialEncodable, SerialDecodable)]
+pub struct BlockOrder {
+    /// Slot UID
+    pub slot: u64,
+    /// Block headerhash of that slot
+    pub block: blake3::Hash,
+}
+
+/// [`BlockOrder`] sled tree
+const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order";
+
+/// The `BlockOrderStore` is a `sled` tree storing the order of the
+/// blockchain's slots, where the key is the slot uid, and the value is
+/// the blocks' hash. [`BlockStore`] can be queried with this hash.
+#[derive(Clone)]
+pub struct BlockOrderStore(pub sled::Tree);
+
+impl BlockOrderStore {
+    /// Opens a new or existing `BlockOrderStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_BLOCK_ORDER_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of slots and blockhashes into the store.
+    pub fn insert(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
+        let batch = self.insert_batch(slots, hashes)?;
+        self.0.apply_batch(batch)?;
+        Ok(())
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    /// The block slot is used as the key, and the blockhash is used as value.
+    pub fn insert_batch(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<sled::Batch> {
+        if slots.len() != hashes.len() {
+            return Err(Error::InvalidInputLengths)
+        }
+
+        let mut batch = sled::Batch::default();
+
+        for (i, sl) in slots.iter().enumerate() {
+            batch.insert(&sl.to_be_bytes(), hashes[i].as_bytes());
+        }
+
+        Ok(batch)
+    }
+
+    /// Check if the blockorderstore 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 blockorderstore.
+    /// The resulting vector contains `Option`, which is `Some` if the slot
+    /// was found in the blockstore, 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<blake3::Hash>>> {
+        let mut ret = Vec::with_capacity(slots.len());
+
+        for slot in slots {
+            if let Some(found) = self.0.get(slot.to_be_bytes())? {
+                let hash_bytes: [u8; 32] = found.as_ref().try_into().unwrap();
+                let hash = blake3::Hash::from(hash_bytes);
+                ret.push(Some(hash));
+            } else {
+                if strict {
+                    return Err(Error::BlockSlotNotFound(*slot))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve all slots from the blockorderstore in the form of a tuple
+    /// (`slot`, `blockhash`).
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<(u64, blake3::Hash)>> {
+        let mut slots = vec![];
+
+        for slot in self.0.iter() {
+            let (key, value) = slot.unwrap();
+            let slot_bytes: [u8; 8] = key.as_ref().try_into().unwrap();
+            let hash_bytes: [u8; 32] = value.as_ref().try_into().unwrap();
+            let slot = u64::from_be_bytes(slot_bytes);
+            let hash = blake3::Hash::from(hash_bytes);
+            slots.push((slot, hash));
+        }
+
+        Ok(slots)
+    }
+
+    /// Fetch n hashes 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 `BlockOrderStore`.
+    pub fn get_after(&self, slot: u64, n: u64) -> Result<Vec<blake3::Hash>> {
+        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 blockhash = deserialize(&found.1)?;
+                ret.push(blockhash);
+                counter += 1;
+                continue
+            }
+            break
+        }
+
+        Ok(ret)
+    }
+
+    /// Fetch the last blockhash in the tree, based on the `Ord`
+    /// implementation for `Vec<u8>`.
+    pub fn get_last(&self) -> Result<(u64, blake3::Hash)> {
+        let found = self.0.last()?.unwrap();
+
+        let slot_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
+        let hash_bytes: [u8; 32] = found.1.as_ref().try_into().unwrap();
+        let slot = u64::from_be_bytes(slot_bytes);
+        let hash = blake3::Hash::from(hash_bytes);
+
+        Ok((slot, hash))
+    }
+
+    /// Retrieve records count
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    /// Check if sled contains any records
+    pub fn is_empty(&self) -> bool {
+        self.0.len() == 0
+    }
+}
+
+/// This struct represents [`Block`] producer information.
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct BlockProducer {
+    /// Block producer signature
+    pub signature: Signature,
+    /// Proposal transaction
+    pub proposal: Transaction,
+}
+
+impl BlockProducer {
+    pub fn new(signature: Signature, proposal: Transaction) -> Self {
+        Self { signature, proposal }
+    }
+}
+
+impl Default for BlockProducer {
+    fn default() -> Self {
+        let signature = Signature::dummy();
+        let proposal = Transaction::default();
+        Self { signature, proposal }
+    }
+}

+ 287 - 0
src/validator/blockchain/contract_store.rs

@@ -0,0 +1,287 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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.
+ *
+r* 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 std::io::Cursor;
+
+use darkfi_sdk::crypto::ContractId;
+use darkfi_serial::{deserialize, serialize};
+use log::{debug, error};
+
+use crate::{
+    runtime::vm_runtime::SMART_CONTRACT_ZKAS_DB_NAME,
+    zk::{VerifyingKey, ZkCircuit},
+    zkas::ZkBinary,
+    Error, Result,
+};
+
+use super::SledDbOverlayPtr;
+
+const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
+const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode";
+
+/// The `WasmStore` is a `sled` tree that stores the wasm bincode for deployed
+/// contracts.
+#[derive(Clone)]
+pub struct WasmStore(sled::Tree);
+
+impl WasmStore {
+    /// Opens or creates a `WasmStore`. This tree holds the wasm bincode.
+    /// The layout looks like this:
+    /// ```plaintext
+    ///  tree: "_wasm_bincode"
+    ///   key: ContractId
+    /// value: Vec<u8>
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_BINCODE_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Fetches the bincode for a given ContractId
+    /// Returns an error if the bincode is not found.
+    pub fn get(&self, contract_id: ContractId) -> Result<Vec<u8>> {
+        if let Some(bincode) = self.0.get(serialize(&contract_id))? {
+            return Ok(bincode.to_vec())
+        }
+
+        Err(Error::WasmBincodeNotFound)
+    }
+}
+
+/// Overlay structure over a [`WasmStore`] instance.
+pub struct WasmStoreOverlay(SledDbOverlayPtr);
+
+impl WasmStoreOverlay {
+    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree(SLED_BINCODE_TREE)?;
+        Ok(Self(overlay))
+    }
+
+    /// Inserts or replaces the bincode for a given ContractId
+    pub fn insert(&self, contract_id: ContractId, bincode: &[u8]) -> Result<()> {
+        if let Err(e) =
+            self.0.lock().unwrap().insert(SLED_BINCODE_TREE, &serialize(&contract_id), bincode)
+        {
+            error!(target: "blockchain::contractstoreoverlay", "Failed to insert bincode to WasmStore: {}", e);
+            return Err(e.into())
+        }
+
+        Ok(())
+    }
+}
+
+/// The `ContractStateStore` is a `sled` tree that stores pointers to contracts'
+/// databases. See the rustdoc for the impl functions for more info.
+#[derive(Clone)]
+pub struct ContractStateStore(sled::Tree);
+
+impl ContractStateStore {
+    /// Opens or creates a `ContractStateStore`. This main tree holds the links
+    /// of contracts' states.
+    /// The layout looks like this:
+    /// ```plaintext
+    ///  tree: "_contracts"
+    ///   key: ContractId
+    /// value: Vec<blake3(ContractId || tree_name)>
+    /// ```
+    /// These values get mutated with `init()` and `remove()`.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_CONTRACTS_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Do a lookup of an existing contract state. In order to succeed, the
+    /// state must have been previously initialized with `init()`. If the
+    /// state has been found, a handle to it will be returned. Otherwise, we
+    /// return an error.
+    pub fn lookup(
+        &self,
+        db: &sled::Db,
+        contract_id: &ContractId,
+        tree_name: &str,
+    ) -> Result<sled::Tree> {
+        debug!(target: "blockchain::contractstore", "Looking up state tree for {}:{}", contract_id, tree_name);
+
+        let contract_id_bytes = serialize(contract_id);
+        let ptr = contract_id.hash_state_id(tree_name);
+
+        // A guard to make sure we went through init()
+        if !self.0.contains_key(&contract_id_bytes)? {
+            return Err(Error::ContractNotFound(contract_id.to_string()))
+        }
+
+        let state_pointers = self.0.get(&contract_id_bytes)?.unwrap();
+        let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
+
+        // We assume the tree has been created already, so it should be listed
+        // in this array. If not, that's an error.
+        if !state_pointers.contains(&ptr) {
+            return Err(Error::ContractStateNotFound)
+        }
+
+        // We open the tree and return its handle
+        let tree = db.open_tree(ptr)?;
+        Ok(tree)
+    }
+
+    /// Attempt to remove an existing contract state. In order to succeed, the
+    /// state must have been previously initialized with `init()`. If the state
+    /// has been found, its contents in the tree will be cleared, and the pointer
+    /// will be removed from the main `ContractStateStore`. If anything is not
+    /// found as initialized, an error is returned.
+    pub fn remove(&self, db: &sled::Db, contract_id: &ContractId, tree_name: &str) -> Result<()> {
+        debug!(target: "blockchain::contractstore", "Removing state tree for {}:{}", contract_id, tree_name);
+
+        let contract_id_bytes = serialize(contract_id);
+        let ptr = contract_id.hash_state_id(tree_name);
+
+        // A guard to make sure we went through init()
+        if !self.0.contains_key(&contract_id_bytes)? {
+            return Err(Error::ContractNotFound(contract_id.to_string()))
+        }
+
+        let state_pointers = self.0.get(&contract_id_bytes)?.unwrap();
+        let mut state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
+
+        // We assume the tree has been created already, so it should be listed
+        // in this array. If not, that's an error.
+        if !state_pointers.contains(&ptr) {
+            return Err(Error::ContractStateNotFound)
+        }
+
+        // We open the tree and clear it. This is unfortunately not atomic.
+        // TODO: FIXME: Can we make it atomic?
+        let tree = db.open_tree(ptr)?;
+        tree.clear()?;
+
+        // Remove the deleted tree from the state pointer set.
+        state_pointers.retain(|x| *x != ptr);
+        self.0.insert(contract_id_bytes, serialize(&state_pointers))?;
+
+        Ok(())
+    }
+
+    /// Abstraction function for fetching a `ZkBinary` and its respective `VerifyingKey`
+    /// from a contract's zkas sled tree.
+    pub fn get_zkas(
+        &self,
+        db: &sled::Db,
+        contract_id: &ContractId,
+        zkas_ns: &str,
+    ) -> Result<(ZkBinary, VerifyingKey)> {
+        debug!(target: "blockchain::contractstore", "Looking up \"{}:{}\" zkas circuit & vk", contract_id, zkas_ns);
+
+        let zkas_tree = self.lookup(db, contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
+
+        let Some(zkas_bytes) = zkas_tree.get(serialize(&zkas_ns))? else {
+            return Err(Error::ZkasBincodeNotFound)
+        };
+
+        // If anything in this function panics, that means corrupted data managed
+        // to get into this sled tree. This should not be possible.
+        let (zkbin, vkbin): (Vec<u8>, Vec<u8>) = deserialize(&zkas_bytes).unwrap();
+
+        // The first vec is the compiled zkas binary
+        let zkbin = ZkBinary::decode(&zkbin).unwrap();
+
+        // The second one is the serialized VerifyingKey for it
+        let mut vk_buf = Cursor::new(vkbin);
+        let vk = VerifyingKey::read::<Cursor<Vec<u8>>, ZkCircuit>(&mut vk_buf).unwrap();
+
+        Ok((zkbin, vk))
+    }
+}
+
+/// Overlay structure over a [`ContractStateStore`] instance.
+pub struct ContractStateStoreOverlay(SledDbOverlayPtr);
+
+impl ContractStateStoreOverlay {
+    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREE)?;
+        Ok(Self(overlay))
+    }
+
+    /// Try to initialize a new contract state. Contracts can create a number
+    /// of trees, separated by `tree_name`, which they can then use from the
+    /// smart contract API. `init()` will look into the main `ContractStateStoreOverlay`
+    /// tree to check if the smart contract was already deployed, and if so
+    /// it will fetch a vector of these states that were initialized. If the
+    /// state was already found, this function will return an error, because
+    /// in this case the handle should be fetched using `lookup()`.
+    /// If the tree was not initialized previously, it will be appended to
+    /// the main `ContractStateStoreOverlay` tree and a handle to it will be
+    /// returned.
+    pub fn init(&self, contract_id: &ContractId, tree_name: &str) -> Result<[u8; 32]> {
+        debug!(target: "blockchain::contractstoreoverlay", "Initializing state overlay tree for {}:{}", contract_id, tree_name);
+
+        let contract_id_bytes = serialize(contract_id);
+        let ptr = contract_id.hash_state_id(tree_name);
+        let mut lock = self.0.lock().unwrap();
+
+        // See if there are existing state trees.
+        // If not, just start with an empty vector.
+        let mut state_pointers: Vec<[u8; 32]> =
+            if lock.contains_key(SLED_CONTRACTS_TREE, &contract_id_bytes)? {
+                let bytes = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
+                deserialize(&bytes)?
+            } else {
+                vec![]
+            };
+
+        // If the db was never initialized, it should not be in here.
+        if state_pointers.contains(&ptr) {
+            return Err(Error::ContractAlreadyInitialized)
+        }
+
+        // Now we add it so it's marked as initialized and create its tree.
+        state_pointers.push(ptr);
+        lock.insert(SLED_CONTRACTS_TREE, &contract_id_bytes, &serialize(&state_pointers))?;
+        lock.open_tree(&ptr)?;
+
+        Ok(ptr)
+    }
+
+    /// Do a lookup of an existing contract state. In order to succeed, the
+    /// state must have been previously initialized with `init()`. If the
+    /// state has been found, a handle to it will be returned. Otherwise, we
+    /// return an error.
+    pub fn lookup(&self, contract_id: &ContractId, tree_name: &str) -> Result<[u8; 32]> {
+        debug!(target: "blockchain::contractstoreoverlay", "Looking up state tree for {}:{}", contract_id, tree_name);
+
+        let contract_id_bytes = serialize(contract_id);
+        let ptr = contract_id.hash_state_id(tree_name);
+        let mut lock = self.0.lock().unwrap();
+
+        // A guard to make sure we went through init()
+        if !lock.contains_key(SLED_CONTRACTS_TREE, &contract_id_bytes)? {
+            return Err(Error::ContractNotFound(contract_id.to_string()))
+        }
+
+        let state_pointers = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
+        let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
+
+        // We assume the tree has been created already, so it should be listed
+        // in this array. If not, that's an error.
+        if !state_pointers.contains(&ptr) {
+            return Err(Error::ContractStateNotFound)
+        }
+
+        // We open the tree and return its handle
+        lock.open_tree(&ptr)?;
+        Ok(ptr)
+    }
+}

+ 166 - 0
src/validator/blockchain/header_store.rs

@@ -0,0 +1,166 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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_sdk::crypto::{MerkleNode, MerkleTree};
+use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
+
+use crate::{util::time::Timestamp, Error, Result};
+
+use super::block_store::BLOCK_VERSION;
+
+/// This struct represents a tuple of the form (version, previous, epoch, slot, timestamp, merkle_root).
+#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
+pub struct Header {
+    /// Block version
+    pub version: u8,
+    /// Previous block hash
+    pub previous: blake3::Hash,
+    /// Epoch
+    pub epoch: u64,
+    /// Slot UID
+    pub slot: u64,
+    /// Block creation timestamp
+    pub timestamp: Timestamp,
+    /// Root of the transaction hashes merkle tree
+    pub root: MerkleNode,
+}
+
+impl Header {
+    pub fn new(
+        previous: blake3::Hash,
+        epoch: u64,
+        slot: u64,
+        timestamp: Timestamp,
+        root: MerkleNode,
+    ) -> Self {
+        let version = BLOCK_VERSION;
+        Self { version, previous, epoch, slot, timestamp, root }
+    }
+
+    /// Generate the genesis block for provided genesis info.
+    pub fn genesis_header(genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Self {
+        let root = MerkleTree::new(100).root(0).unwrap();
+        Self::new(genesis_data, 0, 0, genesis_ts, root)
+    }
+
+    /// Calculate the header hash
+    pub fn headerhash(&self) -> blake3::Hash {
+        blake3::hash(&serialize(self))
+    }
+}
+
+impl Default for Header {
+    /// Represents the genesis header on current timestamp
+    fn default() -> Self {
+        Header::new(
+            blake3::hash(b"Let there be dark!"),
+            0,
+            0,
+            Timestamp::current_time(),
+            MerkleTree::new(100).root(0).unwrap(),
+        )
+    }
+}
+
+/// [`Header`] sled tree
+const SLED_HEADER_TREE: &[u8] = b"_headers";
+
+/// The `HeaderStore` is a `sled` tree storing all the blockchain's blocks' headers
+/// where the key is the headers' hash, and value is the serialized header.
+#[derive(Clone)]
+pub struct HeaderStore(pub sled::Tree);
+
+impl HeaderStore {
+    /// Opens a new or existing `HeaderStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_HEADER_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of [`Header`] into the blockstore.
+    pub fn insert(&self, headers: &[Header]) -> Result<Vec<blake3::Hash>> {
+        let (batch, ret) = self.insert_batch(headers)?;
+        self.0.apply_batch(batch)?;
+        Ok(ret)
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    /// The headers are hashed with BLAKE3 and this header hash is used as
+    /// the key, while value is the serialized [`Header`] itself.
+    /// On success, the function returns the header hashes in the same
+    /// order, along with the corresponding operation batch.
+    pub fn insert_batch(&self, headers: &[Header]) -> Result<(sled::Batch, Vec<blake3::Hash>)> {
+        let mut ret = Vec::with_capacity(headers.len());
+        let mut batch = sled::Batch::default();
+
+        for header in headers {
+            let serialized = serialize(header);
+            let headerhash = blake3::hash(&serialized);
+            batch.insert(headerhash.as_bytes(), serialized);
+            ret.push(headerhash);
+        }
+
+        Ok((batch, ret))
+    }
+
+    /// Check if the headerstore contains a given headerhash.
+    pub fn contains(&self, headerhash: &blake3::Hash) -> Result<bool> {
+        Ok(self.0.contains_key(headerhash.as_bytes())?)
+    }
+
+    /// Fetch given headerhashes from the headerstore.
+    /// The resulting vector contains `Option`, which is `Some` if the header
+    /// was found in the headerstore, 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 header was not found.
+    pub fn get(&self, headerhashes: &[blake3::Hash], strict: bool) -> Result<Vec<Option<Header>>> {
+        let mut ret = Vec::with_capacity(headerhashes.len());
+
+        for hash in headerhashes {
+            if let Some(found) = self.0.get(hash.as_bytes())? {
+                let header = deserialize(&found)?;
+                ret.push(Some(header));
+            } else {
+                if strict {
+                    let s = hash.to_hex().as_str().to_string();
+                    return Err(Error::HeaderNotFound(s))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve all headers from the headerstore in the form of a tuple
+    /// (`headerhash`, `header`).
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<(blake3::Hash, Header)>> {
+        let mut headers = vec![];
+
+        for header in self.0.iter() {
+            let (key, value) = header.unwrap();
+            let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
+            let header = deserialize(&value)?;
+            headers.push((hash_bytes.into(), header));
+        }
+
+        Ok(headers)
+    }
+}

+ 458 - 0
src/validator/blockchain/mod.rs

@@ -0,0 +1,458 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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 std::sync::{Arc, Mutex};
+
+use log::debug;
+use sled::Transactional;
+
+use darkfi_sdk::blockchain::Slot;
+use darkfi_serial::serialize;
+
+use crate::{tx::Transaction, Error, Result};
+
+pub mod block_store;
+pub use block_store::{Block, BlockInfo, BlockOrderStore, BlockStore};
+
+pub mod header_store;
+pub use header_store::{Header, HeaderStore};
+
+pub mod slot_store;
+pub use slot_store::{SlotStore, SlotStoreOverlay};
+
+pub mod tx_store;
+pub use tx_store::{PendingTxOrderStore, PendingTxStore, TxStore};
+
+pub mod contract_store;
+pub use contract_store::{
+    ContractStateStore, ContractStateStoreOverlay, WasmStore, WasmStoreOverlay,
+};
+
+/// Structure holding all sled trees that define the concept of Blockchain.
+#[derive(Clone)]
+pub struct Blockchain {
+    /// Main pointer to the sled db connection
+    pub sled_db: sled::Db,
+    /// Headers sled tree
+    pub headers: HeaderStore,
+    /// Blocks sled tree
+    pub blocks: BlockStore,
+    /// Block order sled tree
+    pub order: BlockOrderStore,
+    /// Slot sled tree
+    pub slots: SlotStore,
+    /// Transactions sled tree
+    pub transactions: TxStore,
+    /// Pending transactions sled tree
+    pub pending_txs: PendingTxStore,
+    /// Pending transactions order sled tree
+    pub pending_txs_order: PendingTxOrderStore,
+    /// Contract states
+    pub contracts: ContractStateStore,
+    /// Wasm bincodes
+    pub wasm_bincode: WasmStore,
+}
+
+impl Blockchain {
+    /// Instantiate a new `Blockchain` with the given `sled` database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let headers = HeaderStore::new(db)?;
+        let blocks = BlockStore::new(db)?;
+        let order = BlockOrderStore::new(db)?;
+        let slots = SlotStore::new(db)?;
+        let transactions = TxStore::new(db)?;
+        let pending_txs = PendingTxStore::new(db)?;
+        let pending_txs_order = PendingTxOrderStore::new(db)?;
+        let contracts = ContractStateStore::new(db)?;
+        let wasm_bincode = WasmStore::new(db)?;
+
+        Ok(Self {
+            sled_db: db.clone(),
+            headers,
+            blocks,
+            order,
+            slots,
+            transactions,
+            pending_txs,
+            pending_txs_order,
+            contracts,
+            wasm_bincode,
+        })
+    }
+
+    /// A block is considered valid when its parent hash is equal to the hash of the
+    /// previous block and their slots are incremental.
+    /// Additional validity rules can be applied.
+    pub fn validate_block(&self, block: &BlockInfo, previous_block: &BlockInfo) -> Result<()> {
+        let error = Err(Error::BlockIsInvalid(block.blockhash().to_string()));
+        let previous_block_hash = previous_block.blockhash();
+
+        // Check previous hash
+        if block.header.previous != previous_block_hash {
+            return error
+        }
+
+        // Check timestamps are incremental
+        if block.header.timestamp <= previous_block.header.timestamp {
+            return error
+        }
+
+        // Check slots are incremental
+        if block.header.slot <= previous_block.header.slot {
+            return error
+        }
+
+        // Verify slots exist
+        let mut slots = block.slots.clone();
+        if slots.is_empty() {
+            return error
+        }
+
+        // Sort them just to be safe
+        slots.sort_by(|a, b| b.id.cmp(&a.id));
+
+        // Verify first slot increments from previous block
+        if slots[0].id <= previous_block.header.slot {
+            return error
+        }
+
+        // Check all slot cover same sequence
+        for slot in &slots {
+            if !slot.fork_hashes.contains(&previous_block_hash) {
+                return error
+            }
+            if !slot.fork_previous_hashes.contains(&previous_block.header.previous) {
+                return error
+            }
+        }
+
+        // Check block slot is the last slot in the slice
+        if slots.last().unwrap().id != block.header.slot {
+            return error
+        }
+
+        // TODO: also validate slots etas and sigmas if we can derive them
+        // from previous slots
+
+        Ok(())
+    }
+
+    /// A blockchain is considered valid, when every block is valid,
+    /// based on validate_block checks.
+    /// Be careful as this will try to load everything in memory.
+    pub fn validate_chain(&self) -> Result<()> {
+        // We use block order store here so we have all blocks in order
+        let blocks = self.order.get_all()?;
+        for (index, block) in blocks[1..].iter().enumerate() {
+            let full_blocks = self.get_blocks_by_hash(&[block.1, blocks[index].1])?;
+            self.validate_block(&full_blocks[0], &full_blocks[1])?;
+        }
+
+        Ok(())
+    }
+
+    /// Insert a given [`BlockInfo`] into the blockchain database.
+    /// This functions wraps all the logic of separating the block into specific
+    /// data that can be fed into the different trees of the database.
+    /// Upon success, the functions returns the block hash that
+    /// were given and appended to the ledger.
+    pub fn add_block(&self, block: &BlockInfo) -> Result<blake3::Hash> {
+        let mut trees = vec![];
+        let mut batches = vec![];
+
+        // Store transactions
+        let (txs_batch, _) = self.transactions.insert_batch(&block.txs)?;
+        trees.push(self.transactions.0.clone());
+        batches.push(txs_batch);
+
+        // Store header
+        let (headers_batch, _) = self.headers.insert_batch(&[block.header.clone()])?;
+        trees.push(self.headers.0.clone());
+        batches.push(headers_batch);
+
+        // Store block
+        let blk: Block = Block::from(block.clone());
+        let (bocks_batch, block_hashes) = self.blocks.insert_batch(&[blk])?;
+        let block_hash = block_hashes[0];
+        trees.push(self.blocks.0.clone());
+        batches.push(bocks_batch);
+
+        // Store block order
+        let blocks_order_batch = self.order.insert_batch(&[block.header.slot], &[block_hash])?;
+        trees.push(self.order.0.clone());
+        batches.push(blocks_order_batch);
+
+        // Store slot checkpoints
+        let slots_batch = self.slots.insert_batch(&block.slots)?;
+        trees.push(self.slots.0.clone());
+        batches.push(slots_batch);
+
+        // Perform an atomic transaction over the trees and apply the batches.
+        self.atomic_write(&trees, &batches)?;
+
+        Ok(block_hash)
+    }
+
+    /// Check if the given [`BlockInfo`] is in the database and all trees.
+    pub fn has_block(&self, block: &BlockInfo) -> Result<bool> {
+        let blockhash = match self.order.get(&[block.header.slot], true) {
+            Ok(v) => v[0].unwrap(),
+            Err(_) => return Ok(false),
+        };
+
+        // Check if we have all transactions
+        let txs: Vec<blake3::Hash> =
+            block.txs.iter().map(|x| blake3::hash(&serialize(x))).collect();
+        if self.transactions.get(&txs, true).is_err() {
+            return Ok(false)
+        }
+
+        // Check if we have all slots
+        let slots: Vec<u64> = block.slots.iter().map(|x| x.id).collect();
+        if self.slots.get(&slots, true).is_err() {
+            return Ok(false)
+        }
+
+        // Check provided info produces the same hash
+        Ok(blockhash == block.blockhash())
+    }
+
+    /// Retrieve [`BlockInfo`]s by given hashes. Fails if any of them is not found.
+    pub fn get_blocks_by_hash(&self, hashes: &[blake3::Hash]) -> Result<Vec<BlockInfo>> {
+        let blocks = self.blocks.get(hashes, true)?;
+        let blocks: Vec<Block> = blocks.iter().map(|x| x.clone().unwrap()).collect();
+        let ret = self.get_blocks_infos(&blocks)?;
+
+        Ok(ret)
+    }
+
+    /// Retrieve all [`BlockInfo`] for given slice of [`Block`].
+    /// Fails if any of them is not found
+    fn get_blocks_infos(&self, blocks: &[Block]) -> Result<Vec<BlockInfo>> {
+        let mut ret = Vec::with_capacity(blocks.len());
+        for block in blocks {
+            let headers = self.headers.get(&[block.header], true)?;
+            // Since we used strict get, its safe to unwrap here
+            let header = headers[0].clone().unwrap();
+
+            let txs = self.transactions.get(&block.txs, true)?;
+            let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
+
+            let slots = self.slots.get(&block.slots, true)?;
+            let slots = slots.iter().map(|x| x.clone().unwrap()).collect();
+
+            let info = BlockInfo::new(header, txs, block.producer.clone(), slots);
+            ret.push(info);
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve [`BlockInfo`]s by given slots. Does not fail if any of them are not found.
+    pub fn get_blocks_by_slot(&self, slots: &[u64]) -> Result<Vec<BlockInfo>> {
+        debug!(target: "blockchain", "get_blocks_by_slot(): {:?}", slots);
+        let blockhashes = self.order.get(slots, false)?;
+
+        let mut hashes = vec![];
+        for i in blockhashes.into_iter().flatten() {
+            hashes.push(i);
+        }
+
+        self.get_blocks_by_hash(&hashes)
+    }
+
+    /// Retrieve n blocks after given start slot.
+    pub fn get_blocks_after(&self, slot: u64, n: u64) -> Result<Vec<BlockInfo>> {
+        debug!(target: "blockchain", "get_blocks_after(): {} -> {}", slot, n);
+        let hashes = self.order.get_after(slot, n)?;
+        self.get_blocks_by_hash(&hashes)
+    }
+
+    /// Retrieve stored blocks count
+    pub fn len(&self) -> usize {
+        self.order.len()
+    }
+
+    /// Retrieve stored txs count
+    pub fn txs_len(&self) -> usize {
+        self.transactions.len()
+    }
+
+    /// Check if blockchain contains any blocks
+    pub fn is_empty(&self) -> bool {
+        self.order.len() == 0
+    }
+
+    /// Retrieve the last block slot and hash.
+    pub fn last(&self) -> Result<(u64, blake3::Hash)> {
+        self.order.get_last()
+    }
+
+    /// Retrieve the last block info.
+    pub fn last_block(&self) -> Result<BlockInfo> {
+        let (_, hash) = self.last()?;
+        Ok(self.get_blocks_by_hash(&[hash])?[0].clone())
+    }
+
+    /// Retrieve the last slot.
+    pub fn last_slot(&self) -> Result<Slot> {
+        self.slots.get_last()
+    }
+
+    /// Retrieve n slots after given start slot.
+    pub fn get_slots_after(&self, slot: u64, n: u64) -> Result<Vec<Slot>> {
+        debug!(target: "blockchain", "get_slots_after(): {} -> {}", slot, n);
+        self.slots.get_after(slot, n)
+    }
+
+    /// Retrieve [`Slot`]s by given ids. Does not fail if any of them are not found.
+    pub fn get_slots_by_id(&self, ids: &[u64]) -> Result<Vec<Option<Slot>>> {
+        debug!(target: "blockchain", "get_slots_by_id(): {:?}", ids);
+        self.slots.get(ids, true)
+    }
+
+    /// Check if the given [`Slot`] is in the database and all trees.
+    pub fn has_slot(&self, slot: &Slot) -> Result<bool> {
+        Ok(self.slots.get(&[slot.id], true).is_ok())
+    }
+
+    /// Check if block order for the given slot is in the database.
+    pub fn has_slot_order(&self, slot: u64) -> Result<bool> {
+        let vec = match self.order.get(&[slot], true) {
+            Ok(v) => v,
+            Err(_) => return Ok(false),
+        };
+        Ok(!vec.is_empty())
+    }
+
+    /// Insert a given slice of pending transactions into the blockchain database.
+    /// On success, the function returns the transaction hashes in the same order
+    /// as the input transactions.
+    pub fn add_pending_txs(&self, txs: &[Transaction]) -> Result<Vec<blake3::Hash>> {
+        let (txs_batch, txs_hashes) = self.pending_txs.insert_batch(txs)?;
+        let txs_order_batch = self.pending_txs_order.insert_batch(&txs_hashes)?;
+
+        // Perform an atomic transaction over the trees and apply the batches.
+        let trees = [self.pending_txs.0.clone(), self.pending_txs_order.0.clone()];
+        let batches = [txs_batch, txs_order_batch];
+        self.atomic_write(&trees, &batches)?;
+
+        Ok(txs_hashes)
+    }
+
+    /// Retrieve all transactions from the pending tx store.
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_pending_txs(&self) -> Result<Vec<Transaction>> {
+        let txs = self.pending_txs.get_all()?;
+        let indexes = self.pending_txs_order.get_all()?;
+        if txs.len() != indexes.len() {
+            return Err(Error::InvalidInputLengths)
+        }
+
+        let mut ret = Vec::with_capacity(txs.len());
+        for index in indexes {
+            ret.push(txs.get(&index.1).unwrap().clone());
+        }
+
+        Ok(ret)
+    }
+
+    /// Remove a given slice of pending transactions from the blockchain database.
+    pub fn remove_pending_txs(&self, txs: &[Transaction]) -> Result<()> {
+        let txs_hashes: Vec<blake3::Hash> =
+            txs.iter().map(|x| blake3::hash(&serialize(x))).collect();
+        let indexes = self.pending_txs_order.get_all()?;
+        // We could do indexes.iter().map(|x| txs_hashes.contains(x.1)).collect.map(|x| x.0).collect but this is faster
+        // since we don't do the second iteration
+        let mut removed_indexes = vec![];
+        for index in indexes {
+            if txs_hashes.contains(&index.1) {
+                removed_indexes.push(index.0);
+            }
+        }
+
+        let txs_batch = self.pending_txs.remove_batch(&txs_hashes);
+        let txs_order_batch = self.pending_txs_order.remove_batch(&removed_indexes);
+
+        // Perform an atomic transaction over the trees and apply the batches.
+        let trees = [self.pending_txs.0.clone(), self.pending_txs_order.0.clone()];
+        let batches = [txs_batch, txs_order_batch];
+        self.atomic_write(&trees, &batches)?;
+
+        Ok(())
+    }
+
+    /// Auxiliary function to write to multiple trees completely atomic.
+    fn atomic_write(&self, trees: &[sled::Tree], batches: &[sled::Batch]) -> Result<()> {
+        if trees.len() != batches.len() {
+            return Err(Error::InvalidInputLengths)
+        }
+
+        trees.transaction(|trees| {
+            for (index, tree) in trees.iter().enumerate() {
+                tree.apply_batch(&batches[index])?;
+            }
+
+            Ok::<(), sled::transaction::ConflictableTransactionError<sled::Error>>(())
+        })?;
+
+        Ok(())
+    }
+}
+
+/// Atomic pointer to sled db overlay.
+pub type SledDbOverlayPtr = Arc<Mutex<sled_overlay::SledDbOverlay>>;
+
+/// Atomic pointer to blockchain overlay.
+pub type BlockchainOverlayPtr = Arc<Mutex<BlockchainOverlay>>;
+
+/// Overlay structure over a [`Blockchain`] instance.
+pub struct BlockchainOverlay {
+    /// Main [`sled_overlay::SledDbOverlay`] to the sled db connection
+    pub overlay: SledDbOverlayPtr,
+    /// Slots overlay
+    pub slots: SlotStoreOverlay,
+    /// Contract states overlay
+    pub contracts: ContractStateStoreOverlay,
+    /// Wasm bincodes overlay
+    pub wasm_bincode: WasmStoreOverlay,
+}
+
+impl BlockchainOverlay {
+    /// Instantiate a new `BlockchainOverlay` over the given [`Blockchain`] instance.
+    pub fn new(blockchain: &Blockchain) -> Result<BlockchainOverlayPtr> {
+        let overlay = Arc::new(Mutex::new(sled_overlay::SledDbOverlay::new(&blockchain.sled_db)));
+        let slots = SlotStoreOverlay::new(overlay.clone())?;
+        let contracts = ContractStateStoreOverlay::new(overlay.clone())?;
+        let wasm_bincode = WasmStoreOverlay::new(overlay.clone())?;
+
+        Ok(Arc::new(Mutex::new(Self { overlay, slots, contracts, wasm_bincode })))
+    }
+
+    /// Checkpoint overlay so we can revert to it, if needed.
+    pub fn checkpoint(&self) {
+        self.overlay.lock().unwrap().checkpoint();
+    }
+
+    /// Revert to current overlay checkpoint.
+    pub fn revert_to_checkpoint(&self) -> Result<()> {
+        self.overlay.lock().unwrap().revert_to_checkpoint()?;
+
+        Ok(())
+    }
+}

+ 162 - 0
src/validator/blockchain/slot_store.rs

@@ -0,0 +1,162 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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/>.
+ */
+
+// [`Slot`] is defined in the sdk so contracts can use it
+use darkfi_sdk::blockchain::Slot;
+use darkfi_serial::{deserialize, serialize};
+
+use crate::{Error, Result};
+
+use super::SledDbOverlayPtr;
+
+const SLED_SLOT_TREE: &[u8] = b"_slots";
+
+/// The `SlotStore` is a `sled` tree storing the blockhains' slots,
+/// where the key is the slot uid, and the value is is the serialized slot.
+#[derive(Clone)]
+pub struct SlotStore(pub sled::Tree);
+
+impl SlotStore {
+    /// Opens a new or existing `SlotStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_SLOT_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of [`Slot`] into the slot store.
+    pub fn insert(&self, slots: &[Slot]) -> Result<()> {
+        let batch = self.insert_batch(slots)?;
+        self.0.apply_batch(batch)?;
+        Ok(())
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    /// The slot id is used as the key, while value is the serialized [`Slot`] itself.
+    pub fn insert_batch(&self, slots: &[Slot]) -> Result<sled::Batch> {
+        let mut batch = sled::Batch::default();
+
+        for slot in slots {
+            let serialized = serialize(slot);
+            batch.insert(&slot.id.to_be_bytes(), serialized);
+        }
+
+        Ok(batch)
+    }
+
+    /// Check if the slot store contains a given id.
+    pub fn contains(&self, id: u64) -> Result<bool> {
+        Ok(self.0.contains_key(id.to_be_bytes())?)
+    }
+
+    /// Fetch given slots from the slot store.
+    /// The resulting vector contains `Option`, which is `Some` if the slot
+    /// was found in the slot store, 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, ids: &[u64], strict: bool) -> Result<Vec<Option<Slot>>> {
+        let mut ret = Vec::with_capacity(ids.len());
+
+        for id in ids {
+            if let Some(found) = self.0.get(id.to_be_bytes())? {
+                let slot = deserialize(&found)?;
+                ret.push(Some(slot));
+            } else {
+                if strict {
+                    return Err(Error::SlotNotFound(*id))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve all slot from the slot store.
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<Slot>> {
+        let mut slots = vec![];
+
+        for slot in self.0.iter() {
+            let (_, value) = slot.unwrap();
+            let slot = deserialize(&value)?;
+            slots.push(slot);
+        }
+
+        Ok(slots)
+    }
+
+    /// Fetch n slots 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 `SlotStore`.
+    pub fn get_after(&self, id: u64, n: u64) -> Result<Vec<Slot>> {
+        let mut ret = vec![];
+
+        let mut key = id;
+        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 slot = deserialize(&found.1)?;
+                ret.push(slot);
+                counter += 1;
+                continue
+            }
+            break
+        }
+
+        Ok(ret)
+    }
+
+    /// Fetch the last slot in the tree, based on the `Ord`
+    /// implementation for `Vec<u8>`. This should not be able to
+    /// fail because we initialize the store with the genesis slot.
+    pub fn get_last(&self) -> Result<Slot> {
+        let found = self.0.last()?.unwrap();
+        let slot = deserialize(&found.1)?;
+        Ok(slot)
+    }
+
+    /// Retrieve records count
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.len() == 0
+    }
+}
+
+/// Overlay structure over a [`SlotStore`] instance.
+pub struct SlotStoreOverlay(SledDbOverlayPtr);
+
+impl SlotStoreOverlay {
+    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree(SLED_SLOT_TREE)?;
+        Ok(Self(overlay))
+    }
+
+    /// Fetch given id from the slot store.
+    pub fn get(&self, id: u64) -> Result<Vec<u8>> {
+        match self.0.lock().unwrap().get(SLED_SLOT_TREE, &id.to_be_bytes())? {
+            Some(found) => Ok(found.to_vec()),
+            None => Err(Error::SlotNotFound(id)),
+        }
+    }
+}

+ 297 - 0
src/validator/blockchain/tx_store.rs

@@ -0,0 +1,297 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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 std::collections::HashMap;
+
+use darkfi_serial::{deserialize, serialize};
+
+use crate::{tx::Transaction, Error, Result};
+
+const SLED_TX_TREE: &[u8] = b"_transactions";
+const SLED_PENDING_TX_TREE: &[u8] = b"_pending_transactions";
+const SLED_PENDING_TX_ORDER_TREE: &[u8] = b"_pending_transactions_order";
+
+/// The `TxStore` is a `sled` tree storing all the blockchain's
+/// transactions where the key is the transaction hash, and the value is
+/// the serialized transaction.
+#[derive(Clone)]
+pub struct TxStore(pub sled::Tree);
+
+impl TxStore {
+    /// Opens a new or existing `TxStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_TX_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of [`Transaction`] into the txstore.
+    pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
+        let (batch, ret) = self.insert_batch(transactions)?;
+        self.0.apply_batch(batch)?;
+        Ok(ret)
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    /// The transactions are hashed with BLAKE3 and this hash is used as
+    /// the key, while the value is the serialized [`Transaction`] itself.
+    /// On success, the function returns the transaction hashes in the same
+    /// order as the input transactions, along with the corresponding operation
+    /// batch.
+    pub fn insert_batch(
+        &self,
+        transactions: &[Transaction],
+    ) -> Result<(sled::Batch, Vec<blake3::Hash>)> {
+        let mut ret = Vec::with_capacity(transactions.len());
+        let mut batch = sled::Batch::default();
+
+        for tx in transactions {
+            let serialized = serialize(tx);
+            let tx_hash = blake3::hash(&serialized);
+            batch.insert(tx_hash.as_bytes(), serialized);
+            ret.push(tx_hash);
+        }
+
+        Ok((batch, ret))
+    }
+
+    /// Check if the txstore contains a given transaction hash.
+    pub fn contains(&self, tx_hash: &blake3::Hash) -> Result<bool> {
+        Ok(self.0.contains_key(tx_hash.as_bytes())?)
+    }
+
+    /// Fetch given tx hashes from the txstore.
+    /// The resulting vector contains `Option`, which is `Some` if the tx
+    /// was found in the txstore, 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 block was not found.
+    pub fn get(
+        &self,
+        tx_hashes: &[blake3::Hash],
+        strict: bool,
+    ) -> Result<Vec<Option<Transaction>>> {
+        let mut ret = Vec::with_capacity(tx_hashes.len());
+
+        for tx_hash in tx_hashes {
+            if let Some(found) = self.0.get(tx_hash.as_bytes())? {
+                let tx = deserialize(&found)?;
+                ret.push(Some(tx));
+            } else {
+                if strict {
+                    let s = tx_hash.to_hex().as_str().to_string();
+                    return Err(Error::TransactionNotFound(s))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve all transactions from the txstore in the form of a tuple
+    /// (`tx_hash`, `tx`).
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<(blake3::Hash, Transaction)>> {
+        let mut txs = vec![];
+
+        for tx in self.0.iter() {
+            let (key, value) = tx.unwrap();
+            let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
+            let tx = deserialize(&value)?;
+            txs.push((hash_bytes.into(), tx));
+        }
+
+        Ok(txs)
+    }
+
+    /// Retrieve records count
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.len() == 0
+    }
+}
+
+/// The `PendingTxStore` is a `sled` tree storing all the node pending
+/// transactions where the key is the transaction hash, and the value is
+/// the serialized transaction.
+#[derive(Clone)]
+pub struct PendingTxStore(pub sled::Tree);
+
+impl PendingTxStore {
+    /// Opens a new or existing `PendingTxStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_PENDING_TX_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of [`Transaction`] into the pending tx store.   
+    pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
+        let (batch, ret) = self.insert_batch(transactions)?;
+        self.0.apply_batch(batch)?;
+        Ok(ret)
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    /// The transactions are hashed with BLAKE3 and this hash is used as
+    /// the key, while the value is the serialized [`Transaction`] itself.
+    /// On success, the function returns the transaction hashes in the same
+    /// order as the input transactions, along with the corresponding operation
+    /// batch.
+    pub fn insert_batch(
+        &self,
+        transactions: &[Transaction],
+    ) -> Result<(sled::Batch, Vec<blake3::Hash>)> {
+        let mut ret = Vec::with_capacity(transactions.len());
+        let mut batch = sled::Batch::default();
+
+        for tx in transactions {
+            let serialized = serialize(tx);
+            let tx_hash = blake3::hash(&serialized);
+            batch.insert(tx_hash.as_bytes(), serialized);
+            ret.push(tx_hash);
+        }
+
+        Ok((batch, ret))
+    }
+
+    /// Check if the pending tx store contains a given transaction hash.
+    pub fn contains(&self, tx_hash: &blake3::Hash) -> Result<bool> {
+        Ok(self.0.contains_key(tx_hash.as_bytes())?)
+    }
+
+    /// Retrieve all transactions from the pending tx store in the form of
+    /// a HashMap with key the transaction hash and value the transaction
+    /// itself.
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<HashMap<blake3::Hash, Transaction>> {
+        let mut txs = HashMap::new();
+
+        for tx in self.0.iter() {
+            let (key, value) = tx.unwrap();
+            let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
+            let tx = deserialize(&value)?;
+            txs.insert(hash_bytes.into(), tx);
+        }
+
+        Ok(txs)
+    }
+
+    /// Remove a slice of [`blake3::Hash`] from the pending tx store.
+    pub fn remove(&self, txs_hashes: &[blake3::Hash]) -> Result<()> {
+        let batch = self.remove_batch(txs_hashes);
+        self.0.apply_batch(batch)?;
+        Ok(())
+    }
+
+    /// Generate the sled batch corresponding to a remove, so caller
+    /// can handle the write operation.
+    pub fn remove_batch(&self, txs_hashes: &[blake3::Hash]) -> sled::Batch {
+        let mut batch = sled::Batch::default();
+
+        for tx_hash in txs_hashes {
+            batch.remove(tx_hash.as_bytes());
+        }
+
+        batch
+    }
+}
+
+/// The `PendingTxOrderStore` is a `sled` tree storing the order of all
+/// the node pending transactions where the key is an incremental value,
+/// and the value is the serialized transaction.
+#[derive(Clone)]
+pub struct PendingTxOrderStore(pub sled::Tree);
+
+impl PendingTxOrderStore {
+    /// Opens a new or existing `PendingTxOrderStore` on the given sled database.
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_PENDING_TX_ORDER_TREE)?;
+        Ok(Self(tree))
+    }
+
+    /// Insert a slice of [`blake3::Hash`] into the pending tx order store.
+    /// With sled, the operation is done as a batch.
+    pub fn insert(&self, txs_hashes: &[blake3::Hash]) -> Result<()> {
+        let batch = self.insert_batch(txs_hashes)?;
+        self.0.apply_batch(batch)?;
+        Ok(())
+    }
+
+    /// Generate the sled batch corresponding to an insert, so caller
+    /// can handle the write operation.
+    pub fn insert_batch(&self, txs_hashes: &[blake3::Hash]) -> Result<sled::Batch> {
+        let mut batch = sled::Batch::default();
+
+        let mut next_index = match self.0.last()? {
+            Some(n) => {
+                let prev_bytes: [u8; 8] = n.0.as_ref().try_into().unwrap();
+                let prev = u64::from_be_bytes(prev_bytes);
+                prev + 1
+            }
+            None => 0,
+        };
+
+        for txs_hash in txs_hashes {
+            batch.insert(&next_index.to_be_bytes(), txs_hash.as_bytes());
+            next_index += 1;
+        }
+
+        Ok(batch)
+    }
+
+    /// Retrieve all transactions from the pending tx order store in the form
+    /// of a tuple (`u64`, `blake3::Hash`).
+    /// Be careful as this will try to load everything in memory.
+    pub fn get_all(&self) -> Result<Vec<(u64, blake3::Hash)>> {
+        let mut txs = vec![];
+
+        for tx in self.0.iter() {
+            let (key, value) = tx.unwrap();
+            let index_bytes: [u8; 8] = key.as_ref().try_into().unwrap();
+            let hash_bytes: [u8; 32] = value.as_ref().try_into().unwrap();
+            let index = u64::from_be_bytes(index_bytes);
+            let hash = blake3::Hash::from(hash_bytes);
+            txs.push((index, hash));
+        }
+
+        Ok(txs)
+    }
+
+    /// Remove a slice of [`u64`] from the pending tx order store.
+    pub fn remove(&self, indexes: &[u64]) -> Result<()> {
+        let batch = self.remove_batch(indexes);
+        self.0.apply_batch(batch)?;
+        Ok(())
+    }
+
+    /// Generate the sled batch corresponding to a remove, so caller
+    /// can handle the write operation.
+    pub fn remove_batch(&self, indexes: &[u64]) -> sled::Batch {
+        let mut batch = sled::Batch::default();
+
+        for index in indexes {
+            batch.remove(&index.to_be_bytes());
+        }
+
+        batch
+    }
+}

+ 3 - 0
src/validator/mod.rs

@@ -37,6 +37,9 @@ use crate::{
     Result,
     Result,
 };
 };
 
 
+/// DarkFi blockchain
+pub mod blockchain;
+
 /// DarkFi consensus
 /// DarkFi consensus
 pub mod consensus;
 pub mod consensus;
 use consensus::Consensus;
 use consensus::Consensus;

+ 148 - 0
tests/blockchain.rs

@@ -0,0 +1,148 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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::{
+    validator::blockchain::{BlockInfo, Blockchain, Header},
+    Error, Result,
+};
+use darkfi_sdk::{
+    blockchain::Slot,
+    pasta::{group::ff::Field, pallas},
+};
+
+struct Harness {
+    pub alice: Blockchain,
+    pub bob: Blockchain,
+}
+
+impl Harness {
+    fn new() -> Result<Self> {
+        let alice = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
+        let bob = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
+        Ok(Self { alice, bob })
+    }
+
+    fn is_empty(&self) {
+        assert!(self.alice.is_empty());
+        assert!(self.bob.is_empty());
+    }
+
+    fn validate_chains(&self) -> Result<()> {
+        self.alice.validate_chain()?;
+        self.bob.validate_chain()?;
+
+        assert_eq!(self.alice.len(), self.bob.len());
+
+        Ok(())
+    }
+
+    fn generate_next_block(&self, previous: &BlockInfo) -> BlockInfo {
+        let previous_hash = previous.blockhash();
+        // We increment timestamp so we don't have to use sleep
+        let mut timestamp = previous.header.timestamp;
+        timestamp.add(1);
+        let header = Header::new(
+            previous_hash,
+            previous.header.epoch,
+            previous.header.slot + 1,
+            timestamp,
+            previous.header.root.clone(),
+        );
+        let slot = Slot::new(
+            previous.header.slot + 1,
+            pallas::Base::ZERO,
+            vec![previous_hash],
+            vec![previous.header.previous.clone()],
+            pallas::Base::ZERO,
+            pallas::Base::ZERO,
+        );
+        BlockInfo::new(header, vec![], previous.producer.clone(), vec![slot])
+    }
+
+    fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
+        self.add_blocks_to_chain(&self.alice, blocks)?;
+        self.add_blocks_to_chain(&self.bob, blocks)?;
+
+        Ok(())
+    }
+
+    // This is what the validator will execute when it receives a block.
+    fn add_blocks_to_chain(&self, blockchain: &Blockchain, blocks: &[BlockInfo]) -> Result<()> {
+        // TODO: Use an overlay to revert changes in case of errors
+        // Create overlay
+
+        // When we insert genesis, chain is empty
+        let mut previous =
+            if !blockchain.is_empty() { Some(blockchain.last_block()?) } else { None };
+
+        // Validate and insert each block
+        for block in blocks {
+            // Check if block already exists
+            if blockchain.has_block(block)? {
+                return Err(Error::BlockAlreadyExists(block.blockhash().to_string()))
+            }
+
+            // This will be true for every insert, apart from genesis
+            if let Some(p) = previous {
+                blockchain.validate_block(block, &p)?;
+            }
+
+            // Insert block
+            blockchain.add_block(block)?;
+
+            // Use last inserted block as next iteration previous
+            previous = Some(block.clone());
+        }
+
+        // Write overlay
+
+        Ok(())
+    }
+}
+
+#[async_std::test]
+async fn blockchain_add_blocks() -> Result<()> {
+    // Initialize harness
+    let th = Harness::new()?;
+
+    // Check that nothing exists
+    th.is_empty();
+
+    // We generate some blocks
+    let mut blocks = vec![];
+
+    let genesis_block = BlockInfo::default();
+    blocks.push(genesis_block.clone());
+
+    let block = th.generate_next_block(&genesis_block);
+    blocks.push(block.clone());
+
+    let block = th.generate_next_block(&block);
+    blocks.push(block.clone());
+
+    let block = th.generate_next_block(&block);
+    blocks.push(block.clone());
+
+    th.add_blocks(&blocks)?;
+
+    // Validate chains
+    th.validate_chains()?;
+
+    // Thanks for reading
+    Ok(())
+}