Sfoglia il codice sorgente

blockchain: rewrite completed

Attention: this commit breaks darkfid/faucetd network functionalities, so nodes can't sync or participate in the protocol. Wait for their rewrite(TM)
aggstam 3 anni fa
parent
commit
feabf5a67a

+ 358 - 119
src/blockchain/block_store.rs

@@ -16,129 +16,199 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_serial::{deserialize, serialize};
+use darkfi_sdk::{blockchain::Slot, crypto::schnorr::Signature};
+use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
+
+use crate::{tx::Transaction, Error, Result};
+
+use super::{Header, SledDbOverlayPtr};
+
+/// 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>,
+}
 
-use crate::{
-    consensus::{Block, Header},
-    util::time::Timestamp,
-    Error, Result,
-};
+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 }
+    }
 
-const SLED_HEADER_TREE: &[u8] = b"_headers";
-const SLED_BLOCK_TREE: &[u8] = b"_blocks";
-const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order";
+    /// Calculate the block hash
+    pub fn blockhash(&self) -> blake3::Hash {
+        blake3::hash(&serialize(self))
+    }
+}
 
-/// 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(sled::Tree);
-
-impl HeaderStore {
-    /// Opens a new or existing `HeaderStore` on the given sled database.
-    pub fn new(db: &sled::Db, genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Result<Self> {
-        let tree = db.open_tree(SLED_HEADER_TREE)?;
-        let store = Self(tree);
-
-        // In case the store is empty, initialize it with the genesis header.
-        if store.0.is_empty() {
-            let genesis_header = Header::genesis_header(genesis_ts, genesis_data);
-            store.insert(&[genesis_header])?;
+/// 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![Slot::default()],
         }
+    }
+}
 
-        Ok(store)
+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 }
     }
 
-    /// Insert a slice of [`Header`] into the blockstore. With sled, the
-    /// operation is done as a batch.
-    /// The headers are hashed with BLAKE3 and this headerhash is used as
-    /// the key, while value is the serialized [`Header`] itself.
-    /// On success, the function returns the header hashes in the same order.
-    pub fn insert(&self, headers: &[Header]) -> Result<Vec<blake3::Hash>> {
-        let mut ret = Vec::with_capacity(headers.len());
-        let mut batch = sled::Batch::default();
+    /// Calculate the block hash
+    pub fn blockhash(&self) -> blake3::Hash {
+        let block: Block = self.clone().into();
+        block.blockhash()
+    }
+
+    /// 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(&self, previous: &Self) -> Result<()> {
+        let error = Err(Error::BlockIsInvalid(self.blockhash().to_string()));
+        let previous_hash = previous.blockhash();
 
-        for header in headers {
-            let serialized = serialize(header);
-            let headerhash = blake3::hash(&serialized);
-            batch.insert(headerhash.as_bytes(), serialized);
-            ret.push(headerhash);
+        // Check previous hash
+        if self.header.previous != previous_hash {
+            return error
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(ret)
-    }
+        // Check timestamps are incremental
+        if self.header.timestamp <= previous.header.timestamp {
+            return error
+        }
 
-    /// 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())?)
-    }
+        // Check slots are incremental
+        if self.header.slot <= previous.header.slot {
+            return error
+        }
 
-    /// 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());
+        // Verify slots exist
+        let mut slots = self.slots.clone();
+        if slots.is_empty() {
+            return error
+        }
 
-        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);
+        // 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.header.slot {
+            return error
+        }
+
+        // Check all slot cover same sequence
+        for slot in &slots {
+            if !slot.fork_hashes.contains(&previous_hash) {
+                return error
+            }
+            if !slot.fork_previous_hashes.contains(&previous.header.previous) {
+                return error
             }
         }
 
-        Ok(ret)
-    }
+        // Check block slot is the last slot in the slice
+        if slots.last().unwrap().id != self.header.slot {
+            return error
+        }
 
-    /// 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![];
+        // TODO: also validate slots etas and sigmas if we can derive them
+        // from previous slots
 
-        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(())
+    }
+}
 
-        Ok(headers)
+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(sled::Tree);
+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, genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Result<Self> {
+    pub fn new(db: &sled::Db) -> Result<Self> {
         let tree = db.open_tree(SLED_BLOCK_TREE)?;
-        let store = Self(tree);
-        // In case the store is empty, initialize it with the genesis block.
-        if store.0.is_empty() {
-            let genesis_block = Block::genesis_block(genesis_ts, genesis_data);
-            store.insert(&[genesis_block])?;
-        }
+        Ok(Self(tree))
+    }
 
-        Ok(store)
+    /// 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)
     }
 
-    /// Insert a slice of [`Block`] into the store. With sled, the
-    /// operation is done as a batch.
-    /// The block are hashed with BLAKE3 and this blockhash is used as
+    /// 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(&self, blocks: &[Block]) -> Result<Vec<blake3::Hash>> {
+    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();
 
@@ -149,8 +219,7 @@ impl BlockStore {
             ret.push(blockhash);
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(ret)
+        Ok((batch, ret))
     }
 
     /// Check if the blockstore contains a given blockhash.
@@ -158,19 +227,15 @@ impl BlockStore {
         Ok(self.0.contains_key(blockhash.as_bytes())?)
     }
 
-    /// Fetch given blockhashhashes from the blockstore.
+    /// 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,
-        blockhashhashes: &[blake3::Hash],
-        strict: bool,
-    ) -> Result<Vec<Option<Block>>> {
-        let mut ret = Vec::with_capacity(blockhashhashes.len());
-
-        for hash in blockhashhashes {
+    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));
@@ -203,40 +268,106 @@ impl BlockStore {
     }
 }
 
+/// Overlay structure over a [`BlockStore`] instance.
+pub struct BlockStoreOverlay(SledDbOverlayPtr);
+
+impl BlockStoreOverlay {
+    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE)?;
+        Ok(Self(overlay))
+    }
+
+    /// Insert a slice of [`Block`] into the overlay.
+    /// The block 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(&self, blocks: &[Block]) -> Result<Vec<blake3::Hash>> {
+        let mut ret = Vec::with_capacity(blocks.len());
+        let mut lock = self.0.lock().unwrap();
+
+        for block in blocks {
+            let serialized = serialize(block);
+            let blockhash = blake3::hash(&serialized);
+            lock.insert(SLED_BLOCK_TREE, blockhash.as_bytes(), &serialized)?;
+            ret.push(blockhash);
+        }
+
+        Ok(ret)
+    }
+
+    /// Fetch given block hashes from the overlay.
+    /// The resulting vector contains `Option`, which is `Some` if the block
+    /// was found in the overlay, 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());
+        let lock = self.0.lock().unwrap();
+
+        for hash in block_hashes {
+            if let Some(found) = lock.get(SLED_BLOCK_TREE, 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)
+    }
+}
+
+/// 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(sled::Tree);
+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, genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Result<Self> {
+    pub fn new(db: &sled::Db) -> Result<Self> {
         let tree = db.open_tree(SLED_BLOCK_ORDER_TREE)?;
-        let store = Self(tree);
-
-        // In case the store is empty, initialize it with the genesis block.
-        if store.0.is_empty() {
-            let genesis_block = Block::genesis_block(genesis_ts, genesis_data);
-            store.insert(&[0], &[genesis_block.blockhash()])?;
-        }
+        Ok(Self(tree))
+    }
 
-        Ok(store)
+    /// 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(())
     }
 
-    /// Insert a slice of slots and blockhashes into the store. With sled, the
-    /// operation is done as a batch.
+    /// 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(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
-        assert_eq!(slots.len(), hashes.len());
+    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());
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(())
+        Ok(batch)
     }
 
     /// Check if the blockorderstore contains a given slot.
@@ -309,9 +440,24 @@ impl BlockOrderStore {
         Ok(ret)
     }
 
+    /// Fetch the first blockhash in the tree, based on the `Ord`
+    /// implementation for `Vec<u8>`.
+    pub fn get_first(&self) -> Result<(u64, blake3::Hash)> {
+        let found = match self.0.first()? {
+            Some(s) => s,
+            None => return Err(Error::SlotNotFound(0)),
+        };
+
+        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))
+    }
+
     /// Fetch the last blockhash 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 block.
