Просмотр исходного кода

blockchain: replaced sled-overlay with kvdb-overlay

x 1 неделя назад
Родитель
Сommit
0a2eed294c
49 измененных файлов с 913 добавлено и 533 удалено
  1. 133 3
      Cargo.lock
  2. 4 2
      Cargo.toml
  3. 1 1
      bin/darkfid/Cargo.toml
  4. 5 7
      bin/darkfid/src/lib.rs
  5. 7 7
      bin/darkfid/src/main.rs
  6. 1 2
      bin/darkfid/src/registry/mod.rs
  7. 7 9
      bin/darkfid/src/registry/model.rs
  8. 6 17
      bin/darkfid/src/rpc/blockchain.rs
  9. 2 2
      bin/darkfid/src/rpc/tx.rs
  10. 1 1
      bin/darkfid/src/task/garbage_collect.rs
  11. 4 4
      bin/darkfid/src/task/sync.rs
  12. 3 2
      bin/darkfid/src/tests/forks.rs
  13. 21 15
      bin/darkfid/src/tests/harness.rs
  14. 9 9
      bin/darkfid/src/tests/mod.rs
  15. 2 1
      bin/darkfid/src/tests/sync_forks.rs
  16. 74 71
      src/blockchain/block_store.rs
  17. 74 79
      src/blockchain/contract_store.rs
  18. 41 39
      src/blockchain/header_store.rs
  19. 70 97
      src/blockchain/mod.rs
  20. 58 59
      src/blockchain/tx_store.rs
  21. 1 1
      src/contract/dao/src/lib.rs
  22. 1 1
      src/contract/deployooor/src/lib.rs
  23. 1 1
      src/contract/money/src/entrypoint.rs
  24. 1 1
      src/contract/money/src/lib.rs
  25. 1 1
      src/contract/test-harness/Cargo.toml
  26. 2 2
      src/contract/test-harness/src/dao_propose.rs
  27. 11 8
      src/contract/test-harness/src/lib.rs
  28. 7 4
      src/contract/test-harness/src/vks.rs
  29. 4 0
      src/error.rs
  30. 9 2
      src/runtime/import/db/db_contains_key.rs
  31. 1 1
      src/runtime/import/db/db_del.rs
  32. 9 1
      src/runtime/import/db/db_get.rs
  33. 2 2
      src/runtime/import/db/db_init.rs
  34. 1 1
      src/runtime/import/db/db_set.rs
  35. 5 1
      src/runtime/import/db/mod.rs
  36. 2 2
      src/runtime/import/db/zkas_db_set.rs
  37. 5 5
      src/runtime/import/merkle.rs
  38. 20 18
      src/runtime/import/smt.rs
  39. 3 2
      src/sdk/Cargo.toml
  40. 1 0
      src/sdk/src/lib.rs
  41. 1 1
      src/sdk/src/monotree/mod.rs
  42. 17 20
      src/sdk/src/monotree/tree.rs
  43. 1 0
      src/serial/Cargo.toml
  44. 3 0
      src/serial/src/types.rs
  45. 245 0
      src/serial/src/types/kvdb_overlay.rs
  46. 17 12
      src/validator/consensus.rs
  47. 8 8
      src/validator/mod.rs
  48. 5 5
      src/validator/pow.rs
  49. 6 6
      src/validator/verification.rs

+ 133 - 3
Cargo.lock

@@ -1150,12 +1150,24 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
 [[package]]
 name = "bytes"
 version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
 
+[[package]]
+name = "byteview"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d74937895e761d5984206f82ca8ff7725d0fd8021921011921894b41ab1b9f7"
+
 [[package]]
 name = "camino"
 version = "1.2.2"
@@ -1444,6 +1456,12 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "compare"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0095f6103c2a8b44acd6fd15960c801dafebf02e21940360833e0673f48ba7"
+
 [[package]]
 name = "compression-codecs"
 version = "0.4.38"
@@ -1831,6 +1849,16 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "crossbeam-skiplist"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df29de440c58ca2cc6e587ec3d22347551a32435fbde9d2bff64e78a9ffa151b"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
 [[package]]
 name = "crossbeam-utils"
 version = "0.8.21"
