Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: rocksdb as optional storage #1728

Merged
merged 15 commits into from
Jan 25, 2023
Merged
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ fern-logger = { version = "0.5.0", default-features = false }

[features]
# only default for now, should later be replaced by stronghold since stronghold is more secure
default = [ "mnemonic", "storage", "stronghold" ]
default = [ "mnemonic", "storage", "rocksdb", "stronghold" ]
ledger_nano = [ "iota-client/ledger_nano" ]
stronghold = [ "iota-client/stronghold" ]
storage = [ "rocksdb" ]
storage = []
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
message_interface = []
events = []
mnemonic = []
Expand Down
20 changes: 12 additions & 8 deletions src/account_manager/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use tokio::sync::RwLock;

#[cfg(feature = "storage")]
use crate::account::handle::AccountHandle;
#[cfg(feature = "events")]
use crate::events::EventEmitter;
#[cfg(all(feature = "storage", not(feature = "rocksdb")))]
use crate::storage::adapter::memory::Memory;
#[cfg(feature = "storage")]
use crate::storage::constants::ROCKSDB_FOLDERNAME;
#[cfg(feature = "storage")]
use crate::storage::manager::ManagerStorage;
use crate::{
account::handle::AccountHandle,
storage::{constants::default_storage_path, manager::ManagerStorage},
};
use crate::{account_manager::AccountManager, ClientOptions};

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
Expand All @@ -48,10 +49,10 @@ pub struct StorageOptions {
impl Default for StorageOptions {
fn default() -> Self {
StorageOptions {
storage_path: ROCKSDB_FOLDERNAME.into(),
storage_path: default_storage_path().into(),
storage_file_name: None,
storage_encryption_key: None,
manager_store: ManagerStorage::Rocksdb,
manager_store: ManagerStorage::default(),
}
}
}
Expand Down Expand Up @@ -121,9 +122,12 @@ impl AccountManagerBuilder {
return Err(crate::Error::MissingParameter("secret_manager"));
}
}
#[cfg(feature = "storage")]
#[cfg(all(feature = "rocksdb", feature = "storage"))]
let storage =
crate::storage::adapter::rocksdb::RocksdbStorageAdapter::new(storage_options.storage_path.clone())?;
#[cfg(all(not(feature = "rocksdb"), feature = "storage"))]
let storage = Memory::default();

#[cfg(feature = "storage")]
let storage_manager = crate::storage::manager::new_storage_manager(
None,
Expand Down
42 changes: 42 additions & 0 deletions src/storage/adapter/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashMap;

use super::{storage_err, StorageAdapter};

/// The storage id.
pub const STORAGE_ID: &str = "Memory";

/// A storage adapter that stores data in memory.
#[derive(Debug, Default)]
pub struct Memory(HashMap<String, String>);

#[async_trait::async_trait]
impl StorageAdapter for Memory {
fn id(&self) -> &'static str {
STORAGE_ID
}

async fn get(&self, key: &str) -> crate::Result<String> {
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
self.0.get(key).ok_or_else(|| storage_err(key)).cloned()
}

/// Saves or updates a record on the storage.
async fn set(&mut self, key: &str, record: String) -> crate::Result<()> {
self.0.insert(key.to_string(), record);
Ok(())
}

/// Batch write.
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
async fn batch_set(&mut self, records: HashMap<String, String>) -> crate::Result<()> {
self.0.extend(records.into_iter());
Ok(())
}

/// Removes a record from the storage.
async fn remove(&mut self, key: &str) -> crate::Result<()> {
self.0.remove(key);
Ok(())
}
}
6 changes: 6 additions & 0 deletions src/storage/adapter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// Copyright 2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

pub mod memory;
/// RocksDB storage adapter.
#[cfg(feature = "rocksdb")]
pub mod rocksdb;

thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
use std::collections::HashMap;

fn storage_err<E: ToString>(error: E) -> crate::Error {
crate::Error::Storage(error.to_string())
}

/// The storage adapter.
#[async_trait::async_trait]
pub trait StorageAdapter: std::fmt::Debug {
Expand Down
6 changes: 1 addition & 5 deletions src/storage/adapter/rocksdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{collections::HashMap, path::Path, sync::Arc};
use rocksdb::{DBCompressionType, Options, WriteBatch, DB};
use tokio::sync::Mutex;

use super::StorageAdapter;
use super::{storage_err, StorageAdapter};

/// The storage id.
pub const STORAGE_ID: &str = "RocksDB";
Expand All @@ -17,10 +17,6 @@ pub struct RocksdbStorageAdapter {
db: Arc<Mutex<DB>>,
}

fn storage_err<E: ToString>(error: E) -> crate::Error {
crate::Error::Storage(error.to_string())
}

impl RocksdbStorageAdapter {
/// Initialises the storage adapter.
pub fn new(path: impl AsRef<Path>) -> crate::Result<Self> {
Expand Down
8 changes: 8 additions & 0 deletions src/storage/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@
pub const DEFAULT_STORAGE_PATH: &str = "./storage";

/// The default RocksDB storage path.
#[cfg(feature = "rocksdb")]
pub(crate) const ROCKSDB_FOLDERNAME: &str = "walletdb";

pub const fn default_storage_path() -> &'static str {
#[cfg(feature = "rocksdb")]
return ROCKSDB_FOLDERNAME;
#[cfg(not(feature = "rocksdb"))]
DEFAULT_STORAGE_PATH
}

pub(crate) const ACCOUNT_MANAGER_INDEXATION_KEY: &str = "iota-wallet-account-manager";

pub(crate) const SECRET_MANAGER_KEY: &str = "secret_manager";
Expand Down
16 changes: 14 additions & 2 deletions src/storage/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,23 @@ use crate::{
/// The storage used by the manager.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) enum ManagerStorage {
#[cfg(feature = "stronghold")]
/// Stronghold storage.
#[cfg(feature = "stronghold")]
Stronghold,
/// RocksDB storage.
#[cfg(feature = "rocksdb")]
Rocksdb,
/// Storage backed by a Map in memory.
Memory,
}

impl Default for ManagerStorage {
fn default() -> ManagerStorage {
#[cfg(feature = "rocksdb")]
return ManagerStorage::Rocksdb;
#[cfg(not(feature = "rocksdb"))]
ManagerStorage::Memory
}
}

pub(crate) type StorageManagerHandle = Arc<Mutex<StorageManager>>;
Expand Down Expand Up @@ -63,8 +75,8 @@ pub(crate) async fn new_storage_manager(
Ok(Arc::new(Mutex::new(storage_manager)))
}

#[derive(Debug)]
/// Storage manager
#[derive(Debug)]
pub struct StorageManager {
pub(crate) storage: Storage,
// account indexes for accounts in the database
Expand Down