+    /// implementation for `Vec<u8>`.
     pub fn get_last(&self) -> Result<(u64, blake3::Hash)> {
         let found = self.0.last()?.unwrap();
 
@@ -330,6 +476,99 @@ impl BlockOrderStore {
 
     /// Check if sled contains any records
     pub fn is_empty(&self) -> bool {
-        self.0.len() == 0
+        self.0.is_empty()
+    }
+}
+
+/// Overlay structure over a [`BlockOrderStore`] instance.
+pub struct BlockOrderStoreOverlay(SledDbOverlayPtr);
+
+impl BlockOrderStoreOverlay {
+    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree(SLED_BLOCK_ORDER_TREE)?;
+        Ok(Self(overlay))
+    }
+
+    /// Insert a slice of slots and blockhashes into the store. With sled, the
+    /// operation is done as a batch.
+    /// The block slot is used as the key, and the blockhash is used as value.
+    pub fn insert(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
+        if slots.len() != hashes.len() {
+            return Err(Error::InvalidInputLengths)
+        }
+
+        let mut lock = self.0.lock().unwrap();
+
+        for (i, sl) in slots.iter().enumerate() {
+            lock.insert(SLED_BLOCK_ORDER_TREE, &sl.to_be_bytes(), hashes[i].as_bytes())?;
+        }
+
+        Ok(())
+    }
+
+    /// Fetch given slots from the overlay.
+    /// The resulting vector contains `Option`, which is `Some` if the slot
+    /// was found in the overlay, 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());
+        let lock = self.0.lock().unwrap();
+
+        for slot in slots {
+            if let Some(found) = lock.get(SLED_BLOCK_ORDER_TREE, &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)
+    }
+
+    /// Fetch the last blockhash in the overlay, based on the `Ord`
+    /// implementation for `Vec<u8>`.
+    pub fn get_last(&self) -> Result<(u64, blake3::Hash)> {
+        let found = self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)?.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))
+    }
+
+    /// Check if overlay contains any records
+    pub fn is_empty(&self) -> Result<bool> {
+        Ok(self.0.lock().unwrap().is_empty(SLED_BLOCK_ORDER_TREE)?)
+    }
+}
+
+/// 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 }
     }
 }

+ 7 - 6
src/blockchain/contract_store.rs

@@ -23,13 +23,14 @@ use darkfi_serial::{deserialize, serialize};
 use log::{debug, error};
 
 use crate::{
-    blockchain::SledDbOverlayPtr,
     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";
 
@@ -142,6 +143,8 @@ impl ContractStateStore {
     /// 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.
+    /// NOTE: this function is not used right now, we keep it for future proofing,
+    ///       and its obviously untested.
     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);
 
@@ -162,15 +165,13 @@ impl ContractStateStore {
             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))?;
 
+        // Drop the deleted tree from the database
+        db.drop_tree(ptr)?;
+
         Ok(())
     }
 

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


+ 261 - 63
src/blockchain/mod.rs

@@ -19,25 +19,26 @@
 use std::sync::{Arc, Mutex};
 
 use log::debug;
+use sled::Transactional;
 
 use darkfi_sdk::blockchain::Slot;
 use darkfi_serial::serialize;
 
-use crate::{
-    consensus::{Block, BlockInfo},
-    tx::Transaction,
-    util::time::Timestamp,
-    Result,
-};
+use crate::{tx::Transaction, Error, Result};
 
 pub mod block_store;
-pub use block_store::{BlockOrderStore, BlockStore, HeaderStore};
+pub use block_store::{
+    Block, BlockInfo, BlockOrderStore, BlockOrderStoreOverlay, BlockStore, BlockStoreOverlay,
+};
+
+pub mod header_store;
+pub use header_store::{Header, HeaderStore, HeaderStoreOverlay};
 
 pub mod slot_store;
 pub use slot_store::{SlotStore, SlotStoreOverlay};
 
 pub mod tx_store;
-pub use tx_store::{PendingTxOrderStore, PendingTxStore, TxStore};
+pub use tx_store::{PendingTxOrderStore, PendingTxStore, TxStore, TxStoreOverlay};
 
 pub mod contract_store;
 pub use contract_store::{
@@ -71,11 +72,11 @@ pub struct Blockchain {
 
 impl Blockchain {
     /// Instantiate a new `Blockchain` with the given `sled` database.
-    pub fn new(db: &sled::Db, genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Result<Self> {
-        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 slots = SlotStore::new(db, genesis_data)?;
+    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)?;
@@ -96,32 +97,60 @@ impl Blockchain {
         })
     }
 
-    /// Insert a given slice of [`BlockInfo`] into the blockchain database.
+    /// 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(&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(&[blocks[index].1, block.1])?;
+            full_blocks[1].validate(&full_blocks[0])?;
+        }
+
+        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 a vector of the block hashes that
+    /// Upon success, the functions returns the block hash that
     /// were given and appended to the ledger.
-    pub fn add(&self, blocks: &[BlockInfo]) -> Result<Vec<blake3::Hash>> {
-        let mut ret = Vec::with_capacity(blocks.len());
-
-        // TODO: Make db writes here completely atomic
-        for block in blocks {
-            // Store transactions
-            self.transactions.insert(&block.txs)?;
-
-            // Store header
-            self.headers.insert(&[block.header.clone()])?;
-
-            // Store block
-            let blk: Block = Block::from(block.clone());
-            let blockhash = self.blocks.insert(&[blk])?;
-            ret.push(blockhash[0]);
-
-            // Store block order
-            self.order.insert(&[block.header.slot], &[blockhash[0]])?;
-        }
-
-        Ok(ret)
+    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.
@@ -131,21 +160,37 @@ impl Blockchain {
             Err(_) => return Ok(false),
         };
 
-        // TODO: Check if we have all transactions
+        // 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 are not found.
+    /// 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 mut ret = Vec::with_capacity(hashes.len());
-
         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)?;
 
-        for block in blocks {
-            let block = block.unwrap();
+        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();
@@ -153,7 +198,10 @@ impl Blockchain {
             let txs = self.transactions.get(&block.txs, true)?;
             let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
 
-            let info = BlockInfo::new(header, txs, block.lead_info.clone());
+            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);
         }
 
@@ -192,7 +240,12 @@ impl Blockchain {
 
     /// Check if blockchain contains any blocks
     pub fn is_empty(&self) -> bool {
-        self.order.len() == 0
+        self.order.is_empty()
+    }
+
+    /// Retrieve genesis (first) block slot and hash.
+    pub fn genesis(&self) -> Result<(u64, blake3::Hash)> {
+        self.order.get_first()
     }
 
     /// Retrieve the last block slot and hash.
@@ -200,6 +253,12 @@ impl Blockchain {
         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()
@@ -211,11 +270,6 @@ impl Blockchain {
         self.slots.get_after(slot, n)
     }
 
-    /// Insert a given slice of [`Slot`] into the blockchain database.
-    pub fn add_slots(&self, slots: &[Slot]) -> Result<()> {
-        self.slots.insert(slots)
-    }
-
     /// 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);
@@ -240,9 +294,13 @@ impl Blockchain {
     /// 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>> {
-        // TODO: Make db writes here completely atomic
-        let txs_hashes = self.pending_txs.insert(txs)?;
-        self.pending_txs_order.insert(&txs_hashes)?;
+        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)
     }
@@ -252,7 +310,9 @@ impl Blockchain {
     pub fn get_pending_txs(&self) -> Result<Vec<Transaction>> {
         let txs = self.pending_txs.get_all()?;
         let indexes = self.pending_txs_order.get_all()?;
-        assert_eq!(txs.len(), indexes.len());
+        if txs.len() != indexes.len() {
+            return Err(Error::InvalidInputLengths)
+        }
 
         let mut ret = Vec::with_capacity(txs.len());
         for index in indexes {
@@ -264,13 +324,11 @@ impl Blockchain {
 
     /// Remove a given slice of pending transactions from the blockchain database.
     pub fn remove_pending_txs(&self, txs: &[Transaction]) -> Result<()> {
-        let mut txs_hashes = Vec::with_capacity(txs.len());
-        for tx in txs {
-            let tx_hash = blake3::hash(&serialize(tx));
-            txs_hashes.push(tx_hash);
-        }
-
+        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) {
@@ -278,9 +336,30 @@ impl Blockchain {
             }
         }
 
-        // TODO: Make db writes here completely atomic
-        self.pending_txs.remove(&txs_hashes)?;
-        self.pending_txs_order.remove(&removed_indexes)?;
+        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(())
     }
@@ -296,8 +375,16 @@ pub type BlockchainOverlayPtr = Arc<Mutex<BlockchainOverlay>>;
 pub struct BlockchainOverlay {
     /// Main [`sled_overlay::SledDbOverlay`] to the sled db connection
     pub overlay: SledDbOverlayPtr,
+    /// Headers overlay
+    pub headers: HeaderStoreOverlay,
+    /// Blocks overlay
+    pub blocks: BlockStoreOverlay,
+    /// Block order overlay
+    pub order: BlockOrderStoreOverlay,
     /// Slots overlay
     pub slots: SlotStoreOverlay,
+    /// Transactions overlay
+    pub transactions: TxStoreOverlay,
     /// Contract states overlay
     pub contracts: ContractStateStoreOverlay,
     /// Wasm bincodes overlay
@@ -308,11 +395,122 @@ 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 headers = HeaderStoreOverlay::new(overlay.clone())?;
+        let blocks = BlockStoreOverlay::new(overlay.clone())?;
+        let order = BlockOrderStoreOverlay::new(overlay.clone())?;
         let slots = SlotStoreOverlay::new(overlay.clone())?;
+        let transactions = TxStoreOverlay::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 })))
+        Ok(Arc::new(Mutex::new(Self {
+            overlay,
+            headers,
+            blocks,
+            order,
+            slots,
+            transactions,
+            contracts,
+            wasm_bincode,
+        })))
+    }
+
+    /// Check if blockchain contains any blocks
+    pub fn is_empty(&self) -> Result<bool> {
+        self.order.is_empty()
+    }
+
+    /// 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())
+    }
+
+    /// Insert a given [`BlockInfo`] into the overlay.
+    /// This functions wraps all the logic of separating the block into specific
+    /// data that can be fed into the different trees of the overlay.
+    /// Upon success, the functions returns the block hash that
+    /// were given and appended to the overlay.
+    /// Since we are adding to the overlay, we don't need to exeucte
+    /// the writes atomically.
+    pub fn add_block(&self, block: &BlockInfo) -> Result<blake3::Hash> {
+        // Store transactions
+        self.transactions.insert(&block.txs)?;
+
+        // Store header
+        self.headers.insert(&[block.header.clone()])?;
+
+        // Store block
+        let blk: Block = Block::from(block.clone());
+        let block_hash = self.blocks.insert(&[blk])?[0];
+
+        // Store block order
+        self.order.insert(&[block.header.slot], &[block_hash])?;
+
+        // Store slot checkpoints
+        self.slots.insert(&block.slots)?;
+
+        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)
     }
 
     /// Checkpoint overlay so we can revert to it, if needed.

