darkfi/blockchain/
contract_store.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10r* This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::{collections::BTreeMap, io::Cursor};
20
21use darkfi_sdk::{
22    crypto::contract_id::{
23        ContractId, NATIVE_CONTRACT_IDS_BYTES, SMART_CONTRACT_MONOTREE_DB_NAME,
24        SMART_CONTRACT_ZKAS_DB_NAME,
25    },
26    monotree::{Hash as StateHash, Monotree, SledOverlayDb, SledTreeDb, EMPTY_HASH},
27};
28use darkfi_serial::{deserialize, serialize};
29use sled_overlay::{sled, SledDbOverlayStateDiff};
30use tracing::{debug, error};
31
32use crate::{
33    zk::{empty_witnesses, VerifyingKey, ZkCircuit},
34    zkas::ZkBinary,
35    Error, Result,
36};
37
38use super::{parse_record, SledDbOverlayPtr};
39
40pub const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
41pub const SLED_CONTRACTS_TREES_TREE: &[u8] = b"_contracts_trees";
42// blake3 hash of `_contracts_monotree`
43pub const SLED_CONTRACTS_MONOTREE_TREE: &[u8; 32] = &[
44    82, 161, 124, 97, 228, 243, 197, 75, 11, 86, 60, 214, 241, 24, 64, 100, 86, 48, 159, 147, 254,
45    116, 94, 17, 165, 22, 39, 3, 149, 120, 122, 175,
46];
47pub const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode";
48
49/// The `ContractStore` is a structure representing all `sled` trees related
50/// to storing the blockchain's contracts information.
51#[derive(Clone)]
52pub struct ContractStore {
53    /// The `sled` tree storing the wasm bincode for deployed contracts.
54    /// The layout looks like this:
55    /// ```plaintext
56    ///  tree: "_wasm_bincode"
57    ///   key: ContractId
58    /// value: Vec<u8>
59    pub wasm: sled::Tree,
60    /// The `sled` tree storing the pointers to contracts' databases.
61    /// See the rustdoc for the impl functions for more info.
62    /// The layout looks like this:
63    /// ```plaintext
64    ///  tree: "_contracts"
65    ///   key: ContractId
66    /// value: Vec<blake3(ContractId || tree_name)>
67    /// ```
68    /// These values get mutated with `init()` and `remove()`.
69    pub state: sled::Tree,
70    /// The `sled` tree storing the inverse pointers to contracts'
71    /// databases. See the rustdoc for the impl functions for more
72    /// info.
73    /// The layout looks like this:
74    /// ```plaintext
75    ///  tree: "_contracts_trees"
76    ///   key: blake3(ContractId || tree_name)
77    /// value: ContractId
78    /// ```
79    /// These values get mutated with `init()` and `remove()`.
80    pub state_trees: sled::Tree,
81    /// The `sled` tree storing the full contracts states monotree,
82    /// excluding native contracts wasm bincodes.
83    /// The layout looks like this:
84    /// ```plaintext
85    ///  tree: "blake3(_contracts_monotree)"
86    ///   key: blake3(ContractId)
87    /// value: blake3(contract monotree root)
88    /// ```
89    /// These values get mutated on each block/proposal append with
90    /// `update_state_monotree()`.
91    pub state_monotree: sled::Tree,
92}
93
94impl ContractStore {
95    /// Opens a new or existing `ContractStore` on the given sled database.
96    pub fn new(db: &sled::Db) -> Result<Self> {
97        let wasm = db.open_tree(SLED_BINCODE_TREE)?;
98        let state = db.open_tree(SLED_CONTRACTS_TREE)?;
99        let state_trees = db.open_tree(SLED_CONTRACTS_TREES_TREE)?;
100        let state_monotree = db.open_tree(SLED_CONTRACTS_MONOTREE_TREE)?;
101        Ok(Self { wasm, state, state_trees, state_monotree })
102    }
103
104    /// Fetches the bincode for a given ContractId from the store's wasm tree.
105    /// Returns an error if the bincode is not found.
106    pub fn get(&self, contract_id: ContractId) -> Result<Vec<u8>> {
107        if let Some(bincode) = self.wasm.get(serialize(&contract_id))? {
108            return Ok(bincode.to_vec())
109        }
110
111        Err(Error::WasmBincodeNotFound)
112    }
113
114    /// Do a lookup of an existing contract state. In order to succeed, the
115    /// state must have been previously initialized with `init()`. If the
116    /// state has been found, a handle to it will be returned. Otherwise, we
117    /// return an error.
118    pub fn lookup(
119        &self,
120        db: &sled::Db,
121        contract_id: &ContractId,
122        tree_name: &str,
123    ) -> Result<sled::Tree> {
124        debug!(target: "blockchain::contractstore::lookup", "Looking up state tree for {contract_id}:{tree_name}");
125
126        // A guard to make sure we went through init()
127        let contract_id_bytes = serialize(contract_id);
128        if !self.state.contains_key(&contract_id_bytes)? {
129            return Err(Error::ContractNotFound(contract_id.to_string()))
130        }
131
132        let state_pointers = self.state.get(&contract_id_bytes)?.unwrap();
133        let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
134
135        // We assume the tree has been created already, so it should be listed
136        // in this array. If not, that's an error.
137        let ptr = contract_id.hash_state_id(tree_name);
138        if !state_pointers.contains(&ptr) {
139            return Err(Error::ContractStateNotFound)
140        }
141
142        // We open the tree and return its handle
143        let tree = db.open_tree(ptr)?;
144        Ok(tree)
145    }
146
147    /// Attempt to remove an existing contract state. In order to succeed, the
148    /// state must have been previously initialized with `init()`. If the state
149    /// has been found, its contents in the tree will be cleared, and the pointer
150    /// will be removed from the main `ContractStateStore`. If anything is not
151    /// found as initialized, an error is returned.
152    /// NOTE: this function is not used right now, we keep it for future proofing,
153    ///       and its obviously untested.
154    pub fn remove(&self, db: &sled::Db, contract_id: &ContractId, tree_name: &str) -> Result<()> {
155        debug!(target: "blockchain::contractstore::remove", "Removing state tree for {contract_id}:{tree_name}");
156
157        // A guard to make sure we went through init()
158        let contract_id_bytes = serialize(contract_id);
159        if !self.state.contains_key(&contract_id_bytes)? {
160            return Err(Error::ContractNotFound(contract_id.to_string()))
161        }
162
163        let state_pointers = self.state.get(&contract_id_bytes)?.unwrap();
164        let mut state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
165
166        // We assume the tree has been created already, so it should be listed
167        // in this array. If not, that's an error.
168        let ptr = contract_id.hash_state_id(tree_name);
169        if !state_pointers.contains(&ptr) {
170            return Err(Error::ContractStateNotFound)
171        }
172        if !self.state_trees.contains_key(ptr)? {
173            return Err(Error::ContractStateNotFound)
174        }
175
176        // Remove the deleted tree from the state pointer set.
177        state_pointers.retain(|x| *x != ptr);
178        self.state.insert(contract_id_bytes, serialize(&state_pointers))?;
179        self.state_trees.remove(ptr)?;
180
181        // Drop the deleted tree from the database
182        db.drop_tree(ptr)?;
183
184        Ok(())
185    }
186
187    /// Abstraction function for fetching a `ZkBinary` and its respective `VerifyingKey`
188    /// from a contract's zkas sled tree.
189    pub fn get_zkas(
190        &self,
191        db: &sled::Db,
192        contract_id: &ContractId,
193        zkas_ns: &str,
194    ) -> Result<(ZkBinary, VerifyingKey)> {
195        debug!(target: "blockchain::contractstore::get_zkas", "Looking up \"{contract_id}:{zkas_ns}\" zkas circuit & vk");
196
197        let zkas_tree = self.lookup(db, contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
198
199        let Some(zkas_bytes) = zkas_tree.get(serialize(&zkas_ns))? else {
200            return Err(Error::ZkasBincodeNotFound)
201        };
202
203        // If anything in this function panics, that means corrupted data managed
204        // to get into this sled tree. This should not be possible.
205        let (zkbin, vkbin): (Vec<u8>, Vec<u8>) = deserialize(&zkas_bytes).unwrap();
206
207        // The first vec is the compiled zkas binary
208        let zkbin = ZkBinary::decode(&zkbin, false).unwrap();
209
210        // Construct the circuit to be able to read the VerifyingKey
211        let circuit = ZkCircuit::new(empty_witnesses(&zkbin).unwrap(), &zkbin);
212
213        // The second one is the serialized VerifyingKey for it
214        let mut vk_buf = Cursor::new(vkbin);
215        let vk = VerifyingKey::read::<Cursor<Vec<u8>>, ZkCircuit>(&mut vk_buf, circuit).unwrap();
216
217        Ok((zkbin, vk))
218    }
219
220    /// Retrieve all wasm bincodes from the store's wasm tree in the form
221    /// of a tuple (`contract_id`, `bincode`).
222    /// Be careful as this will try to load everything in memory.
223    pub fn get_all_wasm(&self) -> Result<Vec<(ContractId, Vec<u8>)>> {
224        let mut bincodes = vec![];
225
226        for bincode in self.wasm.iter() {
227            let bincode = bincode.unwrap();
228            let contract_id = deserialize(&bincode.0)?;
229            bincodes.push((contract_id, bincode.1.to_vec()));
230        }
231
232        Ok(bincodes)
233    }
234
235    /// Retrieve all contract states from the store's state tree in the
236    /// form of a tuple (`contract_id`, `state_hashes`).
237    /// Be careful as this will try to load everything in memory.
238    pub fn get_all_states(&self) -> Result<Vec<(ContractId, Vec<blake3::Hash>)>> {
239        let mut contracts = vec![];
240
241        for contract in self.state.iter() {
242            contracts.push(parse_record(contract.unwrap())?);
243        }
244
245        Ok(contracts)
246    }
247
248    /// Retrieve provided key value bytes from a contract's zkas sled tree.
249    pub fn get_state_tree_value(
250        &self,
251        db: &sled::Db,
252        contract_id: &ContractId,
253        tree_name: &str,
254        key: &[u8],
255    ) -> Result<Vec<u8>> {
256        debug!(target: "blockchain::contractstore::get_state_tree_value", "Looking up state tree value for {contract_id}:{tree_name}");
257
258        // Grab the state tree
259        let state_tree = self.lookup(db, contract_id, tree_name)?;
260
261        // Grab the key value
262        match state_tree.get(key)? {
263            Some(value) => Ok(value.to_vec()),
264            None => Err(Error::DatabaseError(format!(
265                "State tree {contract_id}:{tree_name} doesn't contain key: {key:?}"
266            ))),
267        }
268    }
269
270    /// Retrieve all records from a contract's zkas sled tree, as a `BTreeMap`.
271    /// Be careful as this will try to load everything in memory.
272    pub fn get_state_tree_records(
273        &self,
274        db: &sled::Db,
275        contract_id: &ContractId,
276        tree_name: &str,
277    ) -> Result<BTreeMap<Vec<u8>, Vec<u8>>> {
278        debug!(target: "blockchain::contractstore::get_state_tree_records", "Looking up state tree records for {contract_id}:{tree_name}");
279
280        // Grab the state tree
281        let state_tree = self.lookup(db, contract_id, tree_name)?;
282
283        // Retrieve its records
284        let mut ret = BTreeMap::new();
285        for record in state_tree.iter() {
286            let (key, value) = record.unwrap();
287            ret.insert(key.to_vec(), value.to_vec());
288        }
289
290        Ok(ret)
291    }
292
293    /// Retrieve contracts states Monotree(SMT) current root.
294    ///
295    /// Note: native contracts wasm bincodes are excluded.
296    pub fn get_state_monotree_root(&self) -> Result<StateHash> {
297        let monotree_db = SledTreeDb::new(&self.state_monotree);
298        let monotree = Monotree::new(monotree_db);
299        Ok(match monotree.get_headroot()? {
300            Some(hash) => hash,
301            None => *EMPTY_HASH,
302        })
303    }
304}
305
306/// Overlay structure over a [`ContractStore`] instance.
307pub struct ContractStoreOverlay(SledDbOverlayPtr);
308
309impl ContractStoreOverlay {
310    pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
311        overlay.lock().unwrap().open_tree(SLED_BINCODE_TREE, true)?;
312        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREE, true)?;
313        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREES_TREE, true)?;
314        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_MONOTREE_TREE, true)?;
315        Ok(Self(overlay.clone()))
316    }
317
318    /// Fetches the bincode for a given ContractId from the overlay's wasm tree.
319    /// Returns an error if the bincode is not found.
320    pub fn get(&self, contract_id: ContractId) -> Result<Vec<u8>> {
321        if let Some(bincode) =
322            self.0.lock().unwrap().get(SLED_BINCODE_TREE, &serialize(&contract_id))?
323        {
324            return Ok(bincode.to_vec())
325        }
326
327        Err(Error::WasmBincodeNotFound)
328    }
329
330    /// Inserts or replaces the bincode for a given ContractId into the overlay's
331    /// wasm tree.
332    pub fn insert(&self, contract_id: ContractId, bincode: &[u8]) -> Result<()> {
333        if let Err(e) =
334            self.0.lock().unwrap().insert(SLED_BINCODE_TREE, &serialize(&contract_id), bincode)
335        {
336            error!(target: "blockchain::contractstoreoverlay::insert", "Failed to insert bincode to Wasm tree: {e}");
337            return Err(e.into())
338        }
339
340        Ok(())
341    }
342
343    /// Try to initialize a new contract state. Contracts can create a number
344    /// of trees, separated by `tree_name`, which they can then use from the
345    /// smart contract API. `init()` will look into the main `ContractStateStoreOverlay`
346    /// tree to check if the smart contract was already deployed, and if so
347    /// it will fetch a vector of these states that were initialized. If the
348    /// state was already found, this function will return an error, because
349    /// in this case the handle should be fetched using `lookup()`.
350    /// If the tree was not initialized previously, it will be appended to
351    /// the main `ContractStateStoreOverlay` tree and a handle to it will be
352    /// returned.
353    pub fn init(&self, contract_id: &ContractId, tree_name: &str) -> Result<[u8; 32]> {
354        debug!(target: "blockchain::contractstoreoverlay::init", "Initializing state overlay tree for {contract_id}:{tree_name}");
355        let mut lock = self.0.lock().unwrap();
356
357        // See if there are existing state trees.
358        // If not, just start with an empty vector.
359        let contract_id_bytes = serialize(contract_id);
360        let mut state_pointers: Vec<[u8; 32]> =
361            if lock.contains_key(SLED_CONTRACTS_TREE, &contract_id_bytes)? {
362                let bytes = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
363                deserialize(&bytes)?
364            } else {
365                vec![]
366            };
367
368        // If the db was never initialized, it should not be in here.
369        let ptr = contract_id.hash_state_id(tree_name);
370        if state_pointers.contains(&ptr) {
371            return Err(Error::ContractAlreadyInitialized)
372        }
373
374        // Now we add it so it's marked as initialized and create its tree.
375        state_pointers.push(ptr);
376        lock.insert(SLED_CONTRACTS_TREE, &contract_id_bytes, &serialize(&state_pointers))?;
377        lock.insert(SLED_CONTRACTS_TREES_TREE, &ptr, &contract_id_bytes)?;
378        lock.open_tree(&ptr, false)?;
379
380        Ok(ptr)
381    }
382
383    /// Do a lookup of an existing contract state. In order to succeed, the
384    /// state must have been previously initialized with `init()`. If the
385    /// state has been found, a handle to it will be returned. Otherwise, we
386    /// return an error.
387    pub fn lookup(&self, contract_id: &ContractId, tree_name: &str) -> Result<[u8; 32]> {
388        debug!(target: "blockchain::contractstoreoverlay::lookup", "Looking up state tree for {contract_id}:{tree_name}");
389        let mut lock = self.0.lock().unwrap();
390
391        // A guard to make sure we went through init()
392        let contract_id_bytes = serialize(contract_id);
393        if !lock.contains_key(SLED_CONTRACTS_TREE, &contract_id_bytes)? {
394            return Err(Error::ContractNotFound(contract_id.to_string()))
395        }
396
397        let state_pointers = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
398        let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
399
400        // We assume the tree has been created already, so it should be listed
401        // in this array. If not, that's an error.
402        let ptr = contract_id.hash_state_id(tree_name);
403        if !state_pointers.contains(&ptr) {
404            return Err(Error::ContractStateNotFound)
405        }
406        if !lock.contains_key(SLED_CONTRACTS_TREES_TREE, &ptr)? {
407            return Err(Error::ContractStateNotFound)
408        }
409
410        // We open the tree and return its handle
411        lock.open_tree(&ptr, false)?;
412        Ok(ptr)
413    }
414
415    /// Abstraction function for fetching a `ZkBinary` and its respective `VerifyingKey`
416    /// from a contract's zkas sled tree.
417    pub fn get_zkas(
418        &self,
419        contract_id: &ContractId,
420        zkas_ns: &str,
421    ) -> Result<(ZkBinary, VerifyingKey)> {
422        debug!(target: "blockchain::contractstoreoverlay::get_zkas", "Looking up \"{contract_id}:{zkas_ns}\" zkas circuit & vk");
423
424        let zkas_tree = self.lookup(contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
425
426        let Some(zkas_bytes) = self.0.lock().unwrap().get(&zkas_tree, &serialize(&zkas_ns))? else {
427            return Err(Error::ZkasBincodeNotFound)
428        };
429
430        // If anything in this function panics, that means corrupted data managed
431        // to get into this sled tree. This should not be possible.
432        let (zkbin, vkbin): (Vec<u8>, Vec<u8>) = deserialize(&zkas_bytes).unwrap();
433
434        // The first vec is the compiled zkas binary
435        let zkbin = ZkBinary::decode(&zkbin, false).unwrap();
436
437        // Construct the circuit to be able to read the VerifyingKey
438        let circuit = ZkCircuit::new(empty_witnesses(&zkbin).unwrap(), &zkbin);
439
440        // The second one is the serialized VerifyingKey for it
441        let mut vk_buf = Cursor::new(vkbin);
442        let vk = VerifyingKey::read::<Cursor<Vec<u8>>, ZkCircuit>(&mut vk_buf, circuit).unwrap();
443
444        Ok((zkbin, vk))
445    }
446
447    /// Retrieve contracts states Monotree(SMT) current root.
448    ///
449    /// Note: native contracts wasm bincodes are excluded.
450    pub fn get_state_monotree_root(&self) -> Result<StateHash> {
451        let mut lock = self.0.lock().unwrap();
452        let monotree_db = SledOverlayDb::new(&mut lock, SLED_CONTRACTS_MONOTREE_TREE)?;
453        let monotree = Monotree::new(monotree_db);
454        Ok(match monotree.get_headroot()? {
455            Some(hash) => hash,
456            None => *EMPTY_HASH,
457        })
458    }
459
460    /// Retrieve all updated contracts states and wasm bincodes from
461    /// provided overlay diff, update their monotrees in the overlay
462    /// their root records in the contracts states Monotree(SMT) and
463    /// return its current root. The provided diff must always append
464    /// new changes to the monotrees and it shouldn't contain dropped
465    /// contracts.
466    ///
467    /// Note: native contracts wasm bincodes are excluded.
468    pub fn update_state_monotree(&self, diff: &SledDbOverlayStateDiff) -> Result<StateHash> {
469        // Grab lock over the overlay
470        let mut lock = self.0.lock().unwrap();
471        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Retrieving contracts updates...");
472
473        // Iterate over diff caches to find all contracts updates
474        let mut contracts_updates: BTreeMap<[u8; 32], ContractMonotreeUpdates> = BTreeMap::new();
475        for (state_key, (state_cache, _)) in &diff.caches {
476            // Grab new/redeployed contracts wasm bincodes to include them
477            // in their monotrees, excluding native ones.
478            if state_key == SLED_BINCODE_TREE {
479                for (contract_id_bytes, (_, value)) in &state_cache.cache {
480                    // Grab the actual contract ID bytes
481                    let contract_id_bytes = deserialize(contract_id_bytes)?;
482
483                    // Skip native ones
484                    if NATIVE_CONTRACT_IDS_BYTES.contains(&contract_id_bytes) {
485                        continue
486                    }
487
488                    // Grab its contract monotree state pointer
489                    let contract_id: ContractId = deserialize(&contract_id_bytes)?;
490                    let monotree_pointer =
491                        contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
492
493                    // Grab its record from the map
494                    let mut contract_updates = match contracts_updates.remove(&contract_id_bytes) {
495                        Some(r) => r,
496                        None => ContractMonotreeUpdates::new(monotree_pointer),
497                    };
498
499                    // Create the new/updated wasm bincode record
500                    let key = blake3::hash(&contract_id_bytes);
501                    let value = blake3::hash(value);
502                    contract_updates.inserts.push((key, value));
503
504                    // Insert the update record
505                    contracts_updates.insert(contract_id_bytes, contract_updates);
506                }
507                continue
508            }
509
510            // Check if that cache is not a contract state one.
511            // Overlay protected trees are all the native/non-contract
512            // ones.
513            if lock.state.protected_tree_names.contains(state_key) {
514                continue
515            }
516
517            // Grab the actual state key
518            let state_key: [u8; 32] = deserialize(state_key)?;
519
520            // Grab its contract id
521            let Some(contract_id_bytes) = lock.get(SLED_CONTRACTS_TREES_TREE, &state_key)? else {
522                return Err(Error::ContractStateNotFound)
523            };
524            let contract_id_bytes: [u8; 32] = deserialize(&contract_id_bytes)?;
525            let contract_id: ContractId = deserialize(&contract_id_bytes)?;
526
527            // Skip the actual monotree state cache
528            let monotree_pointer = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
529            if monotree_pointer == state_key {
530                continue
531            }
532
533            // Grab its record from the map
534            let mut contract_updates = match contracts_updates.remove(&contract_id_bytes) {
535                Some(r) => r,
536                None => ContractMonotreeUpdates::new(monotree_pointer),
537            };
538
539            // Handle the contract zkas tree
540            if contract_id.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME) == state_key {
541                // Grab the new/updated keys
542                for (key, (_, value)) in &state_cache.cache {
543                    // Prefix key with its tree name
544                    let mut hasher = blake3::Hasher::new();
545                    hasher.update(&state_key);
546                    hasher.update(key);
547                    let key = hasher.finalize();
548
549                    // Exclude its verifying key from the value
550                    let (zkbin, _): (Vec<u8>, Vec<u8>) = deserialize(value)?;
551                    let value = blake3::hash(&zkbin);
552                    contract_updates.inserts.push((key, value));
553                }
554
555                // Insert the update record
556                contracts_updates.insert(contract_id_bytes, contract_updates);
557                continue
558            }
559
560            // Grab the new/updated keys
561            for (key, (_, value)) in &state_cache.cache {
562                // Prefix key with its tree name
563                let mut hasher = blake3::Hasher::new();
564                hasher.update(&state_key);
565                hasher.update(key);
566                let key = hasher.finalize();
567                let value = blake3::hash(value);
568                contract_updates.inserts.push((key, value));
569            }
570
571            // Grab the dropped keys
572            for key in state_cache.removed.keys() {
573                // Prefix key with its tree name
574                let mut hasher = blake3::Hasher::new();
575                hasher.update(&state_key);
576                hasher.update(key);
577                let key = hasher.finalize();
578                contract_updates.removals.push(key);
579            }
580
581            // Insert the update record
582            contracts_updates.insert(contract_id_bytes, contract_updates);
583        }
584
585        // Apply all contracts updates and grab their new roots
586        let mut contracts_roots = BTreeMap::new();
587        for (contract_id_bytes, contract_updates) in contracts_updates {
588            let contract_id: ContractId = deserialize(&contract_id_bytes)?;
589            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating monotree for contract: {contract_id}");
590
591            // Grab its monotree
592            let monotree_db = SledOverlayDb::new(&mut lock, &contract_updates.monotree_pointer)?;
593            let mut monotree = Monotree::new(monotree_db);
594            let mut monotree_root = monotree.get_headroot()?;
595            let monotree_root_hash = match monotree_root {
596                Some(hash) => blake3::Hash::from(hash),
597                None => blake3::Hash::from(*EMPTY_HASH),
598            };
599            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Current root: {monotree_root_hash}");
600
601            // Update or insert new records
602            for (key, value) in &contract_updates.inserts {
603                debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Inserting key {key} with value: {value}");
604                monotree_root =
605                    monotree.insert(monotree_root.as_ref(), key.as_bytes(), value.as_bytes())?;
606            }
607
608            // Remove dropped records
609            for key in &contract_updates.removals {
610                debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Removing key: {key}");
611                monotree_root = monotree.remove(monotree_root.as_ref(), key.as_bytes())?;
612            }
613
614            // Set root
615            monotree.set_headroot(monotree_root.as_ref());
616
617            // Keep track of the new root for the main monotree
618            let monotree_root = match monotree_root {
619                Some(hash) => hash,
620                None => *EMPTY_HASH,
621            };
622            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New root: {}", blake3::Hash::from(monotree_root));
623            contracts_roots.insert(contract_id_bytes, monotree_root);
624        }
625
626        // Grab the contracts states monotree
627        let monotree_db = SledOverlayDb::new(&mut lock, SLED_CONTRACTS_MONOTREE_TREE)?;
628        let mut monotree = Monotree::new(monotree_db);
629        let mut monotree_root = monotree.get_headroot()?;
630        let monotree_root_hash = match monotree_root {
631            Some(hash) => blake3::Hash::from(hash),
632            None => blake3::Hash::from(*EMPTY_HASH),
633        };
634        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating global monotree with root: {monotree_root_hash}");
635
636        // Insert new/updated contracts monotrees roots
637        for (contract_id_bytes, contract_monotree_root) in &contracts_roots {
638            let contract_id: ContractId = deserialize(contract_id_bytes)?;
639            debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Inserting key {contract_id} with value: {}", blake3::Hash::from(*contract_monotree_root));
640            monotree_root = monotree.insert(
641                monotree_root.as_ref(),
642                contract_id_bytes,
643                contract_monotree_root,
644            )?;
645        }
646
647        // Set new global root
648        monotree.set_headroot(monotree_root.as_ref());
649
650        // Return its hash
651        let monotree_root = match monotree_root {
652            Some(hash) => hash,
653            None => *EMPTY_HASH,
654        };
655        debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "New global root: {}", blake3::Hash::from(monotree_root));
656
657        Ok(monotree_root)
658    }
659}
660
661/// Auxiliary struct representing a contract monotree updates.
662struct ContractMonotreeUpdates {
663    monotree_pointer: [u8; 32],
664    inserts: Vec<(blake3::Hash, blake3::Hash)>,
665    removals: Vec<blake3::Hash>,
666}
667
668impl ContractMonotreeUpdates {
669    fn new(monotree_pointer: [u8; 32]) -> Self {
670        Self { monotree_pointer, inserts: vec![], removals: vec![] }
671    }
672}