Quellcode durchsuchen

blockchain/contract_store: monotree handling refactor

This commit replaces the in-memory contracts states monotree with a sled one. Monotree update handling is changed to always use new changes, as contracts or their tree drops are handled via the overlay reverts. Each contract now includes its wasm as a record in its monotree, so no separate wasm monotree is needed. Native contracts zkas is also included in their monotree, locking it down from changes.
skoupidi vor 6 Monaten
Ursprung
Commit
830497f5c7

+ 1 - 1
bin/darkfid/genesis_block_localnet

@@ -1 +1 @@
-AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAAAAAACyKVVpAAAAADs822+Z8eocSeM2cBLVQuRsIQzi4hikKA26/mTDYRMIIlQXZEaVFnJWvmiC+TszUPIPCL7dIay1KIvSqKILIm4AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAAAAAAAuTGppAAAAADs822+Z8eocSeM2cBLVQuRsIQzi4hikKA26/mTDYRMI9JKJ8RgIn79FQjTthZZ4JNlbuoMTaklPpyWHRVmBLIUAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

+ 1 - 1
bin/darkfid/genesis_block_mainnet

@@ -1 +1 @@
-AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAAAAAAC+KVVpAAAAADs822+Z8eocSeM2cBLVQuRsIQzi4hikKA26/mTDYRMIIlQXZEaVFnJWvmiC+TszUPIPCL7dIay1KIvSqKILIm4AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAAAAAAB2TGppAAAAADs822+Z8eocSeM2cBLVQuRsIQzi4hikKA26/mTDYRMI9JKJ8RgIn79FQjTthZZ4JNlbuoMTaklPpyWHRVmBLIUAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

+ 1 - 1
bin/darkfid/genesis_block_testnet

@@ -1 +1 @@
-AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAAAAAAC4KVVpAAAAADs822+Z8eocSeM2cBLVQuRsIQzi4hikKA26/mTDYRMIIlQXZEaVFnJWvmiC+TszUPIPCL7dIay1KIvSqKILIm4AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAAAAAABTTGppAAAAADs822+Z8eocSeM2cBLVQuRsIQzi4hikKA26/mTDYRMI9JKJ8RgIn79FQjTthZZ4JNlbuoMTaklPpyWHRVmBLIUAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

+ 3 - 10
bin/darkfid/src/registry/model.rs