+ 57 - 19
src/blockchain/slot_store.rs

@@ -16,37 +16,39 @@
  * 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::{blockchain::SledDbOverlayPtr, Error, Result};
+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(sled::Tree);
+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, genesis_block: blake3::Hash) -> Result<Self> {
+    pub fn new(db: &sled::Db) -> Result<Self> {
         let tree = db.open_tree(SLED_SLOT_TREE)?;
-        let store = Self(tree);
-
-        // In case the store is empty, initialize it with the genesis slot.
-        if store.0.is_empty() {
-            let genesis_slot = Slot::genesis_slot(genesis_block);
-            store.insert(&[genesis_slot])?;
-        }
-
-        Ok(store)
+        Ok(Self(tree))
     }
 
     /// Insert a slice of [`Slot`] into the slot store.
-    /// With sled, the operation is done as a batch.
-    /// The slot id is used as the key, while value is the serialized [`Slot`] itself.
     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 {
@@ -54,8 +56,7 @@ impl SlotStore {
             batch.insert(&slot.id.to_be_bytes(), serialized);
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(())
+        Ok(batch)
     }
 
     /// Check if the slot store contains a given id.
@@ -138,7 +139,7 @@ impl SlotStore {
     }
 
     pub fn is_empty(&self) -> bool {
-        self.0.len() == 0
+        self.0.is_empty()
     }
 }
 
@@ -151,11 +152,48 @@ impl SlotStoreOverlay {
         Ok(Self(overlay))
     }
 
-    /// Fetch given id from the slot store.
-    pub fn get(&self, id: u64) -> Result<Vec<u8>> {
+    /// Insert a slice of [`Slot`] into the overlay.
+    /// The slot id is used as the key, while value is the serialized [`Slot`] itself.
+    pub fn insert(&self, slots: &[Slot]) -> Result<()> {
+        let mut lock = self.0.lock().unwrap();
+
+        for slot in slots {
+            let serialized = serialize(slot);
+            lock.insert(SLED_SLOT_TREE, &slot.id.to_be_bytes(), &serialized)?;
+        }
+
+        Ok(())
+    }
+
+    /// Fetch slot from the overlay by id.
+    pub fn get_by_id(&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)),
         }
     }
+
+    /// Fetch given slots from the overlay.
+    /// The resulting vector contains `Option`, which is `Some` if the slot
+    /// was found in the overlay, 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());
+        let lock = self.0.lock().unwrap();
+
+        for id in ids {
+            if let Some(found) = lock.get(SLED_SLOT_TREE, &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)
+    }
 }

+ 123 - 24
src/blockchain/tx_store.rs

@@ -22,6 +22,8 @@ use darkfi_serial::{deserialize, serialize};
 
 use crate::{tx::Transaction, Error, Result};
 
+use super::SledDbOverlayPtr;
+
 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";
@@ -30,7 +32,7 @@ const SLED_PENDING_TX_ORDER_TREE: &[u8] = b"_pending_transactions_order";
 /// transactions where the key is the transaction hash, and the value is
 /// the serialized transaction.
 #[derive(Clone)]
-pub struct TxStore(sled::Tree);
+pub struct TxStore(pub sled::Tree);
 
 impl TxStore {
     /// Opens a new or existing `TxStore` on the given sled database.
@@ -39,13 +41,24 @@ impl TxStore {
         Ok(Self(tree))
     }
 
-    /// Insert a slice of [`Transaction`] into the txstore. With sled, the
-    /// operation is done as a batch.
+    /// 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.
-    pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
+    /// 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();
 
@@ -56,8 +69,7 @@ impl TxStore {
             ret.push(tx_hash);
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(ret)
+        Ok((batch, ret))
     }
 
     /// Check if the txstore contains a given transaction hash.
@@ -115,7 +127,65 @@ impl TxStore {
     }
 
     pub fn is_empty(&self) -> bool {
-        self.0.len() == 0
+        self.0.is_empty()
+    }
+}
+
+/// Overlay structure over a [`TxStore`] instance.
+pub struct TxStoreOverlay(SledDbOverlayPtr);
+
+impl TxStoreOverlay {
+    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree(SLED_TX_TREE)?;
+        Ok(Self(overlay))
+    }
+
+    /// Insert a slice of [`Transaction`] into the overlay.
+    /// 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.
+    pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
+        let mut ret = Vec::with_capacity(transactions.len());
+        let mut lock = self.0.lock().unwrap();
+
+        for tx in transactions {
+            let serialized = serialize(tx);
+            let tx_hash = blake3::hash(&serialized);
+            lock.insert(SLED_TX_TREE, tx_hash.as_bytes(), &serialized)?;
+            ret.push(tx_hash);
+        }
+
+        Ok(ret)
+    }
+
+    /// Fetch given tx hashes from the overlay.
+    /// The resulting vector contains `Option`, which is `Some` if the tx
+    /// was found in the overlay, 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());
+        let lock = self.0.lock().unwrap();
+
+        for tx_hash in tx_hashes {
+            if let Some(found) = lock.get(SLED_TX_TREE, 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)
     }
 }
 
