diff --git a/.gitignore b/.gitignore index 56e7a26582..47d4ca0e5e 100755 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ scripts/mm2/seed/unparsed.txt # Private IDE configuration /mm2src/*/.vscode/tasks.json /mm2src/*/.vscode/c_cpp_properties.json +/mm2src/*/Cargo.lock /.vscode/settings.json /.vscode/tasks.json /.vscode/c_cpp_properties.json diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 880c60f90d..6a44bf2ddb 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -4,8 +4,6 @@ pub mod tx_history_events; mod tx_streaming_tests; pub mod z_balance_streaming; mod z_coin_errors; -#[cfg(all(test, not(target_arch = "wasm32"), feature = "zhtlc-native-tests"))] -mod z_coin_native_tests; mod z_htlc; mod z_rpc; mod z_tx_history; @@ -72,7 +70,8 @@ use std::num::TryFromIntError; use std::path::PathBuf; use std::sync::Arc; pub use z_coin_errors::*; -use z_htlc::{z_p2sh_spend, z_send_dex_fee, z_send_htlc}; +pub use z_htlc::z_send_dex_fee; +use z_htlc::{z_p2sh_spend, z_send_htlc}; use z_rpc::init_light_client; pub use z_rpc::{FirstSyncBlock, SyncStatus}; use z_rpc::{SaplingSyncConnector, SaplingSyncGuard}; @@ -91,8 +90,6 @@ use zcash_primitives::{constants::mainnet as z_mainnet_constants, sapling::Payme zip32::ExtendedFullViewingKey, zip32::ExtendedSpendingKey}; use zcash_proofs::prover::LocalTxProver; -use self::storage::store_change_output; - cfg_native!( use common::{async_blocking, sha256_digest}; use zcash_client_sqlite::error::SqliteClientError as ZcashClientError; @@ -282,12 +279,19 @@ impl ZCoin { /// Asynchronously checks the synchronization status and returns `true` if /// the Sapling state has finished synchronizing, meaning that the block number is available. /// Otherwise, it returns `false`. + #[cfg(any(test, feature = "run-docker-tests"))] #[inline] pub async fn is_sapling_state_synced(&self) -> bool { - matches!( - self.sync_status().await, - Ok(SyncStatus::Finished { block_number: _, .. }) - ) + use futures::StreamExt; + + let mut watcher = self.z_fields.sync_state_connector.lock().await; + while let Some(sync) = watcher.sync_watcher.next().await { + if matches!(sync, SyncStatus::Finished { .. }) { + return true; + } + } + + false } #[inline] @@ -475,12 +479,6 @@ impl ZCoin { .await? .tx_result?; - // Store any change outputs we created in this transaction by decrypting them with our keys - // and saving them to the wallet database for future spends - store_change_output(self.consensus_params_ref(), &self.z_fields.light_wallet_db, &tx) - .await - .map_to_mm(GenTxError::SaveChangeNotesError)?; - let additional_data = AdditionalTxData { received_by_me, spent_by_me: sat_from_big_decimal(&total_input_amount, self.decimals())?, @@ -1079,7 +1077,6 @@ impl<'a> ZCoinBuilder<'a> { .await .mm_err(|err| ZCoinBuildError::ZCashParamsError(err.to_string()))? { - // save params params_db .download_and_save_params() .await @@ -1096,21 +1093,27 @@ impl<'a> ZCoinBuilder<'a> { } } -/// Initialize `ZCoin` with a forced `z_spending_key`. +/// Initialize `ZCoin` with a forced `z_spending_key` for dockerized tests. /// db_dir_path is where ZOMBIE_wallet.db located /// Note that ZOMBIE_cache.db (db where blocks are downloaded to create ZOMBIE_wallet.db) is created in-memory (see BlockDbImpl::new fn) -#[cfg(all(test, feature = "zhtlc-native-tests"))] +#[cfg(any(test, feature = "run-docker-tests"))] #[allow(clippy::too_many_arguments)] -async fn z_coin_from_conf_and_params_with_z_key( +pub async fn z_coin_from_conf_and_params_with_docker( ctx: &MmArc, ticker: &str, conf: &Json, params: &ZcoinActivationParams, priv_key_policy: PrivKeyBuildPolicy, db_dir_path: PathBuf, - z_spending_key: ExtendedSpendingKey, protocol_info: ZcoinProtocolInfo, + spending_key: &str, ) -> Result> { + use zcash_client_backend::encoding::decode_extended_spending_key; + let z_spending_key = + decode_extended_spending_key(z_mainnet_constants::HRP_SAPLING_EXTENDED_SPENDING_KEY, spending_key) + .unwrap() + .unwrap(); + let builder = ZCoinBuilder::new( ctx, ticker, diff --git a/mm2src/coins/z_coin/storage.rs b/mm2src/coins/z_coin/storage.rs index dd3042a169..e2534281b7 100644 --- a/mm2src/coins/z_coin/storage.rs +++ b/mm2src/coins/z_coin/storage.rs @@ -10,8 +10,6 @@ pub mod walletdb; pub(crate) use z_params::ZcashParamsWasmImpl; pub use walletdb::*; -use zcash_extras::wallet::decrypt_and_store_transaction; -use zcash_primitives::transaction::Transaction; use mm2_err_handle::mm_error::MmResult; #[cfg(target_arch = "wasm32")] @@ -190,18 +188,3 @@ pub async fn scan_cached_block( Ok(txs) } - -/// Processes and stores any change outputs created in the transaction by: -/// - Decrypting outputs using wallet viewing keys -/// - Adding decrypted change notes to the wallet database -/// - Making change notes available for future spends -pub(crate) async fn store_change_output( - params: &ZcoinConsensusParams, - shared_db: &WalletDbShared, - tx: &Transaction, -) -> Result<(), String> { - let mut data = try_s!(shared_db.db.get_update_ops()); - try_s!(decrypt_and_store_transaction(params, &mut data, tx).await); - - Ok(()) -} diff --git a/mm2src/coins/z_coin/tx_streaming_tests/native.rs b/mm2src/coins/z_coin/tx_streaming_tests/native.rs index b2b9983661..f4bc2849dc 100644 --- a/mm2src/coins/z_coin/tx_streaming_tests/native.rs +++ b/mm2src/coins/z_coin/tx_streaming_tests/native.rs @@ -1,15 +1,15 @@ -use common::custom_futures::timeout::FutureTimerExt; -use common::{block_on, Future01CompatExt}; -use mm2_core::mm_ctx::MmCtxBuilder; -use mm2_test_helpers::for_tests::{pirate_conf, ARRR}; -use std::time::Duration; - use super::light_zcoin_activation_params; use crate::z_coin::tx_history_events::ZCoinTxHistoryEventStreamer; use crate::z_coin::z_coin_from_conf_and_params; use crate::z_coin::z_htlc::z_send_dex_fee; use crate::{CoinProtocol, DexFee, MarketCoinOps, MmCoin, PrivKeyBuildPolicy}; +use common::custom_futures::timeout::FutureTimerExt; +use common::{block_on, Future01CompatExt}; +use mm2_core::mm_ctx::MmCtxBuilder; +use mm2_test_helpers::for_tests::{pirate_conf, ARRR}; +use std::time::Duration; + #[test] #[ignore] // Ignored because we don't have zcash params in CI. TODO: Why not download them on demand like how we do in wasm (see download_and_save_params). fn test_zcoin_tx_streaming() { diff --git a/mm2src/coins/z_coin/z_coin_errors.rs b/mm2src/coins/z_coin/z_coin_errors.rs index f7e3d65a5d..3c6e44a32a 100644 --- a/mm2src/coins/z_coin/z_coin_errors.rs +++ b/mm2src/coins/z_coin/z_coin_errors.rs @@ -130,7 +130,6 @@ pub enum GenTxError { FailedToCreateNote, SpendableNotesError(String), Internal(String), - SaveChangeNotesError(String), } impl From for GenTxError { @@ -178,8 +177,7 @@ impl From for WithdrawError { | GenTxError::LightClientErr(_) | GenTxError::SpendableNotesError(_) | GenTxError::FailedToCreateNote - | GenTxError::Internal(_) - | GenTxError::SaveChangeNotesError(_) => WithdrawError::InternalError(gen_tx.to_string()), + | GenTxError::Internal(_) => WithdrawError::InternalError(gen_tx.to_string()), } } } diff --git a/mm2src/coins/z_coin/z_htlc.rs b/mm2src/coins/z_coin/z_htlc.rs index 13093b80b0..ffda1aba42 100644 --- a/mm2src/coins/z_coin/z_htlc.rs +++ b/mm2src/coins/z_coin/z_htlc.rs @@ -5,7 +5,6 @@ // taker payment spend - https://zombie.explorer.lordofthechains.com/tx/af6bb0f99f9a5a070a0c1f53d69e4189b0e9b68f9d66e69f201a6b6d9f93897e // maker payment spend - https://rick.explorer.dexstats.info/tx/6a2dcc866ad75cebecb780a02320073a88bcf5e57ddccbe2657494e7747d591e -use super::storage::store_change_output; use super::{GenTxError, ZCoin}; use crate::utxo::rpc_clients::{UtxoRpcClientEnum, UtxoRpcError}; use crate::utxo::utxo_common::payment_script; @@ -208,12 +207,6 @@ pub async fn z_p2sh_spend( let mut tx_buffer = Vec::with_capacity(1024); zcash_tx.write(&mut tx_buffer)?; - // Store any change outputs we created in this transaction by decrypting them with our keys - // and saving them to the wallet database for future spends - store_change_output(coin.consensus_params_ref(), &coin.z_fields.light_wallet_db, &zcash_tx) - .await - .map_to_mm(|err| ZP2SHSpendError::GenTxError(GenTxError::SaveChangeNotesError(err)))?; - coin.utxo_rpc_client() .send_raw_transaction(tx_buffer.into()) .compat() diff --git a/mm2src/coins/z_coin/z_rpc.rs b/mm2src/coins/z_coin/z_rpc.rs index 91d897d126..40eef387c0 100644 --- a/mm2src/coins/z_coin/z_rpc.rs +++ b/mm2src/coins/z_coin/z_rpc.rs @@ -944,7 +944,7 @@ type SyncWatcher = AsyncReceiver; type NewTxNotifier = AsyncSender)>>; pub(super) struct SaplingSyncConnector { - sync_watcher: SyncWatcher, + pub(super) sync_watcher: SyncWatcher, on_tx_gen_notifier: NewTxNotifier, abort_handle: Arc>, first_sync_block: FirstSyncBlock, diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index 3199ed0cd3..0e208a1f86 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -1,3 +1,4 @@ +use coins::z_coin::ZCoin; pub use common::{block_on, block_on_f01, now_ms, now_sec, wait_until_ms, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; pub use mm2_number::MmNumber; use mm2_rpc::data::legacy::BalanceResponse; @@ -8,6 +9,7 @@ pub use mm2_test_helpers::for_tests::{check_my_swap_status, check_recent_swaps, TAKER_SUCCESS_EVENTS}; use super::eth_docker_tests::{erc20_contract_checksum, fill_eth, fill_eth_erc20_with_private_key, swap_contract}; +use super::z_coin_docker_tests::z_coin_from_spending_key; use bitcrypto::{dhash160, ChecksumType}; use chain::TransactionOutput; use coins::eth::addr_from_raw_pubkey; @@ -57,6 +59,7 @@ lazy_static! { static ref MY_COIN1_LOCK: Mutex<()> = Mutex::new(()); static ref QTUM_LOCK: Mutex<()> = Mutex::new(()); static ref FOR_SLP_LOCK: Mutex<()> = Mutex::new(()); + static ref ZOMBIE_LOCK: Mutex<()> = Mutex::new(()); pub static ref SLP_TOKEN_ID: Mutex = Mutex::new(H256::default()); // Private keys supplied with 1000 SLP tokens on tests initialization. // Due to the SLP protocol limitations only 19 outputs (18 + change) can be sent in one transaction, which is sufficient for now though. @@ -126,6 +129,9 @@ pub const UTXO_ASSET_DOCKER_IMAGE: &str = "docker.io/artempikulin/testblockchain pub const UTXO_ASSET_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/artempikulin/testblockchain:multiarch"; pub const GETH_DOCKER_IMAGE: &str = "docker.io/ethereum/client-go"; pub const GETH_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/ethereum/client-go:stable"; +pub const ZOMBIE_ASSET_DOCKER_IMAGE: &str = "docker.io/borngraced/zombietestrunner"; +pub const ZOMBIE_ASSET_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/borngraced/zombietestrunner:multiarch"; + #[allow(dead_code)] pub const SIA_DOCKER_IMAGE: &str = "docker.io/alrighttt/walletd-komodo"; #[allow(dead_code)] @@ -221,6 +227,24 @@ impl UtxoAssetDockerOps { } } +pub struct ZCoinAssetDockerOps { + #[allow(dead_code)] + ctx: MmArc, + coin: ZCoin, +} + +impl CoinDockerOps for ZCoinAssetDockerOps { + fn rpc_client(&self) -> &UtxoRpcClientEnum { &self.coin.as_ref().rpc_client } +} + +impl ZCoinAssetDockerOps { + pub fn new() -> ZCoinAssetDockerOps { + let (ctx, coin) = block_on(z_coin_from_spending_key("secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe")); + + ZCoinAssetDockerOps { ctx, coin } + } +} + pub struct BchDockerOps { #[allow(dead_code)] ctx: MmArc, @@ -463,6 +487,38 @@ pub fn ibc_relayer_node(docker: &'_ Cli, runtime_dir: PathBuf) -> DockerNode<'_> } } +pub fn zombie_asset_docker_node(docker: &Cli, port: u16) -> DockerNode<'_> { + let image = GenericImage::new(ZOMBIE_ASSET_DOCKER_IMAGE, "multiarch") + .with_volume(zcash_params_path().display().to_string(), "/root/.zcash-params") + .with_env_var("COIN_RPC_PORT", port.to_string()) + .with_wait_for(WaitFor::message_on_stdout("config is ready")); + + let image = RunnableImage::from(image).with_mapped_port((port, port)); + let container = docker.run(image); + let config_ticker = "ZOMBIE"; + let mut conf_path = coin_daemon_data_dir(config_ticker, true); + + std::fs::create_dir_all(&conf_path).unwrap(); + conf_path.push(format!("{}.conf", config_ticker)); + Command::new("docker") + .arg("cp") + .arg(format!("{}:/data/node_0/{}.conf", container.id(), config_ticker)) + .arg(&conf_path) + .status() + .expect("Failed to execute docker command"); + + let timeout = wait_until_ms(3000); + while !conf_path.exists() { + assert!(now_ms() < timeout, "Test timed out"); + } + + DockerNode { + container, + ticker: config_ticker.into(), + port, + } +} + pub fn rmd160_from_priv(privkey: Secp256k1Secret) -> H160 { let secret = SecretKey::from_slice(privkey.as_slice()).unwrap(); let public = PublicKey::from_secret_key(&Secp256k1::new(), &secret); diff --git a/mm2src/mm2_main/tests/docker_tests/mod.rs b/mm2src/mm2_main/tests/docker_tests/mod.rs index 4b969b9fec..8dce2cdfb8 100644 --- a/mm2src/mm2_main/tests/docker_tests/mod.rs +++ b/mm2src/mm2_main/tests/docker_tests/mod.rs @@ -11,6 +11,7 @@ mod swap_watcher_tests; mod swaps_confs_settings_sync_tests; mod swaps_file_lock_tests; mod tendermint_tests; +mod z_coin_docker_tests; // dummy test helping IDE to recognize this as test module #[test] 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 new file mode 100644 index 0000000000..9abcfbe506 --- /dev/null +++ b/mm2src/mm2_main/tests/docker_tests/z_coin_docker_tests.rs @@ -0,0 +1,238 @@ +use bitcrypto::dhash160; +use coins::z_coin::{z_coin_from_conf_and_params_with_docker, z_send_dex_fee, ZCoin, ZcoinActivationParams, + ZcoinRpcMode}; +use coins::{coin_errors::ValidatePaymentError, CoinProtocol, DexFee, PrivKeyBuildPolicy, RefundPaymentArgs, + SendPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash, ValidateFeeArgs}; +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 tokio::sync::Mutex; + +// https://github.com/KomodoPlatform/librustzcash/blob/4e030a0f44cc17f100bf5f019563be25c5b8755f/zcash_client_backend/src/data_api/wallet.rs#L72-L73 +lazy_static! { + static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); +} + +/// Build asset `ZCoin` from ticker and spendingkey str without filling the balance. +pub async fn z_coin_from_spending_key(spending_key: &str) -> (MmArc, ZCoin) { + let ctx = MmCtxBuilder::new().into_mm_arc(); + let mut conf = zombie_conf_for_docker(); + let params = ZcoinActivationParams { + mode: ZcoinRpcMode::Native, + ..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), + }; + + let coin = z_coin_from_conf_and_params_with_docker( + &ctx, + "ZOMBIE", + &conf, + ¶ms, + PrivKeyBuildPolicy::IguanaPrivKey(pk_data.into()), + db_folder, + protocol_info, + spending_key, + ) + .await + .unwrap(); + + (ctx, coin) +} + +#[ignore] +#[tokio::test(flavor = "multi_thread")] +async fn zombie_coin_send_and_refund_maker_payment() { + let _lock = TEST_MUTEX.lock().await; + + let (_ctx, coin) = z_coin_from_spending_key("secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").await; + let time_lock = now_sec() - 3600; + let secret_hash = [0; 20]; + + let maker_uniq_data = [3; 32]; + + let taker_uniq_data = [5; 32]; + let taker_key_pair = coin.derive_htlc_key_pair(taker_uniq_data.as_slice()); + let taker_pub = taker_key_pair.public(); + + let args = SendPaymentArgs { + time_lock_duration: 0, + time_lock, + other_pubkey: taker_pub, + secret_hash: &secret_hash, + amount: "0.01".parse().unwrap(), + swap_contract_address: &None, + swap_unique_data: maker_uniq_data.as_slice(), + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until: 0, + }; + let tx = coin.send_maker_payment(args).await.unwrap(); + log!("swap tx {}", hex::encode(tx.tx_hash_as_bytes().0)); + + let refund_args = RefundPaymentArgs { + payment_tx: &tx.tx_hex(), + time_lock, + other_pubkey: taker_pub, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: &secret_hash, + }, + swap_contract_address: &None, + swap_unique_data: maker_uniq_data.as_slice(), + watcher_reward: false, + }; + let refund_tx = coin.send_maker_refunds_payment(refund_args).await.unwrap(); + log!("refund tx {}", hex::encode(refund_tx.tx_hash_as_bytes().0)); + drop(_lock); +} + +#[ignore] +#[tokio::test(flavor = "multi_thread")] +async fn zombie_coin_send_and_spend_maker_payment() { + let _lock = TEST_MUTEX.lock().await; + + let (_ctx, coin) = z_coin_from_spending_key("secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").await; + + let lock_time = now_sec() - 1000; + let secret = [0; 32]; + let secret_hash = dhash160(&secret); + + let maker_uniq_data = [3; 32]; + let maker_key_pair = coin.derive_htlc_key_pair(maker_uniq_data.as_slice()); + let maker_pub = maker_key_pair.public(); + + let taker_uniq_data = [5; 32]; + let taker_key_pair = coin.derive_htlc_key_pair(taker_uniq_data.as_slice()); + let taker_pub = taker_key_pair.public(); + + let maker_payment_args = SendPaymentArgs { + time_lock_duration: 0, + time_lock: lock_time, + other_pubkey: taker_pub, + secret_hash: secret_hash.as_slice(), + amount: "0.01".parse().unwrap(), + swap_contract_address: &None, + swap_unique_data: maker_uniq_data.as_slice(), + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until: 0, + }; + + let tx = coin.send_maker_payment(maker_payment_args).await.unwrap(); + log!("swap tx {}", hex::encode(tx.tx_hash_as_bytes().0)); + let spends_payment_args = SpendPaymentArgs { + other_payment_tx: &tx.tx_hex(), + time_lock: lock_time, + other_pubkey: maker_pub, + secret: &secret, + secret_hash: secret_hash.as_slice(), + swap_contract_address: &None, + swap_unique_data: taker_uniq_data.as_slice(), + watcher_reward: false, + }; + let spend_tx = coin.send_taker_spends_maker_payment(spends_payment_args).await.unwrap(); + log!("spend tx {}", hex::encode(spend_tx.tx_hash_as_bytes().0)); + drop(_lock); +} + +#[ignore] +#[tokio::test(flavor = "multi_thread")] +async fn prepare_zombie_sapling_cache() { + let (_ctx, coin) = z_coin_from_spending_key("secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").await; + + assert!(coin.is_sapling_state_synced().await); +} + +#[ignore] +#[tokio::test(flavor = "multi_thread")] +async fn zombie_coin_send_dex_fee() { + let _lock = TEST_MUTEX.lock().await; + + let (_ctx, coin) = z_coin_from_spending_key("secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").await; + + let dex_fee = DexFee::Standard("0.01".into()); + let tx = z_send_dex_fee(&coin, dex_fee, &[1; 16]).await.unwrap(); + log!("dex fee tx {}", tx.txid()); + drop(_lock); +} + +#[ignore] +#[tokio::test(flavor = "multi_thread")] +async fn zombie_coin_validate_dex_fee() { + let _lock = TEST_MUTEX.lock().await; + let (_ctx, coin) = z_coin_from_spending_key("secret-extended-key-main1q0k2ga2cqqqqpq8m8j6yl0say83cagrqp53zqz54w38ezs8ly9ly5ptamqwfpq85u87w0df4k8t2lwyde3n9v0gcr69nu4ryv60t0kfcsvkr8h83skwqex2nf0vr32794fmzk89cpmjptzc22lgu5wfhhp8lgf3f5vn2l3sge0udvxnm95k6dtxj2jwlfyccnum7nz297ecyhmd5ph526pxndww0rqq0qly84l635mec0x4yedf95hzn6kcgq8yxts26k98j9g32kjc8y83fe").await; + + // let balance = coin.my_balance().compat().await; + + let dex_fee = DexFee::Standard("0.01".into()); + let tx = z_send_dex_fee(&coin, dex_fee, &[1; 16]).await.unwrap(); + log!("dex fee tx {}", tx.txid()); + let tx = tx.into(); + + let validate_fee_args = ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &[], + dex_fee: &DexFee::Standard(MmNumber::from("0.001")), + min_block_number: 4, + uuid: &[1; 16], + }; + // Invalid amount should return an error + let err = coin.validate_fee(validate_fee_args).await.unwrap_err().into_inner(); + match err { + ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Dex fee has invalid amount")), + _ => panic!("Expected `WrongPaymentTx`: {:?}", err), + } + + // Invalid memo should return an error + let validate_fee_args = ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &[], + dex_fee: &DexFee::Standard(MmNumber::from("0.01")), + min_block_number: 10, + uuid: &[2; 16], + }; + let err = coin.validate_fee(validate_fee_args).await.unwrap_err().into_inner(); + match err { + ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Dex fee has invalid memo")), + _ => panic!("Expected `WrongPaymentTx`: {:?}", err), + } + + // Confirmed before min block + let validate_fee_args = ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &[], + dex_fee: &DexFee::Standard(MmNumber::from("0.01")), + min_block_number: 20000, + uuid: &[1; 16], + }; + let err = coin.validate_fee(validate_fee_args).await.unwrap_err().into_inner(); + match err { + ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min block")), + _ => panic!("Expected `WrongPaymentTx`: {:?}", err), + } + + // Success validation + let validate_fee_args = ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &[], + dex_fee: &DexFee::Standard(MmNumber::from("0.01")), + min_block_number: 20, + uuid: &[1; 16], + }; + coin.validate_fee(validate_fee_args).await.unwrap(); + drop(_lock); +} diff --git a/mm2src/mm2_main/tests/docker_tests_main.rs b/mm2src/mm2_main/tests/docker_tests_main.rs index 480e3a63ee..e5fdaafa8b 100644 --- a/mm2src/mm2_main/tests/docker_tests_main.rs +++ b/mm2src/mm2_main/tests/docker_tests_main.rs @@ -55,6 +55,7 @@ pub fn docker_tests_runner(tests: &[&TestDescAndFn]) { const IMAGES: &[&str] = &[ UTXO_ASSET_DOCKER_IMAGE_WITH_TAG, QTUM_REGTEST_DOCKER_IMAGE_WITH_TAG, + ZOMBIE_ASSET_DOCKER_IMAGE_WITH_TAG, GETH_DOCKER_IMAGE_WITH_TAG, NUCLEUS_IMAGE, ATOM_IMAGE_WITH_TAG, @@ -76,12 +77,15 @@ pub fn docker_tests_runner(tests: &[&TestDescAndFn]) { let qtum_node = qtum_docker_node(&docker, 9000); let for_slp_node = utxo_asset_docker_node(&docker, "FORSLP", 10000); let geth_node = geth_docker_node(&docker, "ETH", 8545); + let zombie_node = zombie_asset_docker_node(&docker, 7090); let utxo_ops = UtxoAssetDockerOps::from_ticker("MYCOIN"); let utxo_ops1 = UtxoAssetDockerOps::from_ticker("MYCOIN1"); let qtum_ops = QtumDockerOps::new(); let for_slp_ops = BchDockerOps::from_ticker("FORSLP"); + let zombie_ops = ZCoinAssetDockerOps::new(); + zombie_ops.wait_ready(4); qtum_ops.wait_ready(2); qtum_ops.initialize_contracts(); for_slp_ops.wait_ready(4); @@ -93,13 +97,14 @@ pub fn docker_tests_runner(tests: &[&TestDescAndFn]) { init_geth_node(); prepare_ibc_channels(ibc_relayer_node.container.id()); - thread::sleep(Duration::from_secs(10)); + thread::sleep(Duration::from_secs(12)); wait_until_relayer_container_is_ready(ibc_relayer_node.container.id()); containers.push(utxo_node); containers.push(utxo_node1); containers.push(qtum_node); containers.push(for_slp_node); + containers.push(zombie_node); containers.push(geth_node); containers.push(nucleus_node); containers.push(atom_node); diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index d8aee65fe9..621e76a597 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -473,14 +473,18 @@ pub enum Mm2InitPrivKeyPolicy { GlobalHDAccount, } -pub fn zombie_conf() -> Json { +pub fn zombie_conf() -> Json { zombie_conf_inner(None) } + +pub fn zombie_conf_for_docker() -> Json { zombie_conf_inner(Some(10)) } + +pub fn zombie_conf_inner(custom_blocktime: Option) -> Json { json!({ "coin":"ZOMBIE", "asset":"ZOMBIE", "txversion":4, - "overwintered":1, + "overwintered": 1, "mm2":1, - "avg_blocktime": 60, + "avg_blocktime": custom_blocktime.unwrap_or(60), "protocol":{ "type":"ZHTLC", "protocol_data": {