diff --git a/Cargo.lock b/Cargo.lock index f24abff6fc..13ad126fd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -970,6 +970,7 @@ dependencies = [ "parking_lot", "rpc", "rpc_task", + "secp256k1 0.24.3", "ser_error", "ser_error_derive", "serde", @@ -3883,6 +3884,7 @@ dependencies = [ "libp2p", "mm2_err_handle", "mm2_event_stream", + "mm2_io", "mm2_metrics", "mm2_rpc", "primitives", diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index 26c3b39c97..64cc91c03d 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -13,7 +13,7 @@ enable-sia = [ default = [] run-docker-tests = [] for-tests = ["dep:mocktopus"] -new-db-arch = [] +new-db-arch = ["mm2_core/new-db-arch"] [lib] path = "lp_coins.rs" diff --git a/mm2src/coins/coin_errors.rs b/mm2src/coins/coin_errors.rs index 3e9bbc7349..486a07fc83 100644 --- a/mm2src/coins/coin_errors.rs +++ b/mm2src/coins/coin_errors.rs @@ -108,3 +108,8 @@ pub enum MyAddressError { UnexpectedDerivationMethod(String), InternalError(String), } + +#[derive(Debug, Display)] +pub enum AddressFromPubkeyError { + InternalError(String), +} diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 6fe52c0fa5..0bb6d7b1b2 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -102,7 +102,7 @@ use web3::{self, Web3}; cfg_wasm32! { use common::{now_ms, wait_until_ms}; use crypto::MetamaskArc; - use ethereum_types::{H264, H520}; + use ethereum_types::{H264 as EthH264, H520 as EthH520}; use mm2_metamask::MetamaskError; use web3::types::TransactionRequest; } @@ -940,7 +940,7 @@ macro_rules! tx_type_from_pay_for_gas_option { impl EthCoinImpl { #[cfg(not(target_arch = "wasm32"))] fn eth_traces_path(&self, ctx: &MmArc, my_address: Address) -> PathBuf { - ctx.dbdir() + ctx.address_dir(&my_address.display_address()) .join("TRANSACTIONS") .join(format!("{}_{:#02x}_trace.json", self.ticker, my_address)) } @@ -948,7 +948,8 @@ impl EthCoinImpl { /// Load saved ETH traces from local DB #[cfg(not(target_arch = "wasm32"))] fn load_saved_traces(&self, ctx: &MmArc, my_address: Address) -> Option { - let content = gstuff::slurp(&self.eth_traces_path(ctx, my_address)); + let path = self.eth_traces_path(ctx, my_address); + let content = gstuff::slurp(&path); if content.is_empty() { None } else { @@ -970,9 +971,8 @@ impl EthCoinImpl { #[cfg(not(target_arch = "wasm32"))] fn store_eth_traces(&self, ctx: &MmArc, my_address: Address, traces: &SavedTraces) { let content = json::to_vec(traces).unwrap(); - let tmp_file = format!("{}.tmp", self.eth_traces_path(ctx, my_address).display()); - std::fs::write(&tmp_file, content).unwrap(); - std::fs::rename(tmp_file, self.eth_traces_path(ctx, my_address)).unwrap(); + let path = self.eth_traces_path(ctx, my_address); + mm2_io::fs::write(&path, &content, true).unwrap(); } /// Store ETH traces to local DB @@ -984,7 +984,7 @@ impl EthCoinImpl { #[cfg(not(target_arch = "wasm32"))] fn erc20_events_path(&self, ctx: &MmArc, my_address: Address) -> PathBuf { - ctx.dbdir() + ctx.address_dir(&my_address.display_address()) .join("TRANSACTIONS") .join(format!("{}_{:#02x}_events.json", self.ticker, my_address)) } @@ -993,9 +993,8 @@ impl EthCoinImpl { #[cfg(not(target_arch = "wasm32"))] fn store_erc20_events(&self, ctx: &MmArc, my_address: Address, events: &SavedErc20Events) { let content = json::to_vec(events).unwrap(); - let tmp_file = format!("{}.tmp", self.erc20_events_path(ctx, my_address).display()); - std::fs::write(&tmp_file, content).unwrap(); - std::fs::rename(tmp_file, self.erc20_events_path(ctx, my_address)).unwrap(); + let path = self.erc20_events_path(ctx, my_address); + mm2_io::fs::write(&path, &content, true).unwrap(); } /// Store ERC20 events to local DB @@ -1008,7 +1007,8 @@ impl EthCoinImpl { /// Load saved ERC20 events from local DB #[cfg(not(target_arch = "wasm32"))] fn load_saved_erc20_events(&self, ctx: &MmArc, my_address: Address) -> Option { - let content = gstuff::slurp(&self.erc20_events_path(ctx, my_address)); + let path = self.erc20_events_path(ctx, my_address); + let content = gstuff::slurp(&path); if content.is_empty() { None } else { @@ -2329,6 +2329,11 @@ impl MarketCoinOps for EthCoin { } } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let addr = addr_from_raw_pubkey(&pubkey.0).map_err(AddressFromPubkeyError::InternalError)?; + Ok(addr.display_address()) + } + async fn get_public_key(&self) -> Result> { match self.priv_key_policy { EthPrivKeyPolicy::Iguana(ref key_pair) diff --git a/mm2src/coins/eth/v2_activation.rs b/mm2src/coins/eth/v2_activation.rs index ecdb4a5c37..bc00a35cba 100644 --- a/mm2src/coins/eth/v2_activation.rs +++ b/mm2src/coins/eth/v2_activation.rs @@ -984,9 +984,9 @@ async fn check_metamask_supports_chain_id( } #[cfg(target_arch = "wasm32")] -fn compress_public_key(uncompressed: H520) -> MmResult { +fn compress_public_key(uncompressed: EthH520) -> MmResult { let public_key = PublicKey::from_slice(uncompressed.as_bytes()) .map_to_mm(|e| EthActivationV2Error::InternalError(e.to_string()))?; let compressed = public_key.serialize(); - Ok(H264::from(compressed)) + Ok(EthH264::from(compressed)) } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 949c39a857..64aa153aaa 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -10,7 +10,7 @@ mod ln_sql; pub mod ln_storage; pub mod ln_utils; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentResult}; use crate::lightning::ln_utils::{filter_channels, pay_invoice_with_max_total_cltv_expiry_delta, PaymentError}; use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsigned}; @@ -65,7 +65,7 @@ use mm2_err_handle::prelude::*; use mm2_net::ip_addr::myipaddr; use mm2_number::{BigDecimal, MmNumber}; use parking_lot::Mutex as PaMutex; -use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; use script::TransactionInputSigner; use secp256k1v24::PublicKey; use serde::Deserialize; @@ -942,6 +942,12 @@ impl MarketCoinOps for LightningCoin { fn my_address(&self) -> MmResult { Ok(self.my_node_id()) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + PublicKey::from_slice(&pubkey.0) + .map(|pubkey| pubkey.to_string()) + .map_to_mm(|e| AddressFromPubkeyError::InternalError(format!("Couldn't parse bytes into secp pubkey: {e}"))) + } + async fn get_public_key(&self) -> Result> { Ok(self.my_node_id()) } fn sign_message_hash(&self, message: &str) -> Option<[u8; 32]> { diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index 79868908fa..68f3c7f7ab 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -22,7 +22,7 @@ use mm2_core::mm_ctx::MmArc; use std::collections::hash_map::Entry; use std::fs::File; use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; pub const PAYMENT_RETRY_ATTEMPTS: usize = 5; @@ -54,13 +54,15 @@ impl From for RpcBestBlock { } #[inline] -fn ln_data_dir(ctx: &MmArc, ticker: &str) -> PathBuf { ctx.dbdir().join("LIGHTNING").join(ticker) } +fn ln_data_dir(ctx: &MmArc, platform_coin_address: &str, ticker: &str) -> PathBuf { + ctx.address_dir(platform_coin_address).join("LIGHTNING").join(ticker) +} #[inline] -fn ln_data_backup_dir(ctx: &MmArc, path: Option, ticker: &str) -> Option { +fn ln_data_backup_dir(path: Option, platform_coin_address: &str, ticker: &str) -> Option { path.map(|p| { PathBuf::from(&p) - .join(hex::encode(ctx.rmd160().as_slice())) + .join(platform_coin_address) .join("LIGHTNING") .join(ticker) }) @@ -68,11 +70,12 @@ fn ln_data_backup_dir(ctx: &MmArc, path: Option, ticker: &str) -> Option pub async fn init_persister( ctx: &MmArc, + platform_coin_address: &str, ticker: String, backup_path: Option, ) -> EnableLightningResult> { - let ln_data_dir = ln_data_dir(ctx, &ticker); - let ln_data_backup_dir = ln_data_backup_dir(ctx, backup_path, &ticker); + let ln_data_dir = ln_data_dir(ctx, platform_coin_address, &ticker); + let ln_data_backup_dir = ln_data_backup_dir(backup_path, platform_coin_address, &ticker); let persister = Arc::new(LightningFilesystemPersister::new(ln_data_dir, ln_data_backup_dir)); let is_initialized = persister.is_fs_initialized().await?; @@ -83,16 +86,15 @@ pub async fn init_persister( Ok(persister) } -pub async fn init_db(ctx: &MmArc, ticker: String) -> EnableLightningResult { - let db = SqliteLightningDB::new( - ticker, - ctx.sqlite_connection - .get() - .ok_or(MmError::new(EnableLightningError::DbError( - "sqlite_connection is not initialized".into(), - )))? - .clone(), - )?; +pub async fn init_db( + ctx: &MmArc, + platform_coin_address: &str, + ticker: String, +) -> EnableLightningResult { + let conn = ctx + .address_db(platform_coin_address) + .map_err(|e| EnableLightningError::IOError(e.to_string()))?; + let db = SqliteLightningDB::new(ticker, Arc::new(Mutex::new(conn)))?; if !db.is_db_initialized().await? { db.init_db().await?; diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 83f1aa0026..d55add2c2a 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -71,7 +71,7 @@ use mm2_rpc::data::legacy::{EnabledCoin, GetEnabledResponse, Mm2RpcResult}; #[cfg(any(test, feature = "for-tests"))] use mocktopus::macros::*; use parking_lot::Mutex as PaMutex; -use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; use rpc_command::tendermint::ibc::ChannelId; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{self as json, Value as Json}; @@ -214,7 +214,8 @@ pub mod lp_price; pub mod watcher_common; pub mod coin_errors; -use coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentResult}; +use coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentError, ValidatePaymentFut, + ValidatePaymentResult}; use crypto::secret_hash_algo::SecretHashAlgo; pub mod eth; @@ -2076,6 +2077,8 @@ pub trait MarketCoinOps { fn my_address(&self) -> MmResult; + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult; + async fn get_public_key(&self) -> Result>; fn sign_message_hash(&self, _message: &str) -> Option<[u8; 32]>; diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index ac10ba3151..ad01bba16f 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1,4 +1,4 @@ -use crate::coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentError, ValidatePaymentResult}; use crate::eth::{self, u256_to_big_decimal, wei_from_big_decimal, TryToAddress}; use crate::qrc20::rpc_clients::{LogEntry, Qrc20ElectrumOps, Qrc20NativeOps, Qrc20RpcOps, TopicFilter, TxReceipt, ViewContractCallType}; @@ -44,7 +44,8 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; #[cfg(test)] use mocktopus::macros::*; -use rpc::v1::types::{Bytes as BytesJson, ToTxHash, Transaction as RpcTransaction, H160 as H160Json, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, ToTxHash, Transaction as RpcTransaction, H160 as H160Json, H256 as H256Json, + H264 as H264Json}; use script::{Builder as ScriptBuilder, Opcode, Script, TransactionInputSigner}; use script_pubkey::generate_contract_call_script_pubkey; use serde_json::{self as json, Value as Json}; @@ -1024,6 +1025,11 @@ impl MarketCoinOps for Qrc20Coin { fn my_address(&self) -> MmResult { utxo_common::my_address(self) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let pubkey = Public::Compressed((*pubkey).into()); + Ok(UtxoCommonOps::address_from_pubkey(self, &pubkey).to_string()) + } + async fn get_public_key(&self) -> Result> { let pubkey = utxo_common::my_public_key(self.as_ref())?; Ok(pubkey.to_string()) diff --git a/mm2src/coins/siacoin.rs b/mm2src/coins/siacoin.rs index bb5ec12353..08458e5c0d 100644 --- a/mm2src/coins/siacoin.rs +++ b/mm2src/coins/siacoin.rs @@ -1,12 +1,13 @@ use super::{BalanceError, CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, SwapOps, TradeFee, TransactionEnum}; -use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, ConfirmPaymentInput, - DexFee, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, PrivKeyBuildPolicy, PrivKeyPolicy, - RawTransactionResult, RefundPaymentArgs, SearchForSwapTxSpendInput, SendPaymentArgs, - SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, TradePreimageFut, TradePreimageResult, - TradePreimageValue, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateFeeArgs, ValidateOtherPubKeyErr, ValidatePaymentInput, ValidatePaymentResult, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WeakSpawner, WithdrawFut, WithdrawRequest}; +use crate::{coin_errors::MyAddressError, AddressFromPubkeyError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, + ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, + PrivKeyBuildPolicy, PrivKeyPolicy, RawTransactionResult, RefundPaymentArgs, SearchForSwapTxSpendInput, + SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateFeeArgs, ValidateOtherPubKeyErr, ValidatePaymentInput, + ValidatePaymentResult, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WeakSpawner, WithdrawFut, + WithdrawRequest}; use async_trait::async_trait; use common::executor::AbortedError; pub use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature}; @@ -16,7 +17,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, BigInt, MmNumber}; -use rpc::v1::types::Bytes as BytesJson; +use rpc::v1::types::{Bytes as BytesJson, H264 as H264Json}; use serde_json::Value as Json; use std::ops::Deref; use std::sync::Arc; @@ -312,6 +313,14 @@ impl MarketCoinOps for SiaCoin { Ok(address.to_string()) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let pubkey = PublicKey::from_bytes(&pubkey.0[..32]).map_err(|e| { + AddressFromPubkeyError::InternalError(format!("Couldn't parse bytes into ed25519 pubkey: {e:?}")) + })?; + let address = SpendPolicy::PublicKey(pubkey).address(); + Ok(address.to_string()) + } + async fn get_public_key(&self) -> Result> { unimplemented!() } fn sign_message_hash(&self, _message: &str) -> Option<[u8; 32]> { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 201e215c19..b43c54bdf2 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -4,7 +4,7 @@ use super::htlc::{ClaimHtlcMsg, ClaimHtlcProto, CreateHtlcMsg, CreateHtlcProto, use super::ibc::transfer_v1::MsgTransfer; use super::ibc::IBC_GAS_LIMIT_DEFAULT; use super::rpc::*; -use crate::coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentError, ValidatePaymentResult}; use crate::hd_wallet::{HDPathAccountToAddressId, WithdrawFrom}; use crate::rpc_command::tendermint::ibc::ChannelId; use crate::rpc_command::tendermint::staking::{ClaimRewardsPayload, Delegation, DelegationPayload, @@ -78,7 +78,7 @@ use num_traits::Zero; use parking_lot::Mutex as PaMutex; use primitives::hash::H256; use regex::Regex; -use rpc::v1::types::Bytes as BytesJson; +use rpc::v1::types::{Bytes as BytesJson, H264 as H264Json}; use serde_json::{self as json, Value as Json}; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; @@ -3320,6 +3320,12 @@ impl MarketCoinOps for TendermintCoin { fn my_address(&self) -> MmResult { Ok(self.account_id.to_string()) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let address = account_id_from_raw_pubkey(&self.account_prefix, &pubkey.0) + .map_err(|e| AddressFromPubkeyError::InternalError(e.to_string()))?; + Ok(address.to_string()) + } + async fn get_public_key(&self) -> Result> { let key = SigningKey::from_slice(self.activation_policy.activated_key_or_err()?.as_slice()) .expect("privkey validity is checked on coin creation"); diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index b30d07c1a5..d70b316d26 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -3,7 +3,7 @@ use super::ibc::IBC_GAS_LIMIT_DEFAULT; use super::{create_withdraw_msg_as_any, TendermintCoin, TendermintFeeDetails, GAS_LIMIT_DEFAULT, MIN_TX_SATOSHIS, TIMEOUT_HEIGHT_DELTA, TX_DEFAULT_MEMO}; -use crate::coin_errors::ValidatePaymentResult; +use crate::coin_errors::{AddressFromPubkeyError, ValidatePaymentResult}; use crate::utxo::utxo_common::big_decimal_from_sat; use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFut, BigDecimal, CheckIfMyPaymentSentArgs, CoinBalance, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, @@ -29,7 +29,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::MmNumber; use primitives::hash::H256; -use rpc::v1::types::Bytes as BytesJson; +use rpc::v1::types::{Bytes as BytesJson, H264 as H264Json}; use serde_json::Value as Json; use std::ops::Deref; use std::str::FromStr; @@ -269,6 +269,10 @@ impl MarketCoinOps for TendermintToken { fn my_address(&self) -> MmResult { self.platform_coin.my_address() } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + self.platform_coin.address_from_pubkey(pubkey) + } + async fn get_public_key(&self) -> Result> { self.platform_coin.get_public_key().await } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 278492638c..0faae14886 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -3,7 +3,7 @@ use super::{CoinBalance, CommonSwapOpsV2, FindPaymentSpendError, FundingTxSpend, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, RefundTakerPaymentArgs, SearchForFundingSpendErr, SwapOps, TradeFee, TransactionEnum, TransactionFut}; -use crate::coin_errors::ValidatePaymentResult; +use crate::coin_errors::{AddressFromPubkeyError, ValidatePaymentResult}; use crate::hd_wallet::AddrToString; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, @@ -28,7 +28,7 @@ use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; #[cfg(any(test, feature = "for-tests"))] use mocktopus::macros::*; -use rpc::v1::types::Bytes as BytesJson; +use rpc::v1::types::{Bytes as BytesJson, H264 as H264Json}; use serde_json::Value as Json; use std::fmt::{Display, Formatter}; use std::ops::Deref; @@ -65,6 +65,8 @@ impl MarketCoinOps for TestCoin { fn my_address(&self) -> MmResult { unimplemented!() } + fn address_from_pubkey(&self, _pubkey: &H264Json) -> MmResult { unimplemented!() } + async fn get_public_key(&self) -> Result> { unimplemented!() } fn sign_message_hash(&self, _message: &str) -> Option<[u8; 32]> { unimplemented!() } diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 6fd2f28fe4..53e9bc7cdb 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -624,6 +624,7 @@ pub struct UtxoCoinFields { /// The cache of recently send transactions used to track the spent UTXOs and replace them with new outputs /// The daemon needs some time to update the listunspent list for address which makes it return already spent UTXOs /// This cache helps to prevent UTXO reuse in such cases + // TODO: change the type of `recently_spent_outpoints` to `AsyncMutex>` to better support HD wallets. pub recently_spent_outpoints: AsyncMutex, pub tx_hash_algo: TxHashAlgo, /// The flag determines whether to use mature unspent outputs *only* to generate transactions. diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 76a2f5d708..7e18dfc913 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1,6 +1,6 @@ use super::*; use crate::coin_balance::{EnableCoinBalanceError, HDAddressBalance, HDWalletBalance, HDWalletBalanceOps}; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentResult}; use crate::hd_wallet::{ExtractExtendedPubkey, HDCoinAddress, HDCoinWithdrawOps, HDExtractPubkeyError, HDXPubExtractor, TrezorCoinError, WithdrawSenderAddress}; use crate::my_tx_history_v2::{CoinWithTxHistoryV2, MyTxHistoryErrorV2, MyTxHistoryTarget, TxDetailsBuilder, @@ -34,6 +34,7 @@ use keys::CashAddress; pub use keys::NetworkPrefix as CashAddrPrefix; use mm2_metrics::MetricsArc; use mm2_number::MmNumber; +use rpc::v1::types::H264 as H264Json; use serde_json::{self as json, Value as Json}; use serialization::{deserialize, CoinVariant}; use std::sync::MutexGuard; @@ -1134,6 +1135,11 @@ impl MarketCoinOps for BchCoin { fn my_address(&self) -> MmResult { utxo_common::my_address(self) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let pubkey = Public::Compressed((*pubkey).into()); + Ok(UtxoCommonOps::address_from_pubkey(self, &pubkey).to_string()) + } + async fn get_public_key(&self) -> Result> { let pubkey = utxo_common::my_public_key(&self.utxo_arc)?; Ok(pubkey.to_string()) diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index e3214552e6..cab1d3c5d5 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -2,7 +2,7 @@ use super::utxo_common::utxo_prepare_addresses_for_balance_stream_if_enabled; use super::*; use crate::coin_balance::{self, EnableCoinBalanceError, EnabledCoinBalanceParams, HDAccountBalance, HDAddressBalance, HDWalletBalance, HDWalletBalanceOps}; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentResult}; use crate::hd_wallet::{ExtractExtendedPubkey, HDCoinAddress, HDCoinWithdrawOps, HDConfirmAddress, HDExtractPubkeyError, HDXPubExtractor, TrezorCoinError, WithdrawSenderAddress}; use crate::my_tx_history_v2::{CoinWithTxHistoryV2, MyTxHistoryErrorV2, MyTxHistoryTarget, TxHistoryStorage}; @@ -40,6 +40,7 @@ use futures::{FutureExt, TryFutureExt}; use keys::AddressHashEnum; use mm2_metrics::MetricsArc; use mm2_number::MmNumber; +use rpc::v1::types::H264 as H264Json; use serde::Serialize; use serialization::CoinVariant; use utxo_signer::UtxoSignerOps; @@ -757,6 +758,11 @@ impl MarketCoinOps for QtumCoin { fn my_address(&self) -> MmResult { utxo_common::my_address(self) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let pubkey = Public::Compressed((*pubkey).into()); + Ok(UtxoCommonOps::address_from_pubkey(self, &pubkey).to_string()) + } + async fn get_public_key(&self) -> Result> { let pubkey = utxo_common::my_public_key(&self.utxo_arc)?; Ok(pubkey.to_string()) diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 5229f07180..a0b29006cc 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -3,7 +3,7 @@ //! Tracking issue: https://github.com/KomodoPlatform/atomicDEX-API/issues/701 //! More info about the protocol and implementation guides can be found at https://slp.dev/ -use crate::coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentError, ValidatePaymentResult}; use crate::my_tx_history_v2::{CoinWithTxHistoryV2, MyTxHistoryErrorV2, MyTxHistoryTarget}; use crate::tx_history_storage::{GetTxHistoryFilters, WalletId}; use crate::utxo::bch::BchCoin; @@ -44,7 +44,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; use primitives::hash::H256; -use rpc::v1::types::{Bytes as BytesJson, ToTxHash, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, ToTxHash, H256 as H256Json, H264 as H264Json}; use script::bytes::Bytes; use script::{Builder as ScriptBuilder, Opcode, Script, TransactionInputSigner}; use serde_json::Value as Json; @@ -1102,6 +1102,11 @@ impl MarketCoinOps for SlpToken { slp_address.encode().map_to_mm(MyAddressError::InternalError) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + // TODO: We have two `address_from_pubkey`s, one in MarketCoinOps and one in UtxoCommonOps. We should give them different names. + MarketCoinOps::address_from_pubkey(&self.platform_coin, pubkey) + } + async fn get_public_key(&self) -> Result> { let pubkey = utxo_common::my_public_key(self.platform_coin.as_ref())?; Ok(pubkey.to_string()) @@ -1122,7 +1127,7 @@ impl MarketCoinOps for SlpToken { let signature = CompactSignature::try_from(STANDARD.decode(signature)?) .map_to_mm(|err| VerificationError::SignatureDecodingError(err.to_string()))?; let pubkey = Public::recover_compact(&H256::from(message_hash), &signature)?; - let address_from_pubkey = self.platform_coin.address_from_pubkey(&pubkey); + let address_from_pubkey = UtxoCommonOps::address_from_pubkey(&self.platform_coin, &pubkey); let slp_address = self .platform_coin .slp_address(&address_from_pubkey) diff --git a/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs b/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs index 80fd41c3ee..03ccd6fd8b 100644 --- a/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs +++ b/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs @@ -1,4 +1,4 @@ -use crate::hd_wallet::{load_hd_accounts_from_storage, HDAccountsMutex, HDWallet, HDWalletCoinStorage, +use crate::hd_wallet::{load_hd_accounts_from_storage, HDAccountsMutex, HDWallet, HDWalletCoinStorage, HDWalletOps, HDWalletStorageError, DEFAULT_GAP_LIMIT}; use crate::utxo::rpc_clients::{ElectrumClient, ElectrumClientSettings, ElectrumConnectionSettings, EstimateFeeMethod, UtxoRpcClientEnum}; @@ -19,7 +19,6 @@ use derive_more::Display; use futures::channel::mpsc::{channel, Receiver as AsyncReceiver}; use futures::compat::Future01CompatExt; use futures::lock::Mutex as AsyncMutex; -use keys::bytes::Bytes; pub use keys::{Address, AddressBuilder, AddressFormat as UtxoAddressFormat, AddressHashEnum, AddressScriptType, KeyPair, Private, Public, Secret}; use mm2_core::mm_ctx::MmArc; @@ -298,11 +297,6 @@ pub trait UtxoFieldsWithHardwareWalletBuilder: UtxoCoinBuilderCommonOps { } let hd_wallet_rmd160 = self.trezor_wallet_rmd160()?; - // For now, use a default script pubkey. - // TODO change the type of `recently_spent_outpoints` to `AsyncMutex>` - let my_script_pubkey = Bytes::new(); - let recently_spent_outpoints = AsyncMutex::new(RecentlySpentOutPoints::new(my_script_pubkey)); - let address_format = self.address_format()?; let path_to_coin = conf .derivation_path @@ -327,6 +321,13 @@ pub trait UtxoFieldsWithHardwareWalletBuilder: UtxoCoinBuilderCommonOps { address_format, }; + let my_address = hd_wallet + .get_enabled_address() + .await + .ok_or_else(|| UtxoCoinBuildError::Internal("Failed to get enabled address from HD wallet".to_owned()))?; + let my_script_pubkey = output_script(&my_address.address).map(|script| script.to_bytes())?; + let recently_spent_outpoints = AsyncMutex::new(RecentlySpentOutPoints::new(my_script_pubkey)); + // Create an abortable system linked to the `MmCtx` so if the context is stopped via `MmArc::stop`, // all spawned futures related to this `UTXO` coin will be aborted as well. let abortable_system: AbortableQueue = self.ctx().abortable_system.create_subsystem()?; @@ -681,7 +682,7 @@ pub trait UtxoCoinBuilderCommonOps { } #[cfg(not(target_arch = "wasm32"))] - fn tx_cache_path(&self) -> PathBuf { self.ctx().dbdir().join("TX_CACHE") } + fn tx_cache_path(&self) -> PathBuf { self.ctx().global_dir().join("TX_CACHE") } fn block_header_status_channel( &self, diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index d8191dfef6..ec9fd8adb9 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -2,7 +2,7 @@ use super::utxo_common::utxo_prepare_addresses_for_balance_stream_if_enabled; use super::*; use crate::coin_balance::{self, EnableCoinBalanceError, EnabledCoinBalanceParams, HDAccountBalance, HDAddressBalance, HDWalletBalance, HDWalletBalanceOps}; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentResult}; use crate::hd_wallet::{ExtractExtendedPubkey, HDCoinAddress, HDCoinWithdrawOps, HDConfirmAddress, HDExtractPubkeyError, HDXPubExtractor, TrezorCoinError, WithdrawSenderAddress}; use crate::my_tx_history_v2::{CoinWithTxHistoryV2, MyTxHistoryErrorV2, MyTxHistoryTarget, TxHistoryStorage}; @@ -43,6 +43,7 @@ use futures::{FutureExt, TryFutureExt}; use mm2_metrics::MetricsArc; use mm2_number::MmNumber; #[cfg(test)] use mocktopus::macros::*; +use rpc::v1::types::H264 as H264Json; use script::Opcode; use utxo_signer::UtxoSignerOps; @@ -848,6 +849,11 @@ impl MarketCoinOps for UtxoStandardCoin { fn my_address(&self) -> MmResult { utxo_common::my_address(self) } + fn address_from_pubkey(&self, pubkey: &H264Json) -> MmResult { + let pubkey = Public::Compressed((*pubkey).into()); + Ok(UtxoCommonOps::address_from_pubkey(self, &pubkey).to_string()) + } + fn sign_message_hash(&self, message: &str) -> Option<[u8; 32]> { utxo_common::sign_message_hash(self.as_ref(), message) } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index ffad4d09ea..d1487fc029 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -8,7 +8,7 @@ mod z_htlc; mod z_rpc; mod z_tx_history; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; +use crate::coin_errors::{AddressFromPubkeyError, MyAddressError, ValidatePaymentResult}; use crate::hd_wallet::HDPathAccountToAddressId; use crate::my_tx_history_v2::{MyTxHistoryErrorV2, MyTxHistoryRequestV2, MyTxHistoryResponseV2}; use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawInProgressStatus, WithdrawTaskHandleShared}; @@ -57,7 +57,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; #[cfg(test)] use mocktopus::macros::*; -use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as H256Json, H264 as H264Json}; use script::{Builder as ScriptBuilder, Opcode, Script, TransactionInputSigner}; use serde_json::Value as Json; use serialization::CoinVariant; @@ -396,7 +396,7 @@ impl ZCoin { let spendable_notes = self .spendable_notes_ordered() .await - .map_err(|err| GenTxError::SpendableNotesError(err.to_string()))?; + .mm_err(|err| GenTxError::SpendableNotesError(err.to_string()))?; let mut total_input_amount = BigDecimal::from(0); let mut change = BigDecimal::from(0); @@ -826,10 +826,6 @@ pub async fn z_coin_from_conf_and_params( protocol_info: ZcoinProtocolInfo, priv_key_policy: PrivKeyBuildPolicy, ) -> Result> { - #[cfg(target_arch = "wasm32")] - let db_dir_path = PathBuf::new(); - #[cfg(not(target_arch = "wasm32"))] - let db_dir_path = ctx.dbdir(); let z_spending_key = None; let builder = ZCoinBuilder::new( ctx, @@ -837,10 +833,9 @@ pub async fn z_coin_from_conf_and_params( conf, params, priv_key_policy, - db_dir_path, z_spending_key, protocol_info, - ); + )?; builder.build().await } @@ -872,10 +867,9 @@ pub struct ZCoinBuilder<'a> { z_coin_params: &'a ZcoinActivationParams, utxo_params: UtxoActivationParams, priv_key_policy: PrivKeyBuildPolicy, - #[cfg_attr(target_arch = "wasm32", allow(unused))] - db_dir_path: PathBuf, - /// `Some` if `ZCoin` should be initialized with a forced spending key. - z_spending_key: Option, + z_spending_key: ExtendedSpendingKey, + my_z_addr: PaymentAddress, + my_z_addr_encoded: String, protocol_info: ZcoinProtocolInfo, } @@ -908,19 +902,6 @@ impl<'a> UtxoCoinBuilder for ZCoinBuilder<'a> { let utxo = self.build_utxo_fields().await?; let utxo_arc = UtxoArc::new(utxo); - let z_spending_key = match self.z_spending_key { - Some(ref z_spending_key) => z_spending_key.clone(), - None => extended_spending_key_from_protocol_info_and_policy( - &self.protocol_info, - &self.priv_key_policy, - self.z_coin_params.account, - )?, - }; - - let (_, my_z_addr) = z_spending_key - .default_address() - .map_err(|_| MmError::new(ZCoinBuildError::GetAddressError))?; - let dex_fee_addr = decode_payment_address( self.protocol_info.consensus_params.hrp_sapling_payment_address(), DEX_FEE_Z_ADDR, @@ -936,18 +917,11 @@ impl<'a> UtxoCoinBuilder for ZCoinBuilder<'a> { .expect("DEX_BURN_Z_ADDR is a valid z-address"); let z_tx_prover = self.z_tx_prover().await?; - let my_z_addr_encoded = encode_payment_address( - self.protocol_info.consensus_params.hrp_sapling_payment_address(), - &my_z_addr, - ); - let blocks_db = self.init_blocks_db().await?; let (sync_state_connector, light_wallet_db) = match &self.z_coin_params.mode { #[cfg(not(target_arch = "wasm32"))] - ZcoinRpcMode::Native => { - init_native_client(&self, self.native_client()?, blocks_db, &z_spending_key).await? - }, + ZcoinRpcMode::Native => init_native_client(&self, self.native_client()?, blocks_db).await?, ZcoinRpcMode::Light { light_wallet_d_servers, sync_params, @@ -960,7 +934,6 @@ impl<'a> UtxoCoinBuilder for ZCoinBuilder<'a> { blocks_db, sync_params, skip_sync_params.unwrap_or_default(), - &z_spending_key, ) .await? }, @@ -969,10 +942,10 @@ impl<'a> UtxoCoinBuilder for ZCoinBuilder<'a> { let z_fields = Arc::new(ZCoinFields { dex_fee_addr, dex_burn_addr, - my_z_addr, - my_z_addr_encoded, - evk: ExtendedFullViewingKey::from(&z_spending_key), - z_spending_key, + my_z_addr: self.my_z_addr, + my_z_addr_encoded: self.my_z_addr_encoded, + evk: ExtendedFullViewingKey::from(&self.z_spending_key), + z_spending_key: self.z_spending_key, z_tx_prover: Arc::new(z_tx_prover), light_wallet_db, consensus_params: self.protocol_info.consensus_params, @@ -991,10 +964,9 @@ impl<'a> ZCoinBuilder<'a> { conf: &'a Json, z_coin_params: &'a ZcoinActivationParams, priv_key_policy: PrivKeyBuildPolicy, - db_dir_path: PathBuf, z_spending_key: Option, protocol_info: ZcoinProtocolInfo, - ) -> ZCoinBuilder<'a> { + ) -> MmResult, ZCoinBuildError> { let utxo_mode = match &z_coin_params.mode { #[cfg(not(target_arch = "wasm32"))] ZcoinRpcMode::Native => UtxoRpcMode::Native, @@ -1023,27 +995,49 @@ impl<'a> ZCoinBuilder<'a> { // This is not used for Zcoin so we just provide a default value path_to_address: HDPathAccountToAddressId::default(), }; - ZCoinBuilder { + + let z_spending_key = match z_spending_key { + Some(ref z_spending_key) => z_spending_key.clone(), + None => extended_spending_key_from_protocol_info_and_policy( + &protocol_info, + &priv_key_policy, + z_coin_params.account, + )?, + }; + + let (_, my_z_addr) = z_spending_key + .default_address() + .map_to_mm(|_| ZCoinBuildError::GetAddressError)?; + + let my_z_addr_encoded = + encode_payment_address(protocol_info.consensus_params.hrp_sapling_payment_address(), &my_z_addr); + + Ok(ZCoinBuilder { ctx, ticker, conf, z_coin_params, utxo_params, priv_key_policy, - db_dir_path, z_spending_key, + my_z_addr, + my_z_addr_encoded, protocol_info, - } + }) } async fn init_blocks_db(&self) -> Result> { - let cache_db_path = self.db_dir_path.join(format!("{}_cache.db", self.ticker)); - let ctx = self.ctx.clone(); + let ctx = &self.ctx; let ticker = self.ticker.to_string(); - BlockDbImpl::new(&ctx, ticker, cache_db_path) - .map_err(|err| MmError::new(ZcoinClientInitError::ZcoinStorageError(err.to_string()))) + #[cfg(target_arch = "wasm32")] + let cache_db_path = PathBuf::new(); + #[cfg(not(target_arch = "wasm32"))] + let cache_db_path = self.ctx.global_dir().join(format!("{}_cache.db", self.ticker)); + + BlockDbImpl::new(ctx, ticker, cache_db_path) .await + .mm_err(|err| ZcoinClientInitError::ZcoinStorageError(err.to_string())) } #[cfg(not(target_arch = "wasm32"))] @@ -1102,7 +1096,6 @@ pub async fn z_coin_from_conf_and_params_with_docker( conf: &Json, params: &ZcoinActivationParams, priv_key_policy: PrivKeyBuildPolicy, - db_dir_path: PathBuf, protocol_info: ZcoinProtocolInfo, spending_key: &str, ) -> Result> { @@ -1118,10 +1111,9 @@ pub async fn z_coin_from_conf_and_params_with_docker( conf, params, priv_key_policy, - db_dir_path, Some(z_spending_key), protocol_info, - ); + )?; println!("ZOMBIE_wallet.db will be synch'ed with the chain, this may take a while for the first time."); println!("You may also run prepare_zombie_sapling_cache test to update ZOMBIE_wallet.db before running tests."); @@ -1134,6 +1126,11 @@ impl MarketCoinOps for ZCoin { fn my_address(&self) -> MmResult { Ok(self.z_fields.my_z_addr_encoded.clone()) } + fn address_from_pubkey(&self, _pubkey: &H264Json) -> MmResult { + // NOTE: We can't derive a z-address from pubkey, so we will just return our own z_address. + Ok(self.z_fields.my_z_addr_encoded.clone()) + } + async fn get_public_key(&self) -> Result> { let pubkey = utxo_common::my_public_key(self.as_ref())?; Ok(pubkey.to_string()) @@ -1430,7 +1427,7 @@ impl SwapOps for ZCoin { .get_verbose_transaction(&tx_hash.into()) .compat() .await - .map_err(|e| MmError::new(ValidatePaymentError::InvalidRpcResponse(e.into_inner().to_string())))?; + .mm_err(|e| ValidatePaymentError::InvalidRpcResponse(e.to_string()))?; let mut encoded = Vec::with_capacity(1024); z_tx.write(&mut encoded).expect("Writing should not fail"); diff --git a/mm2src/coins/z_coin/storage/blockdb/blockdb_sql_storage.rs b/mm2src/coins/z_coin/storage/blockdb/blockdb_sql_storage.rs index 44721b4364..8dd4dd39f7 100644 --- a/mm2src/coins/z_coin/storage/blockdb/blockdb_sql_storage.rs +++ b/mm2src/coins/z_coin/storage/blockdb/blockdb_sql_storage.rs @@ -48,6 +48,7 @@ impl BlockDbImpl { #[cfg(not(test))] pub async fn new(_ctx: &MmArc, ticker: String, path: PathBuf) -> ZcoinStorageRes { async_blocking(move || { + mm2_io::fs::create_parents(&path).map_err(|err| ZcoinStorageError::IoError(err.to_string()))?; let conn = Connection::open(path).map_to_mm(|err| ZcoinStorageError::DbError(err.to_string()))?; let conn = Arc::new(Mutex::new(conn)); let conn_lock = conn.lock().unwrap(); diff --git a/mm2src/coins/z_coin/storage/walletdb/wallet_sql_storage.rs b/mm2src/coins/z_coin/storage/walletdb/wallet_sql_storage.rs index 3a957d375f..0408182ae0 100644 --- a/mm2src/coins/z_coin/storage/walletdb/wallet_sql_storage.rs +++ b/mm2src/coins/z_coin/storage/walletdb/wallet_sql_storage.rs @@ -11,7 +11,7 @@ use zcash_extras::{WalletRead, WalletWrite}; use zcash_primitives::block::BlockHash; use zcash_primitives::consensus::BlockHeight; use zcash_primitives::transaction::TxId; -use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +use zcash_primitives::zip32::ExtendedFullViewingKey; /// `create_wallet_db` is responsible for creating a new Zcoin wallet database, initializing it /// with the provided parameters, and executing various initialization steps. These steps include checking and @@ -24,6 +24,9 @@ pub async fn create_wallet_db( evk: ExtendedFullViewingKey, continue_from_prev_sync: bool, ) -> Result, MmError> { + mm2_io::fs::create_parents_async(&wallet_db_path) + .await + .map_err(|err| ZcoinClientInitError::ZcoinStorageError(err.to_string()))?; let db = async_blocking(move || { WalletDbAsync::for_path(wallet_db_path, consensus_params) .map_to_mm(|err| ZcoinClientInitError::ZcoinStorageError(err.to_string())) @@ -81,16 +84,15 @@ impl<'a> WalletDbShared { pub async fn new( builder: &ZCoinBuilder<'a>, checkpoint_block: Option, - z_spending_key: &ExtendedSpendingKey, continue_from_prev_sync: bool, ) -> ZcoinStorageRes { let ticker = builder.ticker; let consensus_params = builder.protocol_info.consensus_params.clone(); let wallet_db = create_wallet_db( - builder.db_dir_path.join(format!("{ticker}_wallet.db")), + builder.ctx.wallet_dir().join(format!("{ticker}_wallet.db")), consensus_params, checkpoint_block, - ExtendedFullViewingKey::from(z_spending_key), + ExtendedFullViewingKey::from(&builder.z_spending_key), continue_from_prev_sync, ) .await diff --git a/mm2src/coins/z_coin/storage/walletdb/wasm/storage.rs b/mm2src/coins/z_coin/storage/walletdb/wasm/storage.rs index d9ac5ec322..06d06c4d32 100644 --- a/mm2src/coins/z_coin/storage/walletdb/wasm/storage.rs +++ b/mm2src/coins/z_coin/storage/walletdb/wasm/storage.rs @@ -33,7 +33,7 @@ use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness}; use zcash_primitives::sapling::{Node, Nullifier, PaymentAddress}; use zcash_primitives::transaction::components::Amount; use zcash_primitives::transaction::{Transaction, TxId}; -use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +use zcash_primitives::zip32::ExtendedFullViewingKey; const DB_NAME: &str = "wallet_db_cache"; const DB_VERSION: u32 = 1; @@ -54,7 +54,6 @@ impl<'a> WalletDbShared { pub async fn new( builder: &ZCoinBuilder<'a>, checkpoint_block: Option, - z_spending_key: &ExtendedSpendingKey, continue_from_prev_sync: bool, ) -> ZcoinStorageRes { let ticker = builder.ticker; @@ -62,7 +61,7 @@ impl<'a> WalletDbShared { let db = WalletIndexedDb::new(builder.ctx, ticker, consensus_params).await?; let extrema = db.block_height_extrema().await?; let get_evk = db.get_extended_full_viewing_keys().await?; - let evk = ExtendedFullViewingKey::from(z_spending_key); + let evk = ExtendedFullViewingKey::from(&builder.z_spending_key); let min_sync_height = extrema.map(|(min, _)| u32::from(min)); let init_block_height = checkpoint_block.clone().map(|block| block.height); diff --git a/mm2src/coins/z_coin/z_coin_native_tests.rs b/mm2src/coins/z_coin/z_coin_native_tests.rs index 4e5ffc4325..892da5401e 100644 --- a/mm2src/coins/z_coin/z_coin_native_tests.rs +++ b/mm2src/coins/z_coin/z_coin_native_tests.rs @@ -26,7 +26,6 @@ use bitcrypto::dhash160; use common::{block_on, now_sec}; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_test_helpers::for_tests::zombie_conf; -use std::path::PathBuf; use std::time::Duration; use zcash_client_backend::encoding::decode_extended_spending_key; @@ -50,7 +49,6 @@ async fn zombie_coin_send_and_refund_maker_payment() { let mut conf = zombie_conf(); let params = native_zcoin_activation_params(); let pk_data = [1; 32]; - let db_dir = PathBuf::from("./for_tests"); let z_key = decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, "secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").unwrap().unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, @@ -63,7 +61,6 @@ async fn zombie_coin_send_and_refund_maker_payment() { &conf, ¶ms, PrivKeyBuildPolicy::IguanaPrivKey(pk_data.into()), - db_dir, z_key, protocol_info, ) @@ -115,7 +112,6 @@ async fn zombie_coin_send_and_spend_maker_payment() { let mut conf = zombie_conf(); let params = native_zcoin_activation_params(); let pk_data = [1; 32]; - let db_dir = PathBuf::from("./for_tests"); let z_key = decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, "secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").unwrap().unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, @@ -128,7 +124,6 @@ async fn zombie_coin_send_and_spend_maker_payment() { &conf, ¶ms, PrivKeyBuildPolicy::IguanaPrivKey(pk_data.into()), - db_dir, z_key, protocol_info, ) @@ -184,17 +179,15 @@ async fn zombie_coin_send_dex_fee() { let mut conf = zombie_conf(); let params = native_zcoin_activation_params(); let priv_key = PrivKeyBuildPolicy::IguanaPrivKey([1; 32].into()); - let db_dir = PathBuf::from("./for_tests"); let z_key = decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, "secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").unwrap().unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, other_protocol => panic!("Failed to get protocol from config: {:?}", other_protocol), }; - let coin = - z_coin_from_conf_and_params_with_z_key(&ctx, "ZOMBIE", &conf, ¶ms, priv_key, db_dir, z_key, protocol_info) - .await - .unwrap(); + let coin = z_coin_from_conf_and_params_with_z_key(&ctx, "ZOMBIE", &conf, ¶ms, priv_key, z_key, protocol_info) + .await + .unwrap(); let dex_fee = DexFee::WithBurn { fee_amount: "0.0075".into(), @@ -211,17 +204,15 @@ async fn zombie_coin_send_standard_dex_fee() { let mut conf = zombie_conf(); let params = native_zcoin_activation_params(); let priv_key = PrivKeyBuildPolicy::IguanaPrivKey([1; 32].into()); - let db_dir = PathBuf::from("./for_tests"); let z_key = decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, "secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").unwrap().unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, other_protocol => panic!("Failed to get protocol from config: {:?}", other_protocol), }; - let coin = - z_coin_from_conf_and_params_with_z_key(&ctx, "ZOMBIE", &conf, ¶ms, priv_key, db_dir, z_key, protocol_info) - .await - .unwrap(); + let coin = z_coin_from_conf_and_params_with_z_key(&ctx, "ZOMBIE", &conf, ¶ms, priv_key, z_key, protocol_info) + .await + .unwrap(); let dex_fee = DexFee::Standard("0.01".into()); let tx = z_send_dex_fee(&coin, dex_fee, &[1; 16]).await.unwrap(); @@ -235,7 +226,6 @@ fn prepare_zombie_sapling_cache() { let mut conf = zombie_conf(); let params = native_zcoin_activation_params(); let priv_key = PrivKeyBuildPolicy::IguanaPrivKey([1; 32].into()); - let db_dir = PathBuf::from("./for_tests"); let z_key = decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, "secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").unwrap().unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, @@ -248,7 +238,6 @@ fn prepare_zombie_sapling_cache() { &conf, ¶ms, priv_key, - db_dir, z_key, protocol_info, )) @@ -265,17 +254,15 @@ async fn zombie_coin_validate_dex_fee() { let mut conf = zombie_conf(); let params = native_zcoin_activation_params(); let priv_key = PrivKeyBuildPolicy::IguanaPrivKey([1; 32].into()); - let db_dir = PathBuf::from("./for_tests"); let z_key = decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, "secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").unwrap().unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, other_protocol => panic!("Failed to get protocol from config: {:?}", other_protocol), }; - let coin = - z_coin_from_conf_and_params_with_z_key(&ctx, "ZOMBIE", &conf, ¶ms, priv_key, db_dir, z_key, protocol_info) - .await - .unwrap(); + let coin = z_coin_from_conf_and_params_with_z_key(&ctx, "ZOMBIE", &conf, ¶ms, priv_key, z_key, protocol_info) + .await + .unwrap(); // https://zombie.explorer.lordofthechains.com/tx/9390a26810342151f48f455b09e5d087a5429cbba08f2381b02c43b76f813e29 let tx_hex = "0400008085202f8900000000000001030c00e8030000000000000169e7017fbd969be53da2c1b8812002baaf59ce98b230a9c1001397ba7f4db8676bd77e8ea644b67067d1f996d8d81c279961343f00a10095bccbddc341c98539287c900cf969688ddc574786e0e34bd6d3ec2ffaab5e2d472848781b116906669786c14c5c608b20dc23c9566fd46861f6a258b5ffc6de73495b56f4823e098c8664eab895d5cd31c013428ae2cbe940dc236ca40465ea2b912ce6c36555b2affb1f38b99b28dc593d865b0b948d567f9315df666d2e65e666d829b9823154bae0410bd885582b4a8a6eb4b9ae214b59ffd9b1167b7cd48f48a11cbd67c08f4e01ed4fd78fc91d0c9e70baa4f25761ef6c78cd7268b307aaa6ece2b443937eb4beac2c8843279a8879adbe0b381e65d0b674f2feeb54b78f80b377f66baab72c4cf9f10dde48f343c001df91a1a6d252ad8eca26eea0fdee49ad7024b505e55b4e082e94616794ddd7c2b852594b4b7af2292f0aa9e34f38322f548f1a21c015e92dbfd239ce18144f3b8045e9efa3de6b4c6b338f01d0adeb26a088a3c8c00503b67b2980b7663e97541e2944e4ad3588554966b6a930d2dc01d9fc7f8a846583fcf3b721f979705eff5bb9bb1fb0cad9ad941ceb3f581710efd8c50713a53751a0a196322ef8618bf1e097383666e91b5133ba81645d2b542181476eba2326cd02fb29a9f09edc46ea04b32ed9243597318d23b955a2570d78cbfb46cc26c1807eddd1de4785b6e752f859f7e25fc67f9e8a00feafac6fd7781eb72a663d9b80c10e9c387abc4d41294b3573785fd53bc56ccac2edf5c7bbb99cb3bcf87161fa893d2e1aabfee75754767cef07a12e44bb707720e727e585a258356cc797ecee8263c0f61cfc8ffa0360c758f1348ac44c186e12ce0f4faad43b4638abd4a0bc9fd4a6fa4352c20cc771241f95c26f1671ca95c8f4a63a8318dc43299f54e8a899df78ccfd3112a0d5ea637847dd2e3b05be8c0658dd0d7d814473fa5369957c00e84df600df23faaee5faa17b9ededad4731e5e9c1099dfddf5264756800dcfcad4b006b736d1d47c59a019acde4dc22249fc40846b77b43294e32a21db745e1bec790324c3d505edc79388a6e44b02841b26306ed48cfce1e941642c30792315016dba03797c8e4e279eec5b78aad602620471f24c25aea3aaa57509aa9eef2057f11bc95bad708918f2f0df74ac179d7dffc772b2c603dd89e7aea0e8f94f1a8bab4a4fba10bf05c88fbe4b021b3faff3d558e32e4bc20be4bed62d653674ce697390e098e590a3e354cb4a1e703474de8aab30cd76cf7e237f2e66bf486c4fc6c22028764e95adf7d8fa018f44b51ae6acfa3bf80f14c45c06623b916d79649abe0a2b229f96e60e421f6e734160da37f01e915cf73d1cacd1eb7f06c26c33b4d8e4dde264f3cfe84bada0601d1c03aa31c5938750ca0b852f3177883cae9f285d582a4eb38c05f8ef6e5cff5be0745e1ec66e20752bfd5bd5a1590fa280ace3e9786e0022e7ae3c48bcca14e9c5513bc8b57e15820a685f8348159862be0579a35d8ac9d1abaf36d9274c7e750fd9ad265c0d8f08c95ed9ce69eef3a55aef05f2d5d601f80f472689f3428e4f0095829a459813d5dace7e6137a752ae5567982e67b2092afeba99561fbe4e716f67bd1b4e8de1f376dec30eed27371bcc42d7de2ea0f4288054618e9afa002a2d1996b7a70a9683229f28bab811b67629dad527f325c0f12e19d92bac51e5924f27048fa118673b52b296b3642ec946d9915ded0ae84e1a2236da65f672bdad75a22cc0ea751c07e56d2ec22caa41afc98ec6b37a8c1b6a5378a81f2cdb2228f4efb8d7f35c0086a955e1b04bd09bd7e056c949fab1805f733a8b2061adad0c2b7fae33d21363de911e517b21a1539dfa1b3cbb1ea0dbfa3ffff23bbac01183f852de41e798fca5a278b711893175aeaded90873574d8de30b360f39ea239492c630eda4a811d3bb7a125054d5ca74bb6698aeea1a417ad19415ca0e5ca36abc2f96725986f73bcbe3113e391010d08f58f05979c7cef26ff92506c5d1eb2a2f6f5689e9a39957f0723bef3262f5190de996234d4f00b73ed74d78fdf1e6bf31161e16bd083bc6fbddc4eba85c17067e15f08019e5ed943de8e23a974d516abc641e85e641b03779816c30b3449a16b142417c1ff93ab7fa8f96a175e9ef73b3f06ac76788c27889d426efa78d5b8ce35be4591902f7766fe579a0aa28229235a920d26264c09625dea807f619a040f08931d6e1fe57ff0c48ea476be93a16d1fc8de3617984eeebcf14b63c839b41f8f9305402d1288c8e481a4fa5c3302bb1f83e3f0dc8ff9550f9bacb44bccb58f3de152abef5d578afed1c29dc89495b9e54a0c6d00f1dba45a2cf68c9512d9a9ff0b2531e58e47428a99cb246ca23f867b660dc71785b57407cc292f735634c602409792c4640831809f1f1e51903273b623aa0ae0cdd335c7b9db360b0bceb0d15f2313e1944800f30f82ed5bb07cfa1c4740c2bf2806539a4afac1f79d779b923ad8dc2493ebb2d2fce9aea58a009d64e7d1b71ca6893b076e41f7e88a4b51b5402e3fa6c60fa65a686adea229f0164318c9fa1b6d2d2218e5ada710daffecb6b7dd8bf7447658795c4c7a0ad710c4f02fd19017a0575f9467600cdca019793f2f49d197dbfc937828e5790b90929e5ca16037ec79734b64feec36b36c220a2979c45dd51e24c9fb21d8634471aac20c6f179f90c0d61c7b3d89826d146b157bedd8f6b66f6edfabfe04b49f2f2d999fc2e578a440bafd524c82ae614dc8017e379cf926e042f4fbd6f0628fde52de18d764ba8385b77569eda30d5a3617fb0a0c7fd26c821308c3ae98498d33b974cb318a04af3ea3fbcb13fc62fc952aaef095423da9ec7bdc7b77adbd403931189ddc98fe19a06711415b40a9a68812bb7c5453b7b2377910c7b89c99b379e038a7940487c0fd2405456ee55ab6ead3ef25a8a5b1abcae479c24f5e6869057e0bdabcdf352b4a64a3e385171a6e14c8102b2a187034e21705e3a457167fe0dc0d63d6e8d489c9a18c9d84b541504d36b086c2c63cc1a34c0080122c5d60ca33ab60289d16f21e1ded753607267c2093b1c587b89da9df65584fbe3ff9eb7f91d64e33912b8e91adc27191d22f8e835be6bb24546f21488f7abcb29339c34058d4f4093096144b17b8ab76a346275b7e7c80bca59d20e0bb482bb2a9cc3c9515cc1b5be17348c65c73e9fb1ed77d423c509f7cff0e355a34d080d310f3b848dbc209bbba6b6b109fb8d9556dca0fab086e197327ab423d5d762b68961244d8d22c30a8a3a116770bb15b5a0a347091a843b68d6a8e0f1c79f12523a7561c1233cd44db90f6cd3c1ce5fc13f8382177b5522aae028379269b71ae2a42f41dff7374ed7e83c89566f57297b82478b04359a2c199ce8f842112b7450cc1e2e2e394cda4c67e0b2302e21f6af997607ceefd067f77be8900bb3ecb3e30782477aa76861b286b9ddc9e36fcebb50f04f9516e02da31e6219bb5bcb81ee673d95be14c1bd2be4909556d6dbca0365292c582dedcafcc60b255ab7bcd9d977a4139f394ca1da81040e784fd8e7534f230bc5201e7f1db47eadc30f37609d5bbaba624157d98d65029bbab766b6c23c3049a32b894c0cfcb40913ba1cd2d5acda7d2acc920fd01c36f28fc6b7ffd01a37b17fc3235d0dbe9b8098530bed6894b288604b8689f4aafc22cdf211fb95ef5c90cae62a250234e6f790e9a15012acac88305dc4f91fd564a9ab8bb27c057ec5dd46fe952a7be557caea9b7b1d6118aa42df79b8c207e2bae6c34d67dc32b4360ad20b3e609e9caeb7f432ad51cfce139f2d4eb9ed219f4323acd5685e0e0409939eb662175a83fa083f500516dbcb091a3448cb24c3198c8fc547fbda3cb0894edeceef7ccb4ad746aa06f4038b63ab4095a9c390656520561ba3763b1057b3af7cb548342a2bfc2ab725b01b12a7adfc30d7d9632acafd2595cde406b8637a911b7c86f7b09b11f58acec3f1a1bd7cf6853331b48d7907ed699d91fbdbcab8001e3d8d3a26b491b6e2d98c5e149847a07a2b7faa1f567cd4bc9c83ad553339632f3dcacb890c5222656b3349ddd5c8eacaa490ac0b2b38f8a26da9ce7789f5601769a7f10b93125cb93b589bda4ddb4e8795817b60cc149af7c0699b2bbbf655f2f5ec170d6af51213e8c725e699d181923ecf10c6f1069f46e6bc89c7a29d2ebe133b5c0c4b67826a93add7d4824e60b4c5f0cee358abedb50c54a59e95185d7a80081f2dddba5c7c7c637b2dfe8575ddaa71306a2725c9ec17b8e4e1f271a442f6798cc21bbd55c2d69819ddde37a8e8d6a812c41a3e58719b7c96e9375155c4a873ed698ad37144ef32e3fe41cce9c48bbe31441dbbeec7b97734769063d6d04cd8d4963f09f7101bf57cb97a83452cc5de873c5ac0ce001c471c9fcd3275d90a118dd4c25a525d9fb358ff85104b98136850786b387fa17cc1a1d128bc5f7c365ec7920ea677e4c8023071a958647d9fbd27e29d7d099b4dfbbac086ac2af00407fd12092ef1f4847bf8988d839e49a6b5b42482c3dde77022ace66e1ca15b46f2df88d053c1bc3623110b3be74b08749eba6d22f87a44cf7cc1997e7e45d0e"; diff --git a/mm2src/coins/z_coin/z_rpc.rs b/mm2src/coins/z_coin/z_rpc.rs index 40eef387c0..aba2d0d55f 100644 --- a/mm2src/coins/z_coin/z_rpc.rs +++ b/mm2src/coins/z_coin/z_rpc.rs @@ -29,7 +29,6 @@ use z_coin_grpc::{BlockId, BlockRange, TreeState, TxFilter}; use zcash_extras::{WalletRead, WalletWrite}; use zcash_primitives::consensus::BlockHeight; use zcash_primitives::transaction::TxId; -use zcash_primitives::zip32::ExtendedSpendingKey; pub(crate) mod z_coin_grpc { tonic::include_proto!("pirate.wallet.sdk.rpc"); @@ -508,7 +507,6 @@ pub(super) async fn init_light_client<'a>( blocks_db: BlockDbImpl, sync_params: &Option, skip_sync_params: bool, - z_spending_key: &ExtendedSpendingKey, ) -> Result<(AsyncMutex, WalletDbShared), MmError> { let coin = builder.ticker.to_string(); let (sync_status_notifier, sync_watcher) = channel(1); @@ -541,8 +539,7 @@ pub(super) async fn init_light_client<'a>( // check if no sync_params was provided and continue syncing from last height in db if it's > 0 or skip_sync_params is true. let continue_from_prev_sync = (min_height > 0 && sync_params.is_none()) || (skip_sync_params && min_height < sapling_activation_height); - let wallet_db = - WalletDbShared::new(builder, maybe_checkpoint_block, z_spending_key, continue_from_prev_sync).await?; + let wallet_db = WalletDbShared::new(builder, maybe_checkpoint_block, continue_from_prev_sync).await?; // Check min_height in blocks_db and rewind blocks_db to 0 if sync_height != min_height if !continue_from_prev_sync && (sync_height != min_height) { // let user know we're clearing cache and re-syncing from new provided height. @@ -586,7 +583,6 @@ pub(super) async fn init_native_client<'a>( builder: &ZCoinBuilder<'a>, native_client: NativeClient, blocks_db: BlockDbImpl, - z_spending_key: &ExtendedSpendingKey, ) -> Result<(AsyncMutex, WalletDbShared), MmError> { let coin = builder.ticker.to_string(); let (sync_status_notifier, sync_watcher) = channel(1); @@ -600,7 +596,7 @@ pub(super) async fn init_native_client<'a>( is_pre_sapling: false, actual: checkpoint_height, }; - let wallet_db = WalletDbShared::new(builder, checkpoint_block, z_spending_key, true) + let wallet_db = WalletDbShared::new(builder, checkpoint_block, true) .await .mm_err(|err| ZcoinClientInitError::ZcoinStorageError(err.to_string()))?; diff --git a/mm2src/coins_activation/Cargo.toml b/mm2src/coins_activation/Cargo.toml index be951c67d4..3ed8213e3b 100644 --- a/mm2src/coins_activation/Cargo.toml +++ b/mm2src/coins_activation/Cargo.toml @@ -28,6 +28,7 @@ mm2_number = { path = "../mm2_number" } parking_lot = { version = "0.12.0", features = ["nightly"] } rpc = { path = "../mm2_bitcoin/rpc" } rpc_task = { path = "../rpc_task" } +secp256k1 = { version = "0.24" } ser_error = { path = "../derives/ser_error" } ser_error_derive = { path = "../derives/ser_error_derive" } serde = "1.0" diff --git a/mm2src/coins_activation/src/lightning_activation.rs b/mm2src/coins_activation/src/lightning_activation.rs index 1d2f9ec232..105fb2c422 100644 --- a/mm2src/coins_activation/src/lightning_activation.rs +++ b/mm2src/coins_activation/src/lightning_activation.rs @@ -20,7 +20,7 @@ use common::executor::{SpawnFuture, Timer}; use crypto::hw_rpc_task::{HwRpcTaskAwaitingStatus, HwRpcTaskUserAction}; use derive_more::Display; use futures::compat::Future01CompatExt; -use lightning::chain::keysinterface::KeysInterface; +use lightning::chain::keysinterface::{KeysInterface, Recipient}; use lightning::chain::Access; use lightning::routing::gossip; use lightning::routing::router::DefaultRouter; @@ -29,6 +29,7 @@ use lightning_invoice::payment; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use parking_lot::Mutex as PaMutex; +use secp256k1::Secp256k1; use ser_error_derive::SerializeErrorType; use serde_derive::{Deserialize, Serialize}; use serde_json::{self as json, Value as Json}; @@ -350,11 +351,16 @@ async fn start_lightning( // Initialize the Logger let logger = ctx.log.0.clone(); - // Initialize Persister - let persister = init_persister(ctx, conf.ticker.clone(), params.backup_path).await?; - // Initialize the KeysManager let keys_manager = init_keys_manager(&platform)?; + let node_id = keys_manager + .get_node_secret(Recipient::Node) + .map_err(|e| EnableLightningError::Internal(format!("Error while getting node id: {:?}", e)))? + .public_key(&Secp256k1::new()); + let node_id = node_id.to_string(); + + // Initialize Persister + let persister = init_persister(ctx, &node_id, conf.ticker.clone(), params.backup_path).await?; // Initialize the P2PGossipSync. This is used for providing routes to send payments over task_handle.update_in_progress_status(LightningInProgressStatus::ReadingNetworkGraphFromFile)?; @@ -371,7 +377,7 @@ async fn start_lightning( )); // Initialize DB - let db = init_db(ctx, conf.ticker.clone()).await?; + let db = init_db(ctx, &node_id, conf.ticker.clone()).await?; // Initialize the ChannelManager task_handle.update_in_progress_status(LightningInProgressStatus::InitializingChannelManager)?; diff --git a/mm2src/mm2_core/Cargo.toml b/mm2src/mm2_core/Cargo.toml index 37cf759a9d..747875d5b8 100644 --- a/mm2src/mm2_core/Cargo.toml +++ b/mm2src/mm2_core/Cargo.toml @@ -41,6 +41,7 @@ timed-map = { version = "1.4", features = ["rustc-hash", "wasm"] } wasm-bindgen-test = { version = "0.3.2" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +mm2_io = { path = "../mm2_io" } rustls = { version = "0.21", default-features = false } tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net"] } timed-map = { version = "1.4", features = ["rustc-hash"] } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 292dc69ca6..5d01b81245 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -38,6 +38,7 @@ cfg_native! { use mm2_metrics::MmMetricsError; use std::net::{IpAddr, SocketAddr, AddrParseError}; use std::path::{Path, PathBuf}; + use derive_more::Display; use std::sync::MutexGuard; } @@ -348,8 +349,13 @@ impl MmCtx { /// /// Such directory isn't bound to a specific seed/wallet or address. /// Data that should be stored there is public and shared between all seeds and addresses (e.g. stats, block headers, etc...). - #[cfg(all(feature = "new-db-arch", not(target_arch = "wasm32")))] - pub fn global_dir(&self) -> PathBuf { self.db_root().join("global") } + #[cfg(not(target_arch = "wasm32"))] + pub fn global_dir(&self) -> PathBuf { + if cfg!(not(feature = "new-db-arch")) { + return self.dbdir(); + } + self.db_root().join("global") + } /// Returns the path to wallet's data directory. /// @@ -357,8 +363,11 @@ impl MmCtx { /// For HD wallets, this `rmd160` is derived from `mm2_internal_derivation_path`. /// For Iguana, this `rmd160` is simply a hash of the seed. /// Use this directory to store seed/wallet related data rather than address related data (e.g. HD wallet accounts, HD wallet tx history, etc...) - #[cfg(all(feature = "new-db-arch", not(target_arch = "wasm32")))] + #[cfg(not(target_arch = "wasm32"))] pub fn wallet_dir(&self) -> PathBuf { + if cfg!(not(feature = "new-db-arch")) { + return self.dbdir(); + } self.db_root() .join("wallets") .join(hex::encode(self.rmd160().as_slice())) @@ -368,13 +377,12 @@ impl MmCtx { /// /// Use this directory for data related to a specific address and only that specific address (e.g. swap data, order data, etc...). /// This makes sure that when this address is activated using a different technique, this data is still accessible. - #[cfg(all(feature = "new-db-arch", not(target_arch = "wasm32")))] - pub fn address_dir(&self, address: &str) -> Result { - let path = self.db_root().join("addresses").join(address); - if !path.exists() { - std::fs::create_dir_all(&path).map_err(AddressDataError::CreateAddressDirFailure)?; + #[cfg(not(target_arch = "wasm32"))] + pub fn address_dir(&self, address: &str) -> PathBuf { + if cfg!(not(feature = "new-db-arch")) { + return self.dbdir(); } - Ok(path) + self.db_root().join("addresses").join(address) } /// Returns a SQL connection to the global database. @@ -396,9 +404,10 @@ impl MmCtx { } /// Returns a SQL connection to the address database. - #[cfg(all(feature = "new-db-arch", not(target_arch = "wasm32")))] + #[cfg(not(target_arch = "wasm32"))] pub fn address_db(&self, address: &str) -> Result { - let path = self.address_dir(address)?.join("MM2.db"); + let path = self.address_dir(address).join("MM2.db"); + mm2_io::fs::create_parents(&path).map_err(|err| AddressDataError::CreateAddressDirFailure(err.into_inner()))?; log_sqlite_file_open_attempt(&path); let connection = Connection::open(path).map_err(AddressDataError::SqliteConnectionFailure)?; Ok(connection) @@ -533,7 +542,8 @@ impl Drop for MmCtx { } } -#[cfg(all(feature = "new-db-arch", not(target_arch = "wasm32")))] +#[cfg(not(target_arch = "wasm32"))] +#[derive(Debug, Display)] pub enum AddressDataError { CreateAddressDirFailure(std::io::Error), SqliteConnectionFailure(db_common::sqlite::rusqlite::Error), diff --git a/mm2src/mm2_io/src/file_lock.rs b/mm2src/mm2_io/src/file_lock.rs index 9a2e04bdef..334919a44f 100644 --- a/mm2src/mm2_io/src/file_lock.rs +++ b/mm2src/mm2_io/src/file_lock.rs @@ -4,6 +4,8 @@ use gstuff::now_float; use mm2_err_handle::prelude::*; use std::path::{Path, PathBuf}; +use crate::fs::create_parents; + pub type FileLockResult = std::result::Result>; #[derive(Debug, Display)] @@ -45,6 +47,10 @@ fn read_timestamp(path: &dyn AsRef) -> FileLockResult> { impl> FileLock { pub fn lock(lock_path: T, ttl_sec: f64) -> FileLockResult>> { + create_parents(&lock_path.as_ref()).map_err(|e| FileLockError::ErrorCreatingLockFile { + path: lock_path.as_ref().to_path_buf(), + error: e.to_string(), + })?; match std::fs::OpenOptions::new() .write(true) .create_new(true) diff --git a/mm2src/mm2_io/src/fs.rs b/mm2src/mm2_io/src/fs.rs index 960886a2b2..739e1c950d 100644 --- a/mm2src/mm2_io/src/fs.rs +++ b/mm2src/mm2_io/src/fs.rs @@ -111,11 +111,6 @@ pub async fn remove_file_async>(path: P) -> IoResult<()> { Ok(async_fs::remove_file(path.as_ref()).await?) } -pub fn write(path: &dyn AsRef, contents: &dyn AsRef<[u8]>) -> Result<(), String> { - try_s!(fs::write(path, contents)); - Ok(()) -} - /// Read a folder asynchronously and return a list of files. pub async fn read_dir_async>(dir: P) -> IoResult> { use futures::StreamExt; @@ -276,10 +271,88 @@ where read_files_with_extension(dir_path, "json").await } +/// Creates all the directories along the path to a file if they do not exist. +pub fn create_parents(path: &impl AsRef) -> IoResult<()> { + let parent_dir = path.as_ref().parent(); + let Some(parent_dir) = parent_dir else { + return MmError::err( + io::Error::new( + io::ErrorKind::InvalidInput, + format!("{} has no parent directory", path.as_ref().display()), + )) + }; + match fs::metadata(parent_dir) { + // Path exists, make sure it's a directory (and not a file for example). + Ok(metadata) => { + if !metadata.is_dir() { + return MmError::err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("{} is not a directory", parent_dir.display()), + )); + } + }, + // This path doesn't exist, create it. + Err(_) => fs::create_dir_all(parent_dir)?, + } + Ok(()) +} + +/// Similar to [`create_parents`], but using non-blocking async IO operations. +/// +/// Creates all the directories along the path to a file if they do not exist. +pub async fn create_parents_async(path: &Path) -> IoResult<()> { + let parent_dir = path.parent(); + let Some(parent_dir) = parent_dir else { + return MmError::err( + io::Error::new( + io::ErrorKind::InvalidInput, + format!("{} has no parent directory", path.display()), + )) + }; + match async_fs::metadata(parent_dir).await { + // Path exists, make sure it's a directory (and not a file, for instance). + Ok(metadata) => { + if !metadata.is_dir() { + return MmError::err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("{} is not a directory", parent_dir.display()), + )); + } + }, + // This path doesn't exist, try to create it. + Err(_) => async_fs::create_dir_all(parent_dir).await?, + } + Ok(()) +} + +/// Writes the `content` to the file at `path`. +/// +/// This also creates any intermediary directories up to the file itself if they do not exist. +/// If `use_tmp_file` is true, it writes to a temporary file first and then renames it to the final file name +/// to ensure atomicity. +pub fn write(path: &impl AsRef, content: &[u8], use_tmp_file: bool) -> IoResult<()> { + // Create all the directories in the path. + create_parents(path)?; + let path_tmp = if use_tmp_file { + PathBuf::from(format!("{}.tmp", path.as_ref().display())) + } else { + path.as_ref().to_path_buf() + }; + // Write the file content into the temp file and then rename the temp file into the desired name. + fs::write(&path_tmp, content)?; + if use_tmp_file { + fs::rename(&path_tmp, path.as_ref()).error_log_passthrough()? + } + Ok(()) +} + pub async fn write_json(t: &T, path: &Path, use_tmp_file: bool) -> FsJsonResult<()> where T: Serialize, { + create_parents_async(path) + .await + .map_err(|err| FsJsonError::IoWriting(err.into_inner()))?; let content = json::to_vec(t).map_to_mm(FsJsonError::Serializing)?; let path_tmp = if use_tmp_file { diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index f1a365d38a..fe2717b3d2 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -25,7 +25,7 @@ enable-sia = ["coins/enable-sia", "coins_activation/enable-sia"] sepolia-maker-swap-v2-tests = [] sepolia-taker-swap-v2-tests = [] test-ext-api = ["trading_api/test-ext-api"] -new-db-arch = [] # A temporary feature to integrate the new db architecture incrementally +new-db-arch = ["mm2_core/new-db-arch"] # A temporary feature to integrate the new db architecture incrementally [dependencies] async-std = { version = "1.5", features = ["unstable"] } diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 7f6cf266e8..2c41131927 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -962,10 +962,12 @@ pub struct TransactionIdentifier { } #[cfg(not(target_arch = "wasm32"))] -pub fn my_swaps_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("MY") } +pub fn my_swaps_dir(ctx: &MmArc, address: &str) -> PathBuf { ctx.address_dir(address).join("SWAPS").join("MY") } #[cfg(not(target_arch = "wasm32"))] -pub fn my_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { my_swaps_dir(ctx).join(format!("{}.json", uuid)) } +pub fn my_swap_file_path(ctx: &MmArc, address: &str, uuid: &Uuid) -> PathBuf { + my_swaps_dir(ctx, address).join(format!("{}.json", uuid)) +} pub async fn insert_new_swap_to_db( ctx: MmArc, @@ -1080,7 +1082,7 @@ pub async fn my_swap_status(ctx: MmArc, req: Json) -> Result>, match swap_type { Some(LEGACY_SWAP_TYPE) => { - let status = match SavedSwap::load_my_swap_from_db(&ctx, uuid).await { + let status = match SavedSwap::load_my_swap_from_db(&ctx, None, uuid).await { Ok(Some(status)) => status, Ok(None) => return Err("swap data is not found".to_owned()), Err(e) => return ERR!("{}", e), @@ -1142,7 +1144,7 @@ struct SwapStatus { /// Broadcasts `my` swap status to P2P network async fn broadcast_my_swap_status(ctx: &MmArc, uuid: Uuid) -> Result<(), String> { - let mut status = match try_s!(SavedSwap::load_my_swap_from_db(ctx, uuid).await) { + let mut status = match try_s!(SavedSwap::load_my_swap_from_db(ctx, None, uuid).await) { Some(status) => status, None => return ERR!("swap data is not found"), }; @@ -1251,7 +1253,7 @@ pub async fn latest_swaps_for_pair( let mut swaps = Vec::with_capacity(db_result.uuids_and_types.len()); // TODO this is needed for trading bot, which seems not used as of now. Remove the code? for (uuid, _) in db_result.uuids_and_types.iter() { - let swap = match SavedSwap::load_my_swap_from_db(&ctx, *uuid).await { + let swap = match SavedSwap::load_my_swap_from_db(&ctx, None, *uuid).await { Ok(Some(swap)) => swap, Ok(None) => { error!("No such swap with the uuid '{}'", uuid); @@ -1278,7 +1280,7 @@ pub async fn my_recent_swaps_rpc(ctx: MmArc, req: Json) -> Result match SavedSwap::load_my_swap_from_db(&ctx, *uuid).await { + LEGACY_SWAP_TYPE => match SavedSwap::load_my_swap_from_db(&ctx, None, *uuid).await { Ok(Some(swap)) => { let swap_json = try_s!(json::to_value(MySwapStatusResponse::from(swap))); swaps.push(swap_json) @@ -1329,7 +1331,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let mut coins = HashSet::new(); let legacy_unfinished_uuids = try_s!(get_unfinished_swaps_uuids(ctx.clone(), LEGACY_SWAP_TYPE).await); for uuid in legacy_unfinished_uuids { - let swap = match SavedSwap::load_my_swap_from_db(&ctx, uuid).await { + let swap = match SavedSwap::load_my_swap_from_db(&ctx, None, uuid).await { Ok(Some(s)) => s, Ok(None) => { warn!("Swap {} is indexed, but doesn't exist in DB", uuid); @@ -1477,7 +1479,7 @@ pub async fn coins_needed_for_kick_start(ctx: MmArc) -> Result> pub async fn recover_funds_of_swap(ctx: MmArc, req: Json) -> Result>, String> { let uuid: Uuid = try_s!(json::from_value(req["params"]["uuid"].clone())); - let swap = match SavedSwap::load_my_swap_from_db(&ctx, uuid).await { + let swap = match SavedSwap::load_my_swap_from_db(&ctx, None, uuid).await { Ok(Some(swap)) => swap, Ok(None) => return ERR!("swap data is not found"), Err(e) => return ERR!("{}", e), @@ -1554,7 +1556,7 @@ pub async fn active_swaps_rpc(ctx: MmArc, req: Json) -> Result> for (uuid, swap_type) in uuids_with_types.iter() { match *swap_type { LEGACY_SWAP_TYPE => { - let status = match SavedSwap::load_my_swap_from_db(&ctx, *uuid).await { + let status = match SavedSwap::load_my_swap_from_db(&ctx, None, *uuid).await { Ok(Some(status)) => status, Ok(None) => continue, Err(e) => { @@ -2240,7 +2242,7 @@ mod lp_swap_tests { uuid, None, conf_settings, - rick_maker.into(), + rick_maker.clone().into(), morty_maker.into(), lock_duration, None, @@ -2261,7 +2263,7 @@ mod lp_swap_tests { uuid, None, conf_settings, - rick_taker.into(), + rick_taker.clone().into(), morty_taker.into(), lock_duration, None, @@ -2274,15 +2276,18 @@ mod lp_swap_tests { run_taker_swap(RunTakerSwapInput::StartNew(taker_swap), taker_ctx.clone()), )); + let makers_maker_coin_address = rick_maker.my_address().unwrap(); + let takers_maker_coin_address = rick_taker.my_address().unwrap(); + println!( "Maker swap path {}", - std::fs::canonicalize(my_swap_file_path(&maker_ctx, &uuid)) + std::fs::canonicalize(my_swap_file_path(&maker_ctx, &makers_maker_coin_address, &uuid)) .unwrap() .display() ); println!( "Taker swap path {}", - std::fs::canonicalize(my_swap_file_path(&taker_ctx, &uuid)) + std::fs::canonicalize(my_swap_file_path(&taker_ctx, &takers_maker_coin_address, &uuid)) .unwrap() .display() ); diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index c7e5a43329..7ec3a86245 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -80,7 +80,7 @@ pub const MAKER_ERROR_EVENTS: [&str; 15] = [ pub const MAKER_PAYMENT_SENT_LOG: &str = "Maker payment sent"; #[cfg(not(target_arch = "wasm32"))] -pub fn stats_maker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("STATS").join("MAKER") } +pub fn stats_maker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.global_dir().join("SWAPS").join("STATS").join("MAKER") } #[cfg(not(target_arch = "wasm32"))] pub fn stats_maker_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { @@ -88,10 +88,14 @@ pub fn stats_maker_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { } async fn save_my_maker_swap_event(ctx: &MmArc, swap: &MakerSwap, event: MakerSavedEvent) -> Result<(), String> { - let swap = match SavedSwap::load_my_swap_from_db(ctx, swap.uuid).await { + let maker_coin_pub = swap.my_maker_coin_htlc_pub(); + let maker_coin_address = try_s!(swap.maker_coin.address_from_pubkey(&maker_coin_pub)); + let swap = match SavedSwap::load_my_swap_from_db(ctx, Some(&maker_coin_address), swap.uuid).await { Ok(Some(swap)) => swap, Ok(None) => SavedSwap::Maker(MakerSavedSwap { uuid: swap.uuid, + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + maker_address: maker_coin_address, my_order_uuid: swap.my_order_uuid, maker_amount: Some(swap.maker_amount.clone()), maker_coin: Some(swap.maker_coin.ticker().to_owned()), @@ -1360,7 +1364,7 @@ impl MakerSwap { taker_coin: MmCoinEnum, swap_uuid: &Uuid, ) -> Result<(Self, Option), String> { - let saved = match SavedSwap::load_my_swap_from_db(&ctx, *swap_uuid).await { + let saved = match SavedSwap::load_my_swap_from_db(&ctx, None, *swap_uuid).await { Ok(Some(saved)) => saved, Ok(None) => return ERR!("Couldn't find a swap with the uuid '{}'", swap_uuid), Err(e) => return ERR!("{}", e), @@ -1855,6 +1859,8 @@ impl MakerSwapStatusChanged { #[derive(Debug, Default, PartialEq, Serialize, Deserialize)] pub struct MakerSavedSwap { pub uuid: Uuid, + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + pub maker_address: String, pub my_order_uuid: Option, pub events: Vec, pub maker_amount: Option, @@ -1911,6 +1917,8 @@ impl MakerSavedSwap { MakerSavedSwap { uuid: Default::default(), + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + maker_address: "".to_string(), my_order_uuid: None, events, maker_amount: Some(maker_amount.to_decimal()), diff --git a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs index e389597f92..3d9edf5c69 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -73,15 +73,18 @@ pub async fn recreate_swap_data(ctx: MmArc, args: RecreateSwapRequest) -> Recrea }, InputSwap::SavedSwap(SavedSwap::Taker(taker_swap)) | InputSwap::TakerSavedSwap(taker_swap) => { recreate_maker_swap(ctx, taker_swap) + .await .map(SavedSwap::from) .map(|swap| RecreateSwapResponse { swap }) }, } } -fn recreate_maker_swap(ctx: MmArc, taker_swap: TakerSavedSwap) -> RecreateSwapResult { +async fn recreate_maker_swap(ctx: MmArc, taker_swap: TakerSavedSwap) -> RecreateSwapResult { let mut maker_swap = MakerSavedSwap { uuid: taker_swap.uuid, + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + maker_address: String::new(), my_order_uuid: taker_swap.my_order_uuid, events: Vec::new(), maker_amount: taker_swap.maker_amount, @@ -121,7 +124,7 @@ fn recreate_maker_swap(ctx: MmArc, taker_swap: TakerSavedSwap) -> RecreateSwapRe taker_p2p_pubkey.copy_from_slice(&started_event.my_persistent_pub.0[1..33]); let maker_started_event = MakerSwapEvent::Started(MakerSwapData { taker_coin: started_event.taker_coin, - maker_coin: started_event.maker_coin, + maker_coin: started_event.maker_coin.clone(), taker_pubkey: H256Json::from(taker_p2p_pubkey), // We could parse the `TakerSwapEvent::TakerPaymentSpent` event. // As for now, don't try to find the secret in the events since we can refund without it. @@ -176,6 +179,23 @@ fn recreate_maker_swap(ctx: MmArc, taker_swap: TakerSavedSwap) -> RecreateSwapRe .events .extend(convert_taker_to_maker_events(event_it, wait_refund_until)); + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + { + // TODO(new-db-arch): Execute this plan: https://github.com/KomodoPlatform/komodo-defi-framework/pull/2398#discussion_r2036035916 + // instead of making the maker_address/address_dir available for the importer (i.e. let them find it themselves). + let maker_coin_ticker = started_event.maker_coin; + let maker_coin = lp_coinfind(&ctx, &maker_coin_ticker) + .await + .map_to_mm(RecreateSwapError::Internal)? + .or_mm_err(move || RecreateSwapError::NoSuchCoin { + coin: maker_coin_ticker, + })?; + maker_swap.maker_address = negotiated_event + .maker_coin_htlc_pubkey + .and_then(|pubkey| maker_coin.address_from_pubkey(&pubkey).ok()) + .unwrap_or("Couldn't get the maker coin address. Please set it manually.".to_string()); + } + Ok(maker_swap) } @@ -285,6 +305,8 @@ fn convert_taker_to_maker_events( async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> RecreateSwapResult { let mut taker_swap = TakerSavedSwap { uuid: maker_swap.uuid, + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + maker_address: String::new(), my_order_uuid: Some(maker_swap.uuid), events: Vec::new(), maker_amount: maker_swap.maker_amount, @@ -382,6 +404,14 @@ async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> Recreate coin: maker_coin_ticker, })?; + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + { + taker_swap.maker_address = negotiated_event + .maker_coin_htlc_pubkey + .and_then(|pubkey| maker_coin.address_from_pubkey(&pubkey).ok()) + .unwrap_or("Couldn't get the maker coin address. Please set it manually.".to_string()); + } + // Then we can continue to process success Maker events. let wait_refund_until = negotiated_event.taker_payment_locktime + 3700; taker_swap @@ -509,7 +539,7 @@ mod tests { let ctx = MmCtxBuilder::default().into_mm_arc(); - let maker_actual_swap = recreate_maker_swap(ctx, taker_saved_swap).expect("!recreate_maker_swap"); + let maker_actual_swap = block_on(recreate_maker_swap(ctx, taker_saved_swap)).expect("!recreate_maker_swap"); println!("{}", json::to_string(&maker_actual_swap).unwrap()); assert_eq!(maker_actual_swap, maker_expected_swap); } @@ -527,7 +557,7 @@ mod tests { let ctx = MmCtxBuilder::default().into_mm_arc(); - let maker_actual_swap = recreate_maker_swap(ctx, taker_saved_swap).expect("!recreate_maker_swap"); + let maker_actual_swap = block_on(recreate_maker_swap(ctx, taker_saved_swap)).expect("!recreate_maker_swap"); println!("{}", json::to_string(&maker_actual_swap).unwrap()); assert_eq!(maker_actual_swap, maker_expected_swap); } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 185edd1584..158843dadb 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -75,6 +75,14 @@ impl SavedSwap { } } + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + pub fn maker_address(&self) -> &str { + match self { + SavedSwap::Maker(swap) => &swap.maker_address, + SavedSwap::Taker(swap) => &swap.maker_address, + } + } + pub fn maker_coin_ticker(&self) -> Result { match self { SavedSwap::Maker(swap) => swap.maker_coin(), @@ -159,7 +167,11 @@ impl SavedSwap { #[async_trait] pub trait SavedSwapIo { - async fn load_my_swap_from_db(ctx: &MmArc, uuid: Uuid) -> SavedSwapResult>; + async fn load_my_swap_from_db( + ctx: &MmArc, + address_dir: Option<&str>, + uuid: Uuid, + ) -> SavedSwapResult>; async fn load_all_my_swaps_from_db(ctx: &MmArc) -> SavedSwapResult>; @@ -208,13 +220,30 @@ mod native_impl { #[async_trait] impl SavedSwapIo for SavedSwap { - async fn load_my_swap_from_db(ctx: &MmArc, uuid: Uuid) -> SavedSwapResult> { - let path = my_swap_file_path(ctx, &uuid); + async fn load_my_swap_from_db( + ctx: &MmArc, + address_dir: Option<&str>, + uuid: Uuid, + ) -> SavedSwapResult> { + // TODO(new-db-arch): Set the correct address directory for the new db arch branch (via a query to the global DB). + #[cfg(feature = "new-db-arch")] + let address_dir = address_dir.unwrap_or("Fetch the address directory from the global DB given the UUID."); + #[cfg(not(feature = "new-db-arch"))] + let address_dir = address_dir.unwrap_or("no address directory for old DB architecture (has no effect)"); + let path = my_swap_file_path(ctx, address_dir, &uuid); Ok(read_json(&path).await?) } + #[cfg_attr(feature = "new-db-arch", allow(unreachable_code, unused_variables))] async fn load_all_my_swaps_from_db(ctx: &MmArc) -> SavedSwapResult> { - let path = my_swaps_dir(ctx); + #[cfg(feature = "new-db-arch")] + { + // This method is solely used for migrations. Which we should ditch or refactor with the new DB architecture. + // If we ditch the old migrations, this method should never be called (and should be deleted when we are + // done with the incremental architecture change). + todo!("Fix the dummy address directory in `my_swaps_dir` below or remove this method all together"); + } + let path = my_swaps_dir(ctx, "has no effect in not(feature = 'new-db-arch')"); Ok(read_dir_json(&path).await?) } @@ -239,7 +268,11 @@ mod native_impl { } async fn save_to_db(&self, ctx: &MmArc) -> SavedSwapResult<()> { - let path = my_swap_file_path(ctx, self.uuid()); + #[cfg(feature = "new-db-arch")] + let address_dir = self.maker_address(); + #[cfg(not(feature = "new-db-arch"))] + let address_dir = "no address directory for old DB architecture (has no effect)"; + let path = my_swap_file_path(ctx, address_dir, self.uuid()); write_json(self, &path, USE_TMP_FILE).await?; Ok(()) } @@ -376,7 +409,11 @@ mod wasm_impl { #[async_trait] impl SavedSwapIo for SavedSwap { - async fn load_my_swap_from_db(ctx: &MmArc, uuid: Uuid) -> SavedSwapResult> { + async fn load_my_swap_from_db( + ctx: &MmArc, + _address_dir: Option<&str>, + uuid: Uuid, + ) -> SavedSwapResult> { let swaps_ctx = SwapsContext::from_ctx(ctx).map_to_mm(SavedSwapError::InternalError)?; let db = swaps_ctx.swap_db().await?; let transaction = db.transaction().await?; @@ -486,7 +523,7 @@ mod tests { assert_eq!(item, second_saved_item); } - let actual_saved_swap = SavedSwap::load_my_swap_from_db(&ctx, *saved_swap.uuid()) + let actual_saved_swap = SavedSwap::load_my_swap_from_db(&ctx, None, *saved_swap.uuid()) .await .expect("!load_from_db") .expect("Swap not found"); diff --git a/mm2src/mm2_main/src/lp_swap/swap_lock.rs b/mm2src/mm2_main/src/lp_swap/swap_lock.rs index f4347dde3b..38591deeff 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_lock.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_lock.rs @@ -32,7 +32,6 @@ pub trait SwapLockOps: Sized { #[cfg(not(target_arch = "wasm32"))] mod native_lock { use super::*; - use crate::lp_swap::my_swaps_dir; use mm2_io::file_lock::{FileLock, FileLockError}; use std::path::PathBuf; @@ -57,7 +56,14 @@ mod native_lock { #[async_trait] impl SwapLockOps for SwapLock { async fn lock(ctx: &MmArc, swap_uuid: Uuid, ttl_sec: f64) -> SwapLockResult> { - let lock_path = my_swaps_dir(ctx).join(format!("{}.lock", swap_uuid)); + let lock_path = if cfg!(feature = "new-db-arch") { + ctx.global_dir().join("swap_locks").join(format!("{}.lock", swap_uuid)) + } else { + ctx.global_dir() + .join("SWAPS") + .join("MY") + .join(format!("{}.lock", swap_uuid)) + }; let file_lock = some_or_return_ok_none!(FileLock::lock(lock_path, ttl_sec)?); Ok(Some(SwapLock { file_lock })) diff --git a/mm2src/mm2_main/src/lp_swap/swap_v2_rpcs.rs b/mm2src/mm2_main/src/lp_swap/swap_v2_rpcs.rs index 669d17a492..92a305f252 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_v2_rpcs.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_v2_rpcs.rs @@ -301,7 +301,7 @@ async fn get_swap_data_by_uuid_and_type( ) -> MmResult, GetSwapDataErr> { match swap_type { LEGACY_SWAP_TYPE => { - let saved_swap = SavedSwap::load_my_swap_from_db(ctx, uuid).await?; + let saved_swap = SavedSwap::load_my_swap_from_db(ctx, None, uuid).await?; Ok(saved_swap.map(|swap| match swap { SavedSwap::Maker(m) => SwapRpcData::MakerV1(m), SavedSwap::Taker(t) => SwapRpcData::TakerV1(t), diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 54f5ab3bfe..08657cc572 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -103,7 +103,7 @@ pub const WATCHER_MESSAGE_SENT_LOG: &str = "Watcher message sent..."; pub const MAKER_PAYMENT_SPENT_BY_WATCHER_LOG: &str = "Maker payment is spent by the watcher..."; #[cfg(not(target_arch = "wasm32"))] -pub fn stats_taker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("STATS").join("TAKER") } +pub fn stats_taker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.global_dir().join("SWAPS").join("STATS").join("TAKER") } #[cfg(not(target_arch = "wasm32"))] pub fn stats_taker_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { @@ -111,10 +111,14 @@ pub fn stats_taker_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { } async fn save_my_taker_swap_event(ctx: &MmArc, swap: &TakerSwap, event: TakerSavedEvent) -> Result<(), String> { - let swap = match SavedSwap::load_my_swap_from_db(ctx, swap.uuid).await { + let maker_coin_pub = swap.my_maker_coin_htlc_pub(); + let maker_coin_address = try_s!(swap.maker_coin.address_from_pubkey(&maker_coin_pub)); + let swap = match SavedSwap::load_my_swap_from_db(ctx, Some(&maker_coin_address), swap.uuid).await { Ok(Some(swap)) => swap, Ok(None) => SavedSwap::Taker(TakerSavedSwap { uuid: swap.uuid, + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + maker_address: maker_coin_address, my_order_uuid: swap.my_order_uuid, maker_amount: Some(swap.maker_amount.to_decimal()), maker_coin: Some(swap.maker_coin.ticker().to_owned()), @@ -204,6 +208,8 @@ impl TakerSavedEvent { #[derive(Debug, Deserialize, PartialEq, Serialize)] pub struct TakerSavedSwap { pub uuid: Uuid, + #[cfg(all(not(target_arch = "wasm32"), feature = "new-db-arch"))] + pub maker_address: String, pub my_order_uuid: Option, pub events: Vec, pub maker_amount: Option, @@ -2085,7 +2091,7 @@ impl TakerSwap { taker_coin: MmCoinEnum, swap_uuid: &Uuid, ) -> Result<(Self, Option), String> { - let saved = match SavedSwap::load_my_swap_from_db(&ctx, *swap_uuid).await { + let saved = match SavedSwap::load_my_swap_from_db(&ctx, None, *swap_uuid).await { Ok(Some(saved)) => saved, Ok(None) => return ERR!("Couldn't find a swap with the uuid '{}'", swap_uuid), Err(e) => return ERR!("{}", e), diff --git a/mm2src/mm2_main/tests/docker_tests/z_coin_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/z_coin_docker_tests.rs index 9abcfbe506..f9cd8e99af 100644 --- a/mm2src/mm2_main/tests/docker_tests/z_coin_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/z_coin_docker_tests.rs @@ -7,9 +7,7 @@ use common::now_sec; use lazy_static::lazy_static; use mm2_core::mm_ctx::{MmArc, MmCtxBuilder}; use mm2_number::MmNumber; -use mm2_test_helpers::for_tests::{new_mm2_temp_folder_path, zombie_conf_for_docker}; -use rand::distributions::Alphanumeric; -use rand::{thread_rng, Rng}; +use mm2_test_helpers::for_tests::zombie_conf_for_docker; use tokio::sync::Mutex; // https://github.com/KomodoPlatform/librustzcash/blob/4e030a0f44cc17f100bf5f019563be25c5b8755f/zcash_client_backend/src/data_api/wallet.rs#L72-L73 @@ -26,13 +24,6 @@ pub async fn z_coin_from_spending_key(spending_key: &str) -> (MmArc, ZCoin) { ..Default::default() }; let pk_data = [1; 32]; - let salt: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(4) - .map(char::from) - .collect(); - let db_folder = new_mm2_temp_folder_path(None).join(format!("ZOMBIE_DB_{}", salt)); - std::fs::create_dir_all(&db_folder).unwrap(); let protocol_info = match serde_json::from_value::(conf["protocol"].take()).unwrap() { CoinProtocol::ZHTLC(protocol_info) => protocol_info, other_protocol => panic!("Failed to get protocol from config: {:?}", other_protocol), @@ -44,7 +35,6 @@ pub async fn z_coin_from_spending_key(spending_key: &str) -> (MmArc, ZCoin) { &conf, ¶ms, PrivKeyBuildPolicy::IguanaPrivKey(pk_data.into()), - db_folder, protocol_info, spending_key, )