@@ -123,7 +193,7 @@ impl TxStore {
 /// transactions where the key is the transaction hash, and the value is
 /// the serialized transaction.
 #[derive(Clone)]
-pub struct PendingTxStore(sled::Tree);
+pub struct PendingTxStore(pub sled::Tree);
 
 impl PendingTxStore {
     /// Opens a new or existing `PendingTxStore` on the given sled database.
@@ -132,13 +202,24 @@ impl PendingTxStore {
         Ok(Self(tree))
     }
 
-    /// Insert a slice of [`Transaction`] into the pending tx store.
-    /// With sled, the operation is done as a batch.
+    /// 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.
-    pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
+    /// 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();
 
@@ -149,8 +230,7 @@ impl PendingTxStore {
             ret.push(tx_hash);
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(ret)
+        Ok((batch, ret))
     }
 
     /// Check if the pending tx store contains a given transaction hash.
@@ -176,16 +256,22 @@ impl PendingTxStore {
     }
 
     /// Remove a slice of [`blake3::Hash`] from the pending tx store.
-    /// With sled, the operation is done as a batch.
     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());
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(())
+        batch
     }
 }
 
@@ -193,7 +279,7 @@ impl PendingTxStore {
 /// the node pending transactions where the key is an incremental value,
 /// and the value is the serialized transaction.
 #[derive(Clone)]
-pub struct PendingTxOrderStore(sled::Tree);
+pub struct PendingTxOrderStore(pub sled::Tree);
 
 impl PendingTxOrderStore {
     /// Opens a new or existing `PendingTxOrderStore` on the given sled database.
@@ -205,6 +291,14 @@ impl PendingTxOrderStore {
     /// 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()? {
@@ -221,8 +315,7 @@ impl PendingTxOrderStore {
             next_index += 1;
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(())
+        Ok(batch)
     }
 
     /// Retrieve all transactions from the pending tx order store in the form
@@ -244,15 +337,21 @@ impl PendingTxOrderStore {
     }
 
     /// Remove a slice of [`u64`] from the pending tx order store.
-    /// With sled, the operation is done as a batch.
     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());
         }
 
-        self.0.apply_batch(batch)?;
-        Ok(())
+        batch
     }
 }

+ 6 - 1
src/consensus/proto/protocol_sync.rs

@@ -107,6 +107,7 @@ impl ProtocolSync {
             );
 
             // Extra validations can be added here
