/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2025 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 .
*/
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,
},
monotree::{MemoryDb, Monotree, SledOverlayDb, SledTreeDb, EMPTY_HASH},
};
use darkfi_serial::{deserialize, serialize};
use tracing::{debug, error};
use sled_overlay::{serial::parse_record, sled};
use crate::{
zk::{empty_witnesses, VerifyingKey, ZkCircuit},
zkas::ZkBinary,
Error, Result,
};
use super::SledDbOverlayPtr;
pub const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
pub const SLED_CONTRACTS_TREES_TREE: &[u8] = b"_contracts_trees";
pub const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode";
/// The `ContractStore` is a structure representing all `sled` trees related
/// to storing the blockchain's contracts information.
#[derive(Clone)]
pub struct ContractStore {
/// The `sled` tree storing the wasm bincode for deployed contracts.
/// The layout looks like this:
/// ```plaintext
/// tree: "_wasm_bincode"
/// key: ContractId
/// value: Vec
pub wasm: sled::Tree,
/// The `sled` tree storing the pointers to contracts' databases.
/// See the rustdoc for the impl functions for more info.
/// The layout looks like this:
/// ```plaintext
/// tree: "_contracts"
/// key: ContractId
/// value: Vec
/// ```
/// These values get mutated with `init()` and `remove()`.
pub state: sled::Tree,
/// The `sled` tree storing the inverse pointers to contracts'
/// databases. See the rustdoc for the impl functions for more
/// info.
/// The layout looks like this:
/// ```plaintext
/// tree: "_contracts_trees"
/// key: blake3(ContractId || tree_name)
/// value: ContractId
/// ```
/// These values get mutated with `init()` and `remove()`.
pub state_trees: sled::Tree,
}
impl ContractStore {
/// Opens a new or existing `ContractStore` on the given sled database.
pub fn new(db: &sled::Db) -> Result {
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 })
}
/// Fetches the bincode for a given ContractId from the store's wasm tree.
/// Returns an error if the bincode is not found.
pub fn get(&self, contract_id: ContractId) -> Result> {
if let Some(bincode) = self.wasm.get(serialize(&contract_id))? {
return Ok(bincode.to_vec())
}
Err(Error::WasmBincodeNotFound)
}
/// 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 {
debug!(target: "blockchain::contractstore", "Looking up state tree for {contract_id}:{tree_name}");
// A guard to make sure we went through init()
let contract_id_bytes = serialize(contract_id);
if !self.state.contains_key(&contract_id_bytes)? {
return Err(Error::ContractNotFound(contract_id.to_string()))
}
let state_pointers = self.state.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.
let ptr = contract_id.hash_state_id(tree_name);
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}");
// A guard to make sure we went through init()
let contract_id_bytes = serialize(contract_id);
if !self.state.contains_key(&contract_id_bytes)? {
return Err(Error::ContractNotFound(contract_id.to_string()))
}
let state_pointers = self.state.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.
let ptr = contract_id.hash_state_id(tree_name);
if !state_pointers.contains(&ptr) {
return Err(Error::ContractStateNotFound)
}
if !self.state_trees.contains_key(ptr)? {
return Err(Error::ContractStateNotFound)
}
// Remove the deleted tree from the state pointer set.
state_pointers.retain(|x| *x != ptr);
self.state.insert(contract_id_bytes, serialize(&state_pointers))?;
self.state_trees.remove(ptr)?;
// 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 \"{contract_id}:{zkas_ns}\" zkas circuit & vk");
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, Vec) = deserialize(&zkas_bytes).unwrap();
// The first vec is the compiled zkas binary
let zkbin = ZkBinary::decode(&zkbin).unwrap();
// Construct the circuit to be able to read the VerifyingKey
let circuit = ZkCircuit::new(empty_witnesses(&zkbin).unwrap(), &zkbin);
// The second one is the serialized VerifyingKey for it
let mut vk_buf = Cursor::new(vkbin);
let vk = VerifyingKey::read::>, ZkCircuit>(&mut vk_buf, circuit).unwrap();
Ok((zkbin, vk))
}
/// Retrieve all wasm bincodes from the store's wasm tree in the form
/// of a tuple (`contract_id`, `bincode`).
/// Be careful as this will try to load everything in memory.
pub fn get_all_wasm(&self) -> Result)>> {
let mut bincodes = vec![];
for bincode in self.wasm.iter() {
let bincode = bincode.unwrap();
let contract_id = deserialize(&bincode.0)?;
bincodes.push((contract_id, bincode.1.to_vec()));
}
Ok(bincodes)
}
/// Retrieve all contract states from the store's state tree in the
/// form of a tuple (`contract_id`, `state_hashes`).
/// Be careful as this will try to load everything in memory.
pub fn get_all_states(&self) -> Result)>> {
let mut contracts = vec![];
for contract in self.state.iter() {
contracts.push(parse_record(contract.unwrap())?);
}
Ok(contracts)
}
/// Retrieve provided key value bytes from a contract's zkas sled tree.
pub fn get_state_tree_value(
&self,
db: &sled::Db,
contract_id: &ContractId,
tree_name: &str,
key: &[u8],
) -> Result> {
debug!(target: "blockchain::contractstore", "Looking up state tree value for {contract_id}:{tree_name}");
// Grab the state tree
let state_tree = self.lookup(db, contract_id, tree_name)?;
// Grab the key value
match state_tree.get(key)? {
Some(value) => Ok(value.to_vec()),
None => Err(Error::DatabaseError(format!(
"State tree {contract_id}:{tree_name} doesn't contain key: {key:?}"
))),
}
}
/// Retrieve all records from a contract's zkas sled tree, as a `BTreeMap`.
/// Be careful as this will try to load everything in memory.
pub fn get_state_tree_records(
&self,
db: &sled::Db,
contract_id: &ContractId,
tree_name: &str,
) -> Result, Vec>> {
debug!(target: "blockchain::contractstore", "Looking up state tree records for {contract_id}:{tree_name}");
// Grab the state tree
let state_tree = self.lookup(db, contract_id, tree_name)?;
// Retrieve its records
let mut ret = BTreeMap::new();
for record in state_tree.iter() {
let (key, value) = record.unwrap();
ret.insert(key.to_vec(), value.to_vec());
}
Ok(ret)
}
/// 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, db: &sled::Db) -> Result> {
// Initialize the 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,
};
root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
}
// Iterate over current contracts wasm bincodes to compute its monotree root
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
wasm_monotree_root = wasm_monotree.insert(
wasm_monotree_root.as_ref(),
blake3::hash(&key).as_bytes(),
blake3::hash(&value).as_bytes(),
)?;
}
// Insert wasm bincodes root to the global monotree
let wasm_monotree_root = match wasm_monotree_root {
Some(hash) => hash,
None => *EMPTY_HASH,
};
root = tree.insert(
root.as_ref(),
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
&wasm_monotree_root,
)?;
tree.set_headroot(root.as_ref());
Ok(tree)
}
}
/// Overlay structure over a [`ContractStore`] instance.
pub struct ContractStoreOverlay(SledDbOverlayPtr);
impl ContractStoreOverlay {
pub fn new(overlay: &SledDbOverlayPtr) -> Result {
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)?;
Ok(Self(overlay.clone()))
}
/// Fetches the bincode for a given ContractId from the overlay's wasm tree.
/// Returns an error if the bincode is not found.
pub fn get(&self, contract_id: ContractId) -> Result> {
if let Some(bincode) =
self.0.lock().unwrap().get(SLED_BINCODE_TREE, &serialize(&contract_id))?
{
return Ok(bincode.to_vec())
}
Err(Error::WasmBincodeNotFound)
}
/// Inserts or replaces the bincode for a given ContractId into the overlay's
/// wasm tree.
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 Wasm tree: {e}");
return Err(e.into())
}
Ok(())
}
/// 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 mut lock = self.0.lock().unwrap();
// See if there are existing state trees.
// If not, just start with an empty vector.
let contract_id_bytes = serialize(contract_id);
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.
let ptr = contract_id.hash_state_id(tree_name);
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.insert(SLED_CONTRACTS_TREES_TREE, &ptr, &contract_id_bytes)?;
lock.open_tree(&ptr, false)?;
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 mut lock = self.0.lock().unwrap();
// A guard to make sure we went through init()
let contract_id_bytes = serialize(contract_id);
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.
let ptr = contract_id.hash_state_id(tree_name);
if !state_pointers.contains(&ptr) {
return Err(Error::ContractStateNotFound)
}
if !lock.contains_key(SLED_CONTRACTS_TREES_TREE, &ptr)? {
return Err(Error::ContractStateNotFound)
}
// We open the tree and return its handle
lock.open_tree(&ptr, false)?;
Ok(ptr)
}
/// Abstraction function for fetching a `ZkBinary` and its respective `VerifyingKey`
/// from a contract's zkas sled tree.
pub fn get_zkas(
&self,
contract_id: &ContractId,
zkas_ns: &str,
) -> Result<(ZkBinary, VerifyingKey)> {
debug!(target: "blockchain::contractstore", "Looking up \"{contract_id}:{zkas_ns}\" zkas circuit & vk");
let zkas_tree = self.lookup(contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
let Some(zkas_bytes) = self.0.lock().unwrap().get(&zkas_tree, &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, Vec) = deserialize(&zkas_bytes).unwrap();
// The first vec is the compiled zkas binary
let zkbin = ZkBinary::decode(&zkbin).unwrap();
// Construct the circuit to be able to read the VerifyingKey
let circuit = ZkCircuit::new(empty_witnesses(&zkbin).unwrap(), &zkbin);
// The second one is the serialized VerifyingKey for it
let mut vk_buf = Cursor::new(vkbin);
let vk = VerifyingKey::read::>, ZkCircuit>(&mut vk_buf, circuit).unwrap();
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.
///
/// Note: native contracts zkas tree and wasm bincodes are excluded.
pub fn get_state_monotree(&self) -> Result> {
let mut lock = self.0.lock().unwrap();
// Grab all states monotrees pointers
let mut states_monotrees_pointers = vec![];
for state_record in lock.iter(SLED_CONTRACTS_TREE)? {
// 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 !lock.contains_key(SLED_CONTRACTS_TREES_TREE, &state_monotree_ptr)? {
return Err(Error::ContractStateNotFound)
}
states_monotrees_pointers.push((contract_id, state_monotree_ptr));
}
// Initialize the 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_monotree_ptr) in states_monotrees_pointers {
// Grab its monotree
let state_monotree_db = SledOverlayDb::new(&mut lock, &state_monotree_ptr)?;
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,
};
root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
}
// Iterate over current contracts wasm bincodes to compute its monotree root
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
wasm_monotree_root = wasm_monotree.insert(
wasm_monotree_root.as_ref(),
blake3::hash(&key).as_bytes(),
blake3::hash(&value).as_bytes(),
)?;
}
// Insert wasm bincodes root to the global monotree
let wasm_monotree_root = match wasm_monotree_root {
Some(hash) => hash,
None => *EMPTY_HASH,
};
root = tree.insert(
root.as_ref(),
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
&wasm_monotree_root,
)?;
tree.set_headroot(root.as_ref());
drop(lock);
// Update the monotree to the latest overlay changes
self.update_state_monotree(&mut tree)?;
Ok(tree)
}
/// Retrieve all updated contracts states and wasm bincodes
/// monotrees roots and update their records in the provided
/// Monotree(SMT).
///
/// Note: native contracts zkas tree and wasm bincodes are excluded.
pub fn update_state_monotree(&self, tree: &mut Monotree) -> Result<()> {
let mut lock = self.0.lock().unwrap();
// Iterate over overlay's caches
let mut root = tree.get_headroot()?;
let mut states_monotrees_pointers = vec![];
for (state_key, state_cache) in &lock.state.caches {
// Check if that cache is a contract state one.
// Overlay protected trees are all the native/non-contract ones.
if !lock.state.protected_tree_names.contains(state_key) {
let state_key = deserialize(state_key)?;
// Skip native zkas tree
if NATIVE_CONTRACT_ZKAS_DB_NAMES.contains(&state_key) {
continue
}
// Grab its contract id
let Some(record) = lock.get(SLED_CONTRACTS_TREES_TREE, &state_key)? else {
return Err(Error::ContractStateNotFound)
};
let contract_id: ContractId = deserialize(&record)?;
// 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 {
continue
}
// Grab its monotree pointer and its cache state
states_monotrees_pointers.push((
contract_id,
state_monotree_ptr,
state_cache.state.removed.clone(),
state_cache.state.cache.clone(),
));
continue
}
// Skip if its not the wasm bincodes cache
if state_key != SLED_BINCODE_TREE {
continue
}
// Check if wasm bincodes cache is updated
if state_cache.state.cache.is_empty() && state_cache.state.removed.is_empty() {
continue
}
// Iterate over current contracts wasm bincodes to compute its monotree root
debug!(target: "blockchain::contractstore::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 state_cache.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::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(),
)?;
}
// Insert wasm bincodes root to the global monotree
let wasm_monotree_root = match wasm_monotree_root {
Some(hash) => hash,
None => *EMPTY_HASH,
};
debug!(target: "blockchain::contractstore::update_state_monotree", "New root: {}", blake3::hash(&wasm_monotree_root));
root = tree.insert(
root.as_ref(),
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
&wasm_monotree_root,
)?;
debug!(target: "blockchain::contractstore::update_state_monotree", "New global root: {}", blake3::hash(&root.unwrap()));
}
// Iterate over contract states monotrees pointers
for (contract_id, state_monotree_ptr, removed, cache) in states_monotrees_pointers {
debug!(target: "blockchain::contractstore::update_state_monotree", "Updating monotree for contract: {contract_id}");
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 = state_monotree.get_headroot()?;
// Remove dropped records
for key in &removed {
let key = blake3::hash(key);
debug!(target: "blockchain::contractstore::update_state_monotree", "Removed key: {key}");
state_monotree_root =
state_monotree.remove(state_monotree_root.as_ref(), key.as_bytes())?;
}
// Update or insert new records
for (key, value) in &cache {
let key = blake3::hash(key);
let value = blake3::hash(value);
debug!(target: "blockchain::contractstore::update_state_monotree", "Updating key {key} with value: {value}");
state_monotree_root = state_monotree.insert(
state_monotree_root.as_ref(),
key.as_bytes(),
value.as_bytes(),
)?;
}
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::contractstore::update_state_monotree", "New root: {}", blake3::hash(&state_monotree_root));
root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
debug!(target: "blockchain::contractstore::update_state_monotree", "New global root: {}", blake3::hash(&root.unwrap()));
}
tree.set_headroot(root.as_ref());
Ok(())
}
}