@@ -2017,6 +2045,7 @@ dependencies = [
  "halo2_proofs",
  "hex",
  "httparse",
+ "kvdb-overlay",
  "lazy_static",
  "libc",
  "monero",
@@ -2071,10 +2100,10 @@ dependencies = [
  "darkfi_dao_contract",
  "darkfi_deployooor_contract",
  "darkfi_money_contract",
+ "kvdb-overlay",
  "num-bigint",
  "parking_lot 0.12.5",
  "rand 0.8.6",
- "sled-overlay",
  "tracing",
  "tracing-subscriber",
 ]
@@ -2112,6 +2141,7 @@ dependencies = [
  "halo2_gadgets",
  "halo2_proofs",
  "hashbrown 0.17.1",
+ "kvdb-overlay",
  "lazy_static",
  "num",
  "num-bigint",
@@ -2120,7 +2150,6 @@ dependencies = [
  "rand 0.8.6",
  "rand_core 0.6.4",
  "sha2 0.11.0",
- "sled-overlay",
  "subtle",
  "thiserror 2.0.18",
 ]
@@ -2151,6 +2180,7 @@ dependencies = [
  "bridgetree",
  "darkfi-derive",
  "futures-lite",
+ "kvdb-overlay",
  "num-bigint",
  "pasta_curves",
  "semver",
@@ -2228,13 +2258,13 @@ dependencies = [
  "darkfi_money_contract",
  "easy-parallel",
  "hex",
+ "kvdb-overlay",
  "monero",
  "num-bigint",
  "rand 0.8.6",
  "serde",
  "signal-hook",
  "signal-hook-async-std",
- "sled-overlay",
  "smol",
  "structopt",
  "structopt-toml",
@@ -3248,6 +3278,23 @@ dependencies = [
  "static_assertions",
 ]
 
+[[package]]
+name = "fjall"
+version = "3.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5427b70d8592043024955a4a40b5cf44af323fdad7c1f62848a01ec7806709e"
+dependencies = [
+ "byteorder-lite",
+ "byteview",
+ "dashmap",
+ "flume",
+ "log",
+ "lsm-tree",
+ "lz4_flex",
+ "tempfile",
+ "xxhash-rust",
+]
+
 [[package]]
 name = "flate2"
 version = "1.1.9"
@@ -3270,6 +3317,15 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a"
 
+[[package]]
+name = "flume"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
+dependencies = [
+ "spin",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -4385,6 +4441,15 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "interval-heap"
+version = "0.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11274e5e8e89b8607cfedc2910b6626e998779b48a019151c7604d0adcb86ac6"
+dependencies = [
+ "compare",
+]
+
 [[package]]
 name = "intrusive-collections"
 version = "0.9.7"
@@ -4609,6 +4674,16 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "kvdb-overlay"
+version = "0.1.0"
+source = "git+https://git.dark.fi/darkrenaissance/kvdb-overlay#7fdf312f9cc13d0a1cdd430b2ed07baf1c847882"
+dependencies = [
+ "fjall",
+ "tempfile",
+ "thiserror 2.0.18",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.5.0"
@@ -4835,11 +4910,36 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
 
+[[package]]
+name = "lsm-tree"
+version = "3.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c4da84fa3a8638fb26d4432bfeb69a1b560905ee2980134d8993e2cfa0dbf3"
+dependencies = [
+ "byteorder-lite",
+ "byteview",
+ "crossbeam-skiplist",
+ "enum_dispatch",
+ "interval-heap",
+ "log",
+ "lz4_flex",
+ "quick_cache",
+ "rustc-hash 2.1.2",
+ "self_cell",
+ "sfa",
+ "tempfile",
+ "varint-rs",
+ "xxhash-rust",
+]
+
 [[package]]
 name = "lz4_flex"
 version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e"
+dependencies = [
+ "twox-hash 2.1.2",
+]
 
 [[package]]
 name = "mach2"
@@ -6260,6 +6360,16 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "quick_cache"
+version = "0.6.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9c6658afe513a3b484e3abfdaa0d03ef3c0bbf017542c178dd55f94eb3051f9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.16.1",
+]
+
 [[package]]
 name = "quinn-proto-smol"
 version = "0.12.0"
@@ -7178,6 +7288,17 @@ dependencies = [
  "syn 2.0.117",
 ]
 
+[[package]]
+name = "sfa"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1296838937cab56cd6c4eeeb8718ec777383700c33f060e2869867bd01d1175"
+dependencies = [
+ "byteorder-lite",
+ "log",
+ "xxhash-rust",
+]
+
 [[package]]
 name = "sha1"
 version = "0.10.6"
@@ -7508,6 +7629,9 @@ name = "spin"
 version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
 
 [[package]]
 name = "spki"
@@ -9981,6 +10105,12 @@ dependencies = [
  "rayon",
 ]
 
+[[package]]
+name = "varint-rs"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa6c38708f6257f1ec2ca7e5a11f9bbf58a27d7060078b6b333624968183d96"
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"

+ 4 - 2
Cargo.toml

@@ -117,6 +117,7 @@ wasmer-middlewares = {version = "6.1.0", optional = true}
 
 # Blockchain store
 sled-overlay = {version = "0.1.20", optional = true}
+kvdb-overlay = {git = "https://git.dark.fi/darkrenaissance/kvdb-overlay", version = "0.1.0", optional = true}
 
 # Miner
 randomx = {git = "https://codeberg.org/darkrenaissance/RandomX", rev = "0e31d5a", optional = true}
@@ -146,7 +147,7 @@ async-sdk = [
 ]
 
 blockchain = [
-    "sled-overlay",
+    "kvdb-overlay",
     "monero",
     "num-bigint",
     "primitive-types",
@@ -154,7 +155,8 @@ blockchain = [
     "tiny-keccak",
 
     "darkfi-serial/num-bigint",
-    "darkfi-serial/sled-overlay",
+    "darkfi-serial/kvdb-overlay",
+    "darkfi-sdk/monotree",
 
     "tx",
     "util",

+ 1 - 1
bin/darkfid/Cargo.toml

@@ -19,10 +19,10 @@ darkfi-serial = {path = "../../src/serial"}
 # Misc
 blake3 = "1.8.5"
 bs58 = "0.5.1"
+kvdb-overlay = {git = "https://git.dark.fi/darkrenaissance/kvdb-overlay", version = "0.1.0"}
 tracing = "0.1.44"
 num-bigint = "0.4.6"
 rand = "0.8.6"
-sled-overlay = "0.1.20"
 toml = "0.9.8"
 
 # JSON-RPC

+ 5 - 7
bin/darkfid/src/lib.rs

@@ -122,14 +122,14 @@ impl Darkfid {
     /// along with all the corresponding background tasks.
     pub async fn init(
         network: Network,
-        sled_db: &sled_overlay::sled::Db,
+        kvdb: &kvdb_overlay::Database,
         config: &ValidatorConfig,
         net_settings: &Settings,
         ex: &ExecutorPtr,
     ) -> Result<DarkfidPtr> {
         info!(target: "darkfid::Darkfid::init", "Initializing a Darkfi daemon...");
         // Initialize validator
-        let validator = Validator::new(sled_db, config).await?;
+        let validator = Validator::new(kvdb, config).await?;
 
         // Initialize P2P network
         let p2p_handler = DarkfidP2pHandler::init(net_settings, ex).await?;
@@ -314,11 +314,9 @@ impl Darkfid {
         info!(target: "darkfid::Darkfid::stop", "Stopping consensus task...");
         self.consensus_task.stop().await;
 
-        // Flush sled database data
-        info!(target: "darkfid::Darkfid::stop", "Flushing sled database...");
-        let flushed_bytes =
-            self.node.validator.read().await.blockchain.sled_db.flush_async().await?;
-        info!(target: "darkfid::Darkfid::stop", "Flushed {flushed_bytes} bytes");
+        // Flush kvdb database data
+        info!(target: "darkfid::Darkfid::stop", "Flushing kvdb database...");
+        self.node.validator.read().await.blockchain.kvdb.flush_default_mode_async().await?;
 
         info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
         Ok(())

+ 7 - 7
bin/darkfid/src/main.rs

@@ -173,9 +173,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     let bytes = base64::decode(genesis_block.trim()).unwrap();
     let genesis_block: BlockInfo = deserialize_async(&bytes).await?;
 
-    // Initialize or open sled database
+    // Initialize or open kvdb database
     let db_path = expand_path(&blockchain_config.database)?;
-    let sled_db = sled_overlay::sled::open(&db_path)?;
+    let kvdb = kvdb_overlay::Database::open_default(&db_path)?;
 
     // Initialize validator configuration
     let pow_fixed_difficulty = if let Some(diff) = blockchain_config.pow_fixed_difficulty {
@@ -197,7 +197,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     // Check if reset was requested
     if let Some(height) = args.reset {
         info!(target: "darkfid", "Node will reset validator state to height: {height}");
-        let validator = Validator::new(&sled_db, &config).await?;
+        let validator = Validator::new(&kvdb, &config).await?;
         validator.write().await.reset_to_height(height).await?;
         info!(target: "darkfid", "Validator state reset successfully!");
         return Ok(())
@@ -206,7 +206,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     // Check if sync headers purge was requested
     if args.purge_sync {
         info!(target: "darkfid", "Node will purge all pending sync headers.");
-        let validator = Validator::new(&sled_db, &config).await?;
+        let validator = Validator::new(&kvdb, &config).await?;
         validator.read().await.blockchain.headers.remove_all_sync()?;
         info!(target: "darkfid", "Validator pending sync headers purged successfully!");
         return Ok(())
@@ -215,7 +215,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     // Check if validate was requested
     if args.validate {
         info!(target: "darkfid", "Node will validate existing blockchain state.");
-        let validator = Validator::new(&sled_db, &config).await?;
+        let validator = Validator::new(&kvdb, &config).await?;
         validator
             .read()
             .await
@@ -228,7 +228,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
     // Check if rebuild difficulties was requested
     if args.rebuild_difficulties {
         info!(target: "darkfid", "Node will rebuild difficulties of existing blockchain state.");
-        let validator = Validator::new(&sled_db, &config).await?;
+        let validator = Validator::new(&kvdb, &config).await?;
         validator
             .read()
             .await
@@ -242,7 +242,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
         (env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), blockchain_config.net).try_into()?;
 
     // Generate the daemon
-    let daemon = Darkfid::init(network, &sled_db, &config, &p2p_settings, &ex).await?;
+    let daemon = Darkfid::init(network, &kvdb, &config, &p2p_settings, &ex).await?;
 
     // Start the daemon
     let config = ConsensusInitTaskConfig {

+ 1 - 2
bin/darkfid/src/registry/mod.rs

@@ -21,7 +21,6 @@ use std::{
     sync::Arc,
 };
 
-use sled_overlay::sled::IVec;
 use smol::lock::{Mutex, RwLock};
 use tinyjson::JsonValue;
 use tracing::{error, info};
@@ -327,7 +326,7 @@ impl DarkfiMinersRegistryState {
 
     /// Auxiliary function to retrieve all current block templates
     /// newly opened trees.
-    pub fn new_trees(&self) -> BTreeSet<IVec> {
+    pub fn new_trees(&self) -> BTreeSet<String> {
         let mut new_trees = BTreeSet::new();
         for block_template in self.block_templates.values() {
             for new_tree in &block_template.new_trees {

+ 7 - 9
bin/darkfid/src/registry/model.rs

@@ -19,7 +19,6 @@
 use std::{collections::HashMap, str::FromStr};
 
 use rand::rngs::OsRng;
-use sled_overlay::sled::IVec;
 use tinyjson::JsonValue;
 use tracing::info;
 
@@ -140,8 +139,8 @@ impl MinerRewardsRecipientConfig {
 pub struct BlockTemplate {
     /// Block that is being mined
     pub block: BlockInfo,
-    /// New `sled` trees opened the overlay this block was generated
-    pub new_trees: Vec<IVec>,
+    /// New kvdb trees opened the overlay this block was generated
+    pub new_trees: Vec<String>,
     /// RandomX current and next keys pair
     pub randomx_keys: (HeaderHash, Option<HeaderHash>),
     /// Compacted block mining target
@@ -157,7 +156,7 @@ pub struct BlockTemplate {
 impl BlockTemplate {
     fn new(
         block: BlockInfo,
-        new_trees: Vec<IVec>,
+        new_trees: Vec<String>,
         randomx_keys: (HeaderHash, Option<HeaderHash>),
         target: Vec<u8>,
         difficulty: f64,
@@ -281,11 +280,10 @@ impl PowRewardV1Zk {
         );
 
         let validator = validator.read().await;
-        let (zkbin, _) = validator.blockchain.contracts.get_zkas(
-            &validator.blockchain.sled_db,
-            &MONEY_CONTRACT_ID,
-            MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-        )?;
+        let (zkbin, _) = validator
+            .blockchain
+            .contracts
+            .get_zkas(&MONEY_CONTRACT_ID, MONEY_CONTRACT_ZKAS_MINT_NS_V1)?;
 
         let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
         let provingkey = ProvingKey::build(zkbin.k, &circuit);

+ 6 - 17
bin/darkfid/src/rpc/blockchain.rs

@@ -365,11 +365,9 @@ impl DarkfiNode {
         };
 
         let validator = self.validator.read().await;
-        let Ok(zkas_db) = validator.blockchain.contracts.lookup(
-            &validator.blockchain.sled_db,
-            &contract_id,
-            SMART_CONTRACT_ZKAS_DB_NAME,
-        ) else {
+        let Ok(zkas_db) =
+            validator.blockchain.contracts.lookup(&contract_id, SMART_CONTRACT_ZKAS_DB_NAME)
+        else {
             error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Did not find zkas db for ContractId: {contract_id}");
             return server_error(RpcError::ContractZkasDbNotFound, id, None)
         };
@@ -380,7 +378,7 @@ impl DarkfiNode {
         for i in zkas_db.iter() {
             debug!(target: "darkfid::rpc::blockchain_lookup_zkas", "Iterating over zkas db");
             let Ok((zkas_ns, zkas_bytes)) = i else {
-                error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Internal sled error iterating db");
+                error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Internal kvdb error iterating db");
                 return JsonError::new(InternalError, None, id).into()
             };
 
@@ -469,11 +467,7 @@ impl DarkfiNode {
         let tree_name = params[1].get::<String>().unwrap();
 
         let validator = self.validator.read().await;
-        match validator.blockchain.contracts.get_state_tree_records(
-            &validator.blockchain.sled_db,
-            &contract_id,
-            tree_name,
-        ) {
+        match validator.blockchain.contracts.get_state_tree_records(&contract_id, tree_name) {
             Ok(records) => JsonResponse::new(
                 JsonValue::String(base64::encode(&serialize_async(&records).await)),
                 id,
@@ -534,12 +528,7 @@ impl DarkfiNode {
         };
 
         let validator = self.validator.read().await;
-        match validator.blockchain.contracts.get_state_tree_value(
-            &validator.blockchain.sled_db,
-            &contract_id,
-            tree_name,
-            &key,
-        ) {
+        match validator.blockchain.contracts.get_state_tree_value(&contract_id, tree_name, &key) {
             Ok(value) => JsonResponse::new(JsonValue::String(base64::encode(&value)), id).into(),
             Err(e) => {
                 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed fetching contract state key value: {e}");

+ 2 - 2
bin/darkfid/src/rpc/tx.rs

@@ -228,8 +228,8 @@ impl DarkfiNode {
         drop(validator);
 
         // Rebroadcast all pending transactions
-        for value in pending.values() {
-            let value = match value {
+        for record in pending {
+            let (_, value) = match record {
                 Ok(v) => v,
                 Err(e) => {
                     error!(target: "darkfid::rpc::tx_rebroadcast_pending", "Failed retrieving pending tx: {e}");

+ 1 - 1
bin/darkfid/src/task/garbage_collect.rs

@@ -69,7 +69,7 @@ pub async fn garbage_collect_task(receiver: Receiver<()>, node: DarkfiNodePtr) -
         // Check if our mempool is empty
         trigger_queue_check!(receiver, 'outer);
         let validator = node.validator.read().await;
-        if validator.blockchain.transactions.pending.is_empty() {
+        if validator.blockchain.transactions.pending.is_empty()? {
             debug!(target: "darkfid::task::garbage_collect_task", "No pending transactions to process");
             continue
         }

+ 4 - 4
bin/darkfid/src/task/sync.rs

@@ -393,12 +393,12 @@ async fn retrieve_headers(
             // Store the headers
             validator.blockchain.headers.insert_sync(&response_headers)?;
             last_tip_height = response_headers[0].height;
-            info!(target: "darkfid::task::sync::retrieve_headers", "Headers received: {}/{total}", validator.blockchain.headers.len_sync());
+            info!(target: "darkfid::task::sync::retrieve_headers", "Headers received: {}/{total}", validator.blockchain.headers.len_sync()?);
         }
     }
 
     // Check if we retrieved any new headers
-    if validator.blockchain.headers.is_empty_sync() {
+    if validator.blockchain.headers.is_empty_sync()? {
         return Ok(());
     }
 
@@ -409,7 +409,7 @@ async fn retrieve_headers(
     // in memory.
     info!(target: "darkfid::task::sync::retrieve_headers", "Verifying headers sequence...");
     let mut verified_headers = 0;
-    let total = validator.blockchain.headers.len_sync();
+    let total = validator.blockchain.headers.len_sync()?;
     // First we verify the first `BATCH` sequence, using the last known header
     // as the first sync header previous.
     let mut headers = validator.blockchain.headers.get_after_sync(0, BATCH)?;
@@ -486,7 +486,7 @@ async fn retrieve_blocks(
 
     let mut received_blocks = 0;
     let mut validator = node.validator.write().await;
-    let total = validator.blockchain.headers.len_sync();
+    let total = validator.blockchain.headers.len_sync()?;
     'blocks_loop: loop {
         // Check if all our peers are failing
         let mut count = 0;

+ 3 - 2
bin/darkfid/src/tests/forks.rs

@@ -21,7 +21,7 @@ use darkfi::{
     validator::{consensus::Fork, pow::PoWModule},
     Result,
 };
-use sled_overlay::sled;
+use kvdb_overlay::Database;
 
 #[test]
 fn forks() -> Result<()> {
@@ -31,7 +31,8 @@ fn forks() -> Result<()> {
         let record2 = HeaderHash::new(blake3::hash(b"Never skip brain day.").into());
 
         // Create a temporary blockchain
-        let blockchain = Blockchain::new(&sled::Config::new().temporary(true).open()?)?;
+        let (kvdb, _kvdb_folder) = Database::open_temp()?;
+        let blockchain = Blockchain::new(&kvdb)?;
 
         // Generate and insert default genesis block
         let genesis_block = BlockInfo::default();

+ 21 - 15
bin/darkfid/src/tests/harness.rs

@@ -45,8 +45,8 @@ use darkfi_sdk::{
     ContractCall,
 };
 use darkfi_serial::Encodable;
+use kvdb_overlay::{Database, TempDir};
 use num_bigint::BigUint;
-use sled_overlay::sled;
 use url::Url;
 
 use crate::{
@@ -71,6 +71,7 @@ pub struct Harness {
     pub validator_config: ValidatorConfig,
     pub alice: DarkfiNodePtr,
     pub bob: DarkfiNodePtr,
+    _temp_dirs: Vec<TempDir>,
 }
 
 impl Harness {
@@ -89,8 +90,8 @@ impl Harness {
         genesis_block.append_txs(vec![producer_tx]);
 
         // Compute genesis contracts states monotree root
-        let sled_db = sled::Config::new().temporary(true).open()?;
-        let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
+        let (kvdb, _kvdb_folder) = Database::open_temp()?;
+        let overlay = BlockchainOverlay::new(&Blockchain::new(&kvdb)?)?;
         let (_, vks) = vks::get_cached_pks_and_vks()?;
         vks::inject(&overlay, &vks)?;
         deploy_native_contracts(&overlay, config.pow_target).await?;
@@ -111,6 +112,7 @@ impl Harness {
         };
 
         // Generate validators
+        let mut _temp_dirs = vec![];
         let mut settings = Settings {
             active_profiles: vec!["tcp+tls".to_string()],
             localnet: true,
@@ -121,15 +123,19 @@ impl Harness {
         // Alice
         let alice_url = Url::parse(&config.alice_url)?;
         settings.inbound_addrs = vec![alice_url.clone()];
-        let alice = generate_node(&vks, &validator_config, &settings, ex, true, None).await?;
+        let (alice, alice_folder) =
+            generate_node(&vks, &validator_config, &settings, ex, true, None).await?;
+        _temp_dirs.push(alice_folder);
 
         // Bob
         let bob_url = Url::parse(&config.bob_url)?;
         settings.inbound_addrs = vec![bob_url];
         settings.peers = vec![alice_url];
-        let bob = generate_node(&vks, &validator_config, &settings, ex, false, None).await?;
+        let (bob, bob_folder) =
+            generate_node(&vks, &validator_config, &settings, ex, false, None).await?;
+        _temp_dirs.push(bob_folder);
 
-        Ok(Self { config, vks, validator_config, alice, bob })
+        Ok(Self { config, vks, validator_config, alice, bob, _temp_dirs })
     }
 
     pub async fn validate_chains(&self, total_blocks: usize) -> Result<()> {
@@ -143,11 +149,11 @@ impl Harness {
         bob.validate_blockchain(self.config.pow_target, self.config.pow_fixed_difficulty.clone())
             .await?;
 
-        let alice_blockchain_len = alice.blockchain.len();
-        assert_eq!(alice_blockchain_len, bob.blockchain.len());
+        let alice_blockchain_len = alice.blockchain.len()?;
+        assert_eq!(alice_blockchain_len, bob.blockchain.len()?);
         assert_eq!(alice_blockchain_len, total_blocks);
-        assert!(alice.blockchain.headers.is_empty_sync());
-        assert!(bob.blockchain.headers.is_empty_sync());
+        assert!(alice.blockchain.headers.is_empty_sync()?);
+        assert!(bob.blockchain.headers.is_empty_sync()?);
 
         Ok(())
     }
@@ -287,15 +293,15 @@ pub async fn generate_node(
     ex: &Arc<smol::Executor<'static>>,
     skip_sync: bool,
     checkpoint: Option<(u32, HeaderHash)>,
-) -> Result<DarkfiNodePtr> {
-    let sled_db = sled::Config::new().temporary(true).open()?;
-    let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
+) -> Result<(DarkfiNodePtr, TempDir)> {
+    let (kvdb, kvdb_folder) = Database::open_temp()?;
+    let overlay = BlockchainOverlay::new(&Blockchain::new(&kvdb)?)?;
     vks::inject(&overlay, vks)?;
     deploy_native_contracts(&overlay, config.pow_target).await?;
     let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
     overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
     overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
-    let validator = Validator::new(&sled_db, config).await?;
+    let validator = Validator::new(&kvdb, config).await?;
 
     let mut subscribers = HashMap::new();
     subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
@@ -319,5 +325,5 @@ pub async fn generate_node(
         node.validator.write().await.synced = true;
     }
 
-    Ok(node)
+    Ok((node, kvdb_folder))
 }

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

@@ -120,7 +120,7 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     settings.inbound_addrs = vec![charlie_url];
     let bob_url = th.bob.p2p_handler.p2p.settings().read().await.inbound_addrs[0].clone();
     settings.peers = vec![bob_url];
-    let charlie = generate_node(
+    let (charlie, _kvdb_folder) = generate_node(
         &th.vks,
         &th.validator_config,
         &settings,
@@ -132,8 +132,8 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     // Verify node synced
     let alice = th.alice.validator.read().await;
     let charlie_validator = charlie.validator.read().await;
-    assert_eq!(alice.blockchain.len(), charlie_validator.blockchain.len());
-    assert!(charlie_validator.blockchain.headers.is_empty_sync());
+    assert_eq!(alice.blockchain.len()?, charlie_validator.blockchain.len()?);
+    assert!(charlie_validator.blockchain.headers.is_empty_sync()?);
     // Node must have just the best fork
     let index = best_fork_index(&alice.consensus.forks)?;
     let best_fork = &alice.consensus.forks[index];
@@ -173,7 +173,7 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     loop {
         th.add_blocks(&[th.generate_next_block(&mut fork).await?]).await?;
         // Check if confirmation occured
-        if th.alice.validator.read().await.blockchain.len() > 4 {
+        if th.alice.validator.read().await.blockchain.len()? > 4 {
             break
         }
     }
@@ -194,8 +194,8 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     let mut charlie = charlie.validator.write().await;
     charlie.confirmation().await?;
     charlie.validate_blockchain(pow_target, pow_fixed_difficulty).await?;
-    assert_eq!(alice.blockchain.len(), charlie.blockchain.len());
-    assert!(charlie.blockchain.headers.is_empty_sync());
+    assert_eq!(alice.blockchain.len()?, charlie.blockchain.len()?);
+    assert!(charlie.blockchain.headers.is_empty_sync()?);
     assert_eq!(last, charlie.blockchain.last()?.1);
     assert_eq!(charlie.consensus.forks.len(), 1);
     assert_eq!(charlie.consensus.forks[0].proposals.len(), 2);
@@ -255,9 +255,9 @@ fn darkfid_programmatic_control() -> Result<()> {
                 let mut genesis_block = darkfi::blockchain::BlockInfo::default();
                 let producer_tx = genesis_block.txs.pop().unwrap();
                 genesis_block.append_txs(vec![producer_tx]);
-                let sled_db = sled_overlay::sled::Config::new().temporary(true).open().unwrap();
+                let (kvdb, _kvdb_folder) = kvdb_overlay::Database::open_temp().unwrap();
                 let overlay = darkfi::blockchain::BlockchainOverlay::new(
-                    &darkfi::blockchain::Blockchain::new(&sled_db).unwrap(),
+                    &darkfi::blockchain::Blockchain::new(&kvdb).unwrap(),
                 )
                 .unwrap();
                 let (_, vks) = darkfi_contract_test_harness::vks::get_cached_pks_and_vks().unwrap();
@@ -291,7 +291,7 @@ fn darkfid_programmatic_control() -> Result<()> {
                 // Initialize a daemon
                 let daemon = crate::Darkfid::init(
                     Network::Mainnet,
-                    &sled_db,
+                    &kvdb,
                     &config,
                     &darkfi::net::Settings::default(),
                     &ex,

+ 2 - 1
bin/darkfid/src/tests/sync_forks.rs

@@ -77,7 +77,8 @@ async fn sync_forks_real(ex: Arc<Executor<'static>>) -> Result<()> {
     settings.inbound_addrs = vec![charlie_url];
     let bob_url = th.bob.p2p_handler.p2p.settings().read().await.inbound_addrs[0].clone();
     settings.peers = vec![bob_url];
-    let charlie = generate_node(&th.vks, &th.validator_config, &settings, &ex, false, None).await?;
+    let (charlie, _kvdb_folder) =
+        generate_node(&th.vks, &th.validator_config, &settings, &ex, false, None).await?;
 
     // Verify node synced the best fork
     let alice = th.alice.validator.read().await;

+ 74 - 71
src/blockchain/block_store.rs

@@ -27,17 +27,17 @@ use darkfi_sdk::{
 #[cfg(feature = "async-serial")]
 use darkfi_serial::async_trait;
 use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
+use kvdb_overlay::{Batch, Database, DatabaseOverlayStateDiff, Tree};
 use num_bigint::BigUint;
-use sled_overlay::{sled, SledDbOverlayStateDiff};
 
 use crate::{tx::Transaction, util::time::Timestamp, Error, Result};
 
-use super::{parse_record, parse_u32_key_record, Header, HeaderHash, SledDbOverlayPtr};
+use super::{parse_record, parse_u32_key_record, Header, HeaderHash, KvdbOverlayPtr};
 
 /// This struct represents a tuple of the form (`header`, `txs`, `signature`).
 ///
 /// The header and transactions are stored as hashes, serving as pointers to the actual data
-/// in the sled database.
+/// in the kvdb database.
 /// NOTE: This struct fields are considered final, as it represents a blockchain block.
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct Block {
@@ -234,46 +234,49 @@ impl BlockDifficulty {
     }
 }
 
-pub const SLED_BLOCK_TREE: &[u8] = b"_blocks";
-pub const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order";
-pub const SLED_BLOCK_DIFFICULTY_TREE: &[u8] = b"_block_difficulty";
-pub const SLED_BLOCK_STATE_INVERSE_DIFF_TREE: &[u8] = b"_block_state_inverse_diff";
+pub const KVDB_BLOCK_TREE: &str = "_blocks";
+pub const KVDB_BLOCK_ORDER_TREE: &str = "_block_order";
+pub const KVDB_BLOCK_DIFFICULTY_TREE: &str = "_block_difficulty";
+pub const KVDB_BLOCK_STATE_INVERSE_DIFF_TREE: &str = "_block_state_inverse_diff";
 
-/// The `BlockStore` is a structure representing all `sled` trees related
+/// The `BlockStore` is a structure representing all kvdb trees related
 /// to storing the blockchain's blocks information.
 #[derive(Clone)]
 pub struct BlockStore {
-    /// Main `sled` tree, storing all the blockchain's blocks, where the
+    /// Main pointer to the kvdb connection
+    pub kvdb: Database,
+    /// Main kvdb tree, storing all the blockchain's blocks, where the
     /// key is the blocks' hash, and value is the serialized block.
-    pub main: sled::Tree,
-    /// The `sled` tree storing the order of the blockchain's blocks,
+    pub main: Tree,
+    /// The kvdb tree storing the order of the blockchain's blocks,
     /// where the key is the height number, and the value is the blocks'
     /// hash.
-    pub order: sled::Tree,
-    /// The `sled` tree storing the difficulty information of the
+    pub order: Tree,
+    /// The kvdb tree storing the difficulty information of the
     /// blockchain's blocks, where the key is the block height number,
     /// and the value is the blocks' hash.
-    pub difficulty: sled::Tree,
-    /// The `sled` tree storing each blocks' full database state inverse
+    pub difficulty: Tree,
+    /// The kvdb tree storing each blocks' full database state inverse
     /// changes, where the key is the block height number, and the value
     /// is the serialized database inverse diff.
-    pub state_inverse_diff: sled::Tree,
+    pub state_inverse_diff: Tree,
 }
 
 impl BlockStore {
-    /// Opens a new or existing `BlockStore` on the given sled database.
-    pub fn new(db: &sled::Db) -> Result<Self> {
-        let main = db.open_tree(SLED_BLOCK_TREE)?;
-        let order = db.open_tree(SLED_BLOCK_ORDER_TREE)?;
-        let difficulty = db.open_tree(SLED_BLOCK_DIFFICULTY_TREE)?;
-        let state_inverse_diff = db.open_tree(SLED_BLOCK_STATE_INVERSE_DIFF_TREE)?;
-        Ok(Self { main, order, difficulty, state_inverse_diff })
+    /// Opens a new or existing `BlockStore` on the given key-value
+    /// database.
+    pub fn new(kvdb: &Database) -> Result<Self> {
+        let main = kvdb.open_tree_default(KVDB_BLOCK_TREE)?;
+        let order = kvdb.open_tree_default(KVDB_BLOCK_ORDER_TREE)?;
+        let difficulty = kvdb.open_tree_default(KVDB_BLOCK_DIFFICULTY_TREE)?;
+        let state_inverse_diff = kvdb.open_tree_default(KVDB_BLOCK_STATE_INVERSE_DIFF_TREE)?;
+        Ok(Self { kvdb: kvdb.clone(), main, order, difficulty, state_inverse_diff })
     }
 
     /// Insert a slice of [`Block`] into the store's main tree.
     pub fn insert(&self, blocks: &[Block]) -> Result<Vec<HeaderHash>> {
         let (batch, ret) = self.insert_batch(blocks);
-        self.main.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.main, &batch)])?;
         Ok(ret)
     }
 
@@ -281,7 +284,7 @@ impl BlockStore {
     /// order tree.
     pub fn insert_order(&self, heights: &[u32], hashes: &[HeaderHash]) -> Result<()> {
         let batch = self.insert_batch_order(heights, hashes);
-        self.order.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.order, &batch)])?;
         Ok(())
     }
 
@@ -289,7 +292,7 @@ impl BlockStore {
     /// difficulty tree.
     pub fn insert_difficulty(&self, block_difficulties: &[BlockDifficulty]) -> Result<()> {
         let batch = self.insert_batch_difficulty(block_difficulties);
-        self.difficulty.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.difficulty, &batch)])?;
         Ok(())
     }
 
@@ -298,36 +301,36 @@ impl BlockStore {
     pub fn insert_state_inverse_diff(
         &self,
         heights: &[u32],
-        diffs: &[SledDbOverlayStateDiff],
+        diffs: &[DatabaseOverlayStateDiff],
     ) -> Result<()> {
         let batch = self.insert_batch_state_inverse_diff(heights, diffs);
-        self.state_inverse_diff.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.state_inverse_diff, &batch)])?;
         Ok(())
     }
 
-    /// Generate the sled batch corresponding to an insert to the main
+    /// Generate the kvdb batch corresponding to an insert to the main
     /// tree, so caller can handle the write operation.
     /// The block's hash() function output is used as the key,
     /// while value is the serialized [`Block`] itself.
     /// On success, the function returns the block hashes in the same order.
-    pub fn insert_batch(&self, blocks: &[Block]) -> (sled::Batch, Vec<HeaderHash>) {
+    pub fn insert_batch(&self, blocks: &[Block]) -> (Batch, Vec<HeaderHash>) {
         let mut ret = Vec::with_capacity(blocks.len());
-        let mut batch = sled::Batch::default();
+        let mut batch = Batch::new();
 
         for block in blocks {
             let blockhash = block.hash();
-            batch.insert(blockhash.inner(), serialize(block));
+            batch.insert(blockhash.inner(), &serialize(block));
             ret.push(blockhash);
         }
 
         (batch, ret)
     }
 
-    /// Generate the sled batch corresponding to an insert to the order
+    /// Generate the kvdb batch corresponding to an insert to the order
     /// tree, so caller can handle the write operation.
     /// The block height is used as the key, and the block hash is used as value.
-    pub fn insert_batch_order(&self, heights: &[u32], hashes: &[HeaderHash]) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    pub fn insert_batch_order(&self, heights: &[u32], hashes: &[HeaderHash]) -> Batch {
+        let mut batch = Batch::new();
 
         for (i, height) in heights.iter().enumerate() {
             batch.insert(&height.to_be_bytes(), hashes[i].inner());
@@ -336,33 +339,33 @@ impl BlockStore {
         batch
     }
 
-    /// Generate the sled batch corresponding to an insert to the difficulty
+    /// Generate the kvdb batch corresponding to an insert to the difficulty
     /// tree, so caller can handle the write operation.
     /// The block's height number is used as the key, while value is
     //  the serialized [`BlockDifficulty`] itself.
-    pub fn insert_batch_difficulty(&self, block_difficulties: &[BlockDifficulty]) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    pub fn insert_batch_difficulty(&self, block_difficulties: &[BlockDifficulty]) -> Batch {
+        let mut batch = Batch::new();
 
         for block_difficulty in block_difficulties {
-            batch.insert(&block_difficulty.height.to_be_bytes(), serialize(block_difficulty));
+            batch.insert(&block_difficulty.height.to_be_bytes(), &serialize(block_difficulty));
         }
 
         batch
     }
 
-    /// Generate the sled batch corresponding to an insert to the database
+    /// Generate the kvdb batch corresponding to an insert to the database
     /// inverse diffs tree, so caller can handle the write operation.
     /// The block height is used as the key, and the serialized database
     /// inverse diff is used as value.
     pub fn insert_batch_state_inverse_diff(
         &self,
         heights: &[u32],
-        diffs: &[SledDbOverlayStateDiff],
-    ) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+        diffs: &[DatabaseOverlayStateDiff],
+    ) -> Batch {
+        let mut batch = Batch::default();
 
         for (i, height) in heights.iter().enumerate() {
-            batch.insert(&height.to_be_bytes(), serialize(&diffs[i]));
+            batch.insert(&height.to_be_bytes(), &serialize(&diffs[i]));
         }
 
         batch
@@ -375,7 +378,7 @@ impl BlockStore {
 
     /// Check if the store's order tree contains a given height.
     pub fn contains_order(&self, height: u32) -> Result<bool> {
-        Ok(self.order.contains_key(height.to_be_bytes())?)
+        Ok(self.order.contains_key(&height.to_be_bytes())?)
     }
 
     /// Fetch given block hashes from the store's main tree.
@@ -410,7 +413,7 @@ impl BlockStore {
         let mut ret = Vec::with_capacity(heights.len());
 
         for height in heights {
-            if let Some(found) = self.order.get(height.to_be_bytes())? {
+            if let Some(found) = self.order.get(&height.to_be_bytes())? {
                 let block_hash = deserialize(&found)?;
                 ret.push(Some(block_hash));
                 continue
@@ -438,7 +441,7 @@ impl BlockStore {
         let mut ret = Vec::with_capacity(heights.len());
 
         for height in heights {
-            if let Some(found) = self.difficulty.get(height.to_be_bytes())? {
+            if let Some(found) = self.difficulty.get(&height.to_be_bytes())? {
                 let block_difficulty = deserialize(&found)?;
                 ret.push(Some(block_difficulty));
                 continue
@@ -462,11 +465,11 @@ impl BlockStore {
         &self,
         heights: &[u32],
         strict: bool,
-    ) -> Result<Vec<Option<SledDbOverlayStateDiff>>> {
+    ) -> Result<Vec<Option<DatabaseOverlayStateDiff>>> {
         let mut ret = Vec::with_capacity(heights.len());
 
         for height in heights {
-            if let Some(found) = self.state_inverse_diff.get(height.to_be_bytes())? {
+            if let Some(found) = self.state_inverse_diff.get(&height.to_be_bytes())? {
                 let state_inverse_diff = deserialize(&found)?;
                 ret.push(Some(state_inverse_diff));
                 continue
@@ -547,7 +550,7 @@ impl BlockStore {
         let mut key = height;
         let mut counter = 0;
         while counter < n {
-            let record = self.order.get_lt(key.to_be_bytes())?;
+            let record = self.order.get_lt(&key.to_be_bytes())?;
             if record.is_none() {
                 break
             }
@@ -569,7 +572,7 @@ impl BlockStore {
         let mut ret = vec![];
 
         let mut key = height;
-        while let Some(found) = self.order.get_gt(key.to_be_bytes())? {
+        while let Some(found) = self.order.get_gt(&key.to_be_bytes())? {
             let (height, hash) = parse_u32_key_record(found)?;
             key = height;
             ret.push(hash);
@@ -644,7 +647,7 @@ impl BlockStore {
         let mut key = height;
         let mut counter = 0;
         while counter < n {
-            let record = self.difficulty.get_lt(key.to_be_bytes())?;
+            let record = self.difficulty.get_lt(&key.to_be_bytes())?;
             if record.is_none() {
                 break
             }
@@ -666,11 +669,11 @@ impl BlockStore {
     pub fn get_state_inverse_diffs_after(
         &self,
         height: u32,
-    ) -> Result<Vec<SledDbOverlayStateDiff>> {
+    ) -> Result<Vec<DatabaseOverlayStateDiff>> {
         let mut ret = vec![];
 
         let mut key = height;
-        while let Some(found) = self.state_inverse_diff.get_gt(key.to_be_bytes())? {
+        while let Some(found) = self.state_inverse_diff.get_gt(&key.to_be_bytes())? {
             let (height, state_inverse_diff) = parse_u32_key_record(found)?;
             key = height;
             ret.push(state_inverse_diff);
@@ -680,25 +683,25 @@ impl BlockStore {
     }
 
     /// Retrieve store's order tree records count.
-    pub fn len(&self) -> usize {
-        self.order.len()
+    pub fn len(&self) -> Result<usize> {
+        Ok(self.order.len()?)
     }
 
     /// Check if store's order tree contains any records.
-    pub fn is_empty(&self) -> bool {
-        self.order.is_empty()
+    pub fn is_empty(&self) -> Result<bool> {
+        Ok(self.order.is_empty()?)
     }
 }
 
 /// Overlay structure over a [`BlockStore`] instance.
-pub struct BlockStoreOverlay(SledDbOverlayPtr);
+pub struct BlockStoreOverlay(KvdbOverlayPtr);
 
 impl BlockStoreOverlay {
-    pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_BLOCK_ORDER_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_BLOCK_DIFFICULTY_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_BLOCK_STATE_INVERSE_DIFF_TREE, true)?;
+    pub fn new(overlay: &KvdbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree_default(KVDB_BLOCK_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_BLOCK_ORDER_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_BLOCK_DIFFICULTY_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_BLOCK_STATE_INVERSE_DIFF_TREE, true)?;
         Ok(Self(overlay.clone()))
     }
 
@@ -712,7 +715,7 @@ impl BlockStoreOverlay {
 
         for block in blocks {
             let blockhash = block.hash();
-            lock.insert(SLED_BLOCK_TREE, blockhash.inner(), &serialize(block))?;
+            lock.insert(KVDB_BLOCK_TREE, blockhash.inner(), &serialize(block))?;
             ret.push(blockhash);
         }
 
@@ -729,7 +732,7 @@ impl BlockStoreOverlay {
         let mut lock = self.0.lock().unwrap();
 
         for (i, height) in heights.iter().enumerate() {
-            lock.insert(SLED_BLOCK_ORDER_TREE, &height.to_be_bytes(), hashes[i].inner())?;
+            lock.insert(KVDB_BLOCK_ORDER_TREE, &height.to_be_bytes(), hashes[i].inner())?;
         }
 
         Ok(())
@@ -741,7 +744,7 @@ impl BlockStoreOverlay {
 
         for block_difficulty in block_difficulties {
             lock.insert(
-                SLED_BLOCK_DIFFICULTY_TREE,
+                KVDB_BLOCK_DIFFICULTY_TREE,
                 &block_difficulty.height.to_be_bytes(),
                 &serialize(block_difficulty),
             )?;
@@ -760,7 +763,7 @@ impl BlockStoreOverlay {
         let lock = self.0.lock().unwrap();
 
         for hash in block_hashes {
-            if let Some(found) = lock.get(SLED_BLOCK_TREE, hash.inner())? {
+            if let Some(found) = lock.get(KVDB_BLOCK_TREE, hash.inner())? {
                 let block = deserialize(&found)?;
                 ret.push(Some(block));
                 continue
@@ -784,7 +787,7 @@ impl BlockStoreOverlay {
         let lock = self.0.lock().unwrap();
 
         for height in heights {
-            if let Some(found) = lock.get(SLED_BLOCK_ORDER_TREE, &height.to_be_bytes())? {
+            if let Some(found) = lock.get(KVDB_BLOCK_ORDER_TREE, &height.to_be_bytes())? {
                 let block_hash = deserialize(&found)?;
                 ret.push(Some(block_hash));
                 continue
@@ -801,7 +804,7 @@ impl BlockStoreOverlay {
     /// Fetch the last block hash in the overlay's order tree, based on the `Ord`
     /// implementation for `Vec<u8>`.
     pub fn get_last(&self) -> Result<(u32, HeaderHash)> {
-        let found = match self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)? {
+        let found = match self.0.lock().unwrap().last(KVDB_BLOCK_ORDER_TREE)? {
             Some(b) => b,
             None => return Err(Error::BlockHeightNotFound(0u32)),
         };
@@ -812,7 +815,7 @@ impl BlockStoreOverlay {
 
     /// Check if overlay's order tree contains any records.
     pub fn is_empty(&self) -> Result<bool> {
-        Ok(self.0.lock().unwrap().is_empty(SLED_BLOCK_ORDER_TREE)?)
+        Ok(self.0.lock().unwrap().is_empty(KVDB_BLOCK_ORDER_TREE)?)
     }
 }
 

+ 74 - 79
src/blockchain/contract_store.rs

@@ -16,17 +16,17 @@ r* This program is distributed in the hope that it will be useful,
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{collections::BTreeMap, io::Cursor};
+use std::{collections::BTreeMap, io::Cursor, str::FromStr};
 
 use darkfi_sdk::{
     crypto::contract_id::{
         ContractId, NATIVE_CONTRACT_IDS_BYTES, SMART_CONTRACT_MONOTREE_DB_NAME,
         SMART_CONTRACT_ZKAS_DB_NAME,
     },
-    monotree::{Hash as StateHash, Monotree, SledOverlayDb, SledTreeDb, EMPTY_HASH},
+    monotree::{Hash as StateHash, KvdbOverlayDb, KvdbTreeDb, Monotree, EMPTY_HASH},
 };
 use darkfi_serial::{deserialize, serialize};
-use sled_overlay::{sled, SledDbOverlayStateDiff};
+use kvdb_overlay::{Database, DatabaseOverlayStateDiff, Tree};
 use tracing::{debug, error};
 
 use crate::{
@@ -35,29 +35,29 @@ use crate::{
     Error, Result,
 };
 
-use super::{parse_record, SledDbOverlayPtr};
+use super::{parse_record, KvdbOverlayPtr};
 
-pub const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
-pub const SLED_CONTRACTS_TREES_TREE: &[u8] = b"_contracts_trees";
+pub const KVDB_CONTRACTS_TREE: &str = "_contracts";
+pub const KVDB_CONTRACTS_TREES_TREE: &str = "_contracts_trees";
 // blake3 hash of `_contracts_monotree`
-pub const SLED_CONTRACTS_MONOTREE_TREE: &[u8; 32] = &[
-    82, 161, 124, 97, 228, 243, 197, 75, 11, 86, 60, 214, 241, 24, 64, 100, 86, 48, 159, 147, 254,
-    116, 94, 17, 165, 22, 39, 3, 149, 120, 122, 175,
-];
-pub const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode";
+pub const KVDB_CONTRACTS_MONOTREE_TREE: &str =
+    "52a17c61e4f3c54b0b563cd6f118406456309f93fe745e11a516270395787aaf";
+pub const KVDB_BINCODE_TREE: &str = "_wasm_bincode";
 
-/// The `ContractStore` is a structure representing all `sled` trees related
+/// The `ContractStore` is a structure representing all kvdb trees related
 /// to storing the blockchain's contracts information.
 #[derive(Clone)]
 pub struct ContractStore {
-    /// The `sled` tree storing the wasm bincode for deployed contracts.
+    /// Main pointer to the kvdb connection
+    pub kvdb: Database,
+    /// The kvdb tree storing the wasm bincode for deployed contracts.
     /// The layout looks like this:
     /// ```plaintext
     ///  tree: "_wasm_bincode"
     ///   key: ContractId
     /// value: Vec<u8>
-    pub wasm: sled::Tree,
-    /// The `sled` tree storing the pointers to contracts' databases.
+    pub wasm: Tree,
+    /// The kvdb tree storing the pointers to contracts' databases.
     /// See the rustdoc for the impl functions for more info.
     /// The layout looks like this:
     /// ```plaintext
@@ -66,8 +66,8 @@ pub struct ContractStore {
     /// value: Vec<blake3(ContractId || tree_name)>
     /// ```
     /// These values get mutated with `init()` and `remove()`.
-    pub state: sled::Tree,
-    /// The `sled` tree storing the inverse pointers to contracts'
+    pub state: Tree,
+    /// The kvdb tree storing the inverse pointers to contracts'
     /// databases. See the rustdoc for the impl functions for more
     /// info.
     /// The layout looks like this:
@@ -77,8 +77,8 @@ pub struct ContractStore {
     /// value: ContractId
     /// ```
     /// These values get mutated with `init()` and `remove()`.
-    pub state_trees: sled::Tree,
-    /// The `sled` tree storing the full contracts states monotree,
+    pub state_trees: Tree,
+    /// The kvdb tree storing the full contracts states monotree,
     /// excluding native contracts wasm bincodes.
     /// The layout looks like this:
     /// ```plaintext
@@ -88,23 +88,24 @@ pub struct ContractStore {
     /// ```
     /// These values get mutated on each block/proposal append with
     /// `update_state_monotree()`.
-    pub state_monotree: sled::Tree,
+    pub state_monotree: Tree,
 }
 
 impl ContractStore {
-    /// Opens a new or existing `ContractStore` on the given sled database.
-    pub fn new(db: &sled::Db) -> Result<Self> {
-        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)?;
-        let state_monotree = db.open_tree(SLED_CONTRACTS_MONOTREE_TREE)?;
-        Ok(Self { wasm, state, state_trees, state_monotree })
+    /// Opens a new or existing `ContractStore` on the given key-value
+    /// database.
+    pub fn new(kvdb: &Database) -> Result<Self> {
+        let wasm = kvdb.open_tree_default(KVDB_BINCODE_TREE)?;
+        let state = kvdb.open_tree_default(KVDB_CONTRACTS_TREE)?;
+        let state_trees = kvdb.open_tree_default(KVDB_CONTRACTS_TREES_TREE)?;
+        let state_monotree = kvdb.open_tree_default(KVDB_CONTRACTS_MONOTREE_TREE)?;
+        Ok(Self { kvdb: kvdb.clone(), wasm, state, state_trees, state_monotree })
     }
 
     /// 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<Vec<u8>> {
-        if let Some(bincode) = self.wasm.get(serialize(&contract_id))? {
+        if let Some(bincode) = self.wasm.get(&serialize(&contract_id))? {
             return Ok(bincode.to_vec())
         }
 
@@ -115,12 +116,7 @@ impl ContractStore {
     /// state must have been previously initialized with `init()`. If the
     /// state has been found, a handle to it will be returned. Otherwise, we
     /// return an error.
-    pub fn lookup(
-        &self,
-        db: &sled::Db,
-        contract_id: &ContractId,
-        tree_name: &str,
-    ) -> Result<sled::Tree> {
+    pub fn lookup(&self, contract_id: &ContractId, tree_name: &str) -> Result<Tree> {
         debug!(target: "blockchain::contractstore::lookup", "Looking up state tree for {contract_id}:{tree_name}");
 
         // A guard to make sure we went through init()
@@ -140,7 +136,7 @@ impl ContractStore {
         }
 
         // We open the tree and return its handle
-        let tree = db.open_tree(ptr)?;
+        let tree = self.kvdb.open_tree_default(&blake3::Hash::from(ptr).to_string())?;
         Ok(tree)
     }
 
@@ -151,7 +147,7 @@ impl ContractStore {
     /// 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<()> {
+    pub fn remove(&self, contract_id: &ContractId, tree_name: &str) -> Result<()> {
         debug!(target: "blockchain::contractstore::remove", "Removing state tree for {contract_id}:{tree_name}");
 
         // A guard to make sure we went through init()
@@ -169,39 +165,38 @@ impl ContractStore {
         if !state_pointers.contains(&ptr) {
             return Err(Error::ContractStateNotFound)
         }
-        if !self.state_trees.contains_key(ptr)? {
+        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)?;
+        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)?;
+        self.kvdb.drop_tree(&blake3::Hash::from(ptr).to_string())?;
 
         Ok(())
     }
 
     /// Abstraction function for fetching a `ZkBinary` and its respective `VerifyingKey`
-    /// from a contract's zkas sled tree.
+    /// from a contract's zkas kvdb tree.
     pub fn get_zkas(
         &self,
-        db: &sled::Db,
         contract_id: &ContractId,
         zkas_ns: &str,
     ) -> Result<(ZkBinary, VerifyingKey)> {
         debug!(target: "blockchain::contractstore::get_zkas", "Looking up \"{contract_id}:{zkas_ns}\" zkas circuit & vk");
 
-        let zkas_tree = self.lookup(db, contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
+        let zkas_tree = self.lookup(contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
 
-        let Some(zkas_bytes) = zkas_tree.get(serialize(&zkas_ns))? else {
+        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.
+        // to get into this kvdb tree. This should not be possible.
         let (zkbin, vkbin): (Vec<u8>, Vec<u8>) = deserialize(&zkas_bytes).unwrap();
 
         // The first vec is the compiled zkas binary
@@ -245,10 +240,9 @@ impl ContractStore {
         Ok(contracts)
     }
 
-    /// Retrieve provided key value bytes from a contract's zkas sled tree.
+    /// Retrieve provided key value bytes from a contract's zkas kvdb tree.
     pub fn get_state_tree_value(
         &self,
-        db: &sled::Db,
         contract_id: &ContractId,
         tree_name: &str,
         key: &[u8],
@@ -256,7 +250,7 @@ impl ContractStore {
         debug!(target: "blockchain::contractstore::get_state_tree_value", "Looking up state tree value for {contract_id}:{tree_name}");
 
         // Grab the state tree
-        let state_tree = self.lookup(db, contract_id, tree_name)?;
+        let state_tree = self.lookup(contract_id, tree_name)?;
 
         // Grab the key value
         match state_tree.get(key)? {
@@ -267,18 +261,17 @@ impl ContractStore {
         }
     }
 
-    /// Retrieve all records from a contract's zkas sled tree, as a `BTreeMap`.
+    /// Retrieve all records from a contract's zkas kvdb 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<BTreeMap<Vec<u8>, Vec<u8>>> {
         debug!(target: "blockchain::contractstore::get_state_tree_records", "Looking up state tree records for {contract_id}:{tree_name}");
 
         // Grab the state tree
-        let state_tree = self.lookup(db, contract_id, tree_name)?;
+        let state_tree = self.lookup(contract_id, tree_name)?;
 
         // Retrieve its records
         let mut ret = BTreeMap::new();
@@ -294,7 +287,7 @@ impl ContractStore {
     ///
     /// Note: native contracts wasm bincodes are excluded.
     pub fn get_state_monotree_root(&self) -> Result<StateHash> {
-        let monotree_db = SledTreeDb::new(&self.state_monotree);
+        let monotree_db = KvdbTreeDb::new(&self.state_monotree);
         let monotree = Monotree::new(monotree_db);
         Ok(match monotree.get_headroot()? {
             Some(hash) => hash,
@@ -304,14 +297,14 @@ impl ContractStore {
 }
 
 /// Overlay structure over a [`ContractStore`] instance.
-pub struct ContractStoreOverlay(SledDbOverlayPtr);
+pub struct ContractStoreOverlay(KvdbOverlayPtr);
 
 impl ContractStoreOverlay {
-    pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_BINCODE_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREES_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_CONTRACTS_MONOTREE_TREE, true)?;
+    pub fn new(overlay: &KvdbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree_default(KVDB_BINCODE_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_CONTRACTS_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_CONTRACTS_TREES_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_CONTRACTS_MONOTREE_TREE, true)?;
         Ok(Self(overlay.clone()))
     }
 
@@ -319,7 +312,7 @@ impl ContractStoreOverlay {
     /// Returns an error if the bincode is not found.
     pub fn get(&self, contract_id: ContractId) -> Result<Vec<u8>> {
         if let Some(bincode) =
-            self.0.lock().unwrap().get(SLED_BINCODE_TREE, &serialize(&contract_id))?
+            self.0.lock().unwrap().get(KVDB_BINCODE_TREE, &serialize(&contract_id))?
         {
             return Ok(bincode.to_vec())
         }
@@ -331,7 +324,7 @@ impl ContractStoreOverlay {
     /// 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)
+            self.0.lock().unwrap().insert(KVDB_BINCODE_TREE, &serialize(&contract_id), bincode)
         {
             error!(target: "blockchain::contractstoreoverlay::insert", "Failed to insert bincode to Wasm tree: {e}");
             return Err(e.into())
@@ -358,8 +351,8 @@ impl ContractStoreOverlay {
         // 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();
+            if lock.contains_key(KVDB_CONTRACTS_TREE, &contract_id_bytes)? {
+                let bytes = lock.get(KVDB_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
                 deserialize(&bytes)?
             } else {
                 vec![]
@@ -373,9 +366,9 @@ impl ContractStoreOverlay {
 
         // 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)?;
+        lock.insert(KVDB_CONTRACTS_TREE, &contract_id_bytes, &serialize(&state_pointers))?;
+        lock.insert(KVDB_CONTRACTS_TREES_TREE, &ptr, &contract_id_bytes)?;
+        lock.open_tree_default(&blake3::Hash::from(ptr).to_string(), false)?;
 
         Ok(ptr)
     }
@@ -390,11 +383,11 @@ impl ContractStoreOverlay {
 
         // 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)? {
+        if !lock.contains_key(KVDB_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 = lock.get(KVDB_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
@@ -403,17 +396,17 @@ impl ContractStoreOverlay {
         if !state_pointers.contains(&ptr) {
             return Err(Error::ContractStateNotFound)
         }
-        if !lock.contains_key(SLED_CONTRACTS_TREES_TREE, &ptr)? {
+        if !lock.contains_key(KVDB_CONTRACTS_TREES_TREE, &ptr)? {
             return Err(Error::ContractStateNotFound)
         }
 
         // We open the tree and return its handle
-        lock.open_tree(&ptr, false)?;
+        lock.open_tree_default(&blake3::Hash::from(ptr).to_string(), false)?;
         Ok(ptr)
     }
 
     /// Abstraction function for fetching a `ZkBinary` and its respective `VerifyingKey`
-    /// from a contract's zkas sled tree.
+    /// from a contract's zkas kvdb tree.
     pub fn get_zkas(
         &self,
         contract_id: &ContractId,
@@ -421,14 +414,15 @@ impl ContractStoreOverlay {
     ) -> Result<(ZkBinary, VerifyingKey)> {
         debug!(target: "blockchain::contractstoreoverlay::get_zkas", "Looking up \"{contract_id}:{zkas_ns}\" zkas circuit & vk");
 
-        let zkas_tree = self.lookup(contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?;
+        let zkas_tree =
+            blake3::Hash::from(self.lookup(contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?).to_string();
 
         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.
+        // to get into this kvdb tree. This should not be possible.
         let (zkbin, vkbin): (Vec<u8>, Vec<u8>) = deserialize(&zkas_bytes).unwrap();
 
         // The first vec is the compiled zkas binary
@@ -449,7 +443,7 @@ impl ContractStoreOverlay {
     /// Note: native contracts wasm bincodes are excluded.
     pub fn get_state_monotree_root(&self) -> Result<StateHash> {
         let mut lock = self.0.lock().unwrap();
-        let monotree_db = SledOverlayDb::new(&mut lock, SLED_CONTRACTS_MONOTREE_TREE)?;
+        let monotree_db = KvdbOverlayDb::new(&mut lock, KVDB_CONTRACTS_MONOTREE_TREE)?;
         let monotree = Monotree::new(monotree_db);
         Ok(match monotree.get_headroot()? {
             Some(hash) => hash,
@@ -465,7 +459,7 @@ impl ContractStoreOverlay {
     /// contracts.
     ///
     /// Note: native contracts wasm bincodes are excluded.
-    pub fn update_state_monotree(&self, diff: &SledDbOverlayStateDiff) -> Result<StateHash> {
+    pub fn update_state_monotree(&self, diff: &DatabaseOverlayStateDiff) -> Result<StateHash> {
         // Grab lock over the overlay
         let mut lock = self.0.lock().unwrap();
         debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Retrieving contracts updates...");
@@ -475,7 +469,7 @@ impl ContractStoreOverlay {
         for (state_key, (state_cache, _)) in &diff.caches {
             // Grab new/redeployed contracts wasm bincodes to include them
             // in their monotrees, excluding native ones.
-            if state_key == SLED_BINCODE_TREE {
+            if state_key == KVDB_BINCODE_TREE {
                 for (contract_id_bytes, (_, value)) in &state_cache.cache {
                     // Grab the actual contract ID bytes
                     let contract_id_bytes = deserialize(contract_id_bytes)?;
@@ -515,10 +509,10 @@ impl ContractStoreOverlay {
             }
 
             // Grab the actual state key
-            let state_key: [u8; 32] = deserialize(state_key)?;
+            let state_key: [u8; 32] = *blake3::Hash::from_str(state_key)?.as_bytes();
 
             // Grab its contract id
-            let Some(contract_id_bytes) = lock.get(SLED_CONTRACTS_TREES_TREE, &state_key)? else {
+            let Some(contract_id_bytes) = lock.get(KVDB_CONTRACTS_TREES_TREE, &state_key)? else {
                 return Err(Error::ContractStateNotFound)
             };
             let contract_id_bytes: [u8; 32] = deserialize(&contract_id_bytes)?;
@@ -589,7 +583,8 @@ impl ContractStoreOverlay {
             debug!(target: "blockchain::contractstoreoverlay::update_state_monotree", "Updating monotree for contract: {contract_id}");
 
             // Grab its monotree
-            let monotree_db = SledOverlayDb::new(&mut lock, &contract_updates.monotree_pointer)?;
+            let monotree_ptr = blake3::Hash::from(contract_updates.monotree_pointer).to_string();
+            let monotree_db = KvdbOverlayDb::new(&mut lock, &monotree_ptr)?;
             let mut monotree = Monotree::new(monotree_db);
             let mut monotree_root = monotree.get_headroot()?;
             let monotree_root_hash = match monotree_root {
@@ -624,7 +619,7 @@ impl ContractStoreOverlay {
         }
 
         // Grab the contracts states monotree
-        let monotree_db = SledOverlayDb::new(&mut lock, SLED_CONTRACTS_MONOTREE_TREE)?;
+        let monotree_db = KvdbOverlayDb::new(&mut lock, KVDB_CONTRACTS_MONOTREE_TREE)?;
         let mut monotree = Monotree::new(monotree_db);
         let mut monotree_root = monotree.get_headroot()?;
         let monotree_root_hash = match monotree_root {

+ 41 - 39
src/blockchain/header_store.rs

@@ -26,13 +26,13 @@ use darkfi_sdk::{
 #[cfg(feature = "async-serial")]
 use darkfi_serial::{async_trait, FutAsyncWriteExt};
 use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable};
-use sled_overlay::sled;
+use kvdb_overlay::{Batch, Database, Tree};
 
 use crate::{util::time::Timestamp, Error, Result};
 
 use super::{
     monero::{extract_aux_merkle_root, MoneroPowData},
-    parse_record, parse_u32_key_record, SledDbOverlayPtr,
+    parse_record, parse_u32_key_record, KvdbOverlayPtr,
 };
 
 /// Struct representing the Proof of Work used in a block.
@@ -236,72 +236,74 @@ impl fmt::Display for Header {
     }
 }
 
-pub const SLED_HEADER_TREE: &[u8] = b"_headers";
-pub const SLED_SYNC_HEADER_TREE: &[u8] = b"_sync_headers";
+pub const KVDB_HEADER_TREE: &str = "_headers";
+pub const KVDB_SYNC_HEADER_TREE: &str = "_sync_headers";
 
-/// The `HeaderStore` is a structure representing all `sled` trees related
+/// The `HeaderStore` is a structure representing all kvdb trees related
 /// to storing the blockchain's blocks's header information.
 #[derive(Clone)]
 pub struct HeaderStore {
-    /// Main `sled` tree, storing all the blockchain's blocks' headers,
+    /// Main pointer to the kvdb connection
+    pub kvdb: Database,
+    /// Main kvdb tree, storing all the blockchain's blocks' headers,
     /// where the key is the headers' hash, and value is the serialized header.
-    pub main: sled::Tree,
-    /// The `sled` tree storing all the node pending headers while syncing,
+    pub main: Tree,
+    /// The kvdb tree storing all the node pending headers while syncing,
     /// where the key is the height number, and the value is the serialized
     /// header.
-    pub sync: sled::Tree,
+    pub sync: Tree,
 }
 
 impl HeaderStore {
-    /// Opens a new or existing `HeaderStore` on the given sled database.
-    pub fn new(db: &sled::Db) -> Result<Self> {
-        let main = db.open_tree(SLED_HEADER_TREE)?;
-        let sync = db.open_tree(SLED_SYNC_HEADER_TREE)?;
-        Ok(Self { main, sync })
+    /// Opens a new or existing `HeaderStore` on the given kvdb database.
+    pub fn new(kvdb: &Database) -> Result<Self> {
+        let main = kvdb.open_tree_default(KVDB_HEADER_TREE)?;
+        let sync = kvdb.open_tree_default(KVDB_SYNC_HEADER_TREE)?;
+        Ok(Self { kvdb: kvdb.clone(), main, sync })
     }
 
     /// Insert a slice of [`Header`] into the store's main tree.
     pub fn insert(&self, headers: &[Header]) -> Result<Vec<HeaderHash>> {
         let (batch, ret) = self.insert_batch(headers);
-        self.main.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.main, &batch)])?;
         Ok(ret)
     }
 
     /// Insert a slice of [`Header`] into the store's sync tree.
     pub fn insert_sync(&self, headers: &[Header]) -> Result<()> {
         let batch = self.insert_batch_sync(headers);
-        self.sync.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.sync, &batch)])?;
         Ok(())
     }
 
-    /// Generate the sled batch corresponding to an insert to the main
+    /// Generate the kvdb batch corresponding to an insert to the main
     /// tree, so caller can handle the write operation.
     /// The header's hash() function output is used as the key,
     /// while value is the serialized [`Header`] itself.
     /// On success, the function returns the header hashes in the same
     /// order, along with the corresponding operation batch.
-    pub fn insert_batch(&self, headers: &[Header]) -> (sled::Batch, Vec<HeaderHash>) {
+    pub fn insert_batch(&self, headers: &[Header]) -> (Batch, Vec<HeaderHash>) {
         let mut ret = Vec::with_capacity(headers.len());
-        let mut batch = sled::Batch::default();
+        let mut batch = Batch::new();
 
         for header in headers {
             let headerhash = header.hash();
-            batch.insert(headerhash.inner(), serialize(header));
+            batch.insert(headerhash.inner(), &serialize(header));
             ret.push(headerhash);
         }
 
         (batch, ret)
     }
 
-    /// Generate the sled batch corresponding to an insert to the sync
+    /// Generate the kvdb batch corresponding to an insert to the sync
     /// tree, so caller can handle the write operation.
     /// The header height is used as the key, while value is the serialized
     /// [`Header`] itself.
-    pub fn insert_batch_sync(&self, headers: &[Header]) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    pub fn insert_batch_sync(&self, headers: &[Header]) -> Batch {
+        let mut batch = Batch::new();
 
         for header in headers {
-            batch.insert(&header.height.to_be_bytes(), serialize(header));
+            batch.insert(&header.height.to_be_bytes(), &serialize(header));
         }
 
         batch
@@ -388,7 +390,7 @@ impl HeaderStore {
         let mut key = height;
         let mut counter = 0;
         while counter < n {
-            if let Some(found) = self.sync.get_gt(key.to_be_bytes())? {
+            if let Some(found) = self.sync.get_gt(&key.to_be_bytes())? {
                 let (height, hash) = parse_u32_key_record(found)?;
                 key = height;
                 ret.push(hash);
@@ -402,19 +404,19 @@ impl HeaderStore {
     }
 
     /// Retrieve store's sync tree records count.
-    pub fn len_sync(&self) -> usize {
-        self.sync.len()
+    pub fn len_sync(&self) -> Result<usize> {
+        Ok(self.sync.len()?)
     }
 
     /// Check if store's sync tree contains any records.
-    pub fn is_empty_sync(&self) -> bool {
-        self.sync.is_empty()
+    pub fn is_empty_sync(&self) -> Result<bool> {
+        Ok(self.sync.is_empty()?)
     }
 
     /// Remove a slice of [`u32`] from the store's sync tree.
     pub fn remove_sync(&self, heights: &[u32]) -> Result<()> {
         let batch = self.remove_batch_sync(heights);
-        self.sync.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.sync, &batch)])?;
         Ok(())
     }
 
@@ -423,14 +425,14 @@ impl HeaderStore {
         let headers = self.get_all_sync()?;
         let heights = headers.iter().map(|h| h.0).collect::<Vec<u32>>();
         let batch = self.remove_batch_sync(&heights);
-        self.sync.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.sync, &batch)])?;
         Ok(())
     }
 
-    /// Generate the sled batch corresponding to a remove from the store's sync
+    /// Generate the kvdb batch corresponding to a remove from the store's sync
     /// tree, so caller can handle the write operation.
-    pub fn remove_batch_sync(&self, heights: &[u32]) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    pub fn remove_batch_sync(&self, heights: &[u32]) -> Batch {
+        let mut batch = Batch::new();
 
         for height in heights {
             batch.remove(&height.to_be_bytes());
@@ -441,11 +443,11 @@ impl HeaderStore {
 }
 
 /// Overlay structure over a [`HeaderStore`] instance.
-pub struct HeaderStoreOverlay(SledDbOverlayPtr);
+pub struct HeaderStoreOverlay(KvdbOverlayPtr);
 
 impl HeaderStoreOverlay {
-    pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_HEADER_TREE, true)?;
+    pub fn new(overlay: &KvdbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree_default(KVDB_HEADER_TREE, true)?;
         Ok(Self(overlay.clone()))
     }
 
@@ -459,7 +461,7 @@ impl HeaderStoreOverlay {
 
         for header in headers {
             let headerhash = header.hash();
-            lock.insert(SLED_HEADER_TREE, headerhash.inner(), &serialize(header))?;
+            lock.insert(KVDB_HEADER_TREE, headerhash.inner(), &serialize(header))?;
             ret.push(headerhash);
         }
 
@@ -476,7 +478,7 @@ impl HeaderStoreOverlay {
         let lock = self.0.lock().unwrap();
 
         for hash in headerhashes {
-            if let Some(found) = lock.get(SLED_HEADER_TREE, hash.inner())? {
+            if let Some(found) = lock.get(KVDB_HEADER_TREE, hash.inner())? {
                 let header = deserialize(&found)?;
                 ret.push(Some(header));
                 continue

+ 70 - 97
src/blockchain/mod.rs

@@ -23,70 +23,68 @@ use std::{
 
 use darkfi_sdk::tx::TransactionHash;
 use darkfi_serial::{deserialize, Decodable};
-use sled_overlay::{
-    sled,
-    sled::{IVec, Transactional},
-};
+use kvdb_overlay::{Database, DatabaseOverlay};
 use tracing::debug;
 
 #[cfg(feature = "async-serial")]
 use darkfi_serial::{deserialize_async, AsyncDecodable};
 
-use crate::{tx::Transaction, util::time::Timestamp, Error, Result};
+use crate::{tx::Transaction, util::time::Timestamp, Result};
 
 /// Block related definitions and storage implementations
 pub mod block_store;
 pub use block_store::{
-    Block, BlockDifficulty, BlockInfo, BlockStore, BlockStoreOverlay, SLED_BLOCK_DIFFICULTY_TREE,
-    SLED_BLOCK_ORDER_TREE, SLED_BLOCK_STATE_INVERSE_DIFF_TREE, SLED_BLOCK_TREE,
+    Block, BlockDifficulty, BlockInfo, BlockStore, BlockStoreOverlay, KVDB_BLOCK_DIFFICULTY_TREE,
+    KVDB_BLOCK_ORDER_TREE, KVDB_BLOCK_STATE_INVERSE_DIFF_TREE, KVDB_BLOCK_TREE,
 };
 
 /// Header definition and storage implementation
 pub mod header_store;
 pub use header_store::{
-    Header, HeaderHash, HeaderStore, HeaderStoreOverlay, SLED_HEADER_TREE, SLED_SYNC_HEADER_TREE,
+    Header, HeaderHash, HeaderStore, HeaderStoreOverlay, KVDB_HEADER_TREE, KVDB_SYNC_HEADER_TREE,
 };
 
 /// Transactions related storage implementations
 pub mod tx_store;
 pub use tx_store::{
-    TxStore, TxStoreOverlay, SLED_PENDING_TX_TREE, SLED_TX_LOCATION_TREE, SLED_TX_TREE,
+    TxStore, TxStoreOverlay, KVDB_PENDING_TX_TREE, KVDB_TX_LOCATION_TREE, KVDB_TX_TREE,
 };
 
 /// Contracts and Wasm storage implementations
 pub mod contract_store;
 pub use contract_store::{
-    ContractStore, ContractStoreOverlay, SLED_BINCODE_TREE, SLED_CONTRACTS_TREE,
-    SLED_CONTRACTS_TREES_TREE,
+    ContractStore, ContractStoreOverlay, KVDB_BINCODE_TREE, KVDB_CONTRACTS_TREE,
+    KVDB_CONTRACTS_TREES_TREE,
 };
 
 /// Monero definitions needed for merge mining
 pub mod monero;
 
-/// Structure holding all sled trees that define the concept of Blockchain.
+/// Structure holding all kvdb trees that define the concept of Blockchain.
 #[derive(Clone)]
 pub struct Blockchain {
-    /// Main pointer to the sled db connection
-    pub sled_db: sled::Db,
-    /// Headers sled tree
+    /// Main pointer to the kvdb connection
+    pub kvdb: Database,
+    /// Headers kvdb tree
     pub headers: HeaderStore,
-    /// Blocks sled tree
+    /// Blocks kvdb tree
     pub blocks: BlockStore,
-    /// Transactions related sled trees
+    /// Transactions related kvdb trees
     pub transactions: TxStore,
-    /// Contracts related sled trees
+    /// Contracts related kvdb trees
     pub contracts: ContractStore,
 }
 
 impl Blockchain {
-    /// Instantiate a new `Blockchain` with the given `sled` database.
-    pub fn new(db: &sled::Db) -> Result<Self> {
-        let headers = HeaderStore::new(db)?;
-        let blocks = BlockStore::new(db)?;
-        let transactions = TxStore::new(db)?;
-        let contracts = ContractStore::new(db)?;
+    /// Instantiate a new `Blockchain` with the given key-value
+    /// database.
+    pub fn new(kvdb: &Database) -> Result<Self> {
+        let headers = HeaderStore::new(kvdb)?;
+        let blocks = BlockStore::new(kvdb)?;
+        let transactions = TxStore::new(kvdb)?;
+        let contracts = ContractStore::new(kvdb)?;
 
-        Ok(Self { sled_db: db.clone(), headers, blocks, transactions, contracts })
+        Ok(Self { kvdb: kvdb.clone(), headers, blocks, transactions, contracts })
     }
 
     /// Insert a given [`BlockInfo`] into the blockchain database.
@@ -95,41 +93,35 @@ impl Blockchain {
     /// Upon success, the functions returns the block hash that
     /// were given and appended to the ledger.
     pub fn add_block(&self, block: &BlockInfo) -> Result<HeaderHash> {
-        let mut trees = vec![];
-        let mut batches = vec![];
+        let mut changes = vec![];
 
         // Store header
         let (headers_batch, _) = self.headers.insert_batch(slice::from_ref(&block.header));
-        trees.push(self.headers.main.clone());
-        batches.push(headers_batch);
+        changes.push((&self.headers.main, &headers_batch));
 
         // Store block
         let blk: Block = Block::from_block_info(block);
         let (bocks_batch, block_hashes) = self.blocks.insert_batch(&[blk]);
         let block_hash = block_hashes[0];
         let block_hash_vec = [block_hash];
-        trees.push(self.blocks.main.clone());
-        batches.push(bocks_batch);
+        changes.push((&self.blocks.main, &bocks_batch));
 
         // Store block order
         let blocks_order_batch =
             self.blocks.insert_batch_order(&[block.header.height], &block_hash_vec);
-        trees.push(self.blocks.order.clone());
-        batches.push(blocks_order_batch);
+        changes.push((&self.blocks.order, &blocks_order_batch));
 
         // Store transactions
         let (txs_batch, txs_hashes) = self.transactions.insert_batch(&block.txs);
-        trees.push(self.transactions.main.clone());
-        batches.push(txs_batch);
+        changes.push((&self.transactions.main, &txs_batch));
 
         // Store transactions_locations
         let txs_locations_batch =
             self.transactions.insert_batch_location(&txs_hashes, block.header.height);
-        trees.push(self.transactions.location.clone());
-        batches.push(txs_locations_batch);
+        changes.push((&self.transactions.location, &txs_locations_batch));
 
         // Perform an atomic transaction over the trees and apply the batches.
-        self.atomic_write(&trees, &batches)?;
+        self.kvdb.atomic_write(&changes)?;
 
         Ok(block_hash)
     }
@@ -222,17 +214,17 @@ impl Blockchain {
     }
 
     /// Retrieve stored blocks count
-    pub fn len(&self) -> usize {
+    pub fn len(&self) -> Result<usize> {
         self.blocks.len()
     }
 
     /// Retrieve stored txs count
-    pub fn txs_len(&self) -> usize {
+    pub fn txs_len(&self) -> Result<usize> {
         self.transactions.len()
     }
 
     /// Check if blockchain contains any blocks
-    pub fn is_empty(&self) -> bool {
+    pub fn is_empty(&self) -> Result<bool> {
         self.blocks.is_empty()
     }
 
@@ -315,23 +307,6 @@ impl Blockchain {
         self.transactions.remove_pending(txs)
     }
 
-    /// Auxiliary function to write to multiple trees completely atomic.
-    fn atomic_write(&self, trees: &[sled::Tree], batches: &[sled::Batch]) -> Result<()> {
-        if trees.len() != batches.len() {
-            return Err(Error::InvalidInputLengths)
-        }
-
-        trees.transaction(|trees| {
-            for (index, tree) in trees.iter().enumerate() {
-                tree.apply_batch(&batches[index])?;
-            }
-
-            Ok::<(), sled::transaction::ConflictableTransactionError<sled::Error>>(())
-        })?;
-
-        Ok(())
-    }
-
     /// Retrieve all blocks contained in the blockchain in order.
     /// Be careful as this will try to load everything in memory.
     pub fn get_all(&self) -> Result<Vec<BlockInfo>> {
@@ -392,7 +367,7 @@ impl Blockchain {
             let inverse_diff = inverse_diff.unwrap();
             lock.add_diff(&inverse_diff)?;
             lock.apply_diff(&inverse_diff)?;
-            self.sled_db.flush()?;
+            self.kvdb.flush_default_mode()?;
         }
         drop(lock);
         drop(overlay_lock);
@@ -463,16 +438,16 @@ impl Blockchain {
     }
 }
 
-/// Atomic pointer to sled db overlay.
-pub type SledDbOverlayPtr = Arc<Mutex<sled_overlay::SledDbOverlay>>;
+/// Atomic pointer to kvdb overlay.
+pub type KvdbOverlayPtr = Arc<Mutex<DatabaseOverlay>>;
 
 /// Atomic pointer to blockchain overlay.
 pub type BlockchainOverlayPtr = Arc<Mutex<BlockchainOverlay>>;
 
 /// Overlay structure over a [`Blockchain`] instance.
 pub struct BlockchainOverlay {
-    /// Main [`sled_overlay::SledDbOverlay`] to the sled db connection
-    pub overlay: SledDbOverlayPtr,
+    /// Main [`kvdb_overlay::DatabaseOverlay`] to the kvdb connection
+    pub overlay: KvdbOverlayPtr,
     /// Headers overlay
     pub headers: HeaderStoreOverlay,
     /// Blocks overlay
@@ -486,25 +461,23 @@ pub struct BlockchainOverlay {
 impl BlockchainOverlay {
     /// Instantiate a new `BlockchainOverlay` over the given [`Blockchain`] instance.
     pub fn new(blockchain: &Blockchain) -> Result<BlockchainOverlayPtr> {
-        // Here we configure all our blockchain sled trees to be protected in the overlay
+        // Here we configure all our blockchain kvdb trees to be protected in the overlay
         let protected_trees = vec![
-            SLED_BLOCK_TREE,
-            SLED_BLOCK_ORDER_TREE,
-            SLED_BLOCK_DIFFICULTY_TREE,
-            SLED_BLOCK_STATE_INVERSE_DIFF_TREE,
-            SLED_HEADER_TREE,
-            SLED_SYNC_HEADER_TREE,
-            SLED_TX_TREE,
-            SLED_TX_LOCATION_TREE,
-            SLED_PENDING_TX_TREE,
-            SLED_CONTRACTS_TREE,
-            SLED_CONTRACTS_TREES_TREE,
-            SLED_BINCODE_TREE,
+            KVDB_BLOCK_TREE.to_string(),
+            KVDB_BLOCK_ORDER_TREE.to_string(),
+            KVDB_BLOCK_DIFFICULTY_TREE.to_string(),
+            KVDB_BLOCK_STATE_INVERSE_DIFF_TREE.to_string(),
+            KVDB_HEADER_TREE.to_string(),
+            KVDB_SYNC_HEADER_TREE.to_string(),
+            KVDB_TX_TREE.to_string(),
+            KVDB_TX_LOCATION_TREE.to_string(),
+            KVDB_PENDING_TX_TREE.to_string(),
+            KVDB_CONTRACTS_TREE.to_string(),
+            KVDB_CONTRACTS_TREES_TREE.to_string(),
+            KVDB_BINCODE_TREE.to_string(),
         ];
-        let overlay = Arc::new(Mutex::new(sled_overlay::SledDbOverlay::new(
-            &blockchain.sled_db,
-            protected_trees,
-        )));
+        let overlay =
+            Arc::new(Mutex::new(DatabaseOverlay::new(&blockchain.kvdb, protected_trees)?));
         let headers = HeaderStoreOverlay::new(&overlay)?;
         let blocks = BlockStoreOverlay::new(&overlay)?;
         let transactions = TxStoreOverlay::new(&overlay)?;
@@ -643,7 +616,7 @@ impl BlockchainOverlay {
         self.overlay.lock().unwrap().revert_to_checkpoint();
     }
 
-    /// Auxiliary function to create a full clone using SledDbOverlay::clone,
+    /// Auxiliary function to create a full clone using DatabaseOverlay::clone,
     /// generating new pointers for the underlying overlays.
     pub fn full_clone(&self) -> Result<BlockchainOverlayPtr> {
         let overlay = Arc::new(Mutex::new(self.overlay.lock().unwrap().clone()));
@@ -656,28 +629,28 @@ impl BlockchainOverlay {
     }
 }
 
-/// Parse a sled record in the form of a tuple (`key`, `value`).
-pub fn parse_record<T1: Decodable, T2: Decodable>(record: (IVec, IVec)) -> Result<(T1, T2)> {
+/// Parse a kvdb record in the form of a tuple (`key`, `value`).
+pub fn parse_record<T1: Decodable, T2: Decodable>(record: (Vec<u8>, Vec<u8>)) -> Result<(T1, T2)> {
     let key = deserialize(&record.0)?;
     let value = deserialize(&record.1)?;
 
     Ok((key, value))
 }
 
-/// Parse a sled record with a u32 key, encoded in Big Endian bytes,
+/// Parse a kvdb record with a u32 key, encoded in Big Endian bytes,
 /// in the form of a tuple (`key`, `value`).
-pub fn parse_u32_key_record<T: Decodable>(record: (IVec, IVec)) -> Result<(u32, T)> {
-    let key_bytes: [u8; 4] = record.0.as_ref().try_into().unwrap();
+pub fn parse_u32_key_record<T: Decodable>(record: (Vec<u8>, Vec<u8>)) -> Result<(u32, T)> {
+    let key_bytes: [u8; 4] = record.0.try_into().unwrap();
     let key = u32::from_be_bytes(key_bytes);
     let value = deserialize(&record.1)?;
 
     Ok((key, value))
 }
 
-/// Parse a sled record with a u64 key, encoded in Big Endian bytes,
+/// Parse a kvdb record with a u64 key, encoded in Big Endian bytes,
 /// in the form of a tuple (`key`, `value`).
-pub fn parse_u64_key_record<T: Decodable>(record: (IVec, IVec)) -> Result<(u64, T)> {
-    let key_bytes: [u8; 8] = record.0.as_ref().try_into().unwrap();
+pub fn parse_u64_key_record<T: Decodable>(record: (Vec<u8>, Vec<u8>)) -> Result<(u64, T)> {
+    let key_bytes: [u8; 8] = record.0.try_into().unwrap();
     let key = u64::from_be_bytes(key_bytes);
     let value = deserialize(&record.1)?;
 
@@ -685,9 +658,9 @@ pub fn parse_u64_key_record<T: Decodable>(record: (IVec, IVec)) -> Result<(u64,
 }
 
 #[cfg(feature = "async-serial")]
-/// Parse a sled record in the form of a tuple (`key`, `value`).
+/// Parse a kvdb record in the form of a tuple (`key`, `value`).
 pub async fn parse_record_async<T1: AsyncDecodable, T2: AsyncDecodable>(
-    record: (IVec, IVec),
+    record: (Vec<u8>, Vec<u8>),
 ) -> Result<(T1, T2)> {
     let key = deserialize_async(&record.0).await?;
     let value = deserialize_async(&record.1).await?;
@@ -696,12 +669,12 @@ pub async fn parse_record_async<T1: AsyncDecodable, T2: AsyncDecodable>(
 }
 
 #[cfg(feature = "async-serial")]
-/// Parse a sled record with a u32 key, encoded in Big Endian bytes,
+/// Parse a kvdb record with a u32 key, encoded in Big Endian bytes,
 /// in the form of a tuple (`key`, `value`).
 pub async fn parse_u32_key_record_async<T: AsyncDecodable>(
-    record: (IVec, IVec),
+    record: (Vec<u8>, Vec<u8>),
 ) -> Result<(u32, T)> {
-    let key_bytes: [u8; 4] = record.0.as_ref().try_into().unwrap();
+    let key_bytes: [u8; 4] = record.0.try_into().unwrap();
     let key = u32::from_be_bytes(key_bytes);
     let value = deserialize_async(&record.1).await?;
 
@@ -709,12 +682,12 @@ pub async fn parse_u32_key_record_async<T: AsyncDecodable>(
 }
 
 #[cfg(feature = "async-serial")]
-/// Parse a sled record with a u64 key, encoded in Big Endian bytes,
+/// Parse a kvdb record with a u64 key, encoded in Big Endian bytes,
 /// in the form of a tuple (`key`, `value`).
 pub async fn parse_u64_key_record_async<T: AsyncDecodable>(
-    record: (IVec, IVec),
+    record: (Vec<u8>, Vec<u8>),
 ) -> Result<(u64, T)> {
-    let key_bytes: [u8; 8] = record.0.as_ref().try_into().unwrap();
+    let key_bytes: [u8; 8] = record.0.try_into().unwrap();
     let key = u64::from_be_bytes(key_bytes);
     let value = deserialize_async(&record.1).await?;
 

+ 58 - 59
src/blockchain/tx_store.rs

@@ -20,89 +20,88 @@ use std::collections::HashMap;
 
 use darkfi_sdk::tx::TransactionHash;
 use darkfi_serial::{deserialize, serialize};
-use sled_overlay::sled;
+use kvdb_overlay::{Batch, Database, Tree};
 
 use crate::{tx::Transaction, Error, Result};
 
-use super::{parse_record, SledDbOverlayPtr};
+use super::{parse_record, KvdbOverlayPtr};
 
-pub const SLED_TX_TREE: &[u8] = b"_transactions";
-pub const SLED_TX_LOCATION_TREE: &[u8] = b"_transaction_location";
-pub const SLED_PENDING_TX_TREE: &[u8] = b"_pending_transactions";
+pub const KVDB_TX_TREE: &str = "_transactions";
+pub const KVDB_TX_LOCATION_TREE: &str = "_transaction_location";
+pub const KVDB_PENDING_TX_TREE: &str = "_pending_transactions";
 
-/// The `TxStore` is a structure representing all `sled` trees related
+/// The `TxStore` is a structure representing all kvdb trees related
 /// to storing the blockchain's transactions information.
 #[derive(Clone)]
 pub struct TxStore {
-    /// Main `sled` tree, storing all the blockchain's transactions, where
+    /// Main pointer to the kvdb connection
+    pub kvdb: Database,
+    /// Main kvdb tree, storing all the blockchain's transactions, where
     /// the key is the transaction hash, and the value is the serialized
     /// transaction.
-    pub main: sled::Tree,
-    /// The `sled` tree storing the location of the blockchain's transactions
+    pub main: Tree,
+    /// The kvdb tree storing the location of the blockchain's transactions
     /// locations, where the key is the transaction hash, and the value is a
     /// serialized tuple containing the height and the vector index of the
     /// block the transaction is included.
-    pub location: sled::Tree,
-    /// The `sled` tree storing all the node pending transactions, where
+    pub location: Tree,
+    /// The kvdb tree storing all the node pending transactions, where
     /// the key is the transaction hash, and the value is the serialized
     /// transaction.
-    pub pending: sled::Tree,
+    pub pending: Tree,
 }
 
 impl TxStore {
-    /// Opens a new or existing `TxStore` on the given sled database.
-    pub fn new(db: &sled::Db) -> Result<Self> {
-        let main = db.open_tree(SLED_TX_TREE)?;
-        let location = db.open_tree(SLED_TX_LOCATION_TREE)?;
-        let pending = db.open_tree(SLED_PENDING_TX_TREE)?;
-        Ok(Self { main, location, pending })
+    /// Opens a new or existing `TxStore` on the given kvdb database.
+    pub fn new(kvdb: &Database) -> Result<Self> {
+        let main = kvdb.open_tree_default(KVDB_TX_TREE)?;
+        let location = kvdb.open_tree_default(KVDB_TX_LOCATION_TREE)?;
+        let pending = kvdb.open_tree_default(KVDB_PENDING_TX_TREE)?;
+        Ok(Self { kvdb: kvdb.clone(), main, location, pending })
     }
 
     /// Insert a slice of [`Transaction`] into the store's main tree.
     pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<TransactionHash>> {
         let (batch, ret) = self.insert_batch(transactions);
-        self.main.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.main, &batch)])?;
         Ok(ret)
     }
 
     /// Insert a slice of [`TransactionHash`] into the store's location tree.
     pub fn insert_location(&self, txs_hashes: &[TransactionHash], block_height: u32) -> Result<()> {
         let batch = self.insert_batch_location(txs_hashes, block_height);
-        self.location.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.location, &batch)])?;
         Ok(())
     }
 
     /// Insert a slice of [`Transaction`] into the store's pending txs tree.
     pub fn insert_pending(&self, transactions: &[Transaction]) -> Result<Vec<TransactionHash>> {
         let (batch, ret) = self.insert_batch_pending(transactions);
-        self.pending.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.pending, &batch)])?;
         Ok(ret)
     }
 
-    /// Generate the sled batch corresponding to an insert to the main tree,
+    /// Generate the kvdb batch corresponding to an insert to the main tree,
     /// so caller can handle the write operation.
     /// The transactions are hashed with BLAKE3 and this hash is used as
     /// the key, while the value is the serialized [`Transaction`] itself.
     /// On success, the function returns the transaction hashes in the same
     /// order as the input transactions, along with the corresponding operation
     /// batch.
-    pub fn insert_batch(
-        &self,
-        transactions: &[Transaction],
-    ) -> (sled::Batch, Vec<TransactionHash>) {
+    pub fn insert_batch(&self, transactions: &[Transaction]) -> (Batch, Vec<TransactionHash>) {
         let mut ret = Vec::with_capacity(transactions.len());
-        let mut batch = sled::Batch::default();
+        let mut batch = Batch::new();
 
         for tx in transactions {
             let tx_hash = tx.hash();
-            batch.insert(tx_hash.inner(), serialize(tx));
+            batch.insert(tx_hash.inner(), &serialize(tx));
             ret.push(tx_hash);
         }
 
         (batch, ret)
     }
 
-    /// Generate the sled batch corresponding to an insert to the location tree,
+    /// Generate the kvdb batch corresponding to an insert to the location tree,
     /// so caller can handle the write operation.
     /// The location tuple is built using the index of each transaction has in
     /// the slice, along with the provided block height
@@ -110,18 +109,18 @@ impl TxStore {
         &self,
         txs_hashes: &[TransactionHash],
         block_height: u32,
-    ) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    ) -> Batch {
+        let mut batch = Batch::new();
 
         for (index, tx_hash) in txs_hashes.iter().enumerate() {
             let serialized = serialize(&(block_height, index as u16));
-            batch.insert(tx_hash.inner(), serialized);
+            batch.insert(tx_hash.inner(), &serialized);
         }
 
         batch
     }
 
-    /// Generate the sled batch corresponding to an insert to the pending txs tree,
+    /// Generate the kvdb batch corresponding to an insert to the pending txs tree,
     /// so caller can handle the write operation.
     /// The transactions are hashed with BLAKE3 and this hash is used as
     /// the key, while the value is the serialized [`Transaction`] itself.
@@ -131,13 +130,13 @@ impl TxStore {
     pub fn insert_batch_pending(
         &self,
         transactions: &[Transaction],
-    ) -> (sled::Batch, Vec<TransactionHash>) {
+    ) -> (Batch, Vec<TransactionHash>) {
         let mut ret = Vec::with_capacity(transactions.len());
-        let mut batch = sled::Batch::default();
+        let mut batch = Batch::new();
 
         for tx in transactions {
             let tx_hash = tx.hash();
-            batch.insert(tx_hash.inner(), serialize(tx));
+            batch.insert(tx_hash.inner(), &serialize(tx));
             ret.push(tx_hash);
         }
 
@@ -283,10 +282,10 @@ impl TxStore {
         n: usize,
     ) -> Result<Vec<Transaction>> {
         let mut txs = vec![];
-        let mut key = tx_hash.inner().into();
+        let mut key = tx_hash.inner().to_vec();
         let mut counter = 0;
         while counter < n {
-            if let Some((tx_hash, tx)) = self.pending.get_gt(key)? {
+            if let Some((tx_hash, tx)) = self.pending.get_gt(&key)? {
                 key = tx_hash;
                 txs.push(deserialize(&tx)?);
                 counter += 1;
@@ -299,26 +298,26 @@ impl TxStore {
     }
 
     /// Retrieve records count of the store's main tree.
-    pub fn len(&self) -> usize {
-        self.main.len()
+    pub fn len(&self) -> Result<usize> {
+        Ok(self.main.len()?)
     }
 
     /// Check if the store's main tree is empty.
-    pub fn is_empty(&self) -> bool {
-        self.main.is_empty()
+    pub fn is_empty(&self) -> Result<bool> {
+        Ok(self.main.is_empty()?)
     }
 
     /// Remove a slice of [`TransactionHash`] from the store's pending txs tree.
     pub fn remove_pending(&self, txs_hashes: &[TransactionHash]) -> Result<()> {
         let batch = self.remove_batch_pending(txs_hashes);
-        self.pending.apply_batch(batch)?;
+        self.kvdb.atomic_write(&[(&self.pending, &batch)])?;
         Ok(())
     }
 
-    /// Generate the sled batch corresponding to a remove from the store's pending
+    /// Generate the kvdb batch corresponding to a remove from the store's pending
     /// txs tree, so caller can handle the write operation.
-    pub fn remove_batch_pending(&self, txs_hashes: &[TransactionHash]) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    pub fn remove_batch_pending(&self, txs_hashes: &[TransactionHash]) -> Batch {
+        let mut batch = Batch::new();
 
         for tx_hash in txs_hashes {
             batch.remove(tx_hash.inner());
@@ -327,10 +326,10 @@ impl TxStore {
         batch
     }
 
-    /// Generate the sled batch corresponding to a remove from the store's pending
+    /// Generate the kvdb batch corresponding to a remove from the store's pending
     /// txs order tree, so caller can handle the write operation.
-    pub fn remove_batch_pending_order(&self, indexes: &[u64]) -> sled::Batch {
-        let mut batch = sled::Batch::default();
+    pub fn remove_batch_pending_order(&self, indexes: &[u64]) -> Batch {
+        let mut batch = Batch::new();
 
         for index in indexes {
             batch.remove(&index.to_be_bytes());
@@ -341,12 +340,12 @@ impl TxStore {
 }
 
 /// Overlay structure over a [`TxStore`] instance.
-pub struct TxStoreOverlay(SledDbOverlayPtr);
+pub struct TxStoreOverlay(KvdbOverlayPtr);
 
 impl TxStoreOverlay {
-    pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
-        overlay.lock().unwrap().open_tree(SLED_TX_TREE, true)?;
-        overlay.lock().unwrap().open_tree(SLED_TX_LOCATION_TREE, true)?;
+    pub fn new(overlay: &KvdbOverlayPtr) -> Result<Self> {
+        overlay.lock().unwrap().open_tree_default(KVDB_TX_TREE, true)?;
+        overlay.lock().unwrap().open_tree_default(KVDB_TX_LOCATION_TREE, true)?;
         Ok(Self(overlay.clone()))
     }
 
@@ -361,7 +360,7 @@ impl TxStoreOverlay {
 
         for tx in transactions {
             let tx_hash = tx.hash();
-            lock.insert(SLED_TX_TREE, tx_hash.inner(), &serialize(tx))?;
+            lock.insert(KVDB_TX_TREE, tx_hash.inner(), &serialize(tx))?;
             ret.push(tx_hash);
         }
 
@@ -376,7 +375,7 @@ impl TxStoreOverlay {
 
         for (index, tx_hash) in txs_hashes.iter().enumerate() {
             let serialized = serialize(&(block_height, index as u16));
-            lock.insert(SLED_TX_LOCATION_TREE, tx_hash.inner(), &serialized)?;
+            lock.insert(KVDB_TX_LOCATION_TREE, tx_hash.inner(), &serialized)?;
         }
 
         Ok(())
@@ -384,7 +383,7 @@ impl TxStoreOverlay {
 
     /// Check if the overlay's main tree contains a given transaction hash.
     pub fn contains(&self, tx_hash: &TransactionHash) -> Result<bool> {
-        Ok(self.0.lock().unwrap().contains_key(SLED_TX_TREE, tx_hash.inner())?)
+        Ok(self.0.lock().unwrap().contains_key(KVDB_TX_TREE, tx_hash.inner())?)
     }
 
     /// Fetch given tx hashes from the overlay's main tree.
@@ -401,7 +400,7 @@ impl TxStoreOverlay {
         let lock = self.0.lock().unwrap();
 
         for tx_hash in tx_hashes {
-            if let Some(found) = lock.get(SLED_TX_TREE, tx_hash.inner())? {
+            if let Some(found) = lock.get(KVDB_TX_TREE, tx_hash.inner())? {
                 let tx = deserialize(&found)?;
                 ret.push(Some(tx));
                 continue
@@ -421,7 +420,7 @@ impl TxStoreOverlay {
     /// was found in the overlay, and otherwise it is `None`, if it has not.
     pub fn get_raw(&self, tx_hash: &[u8; 32]) -> Result<Option<Vec<u8>>> {
         let lock = self.0.lock().unwrap();
-        if let Some(found) = lock.get(SLED_TX_TREE, tx_hash)? {
+        if let Some(found) = lock.get(KVDB_TX_TREE, tx_hash)? {
             return Ok(Some(found.to_vec()))
         }
         Ok(None)
@@ -434,7 +433,7 @@ impl TxStoreOverlay {
     /// is `None`, if it has not.
     pub fn get_location_raw(&self, tx_hash: &[u8; 32]) -> Result<Option<Vec<u8>>> {
         let lock = self.0.lock().unwrap();
-        if let Some(found) = lock.get(SLED_TX_LOCATION_TREE, tx_hash)? {
+        if let Some(found) = lock.get(KVDB_TX_LOCATION_TREE, tx_hash)? {
             return Ok(Some(found.to_vec()))
         }
         Ok(None)

+ 1 - 1
src/contract/dao/src/lib.rs

@@ -60,7 +60,7 @@ pub mod entrypoint;
 /// Client API for interaction with this smart contract
 pub mod client;
 
-// These are the different sled trees that will be created
+// These are the different kvdb trees that will be created
 pub const DAO_CONTRACT_INFO_TREE: &str = "info";
 pub const DAO_CONTRACT_BULLAS_TREE: &str = "bullas";
 pub const DAO_CONTRACT_MERKLE_ROOTS_TREE: &str = "roots";

+ 1 - 1
src/contract/deployooor/src/lib.rs

@@ -54,7 +54,7 @@ pub mod error;
 /// Client API for interaction with this smart contract
 pub mod client;
 
-// These are the different sled trees that will be created
+// These are the different kvdb trees that will be created
 pub const DEPLOY_CONTRACT_INFO_TREE: &str = "info";
 pub const DEPLOY_CONTRACT_LOCK_TREE: &str = "lock";
 

+ 1 - 1
src/contract/money/src/entrypoint.rs

@@ -109,7 +109,7 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
     // zkas circuits can simply be embedded in the wasm and set up by using
     // respective db functions. The special `zkas db` operations exist in
     // order to be able to verify the circuits being bundled and enforcing
-    // a specific tree inside sled, and also creation of VerifyingKey.
+    // a specific tree inside the kvdb, and also creation of VerifyingKey.
     let fee_v1_bincode = include_bytes!("../proof/fee_v1.zk.bin");
     let mint_v1_bincode = include_bytes!("../proof/mint_v1.zk.bin");
     let burn_v1_bincode = include_bytes!("../proof/burn_v1.zk.bin");

+ 1 - 1
src/contract/money/src/lib.rs

@@ -71,7 +71,7 @@ pub mod entrypoint;
 /// Client API for interaction with this smart contract
 pub mod client;
 
-// These are the different sled trees that will be created
+// These are the different kvdb trees that will be created
 pub const MONEY_CONTRACT_INFO_TREE: &str = "info";
 pub const MONEY_CONTRACT_COINS_TREE: &str = "coins";
 pub const MONEY_CONTRACT_COIN_ROOTS_TREE: &str = "coin_roots";

+ 1 - 1
src/contract/test-harness/Cargo.toml

@@ -16,10 +16,10 @@ darkfi_deployooor_contract = {path = "../deployooor", features = ["client", "no-
 
 num-bigint = "0.4.6"
 blake3 = "1.8.5"
+kvdb-overlay = {git = "https://git.dark.fi/darkrenaissance/kvdb-overlay", version = "0.1.0"}
 tracing = "0.1.44"
 tracing-subscriber = { version = "0.3.23", default-features = false, features = ["fmt"] }
 rand = "0.8.6"
-sled-overlay = "0.1.20"
 parking_lot = "0.12.5"
 
 [lints]

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

@@ -72,12 +72,12 @@ impl TestHarness {
             .unwrap()
             .clone();
 
-        // Useful code snippet to dump a sled contract DB
+        // Useful code snippet to dump a kvdb contract DB
         /*{
             let blockchain = &wallet.validator.read().await.blockchain;
             let contracts = &blockchain.contracts;
             let tree = contracts
-                .lookup(&blockchain.sled_db, &MONEY_CONTRACT_ID, "nullifier_roots")
+                .lookup(&blockchain.kvdb, &MONEY_CONTRACT_ID, "nullifier_roots")
                 .unwrap();
             for kv in tree.iter() {
                 let (key, value) = kv.unwrap();

+ 11 - 8
src/contract/test-harness/src/lib.rs

@@ -59,10 +59,10 @@ use darkfi_sdk::{
     pasta::pallas,
 };
 use darkfi_serial::{serialize, Encodable};
+use kvdb_overlay::{Database, TempDir};
 use num_bigint::BigUint;
 use parking_lot::Mutex;
 use rand::rngs::OsRng;
-use sled_overlay::sled;
 use tracing::{debug, warn};
 
 /// Utility module for caching ZK proof PKs and VKs
@@ -105,7 +105,7 @@ pub fn init_logger() {
     // We check this error so we can execute same-file tests in parallel.
     // Otherwise subsequent calls fail to init the logger here.
     if setup_test_logger(
-        &["sled"],
+        &["fjall", "sled"],
         false,
         Level::Info,
         //Level::Verbose,
@@ -138,6 +138,8 @@ pub struct Wallet {
     pub contract_deploy_authority: Keypair,
     /// Holder's [`Validator`] instance
     pub validator: ValidatorPtr,
+    /// Holder's temp database folder
+    pub kvdb_folder: TempDir,
     /// Holder's instance of the Merkle tree for the `Money` contract
     pub money_merkle_tree: MerkleTree,
     /// Holder's instance of the nullifiers SMT tree for the `Money` contract
@@ -173,11 +175,11 @@ impl Wallet {
         vks: &vks::Vks,
         verify_fees: bool,
     ) -> Result<Self> {
-        // Create an in-memory sled db instance for this wallet
-        let sled_db = sled::Config::new().temporary(true).open()?;
+        // Create an in-memory kvdb instance for this wallet
+        let (kvdb, kvdb_folder) = Database::open_temp()?;
 
         // Inject the cached VKs into the database
-        let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
+        let overlay = BlockchainOverlay::new(&Blockchain::new(&kvdb)?)?;
         vks::inject(&overlay, vks)?;
 
         deploy_native_contracts(&overlay, POW_TARGET).await?;
@@ -194,7 +196,7 @@ impl Wallet {
             genesis_block,
             verify_fees,
         };
-        let validator = Validator::new(&sled_db, &validator_config).await?;
+        let validator = Validator::new(&kvdb, &validator_config).await?;
 
         // The Merkle tree for the Money contract is initialized with a
         // "null" leaf at position 0.
@@ -211,6 +213,7 @@ impl Wallet {
             token_mint_authority,
             contract_deploy_authority,
             validator,
+            kvdb_folder,
             money_merkle_tree,
             money_null_smt,
             money_null_smt_snapshot: None,
@@ -367,8 +370,8 @@ impl TestHarness {
         }
 
         // Compute genesis contracts states monotree root
-        let sled_db = sled::Config::new().temporary(true).open()?;
-        let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
+        let (kvdb, _kvdb_folder) = Database::open_temp()?;
+        let overlay = BlockchainOverlay::new(&Blockchain::new(&kvdb)?)?;
         vks::inject(&overlay, &vks)?;
         deploy_native_contracts(&overlay, POW_TARGET).await?;
         let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;

+ 7 - 4
src/contract/test-harness/src/vks.rs

@@ -186,12 +186,15 @@ pub fn inject(overlay: &BlockchainOverlayPtr, vks: &Vks) -> Result<()> {
     let mut overlay = lock.overlay.lock().unwrap();
 
     // Derive the database names for the specific contracts
-    let money_db_name = MONEY_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME);
-    let dao_db_name = DAO_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME);
+    let money_db_name =
+        blake3::Hash::from(MONEY_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME))
+            .to_string();
+    let dao_db_name =
+        blake3::Hash::from(DAO_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME)).to_string();
 
     // Ensure they are open in the overlay
-    overlay.open_tree(&money_db_name, false)?;
-    overlay.open_tree(&dao_db_name, false)?;
+    overlay.open_tree_default(&money_db_name, false)?;
+    overlay.open_tree_default(&dao_db_name, false)?;
 
     for (bincode, namespace, vk) in vks.iter() {
         match namespace.as_str() {

+ 4 - 0
src/error.rs

@@ -374,6 +374,10 @@ pub enum Error {
     #[error(transparent)]
     SledTransactionError(#[from] sled_overlay::sled::transaction::TransactionError),
 
+    #[cfg(feature = "kvdb-overlay")]
+    #[error(transparent)]
+    KvdbError(#[from] kvdb_overlay::Error),
+
     #[error("Transaction {0} not found in database")]
     TransactionNotFound(String),
 

+ 9 - 2
src/runtime/import/db/db_contains_key.rs

@@ -177,13 +177,20 @@ fn db_contains_key_internal(
     }
 
     // On-chain db
-    match env.blockchain.lock().unwrap().overlay.lock().unwrap().contains_key(&db_handle.tree, &key)
+    match env
+        .blockchain
+        .lock()
+        .unwrap()
+        .overlay
+        .lock()
+        .unwrap()
+        .contains_key(&db_handle.tree_str(), &key)
     {
         Ok(v) => i64::from(v), // <- 0=false, 1=true. Convert bool to i64.
         Err(e) => {
             error!(
                 target: "runtime::db::{lt}",
-                "[WASM] [{cid}] {lt}(): sled.tree.contains_key failed: {e}",
+                "[WASM] [{cid}] {lt}(): kvdb.tree.contains_key failed: {e}",
             );
             darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
         }

+ 1 - 1
src/runtime/import/db/db_del.rs

@@ -179,7 +179,7 @@ fn db_del_internal(
         .overlay
         .lock()
         .unwrap()
-        .remove(&db_handle.tree, &key)
+        .remove(&db_handle.tree_str(), &key)
         .is_err()
     {
         error!(

+ 9 - 1
src/runtime/import/db/db_get.rs

@@ -167,7 +167,15 @@ pub(crate) fn db_get_internal(
 
         tree.get(&key).cloned()
     } else {
-        match env.blockchain.lock().unwrap().overlay.lock().unwrap().get(&db_handle.tree, &key) {
+        match env
+            .blockchain
+            .lock()
+            .unwrap()
+            .overlay
+            .lock()
+            .unwrap()
+            .get(&db_handle.tree_str(), &key)
+        {
             Ok(v) => v.map(|iv| iv.to_vec()),
             Err(e) => {
                 error!(

+ 2 - 2
src/runtime/import/db/db_init.rs

@@ -56,7 +56,7 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u
     }
 
     // Subtract used gas.
-    // TODO: There should probably be an additional fee to open a new sled tree.
+    // TODO: There should probably be an additional fee to open a new kvdb tree.
     env.subtract_gas(&mut store, 1);
 
     // Get the wasm memory reader
@@ -127,7 +127,7 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u
 
     // Now try to initialize the tree. If this returns an error,
     // it usually means that this DB was already initialized.
-    // An alternative error might happen if something in sled fails,
+    // An alternative error might happen if something in kvdb fails,
     // for this we should take care to stop the node or do something to
     // be able to gracefully recover.
     // (src/blockchain/contract_store.rs holds this init() function)

+ 1 - 1
src/runtime/import/db/db_set.rs

@@ -196,7 +196,7 @@ fn db_set_internal(
         .overlay
         .lock()
         .unwrap()
-        .insert(&db_handle.tree, &key, &value)
+        .insert(&db_handle.tree_str(), &key, &value)
         .is_err()
     {
         error!(

+ 5 - 1
src/runtime/import/db/mod.rs

@@ -18,7 +18,7 @@
 
 use darkfi_sdk::crypto::contract_id::ContractId;
 
-/// Internal wasm runtime API for sled trees or tx-local dbs
+/// Internal wasm runtime API for kvdb trees or tx-local dbs
 #[derive(PartialEq)]
 pub struct DbHandle {
     pub contract_id: ContractId,
@@ -29,6 +29,10 @@ impl DbHandle {
     pub fn new(contract_id: ContractId, tree: [u8; 32]) -> Self {
         Self { contract_id, tree }
     }
+
+    pub fn tree_str(&self) -> String {
+        blake3::Hash::from(self.tree).to_string()
+    }
 }
 
 pub(crate) mod db_init;

+ 2 - 2
src/runtime/import/db/zkas_db_set.rs

@@ -124,7 +124,7 @@ pub(crate) fn zkas_db_set(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_le
         .overlay
         .lock()
         .unwrap()
-        .get(&db_handle.tree, &serialize(&zkbin.namespace))
+        .get(&db_handle.tree_str(), &serialize(&zkbin.namespace))
     {
         Ok(v) => {
             if let Some(bytes) = v {
@@ -190,7 +190,7 @@ pub(crate) fn zkas_db_set(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_le
         .overlay
         .lock()
         .unwrap()
-        .insert(&db_handle.tree, &key, &value)
+        .insert(&db_handle.tree_str(), &key, &value)
         .is_err()
     {
         error!(

+ 5 - 5
src/runtime/import/merkle.rs

@@ -233,12 +233,12 @@ pub(crate) fn merkle_add_internal(
             }
         }
     } else {
-        match overlay.get(&db_info.tree, &tree_key) {
+        match overlay.get(&db_info.tree_str(), &tree_key) {
             Ok(v) => v.map(|iv| iv.to_vec()),
             Err(e) => {
                 error!(
                     target: "runtime::merkle::{lt}",
-                    "[WASM] [{cid}] {lt}(): Error getting from sled tree: {e}",
+                    "[WASM] [{cid}] {lt}(): Error getting from kvdb tree: {e}",
                 );
                 return darkfi_sdk::error::INTERNAL_ERROR
             }
@@ -305,7 +305,7 @@ pub(crate) fn merkle_add_internal(
         let db_cid = tx_local_db.get_mut(&db_info.contract_id).unwrap();
         let tree = db_cid.get_mut(&db_info.tree).unwrap();
         tree.insert(tree_key, merkle_tree_data);
-    } else if let Err(e) = overlay.insert(&db_info.tree, &tree_key, &merkle_tree_data) {
+    } else if let Err(e) = overlay.insert(&db_info.tree_str(), &tree_key, &merkle_tree_data) {
         error!(
             target: "runtime::merkle::{lt}",
             "[WASM] [{cid}] {lt}(): Could not insert tree to db_info: {e}",
@@ -348,7 +348,7 @@ pub(crate) fn merkle_add_internal(
         let roots_tree = db_cid.entry(db_roots.tree).or_default();
         roots_tree.insert(latest_root_data, value_data);
     } else {
-        if let Err(e) = overlay.insert(&db_roots.tree, &latest_root_data, &value_data) {
+        if let Err(e) = overlay.insert(&db_roots.tree_str(), &latest_root_data, &value_data) {
             error!(
                 target: "runtime::merkle::{lt}",
                 "[WASM] [{cid}] {lt}(): Could not insert to db_roots tree: {e}",
@@ -356,7 +356,7 @@ pub(crate) fn merkle_add_internal(
             return darkfi_sdk::error::INTERNAL_ERROR
         }
 
-        if let Err(e) = overlay.insert(&db_info.tree, &root_key, &latest_root_data) {
+        if let Err(e) = overlay.insert(&db_info.tree_str(), &root_key, &latest_root_data) {
             error!(
                 target: "runtime::merkle::{lt}",
                 "[WASM] [{cid}] {lt}(): Could not insert latest root to db_info: {e}",

+ 20 - 18
src/runtime/import/smt.rs

@@ -35,20 +35,20 @@ use wasmer::{FunctionEnvMut, WasmPtr};
 use super::acl::acl_allow;
 use crate::runtime::vm_runtime::{ContractSection, Env};
 
-/// An SMT adapter for sled overlay storage. Compatible with the WasmDb SMT adapter
-pub struct SledStorage<'a> {
-    overlay: &'a mut sled_overlay::SledDbOverlay,
-    tree_key: &'a [u8],
+/// An SMT adapter for kvdb overlay storage. Compatible with the WasmDb SMT adapter
+pub struct KvdbStorage<'a> {
+    overlay: &'a mut kvdb_overlay::DatabaseOverlay,
+    tree_key: &'a str,
 }
 
-impl StorageAdapter for SledStorage<'_> {
+impl StorageAdapter for KvdbStorage<'_> {
     type Value = pallas::Base;
 
     fn put(&mut self, key: BigUint, value: pallas::Base) -> ContractResult {
         if let Err(e) = self.overlay.insert(self.tree_key, &key.to_bytes_le(), &value.to_repr()) {
             error!(
-                target: "runtime::smt::SledStorage::put",
-                "[WASM] SledStorage::put(): inserting key {key:?}, value {value:?} into DB tree: {:?}: {e}",
+                target: "runtime::smt::KvdbStorage::put",
+                "[WASM] KvdbStorage::put(): inserting key {key:?}, value {value:?} into DB tree: {:?}: {e}",
                 self.tree_key
             );
             return Err(ContractError::SmtPutFailed)
@@ -62,8 +62,8 @@ impl StorageAdapter for SledStorage<'_> {
             Ok(v) => v,
             Err(e) => {
                 error!(
-                    target: "runtime::smt::SledStorage::get",
-                    "[WASM] SledStorage::get(): Fetching key {key:?} from DB tree: {:?}: {e}",
+                    target: "runtime::smt::KvdbStorage::get",
+                    "[WASM] KvdbStorage::get(): Fetching key {key:?} from DB tree: {:?}: {e}",
                     self.tree_key
                 );
                 return None
@@ -80,8 +80,8 @@ impl StorageAdapter for SledStorage<'_> {
     fn del(&mut self, key: &BigUint) -> ContractResult {
         if let Err(e) = self.overlay.remove(self.tree_key, &key.to_bytes_le()) {
             error!(
-                target: "runtime::smt::SledStorage::del",
-                "[WASM] SledStorage::del(): Removing key {key:?} from DB tree: {:?}: {e}",
+                target: "runtime::smt::KvdbStorage::del",
+                "[WASM] KvdbStorage::del(): Removing key {key:?} from DB tree: {:?}: {e}",
                 self.tree_key
             );
             return Err(ContractError::SmtDelFailed)
@@ -204,7 +204,7 @@ pub(crate) fn sparse_merkle_insert_batch(
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // This `key` represents the sled key in info where the latest root is
+    // This `key` represents the kvdb key in info where the latest root is
     let root_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
         Ok(v) => v,
         Err(e) => {
@@ -237,17 +237,17 @@ pub(crate) fn sparse_merkle_insert_batch(
         return darkfi_sdk::error::INTERNAL_ERROR
     }
 
-    // Generate the SledStorage SMT
+    // Generate the KvdbStorage SMT
     let hasher = PoseidonFp::new();
     let lock = env.blockchain.lock().unwrap();
     let mut overlay = lock.overlay.lock().unwrap();
-    let smt_store = SledStorage { overlay: &mut overlay, tree_key: &db_smt.tree };
+    let smt_store = KvdbStorage { overlay: &mut overlay, tree_key: &db_smt.tree_str() };
     let mut smt = SparseMerkleTree::<
         SMT_FP_DEPTH,
         { SMT_FP_DEPTH + 1 },
         pallas::Base,
         PoseidonFp,
-        SledStorage,
+        KvdbStorage,
     >::new(smt_store, hasher, &EMPTY_NODES_FP);
 
     // Count the nullifiers for gas calculation
@@ -302,7 +302,7 @@ pub(crate) fn sparse_merkle_insert_batch(
     }
 
     // Retrieve snapshot root data set
-    let root_value_data_set = match overlay.get(&db_roots.tree, &latest_root_data) {
+    let root_value_data_set = match overlay.get(&db_roots.tree_str(), &latest_root_data) {
         Ok(data) => data,
         Err(e) => {
             error!(
@@ -342,7 +342,9 @@ pub(crate) fn sparse_merkle_insert_batch(
         target: "runtime::smt::sparse_merkle_insert_batch",
         "[WASM] [{cid}] sparse_merkle_insert_batch(): Appending SMT root to db: {latest_root:?}"
     );
-    if overlay.insert(&db_roots.tree, &latest_root_data, &serialize(&root_value_data_set)).is_err()
+    if overlay
+        .insert(&db_roots.tree_str(), &latest_root_data, &serialize(&root_value_data_set))
+        .is_err()
     {
         error!(
             target: "runtime::smt::sparse_merkle_insert_batch",
@@ -356,7 +358,7 @@ pub(crate) fn sparse_merkle_insert_batch(
         target: "runtime::smt::sparse_merkle_insert_batch",
         "[WASM] [{cid}] sparse_merkle_insert_batch(): Replacing latest SMT root pointer"
     );
-    if overlay.insert(&db_info.tree, &root_key, &latest_root_data).is_err() {
+    if overlay.insert(&db_info.tree_str(), &root_key, &latest_root_data).is_err() {
         error!(
             target: "runtime::smt::sparse_merkle_insert_batch",
             "[WASM] [{cid}] sparse_merkle_insert_batch(): Couldn't insert latest root to db_info tree"

+ 3 - 2
src/sdk/Cargo.toml

@@ -14,6 +14,7 @@ doctest = false
 [features]
 default = []
 async = ["darkfi-serial/async"]
+monotree = ["dep:hashbrown", "dep:kvdb-overlay"]
 wasm = []
 
 [dependencies]
@@ -43,10 +44,10 @@ rand = "0.8.6"
 # Misc
 lazy_static = "1.5.0"
 subtle = "2.6.1"
-hashbrown = "0.17.1"
+hashbrown = {version = "0.17.1", optional = true}
 
 # Storage
-sled-overlay = "0.1.20"
+kvdb-overlay = {git = "https://git.dark.fi/darkrenaissance/kvdb-overlay", version = "0.1.0", optional = true}
 
 [dev-dependencies]
 halo2_proofs = {version = "0.3.2", features = ["circuit-params", "dev-graph", "sanity-checks"]}

+ 1 - 0
src/sdk/src/lib.rs

@@ -28,6 +28,7 @@ pub mod blockchain;
 pub mod dark_tree;
 
 /// Native (non-wasm, non-ff) Sparse Merkle Tree
+#[cfg(feature = "monotree")]
 pub mod monotree;
 
 /// Contract deployment utilities

+ 1 - 1
src/sdk/src/monotree/mod.rs

@@ -41,7 +41,7 @@ pub mod bits;
 pub mod node;
 
 pub mod tree;
-pub use tree::{MemoryDb, Monotree, SledOverlayDb, SledTreeDb};
+pub use tree::{KvdbOverlayDb, KvdbTreeDb, MemoryDb, Monotree};
 
 pub mod utils;
 

+ 17 - 20
src/sdk/src/monotree/tree.rs

@@ -18,7 +18,7 @@
  */
 
 use hashbrown::{HashMap, HashSet};
-use sled_overlay::{sled::Tree, SledDbOverlay};
+use kvdb_overlay::{DatabaseOverlay, Tree};
 
 use super::{
     bits::{merge_owned_and_bits, Bits, BitsOwned},
@@ -148,25 +148,25 @@ impl MonotreeStorageAdapter for MemoryDb {
     }
 }
 
-/// sled-tree based storage for Monotree
+/// kvdb-tree based storage for Monotree
 #[derive(Clone)]
-pub struct SledTreeDb {
+pub struct KvdbTreeDb {
     tree: Tree,
     batch: MemCache,
     batch_on: bool,
 }
 
-impl SledTreeDb {
+impl KvdbTreeDb {
     pub fn new(tree: &Tree) -> Self {
         Self { tree: tree.clone(), batch: MemCache::new(), batch_on: false }
     }
 }
 
-impl MonotreeStorageAdapter for SledTreeDb {
+impl MonotreeStorageAdapter for KvdbTreeDb {
     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.tree.insert(slice_to_hash(key), value) {
+        } else if let Err(e) = self.tree.insert(key, &value) {
             return Err(ContractError::IoError(e.to_string()))
         }
 
@@ -207,12 +207,12 @@ impl MonotreeStorageAdapter for SledTreeDb {
     fn finish_batch(&mut self) -> GenericResult<()> {
         if self.batch_on {
             for (key, value) in self.batch.map.drain() {
-                if let Err(e) = self.tree.insert(key, value) {
+                if let Err(e) = self.tree.insert(&key, &value) {
                     return Err(ContractError::IoError(e.to_string()))
                 }
             }
             for key in self.batch.set.drain() {
-                if let Err(e) = self.tree.remove(key) {
+                if let Err(e) = self.tree.remove(&key) {
                     return Err(ContractError::IoError(e.to_string()))
                 }
             }
@@ -223,27 +223,24 @@ impl MonotreeStorageAdapter for SledTreeDb {
     }
 }
 
-/// sled-overlay based storage for Monotree
-pub struct SledOverlayDb<'a> {
-    overlay: &'a mut SledDbOverlay,
-    tree: [u8; 32],
+/// kvdb-overlay based storage for Monotree
+pub struct KvdbOverlayDb<'a> {
+    overlay: &'a mut DatabaseOverlay,
+    tree: String,
     batch: MemCache,
     batch_on: bool,
 }
 
-impl<'a> SledOverlayDb<'a> {
-    pub fn new(
-        overlay: &'a mut SledDbOverlay,
-        tree: &[u8; 32],
-    ) -> GenericResult<SledOverlayDb<'a>> {
-        if let Err(e) = overlay.open_tree(tree, false) {
+impl<'a> KvdbOverlayDb<'a> {
+    pub fn new(overlay: &'a mut DatabaseOverlay, tree: &str) -> GenericResult<KvdbOverlayDb<'a>> {
+        if let Err(e) = overlay.open_tree_default(tree, false) {
             return Err(ContractError::IoError(e.to_string()))
         };
-        Ok(Self { overlay, tree: *tree, batch: MemCache::new(), batch_on: false })
+        Ok(Self { overlay, tree: tree.to_string(), batch: MemCache::new(), batch_on: false })
     }
 }
 
-impl MonotreeStorageAdapter for SledOverlayDb<'_> {
+impl MonotreeStorageAdapter for KvdbOverlayDb<'_> {
     fn put(&mut self, key: &Hash, value: Vec<u8>) -> GenericResult<()> {
         if self.batch_on {
             self.batch.put(key, value);

+ 1 - 0
src/serial/Cargo.toml

@@ -17,6 +17,7 @@ async-trait = {version = "0", optional = true}
 # Supported types for encoding
 blake3 = {version = "1", optional = true}
 bridgetree = {version = "0.7", optional = true}
+kvdb-overlay = {git = "https://git.dark.fi/darkrenaissance/kvdb-overlay", version = "0.1.0", optional = true}
 pasta_curves = {version = "0.5", optional = true}
 semver = {version = "1", optional = true}
 url = {version = "2", optional = true}

+ 3 - 0
src/serial/src/types.rs

@@ -41,3 +41,6 @@ mod bigint;
 
 #[cfg(feature = "sled-overlay")]
 mod sled_overlay;
+
+#[cfg(feature = "kvdb-overlay")]
+mod kvdb_overlay;

+ 245 - 0
src/serial/src/types/kvdb_overlay.rs

@@ -0,0 +1,245 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2026 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::{
+    collections::BTreeMap,
+    io::{Read, Result, Write},
+};
+
+use kvdb_overlay::{DatabaseOverlayStateDiff, TreeOverlayStateDiff};
+
+#[cfg(feature = "async")]
+use crate::{AsyncDecodable, AsyncEncodable};
+#[cfg(feature = "async")]
+use async_trait::async_trait;
+#[cfg(feature = "async")]
+use futures_lite::{AsyncRead, AsyncWrite};
+
+use crate::{Decodable, Encodable, VarInt};
+
+impl Encodable for TreeOverlayStateDiff {
+    fn encode<S: Write>(&self, s: &mut S) -> Result<usize> {
+        let mut len = 0;
+
+        len += VarInt(self.cache.len() as u64).encode(s)?;
+        for (key, (previous, current)) in self.cache.iter() {
+            len += key.encode(s)?;
+            len += previous.encode(s)?;
+            len += current.encode(s)?;
+        }
+
+        len += VarInt(self.removed.len() as u64).encode(s)?;
+        for (key, value) in self.removed.iter() {
+            len += key.encode(s)?;
+            len += value.encode(s)?;
+        }
+
+        Ok(len)
+    }
+}
+
+#[cfg(feature = "async")]
+#[async_trait]
+impl AsyncEncodable for TreeOverlayStateDiff {
+    async fn encode_async<S: AsyncWrite + Unpin + Send>(&self, s: &mut S) -> Result<usize> {
+        let mut len = 0;
+
+        len += VarInt(self.cache.len() as u64).encode_async(s).await?;
+        for (key, (previous, current)) in self.cache.iter() {
+            len += key.encode_async(s).await?;
+            len += previous.encode_async(s).await?;
+            len += current.encode_async(s).await?;
+        }
+
+        len += VarInt(self.removed.len() as u64).encode_async(s).await?;
+        for (key, value) in self.removed.iter() {
+            len += key.encode_async(s).await?;
+            len += value.encode_async(s).await?;
+        }
+
+        Ok(len)
+    }
+}
+
+impl Decodable for TreeOverlayStateDiff {
+    fn decode<D: Read>(d: &mut D) -> Result<Self> {
+        let len = VarInt::decode(d)?.0;
+        let mut cache = BTreeMap::new();
+        for _ in 0..len {
+            let key = Decodable::decode(d)?;
+            let previous = Decodable::decode(d)?;
+            let current = Decodable::decode(d)?;
+            cache.insert(key, (previous, current));
+        }
+
+        let len = VarInt::decode(d)?.0;
+        let mut removed = BTreeMap::new();
+        for _ in 0..len {
+            let key = Decodable::decode(d)?;
+            let entry = Decodable::decode(d)?;
+            removed.insert(key, entry);
+        }
+
+        Ok(Self { cache, removed })
+    }
+}
+
+#[cfg(feature = "async")]
+#[async_trait]
+impl AsyncDecodable for TreeOverlayStateDiff {
+    async fn decode_async<D: AsyncRead + Unpin + Send>(d: &mut D) -> Result<Self> {
+        let len = VarInt::decode_async(d).await?.0;
+        let mut cache = BTreeMap::new();
+        for _ in 0..len {
+            let key = AsyncDecodable::decode_async(d).await?;
+            let previous = AsyncDecodable::decode_async(d).await?;
+            let current = AsyncDecodable::decode_async(d).await?;
+            cache.insert(key, (previous, current));
+        }
+
+        let len = VarInt::decode_async(d).await?.0;
+        let mut removed = BTreeMap::new();
+        for _ in 0..len {
+            let key = AsyncDecodable::decode_async(d).await?;
+            let entry = AsyncDecodable::decode_async(d).await?;
+            removed.insert(key, entry);
+        }
+
+        Ok(Self { cache, removed })
+    }
+}
+
+impl Encodable for DatabaseOverlayStateDiff {
+    fn encode<S: Write>(&self, s: &mut S) -> Result<usize> {
+        let mut len = 0;
+
+        len += VarInt(self.initial_tree_names.len() as u64).encode(s)?;
+        for tree_name in &self.initial_tree_names {
+            len += tree_name.encode(s)?;
+        }
+
+        len += VarInt(self.caches.len() as u64).encode(s)?;
+        for (key, (cache, drop)) in self.caches.iter() {
+            len += key.encode(s)?;
+            len += cache.encode(s)?;
+            len += drop.encode(s)?;
+        }
+
+        len += VarInt(self.dropped_trees.len() as u64).encode(s)?;
+        for (key, (cache, restore)) in self.dropped_trees.iter() {
+            len += key.encode(s)?;
+            len += cache.encode(s)?;
+            len += restore.encode(s)?;
+        }
+
+        Ok(len)
+    }
+}
+
+#[cfg(feature = "async")]
+#[async_trait]
+impl AsyncEncodable for DatabaseOverlayStateDiff {
+    async fn encode_async<S: AsyncWrite + Unpin + Send>(&self, s: &mut S) -> Result<usize> {
+        let mut len = 0;
+
+        len += VarInt(self.initial_tree_names.len() as u64).encode_async(s).await?;
+        for tree_name in &self.initial_tree_names {
+            len += tree_name.encode_async(s).await?;
+        }
+
+        len += VarInt(self.caches.len() as u64).encode_async(s).await?;
+        for (key, (cache, drop)) in self.caches.iter() {
+            len += key.encode_async(s).await?;
+            len += cache.encode_async(s).await?;
+            len += drop.encode_async(s).await?;
+        }
+
+        len += VarInt(self.dropped_trees.len() as u64).encode_async(s).await?;
+        for (key, (cache, restore)) in self.dropped_trees.iter() {
+            len += key.encode_async(s).await?;
+            len += cache.encode_async(s).await?;
+            len += restore.encode_async(s).await?;
+        }
+
+        Ok(len)
+    }
+}
+
+impl Decodable for DatabaseOverlayStateDiff {
+    fn decode<D: Read>(d: &mut D) -> Result<Self> {
+        let len = VarInt::decode(d)?.0;
+        let mut initial_tree_names = vec![];
+        for _ in 0..len {
+            let initial_tree_name = Decodable::decode(d)?;
+            initial_tree_names.push(initial_tree_name);
+        }
+
+        let len = VarInt::decode(d)?.0;
+        let mut caches = BTreeMap::new();
+        for _ in 0..len {
+            let key = Decodable::decode(d)?;
+            let cache = Decodable::decode(d)?;
+            let drop = Decodable::decode(d)?;
+            caches.insert(key, (cache, drop));
+        }
+
+        let len = VarInt::decode(d)?.0;
+        let mut dropped_trees = BTreeMap::new();
+        for _ in 0..len {
+            let key = Decodable::decode(d)?;
+            let cache = Decodable::decode(d)?;
+            let restore = Decodable::decode(d)?;
+            dropped_trees.insert(key, (cache, restore));
+        }
+
+        Ok(Self { initial_tree_names, caches, dropped_trees })
+    }
+}
+
+#[cfg(feature = "async")]
+#[async_trait]
+impl AsyncDecodable for DatabaseOverlayStateDiff {
+    async fn decode_async<D: AsyncRead + Unpin + Send>(d: &mut D) -> Result<Self> {
+        let len = VarInt::decode_async(d).await?.0;
+        let mut initial_tree_names = vec![];
+        for _ in 0..len {
+            let initial_tree_name = AsyncDecodable::decode_async(d).await?;
+            initial_tree_names.push(initial_tree_name);
+        }
+
+        let len = VarInt::decode_async(d).await?.0;
+        let mut caches = BTreeMap::new();
+        for _ in 0..len {
+            let key = AsyncDecodable::decode_async(d).await?;
+            let cache = AsyncDecodable::decode_async(d).await?;
+            let drop = AsyncDecodable::decode_async(d).await?;
+            caches.insert(key, (cache, drop));
+        }
+
+        let len = VarInt::decode_async(d).await?.0;
+        let mut dropped_trees = BTreeMap::new();
+        for _ in 0..len {
+            let key = AsyncDecodable::decode_async(d).await?;
+            let cache = AsyncDecodable::decode_async(d).await?;
+            let restore = AsyncDecodable::decode_async(d).await?;
+            dropped_trees.insert(key, (cache, restore));
+        }
+
+        Ok(Self { initial_tree_names, caches, dropped_trees })
+    }
+}

+ 17 - 12
src/validator/consensus.rs

@@ -16,12 +16,15 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::collections::{BTreeSet, HashMap};
+use std::{
+    collections::{BTreeSet, HashMap},
+    str::FromStr,
+};
 
 use darkfi_sdk::{crypto::MerkleTree, tx::TransactionHash};
-use darkfi_serial::{async_trait, deserialize, SerialDecodable, SerialEncodable};
+use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
+use kvdb_overlay::DatabaseOverlayStateDiff;
 use num_bigint::BigUint;
-use sled_overlay::{database::SledDbOverlayStateDiff, sled::IVec};
 use tracing::{debug, info, warn};
 
 use crate::{
@@ -530,7 +533,7 @@ impl Consensus {
         let mut iter = keep.iter();
         self.forks.retain(|_| *iter.next().unwrap());
 
-        // Remove confirmed proposals txs from the unporposed txs sled
+        // Remove confirmed proposals txs from the unporposed txs kvdb
         // tree.
         self.blockchain.remove_pending_txs_hashes(&confirmed_txs_hashes)?;
 
@@ -588,7 +591,7 @@ impl Consensus {
     /// from the database.
     pub async fn purge_unreferenced_trees(
         &self,
-        referenced_trees: &mut BTreeSet<IVec>,
+        referenced_trees: &mut BTreeSet<String>,
     ) -> Result<()> {
         // Check if we have forks
         if self.forks.is_empty() {
@@ -604,7 +607,7 @@ impl Consensus {
         }
 
         // Retrieve current database trees
-        let current_trees = self.blockchain.sled_db.tree_names();
+        let current_trees = self.blockchain.kvdb.tree_names()?;
 
         // Iterate over current database trees and drop unreferenced
         // contracts ones.
@@ -615,11 +618,13 @@ impl Consensus {
             }
 
             // Check if its a contract tree pointer
-            let Ok(tree) = deserialize::<[u8; 32]>(&tree) else { continue };
+            if blake3::Hash::from_str(&tree).is_err() {
+                continue
+            };
 
             // Drop it
-            debug!(target: "validator::consensus::purge_unreferenced_trees", "Dropping unreferenced tree: {}", blake3::Hash::from(tree));
-            self.blockchain.sled_db.drop_tree(tree)?;
+            debug!(target: "validator::consensus::purge_unreferenced_trees", "Dropping unreferenced tree: {tree}");
+            self.blockchain.kvdb.drop_tree(&tree)?;
         }
 
         Ok(())
@@ -665,7 +670,7 @@ pub struct Fork {
     /// Fork proposal hashes sequence
     pub proposals: Vec<HeaderHash>,
     /// Fork proposal overlay diffs sequence
-    pub diffs: Vec<SledDbOverlayStateDiff>,
+    pub diffs: Vec<DatabaseOverlayStateDiff>,
     /// Current fork mining targets rank, cached for better performance
     pub targets_rank: BigUint,
     /// Current fork hashes rank, cached for better performance
@@ -757,7 +762,7 @@ impl Fork {
         verify_fees: bool,
     ) -> Result<(Vec<Transaction>, u64, u64)> {
         // Check if our mempool is empty
-        if self.blockchain.transactions.pending.is_empty() {
+        if self.blockchain.transactions.pending.is_empty()? {
             return Ok((vec![], 0, 0))
         }
 
@@ -882,7 +887,7 @@ impl Fork {
 
     /// Auxiliary function to retrieve all referenced trees from the
     /// fork overlay and insert them to provided `BTreeSet`.
-    pub fn referenced_trees(&self, trees: &mut BTreeSet<IVec>) {
+    pub fn referenced_trees(&self, trees: &mut BTreeSet<String>) {
         // Grab its current overlay
         let fork_overlay = self.overlay.lock().unwrap();
         let overlay = fork_overlay.overlay.lock().unwrap();

+ 8 - 8
src/validator/mod.rs

@@ -19,8 +19,8 @@
 use std::{collections::HashMap, sync::Arc};
 
 use darkfi_sdk::{blockchain::compute_fee, crypto::MerkleTree};
+use kvdb_overlay::Database;
 use num_bigint::BigUint;
-use sled_overlay::sled;
 use smol::lock::RwLock;
 use tracing::{debug, error, info, warn};
 
@@ -95,11 +95,11 @@ pub struct Validator {
 }
 
 impl Validator {
-    pub async fn new(db: &sled::Db, config: &ValidatorConfig) -> Result<ValidatorPtr> {
+    pub async fn new(kvdb: &Database, config: &ValidatorConfig) -> Result<ValidatorPtr> {
         info!(target: "validator::new", "Initializing Validator");
 
         info!(target: "validator::new", "Initializing Blockchain");
-        let blockchain = Blockchain::new(db)?;
+        let blockchain = Blockchain::new(kvdb)?;
 
         // Create an overlay over whole blockchain so we can write
         // stuff.
@@ -648,7 +648,7 @@ impl Validator {
         pow_fixed_difficulty: Option<BigUint>,
     ) -> Result<()> {
         // An empty blockchain is considered valid
-        let mut blocks_count = self.blockchain.len() as u32;
+        let mut blocks_count = self.blockchain.len()? as u32;
         info!(target: "validator::validate_blockchain", "Validating {blocks_count} blocks...");
         if blocks_count == 0 {
             info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
@@ -656,8 +656,8 @@ impl Validator {
         }
 
         // Create an in memory blockchain overlay
-        let sled_db = sled::Config::new().temporary(true).open()?;
-        let blockchain = Blockchain::new(&sled_db)?;
+        let (kvdb, _folder) = Database::open_temp()?;
+        let blockchain = Blockchain::new(&kvdb)?;
         let overlay = BlockchainOverlay::new(&blockchain)?;
 
         // Set previous
@@ -781,7 +781,7 @@ impl Validator {
         self.blockchain.blocks.difficulty.clear()?;
 
         // An empty blockchain doesn't have difficulty records
-        let mut blocks_count = self.blockchain.len() as u32;
+        let mut blocks_count = self.blockchain.len()? as u32;
         info!(target: "validator::rebuild_block_difficulties", "Rebuilding {blocks_count} block difficulties...");
         if blocks_count == 0 {
             info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
@@ -840,7 +840,7 @@ impl Validator {
         }
 
         // Flush the database
-        self.blockchain.sled_db.flush()?;
+        self.blockchain.kvdb.flush_default_mode()?;
 
         info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
 

+ 5 - 5
src/validator/pow.rs

@@ -696,8 +696,8 @@ mod tests {
     };
 
     use darkfi_sdk::num_traits::Num;
+    use kvdb_overlay::Database;
     use num_bigint::BigUint;
-    use sled_overlay::sled;
 
     use crate::{
         blockchain::{header_store::Header, BlockInfo, Blockchain},
@@ -711,8 +711,8 @@ mod tests {
 
     #[test]
     fn test_wide_difficulty() -> Result<()> {
-        let sled_db = sled::Config::new().temporary(true).open()?;
-        let blockchain = Blockchain::new(&sled_db)?;
+        let (kvdb, _folder) = Database::open_temp()?;
+        let blockchain = Blockchain::new(&kvdb)?;
         let genesis_block = BlockInfo::default();
         blockchain.add_block(&genesis_block)?;
 
@@ -754,8 +754,8 @@ mod tests {
     #[test]
     fn test_miner_correctness() -> Result<()> {
         // Default setup
-        let sled_db = sled::Config::new().temporary(true).open()?;
-        let blockchain = Blockchain::new(&sled_db)?;
+        let (kvdb, _folder) = Database::open_temp()?;
+        let blockchain = Blockchain::new(&kvdb)?;
         let mut genesis_block = BlockInfo::default();
         genesis_block.header.timestamp = 0.into();
         blockchain.add_block(&genesis_block)?;

+ 6 - 6
src/validator/verification.rs

@@ -29,9 +29,9 @@ use darkfi_sdk::{
     pasta::pallas,
 };
 use darkfi_serial::{deserialize_async, serialize_async, AsyncDecodable, AsyncEncodable};
+use kvdb_overlay::DatabaseOverlayStateDiff;
 use num_bigint::BigUint;
 use parking_lot::Mutex;
-use sled_overlay::SledDbOverlayStateDiff;
 use smol::io::Cursor;
 use tracing::{debug, error, warn};
 
@@ -60,7 +60,7 @@ use crate::{
 /// needed.
 pub async fn verify_genesis_block(
     overlay: &BlockchainOverlayPtr,
-    diffs: &[SledDbOverlayStateDiff],
+    diffs: &[DatabaseOverlayStateDiff],
     block: &BlockInfo,
     block_target: u32,
 ) -> Result<()> {
@@ -231,7 +231,7 @@ pub fn validate_blockchain(
 /// needed.
 pub async fn verify_block(
     overlay: &BlockchainOverlayPtr,
-    diffs: &[SledDbOverlayStateDiff],
+    diffs: &[DatabaseOverlayStateDiff],
     module: &mut PoWModule,
     block: &BlockInfo,
     previous: &BlockInfo,
@@ -319,7 +319,7 @@ pub async fn verify_block(
 /// needed.
 pub async fn verify_checkpoint_block(
     overlay: &BlockchainOverlayPtr,
-    diffs: &[SledDbOverlayStateDiff],
+    diffs: &[DatabaseOverlayStateDiff],
     block: &BlockInfo,
     header: &HeaderHash,
     block_target: u32,
@@ -484,7 +484,7 @@ pub async fn verify_producer_transaction(
     debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"metadata\" call");
 
     // Here we'll look up verifying keys and insert them into the map.
-    debug!(target: "validator::verification::verify_producer_transaction", "Performing VerifyingKey lookups from the sled db");
+    debug!(target: "validator::verification::verify_producer_transaction", "Performing VerifyingKey lookups from the kvdb");
     for (zkas_ns, _) in &zkp_pub {
         // TODO: verify this is correct behavior
         let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();
@@ -782,7 +782,7 @@ pub async fn verify_transaction(
         // per-contract map.
         // TODO: This vk map can potentially use a lot of RAM. Perhaps
         // load keys on-demand at verification time?
-        debug!(target: "validator::verification::verify_transaction", "Performing VerifyingKey lookups from the sled db");
+        debug!(target: "validator::verification::verify_transaction", "Performing VerifyingKey lookups from the kvdb");
         for (zkas_ns, _) in &zkp_pub {
             let inner_vk_map = verifying_keys.get_mut(&call.data.contract_id.to_bytes()).unwrap();