+            /*
             let key = order.slot;
             let blocks = match self.state.read().await.blockchain.get_blocks_after(key, BATCH) {
                 Ok(v) => v,
@@ -124,6 +125,8 @@ impl ProtocolSync {
                 "Found {} blocks",
                 blocks.len()
             );
+            */
+            let blocks = vec![BlockInfo::default()];
 
             let response = BlockResponse { blocks };
             if let Err(e) = self.channel.send(response).await {
@@ -138,7 +141,7 @@ impl ProtocolSync {
 
     async fn handle_receive_block(self: Arc<Self>) -> Result<()> {
         debug!(target: "consensus::protocol_sync::handle_receive_block()", "START");
-        let exclude_list = vec![self.channel.address()];
+        let _exclude_list = vec![self.channel.address()];
         loop {
             let info = match self.block_sub.receive().await {
                 Ok(v) => v,
@@ -191,6 +194,7 @@ impl ProtocolSync {
                 target: "consensus::protocol_sync::handle_receive_block()",
                 "Processing received block"
             );
+            /*
             let info_copy = (*info).clone();
             match self.state.write().await.receive_finalized_block(info_copy.clone()).await {
                 Ok(v) => {
@@ -218,6 +222,7 @@ impl ProtocolSync {
                     );
                 }
             };
+            */
         }
     }
 

+ 1 - 2
src/consensus/state.rs

@@ -874,8 +874,7 @@ mod tests {
         // Generate dummy state
         let wallet = WalletDb::new("sqlite::memory:", "foo").await?;
         let sled_db = sled::Config::new().temporary(true).open()?;
-        let blockchain =
-            Blockchain::new(&sled_db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
+        let blockchain = Blockchain::new(&sled_db)?;
         let state = ConsensusState::new(
             wallet,
             blockchain,

+ 2 - 2
src/consensus/task/block_sync.rs

@@ -91,11 +91,11 @@ pub async fn block_sync_task(p2p: net::P2pPtr, state: ValidatorStatePtr) -> Resu
                     channel.send(order).await?;
 
                     // Node stores response data.
-                    let resp = block_response_sub.receive().await?;
+                    let _resp = block_response_sub.receive().await?;
 
                     // Verify and store retrieved blocks
                     debug!(target: "consensus::block_sync", "block_sync_task(): Processing received blocks");
-                    state.write().await.receive_sync_blocks(&resp.blocks).await?;
+                    //state.write().await.receive_sync_blocks(&resp.blocks).await?;
 
                     let last_received = state.read().await.blockchain.last()?;
                     info!(target: "consensus::block_sync", "Last received block: {:?} - {:?}", last_received.0, last_received.1);

+ 4 - 2
src/consensus/task/proposal.rs

@@ -279,9 +279,9 @@ async fn propose_period(consensus_p2p: P2pPtr, state: ValidatorStatePtr) -> bool
 /// async function to wait and execute consensus protocol finalization period.
 /// Returns flag in case node needs to resync.
 async fn finalization_period(
-    sync_p2p: P2pPtr,
+    _sync_p2p: P2pPtr,
     state: ValidatorStatePtr,
-    ex: Arc<smol::Executor<'_>>,
+    _ex: Arc<smol::Executor<'_>>,
 ) -> bool {
     // Node sleeps until finalization sync period starts
     let next_slot_start = state.read().await.consensus.time_keeper.next_n_slot_start(1);
@@ -302,6 +302,7 @@ async fn finalization_period(
     let completed_slot = state.read().await.consensus.time_keeper.current_slot();
 
     // Check if any forks can be finalized
+    /*
     match state.write().await.chain_finalization().await {
         Ok((to_broadcast_block, to_broadcast_slots)) => {
             // Broadcasting in background
@@ -336,6 +337,7 @@ async fn finalization_period(
             error!(target: "consensus::proposal", "consensus: Finalization check failed: {}", e);
         }
     }
+    */
 
     // Verify node didn't skip next slot
     completed_slot != state.read().await.consensus.time_keeper.current_slot()

+ 22 - 15
src/consensus/validator.rs

@@ -34,15 +34,8 @@ use log::{debug, error, info, warn};
 use rand::rngs::OsRng;
 use serde_json::json;
 
-use super::{
-    constants,
-    lead_coin::LeadCoin,
-    state::{ConsensusState, Fork, StateCheckpoint},
-    BlockInfo, BlockProposal, Header, LeadInfo, LeadProof,
-};
-
 use crate::{
-    blockchain::{Blockchain, BlockchainOverlay, BlockchainOverlayPtr},
+    blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr},
     rpc::jsonrpc::JsonNotification,
     runtime::vm_runtime::Runtime,
     system::{Subscriber, SubscriberPtr},
@@ -58,6 +51,13 @@ use crate::{
     Error, Result,
 };
 
+use super::{
+    constants,
+    lead_coin::LeadCoin,
+    state::{ConsensusState, Fork, StateCheckpoint},
+    BlockProposal, Header, LeadInfo, LeadProof,
+};
+
 /// Atomic pointer to validator state.
 pub type ValidatorStatePtr = Arc<RwLock<ValidatorState>>;
 
@@ -121,7 +121,11 @@ impl ValidatorState {
             None
         };
 
-        let blockchain = Blockchain::new(db, genesis_ts, genesis_data)?;
+        let blockchain = Blockchain::new(db)?;
+        let mut genesis_block = BlockInfo::default();
+        genesis_block.header.timestamp = genesis_ts;
+        blockchain.add_block(&genesis_block)?;
+
         let consensus = ConsensusState::new(
             wallet.clone(),
             blockchain.clone(),
@@ -769,13 +773,14 @@ impl ValidatorState {
         let fork = self.consensus.forks[fork_index as usize].clone();
 
         // Retrieving proposals to finalize
-        let mut finalized: Vec<BlockInfo> = vec![];
-        for state_checkpoint in &fork.sequence {
-            finalized.push(state_checkpoint.proposal.clone().into());
+        let finalized: Vec<BlockInfo> = vec![];
+        for _state_checkpoint in &fork.sequence {
+            //finalized.push(state_checkpoint.proposal.clone().into());
         }
 
         // Adding finalized proposals to canonical
         info!(target: "consensus::validator", "consensus: Adding {} finalized block to canonical chain.", finalized.len());
+        /*
         match self.blockchain.add(&finalized) {
             Ok(v) => v,
             Err(e) => {
@@ -783,6 +788,7 @@ impl ValidatorState {
                 return Err(e)
             }
         };
+        */
 
         let blocks_subscriber = self.subscribers.get("blocks").unwrap().clone();
 
@@ -835,6 +841,7 @@ impl ValidatorState {
             "consensus: Adding {} finalized slots to canonical chain.",
             finalized_slots.len()
         );
+        /*
         match self.blockchain.add_slots(&finalized_slots) {
             Ok(v) => v,
             Err(e) => {
@@ -846,7 +853,7 @@ impl ValidatorState {
                 return Err(e)
             }
         };
-
+        */
         // Resetting forks and slots
         self.consensus.forks = vec![];
         self.consensus.slots = vec![];
@@ -892,7 +899,7 @@ impl ValidatorState {
         }
 
         info!(target: "consensus::validator", "receive_blocks(): All state transitions passed. Appending blocks to ledger.");
-        self.blockchain.add(blocks)?;
+        //self.blockchain.add(blocks)?;
 
         Ok(())
     }
@@ -1188,7 +1195,7 @@ impl ValidatorState {
             }
             filtered.push(slot.clone());
         }
-        self.blockchain.add_slots(&filtered[..])?;
+        //self.blockchain.add_slots(&filtered[..])?;
 
         Ok(())
     }

+ 4 - 4
src/contract/dao/tests/harness.rs

@@ -18,7 +18,7 @@
 use std::collections::HashMap;
 
 use darkfi::{
-    consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
+    blockchain::BlockInfo,
     runtime::vm_runtime::SMART_CONTRACT_ZKAS_DB_NAME,
     util::time::TimeKeeper,
     validator::{Validator, ValidatorConfig, ValidatorPtr},
@@ -117,9 +117,9 @@ impl DaoTestHarness {
 
         // NOTE: we are not using consensus constants here so we
         // don't get circular dependencies.
-        let time_keeper = TimeKeeper::new(*TESTNET_GENESIS_TIMESTAMP, 10, 90, 0);
-        let config =
-            ValidatorConfig::new(time_keeper, *TESTNET_GENESIS_HASH_BYTES, faucet_pubkeys.to_vec());
+        let genesis_block = BlockInfo::default();
+        let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
+        let config = ValidatorConfig::new(time_keeper, genesis_block, faucet_pubkeys.to_vec());
         let alice_validator = Validator::new(&alice_sled_db, config).await?;
 
         let money_contract_id = *MONEY_CONTRACT_ID;

+ 15 - 10
src/contract/test-harness/src/lib.rs

@@ -19,7 +19,7 @@
 use std::collections::HashMap;
 
 use darkfi::{
-    consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
+    blockchain::BlockInfo,
     runtime::vm_runtime::SMART_CONTRACT_ZKAS_DB_NAME,
     util::time::TimeKeeper,
     validator::{Validator, ValidatorConfig, ValidatorPtr},
@@ -126,7 +126,11 @@ pub struct Wallet {
 }
 
 impl Wallet {
-    pub async fn new(keypair: Keypair, faucet_pubkeys: &[PublicKey]) -> Result<Self> {
+    pub async fn new(
+        keypair: Keypair,
+        genesis_block: &BlockInfo,
+        faucet_pubkeys: &[PublicKey],
+    ) -> Result<Self> {
         let wallet = WalletDb::new("sqlite::memory:", "foo").await?;
         let sled_db = sled::Config::new().temporary(true).open()?;
 
@@ -136,9 +140,9 @@ impl Wallet {
         // Generate validator
         // NOTE: we are not using consensus constants here so we
         // don't get circular dependencies.
-        let time_keeper = TimeKeeper::new(*TESTNET_GENESIS_TIMESTAMP, 10, 90, 0);
+        let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
         let config =
-            ValidatorConfig::new(time_keeper, *TESTNET_GENESIS_HASH_BYTES, faucet_pubkeys.to_vec());
+            ValidatorConfig::new(time_keeper, genesis_block.clone(), faucet_pubkeys.to_vec());
         let validator = Validator::new(&sled_db, config).await?;
 
         // Create necessary Merkle trees for tracking
@@ -174,26 +178,27 @@ pub struct TestHarness {
 impl TestHarness {
     pub async fn new(contracts: &[String]) -> Result<Self> {
         let mut holders = HashMap::new();
+        let genesis_block = BlockInfo::default();
 
         let faucet_kp = Keypair::random(&mut OsRng);
         let faucet_pubkeys = vec![faucet_kp.public];
-        let faucet = Wallet::new(faucet_kp, &faucet_pubkeys).await?;
+        let faucet = Wallet::new(faucet_kp, &genesis_block, &faucet_pubkeys).await?;
         holders.insert(Holder::Faucet, faucet);
 
         let alice_kp = Keypair::random(&mut OsRng);
-        let alice = Wallet::new(alice_kp, &faucet_pubkeys).await?;
+        let alice = Wallet::new(alice_kp, &genesis_block, &faucet_pubkeys).await?;
         // Alice is inserted at end of function
 
         let bob_kp = Keypair::random(&mut OsRng);
-        let bob = Wallet::new(bob_kp, &faucet_pubkeys).await?;
+        let bob = Wallet::new(bob_kp, &genesis_block, &faucet_pubkeys).await?;
         holders.insert(Holder::Bob, bob);
 
         let charlie_kp = Keypair::random(&mut OsRng);
-        let charlie = Wallet::new(charlie_kp, &faucet_pubkeys).await?;
+        let charlie = Wallet::new(charlie_kp, &genesis_block, &faucet_pubkeys).await?;
         holders.insert(Holder::Charlie, charlie);
 
         let rachel_kp = Keypair::random(&mut OsRng);
-        let rachel = Wallet::new(rachel_kp, &faucet_pubkeys).await?;
+        let rachel = Wallet::new(rachel_kp, &genesis_block, &faucet_pubkeys).await?;
         holders.insert(Holder::Rachel, rachel);
 
         // Get the zkas circuits and build proving keys
@@ -440,7 +445,7 @@ impl TestHarness {
 
         // Store generated slot
         for wallet in self.holders.values() {
-            wallet.validator.write().await.receive_slots(&[slot.clone()]).await?;
+            wallet.validator.write().await.receive_test_slot(&slot).await?;
         }
 
         Ok(slot)

+ 3 - 0
src/error.rs

@@ -318,6 +318,9 @@ pub enum Error {
     #[error("Slot {0} not found in database")]
     SlotNotFound(u64),
 
+    #[error("Future slot {0} was received")]
+    FutureSlotReceived(u64),
+
     #[error("Contract {0} not found in database")]
     ContractNotFound(String),
 

+ 1 - 1
src/runtime/import/util.rs

@@ -182,7 +182,7 @@ pub(crate) fn get_slot(ctx: FunctionEnvMut<Env>, slot: u64) -> i64 {
         return CALLER_ACCESS_DENIED.into()
     }
 
-    let ret = match env.blockchain.lock().unwrap().slots.get(slot) {
+    let ret = match env.blockchain.lock().unwrap().slots.get_by_id(slot) {
         Ok(v) => v,
         Err(e) => {
             error!(target: "runtime::db::db_get_slot()", "Internal error getting from slots tree: {}", e);

+ 5 - 11
src/sdk/src/blockchain.rs

@@ -49,17 +49,11 @@ impl Slot {
     ) -> Self {
         Self { id, previous_eta, fork_hashes, fork_previous_hashes, sigma1, sigma2 }
     }
+}
 
-    /// Generate the genesis slot.
-    pub fn genesis_slot(genesis_block: blake3::Hash) -> Self {
-        let previous_eta = pallas::Base::ZERO;
-        let fork_hashes = vec![];
-        // Since genesis block has no previous,
-        // we will use its own hash as its previous.
-        let fork_previous_hashes = vec![genesis_block];
-        let sigma1 = pallas::Base::ZERO;
-        let sigma2 = pallas::Base::ZERO;
-
-        Self::new(0, previous_eta, fork_hashes, fork_previous_hashes, sigma1, sigma2)
+impl Default for Slot {
+    /// Represents the genesis slot on current timestamp
+    fn default() -> Self {
+        Self::new(0, pallas::Base::ZERO, vec![], vec![], pallas::Base::ZERO, pallas::Base::ZERO)
     }
 }

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

@@ -1,567 +0,0 @@
-/* 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, SledDbOverlayPtr};
-
-/// 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()
-    }
-
-    /// 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(&self, previous: &Self) -> Result<()> {
-        let error = Err(Error::BlockIsInvalid(self.blockhash().to_string()));
-        let previous_hash = previous.blockhash();
-
-        // Check previous hash
-        if self.header.previous != previous_hash {
-            return error
-        }
-
-        // Check timestamps are incremental
-        if self.header.timestamp <= previous.header.timestamp {
-            return error
-        }
-
-        // Check slots are incremental
-        if self.header.slot <= previous.header.slot {
-            return error
-        }
-
-        // Verify slots exist
-        let mut slots = self.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.header.slot {
-            return error
-        }
-
-        // Check all slot cover same sequence
-        for slot in &slots {
-            if !slot.fork_hashes.contains(&previous_hash) {
-                return error
-            }
-            if !slot.fork_previous_hashes.contains(&previous.header.previous) {
-                return error
-            }
-        }
-
-        // Check block slot is the last slot in the slice
-        if slots.last().unwrap().id != self.header.slot {
-            return error
-        }
-
-        // TODO: also validate slots etas and sigmas if we can derive them
-        // from previous slots
-
-        Ok(())
-    }
-}
-
-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)
-    }
-}
-
-/// Overlay structure over a [`BlockStore`] instance.
-pub struct BlockStoreOverlay(SledDbOverlayPtr);
-
-impl BlockStoreOverlay {
-    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE)?;
-        Ok(Self(overlay))
-    }
-
-    /// Insert a slice of [`Block`] into the overlay.
-    /// The block 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(&self, blocks: &[Block]) -> Result<Vec<blake3::Hash>> {
-        let mut ret = Vec::with_capacity(blocks.len());
-        let mut lock = self.0.lock().unwrap();
-
-        for block in blocks {
-            let serialized = serialize(block);
-            let blockhash = blake3::hash(&serialized);
-            lock.insert(SLED_BLOCK_TREE, blockhash.as_bytes(), &serialized)?;
-            ret.push(blockhash);
-        }
-
-        Ok(ret)
-    }
-
-    /// Fetch given block hashes from the overlay.
-    /// The resulting vector contains `Option`, which is `Some` if the block
-    /// was found in the overlay, 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());
-        let lock = self.0.lock().unwrap();
-
-        for hash in block_hashes {
-            if let Some(found) = lock.get(SLED_BLOCK_TREE, 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)
-    }
-}
-
-/// 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.is_empty()
-    }
-}
-
-/// Overlay structure over a [`BlockOrderStore`] instance.
-pub struct BlockOrderStoreOverlay(SledDbOverlayPtr);
-
-impl BlockOrderStoreOverlay {
-    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_BLOCK_ORDER_TREE)?;
-        Ok(Self(overlay))
-    }
-
-    /// Insert a slice of slots and blockhashes into the store. With sled, the
-    /// operation is done as a batch.
-    /// The block slot is used as the key, and the blockhash is used as value.
-    pub fn insert(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
-        if slots.len() != hashes.len() {
-            return Err(Error::InvalidInputLengths)
-        }
-
-        let mut lock = self.0.lock().unwrap();
-
-        for (i, sl) in slots.iter().enumerate() {
-            lock.insert(SLED_BLOCK_ORDER_TREE, &sl.to_be_bytes(), hashes[i].as_bytes())?;
-        }
-
-        Ok(())
-    }
-
-    /// Fetch given slots from the overlay.
-    /// The resulting vector contains `Option`, which is `Some` if the slot
-    /// was found in the overlay, 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());
-        let lock = self.0.lock().unwrap();
-
-        for slot in slots {
-            if let Some(found) = lock.get(SLED_BLOCK_ORDER_TREE, &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)
-    }
-
-    /// Fetch the last blockhash in the overlay, based on the `Ord`
-    /// implementation for `Vec<u8>`.
-    pub fn get_last(&self) -> Result<(u64, blake3::Hash)> {
-        let found = self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)?.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))
-    }
-
-    /// Check if overlay contains any records
-    pub fn is_empty(&self) -> Result<bool> {
-        Ok(self.0.lock().unwrap().is_empty(SLED_BLOCK_ORDER_TREE)?)
-    }
-}
-
-/// 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 }
-    }
-}

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

@@ -1,287 +0,0 @@
-/* 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.
-    /// NOTE: this function is not used right now, we keep it for future proofing,
-    ///       and its obviously untested.
-    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)
-        }
-
-        // Remove the deleted tree from the state pointer set.
-        state_pointers.retain(|x| *x != ptr);
-        self.0.insert(contract_id_bytes, serialize(&state_pointers))?;
-
-        // Drop the deleted tree from the database
-        db.drop_tree(ptr)?;
-
-        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)
-    }
-}

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

@@ -1,522 +0,0 @@
-/* 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, BlockOrderStoreOverlay, BlockStore, BlockStoreOverlay,
-};
-
-pub mod header_store;
-pub use header_store::{Header, HeaderStore, HeaderStoreOverlay};
-
-pub mod slot_store;
-pub use slot_store::{SlotStore, SlotStoreOverlay};
-
-pub mod tx_store;
-pub use tx_store::{PendingTxOrderStore, PendingTxStore, TxStore, TxStoreOverlay};
-
-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 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(&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(&[blocks[index].1, block.1])?;
-            full_blocks[1].validate(&full_blocks[0])?;
-        }
-
-        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.is_empty()
-    }
-
-    /// 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,
-    /// Headers overlay
-    pub headers: HeaderStoreOverlay,
-    /// Blocks overlay
-    pub blocks: BlockStoreOverlay,
-    /// Block order overlay
-    pub order: BlockOrderStoreOverlay,
-    /// Slots overlay
-    pub slots: SlotStoreOverlay,
-    /// Transactions overlay
-    pub transactions: TxStoreOverlay,
-    /// 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 headers = HeaderStoreOverlay::new(overlay.clone())?;
-        let blocks = BlockStoreOverlay::new(overlay.clone())?;
-        let order = BlockOrderStoreOverlay::new(overlay.clone())?;
-        let slots = SlotStoreOverlay::new(overlay.clone())?;
-        let transactions = TxStoreOverlay::new(overlay.clone())?;
-        let contracts = ContractStateStoreOverlay::new(overlay.clone())?;
-        let wasm_bincode = WasmStoreOverlay::new(overlay.clone())?;
-
-        Ok(Arc::new(Mutex::new(Self {
-            overlay,
-            headers,
-            blocks,
-            order,
-            slots,
-            transactions,
-            contracts,
-            wasm_bincode,
-        })))
-    }
-
-    /// Check if blockchain contains any blocks
-    pub fn is_empty(&self) -> Result<bool> {
-        self.order.is_empty()
-    }
-
-    /// 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())
-    }
-
-    /// Insert a given [`BlockInfo`] into the overlay.
-    /// This functions wraps all the logic of separating the block into specific
-    /// data that can be fed into the different trees of the overlay.
-    /// Upon success, the functions returns the block hash that
-    /// were given and appended to the overlay.
-    /// Since we are adding to the overlay, we don't need to exeucte
-    /// the writes atomically.
-    pub fn add_block(&self, block: &BlockInfo) -> Result<blake3::Hash> {
-        // Store transactions
-        self.transactions.insert(&block.txs)?;
-
-        // Store header
-        self.headers.insert(&[block.header.clone()])?;
-
-        // Store block
-        let blk: Block = Block::from(block.clone());
-        let block_hash = self.blocks.insert(&[blk])?[0];
-
-        // Store block order
-        self.order.insert(&[block.header.slot], &[block_hash])?;
-
-        // Store slot checkpoints
-        self.slots.insert(&block.slots)?;
-
-        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)
-    }
-
-    /// 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(())
-    }
-}

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

@@ -1,199 +0,0 @@
-/* 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.is_empty()
-    }
-}
-
-/// 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))
-    }
-
-    /// Insert a slice of [`Slot`] into the overlay.
-    /// The slot id is used as the key, while value is the serialized [`Slot`] itself.
-    pub fn insert(&self, slots: &[Slot]) -> Result<()> {
-        let mut lock = self.0.lock().unwrap();
-
-        for slot in slots {
-            let serialized = serialize(slot);
-            lock.insert(SLED_SLOT_TREE, &slot.id.to_be_bytes(), &serialized)?;
-        }
-
-        Ok(())
-    }
-
-    /// Fetch slot from the overlay by id.
-    pub fn get_by_id(&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)),
-        }
-    }
-
-    /// Fetch given slots from the overlay.
-    /// The resulting vector contains `Option`, which is `Some` if the slot
-    /// was found in the overlay, 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());
-        let lock = self.0.lock().unwrap();
-
-        for id in ids {
-            if let Some(found) = lock.get(SLED_SLOT_TREE, &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)
-    }
-}

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

@@ -1,357 +0,0 @@
-/* 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};
-
-use super::SledDbOverlayPtr;
-
-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.is_empty()
-    }
-}
-
-/// Overlay structure over a [`TxStore`] instance.
-pub struct TxStoreOverlay(SledDbOverlayPtr);
-
-impl TxStoreOverlay {
-    pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_TX_TREE)?;
-        Ok(Self(overlay))
-    }
-
-    /// Insert a slice of [`Transaction`] into the overlay.
-    /// 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.
-    pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
-        let mut ret = Vec::with_capacity(transactions.len());
-        let mut lock = self.0.lock().unwrap();
-
-        for tx in transactions {
-            let serialized = serialize(tx);
-            let tx_hash = blake3::hash(&serialized);
-            lock.insert(SLED_TX_TREE, tx_hash.as_bytes(), &serialized)?;
-            ret.push(tx_hash);
-        }
-
-        Ok(ret)
-    }
-
-    /// Fetch given tx hashes from the overlay.
-    /// The resulting vector contains `Option`, which is `Some` if the tx
-    /// was found in the overlay, 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());
-        let lock = self.0.lock().unwrap();
-
-        for tx_hash in tx_hashes {
-            if let Some(found) = lock.get(SLED_TX_TREE, 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)
-    }
-}
-
-/// 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
-    }
-}

+ 9 - 6
src/validator/consensus/mod.rs

@@ -16,7 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use crate::{blockchain::Blockchain, util::time::TimeKeeper};
+use crate::{
+    blockchain::{BlockInfo, Blockchain},
+    util::time::TimeKeeper,
+};
 
 /// This struct represents the information required by the consensus algorithm
 pub struct Consensus {
@@ -29,11 +32,11 @@ pub struct Consensus {
 }
 
 impl Consensus {
-    pub fn new(
-        blockchain: Blockchain,
-        time_keeper: TimeKeeper,
-        genesis_block: blake3::Hash,
-    ) -> Self {
+    /// Generate a new Consensus state. On init, genesis block
+    /// hash is the BlockInfo::default one, so caller must
+    /// set the correct one, if different.
+    pub fn new(blockchain: Blockchain, time_keeper: TimeKeeper) -> Self {
+        let genesis_block = BlockInfo::default().blockhash();
         Self { blockchain, time_keeper, genesis_block }
     }
 }

+ 84 - 95
src/validator/mod.rs

@@ -19,37 +19,34 @@
 use std::{collections::HashMap, io::Cursor};
 
 use async_std::sync::{Arc, RwLock};
-use darkfi_sdk::{
-    blockchain::Slot,
-    crypto::{PublicKey, CONSENSUS_CONTRACT_ID, DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
-    pasta::pallas,
-};
-use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
+use darkfi_sdk::{blockchain::Slot, crypto::PublicKey, pasta::pallas};
+use darkfi_serial::{Decodable, Encodable, WriteExt};
 use log::{debug, error, info, warn};
 
 use crate::{
-    blockchain::{Blockchain, BlockchainOverlay, BlockchainOverlayPtr},
+    blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr},
     error::TxVerifyFailed,
     runtime::vm_runtime::Runtime,
     tx::Transaction,
     util::time::TimeKeeper,
     zk::VerifyingKey,
-    Result,
+    Error, Result,
 };
 
-/// DarkFi blockchain
-pub mod blockchain;
-
-/// DarkFi consensus
+/// DarkFi consensus module
 pub mod consensus;
 use consensus::Consensus;
 
+/// Helper utilities
+pub mod utils;
+use utils::deploy_native_contracts;
+
 /// Configuration for initializing [`Validator`]
 pub struct ValidatorConfig {
     /// Helper structure to calculate time related operations
     pub time_keeper: TimeKeeper,
     /// Genesis block
-    pub genesis_block: blake3::Hash,
+    pub genesis_block: BlockInfo,
     /// Whitelisted faucet pubkeys (testnet stuff)
     pub faucet_pubkeys: Vec<PublicKey>,
 }
@@ -57,7 +54,7 @@ pub struct ValidatorConfig {
 impl ValidatorConfig {
     pub fn new(
         time_keeper: TimeKeeper,
-        genesis_block: blake3::Hash,
+        genesis_block: BlockInfo,
         faucet_pubkeys: Vec<PublicKey>,
     ) -> Self {
         Self { time_keeper, genesis_block, faucet_pubkeys }
@@ -80,87 +77,47 @@ impl Validator {
         info!(target: "validator", "Initializing Validator");
 
         info!(target: "validator", "Initializing Blockchain");
-        // TODO: Initialize chain, then check if its empty, so we can add the
-        // genesis block and its transactions
-        let blockchain = Blockchain::new(db, config.time_keeper.genesis_ts, config.genesis_block)?;
+        let blockchain = Blockchain::new(db)?;
 
         info!(target: "validator", "Initializing Consensus");
-        let consensus =
-            Consensus::new(blockchain.clone(), config.time_keeper, config.genesis_block);
-
-        // =====================
-        // NATIVE WASM CONTRACTS
-        // =====================
-        // This is the current place where native contracts are being deployed.
-        // When the `Blockchain` object is created, it doesn't care whether it
-        // already has the contract data or not. If there's existing data, it
-        // will just open the necessary db and trees, and give back what it has.
-        // This means that on subsequent runs our native contracts will already
-        // be in a deployed state, so what we actually do here is a redeployment.
-        // This kind of operation should only modify the contract's state in case
-        // it wasn't deployed before (meaning the initial run). Otherwise, it
-        // shouldn't touch anything, or just potentially update the db schemas or
-        // whatever is necessary. This logic should be handled in the init function
-        // of the actual contract, so make sure the native contracts handle this well.
-
-        // The faucet pubkeys are pubkeys which are allowed to create clear inputs
-        // in the Money contract.
-        let money_contract_deploy_payload = serialize(&config.faucet_pubkeys);
-
-        // The DAO contract uses an empty payload to deploy itself.
-        let dao_contract_deploy_payload = vec![];
-
-        // The Consensus contract uses an empty payload to deploy itself.
-        let consensus_contract_deploy_payload = vec![];
-
-        let native_contracts = vec![
-            (
-                "Money Contract",
-                *MONEY_CONTRACT_ID,
-                include_bytes!("../contract/money/money_contract.wasm").to_vec(),
-                money_contract_deploy_payload,
-            ),
-            (
-                "DAO Contract",
-                *DAO_CONTRACT_ID,
-                include_bytes!("../contract/dao/dao_contract.wasm").to_vec(),
-                dao_contract_deploy_payload,
-            ),
-            (
-                "Consensus Contract",
-                *CONSENSUS_CONTRACT_ID,
-                include_bytes!("../contract/consensus/consensus_contract.wasm").to_vec(),
-                consensus_contract_deploy_payload,
-            ),
-        ];
-
-        info!(target: "validator", "Deploying native WASM contracts");
-        let blockchain_overlay = BlockchainOverlay::new(&blockchain)?;
+        let consensus = Consensus::new(blockchain.clone(), config.time_keeper.clone());
 
-        for nc in native_contracts {
-            info!(target: "validator", "Deploying {} with ContractID {}", nc.0, nc.1);
+        // Create the actual state
+        let mut state = Self { blockchain: blockchain.clone(), consensus };
 
-            let mut runtime = Runtime::new(
-                &nc.2[..],
-                blockchain_overlay.clone(),
-                nc.1,
-                consensus.time_keeper.clone(),
-            )?;
+        // Create an overlay over whole blockchain so we can write stuff
+        let blockchain_overlay = BlockchainOverlay::new(&blockchain)?;
 
-            runtime.deploy(&nc.3)?;
+        // Add genesis block if blockchain is empty
+        let genesis_block = match blockchain.genesis() {
+            Ok((_, hash)) => hash,
+            Err(_) => {
+                info!(target: "validator", "Appending genesis block");
+                state
+                    .add_blocks(
+                        blockchain_overlay.clone(),
+                        &config.time_keeper,
+                        &[config.genesis_block.clone()],
+                    )
+                    .await?;
+                config.genesis_block.blockhash()
+            }
+        };
+        state.consensus.genesis_block = genesis_block;
 
-            info!(target: "validator", "Successfully deployed {}", nc.0);
-        }
+        // Deploy native wasm contracts
+        deploy_native_contracts(
+            blockchain_overlay.clone(),
+            &config.time_keeper,
+            &config.faucet_pubkeys,
+        )?;
 
         // Write the changes to the actual chain db
         blockchain_overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
 
-        info!(target: "validator", "Finished deployment of native WASM contracts");
-
-        // Create the actual state
-        let state = Arc::new(RwLock::new(Self { blockchain, consensus }));
+        info!(target: "validator", "Finished initializing validator");
 
-        Ok(state)
+        Ok(Arc::new(RwLock::new(state)))
     }
 
     // ==========================
@@ -175,20 +132,43 @@ impl Validator {
     // 2) When a transaction is being broadcasted to us
     // ==========================
 
-    /// Append to canonical state received finalized slots from block sync task.
-    // TODO: integrate this to receive_blocks, as slots will be part of received block.
-    pub async fn receive_slots(&mut self, slots: &[Slot]) -> Result<()> {
-        debug!(target: "validator", "receive_slots(): Appending slots to ledger");
-        let current_slot = self.consensus.time_keeper.current_slot();
-        let mut filtered = vec![];
-        for slot in slots {
+    /// Append provided blocks to the provided overlay. Block sequence must be valid,
+    /// meaning that each block and its transactions are valid, in order.
+    pub async fn add_blocks(
+        &self,
+        overlay: BlockchainOverlayPtr,
+        _time_keeper: &TimeKeeper,
+        blocks: &[BlockInfo],
+    ) -> Result<()> {
+        // Retrieve last block
+        let lock = overlay.lock().unwrap();
+        let mut previous = if !lock.is_empty()? { Some(lock.last_block()?) } else { None };
+        // Validate and insert each block
+        for block in blocks {
+            // Check if block already exists
+            if lock.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 {
+                block.validate(&p)?;
+            }
+
+            // TODO: Add rest block verifications here
+            /*
+            let current_slot = self.consensus.time_keeper.current_slot();
             if slot.id > current_slot {
-                warn!(target: "validator", "receive_slots(): Ignoring future slot: {}", slot.id);
-                continue
+                return Err(Error::FutureSlotReceived(slot.id))
             }
-            filtered.push(slot.clone());
+            */
+
+            // Insert block
+            lock.add_block(block)?;
+
+            // Use last inserted block as next iteration previous
+            previous = Some(block.clone());
         }
-        self.blockchain.add_slots(&filtered[..])?;
 
         Ok(())
     }
@@ -376,4 +356,13 @@ impl Validator {
         overlay.apply()?;
         Ok(())
     }
+
+    /// Append to canonical state received slot.
+    /// This should be only used for test purposes.
+    pub async fn receive_test_slot(&mut self, slot: &Slot) -> Result<()> {
+        debug!(target: "validator", "receive_slot(): Appending slot to ledger");
+        self.blockchain.slots.insert(&[slot.clone()])?;
+
+        Ok(())
+    }
 }

+ 89 - 0
src/validator/utils.rs

@@ -0,0 +1,89 @@
+/* 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::{PublicKey, CONSENSUS_CONTRACT_ID, DAO_CONTRACT_ID, MONEY_CONTRACT_ID};
+use darkfi_serial::serialize;
+use log::info;
+
+use crate::{
+    blockchain::BlockchainOverlayPtr, runtime::vm_runtime::Runtime, util::time::TimeKeeper, Result,
+};
+
+/// Deploy DarkFi native wasm contracts to provided blockchain overlay.
+/// If overlay already contains the contracts, it will just open the
+/// necessary db and trees, and give back what it has. This means that
+/// on subsequent runs, our native contracts will already be in a deployed
+/// state, so what we actually do here is a redeployment. This kind of
+/// operation should only modify the contract's state in case it wasn't
+/// deployed before (meaning the initial run). Otherwise, it shouldn't
+/// touch anything, or just potentially update the db schemas or whatever
+/// is necessary. This logic should be handled in the init function of
+/// the actual contract, so make sure the native contracts handle this well.
+pub fn deploy_native_contracts(
+    blockchain_overlay: BlockchainOverlayPtr,
+    time_keeper: &TimeKeeper,
+    faucet_pubkeys: &Vec<PublicKey>,
+) -> Result<()> {
+    info!(target: "validator", "Deploying native WASM contracts");
+
+    // The faucet pubkeys are pubkeys which are allowed to create clear inputs
+    // in the Money contract.
+    let money_contract_deploy_payload = serialize(faucet_pubkeys);
+
+    // The DAO contract uses an empty payload to deploy itself.
+    let dao_contract_deploy_payload = vec![];
+
+    // The Consensus contract uses an empty payload to deploy itself.
+    let consensus_contract_deploy_payload = vec![];
+
+    let native_contracts = vec![
+        (
+            "Money Contract",
+            *MONEY_CONTRACT_ID,
+            include_bytes!("../contract/money/money_contract.wasm").to_vec(),
+            money_contract_deploy_payload,
+        ),
+        (
+            "DAO Contract",
+            *DAO_CONTRACT_ID,
+            include_bytes!("../contract/dao/dao_contract.wasm").to_vec(),
+            dao_contract_deploy_payload,
+        ),
+        (
+            "Consensus Contract",
+            *CONSENSUS_CONTRACT_ID,
+            include_bytes!("../contract/consensus/consensus_contract.wasm").to_vec(),
+            consensus_contract_deploy_payload,
+        ),
+    ];
+
+    for nc in native_contracts {
+        info!(target: "validator", "Deploying {} with ContractID {}", nc.0, nc.1);
+
+        let mut runtime =
+            Runtime::new(&nc.2[..], blockchain_overlay.clone(), nc.1, time_keeper.clone())?;
+
+        runtime.deploy(&nc.3)?;
+
+        info!(target: "validator", "Successfully deployed {}", nc.0);
+    }
+
+    info!(target: "validator", "Finished deployment of native WASM contracts");
+
+    Ok(())
+}

+ 1 - 1
tests/blockchain.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi::{
-    validator::blockchain::{BlockInfo, Blockchain, BlockchainOverlay, Header},
+    blockchain::{BlockInfo, Blockchain, BlockchainOverlay, Header},
     Error, Result,
 };
 use darkfi_sdk::{