mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
blockchain/contract_store: use monotrees for contracts states instead of checksums
This commit is contained in:
@@ -1 +1 @@
|
|||||||
AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAOjwIWgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCK/BaU3WspDYuSwz0/x0Zwfam+2FfrnpDxFoPS4kO4BHAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAGALfmgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCNtqJ3nhmdGy6NXo7EAg7BLwPF3/KIUrAsdAvYbVh5izAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAE9GG2gAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAHQLfmgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCNtqJ3nhmdGy6NXo7EAg7BLwPF3/KIUrAsdAvYbVh5izAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAO/wIWgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCK/BaU3WspDYuSwz0/x0Zwfam+2FfrnpDxFoPS4kO4BHAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAG0LfmgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCNtqJ3nhmdGy6NXo7EAg7BLwPF3/KIUrAsdAvYbVh5izAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
|||||||
@@ -301,13 +301,13 @@ comparison the tx in Bitcoin with the most outputs has 2501.
|
|||||||
|
|
||||||
## Contracts states Monotree(SMT)
|
## Contracts states Monotree(SMT)
|
||||||
|
|
||||||
DarkFi is using an optimized Sparse Merkle Tree implementation,
|
DarkFi is using an optimized Sparse Merkle Tree implementation, called
|
||||||
called Monotree.
|
Monotree. Each contract has its own Monotree, containing all its
|
||||||
This tree is constructed using all contracts states trees checksums,
|
database trees hashed records. Additionally, a global tree exists
|
||||||
along with the wasm bincodes tree checksum, excluding native contracts
|
reflecting the total database state. The global tree is constructed
|
||||||
zkas tree and wasm bincodes.
|
using all contracts states monotrees roots, along with the wasm
|
||||||
The checksum of each tree is computed by hashing all its key and values,
|
bincodes monotree root, excluding native contracts zkas tree and wasm
|
||||||
using a `blake3` hasher.
|
bincodes.
|
||||||
For each block, we compute the current tree root and keep it in its header,
|
For each block, we compute the current tree root and keep it in its header,
|
||||||
enabling us to both verify the contacts state after the block insertion,
|
enabling us to both verify the contacts state after the block insertion,
|
||||||
and create proofs commiting to that specific state.
|
and create proofs commiting to that specific state.
|
||||||
@@ -317,7 +317,7 @@ and create proofs commiting to that specific state.
|
|||||||
In this pseudocode we can see how we can use the Monotree to produce
|
In this pseudocode we can see how we can use the Monotree to produce
|
||||||
a proof for a specific state. First, we will create a random set of
|
a proof for a specific state. First, we will create a random set of
|
||||||
key-value `blake3` hash pairs, representing our contract states
|
key-value `blake3` hash pairs, representing our contract states
|
||||||
checksums:
|
roots:
|
||||||
|
|
||||||
```
|
```
|
||||||
keys = random_hashes(100);
|
keys = random_hashes(100);
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ use std::{collections::BTreeMap, io::Cursor};
|
|||||||
use darkfi_sdk::{
|
use darkfi_sdk::{
|
||||||
crypto::contract_id::{
|
crypto::contract_id::{
|
||||||
ContractId, NATIVE_CONTRACT_IDS_BYTES, NATIVE_CONTRACT_ZKAS_DB_NAMES,
|
ContractId, NATIVE_CONTRACT_IDS_BYTES, NATIVE_CONTRACT_ZKAS_DB_NAMES,
|
||||||
SMART_CONTRACT_ZKAS_DB_NAME,
|
SMART_CONTRACT_MONOTREE_DB_NAME, SMART_CONTRACT_ZKAS_DB_NAME,
|
||||||
},
|
},
|
||||||
monotree::{self, Monotree},
|
monotree::{MemoryDb, Monotree, SledOverlayDb, SledTreeDb, EMPTY_HASH},
|
||||||
};
|
};
|
||||||
use darkfi_serial::{deserialize, serialize};
|
use darkfi_serial::{deserialize, serialize};
|
||||||
use sled_overlay::{serial::parse_record, sled, SledDbOverlay};
|
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
use sled_overlay::{serial::parse_record, sled};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
zk::{empty_witnesses, VerifyingKey, ZkCircuit},
|
zk::{empty_witnesses, VerifyingKey, ZkCircuit},
|
||||||
@@ -38,6 +38,7 @@ use crate::{
|
|||||||
use super::SledDbOverlayPtr;
|
use super::SledDbOverlayPtr;
|
||||||
|
|
||||||
pub const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
|
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";
|
pub const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode";
|
||||||
|
|
||||||
/// The `ContractStore` is a structure representing all `sled` trees related
|
/// The `ContractStore` is a structure representing all `sled` trees related
|
||||||
@@ -61,6 +62,17 @@ pub struct ContractStore {
|
|||||||
/// ```
|
/// ```
|
||||||
/// These values get mutated with `init()` and `remove()`.
|
/// These values get mutated with `init()` and `remove()`.
|
||||||
pub state: sled::Tree,
|
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 {
|
impl ContractStore {
|
||||||
@@ -68,7 +80,8 @@ impl ContractStore {
|
|||||||
pub fn new(db: &sled::Db) -> Result<Self> {
|
pub fn new(db: &sled::Db) -> Result<Self> {
|
||||||
let wasm = db.open_tree(SLED_BINCODE_TREE)?;
|
let wasm = db.open_tree(SLED_BINCODE_TREE)?;
|
||||||
let state = db.open_tree(SLED_CONTRACTS_TREE)?;
|
let state = db.open_tree(SLED_CONTRACTS_TREE)?;
|
||||||
Ok(Self { wasm, state })
|
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.
|
/// Fetches the bincode for a given ContractId from the store's wasm tree.
|
||||||
@@ -139,10 +152,14 @@ impl ContractStore {
|
|||||||
if !state_pointers.contains(&ptr) {
|
if !state_pointers.contains(&ptr) {
|
||||||
return Err(Error::ContractStateNotFound)
|
return Err(Error::ContractStateNotFound)
|
||||||
}
|
}
|
||||||
|
if !self.state_trees.contains_key(ptr)? {
|
||||||
|
return Err(Error::ContractStateNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the deleted tree from the state pointer set.
|
// Remove the deleted tree from the state pointer set.
|
||||||
state_pointers.retain(|x| *x != ptr);
|
state_pointers.retain(|x| *x != ptr);
|
||||||
self.state.insert(contract_id_bytes, serialize(&state_pointers))?;
|
self.state.insert(contract_id_bytes, serialize(&state_pointers))?;
|
||||||
|
self.state_trees.remove(ptr)?;
|
||||||
|
|
||||||
// Drop the deleted tree from the database
|
// Drop the deleted tree from the database
|
||||||
db.drop_tree(ptr)?;
|
db.drop_tree(ptr)?;
|
||||||
@@ -257,40 +274,47 @@ impl ContractStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a Monotree(SMT) containing all contracts states
|
/// Generate a Monotree(SMT) containing all contracts states
|
||||||
/// checksums, along with the wasm bincodes checksum.
|
/// roots, along with the wasm bincodes monotree root.
|
||||||
///
|
///
|
||||||
/// Note: native contracts zkas tree and wasm bincodes are excluded.
|
/// Note: native contracts zkas tree and wasm bincodes are excluded.
|
||||||
pub fn get_state_monotree(&self, db: &sled::Db) -> Result<Monotree<monotree::MemoryDb>> {
|
pub fn get_state_monotree(&self, db: &sled::Db) -> Result<Monotree<MemoryDb>> {
|
||||||
// Initialize the monotree
|
// Initialize the monotree
|
||||||
let mut root = None;
|
let mut root = None;
|
||||||
let monotree_db = monotree::MemoryDb::new();
|
let monotree_db = MemoryDb::new();
|
||||||
let mut tree = Monotree::new(monotree_db);
|
let mut tree = Monotree::new(monotree_db);
|
||||||
|
|
||||||
// Iterate over current contracts states records
|
// Iterate over current contracts states records
|
||||||
// TODO: parallelize this with a threadpool
|
for state_record in self.state.iter() {
|
||||||
for state_record in self.state.iter().values() {
|
// Grab its monotree pointer
|
||||||
// Iterate over contract states pointers
|
let (contract_id, state_pointers): (ContractId, Vec<[u8; 32]>) =
|
||||||
let state_pointers: Vec<[u8; 32]> = deserialize(&state_record?)?;
|
parse_record(state_record?)?;
|
||||||
for state_ptr in state_pointers {
|
let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
|
||||||
// Skip native zkas tree
|
|
||||||
if NATIVE_CONTRACT_ZKAS_DB_NAMES.contains(&state_ptr) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the state tree
|
// Check it exists
|
||||||
let state_tree = db.open_tree(state_ptr)?;
|
if !state_pointers.contains(&state_monotree_ptr) {
|
||||||
|
return Err(Error::ContractStateNotFound)
|
||||||
// Compute its checksum
|
|
||||||
let checksum = sled_tree_checksum(&state_tree)?;
|
|
||||||
|
|
||||||
// Insert record to monotree
|
|
||||||
root = tree.insert(root.as_ref(), &state_ptr, &checksum)?;
|
|
||||||
tree.set_headroot(root.as_ref());
|
|
||||||
}
|
}
|
||||||
|
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 checksum
|
// Iterate over current contracts wasm bincodes to compute its monotree root
|
||||||
let mut hasher = blake3::Hasher::new();
|
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() {
|
for record in self.wasm.iter() {
|
||||||
let (key, value) = record?;
|
let (key, value) = record?;
|
||||||
|
|
||||||
@@ -299,16 +323,23 @@ impl ContractStore {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash record
|
// Insert record
|
||||||
hasher.update(&key);
|
wasm_monotree_root = wasm_monotree.insert(
|
||||||
hasher.update(&value);
|
wasm_monotree_root.as_ref(),
|
||||||
|
blake3::hash(&key).as_bytes(),
|
||||||
|
blake3::hash(&value).as_bytes(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert wasm bincodes record to monotree
|
// 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 = tree.insert(
|
||||||
root.as_ref(),
|
root.as_ref(),
|
||||||
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
|
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
|
||||||
hasher.finalize().as_bytes(),
|
&wasm_monotree_root,
|
||||||
)?;
|
)?;
|
||||||
tree.set_headroot(root.as_ref());
|
tree.set_headroot(root.as_ref());
|
||||||
|
|
||||||
@@ -323,6 +354,7 @@ impl ContractStoreOverlay {
|
|||||||
pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
|
pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
|
||||||
overlay.lock().unwrap().open_tree(SLED_BINCODE_TREE, true)?;
|
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_TREE, true)?;
|
||||||
|
overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREES_TREE, true)?;
|
||||||
Ok(Self(overlay.clone()))
|
Ok(Self(overlay.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,6 +417,7 @@ impl ContractStoreOverlay {
|
|||||||
// Now we add it so it's marked as initialized and create its tree.
|
// Now we add it so it's marked as initialized and create its tree.
|
||||||
state_pointers.push(ptr);
|
state_pointers.push(ptr);
|
||||||
lock.insert(SLED_CONTRACTS_TREE, &contract_id_bytes, &serialize(&state_pointers))?;
|
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)?;
|
lock.open_tree(&ptr, false)?;
|
||||||
|
|
||||||
Ok(ptr)
|
Ok(ptr)
|
||||||
@@ -413,6 +446,9 @@ impl ContractStoreOverlay {
|
|||||||
if !state_pointers.contains(&ptr) {
|
if !state_pointers.contains(&ptr) {
|
||||||
return Err(Error::ContractStateNotFound)
|
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
|
// We open the tree and return its handle
|
||||||
lock.open_tree(&ptr, false)?;
|
lock.open_tree(&ptr, false)?;
|
||||||
@@ -452,47 +488,55 @@ impl ContractStoreOverlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a Monotree(SMT) containing all contracts states
|
/// Generate a Monotree(SMT) containing all contracts states
|
||||||
/// checksums, along with the wasm bincodes checksum.
|
/// roots, along with the wasm bincodes monotree roots.
|
||||||
/// Be carefull as this will open all states trees in the overlay.
|
/// Be carefull as this will open all states monotrees in the overlay.
|
||||||
///
|
///
|
||||||
/// Note: native contracts zkas tree and wasm bincodes are excluded.
|
/// Note: native contracts zkas tree and wasm bincodes are excluded.
|
||||||
pub fn get_state_monotree(&self) -> Result<Monotree<monotree::MemoryDb>> {
|
pub fn get_state_monotree(&self) -> Result<Monotree<MemoryDb>> {
|
||||||
let mut lock = self.0.lock().unwrap();
|
let lock = self.0.lock().unwrap();
|
||||||
|
|
||||||
// Grab all states pointers
|
// Grab all states monotrees pointers
|
||||||
let mut states_pointers = vec![];
|
let mut states_monotrees_pointers = vec![];
|
||||||
for state_record in lock.iter(SLED_CONTRACTS_TREE)? {
|
for state_record in lock.iter(SLED_CONTRACTS_TREE)? {
|
||||||
let state_pointers: Vec<[u8; 32]> = deserialize(&state_record?.1)?;
|
// Grab its monotree pointer
|
||||||
for state_ptr in state_pointers {
|
let (contract_id, state_pointers): (ContractId, Vec<[u8; 32]>) =
|
||||||
// Skip native zkas tree
|
parse_record(state_record?)?;
|
||||||
if NATIVE_CONTRACT_ZKAS_DB_NAMES.contains(&state_ptr) {
|
let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
|
||||||
continue
|
|
||||||
}
|
// Check it exists
|
||||||
states_pointers.push(state_ptr);
|
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
|
// Initialize the monotree
|
||||||
let mut root = None;
|
let mut root = None;
|
||||||
let monotree_db = monotree::MemoryDb::new();
|
let monotree_db = MemoryDb::new();
|
||||||
let mut tree = Monotree::new(monotree_db);
|
let mut tree = Monotree::new(monotree_db);
|
||||||
|
|
||||||
// Iterate over contract states pointers
|
// Iterate over contract states monotrees pointers
|
||||||
// TODO: parallelize this with a threadpool
|
for (contract_id, state_monotree_ptr) in states_monotrees_pointers {
|
||||||
for state_ptr in states_pointers {
|
// Grab its monotree
|
||||||
// Open the state tree in the overlay
|
let state_monotree_db = SledOverlayDb::new(&lock, &state_monotree_ptr)?;
|
||||||
lock.open_tree(&state_ptr, false)?;
|
let state_monotree = Monotree::new(state_monotree_db);
|
||||||
|
|
||||||
// Compute its checksum
|
// Insert its root to the global monotree
|
||||||
let checksum = sled_overlay_tree_checksum(&lock, &state_ptr)?;
|
let state_monotree_root = match state_monotree.get_headroot()? {
|
||||||
|
Some(hash) => hash,
|
||||||
// Insert record to monotree
|
None => *EMPTY_HASH,
|
||||||
root = tree.insert(root.as_ref(), &state_ptr, &checksum)?;
|
};
|
||||||
tree.set_headroot(root.as_ref());
|
root = tree.insert(root.as_ref(), &contract_id.to_bytes(), &state_monotree_root)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over current contracts wasm bincodes to compute its checksum
|
// Iterate over current contracts wasm bincodes to compute its monotree root
|
||||||
let mut hasher = blake3::Hasher::new();
|
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)? {
|
for record in lock.iter(SLED_BINCODE_TREE)? {
|
||||||
let (key, value) = record?;
|
let (key, value) = record?;
|
||||||
|
|
||||||
@@ -501,32 +545,38 @@ impl ContractStoreOverlay {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash record
|
// Insert record
|
||||||
hasher.update(&key);
|
wasm_monotree_root = wasm_monotree.insert(
|
||||||
hasher.update(&value);
|
wasm_monotree_root.as_ref(),
|
||||||
|
blake3::hash(&key).as_bytes(),
|
||||||
|
blake3::hash(&value).as_bytes(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert wasm bincodes record to monotree
|
// 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 = tree.insert(
|
||||||
root.as_ref(),
|
root.as_ref(),
|
||||||
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
|
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
|
||||||
hasher.finalize().as_bytes(),
|
&wasm_monotree_root,
|
||||||
)?;
|
)?;
|
||||||
tree.set_headroot(root.as_ref());
|
tree.set_headroot(root.as_ref());
|
||||||
|
|
||||||
Ok(tree)
|
Ok(tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute all updated contracts states and wasm bincodes
|
/// Retrieve all updated contracts states and wasm bincodes
|
||||||
/// checksums and update their records in the provided
|
/// monotrees roots and update their records in the provided
|
||||||
/// Monotree(SMT).
|
/// Monotree(SMT).
|
||||||
///
|
///
|
||||||
/// Note: native contracts zkas tree and wasm bincodes are excluded.
|
/// Note: native contracts zkas tree and wasm bincodes are excluded.
|
||||||
pub fn update_state_monotree(&self, tree: &mut Monotree<monotree::MemoryDb>) -> Result<()> {
|
pub fn update_state_monotree(&self, tree: &mut Monotree<MemoryDb>) -> Result<()> {
|
||||||
let lock = self.0.lock().unwrap();
|
let lock = self.0.lock().unwrap();
|
||||||
|
|
||||||
// Iterate over overlay's caches
|
// Iterate over overlay's caches
|
||||||
// TODO: parallelize this with a threadpool
|
|
||||||
let mut root = tree.get_headroot()?;
|
let mut root = tree.get_headroot()?;
|
||||||
for (state_key, state_cache) in &lock.state.caches {
|
for (state_key, state_cache) in &lock.state.caches {
|
||||||
// Check if that cache is a contract state one.
|
// Check if that cache is a contract state one.
|
||||||
@@ -539,13 +589,48 @@ impl ContractStoreOverlay {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute its checksum
|
// Grab its contract id
|
||||||
let checksum = sled_overlay_tree_checksum(&lock, &state_key)?;
|
let Some(record) = lock.get(SLED_CONTRACTS_TREES_TREE, &state_key)? else {
|
||||||
|
return Err(Error::ContractStateNotFound)
|
||||||
|
};
|
||||||
|
let contract_id: ContractId = deserialize(&record)?;
|
||||||
|
debug!(target: "blockchain::contractstore::update_state_monotree", "Updating monotree for contract: {contract_id}");
|
||||||
|
|
||||||
// Insert record to monotree
|
// Grab its monotree
|
||||||
root = tree.insert(root.as_ref(), &state_key, &checksum)?;
|
let state_monotree_ptr = contract_id.hash_state_id(SMART_CONTRACT_MONOTREE_DB_NAME);
|
||||||
|
let state_monotree_db = SledOverlayDb::new(&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 &state_cache.state.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 &state_cache.state.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(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)?;
|
||||||
tree.set_headroot(root.as_ref());
|
tree.set_headroot(root.as_ref());
|
||||||
|
debug!(target: "blockchain::contractstore::update_state_monotree", "New global root: {}", blake3::hash(&root.unwrap()));
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,10 +644,12 @@ impl ContractStoreOverlay {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over current contracts wasm bincodes to compute
|
// Iterate over current contracts wasm bincodes to compute its monotree root
|
||||||
// its checksum.
|
debug!(target: "blockchain::contractstore::update_state_monotree", "Updating wasm bincodes monotree...");
|
||||||
let mut hasher = blake3::Hasher::new();
|
let mut wasm_monotree_root = None;
|
||||||
for record in lock.iter(SLED_BINCODE_TREE)? {
|
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?;
|
let (key, value) = record?;
|
||||||
|
|
||||||
// Skip native ones
|
// Skip native ones
|
||||||
@@ -570,54 +657,32 @@ impl ContractStoreOverlay {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash record
|
// Insert record
|
||||||
hasher.update(&key);
|
let key = blake3::hash(&key);
|
||||||
hasher.update(&value);
|
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 record to monotree
|
// 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 = tree.insert(
|
||||||
root.as_ref(),
|
root.as_ref(),
|
||||||
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
|
blake3::hash(SLED_BINCODE_TREE).as_bytes(),
|
||||||
hasher.finalize().as_bytes(),
|
&wasm_monotree_root,
|
||||||
)?;
|
)?;
|
||||||
tree.set_headroot(root.as_ref());
|
tree.set_headroot(root.as_ref());
|
||||||
|
debug!(target: "blockchain::contractstore::update_state_monotree", "New global root: {}", blake3::hash(&root.unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Auxiliary function to compute a blake3 checksum for provided sled
|
|
||||||
/// tree.
|
|
||||||
fn sled_tree_checksum(tree: &sled::Tree) -> Result<[u8; 32]> {
|
|
||||||
// Generate a new blake3 hashed
|
|
||||||
let mut hasher = blake3::Hasher::new();
|
|
||||||
|
|
||||||
// Iterate over tree records to compute its checksum
|
|
||||||
for record in tree.iter() {
|
|
||||||
let (key, value) = record?;
|
|
||||||
hasher.update(&key);
|
|
||||||
hasher.update(&value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the finalized hasher bytes
|
|
||||||
Ok(*hasher.finalize().as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Auxiliary function to compute a blake3 checksum for provided sled
|
|
||||||
/// overlay tree.
|
|
||||||
fn sled_overlay_tree_checksum(overlay: &SledDbOverlay, tree_key: &[u8]) -> Result<[u8; 32]> {
|
|
||||||
// Generate a new blake3 hashed
|
|
||||||
let mut hasher = blake3::Hasher::new();
|
|
||||||
|
|
||||||
// Iterate over tree records to compute its checksum
|
|
||||||
for record in overlay.iter(tree_key)? {
|
|
||||||
let (key, value) = record?;
|
|
||||||
hasher.update(&key);
|
|
||||||
hasher.update(&value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the finalized hasher bytes
|
|
||||||
Ok(*hasher.finalize().as_bytes())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ pub use tx_store::{
|
|||||||
pub mod contract_store;
|
pub mod contract_store;
|
||||||
pub use contract_store::{
|
pub use contract_store::{
|
||||||
ContractStore, ContractStoreOverlay, SLED_BINCODE_TREE, SLED_CONTRACTS_TREE,
|
ContractStore, ContractStoreOverlay, SLED_BINCODE_TREE, SLED_CONTRACTS_TREE,
|
||||||
|
SLED_CONTRACTS_TREES_TREE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Monero definitions needed for merge mining
|
/// Monero definitions needed for merge mining
|
||||||
@@ -447,6 +448,7 @@ impl BlockchainOverlay {
|
|||||||
SLED_PENDING_TX_TREE,
|
SLED_PENDING_TX_TREE,
|
||||||
SLED_PENDING_TX_ORDER_TREE,
|
SLED_PENDING_TX_ORDER_TREE,
|
||||||
SLED_CONTRACTS_TREE,
|
SLED_CONTRACTS_TREE,
|
||||||
|
SLED_CONTRACTS_TREES_TREE,
|
||||||
SLED_BINCODE_TREE,
|
SLED_BINCODE_TREE,
|
||||||
];
|
];
|
||||||
let overlay = Arc::new(Mutex::new(sled_overlay::SledDbOverlay::new(
|
let overlay = Arc::new(Mutex::new(sled_overlay::SledDbOverlay::new(
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use darkfi_sdk::{
|
use darkfi_sdk::{
|
||||||
crypto::contract_id::{ContractId, SMART_CONTRACT_ZKAS_DB_NAME},
|
crypto::contract_id::{
|
||||||
|
ContractId, SMART_CONTRACT_MONOTREE_DB_NAME, SMART_CONTRACT_ZKAS_DB_NAME,
|
||||||
|
},
|
||||||
wasm,
|
wasm,
|
||||||
};
|
};
|
||||||
use darkfi_serial::{deserialize, serialize, Decodable};
|
use darkfi_serial::{deserialize, serialize, Decodable};
|
||||||
@@ -138,6 +140,15 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u
|
|||||||
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nor can we allow initializing the special monotree db:
|
||||||
|
if read_db_name == SMART_CONTRACT_MONOTREE_DB_NAME {
|
||||||
|
error!(
|
||||||
|
target: "runtime::db::db_init",
|
||||||
|
"[WASM] [{cid}] db_init(): Attempted to init monotree db"
|
||||||
|
);
|
||||||
|
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
||||||
|
}
|
||||||
|
|
||||||
// Nor can we allow another contract to initialize a db for someone else:
|
// Nor can we allow another contract to initialize a db for someone else:
|
||||||
if cid != read_cid {
|
if cid != read_cid {
|
||||||
error!(
|
error!(
|
||||||
@@ -293,6 +304,14 @@ pub(crate) fn db_lookup(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len:
|
|||||||
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if db_name == SMART_CONTRACT_MONOTREE_DB_NAME {
|
||||||
|
error!(
|
||||||
|
target: "runtime::db::db_lookup",
|
||||||
|
"[WASM] [{cid}] db_lookup(): Attempted to lookup monotree db"
|
||||||
|
);
|
||||||
|
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
||||||
|
}
|
||||||
|
|
||||||
// Lookup contract state
|
// Lookup contract state
|
||||||
let tree_handle = match contracts.lookup(&cid, &db_name) {
|
let tree_handle = match contracts.lookup(&cid, &db_name) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use darkfi_sdk::{
|
use darkfi_sdk::{
|
||||||
crypto::contract_id::{ContractId, SMART_CONTRACT_ZKAS_DB_NAME},
|
crypto::contract_id::{
|
||||||
|
ContractId, SMART_CONTRACT_MONOTREE_DB_NAME, SMART_CONTRACT_ZKAS_DB_NAME,
|
||||||
|
},
|
||||||
tx::TransactionHash,
|
tx::TransactionHash,
|
||||||
wasm, AsHex,
|
wasm, AsHex,
|
||||||
};
|
};
|
||||||
@@ -466,6 +468,12 @@ impl Runtime {
|
|||||||
Err(_) => contracts.init(&env_mut.contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?,
|
Err(_) => contracts.init(&env_mut.contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create the monotree db tree for this contract,
|
||||||
|
// if it doesn't exists.
|
||||||
|
if contracts.lookup(&env_mut.contract_id, SMART_CONTRACT_MONOTREE_DB_NAME).is_err() {
|
||||||
|
contracts.init(&env_mut.contract_id, SMART_CONTRACT_MONOTREE_DB_NAME)?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut db_handles = env_mut.db_handles.borrow_mut();
|
let mut db_handles = env_mut.db_handles.borrow_mut();
|
||||||
db_handles.push(DbHandle::new(env_mut.contract_id, zkas_tree_handle));
|
db_handles.push(DbHandle::new(env_mut.contract_id, zkas_tree_handle));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ use crate::error::ContractError;
|
|||||||
/// The hardcoded db name for the zkas circuits database tree
|
/// The hardcoded db name for the zkas circuits database tree
|
||||||
pub const SMART_CONTRACT_ZKAS_DB_NAME: &str = "_zkas";
|
pub const SMART_CONTRACT_ZKAS_DB_NAME: &str = "_zkas";
|
||||||
|
|
||||||
|
/// The hardcoded db name for the monotree database tree
|
||||||
|
pub const SMART_CONTRACT_MONOTREE_DB_NAME: &str = "_monotree";
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// The idea here is that 0 is not a valid x coordinate for any pallas point,
|
// The idea here is that 0 is not a valid x coordinate for any pallas point,
|
||||||
// therefore a signature cannot be produced for such IDs. This allows us to
|
// therefore a signature cannot be produced for such IDs. This allows us to
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub mod bits;
|
|||||||
pub mod node;
|
pub mod node;
|
||||||
|
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
pub use tree::{MemoryDb, Monotree, SledOverlayDb};
|
pub use tree::{MemoryDb, Monotree, SledOverlayDb, SledTreeDb};
|
||||||
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,8 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use sled_overlay::SledDbOverlay;
|
use sled_overlay::{sled::Tree, SledDbOverlay};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
bits::Bits,
|
bits::Bits,
|
||||||
@@ -150,28 +148,25 @@ impl MonotreeStorageAdapter for MemoryDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sled-overlay-based storage for Monotree
|
/// sled-tree based storage for Monotree
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SledOverlayDb {
|
pub struct SledTreeDb {
|
||||||
overlay: Arc<Mutex<SledDbOverlay>>,
|
tree: Tree,
|
||||||
tree: [u8; 32],
|
|
||||||
batch: MemCache,
|
batch: MemCache,
|
||||||
batch_on: bool,
|
batch_on: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SledOverlayDb {
|
impl SledTreeDb {
|
||||||
pub fn new(overlay: &Arc<Mutex<SledDbOverlay>>, tree: &[u8; 32]) -> Self {
|
pub fn new(tree: &Tree) -> Self {
|
||||||
Self { overlay: overlay.clone(), tree: *tree, batch: MemCache::new(), batch_on: false }
|
Self { tree: tree.clone(), batch: MemCache::new(), batch_on: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonotreeStorageAdapter for SledOverlayDb {
|
impl MonotreeStorageAdapter for SledTreeDb {
|
||||||
fn put(&mut self, key: &Hash, value: Vec<u8>) -> GenericResult<()> {
|
fn put(&mut self, key: &Hash, value: Vec<u8>) -> GenericResult<()> {
|
||||||
if self.batch_on {
|
if self.batch_on {
|
||||||
self.batch.put(key, value);
|
self.batch.put(key, value);
|
||||||
} else if let Err(e) =
|
} else if let Err(e) = self.tree.insert(slice_to_hash(key), value) {
|
||||||
self.overlay.lock().unwrap().insert(&self.tree, &slice_to_hash(key), &value)
|
|
||||||
{
|
|
||||||
return Err(ContractError::IoError(e.to_string()))
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +178,7 @@ impl MonotreeStorageAdapter for SledOverlayDb {
|
|||||||
return Ok(self.batch.get(key));
|
return Ok(self.batch.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.overlay.lock().unwrap().get(&self.tree, key) {
|
match self.tree.get(key) {
|
||||||
Ok(Some(v)) => Ok(Some(v.to_vec())),
|
Ok(Some(v)) => Ok(Some(v.to_vec())),
|
||||||
Ok(None) => Ok(None),
|
Ok(None) => Ok(None),
|
||||||
Err(e) => Err(ContractError::IoError(e.to_string())),
|
Err(e) => Err(ContractError::IoError(e.to_string())),
|
||||||
@@ -193,7 +188,7 @@ impl MonotreeStorageAdapter for SledOverlayDb {
|
|||||||
fn del(&mut self, key: &Hash) -> GenericResult<()> {
|
fn del(&mut self, key: &Hash) -> GenericResult<()> {
|
||||||
if self.batch_on {
|
if self.batch_on {
|
||||||
self.batch.del(key);
|
self.batch.del(key);
|
||||||
} else if let Err(e) = self.overlay.lock().unwrap().remove(&self.tree, key) {
|
} else if let Err(e) = self.tree.remove(key) {
|
||||||
return Err(ContractError::IoError(e.to_string()));
|
return Err(ContractError::IoError(e.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,12 +207,92 @@ impl MonotreeStorageAdapter for SledOverlayDb {
|
|||||||
fn finish_batch(&mut self) -> GenericResult<()> {
|
fn finish_batch(&mut self) -> GenericResult<()> {
|
||||||
if self.batch_on {
|
if self.batch_on {
|
||||||
for (key, value) in self.batch.map.drain() {
|
for (key, value) in self.batch.map.drain() {
|
||||||
if let Err(e) = self.overlay.lock().unwrap().insert(&self.tree, &key, &value) {
|
if let Err(e) = self.tree.insert(key, value) {
|
||||||
return Err(ContractError::IoError(e.to_string()))
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for key in self.batch.set.drain() {
|
for key in self.batch.set.drain() {
|
||||||
if let Err(e) = self.overlay.lock().unwrap().remove(&self.tree, &key) {
|
if let Err(e) = self.tree.remove(key) {
|
||||||
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.batch_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sled-overlay based storage for Monotree
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SledOverlayDb {
|
||||||
|
overlay: SledDbOverlay,
|
||||||
|
tree: [u8; 32],
|
||||||
|
batch: MemCache,
|
||||||
|
batch_on: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SledOverlayDb {
|
||||||
|
pub fn new(overlay: &SledDbOverlay, tree: &[u8; 32]) -> GenericResult<Self> {
|
||||||
|
let mut overlay = overlay.clone();
|
||||||
|
if let Err(e) = overlay.open_tree(tree, false) {
|
||||||
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
|
};
|
||||||
|
Ok(Self { overlay, tree: *tree, batch: MemCache::new(), batch_on: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonotreeStorageAdapter for SledOverlayDb {
|
||||||
|
fn put(&mut self, key: &Hash, value: Vec<u8>) -> GenericResult<()> {
|
||||||
|
if self.batch_on {
|
||||||
|
self.batch.put(key, value);
|
||||||
|
} else if let Err(e) = self.overlay.insert(&self.tree, &slice_to_hash(key), &value) {
|
||||||
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, key: &[u8]) -> GenericResult<Option<Vec<u8>>> {
|
||||||
|
if self.batch_on && self.batch.contains(key) {
|
||||||
|
return Ok(self.batch.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.overlay.get(&self.tree, key) {
|
||||||
|
Ok(Some(v)) => Ok(Some(v.to_vec())),
|
||||||
|
Ok(None) => Ok(None),
|
||||||
|
Err(e) => Err(ContractError::IoError(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn del(&mut self, key: &Hash) -> GenericResult<()> {
|
||||||
|
if self.batch_on {
|
||||||
|
self.batch.del(key);
|
||||||
|
} else if let Err(e) = self.overlay.remove(&self.tree, key) {
|
||||||
|
return Err(ContractError::IoError(e.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_batch(&mut self) -> GenericResult<()> {
|
||||||
|
if !self.batch_on {
|
||||||
|
self.batch.clear();
|
||||||
|
self.batch_on = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_batch(&mut self) -> GenericResult<()> {
|
||||||
|
if self.batch_on {
|
||||||
|
for (key, value) in self.batch.map.drain() {
|
||||||
|
if let Err(e) = self.overlay.insert(&self.tree, &key, &value) {
|
||||||
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key in self.batch.set.drain() {
|
||||||
|
if let Err(e) = self.overlay.remove(&self.tree, &key) {
|
||||||
return Err(ContractError::IoError(e.to_string()))
|
return Err(ContractError::IoError(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user