@@ -33,7 +33,7 @@ use darkfi::{
     validator::{consensus::Fork, verification::apply_producer_transaction, ValidatorPtr},
     zk::{empty_witnesses, ProvingKey, ZkCircuit},
     zkas::ZkBinary,
-    Error, Result,
+    Result,
 };
 use darkfi_money_contract::{
     client::pow_reward_v1::PoWRewardCallBuilder, MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
@@ -302,15 +302,8 @@ pub async fn generate_next_block_template(
     // Grab the updated contracts states root
     let diff =
         extended_fork.overlay.lock().unwrap().overlay.lock().unwrap().diff(&extended_fork.diffs)?;
-    extended_fork
-        .overlay
-        .lock()
-        .unwrap()
-        .contracts
-        .update_state_monotree(&diff, &mut extended_fork.state_monotree)?;
-    let Some(state_root) = extended_fork.state_monotree.get_headroot()? else {
-        return Err(Error::ContractsStatesRootNotFoundError);
-    };
+    let state_root =
+        extended_fork.overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
 
     // Generate the new header
     let mut header =

+ 25 - 6
bin/darkfid/src/task/unknown_proposal.rs

@@ -515,12 +515,18 @@ async fn handle_reorg(
         }
     }
 
-    // Rebuild fork contracts states monotree
-    if let Err(e) = peer_fork.compute_monotree() {
-        error!(target: "darkfid::task::handle_reorg", "Rebuilding peer fork monotree failed: {e}");
-        peer_fork.purge_new_trees();
-        return false
-    }
+    // Grab current overlay diff and use it as the first diff of the
+    // peer fork, so all consecutive diffs represent just the proposal
+    // changes.
+    let diff = match peer_fork.overlay.lock().unwrap().overlay.lock().unwrap().diff(&[]) {
+        Ok(d) => d,
+        Err(e) => {
+            error!(target: "darkfid::task::handle_reorg", "Generate full inverse diff failed: {e}");
+            peer_fork.purge_new_trees();
+            return false
+        }
+    };
+    peer_fork.diffs = vec![diff];
 
     // Retrieve the proposals of the hashes sequence, in batches
     info!(target: "darkfid::task::handle_reorg", "Peer sequence ranks higher than our current best fork, retrieving {} proposals from peer...", peer_header_hashes.len());
@@ -634,6 +640,19 @@ async fn handle_reorg(
 
     // Execute the reorg
     info!(target: "darkfid::task::handle_reorg", "Peer fork ranks higher than our current best fork, executing reorg...");
+    if let Err(e) = peer_fork
+        .overlay
+        .lock()
+        .unwrap()
+        .overlay
+        .lock()
+        .unwrap()
+        .apply_diff(&peer_fork.diffs.remove(0))
+    {
+        error!(target: "darkfid::task::handle_reorg", "Applying full inverse diff failed: {e}");
+        peer_fork.purge_new_trees();
+        return false
+    };
     *validator.consensus.module.write().await = module;
     *forks = vec![peer_fork];
     drop(forks);

+ 4 - 4
bin/darkfid/src/tests/harness.rs

@@ -93,8 +93,9 @@ impl Harness {
         vks::inject(&sled_db, &vks)?;
         let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
         deploy_native_contracts(&overlay, config.pow_target).await?;
+        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
         genesis_block.header.state_root =
-            overlay.lock().unwrap().get_state_monotree()?.get_headroot()?.unwrap();
+            overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
 
         // Generate validators configuration
         // NOTE: we are not using consensus constants here so we
@@ -249,8 +250,8 @@ impl Harness {
             &mut MerkleTree::new(1),
         )
         .await?;
-        block.header.state_root =
-            overlay.lock().unwrap().get_state_monotree()?.get_headroot()?.unwrap();
+        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
+        block.header.state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
 
         // Attach signature
         block.sign(&keypair.secret);
@@ -260,7 +261,6 @@ impl Harness {
             &fork.overlay,
             &fork.diffs,
             &fork.module,
-            &mut fork.state_monotree,
             &block,
             &previous,
             self.alice.validator.verify_fees,

+ 3 - 9
bin/darkfid/src/tests/mod.rs

@@ -86,7 +86,6 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
         &fork.overlay,
         &fork.diffs,
         &fork.module,
-        &mut fork.state_monotree,
         &block3,
         &block2,
         th.alice.validator.verify_fees,
@@ -260,14 +259,9 @@ fn darkfid_programmatic_control() -> Result<()> {
                 )
                 .unwrap();
                 darkfi::validator::utils::deploy_native_contracts(&overlay, 20).await.unwrap();
-                genesis_block.header.state_root = overlay
-                    .lock()
-                    .unwrap()
-                    .get_state_monotree()
-                    .unwrap()
-                    .get_headroot()
-                    .unwrap()
-                    .unwrap();
+                let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[]).unwrap();
+                genesis_block.header.state_root =
+                    overlay.lock().unwrap().contracts.update_state_monotree(&diff).unwrap();
                 let config = darkfi::validator::ValidatorConfig {
                     confirmation_threshold: 1,
                     pow_target: 20,

+ 4 - 6
script/research/gg/src/main.rs

@@ -36,7 +36,7 @@ use darkfi::{
     },
     zk::{empty_witnesses, ProvingKey, ZkCircuit},
     zkas::ZkBinary,
-    Error, Result,
+    Result,
 };
 use darkfi_contract_test_harness::vks;
 use darkfi_money_contract::{
@@ -170,11 +170,9 @@ fn main() -> Result<()> {
                 genesis_block.header.transactions_root = tree.root(0).unwrap();
 
                 // Grab the updated contracts states root
-                let state_monotree = overlay.lock().unwrap().get_state_monotree()?;
-                let Some(state_root) = state_monotree.get_headroot()? else {
-                    return Err(Error::ContractsStatesRootNotFoundError);
-                };
-                genesis_block.header.state_root = state_root;
+                let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
+                genesis_block.header.state_root =
+                    overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
 
                 // Write generated genesis block to stdin
                 let encoded = base64::encode(&serialize_async(&genesis_block).await);

+ 160 - 413
src/blockchain/contract_store.rs

@@ -20,10 +20,10 @@ use std::{collections::BTreeMap, io::Cursor};
 
 use darkfi_sdk::{
     crypto::contract_id::{
-        ContractId, NATIVE_CONTRACT_IDS_BYTES, NATIVE_CONTRACT_ZKAS_DB_NAMES,
-        SMART_CONTRACT_MONOTREE_DB_NAME, SMART_CONTRACT_ZKAS_DB_NAME,
+        ContractId, NATIVE_CONTRACT_IDS_BYTES, SMART_CONTRACT_MONOTREE_DB_NAME,
+        SMART_CONTRACT_ZKAS_DB_NAME,
     },
-    monotree::{MemoryDb, Monotree, SledOverlayDb, SledTreeDb, EMPTY_HASH},
+    monotree::{Hash as StateHash, Monotree, SledOverlayDb, SledTreeDb, EMPTY_HASH},
 };
 use darkfi_serial::{deserialize, serialize};
 use sled_overlay::{sled, SledDbOverlayStateDiff};
@@ -39,6 +39,11 @@ use super::{parse_record, SledDbOverlayPtr};
 
 pub const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
 pub const SLED_CONTRACTS_TREES_TREE: &[u8] = b"_contracts_trees";
+// blake3 hash of `_contracts_monotree`
+pub const SLED_CONTRACTS_MONOTREE_TREE: &[u8; 32] = &[
+    82, 161, 124, 97, 228, 243, 197, 75, 11, 86, 60, 214, 241, 24, 64, 100, 86, 48, 159, 147, 254,
+    116, 94, 17, 165, 22, 39, 3, 149, 120, 122, 175,
+];
 pub const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode";
 
 /// The `ContractStore` is a structure representing all `sled` trees related
@@ -73,6 +78,17 @@ pub struct ContractStore {
     /// ```
     /// These values get mutated with `init()` and `remove()`.
     pub state_trees: sled::Tree,
+    /// The `sled` tree storing the full contracts states monotree,
+    /// excluding native contracts wasm bincodes.
+    /// The layout looks like this:
+    /// ```plaintext
+    ///  tree: "blake3(_contracts_monotree)"
+    ///   key: blake3(ContractId)
+    /// value: blake3(contract monotree root)
+    /// ```
+    /// These values get mutated on each block/proposal append with
+    /// `update_state_monotree()`.
+    pub state_monotree: sled::Tree,
 }
 
 impl ContractStore {
@@ -81,7 +97,8 @@ impl ContractStore {
         let wasm = db.open_tree(SLED_BINCODE_TREE)?;
         let state = db.open_tree(SLED_CONTRACTS_TREE)?;
         let state_trees = db.open_tree(SLED_CONTRACTS_TREES_TREE)?;
-        Ok(Self { wasm, state, state_trees })
+        let state_monotree = db.open_tree(SLED_CONTRACTS_MONOTREE_TREE)?;
+        Ok(Self { wasm, state, state_trees, state_monotree })
     }
 
     /// Fetches the bincode for a given ContractId from the store's wasm tree.
@@ -273,86 +290,16 @@ impl ContractStore {
         Ok(ret)
     }
 
-    /// Generate a Monotree(SMT) containing all contracts states
-    /// roots, along with the wasm bincodes monotree root.
+    /// Retrieve contracts states Monotree(SMT) current root.
     ///
-    /// Note: native contracts zkas tree and wasm bincodes are excluded.
-    pub fn get_state_monotree(&self, db: &sled::Db) -> Result<Monotree<MemoryDb>> {
-        // Initialize the monotree
-        debug!(target: "blockchain::contractstore::get_state_monotree", "Initializing global monotree...");
-        let mut root = None;
-        let monotree_db = MemoryDb::new();
-        let mut tree = Monotree::new(monotree_db);
-
-        // Iterate over current contracts states records
-        for state_record in self.state.iter() {
-            // Grab its monotree pointer
-            let (contract_id, state_pointers): (ContractId, Vec<[u8; 32]>) =
-                parse_record(state_record?)?;
-            let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
-
-            // Check it exists
-            if !state_pointers.contains(&state_monotree_ptr) {
-                return Err(Error::ContractStateNotFound)
-            }
-            if !self.state_trees.contains_key(state_monotree_ptr)? {
-                return Err(Error::ContractStateNotFound)
-            }
-
-            // Grab its monotree
-            let state_tree = db.open_tree(state_monotree_ptr)?;
-            let state_monotree_db = SledTreeDb::new(&state_tree);
-            let state_monotree = Monotree::new(state_monotree_db);
-
-            // Insert its root to the global monotree
-            let state_monotree_root = match state_monotree.get_headroot()? {
-                Some(hash) => hash,
-                None => *EMPTY_HASH,
-            };
-            debug!(target: "blockchain::contractstore::get_state_monotree", "Contract {contract_id} root: {}", blake3::Hash::from(state_monotree_root));
-            root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
-            debug!(target: "blockchain::contractstore::get_state_monotree", "New global root: {}", blake3::Hash::from(root.unwrap()));
-        }
-
-        // Iterate over current contracts wasm bincodes to compute its monotree root
-        debug!(target: "blockchain::contractstore::get_state_monotree", "Initializing wasm bincodes monotree...");
-        let mut wasm_monotree_root = None;
-        let wasm_monotree_db = MemoryDb::new();
-        let mut wasm_monotree = Monotree::new(wasm_monotree_db);
-        for record in self.wasm.iter() {
-            let (key, value) = record?;
-
-            // Skip native ones
-            if NATIVE_CONTRACT_IDS_BYTES.contains(&deserialize(&key)?) {
-                continue
-            }
-
-            // Insert record
-            let key = blake3::hash(&key);
-            let value = blake3::hash(&value);
-            debug!(target: "blockchain::contractstore::get_state_monotree", "Inserting key {key} with value: {value}");
-            wasm_monotree_root = wasm_monotree.insert(
-                wasm_monotree_root.as_ref(),
-                key.as_bytes(),
-                value.as_bytes(),
-            )?;
-        }
-
-        // Insert wasm bincodes root to the global monotree
-        let wasm_monotree_root = match wasm_monotree_root {
+    /// Note: native contracts wasm bincodes are excluded.
+    pub fn get_state_monotree_root(&self) -> Result<StateHash> {
+        let monotree_db = SledTreeDb::new(&self.state_monotree);
+        let monotree = Monotree::new(monotree_db);
+        Ok(match monotree.get_headroot()? {
             Some(hash) => hash,
             None => *EMPTY_HASH,
-        };
-        debug!(target: "blockchain::contractstore::get_state_monotree", "New root: {}", blake3::Hash::from(wasm_monotree_root));
-        root = tree.insert(
-            root.as_ref(),
-            blake3::hash(SLED_BINCODE_TREE).as_bytes(),
-            &wasm_monotree_root,
-        )?;
-        debug!(target: "blockchain::contractstore::get_state_monotree", "New global root: {}", blake3::Hash::from(root.unwrap()));
-        tree.set_headroot(root.as_ref());
-
-        Ok(tree)
+        })
     }
 }
 
@@ -364,6 +311,7 @@ impl ContractStoreOverlay {
         overlay.lock().unwrap().open_tree(SLED_BINCODE_TREE, true)?;
         overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREE, true)?;
         overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREES_TREE, true)?;
+        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_MONOTREE_TREE, true)?;
         Ok(Self(overlay.clone()))
     }
 
@@ -496,220 +444,69 @@ impl ContractStoreOverlay {
         Ok((zkbin, vk))
     }
 
-    /// Generate a Monotree(SMT) containing all contracts states
-    /// roots, along with the wasm bincodes monotree roots.
-    /// Be carefull as this will open all states monotrees in the
-    /// overlay, and all contract state trees if their monotrees
-    /// need rebuild.
+    /// Retrieve contracts states Monotree(SMT) current root.
     ///
-    /// Note: native contracts zkas tree and wasm bincodes are
-    /// excluded.
-    pub fn get_state_monotree(&self) -> Result<Monotree<MemoryDb>> {
+    /// Note: native contracts wasm bincodes are excluded.
+    pub fn get_state_monotree_root(&self) -> Result<StateHash> {
         let mut lock = self.0.lock().unwrap();
-
-        // Grab all states pointers
-        debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Retrieving state pointers...");
-        let mut states_monotrees_pointers = vec![];
-        for state_record in lock.iter(SLED_CONTRACTS_TREE)? {
-            // Grab its monotree pointer
-            let (contract_id, mut state_pointers): (ContractId, Vec<[u8; 32]>) =
-                parse_record(state_record?)?;
-            let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
-
-            // Check it exists
-            if !state_pointers.contains(&state_monotree_ptr) {
-                return Err(Error::ContractStateNotFound)
-            }
-            if !lock.contains_key(SLED_CONTRACTS_TREES_TREE, &state_monotree_ptr)? {
-                return Err(Error::ContractStateNotFound)
-            }
-
-            // Skip native zkas trees
-            if NATIVE_CONTRACT_IDS_BYTES.contains(&contract_id.to_bytes()) {
-                state_pointers.retain(|ptr| !NATIVE_CONTRACT_ZKAS_DB_NAMES.contains(ptr));
-            }
-
-            states_monotrees_pointers.push((contract_id, state_pointers, state_monotree_ptr));
-        }
-
-        // Initialize the monotree
-        debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Initializing global monotree...");
-        let mut root = None;
-        let monotree_db = MemoryDb::new();
-        let mut tree = Monotree::new(monotree_db);
-
-        // Iterate over contract states monotrees pointers
-        for (contract_id, state_pointers, state_monotree_ptr) in states_monotrees_pointers {
-            // Iterate over contract state pointers to find their
-            // inserted keys. If any of them has dropped keys, we must
-            // rebuild the contract state monotree.
-            debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Updating monotree for contract: {contract_id}");
-            let mut rebuild = false;
-            let mut inserts = vec![];
-            'outer: for state_ptr in &state_pointers {
-                // Skip the actual monotree state pointer
-                if state_ptr == &state_monotree_ptr {
-                    continue
-                }
-
-                // Look for it in the overlay
-                for (state_key, state_cache) in &lock.state.caches {
-                    if state_key != state_ptr {
-                        continue
-                    }
-
-                    // Check if it has dropped keys
-                    if !state_cache.state.removed.is_empty() {
-                        rebuild = true;
-                        break 'outer
-                    }
-
-                    // Grab the new/updated keys
-                    for (key, value) in &state_cache.state.cache {
-                        let key = blake3::hash(key);
-                        let value = blake3::hash(value);
-                        inserts.push((key, value))
-                    }
-                    break
-                }
-            }
-
-            // Check if we need to rebuild it
-            if rebuild {
-                // Iterate over all contract states to grab the monotree keys
-                debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Rebuilding monotree...");
-                inserts = vec![];
-                for state_ptr in state_pointers {
-                    // Open the contract state
-                    lock.open_tree(&state_ptr, false)?;
-
-                    // If the pointer is the monotree one, clear it
-                    if state_ptr == state_monotree_ptr {
-                        lock.clear(&state_ptr)?;
-                        continue
-                    }
-
-                    // Grab all its keys
-                    for record in lock.iter(&state_ptr)? {
-                        let (key, value) = record?;
-                        let key = blake3::hash(&key);
-                        let value = blake3::hash(&value);
-                        inserts.push((key, value))
-                    }
-                }
-            }
-
-            // Grab its monotree
-            let state_monotree_db = SledOverlayDb::new(&mut lock, &state_monotree_ptr)?;
-            let mut state_monotree = Monotree::new(state_monotree_db);
-            let mut state_monotree_root =
-                if rebuild { None } else { state_monotree.get_headroot()? };
-            let state_monotree_root_str = match state_monotree_root {
-                Some(hash) => blake3::Hash::from(hash),
-                None => blake3::Hash::from(*EMPTY_HASH),
-            };
-            debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Current root: {state_monotree_root_str}");
-
-            // Update or insert new records
-            for (key, value) in &inserts {
-                debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Inserting key {key} with value: {value}");
-                state_monotree_root = state_monotree.insert(
-                    state_monotree_root.as_ref(),
-                    key.as_bytes(),
-                    value.as_bytes(),
-                )?;
-            }
-
-            // Set root
-            state_monotree.set_headroot(state_monotree_root.as_ref());
-
-            // Insert its root to the global monotree
-            let state_monotree_root = match state_monotree_root {
-                Some(hash) => hash,
-                None => *EMPTY_HASH,
-            };
-            debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "New root: {}", blake3::Hash::from(state_monotree_root));
-            root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
-        }
-
-        // Iterate over current contracts wasm bincodes to compute its monotree root
-        debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Initializing wasm bincodes monotree...");
-        let mut wasm_monotree_root = None;
-        let wasm_monotree_db = MemoryDb::new();
-        let mut wasm_monotree = Monotree::new(wasm_monotree_db);
-        for record in lock.iter(SLED_BINCODE_TREE)? {
-            let (key, value) = record?;
-
-            // Skip native ones
-            if NATIVE_CONTRACT_IDS_BYTES.contains(&deserialize(&key)?) {
-                continue
-            }
-
-            // Insert record
-            let key = blake3::hash(&key);
-            let value = blake3::hash(&value);
-            debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "Inserting key {key} with value: {value}");
-            wasm_monotree_root = wasm_monotree.insert(
-                wasm_monotree_root.as_ref(),
-                key.as_bytes(),
-                value.as_bytes(),
-            )?;
-        }
-
-        // Insert wasm bincodes root to the global monotree
-        let wasm_monotree_root = match wasm_monotree_root {
+        let monotree_db = SledOverlayDb::new(&mut lock, SLED_CONTRACTS_MONOTREE_TREE)?;
+        let monotree = Monotree::new(monotree_db);
+        Ok(match monotree.get_headroot()? {
             Some(hash) => hash,
             None => *EMPTY_HASH,
-        };
-        debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "New root: {}", blake3::Hash::from(wasm_monotree_root));
-        root = tree.insert(
-            root.as_ref(),
-            blake3::hash(SLED_BINCODE_TREE).as_bytes(),
-            &wasm_monotree_root,
-        )?;
-        debug!(target: "blockchain::contractstoreoverlay::get_state_monotree", "New global root: {}", blake3::Hash::from(root.unwrap()));
-        tree.set_headroot(root.as_ref());
-
-        Ok(tree)
+        })
     }
 
     /// Retrieve all updated contracts states and wasm bincodes from
     /// provided overlay diff, update their monotrees in the overlay
