Skip to content

Commit

Permalink
proper shared_db handling and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
borngraced committed Jul 5, 2024
1 parent 90f89b7 commit 8f19481
Show file tree
Hide file tree
Showing 22 changed files with 82 additions and 138 deletions.
4 changes: 3 additions & 1 deletion mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5715,7 +5715,9 @@ impl MmCoin for EthCoin {

async fn account_db_id(&self) -> Option<String> { eth_account_db_id(self).await }

async fn tx_history_db_id(&self) -> Option<String> { eth_shared_db_id(self).await.or(self.account_db_id().await) }
async fn shared_db_id(&self, ctx: &MmArc) -> Option<String> {
eth_shared_db_id(self, ctx).await.or(eth_account_db_id(self).await)
}
}

pub trait TryToAddress {
Expand Down
36 changes: 11 additions & 25 deletions mm2src/coins/eth/v2_activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ use crate::nft::nft_errors::{GetNftInfoError, ParseChainTypeError};
use crate::nft::nft_structs::Chain;
#[cfg(target_arch = "wasm32")] use crate::EthMetamaskPolicy;
use common::executor::AbortedError;
use crypto::shared_db_id::shared_db_id_from_seed;
use crypto::{trezor::TrezorError, Bip32Error, CryptoCtxError, HwError};
use enum_derives::EnumFromTrait;
#[cfg(not(target_arch = "wasm32"))] use futures::SinkExt;
use instant::Instant;
#[cfg(not(target_arch = "wasm32"))]
use mm2_core::sql_connection_pool::DbIds;
use mm2_err_handle::common_errors::WithInternal;
#[cfg(target_arch = "wasm32")]
use mm2_metamask::{from_metamask_error, MetamaskError, MetamaskRpcError, WithMetamaskRpcError};
Expand Down Expand Up @@ -664,16 +661,12 @@ pub(crate) async fn build_address_and_priv_key_policy(
.mm_err(|e| EthActivationV2Error::InternalError(e.to_string()))?;
let activated_key = KeyPair::from_secret_slice(raw_priv_key.as_slice())
.map_to_mm(|e| EthActivationV2Error::InternalError(e.to_string()))?;
let hd_wallet_rmd160 = global_hd_ctx.derive_rmd160();
#[cfg(not(target_arch = "wasm32"))]
{
let db_id = dhash160(activated_key.public().as_bytes());
run_db_migration_for_new_eth_pubkey(ctx, Some(db_id.to_string()), Some(hd_wallet_rmd160.to_string()))
.await?;
}

let bip39_secp_priv_key = global_hd_ctx.root_priv_key().clone();

#[cfg(not(target_arch = "wasm32"))]
run_db_migration_for_new_eth_pubkey(ctx, dhash160(activated_key.public().as_bytes()).to_string()).await?;

let hd_wallet_rmd160 = *ctx.rmd160();
let hd_wallet_storage = HDWalletCoinStorage::init_with_rmd160(ctx, ticker.to_string(), hd_wallet_rmd160)
.await
.mm_err(EthActivationV2Error::from)?;
Expand Down Expand Up @@ -930,13 +923,8 @@ fn compress_public_key(uncompressed: H520) -> MmResult<H264, EthActivationV2Erro
}

#[cfg(not(target_arch = "wasm32"))]
async fn run_db_migration_for_new_eth_pubkey(
ctx: &MmArc,
db_id: Option<String>,
shared_db_id: Option<String>,
) -> MmResult<(), EthActivationV2Error> {
async fn run_db_migration_for_new_eth_pubkey(ctx: &MmArc, db_id: String) -> MmResult<(), EthActivationV2Error> {
info!("Public key hash: {db_id:?}");
info!("Shared Database ID: {shared_db_id:?}");

let db_migration_sender = ctx
.db_migration_watcher
Expand All @@ -945,20 +933,18 @@ async fn run_db_migration_for_new_eth_pubkey(
.get_sender();
let mut db_migration_sender = db_migration_sender.lock().await;
db_migration_sender
.send(DbIds { db_id, shared_db_id })
.send(db_id)
.await
.map_to_mm(|err| EthActivationV2Error::InternalError(err.to_string()))?;

Ok(())
}

pub(super) async fn eth_shared_db_id(coin: &EthCoin) -> Option<String> {
// Use the hd_wallet_rmd160 as the db_id since it's unique to a device and not tied to a single address
coin.derivation_method().hd_wallet().and_then(|hd| {
shared_db_id_from_seed(&hex::encode(hd.hd_wallet_rmd160.as_slice()))
.ok()
.map(|id| hex::encode(id.as_slice()))
})
pub(super) async fn eth_shared_db_id(coin: &EthCoin, ctx: &MmArc) -> Option<String> {
// Use the hd_wallet_rmd160 as the db_id in HD mode since it's unique to a device and not tied to a single address
coin.derivation_method()
.hd_wallet()
.map(|_| ctx.default_shared_db_id().to_string())
}

pub(super) async fn eth_account_db_id(coin: &EthCoin) -> Option<String> {
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/hd_wallet/storage/mock_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub(crate) struct HDWalletMockStorage;
#[async_trait]
#[cfg_attr(test, mockable)]
impl HDWalletStorageInternalOps for HDWalletMockStorage {
async fn init(_ctx: &MmArc, _db_id: Option<&str>) -> HDWalletStorageResult<Self>
async fn init(_ctx: &MmArc) -> HDWalletStorageResult<Self>
where
Self: Sized,
{
Expand Down
6 changes: 3 additions & 3 deletions mm2src/coins/hd_wallet/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub struct HDAccountStorageItem {
#[async_trait]
#[cfg_attr(test, mockable)]
pub(crate) trait HDWalletStorageInternalOps {
async fn init(ctx: &MmArc, db_id: Option<&str>) -> HDWalletStorageResult<Self>
async fn init(ctx: &MmArc) -> HDWalletStorageResult<Self>
where
Self: Sized;

Expand Down Expand Up @@ -225,7 +225,7 @@ impl HDWalletCoinStorage {
let hd_wallet_rmd160 = crypto_ctx
.hw_wallet_rmd160()
.or_mm_err(|| HDWalletStorageError::HDWalletUnavailable)?;
let inner = Box::new(HDWalletStorageInstance::init(ctx, None).await?);
let inner = Box::new(HDWalletStorageInstance::init(ctx).await?);
Ok(HDWalletCoinStorage {
coin,
hd_wallet_rmd160,
Expand All @@ -238,7 +238,7 @@ impl HDWalletCoinStorage {
coin: String,
hd_wallet_rmd160: H160,
) -> HDWalletStorageResult<HDWalletCoinStorage> {
let inner = Box::new(HDWalletStorageInstance::init(ctx, None).await?);
let inner = Box::new(HDWalletStorageInstance::init(ctx).await?);
Ok(HDWalletCoinStorage {
coin,
hd_wallet_rmd160,
Expand Down
6 changes: 3 additions & 3 deletions mm2src/coins/hd_wallet/storage/sqlite_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ pub(super) struct HDWalletSqliteStorage {

#[async_trait]
impl HDWalletStorageInternalOps for HDWalletSqliteStorage {
async fn init(ctx: &MmArc, db_id: Option<&str>) -> HDWalletStorageResult<Self>
async fn init(ctx: &MmArc) -> HDWalletStorageResult<Self>
where
Self: Sized,
{
let shared = ctx.shared_sqlite_conn_opt(db_id).or_mm_err(|| {
let shared = ctx.shared_sqlite_conn_opt().or_mm_err(|| {
HDWalletStorageError::Internal("'MmCtx::shared_sqlite_conn' is not initialized".to_owned())
})?;
let storage = HDWalletSqliteStorage {
Expand Down Expand Up @@ -279,7 +279,7 @@ pub(crate) async fn get_all_storage_items(ctx: &MmArc) -> Vec<HDAccountStorageIt
const SELECT_ALL_ACCOUNTS: &str =
"SELECT account_id, account_xpub, external_addresses_number, internal_addresses_number FROM hd_account";

let conn = ctx.shared_sqlite_conn_opt(None).unwrap();
let conn = ctx.shared_sqlite_conn_opt().unwrap();
let conn = conn.lock().unwrap();
let mut statement = conn.prepare(SELECT_ALL_ACCOUNTS).unwrap();
statement
Expand Down
24 changes: 10 additions & 14 deletions mm2src/coins/hd_wallet/storage/wasm_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,26 +156,22 @@ impl DbInstance for HDWalletDb {
/// The wrapper over the [`CoinsContext::hd_wallet_db`] weak pointer.
pub(super) struct HDWalletIndexedDbStorage {
db: WeakDb<HDWalletDb>,
db_id: Option<String>,
}

#[async_trait]
impl HDWalletStorageInternalOps for HDWalletIndexedDbStorage {
async fn init(ctx: &MmArc, db_id: Option<&str>) -> HDWalletStorageResult<Self>
async fn init(ctx: &MmArc) -> HDWalletStorageResult<Self>
where
Self: Sized,
{
let coins_ctx = CoinsContext::from_ctx(ctx).map_to_mm(HDWalletStorageError::Internal)?;
let db = SharedDb::downgrade(&coins_ctx.hd_wallet_db);
Ok(HDWalletIndexedDbStorage {
db,
db_id: db_id.map(String::from),
})
Ok(HDWalletIndexedDbStorage { db })
}

async fn load_accounts(&self, wallet_id: HDWalletId) -> HDWalletStorageResult<Vec<HDAccountStorageItem>> {
let shared_db = self.get_shared_db()?;
let locked_db = Self::lock_db_mutex(&shared_db, self.db_id.as_deref()).await?;
let locked_db = Self::lock_db_mutex(&shared_db).await?;

let transaction = locked_db.inner.transaction().await?;
let table = transaction.table::<HDAccountTable>().await?;
Expand All @@ -197,7 +193,7 @@ impl HDWalletStorageInternalOps for HDWalletIndexedDbStorage {
account_id: u32,
) -> HDWalletStorageResult<Option<HDAccountStorageItem>> {
let shared_db = self.get_shared_db()?;
let locked_db = Self::lock_db_mutex(&shared_db, self.db_id.as_deref()).await?;
let locked_db = Self::lock_db_mutex(&shared_db).await?;

let transaction = locked_db.inner.transaction().await?;
let table = transaction.table::<HDAccountTable>().await?;
Expand Down Expand Up @@ -239,7 +235,7 @@ impl HDWalletStorageInternalOps for HDWalletIndexedDbStorage {
account: HDAccountStorageItem,
) -> HDWalletStorageResult<()> {
let shared_db = self.get_shared_db()?;
let locked_db = Self::lock_db_mutex(&shared_db, self.db_id.as_deref()).await?;
let locked_db = Self::lock_db_mutex(&shared_db).await?;

let transaction = locked_db.inner.transaction().await?;
let table = transaction.table::<HDAccountTable>().await?;
Expand All @@ -254,7 +250,7 @@ impl HDWalletStorageInternalOps for HDWalletIndexedDbStorage {

async fn clear_accounts(&self, wallet_id: HDWalletId) -> HDWalletStorageResult<()> {
let shared_db = self.get_shared_db()?;
let locked_db = Self::lock_db_mutex(&shared_db, self.db_id.as_deref()).await?;
let locked_db = Self::lock_db_mutex(&shared_db).await?;

let transaction = locked_db.inner.transaction().await?;
let table = transaction.table::<HDAccountTable>().await?;
Expand All @@ -274,8 +270,8 @@ impl HDWalletIndexedDbStorage {
.or_mm_err(|| HDWalletStorageError::Internal("'HDWalletIndexedDbStorage::db' doesn't exist".to_owned()))
}

async fn lock_db_mutex(db: &SharedDb<HDWalletDb>, db_id: Option<&str>) -> HDWalletStorageResult<HDWalletDbLocked> {
db.get_or_initialize_shared(db_id)
async fn lock_db_mutex(db: &SharedDb<HDWalletDb>) -> HDWalletStorageResult<HDWalletDbLocked> {
db.get_or_initialize_shared(None)
.await
.mm_err(HDWalletStorageError::from)
}
Expand All @@ -300,7 +296,7 @@ impl HDWalletIndexedDbStorage {
F: FnOnce(&mut HDAccountTable),
{
let shared_db = self.get_shared_db()?;
let locked_db = Self::lock_db_mutex(&shared_db, self.db_id.as_deref()).await?;
let locked_db = Self::lock_db_mutex(&shared_db).await?;

let transaction = locked_db.inner.transaction().await?;
let table = transaction.table::<HDAccountTable>().await?;
Expand All @@ -323,7 +319,7 @@ impl HDWalletIndexedDbStorage {
#[cfg(any(test, target_arch = "wasm32"))]
pub(super) async fn get_all_storage_items(ctx: &MmArc) -> Vec<HDAccountStorageItem> {
let coins_ctx = CoinsContext::from_ctx(ctx).unwrap();
let db = coins_ctx.hd_wallet_db.get_or_initialize(None).await.unwrap();
let db = coins_ctx.hd_wallet_db.get_or_initialize_shared(None).await.unwrap();
let transaction = db.inner.transaction().await.unwrap();
let table = transaction.table::<HDAccountTable>().await.unwrap();
table
Expand Down
11 changes: 3 additions & 8 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ use hd_wallet::{AccountUpdatingError, AddressDerivingError, HDAccountOps, HDAddr
HDCoinHDAccount, HDExtractPubkeyError, HDPathAccountToAddressId, HDWalletAddress, HDWalletCoinOps,
HDWalletOps, HDWithdrawError, HDXPubExtractor, WithdrawFrom, WithdrawSenderAddress};
use nft::nft_errors::GetNftInfoError;
use primitives::hash::H160;
use qrc20::{qrc20_coin_with_policy, Qrc20ActivationParams, Qrc20Coin, Qrc20FeeDetails};
use rpc_command::{get_new_address::{GetNewAddressTaskManager, GetNewAddressTaskManagerShared},
init_account_balance::{AccountBalanceTaskManager, AccountBalanceTaskManagerShared},
Expand Down Expand Up @@ -3237,13 +3236,9 @@ pub trait MmCoin:
/// If the coin is not derived from an HD wallet, it returns `None`.
async fn account_db_id(&self) -> Option<String> { None }

// Retrieves a unique identifier for the account that is shared across different contexts,
/// such as different derivation methods (HD wallet vs. non-HD wallet)
async fn shared_db_id(&self) -> Option<H160> { None }

/// In normal wallet mode, this function returns the regular `db_id`, which is the RMD160 hash of the public key.
/// In HD wallet mode, it returns `hd_wallet_rmd160`, which is the RMD160 hash unique to the HD wallet/device.
async fn tx_history_db_id(&self) -> Option<String> { None }
// Retrieves db_id for derivation methods (HD wallet vs. non-HD wallet)
// NOTE: this function only needs special handling for coins that supports HD wallet
async fn shared_db_id(&self, _ctx: &MmArc) -> Option<String> { None }

/// Path to tx history file
#[cfg(not(target_arch = "wasm32"))]
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/my_tx_history_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ pub(crate) async fn my_tx_history_v2_impl<Coin>(
where
Coin: CoinWithTxHistoryV2 + MmCoin,
{
let tx_history_storage = TxHistoryStorageBuilder::new(&ctx, coin.tx_history_db_id().await).build()?;
let tx_history_storage = TxHistoryStorageBuilder::new(&ctx, coin.shared_db_id(&ctx).await).build()?;

let wallet_id = coin.history_wallet_id();
let is_storage_init = tx_history_storage.is_initialized_for(&wallet_id).await?;
Expand Down
13 changes: 7 additions & 6 deletions mm2src/coins/tendermint/tendermint_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2424,12 +2424,13 @@ impl MmCoin for TendermintCoin {
None
}

async fn tx_history_db_id(&self) -> Option<String> {
self.activation_policy
.public_key()
.ok()
.map(|k| hex::encode(dhash160(&k.to_bytes())))
.or(self.account_db_id().await) // Fallback to the account db_id for non-HD wallets
async fn shared_db_id(&self, ctx: &MmArc) -> Option<String> {
if let TendermintActivationPolicy::PrivateKey(PrivKeyPolicy::HDWallet { .. }) = self.activation_policy {
return Some(ctx.default_shared_db_id().to_string());
};

// Fallback to the account db_id for non-HD wallets
self.account_db_id().await
}
}

Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/utxo/bch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@ impl MmCoin for BchCoin {

async fn account_db_id(&self) -> Option<String> { utxo_common::account_db_id(self).await }

async fn tx_history_db_id(&self) -> Option<String> { utxo_common::tx_history_db_id(self).await }
async fn shared_db_id(&self, _ctx: &MmArc) -> Option<String> { utxo_common::shared_db_id(self).await }
}

#[async_trait]
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/utxo/qtum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ impl MmCoin for QtumCoin {

async fn account_db_id(&self) -> Option<String> { utxo_common::account_db_id(self).await }

async fn tx_history_db_id(&self) -> Option<String> { utxo_common::tx_history_db_id(self).await }
async fn shared_db_id(&self, _ctx: &MmArc) -> Option<String> { utxo_common::shared_db_id(self).await }
}

#[async_trait]
Expand Down
30 changes: 10 additions & 20 deletions mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,15 @@ pub trait UtxoFieldsWithGlobalHDBuilder: UtxoCoinBuilderCommonOps {
bip39_secp_priv_key: global_hd_ctx.root_priv_key().clone(),
};

#[cfg(not(target_arch = "wasm32"))]
{
// db_id should be the current activated key for this hd wallet
run_db_migration_for_new_utxo_pubkey(self.ctx(), activated_key_pair.public().address_hash().to_string())
.await?
}

let address_format = self.address_format()?;
let hd_wallet_rmd160 = global_hd_ctx.derive_rmd160();
let hd_wallet_rmd160 = *self.ctx().rmd160();
let hd_wallet_storage =
HDWalletCoinStorage::init_with_rmd160(self.ctx(), self.ticker().to_owned(), hd_wallet_rmd160).await?;
let accounts = load_hd_accounts_from_storage(&hd_wallet_storage, path_to_coin)
Expand All @@ -238,14 +245,6 @@ pub trait UtxoFieldsWithGlobalHDBuilder: UtxoCoinBuilderCommonOps {
},
address_format,
};
#[cfg(not(target_arch = "wasm32"))]
{
// db_id should be the current activated key for this hd wallet
let db_id = Some(activated_key_pair.public().address_hash());
// device_rmd_160 is unqiue to a device, hence it can bs used as shared_db_id.
let shared_db_id = Some(hd_wallet_rmd160);
run_db_migration_for_new_utxo_pubkey(self.ctx(), db_id, shared_db_id).await?
}
let derivation_method = DerivationMethod::HDWallet(hd_wallet);
build_utxo_coin_fields_with_conf_and_policy(self, conf, priv_key_policy, derivation_method).await
}
Expand Down Expand Up @@ -1022,17 +1021,8 @@ async fn wait_for_protocol_version_checked(client: &ElectrumClientImpl) -> Resul
}

#[cfg(not(target_arch = "wasm32"))]
pub async fn run_db_migration_for_new_utxo_pubkey(
ctx: &MmArc,
db_id: Option<H160>,
shared_db_id: Option<H160>,
) -> MmResult<(), UtxoCoinBuildError> {
use mm2_core::sql_connection_pool::DbIds;

let db_id = db_id.map(|id| id.to_string());
let shared_db_id = shared_db_id.map(|id| id.to_string());
pub async fn run_db_migration_for_new_utxo_pubkey(ctx: &MmArc, db_id: String) -> MmResult<(), UtxoCoinBuildError> {
info!("Public key hash: {db_id:?}");
info!("Shared Database ID: {shared_db_id:?}");

let db_migration_sender = ctx
.db_migration_watcher
Expand All @@ -1041,7 +1031,7 @@ pub async fn run_db_migration_for_new_utxo_pubkey(
.get_sender();
let mut db_migration_sender = db_migration_sender.lock().await;
db_migration_sender
.send(DbIds { db_id, shared_db_id })
.send(db_id)
.await
.map_to_mm(|err| UtxoCoinBuildError::Internal(err.to_string()))?;

Expand Down
11 changes: 4 additions & 7 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5147,29 +5147,26 @@ where
{
if let Some(hd_wallet) = coin.derivation_method().hd_wallet() {
// we can use hd_wallet_rmd160 as our shared_db_id since it's unique to a device
let db_id = hd_wallet
return hd_wallet
.get_enabled_address()
.await
.map(|addr| hex::encode(addr.pubkey().address_hash().as_slice()));
println!("enable_address: {:?}", db_id);

return db_id;
}

None
}

/// In normal wallet mode, this function returns the regular `db_id`, which is the RMD160 hash of the public key.
/// In HD wallet mode, it returns `hd_wallet_rmd160`, which is the RMD160 hash unique to the HD wallet/device.
pub async fn tx_history_db_id<Coin>(coin: &Coin) -> Option<String>
pub async fn shared_db_id<Coin>(coin: &Coin) -> Option<String>
where
Coin: CoinWithDerivationMethod + HDWalletCoinOps<HDWallet = UtxoHDWallet> + HDCoinWithdrawOps + UtxoCommonOps,
{
// Use the hd_wallet_rmd160 as the db_id since it's unique to a device and not tied to a single address
// Fallback to the account db_id for non-HD wallets.
coin.derivation_method()
.hd_wallet()
.map(|hd| hex::encode(hd.inner.hd_wallet_rmd160.as_slice()))
.or(account_db_id(coin).await) // Fallback to the account db_id for non-HD wallets
.map(|hd| hd.inner.hd_wallet_rmd160.to_string())
}

#[test]
Expand Down
Loading

0 comments on commit 8f19481

Please sign in to comment.