-    /// and their records in the provided Monotree(SMT).
+    /// their root records in the contracts states Monotree(SMT) and
+    /// return its current root. The provided diff must always append
+    /// new changes to the monotrees and it shouldn't contain dropped
+    /// contracts.
     ///
-    /// Note: native contracts zkas tree and wasm bincodes are
-    /// excluded.
-    pub fn update_state_monotree(
-        &self,
-        diff: &SledDbOverlayStateDiff,
-        tree: &mut Monotree<MemoryDb>,
-    ) -> Result<()> {
-        // If a contract was dropped, we must rebuild the monotree from
-        // scratch.
-        if let Some((state_cache, _)) = diff.caches.get(SLED_CONTRACTS_TREE) {
-            if !state_cache.removed.is_empty() {
-                debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Rebuilding global monotree...");
-                *tree = self.get_state_monotree()?;
-                return Ok(());
-            }
-        }
-
+    /// Note: native contracts wasm bincodes are excluded.
+    pub fn update_state_monotree(&self, diff: &SledDbOverlayStateDiff) -> Result<StateHash> {
         // Grab lock over the overlay
         let mut lock = self.0.lock().unwrap();
         debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Retrieving contracts updates...");
 
-        // If a contract tree was dropped, we must rebuild its monotree
-        // from scratch.
-        let mut contracts_updates = BTreeMap::new();
-        if let Some((state_cache, _)) = diff.caches.get(SLED_CONTRACTS_TREES_TREE) {
-            // Mark all the contracts of dropped trees for rebuild
-            for contract_id_bytes in state_cache.removed.values() {
-                contracts_updates.insert(contract_id_bytes.clone(), (true, vec![]));
-            }
-        }
-
         // Iterate over diff caches to find all contracts updates
+        let mut contracts_updates: BTreeMap<[u8; 32], ContractMonotreeUpdates> = BTreeMap::new();
         for (state_key, state_cache) in &diff.caches {
+            // Grab new/redeployed contracts wasm bincodes to include them
+            // in their monotrees, excluding native ones.
+            if state_key == SLED_BINCODE_TREE {
+                for (contract_id_bytes, (_, value)) in &state_cache.0.cache {
+                    // Grab the actual contract ID bytes
+                    let contract_id_bytes = deserialize(contract_id_bytes)?;
+
+                    // Skip native ones
+                    if NATIVE_CONTRACT_IDS_BYTES.contains(&contract_id_bytes) {
+                        continue
+                    }
+
+                    // Grab its contract monotree state pointer
+                    let contract_id: ContractId = deserialize(&contract_id_bytes)?;
+                    let monotree_pointer =
+                        contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
+
+                    // Grab its record from the map
+                    let mut contract_updates = match contracts_updates.remove(&contract_id_bytes) {
+                        Some(r) => r,
+                        None => ContractMonotreeUpdates::new(monotree_pointer),
+                    };
+
+                    // Create the new/updated wasm bincode record
+                    let key = blake3::hash(&contract_id_bytes);
+                    let value = blake3::hash(value);
+                    contract_updates.inserts.push((key, value));
+
+                    // Insert the update record
+                    contracts_updates.insert(contract_id_bytes, contract_updates);
+                }
+                continue
+            }
+
             // Check if that cache is not a contract state one.
             // Overlay protected trees are all the native/non-contract
             // ones.
@@ -718,185 +515,135 @@ impl ContractStoreOverlay {
             }
 
             // Grab the actual state key
-            let state_key = deserialize(state_key)?;
-
-            // Skip native zkas tree
-            if NATIVE_CONTRACT_ZKAS_DB_NAMES.contains(&state_key) {
-                continue
-            }
+            let state_key: [u8; 32] = deserialize(state_key)?;
 
             // Grab its contract id
             let Some(contract_id_bytes) = lock.get(SLED_CONTRACTS_TREES_TREE, &state_key)? else {
                 return Err(Error::ContractStateNotFound)
             };
+            let contract_id_bytes: [u8; 32] = deserialize(&contract_id_bytes)?;
             let contract_id: ContractId = deserialize(&contract_id_bytes)?;
 
             // Skip the actual monotree state cache
-            let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
-            if state_monotree_ptr == state_key {
+            let monotree_pointer = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
+            if monotree_pointer == state_key {
                 continue
             }
 
             // Grab its record from the map
-            let (rebuild, mut inserts) = match contracts_updates.get(&contract_id_bytes) {
-                Some(r) => r.clone(),
-                None => (false, vec![]),
+            let mut contract_updates = match contracts_updates.remove(&contract_id_bytes) {
+                Some(r) => r,
+                None => ContractMonotreeUpdates::new(monotree_pointer),
             };
 
-            // Check if the contract monotree is already marked for
-            // rebuild.
-            if rebuild {
-                continue
-            }
-
-            // If records have been dropped, mark the contract monotree
-            // for rebuild.
-            if !state_cache.0.removed.is_empty() {
-                contracts_updates.insert(contract_id_bytes, (true, vec![]));
-                continue
-            }
-
             // Grab the new/updated keys
             for (key, (_, value)) in &state_cache.0.cache {
-                let key = blake3::hash(key);
+                // Prefix key with its tree name
+                let mut hasher = blake3::Hasher::new();
+                hasher.update(&state_key);
+                hasher.update(key);
+                let key = hasher.finalize();
                 let value = blake3::hash(value);
-                inserts.push((key, value))
+                contract_updates.inserts.push((key, value));
             }
-            contracts_updates.insert(contract_id_bytes, (rebuild, inserts));
-        }
 
-        // Grab current root
-        let mut root = tree.get_headroot()?;
-        let root_str = match root {
-            Some(hash) => blake3::Hash::from(hash),
-            None => blake3::Hash::from(*EMPTY_HASH),
-        };
-        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating global monotree with root: {root_str}");
+            // Grab the dropped keys
+            for key in state_cache.0.removed.keys() {
+                // Prefix key with its tree name
+                let mut hasher = blake3::Hasher::new();
+                hasher.update(&state_key);
+                hasher.update(key);
+                let key = hasher.finalize();
+                contract_updates.removals.push(key);
+            }
 
-        // Iterate over contracts updates
-        for (contract_id_bytes, (rebuild, mut inserts)) in contracts_updates {
+            // Insert the update record
+            contracts_updates.insert(contract_id_bytes, contract_updates);
+        }
+
+        // Apply all contracts updates and grab their new roots
+        let mut contracts_roots = BTreeMap::new();
+        for (contract_id_bytes, contract_updates) in contracts_updates {
             let contract_id: ContractId = deserialize(&contract_id_bytes)?;
-            let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
             debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating monotree for contract: {contract_id}");
 
-            // Check if we need to rebuild it
-            if rebuild {
-                // Grab its state pointers
-                let state_pointers = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
-                let mut state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
-
-                // Skip native zkas trees
-                if NATIVE_CONTRACT_IDS_BYTES.contains(&contract_id.to_bytes()) {
-                    state_pointers.retain(|ptr| !NATIVE_CONTRACT_ZKAS_DB_NAMES.contains(ptr));
-                }
-
-                // Iterate over all contract states to grab the monotree keys
-                debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Rebuilding monotree...");
-                for state_ptr in state_pointers {
-                    // Open the contract state
-                    lock.open_tree(&state_ptr, false)?;
-
-                    // If the pointer is the monotree one, clear it
-                    if state_ptr == state_monotree_ptr {
-                        lock.clear(&state_ptr)?;
-                        continue
-                    }
-
-                    // Grab all its keys
-                    for record in lock.iter(&state_ptr)? {
-                        let (key, value) = record?;
-                        let key = blake3::hash(&key);
-                        let value = blake3::hash(&value);
-                        inserts.push((key, value))
-                    }
-                }
-            }
-
             // Grab its monotree
-            let state_monotree_db = SledOverlayDb::new(&mut lock, &state_monotree_ptr)?;
-            let mut state_monotree = Monotree::new(state_monotree_db);
-            let mut state_monotree_root =
-                if rebuild { None } else { state_monotree.get_headroot()? };
-            let state_monotree_root_str = match state_monotree_root {
+            let monotree_db = SledOverlayDb::new(&mut lock, &contract_updates.monotree_pointer)?;
+            let mut monotree = Monotree::new(monotree_db);
+            let mut monotree_root = monotree.get_headroot()?;
+            let monotree_root_hash = match monotree_root {
                 Some(hash) => blake3::Hash::from(hash),
                 None => blake3::Hash::from(*EMPTY_HASH),
             };
-            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Current root: {state_monotree_root_str}");
+            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Current root: {monotree_root_hash}");
 
             // Update or insert new records
-            for (key, value) in &inserts {
+            for (key, value) in &contract_updates.inserts {
                 debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Inserting key {key} with value: {value}");
-                state_monotree_root = state_monotree.insert(
-                    state_monotree_root.as_ref(),
-                    key.as_bytes(),
-                    value.as_bytes(),
-                )?;
+                monotree_root =
+                    monotree.insert(monotree_root.as_ref(), key.as_bytes(), value.as_bytes())?;
+            }
+
+            // Remove dropped records
+            for key in &contract_updates.removals {
+                debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Removing key: {key}");
+                monotree_root = monotree.remove(monotree_root.as_ref(), key.as_bytes())?;
             }
 
             // Set root
-            state_monotree.set_headroot(state_monotree_root.as_ref());
+            monotree.set_headroot(monotree_root.as_ref());
 
-            // Insert its root to the global monotree
-            let state_monotree_root = match state_monotree_root {
+            // Keep track of the new root for the main monotree
+            let monotree_root = match monotree_root {
                 Some(hash) => hash,
                 None => *EMPTY_HASH,
             };
-            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New root: {}", blake3::Hash::from(state_monotree_root));
-            root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
+            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New root: {}", blake3::Hash::from(monotree_root));
+            contracts_roots.insert(contract_id_bytes, monotree_root);
         }
 
-        // Check if wasm bincodes cache exists
-        let Some((wasm_cache, _)) = diff.caches.get(SLED_BINCODE_TREE) else {
-            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New global root: {}", blake3::Hash::from(root.unwrap()));
-            tree.set_headroot(root.as_ref());
-            return Ok(())
+        // Grab the contracts states monotree
+        let monotree_db = SledOverlayDb::new(&mut lock, SLED_CONTRACTS_MONOTREE_TREE)?;
+        let mut monotree = Monotree::new(monotree_db);
+        let mut monotree_root = monotree.get_headroot()?;
+        let monotree_root_hash = match monotree_root {
+            Some(hash) => blake3::Hash::from(hash),
+            None => blake3::Hash::from(*EMPTY_HASH),
         };
-
-        // Check if wasm bincodes cache is updated
-        if wasm_cache.cache.is_empty() && wasm_cache.removed.is_empty() {
-            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New global root: {}", blake3::Hash::from(root.unwrap()));
-            tree.set_headroot(root.as_ref());
-            return Ok(())
-        }
-
-        // Iterate over current contracts wasm bincodes to compute its monotree root
-        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating wasm bincodes monotree...");
-        let mut wasm_monotree_root = None;
-        let wasm_monotree_db = MemoryDb::new();
-        let mut wasm_monotree = Monotree::new(wasm_monotree_db);
-        for record in lock.iter(SLED_BINCODE_TREE)? {
-            let (key, value) = record?;
-
-            // Skip native ones
-            if NATIVE_CONTRACT_IDS_BYTES.contains(&deserialize(&key)?) {
-                continue
-            }
-
-            // Insert record
-            let key = blake3::hash(&key);
-            let value = blake3::hash(&value);
-            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Inserting key {key} with value: {value}");
-            wasm_monotree_root = wasm_monotree.insert(
-                wasm_monotree_root.as_ref(),
-                key.as_bytes(),
-                value.as_bytes(),
+        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating global monotree with root: {monotree_root_hash}");
+
+        // Insert new/updated contracts monotrees roots
+        for (contract_id_bytes, contract_monotree_root) in &contracts_roots {
+            monotree_root = monotree.insert(
+                monotree_root.as_ref(),
+                contract_id_bytes,
+                contract_monotree_root,
             )?;
         }
 
-        // Insert wasm bincodes root to the global monotree
-        let wasm_monotree_root = match wasm_monotree_root {
+        // Set new global root
+        monotree.set_headroot(monotree_root.as_ref());
+
+        // Return its hash
+        let monotree_root = match monotree_root {
             Some(hash) => hash,
             None => *EMPTY_HASH,
         };
-        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New root: {}", blake3::Hash::from(wasm_monotree_root));
-        root = tree.insert(
-            root.as_ref(),
-            blake3::hash(SLED_BINCODE_TREE).as_bytes(),
-            &wasm_monotree_root,
-        )?;
-        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New global root: {}", blake3::Hash::from(root.unwrap()));
-        tree.set_headroot(root.as_ref());
+        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New global root: {}", blake3::Hash::from(monotree_root));
 
-        Ok(())
+        Ok(monotree_root)
+    }
+}
+
+/// Auxiliary struct representing a contract monotree updates.
+struct ContractMonotreeUpdates {
+    monotree_pointer: [u8; 32],
+    inserts: Vec<(blake3::Hash, blake3::Hash)>,
+    removals: Vec<blake3::Hash>,
+}
+
+impl ContractMonotreeUpdates {
+    fn new(monotree_pointer: [u8; 32]) -> Self {
+        Self { monotree_pointer, inserts: vec![], removals: vec![] }
     }
 }

+ 1 - 22
src/blockchain/mod.rs

@@ -21,10 +21,7 @@ use std::{
     sync::{Arc, Mutex},
 };
 
-use darkfi_sdk::{
-    monotree::{self, Monotree},
-    tx::TransactionHash,
-};
+use darkfi_sdk::tx::TransactionHash;
 use darkfi_serial::{deserialize, Decodable};
 use sled_overlay::{
     sled,
@@ -452,14 +449,6 @@ impl Blockchain {
         Ok(())
     }
 
-    /// Generate a Monotree(SMT) containing all contracts states
-    /// roots, along with the wasm bincodes monotree root.
-    ///
-    /// Note: native contracts zkas tree and wasm bincodes are excluded.
-    pub fn get_state_monotree(&self) -> Result<Monotree<monotree::MemoryDb>> {
-        self.contracts.get_state_monotree(&self.sled_db)
-    }
-
     /// Grab the RandomX VM current and next key, based on provided key
     /// changing height and delay. Optionally, a height can be provided
     /// to get the keys before it.
@@ -717,16 +706,6 @@ impl BlockchainOverlay {
 
         Ok(Arc::new(Mutex::new(Self { overlay, headers, blocks, transactions, contracts })))
     }
-
-    /// Generate a Monotree(SMT) containing all contracts states
-    /// roots, along with the wasm bincodes monotree root.
-    /// A clone is used so we are not affected by the opened trees
-    /// during roots computing.
-    ///
-    /// Note: native contracts zkas tree and wasm bincodes are excluded.
-    pub fn get_state_monotree(&self) -> Result<Monotree<monotree::MemoryDb>> {
-        self.full_clone()?.lock().unwrap().contracts.get_state_monotree()
-    }
 }
 
 /// Parse a sled record in the form of a tuple (`key`, `value`).

+ 2 - 1
src/contract/test-harness/src/lib.rs

@@ -277,8 +277,9 @@ impl TestHarness {
         vks::inject(&sled_db, &vks)?;
         let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
         deploy_native_contracts(&overlay, 90).await?;
+        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
         genesis_block.header.state_root =
-            overlay.lock().unwrap().get_state_monotree()?.get_headroot()?.unwrap();
+            overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
 
         // Create `Wallet` instances
         let mut holders_map = HashMap::new();

+ 2 - 2
src/contract/test-harness/src/money_pow_reward.rs

@@ -139,8 +139,8 @@ impl TestHarness {
             &mut MerkleTree::new(1),
         )
         .await?;
-        block.header.state_root =
-            overlay.lock().unwrap().get_state_monotree()?.get_headroot()?.unwrap();
+        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
+        block.header.state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
 
         // Attach signature
         block.sign(&wallet.keypair.secret);

+ 5 - 42
src/validator/consensus.rs

@@ -18,11 +18,7 @@
 
 use std::collections::{HashMap, HashSet};
 
-use darkfi_sdk::{
-    crypto::MerkleTree,
-    monotree::{self, Monotree},
-    tx::TransactionHash,
-};
+use darkfi_sdk::{crypto::MerkleTree, tx::TransactionHash};
 use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
 use num_bigint::BigUint;
 use sled_overlay::database::SledDbOverlayStateDiff;
@@ -231,9 +227,6 @@ impl Consensus {
             fork.hashes_rank += hash_distance_sq;
         }
 
-        // Rebuild fork contracts states monotree
-        fork.compute_monotree()?;
-
         // Drop forks lock
         drop(forks);
 
@@ -687,13 +680,10 @@ impl Consensus {
         // Grab a lock over current forks
         let lock = self.forks.read().await;
 
-        // Rebuild current canonical contract states monotree
-        let state_monotree = self.blockchain.get_state_monotree()?;
+        // Grab current canonical contracts states monotree root
+        let state_root = self.blockchain.contracts.get_state_monotree_root()?;
 
         // Check that the root matches last block header state root
-        let Some(state_root) = state_monotree.get_headroot()? else {
-            return Err(Error::ContractsStatesRootNotFoundError);
-        };
         let last_block_state_root = self.blockchain.last_header()?.state_root;
         if state_root != last_block_state_root {
             return Err(Error::ContractsStatesRootError(
@@ -746,8 +736,6 @@ pub struct Fork {
     pub overlay: BlockchainOverlayPtr,
     /// Current PoW module state
     pub module: PoWModule,
-    /// Current contracts states Monotree(SMT)
-    pub state_monotree: Monotree<monotree::MemoryDb>,
     /// Fork proposal hashes sequence
     pub proposals: Vec<HeaderHash>,
     /// Fork proposal overlay diffs sequence
@@ -764,8 +752,6 @@ impl Fork {
     pub async fn new(blockchain: Blockchain, module: PoWModule) -> Result<Self> {
         let mempool = blockchain.get_pending_txs()?.iter().map(|tx| tx.hash()).collect();
         let overlay = BlockchainOverlay::new(&blockchain)?;
-        // Build current contract states monotree
-        let state_monotree = overlay.lock().unwrap().get_state_monotree()?;
         // Retrieve last block difficulty to access current ranks
         let last_difficulty = blockchain.last_block_difficulty()?;
         let targets_rank = last_difficulty.ranks.targets_rank;
@@ -774,7 +760,6 @@ impl Fork {
             blockchain,
             overlay,
             module,
-            state_monotree,
             proposals: vec![],
             diffs: vec![],
             mempool,
@@ -940,7 +925,6 @@ impl Fork {
         let blockchain = self.blockchain.clone();
         let overlay = self.overlay.lock().unwrap().full_clone()?;
         let module = self.module.clone();
-        let state_monotree = self.state_monotree.clone();
         let proposals = self.proposals.clone();
         let diffs = self.diffs.clone();
         let mempool = self.mempool.clone();
@@ -951,7 +935,6 @@ impl Fork {
             blockchain,
             overlay,
             module,
-            state_monotree,
             proposals,
             diffs,
             mempool,
@@ -960,12 +943,6 @@ impl Fork {
         })
     }
 
-    /// Build current contract states monotree.
-    pub fn compute_monotree(&mut self) -> Result<()> {
-        self.state_monotree = self.overlay.lock().unwrap().get_state_monotree()?;
-        Ok(())
-    }
-
     /// Auxiliary function to check current contracts states
     /// Monotree(SMT) validity.
     ///
@@ -973,22 +950,8 @@ impl Fork {
     ///       a fork doesn't contain changes over the last appended
     //        proposal.
     pub fn healthcheck(&self) -> Result<()> {
-        // Rebuild current contract states monotree
-        let state_monotree = self.overlay.lock().unwrap().get_state_monotree()?;
-
-        // Check that it matches forks' tree
-        let Some(state_root) = state_monotree.get_headroot()? else {
-            return Err(Error::ContractsStatesRootNotFoundError);
-        };
-        let Some(fork_state_root) = self.state_monotree.get_headroot()? else {
-            return Err(Error::ContractsStatesRootNotFoundError);
-        };
-        if state_root != fork_state_root {
-            return Err(Error::ContractsStatesRootError(
-                blake3::Hash::from_bytes(state_root).to_string(),
-                blake3::Hash::from_bytes(fork_state_root).to_string(),
-            ));
-        }
+        // Grab current contracts states monotree root
+        let state_root = self.overlay.lock().unwrap().contracts.get_state_monotree_root()?;
 
         // Check that the root matches last block header state root
         let last_block_state_root = self.last_proposal()?.block.header.state_root;

+ 10 - 39
src/validator/mod.rs

@@ -105,6 +105,11 @@ impl Validator {
         // Deploy native wasm contracts
         deploy_native_contracts(&overlay, config.pow_target).await?;
 
+        // Update the contracts states monotree in case native
+        // contracts zkas has changed.
+        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
+        overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
+
         // Add genesis block if blockchain is empty
         if blockchain.genesis().is_err() {
             info!(target: "validator::new", "Appending genesis block");
@@ -441,9 +446,6 @@ impl Validator {
         // Grab current PoW module to validate each block
         let mut module = self.consensus.module.read().await.clone();
 
-        // Grab current contracts states monotree to validate each block
-        let mut state_monotree = overlay.lock().unwrap().get_state_monotree()?;
-
         // Keep track of all blocks transactions to remove them from pending txs store
         let mut removed_txs = vec![];
 
@@ -455,15 +457,8 @@ impl Validator {
         // Validate and insert each block
         for (index, block) in blocks.iter().enumerate() {
             // Verify block
-            match verify_checkpoint_block(
-                &overlay,
-                &diffs,
-                &mut state_monotree,
-                block,
-                &headers[index],
-                module.target,
-            )
-            .await
+            match verify_checkpoint_block(&overlay, &diffs, block, &headers[index], module.target)
+                .await
             {
                 Ok(()) => { /* Do nothing */ }
                 // Skip already existing block
@@ -552,9 +547,6 @@ impl Validator {
         // Grab current PoW module to validate each block
         let mut module = self.consensus.module.read().await.clone();
 
-        // Grab current contracts states monotree to validate each block
-        let mut state_monotree = overlay.lock().unwrap().get_state_monotree()?;
-
         // Keep track of all blocks transactions to remove them from pending txs store
         let mut removed_txs = vec![];
 
@@ -566,17 +558,7 @@ impl Validator {
         // Validate and insert each block
         for block in blocks {
             // Verify block
-            match verify_block(
-                &overlay,
-                &diffs,
-                &module,
-                &mut state_monotree,
-                block,
-                previous,
-                self.verify_fees,
-            )
-            .await
-            {
+            match verify_block(&overlay, &diffs, &module, block, previous, self.verify_fees).await {
                 Ok(()) => { /* Do nothing */ }
                 // Skip already existing block
                 Err(Error::BlockAlreadyExists(_)) => {
@@ -786,9 +768,6 @@ impl Validator {
         // Create a PoW module to validate each block
         let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, Some(0))?;
 
-        // Grab current contracts states monotree to validate each block
-        let mut state_monotree = overlay.lock().unwrap().get_state_monotree()?;
-
         // Keep track of all block database state diffs
         let mut diffs = vec![];
 
@@ -801,16 +780,8 @@ impl Validator {
             let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
 
             // Verify block
-            if let Err(e) = verify_block(
-                &overlay,
-                &diffs,
-                &module,
-                &mut state_monotree,
-                &block,
-                &previous,
-                self.verify_fees,
-            )
-            .await
+            if let Err(e) =
+                verify_block(&overlay, &diffs, &module, &block, &previous, self.verify_fees).await
             {
                 error!(target: "validator::validate_blockchain", "Erroneous block found in set: {e}");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;

+ 8 - 21
src/validator/verification.rs

@@ -26,7 +26,6 @@ use darkfi_sdk::{
     },
     dark_tree::dark_forest_leaf_vec_integrity_check,
     deploy::DeployParamsV1,
-    monotree::{self, Monotree},
     pasta::pallas,
 };
 use darkfi_serial::{deserialize_async, serialize_async, AsyncDecodable, AsyncEncodable};
@@ -116,11 +115,9 @@ pub async fn verify_genesis_block(
         return Err(Error::BlockIsInvalid(block_hash))
     }
 
-    // Verify header contracts states root
-    let state_monotree = overlay.lock().unwrap().contracts.get_state_monotree()?;
-    let Some(state_root) = state_monotree.get_headroot()? else {
-        return Err(Error::ContractsStatesRootNotFoundError);
-    };
+    // Update the contracts states monotree and verify header contracts states root
+    let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
+    let state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
     if state_root != block.header.state_root {
         return Err(Error::ContractsStatesRootError(
             blake3::Hash::from_bytes(state_root).to_string(),
@@ -212,7 +209,6 @@ pub async fn verify_block(
     overlay: &BlockchainOverlayPtr,
     diffs: &[SledDbOverlayStateDiff],
     module: &PoWModule,
-    state_monotree: &mut Monotree<monotree::MemoryDb>,
     block: &BlockInfo,
     previous: &BlockInfo,
     verify_fees: bool,
@@ -270,12 +266,9 @@ pub async fn verify_block(
         return Err(Error::BlockIsInvalid(block_hash.as_string()))
     }
 
-    // Update the provided contracts states monotree and verify header contracts states root
+    // Update the contracts states monotree and verify header contracts states root
     let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(diffs)?;
-    overlay.lock().unwrap().contracts.update_state_monotree(&diff, state_monotree)?;
-    let Some(state_root) = state_monotree.get_headroot()? else {
-        return Err(Error::ContractsStatesRootNotFoundError);
-    };
+    let state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
     if state_root != block.header.state_root {
         return Err(Error::ContractsStatesRootError(
             blake3::Hash::from_bytes(state_root).to_string(),
@@ -297,7 +290,6 @@ pub async fn verify_block(
 pub async fn verify_checkpoint_block(
     overlay: &BlockchainOverlayPtr,
     diffs: &[SledDbOverlayStateDiff],
-    state_monotree: &mut Monotree<monotree::MemoryDb>,
     block: &BlockInfo,
     header: &HeaderHash,
     block_target: u32,
@@ -350,12 +342,9 @@ pub async fn verify_checkpoint_block(
         return Err(Error::BlockIsInvalid(block_hash.as_string()))
     }
 
-    // Update the provided contracts states monotree and verify header contracts states root
+    // Update the contracts states monotree and verify header contracts states root
     let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(diffs)?;
-    overlay.lock().unwrap().contracts.update_state_monotree(&diff, state_monotree)?;
-    let Some(state_root) = state_monotree.get_headroot()? else {
-        return Err(Error::ContractsStatesRootNotFoundError);
-    };
+    let state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
     if state_root != block.header.state_root {
         return Err(Error::ContractsStatesRootError(
             blake3::Hash::from_bytes(state_root).to_string(),
@@ -1113,7 +1102,7 @@ pub async fn verify_proposal(
     }
 
     // Check if proposal extends any existing forks
-    let (mut fork, index) = consensus.find_extended_fork(proposal).await?;
+    let (fork, index) = consensus.find_extended_fork(proposal).await?;
 
     // Grab overlay last block
     let previous = fork.overlay.lock().unwrap().last_block()?;
@@ -1123,7 +1112,6 @@ pub async fn verify_proposal(
         &fork.overlay,
         &fork.diffs,
         &fork.module,
-        &mut fork.state_monotree,
         &proposal.block,
         &previous,
         verify_fees,
@@ -1167,7 +1155,6 @@ pub async fn verify_fork_proposal(
         &fork.overlay,
         &fork.diffs,
         &fork.module,
-        &mut fork.state_monotree,
         &proposal.block,
         &previous,
         verify_fees,