From 94e6df2a65454a36bf3ca32a016f427d8459f5c2 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 15 Sep 2022 20:52:13 +0200 Subject: [PATCH 01/36] fix some ignored tests --- mm2src/mm2_main/src/mm2_tests.rs | 32 ++++------ mm2src/mm2_main/src/mm2_tests/structs.rs | 10 ++- mm2src/mm2_main/src/mm2_tests/z_coin_tests.rs | 10 ++- mm2src/mm2_test_helpers/src/for_tests.rs | 63 ++++++++++++++----- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/mm2src/mm2_main/src/mm2_tests.rs b/mm2src/mm2_main/src/mm2_tests.rs index c632796ee0..18d25de5b2 100644 --- a/mm2src/mm2_main/src/mm2_tests.rs +++ b/mm2src/mm2_main/src/mm2_tests.rs @@ -41,13 +41,11 @@ async fn enable_z_coin(mm: &MarketMakerIt, coin: &str) -> ZcoinActivationResult let status = init_z_coin_status(mm, init.result.task_id).await; let status: RpcV2Response = json::from_value(status).unwrap(); - if let InitZcoinStatus::Ready(rpc_result) = status.result { - match rpc_result { - MmRpcResult::Ok { result } => break result, - MmRpcResult::Err(e) => panic!("{} initialization error {:?}", coin, e), - } + match status.result { + InitZcoinStatus::Ok(result) => break result, + InitZcoinStatus::Error(e) => panic!("{} initialization error {:?}", coin, e), + _ => Timer::sleep(1.).await, } - Timer::sleep(1.).await; } } @@ -194,15 +192,11 @@ async fn enable_z_coin_light( let status = init_z_coin_status(mm, init.result.task_id).await; println!("Status {}", json::to_string(&status).unwrap()); let status: RpcV2Response = json::from_value(status).unwrap(); - if let InitZcoinStatus::Ready(rpc_result) = status.result { - match rpc_result { - MmRpcResult::Ok { result } => { - break result; - }, - MmRpcResult::Err(e) => panic!("{} initialization error {:?}", coin, e), - } + match status.result { + InitZcoinStatus::Ok(result) => break result, + InitZcoinStatus::Error(e) => panic!("{} initialization error {:?}", coin, e), + _ => Timer::sleep(1.).await, } - Timer::sleep(1.).await; } } @@ -224,13 +218,11 @@ async fn enable_utxo_v2_electrum( let status = init_utxo_status(mm, init.result.task_id).await; let status: RpcV2Response = json::from_value(status).unwrap(); log!("init_utxo_status: {:?}", status); - if let InitUtxoStatus::Ready(rpc_result) = status.result { - match rpc_result { - MmRpcResult::Ok { result } => break result, - MmRpcResult::Err(e) => panic!("{} initialization error {:?}", coin, e), - } + match status.result { + InitUtxoStatus::Ok(result) => break result, + InitUtxoStatus::Error(e) => panic!("{} initialization error {:?}", coin, e), + _ => Timer::sleep(1.).await, } - Timer::sleep(1.).await; } } diff --git a/mm2src/mm2_main/src/mm2_tests/structs.rs b/mm2src/mm2_main/src/mm2_tests/structs.rs index 214201489c..f8e970226d 100644 --- a/mm2src/mm2_main/src/mm2_tests/structs.rs +++ b/mm2src/mm2_main/src/mm2_tests/structs.rs @@ -625,6 +625,7 @@ pub struct ZcoinActivationResult { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct UtxoStandardActivationResult { + pub ticker: String, pub current_block: u64, pub wallet_balance: EnableCoinBalance, } @@ -645,7 +646,8 @@ pub enum MmRpcResult { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields, tag = "status", content = "details")] pub enum InitZcoinStatus { - Ready(MmRpcResult), + Ok(ZcoinActivationResult), + Error(Json), InProgress(Json), UserActionRequired(Json), } @@ -653,7 +655,8 @@ pub enum InitZcoinStatus { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields, tag = "status", content = "details")] pub enum InitUtxoStatus { - Ready(MmRpcResult), + Ok(UtxoStandardActivationResult), + Error(Json), InProgress(Json), UserActionRequired(Json), } @@ -661,7 +664,8 @@ pub enum InitUtxoStatus { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields, tag = "status", content = "details")] pub enum WithdrawStatus { - Ready(MmRpcResult), + Ok(TransactionDetails), + Error(Json), InProgress(Json), UserActionRequired(Json), } diff --git a/mm2src/mm2_main/src/mm2_tests/z_coin_tests.rs b/mm2src/mm2_main/src/mm2_tests/z_coin_tests.rs index cad12aa317..81788a67f7 100644 --- a/mm2src/mm2_main/src/mm2_tests/z_coin_tests.rs +++ b/mm2src/mm2_main/src/mm2_tests/z_coin_tests.rs @@ -28,13 +28,11 @@ async fn withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &str) -> Tra let status = withdraw_status(mm, init.result.task_id).await; println!("Withdraw status {}", json::to_string(&status).unwrap()); let status: RpcV2Response = json::from_value(status).unwrap(); - if let WithdrawStatus::Ready(rpc_result) = status.result { - match rpc_result { - MmRpcResult::Ok { result } => break result, - MmRpcResult::Err(e) => panic!("{} withdraw error {:?}", coin, e), - } + match status.result { + WithdrawStatus::Ok(result) => break result, + WithdrawStatus::Error(e) => panic!("{} withdraw error {:?}", coin, e), + _ => Timer::sleep(1.).await, } - Timer::sleep(1.).await; } } diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 2372c228d3..a82610bce4 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -1527,7 +1527,7 @@ pub async fn init_withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &st let request = mm .rpc(&json! ({ "userpass": mm.userpass, - "method": "init_withdraw", + "method": "task::withdraw::init", "mmrpc": "2.0", "params": { "coin": coin, @@ -1537,7 +1537,12 @@ pub async fn init_withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &st })) .await .unwrap(); - assert_eq!(request.0, StatusCode::OK, "'init_withdraw' failed: {}", request.1); + assert_eq!( + request.0, + StatusCode::OK, + "'task::withdraw::init' failed: {}", + request.1 + ); json::from_str(&request.1).unwrap() } @@ -1560,7 +1565,7 @@ pub async fn withdraw_status(mm: &MarketMakerIt, task_id: u64) -> Json { let request = mm .rpc(&json! ({ "userpass": mm.userpass, - "method": "withdraw_status", + "method": "task::withdraw::status", "mmrpc": "2.0", "params": { "task_id": task_id, @@ -1568,7 +1573,12 @@ pub async fn withdraw_status(mm: &MarketMakerIt, task_id: u64) -> Json { })) .await .unwrap(); - assert_eq!(request.0, StatusCode::OK, "'withdraw_status' failed: {}", request.1); + assert_eq!( + request.0, + StatusCode::OK, + "'task::withdraw::status' failed: {}", + request.1 + ); json::from_str(&request.1).unwrap() } @@ -1576,7 +1586,7 @@ pub async fn init_z_coin_native(mm: &MarketMakerIt, coin: &str) -> Json { let request = mm .rpc(&json! ({ "userpass": mm.userpass, - "method": "init_z_coin", + "method": "task::enable_z_coin::init", "mmrpc": "2.0", "params": { "ticker": coin, @@ -1589,7 +1599,12 @@ pub async fn init_z_coin_native(mm: &MarketMakerIt, coin: &str) -> Json { })) .await .unwrap(); - assert_eq!(request.0, StatusCode::OK, "'init_z_coin' failed: {}", request.1); + assert_eq!( + request.0, + StatusCode::OK, + "'task::enable_z_coin::init' failed: {}", + request.1 + ); json::from_str(&request.1).unwrap() } @@ -1597,7 +1612,7 @@ pub async fn init_z_coin_light(mm: &MarketMakerIt, coin: &str, electrums: &[&str let request = mm .rpc(&json! ({ "userpass": mm.userpass, - "method": "init_z_coin", + "method": "task::enable_z_coin::init", "mmrpc": "2.0", "params": { "ticker": coin, @@ -1614,7 +1629,12 @@ pub async fn init_z_coin_light(mm: &MarketMakerIt, coin: &str, electrums: &[&str })) .await .unwrap(); - assert_eq!(request.0, StatusCode::OK, "'init_z_coin' failed: {}", request.1); + assert_eq!( + request.0, + StatusCode::OK, + "'task::enable_z_coin::init' failed: {}", + request.1 + ); json::from_str(&request.1).unwrap() } @@ -1622,7 +1642,7 @@ pub async fn init_z_coin_status(mm: &MarketMakerIt, task_id: u64) -> Json { let request = mm .rpc(&json! ({ "userpass": mm.userpass, - "method": "init_z_coin_status", + "method": "task::enable_z_coin::status", "mmrpc": "2.0", "params": { "task_id": task_id, @@ -1630,7 +1650,12 @@ pub async fn init_z_coin_status(mm: &MarketMakerIt, task_id: u64) -> Json { })) .await .unwrap(); - assert_eq!(request.0, StatusCode::OK, "'init_z_coin_status' failed: {}", request.1); + assert_eq!( + request.0, + StatusCode::OK, + "'task::enable_z_coin::status' failed: {}", + request.1 + ); json::from_str(&request.1).unwrap() } @@ -1731,7 +1756,7 @@ pub async fn init_utxo_electrum(mm: &MarketMakerIt, coin: &str, servers: Vec Json { let request = mm .rpc(&json! ({ "userpass": mm.userpass, - "method": "init_utxo_status", + "method": "task::enable_utxo::status", "mmrpc": "2.0", "params": { "task_id": task_id, @@ -1763,6 +1793,11 @@ pub async fn init_utxo_status(mm: &MarketMakerIt, task_id: u64) -> Json { })) .await .unwrap(); - assert_eq!(request.0, StatusCode::OK, "'init_utxo_status' failed: {}", request.1); + assert_eq!( + request.0, + StatusCode::OK, + "'task::enable_utxo::status' failed: {}", + request.1 + ); json::from_str(&request.1).unwrap() } From 92bd0090f7df9f39d799143c34f2b2106852980a Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 15 Sep 2022 22:28:49 +0200 Subject: [PATCH 02/36] move connect_to_lightning_node to rpc_command --- mm2src/coins/lightning.rs | 50 ++-------- mm2src/coins/lightning/ln_errors.rs | 51 +--------- mm2src/coins/lightning/ln_p2p.rs | 12 ++- .../lightning/connect_to_lightning_node.rs | 93 +++++++++++++++++++ mm2src/coins/rpc_command/lightning/mod.rs | 1 + mm2src/coins/rpc_command/mod.rs | 1 + mm2src/crypto/src/hw_client.rs | 4 +- .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- 8 files changed, 120 insertions(+), 95 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs create mode 100644 mm2src/coins/rpc_command/lightning/mod.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 21735e84ae..a84dab160d 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -3,11 +3,11 @@ mod ln_db; pub mod ln_errors; mod ln_events; mod ln_filesystem_persister; -mod ln_p2p; +pub(crate) mod ln_p2p; mod ln_platform; -mod ln_serialization; +pub(crate) mod ln_serialization; mod ln_sql; -mod ln_storage; +pub(crate) mod ln_storage; mod ln_utils; use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum}; @@ -53,14 +53,13 @@ use ln_conf::{ChannelOptions, LightningCoinConf, LightningProtocolConf, Platform use ln_db::{ClosedChannelsFilter, DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, PaymentType}; use ln_errors::{ClaimableBalancesError, ClaimableBalancesResult, CloseChannelError, CloseChannelResult, - ConnectToNodeError, ConnectToNodeResult, EnableLightningError, EnableLightningResult, - GenerateInvoiceError, GenerateInvoiceResult, GetChannelDetailsError, GetChannelDetailsResult, - GetPaymentDetailsError, GetPaymentDetailsResult, ListChannelsError, ListChannelsResult, - ListPaymentsError, ListPaymentsResult, OpenChannelError, OpenChannelResult, SendPaymentError, - SendPaymentResult}; + EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, + GetChannelDetailsError, GetChannelDetailsResult, GetPaymentDetailsError, GetPaymentDetailsResult, + ListChannelsError, ListChannelsResult, ListPaymentsError, ListPaymentsResult, OpenChannelError, + OpenChannelResult, SendPaymentError, SendPaymentResult}; use ln_events::LightningEventHandler; use ln_filesystem_persister::LightningFilesystemPersister; -use ln_p2p::{connect_to_node, ConnectToNodeRes, PeerManager}; +use ln_p2p::{connect_to_node, PeerManager}; use ln_platform::{h256_json_from_txid, Platform}; use ln_serialization::NodeAddress; use ln_storage::{LightningStorage, NodesAddressesMapShared, Scorer}; @@ -75,7 +74,6 @@ use script::{Builder, TransactionInputSigner}; use secp256k1v22::PublicKey; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; -use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::fmt; use std::net::SocketAddr; @@ -800,38 +798,6 @@ pub async fn start_lightning( }) } -#[derive(Deserialize)] -pub struct ConnectToNodeRequest { - pub coin: String, - pub node_address: NodeAddress, -} - -/// Connect to a certain node on the lightning network. -pub async fn connect_to_lightning_node(ctx: MmArc, req: ConnectToNodeRequest) -> ConnectToNodeResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(ConnectToNodeError::UnsupportedCoin(e.ticker().to_string())), - }; - - let node_pubkey = req.node_address.pubkey; - let node_addr = req.node_address.addr; - let res = connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; - - // If a node that we have an open channel with changed it's address, "connect_to_lightning_node" - // can be used to reconnect to the new address while saving this new address for reconnections. - if let ConnectToNodeRes::ConnectedSuccessfully { .. } = res { - if let Entry::Occupied(mut entry) = ln_coin.open_channels_nodes.lock().entry(node_pubkey) { - entry.insert(node_addr); - } - ln_coin - .persister - .save_nodes_addresses(ln_coin.open_channels_nodes) - .await?; - } - - Ok(res.to_string()) -} - #[derive(Clone, Debug, Deserialize, PartialEq)] #[serde(tag = "type", content = "value")] pub enum ChannelOpenAmount { diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index c76102cddc..cdba486a95 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -1,3 +1,4 @@ +use crate::lightning::ln_p2p::ConnectionError; use crate::utxo::rpc_clients::UtxoRpcError; use crate::utxo::GenerateTxError; use crate::{BalanceError, CoinFindError, NumConversError, PrivKeyNotAllowed, UnexpectedDerivationMethod}; @@ -12,7 +13,6 @@ use std::num::TryFromIntError; use utxo_signer::with_key_pair::UtxoSignWithKeyPairError; pub type EnableLightningResult = Result>; -pub type ConnectToNodeResult = Result>; pub type OpenChannelResult = Result>; pub type UpdateChannelResult = Result>; pub type ListChannelsResult = Result>; @@ -78,51 +78,6 @@ impl From for EnableLightningError { fn from(e: UtxoRpcError) -> Self { EnableLightningError::RpcError(e.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum ConnectToNodeError { - #[display(fmt = "Parse error: {}", _0)] - ParseError(String), - #[display(fmt = "Error connecting to node: {}", _0)] - ConnectionError(String), - #[display(fmt = "I/O error {}", _0)] - IOError(String), - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), -} - -impl HttpStatusCode for ConnectToNodeError { - fn status_code(&self) -> StatusCode { - match self { - ConnectToNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - ConnectToNodeError::ParseError(_) - | ConnectToNodeError::IOError(_) - | ConnectToNodeError::ConnectionError(_) => StatusCode::INTERNAL_SERVER_ERROR, - ConnectToNodeError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - } - } -} - -impl From for EnableLightningError { - fn from(err: ConnectToNodeError) -> EnableLightningError { - EnableLightningError::ConnectToNodeError(err.to_string()) - } -} - -impl From for ConnectToNodeError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => ConnectToNodeError::NoSuchCoin(coin), - } - } -} - -impl From for ConnectToNodeError { - fn from(err: std::io::Error) -> ConnectToNodeError { ConnectToNodeError::IOError(err.to_string()) } -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum OpenChannelError { @@ -172,8 +127,8 @@ impl HttpStatusCode for OpenChannelError { } } -impl From for OpenChannelError { - fn from(err: ConnectToNodeError) -> OpenChannelError { OpenChannelError::ConnectToNodeError(err.to_string()) } +impl From for OpenChannelError { + fn from(err: ConnectionError) -> OpenChannelError { OpenChannelError::ConnectToNodeError(err.to_string()) } } impl From for OpenChannelError { diff --git a/mm2src/coins/lightning/ln_p2p.rs b/mm2src/coins/lightning/ln_p2p.rs index 1acd762289..95d266ad51 100644 --- a/mm2src/coins/lightning/ln_p2p.rs +++ b/mm2src/coins/lightning/ln_p2p.rs @@ -29,11 +29,17 @@ pub enum ConnectToNodeRes { ConnectedSuccessfully { pubkey: PublicKey, node_addr: SocketAddr }, } +#[derive(Display)] +pub enum ConnectionError { + #[display(fmt = "Connection error: {}", _0)] + ConnectionError(String), +} + pub async fn connect_to_node( pubkey: PublicKey, node_addr: SocketAddr, peer_manager: Arc, -) -> ConnectToNodeResult { +) -> Result { let peer_manager_ref = peer_manager.clone(); let peer_node_ids = async_blocking(move || peer_manager_ref.get_peer_node_ids()).await; if peer_node_ids.contains(&pubkey) { @@ -44,7 +50,7 @@ pub async fn connect_to_node( match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, node_addr).await { Some(fut) => Box::pin(fut), None => { - return MmError::err(ConnectToNodeError::ConnectionError(format!( + return Err(ConnectionError::ConnectionError(format!( "Failed to connect to node: {}", pubkey ))) @@ -55,7 +61,7 @@ pub async fn connect_to_node( // Make sure the connection is still established. match futures::poll!(&mut connection_closed_future) { std::task::Poll::Ready(_) => { - return MmError::err(ConnectToNodeError::ConnectionError(format!( + return Err(ConnectionError::ConnectionError(format!( "Node {} disconnected before finishing the handshake", pubkey ))); diff --git a/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs b/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs new file mode 100644 index 0000000000..abf3261c1f --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs @@ -0,0 +1,93 @@ +use crate::lightning::ln_errors::EnableLightningError; +use crate::lightning::ln_p2p::{connect_to_node, ConnectToNodeRes, ConnectionError}; +use crate::lightning::ln_serialization::NodeAddress; +use crate::lightning::ln_storage::LightningStorage; +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::HttpStatusCode; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; +use std::collections::hash_map::Entry; + +type ConnectToNodeResult = Result>; + +#[derive(Deserialize)] +pub struct ConnectToNodeRequest { + pub coin: String, + pub node_address: NodeAddress, +} + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum ConnectToNodeError { + #[display(fmt = "Parse error: {}", _0)] + ParseError(String), + #[display(fmt = "Error connecting to node: {}", _0)] + ConnectionError(String), + #[display(fmt = "I/O error {}", _0)] + IOError(String), + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), +} + +impl HttpStatusCode for ConnectToNodeError { + fn status_code(&self) -> StatusCode { + match self { + ConnectToNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + ConnectToNodeError::ParseError(_) + | ConnectToNodeError::IOError(_) + | ConnectToNodeError::ConnectionError(_) => StatusCode::INTERNAL_SERVER_ERROR, + ConnectToNodeError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + } + } +} + +impl From for EnableLightningError { + fn from(err: ConnectToNodeError) -> EnableLightningError { + EnableLightningError::ConnectToNodeError(err.to_string()) + } +} + +impl From for ConnectToNodeError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => ConnectToNodeError::NoSuchCoin(coin), + } + } +} + +impl From for ConnectToNodeError { + fn from(err: std::io::Error) -> ConnectToNodeError { ConnectToNodeError::IOError(err.to_string()) } +} + +impl From for ConnectToNodeError { + fn from(err: ConnectionError) -> ConnectToNodeError { ConnectToNodeError::ConnectionError(err.to_string()) } +} + +/// Connect to a certain node on the lightning network. +pub async fn connect_to_lightning_node(ctx: MmArc, req: ConnectToNodeRequest) -> ConnectToNodeResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(ConnectToNodeError::UnsupportedCoin(e.ticker().to_string())), + }; + + let node_pubkey = req.node_address.pubkey; + let node_addr = req.node_address.addr; + let res = connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; + + // If a node that we have an open channel with changed it's address, "connect_to_lightning_node" + // can be used to reconnect to the new address while saving this new address for reconnections. + if let ConnectToNodeRes::ConnectedSuccessfully { .. } = res { + if let Entry::Occupied(mut entry) = ln_coin.open_channels_nodes.lock().entry(node_pubkey) { + entry.insert(node_addr); + } + ln_coin + .persister + .save_nodes_addresses(ln_coin.open_channels_nodes) + .await?; + } + + Ok(res.to_string()) +} diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs new file mode 100644 index 0000000000..1d9318d9ed --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -0,0 +1 @@ +pub mod connect_to_lightning_node; diff --git a/mm2src/coins/rpc_command/mod.rs b/mm2src/coins/rpc_command/mod.rs index eec08d40ac..945cea77fe 100644 --- a/mm2src/coins/rpc_command/mod.rs +++ b/mm2src/coins/rpc_command/mod.rs @@ -7,3 +7,4 @@ pub mod init_account_balance; pub mod init_create_account; pub mod init_scan_for_new_addresses; pub mod init_withdraw; +#[cfg(not(target_arch = "wasm32"))] pub mod lightning; diff --git a/mm2src/crypto/src/hw_client.rs b/mm2src/crypto/src/hw_client.rs index f7fae7278d..d846914b28 100644 --- a/mm2src/crypto/src/hw_client.rs +++ b/mm2src/crypto/src/hw_client.rs @@ -1,4 +1,6 @@ -use crate::hw_error::{HwError, HwResult}; +use crate::hw_error::HwError; +#[cfg(not(target_arch = "wasm32"))] +use crate::hw_error::HwResult; use async_trait::async_trait; #[cfg(not(target_os = "ios"))] use common::custom_futures::FutureTimerExt; diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 4f22db587f..d0e321905a 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -43,10 +43,11 @@ use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; cfg_native! { - use coins::lightning::{add_trusted_node, close_channel, connect_to_lightning_node, generate_invoice, get_channel_details, + use coins::lightning::{add_trusted_node, close_channel, generate_invoice, get_channel_details, get_claimable_balances, get_payment_details, list_closed_channels_by_filter, list_open_channels_by_filter, list_payments_by_filter, list_trusted_nodes, open_channel, remove_trusted_node, send_payment, update_channel, LightningCoin}; + use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; use coins::z_coin::ZCoin; } From e18b4aa6a25ddc00adfd6d7d5f1939d36bb323d2 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 15 Sep 2022 23:07:26 +0200 Subject: [PATCH 03/36] move open_channel to rpc_command --- mm2src/coins/lightning.rs | 154 +----------- mm2src/coins/lightning/ln_errors.rs | 103 +------- mm2src/coins/lightning/ln_events.rs | 29 ++- mm2src/coins/rpc_command/lightning/mod.rs | 1 + .../rpc_command/lightning/open_channel.rs | 237 ++++++++++++++++++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- 6 files changed, 273 insertions(+), 254 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/open_channel.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index a84dab160d..7cef46b761 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -1,5 +1,5 @@ pub mod ln_conf; -mod ln_db; +pub(crate) mod ln_db; pub mod ln_errors; mod ln_events; mod ln_filesystem_persister; @@ -11,15 +11,14 @@ pub(crate) mod ln_storage; mod ln_utils; use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum}; -use crate::lightning::ln_conf::OurChannelsConfigs; use crate::lightning::ln_errors::{TrustedNodeError, TrustedNodeResult, UpdateChannelError, UpdateChannelResult}; use crate::lightning::ln_events::init_events_abort_handlers; use crate::lightning::ln_serialization::PublicKeyForRPC; use crate::lightning::ln_sql::SqliteLightningDB; use crate::lightning::ln_storage::{NetworkGraph, TrustedNodesShared}; use crate::utxo::rpc_clients::UtxoRpcClientEnum; -use crate::utxo::utxo_common::{big_decimal_from_sat_unsigned, UtxoTxBuilder}; -use crate::utxo::{sat_from_big_decimal, BlockchainNetwork, FeePolicy, GetUtxoListOps, UtxoTxGenerationOps}; +use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; +use crate::utxo::BlockchainNetwork; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, @@ -31,20 +30,18 @@ use bitcoin::hashes::Hash; use bitcoin_hashes::sha256::Hash as Sha256; use bitcrypto::dhash256; use bitcrypto::ChecksumType; -use chain::TransactionOutput; use common::executor::spawn; -use common::log::{error, LogOnError, LogState}; +use common::log::{LogOnError, LogState}; use common::{async_blocking, calc_total_pages, log, now_ms, ten, PagingOptionsEnum}; use futures::{FutureExt, TryFutureExt}; use futures01::Future; -use keys::{hash::H256, AddressHashEnum, CompactSignature, KeyPair, Private, Public}; +use keys::{hash::H256, CompactSignature, KeyPair, Private, Public}; use lightning::chain::channelmonitor::Balance; use lightning::chain::keysinterface::{KeysInterface, KeysManager, Recipient}; use lightning::chain::Access; use lightning::ln::channelmanager::{ChannelDetails, MIN_FINAL_CLTV_EXPIRY}; use lightning::ln::{PaymentHash, PaymentPreimage}; use lightning::routing::gossip; -use lightning::util::config::UserConfig; use lightning_background_processor::{BackgroundProcessor, GossipSync}; use lightning_invoice::payment; use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter}; @@ -55,13 +52,12 @@ use ln_db::{ClosedChannelsFilter, DBChannelDetails, DBPaymentInfo, DBPaymentsFil use ln_errors::{ClaimableBalancesError, ClaimableBalancesResult, CloseChannelError, CloseChannelResult, EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, GetChannelDetailsError, GetChannelDetailsResult, GetPaymentDetailsError, GetPaymentDetailsResult, - ListChannelsError, ListChannelsResult, ListPaymentsError, ListPaymentsResult, OpenChannelError, - OpenChannelResult, SendPaymentError, SendPaymentResult}; + ListChannelsError, ListChannelsResult, ListPaymentsError, ListPaymentsResult, SendPaymentError, + SendPaymentResult}; use ln_events::LightningEventHandler; use ln_filesystem_persister::LightningFilesystemPersister; use ln_p2p::{connect_to_node, PeerManager}; use ln_platform::{h256_json_from_txid, Platform}; -use ln_serialization::NodeAddress; use ln_storage::{LightningStorage, NodesAddressesMapShared, Scorer}; use ln_utils::{ChainMonitor, ChannelManager}; use mm2_core::mm_ctx::MmArc; @@ -70,7 +66,7 @@ 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 script::{Builder, TransactionInputSigner}; +use script::TransactionInputSigner; use secp256k1v22::PublicKey; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; @@ -117,7 +113,7 @@ impl fmt::Debug for LightningCoin { } impl LightningCoin { - fn platform_coin(&self) -> &UtxoStandardCoin { &self.platform.coin } + pub fn platform_coin(&self) -> &UtxoStandardCoin { &self.platform.coin } #[inline] fn my_node_id(&self) -> String { self.channel_manager.get_our_node_id().to_string() } @@ -798,138 +794,6 @@ pub async fn start_lightning( }) } -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(tag = "type", content = "value")] -pub enum ChannelOpenAmount { - Exact(BigDecimal), - Max, -} - -#[derive(Deserialize)] -pub struct OpenChannelRequest { - pub coin: String, - pub node_address: NodeAddress, - pub amount: ChannelOpenAmount, - /// The amount to push to the counterparty as part of the open, in milli-satoshi. Creates inbound liquidity for the channel. - /// By setting push_msat to a value, opening channel request will be equivalent to opening a channel then sending a payment with - /// the push_msat amount. - #[serde(default)] - pub push_msat: u64, - pub channel_options: Option, - pub channel_configs: Option, -} - -#[derive(Serialize)] -pub struct OpenChannelResponse { - rpc_channel_id: u64, - node_address: NodeAddress, -} - -/// Opens a channel on the lightning network. -pub async fn open_channel(ctx: MmArc, req: OpenChannelRequest) -> OpenChannelResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(OpenChannelError::UnsupportedCoin(e.ticker().to_string())), - }; - - // Making sure that the node data is correct and that we can connect to it before doing more operations - let node_pubkey = req.node_address.pubkey; - let node_addr = req.node_address.addr; - connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; - - let platform_coin = ln_coin.platform_coin().clone(); - let decimals = platform_coin.as_ref().decimals; - let my_address = platform_coin.as_ref().derivation_method.iguana_or_err()?; - let (unspents, _) = platform_coin.get_unspent_ordered_list(my_address).await?; - let (value, fee_policy) = match req.amount.clone() { - ChannelOpenAmount::Max => ( - unspents.iter().fold(0, |sum, unspent| sum + unspent.value), - FeePolicy::DeductFromOutput(0), - ), - ChannelOpenAmount::Exact(v) => { - let value = sat_from_big_decimal(&v, decimals)?; - (value, FeePolicy::SendExact) - }, - }; - - // The actual script_pubkey will replace this before signing the transaction after receiving the required - // output script from the other node when the channel is accepted - let script_pubkey = - Builder::build_witness_script(&AddressHashEnum::WitnessScriptHash(Default::default())).to_bytes(); - let outputs = vec![TransactionOutput { value, script_pubkey }]; - - let mut tx_builder = UtxoTxBuilder::new(&platform_coin) - .add_available_inputs(unspents) - .add_outputs(outputs) - .with_fee_policy(fee_policy); - - let fee = platform_coin - .get_tx_fee() - .await - .map_err(|e| OpenChannelError::RpcError(e.to_string()))?; - tx_builder = tx_builder.with_fee(fee); - - let (unsigned, _) = tx_builder.build().await?; - - let amount_in_sat = unsigned.outputs[0].value; - let push_msat = req.push_msat; - let channel_manager = ln_coin.channel_manager.clone(); - - let mut conf = ln_coin.conf.clone(); - if let Some(options) = req.channel_options { - match conf.channel_options.as_mut() { - Some(o) => o.update_according_to(options), - None => conf.channel_options = Some(options), - } - } - if let Some(configs) = req.channel_configs { - match conf.our_channels_configs.as_mut() { - Some(o) => o.update_according_to(configs), - None => conf.our_channels_configs = Some(configs), - } - } - drop_mutability!(conf); - let user_config: UserConfig = conf.into(); - - let rpc_channel_id = ln_coin.db.get_last_channel_rpc_id().await? as u64 + 1; - - let temp_channel_id = async_blocking(move || { - channel_manager - .create_channel(node_pubkey, amount_in_sat, push_msat, rpc_channel_id, Some(user_config)) - .map_to_mm(|e| OpenChannelError::FailureToOpenChannel(node_pubkey.to_string(), format!("{:?}", e))) - }) - .await?; - - { - let mut unsigned_funding_txs = ln_coin.platform.unsigned_funding_txs.lock(); - unsigned_funding_txs.insert(rpc_channel_id, unsigned); - } - - let pending_channel_details = DBChannelDetails::new( - rpc_channel_id, - temp_channel_id, - node_pubkey, - true, - user_config.channel_handshake_config.announced_channel, - ); - - // Saving node data to reconnect to it on restart - ln_coin.open_channels_nodes.lock().insert(node_pubkey, node_addr); - ln_coin - .persister - .save_nodes_addresses(ln_coin.open_channels_nodes) - .await?; - - if let Err(e) = ln_coin.db.add_channel_to_db(pending_channel_details).await { - error!("Unable to add new outbound channel {} to db: {}", rpc_channel_id, e); - } - - Ok(OpenChannelResponse { - rpc_channel_id, - node_address: req.node_address, - }) -} - #[derive(Deserialize)] pub struct UpdateChannelReq { pub coin: String, diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index cdba486a95..68ba0af987 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -1,7 +1,5 @@ -use crate::lightning::ln_p2p::ConnectionError; use crate::utxo::rpc_clients::UtxoRpcError; -use crate::utxo::GenerateTxError; -use crate::{BalanceError, CoinFindError, NumConversError, PrivKeyNotAllowed, UnexpectedDerivationMethod}; +use crate::CoinFindError; use common::HttpStatusCode; use db_common::sqlite::rusqlite::Error as SqlError; use derive_more::Display; @@ -10,10 +8,8 @@ use lightning_invoice::SignOrCreationError; use mm2_err_handle::prelude::*; use rpc::v1::types::H256 as H256Json; use std::num::TryFromIntError; -use utxo_signer::with_key_pair::UtxoSignWithKeyPairError; pub type EnableLightningResult = Result>; -pub type OpenChannelResult = Result>; pub type UpdateChannelResult = Result>; pub type ListChannelsResult = Result>; pub type GetChannelDetailsResult = Result>; @@ -78,103 +74,6 @@ impl From for EnableLightningError { fn from(e: UtxoRpcError) -> Self { EnableLightningError::RpcError(e.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum OpenChannelError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "Balance Error {}", _0)] - BalanceError(String), - #[display(fmt = "Invalid path: {}", _0)] - InvalidPath(String), - #[display(fmt = "Failure to open channel with node {}: {}", _0, _1)] - FailureToOpenChannel(String, String), - #[display(fmt = "RPC error {}", _0)] - RpcError(String), - #[display(fmt = "Internal error: {}", _0)] - InternalError(String), - #[display(fmt = "I/O error {}", _0)] - IOError(String), - #[display(fmt = "DB error {}", _0)] - DbError(String), - ConnectToNodeError(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "Generate Tx Error {}", _0)] - GenerateTxErr(String), - #[display(fmt = "Error converting transaction: {}", _0)] - ConvertTxErr(String), - PrivKeyNotAllowed(String), -} - -impl HttpStatusCode for OpenChannelError { - fn status_code(&self) -> StatusCode { - match self { - OpenChannelError::UnsupportedCoin(_) - | OpenChannelError::RpcError(_) - | OpenChannelError::PrivKeyNotAllowed(_) => StatusCode::BAD_REQUEST, - OpenChannelError::FailureToOpenChannel(_, _) - | OpenChannelError::ConnectToNodeError(_) - | OpenChannelError::InternalError(_) - | OpenChannelError::GenerateTxErr(_) - | OpenChannelError::IOError(_) - | OpenChannelError::DbError(_) - | OpenChannelError::InvalidPath(_) - | OpenChannelError::ConvertTxErr(_) - | OpenChannelError::BalanceError(_) => StatusCode::INTERNAL_SERVER_ERROR, - OpenChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - } - } -} - -impl From for OpenChannelError { - fn from(err: ConnectionError) -> OpenChannelError { OpenChannelError::ConnectToNodeError(err.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => OpenChannelError::NoSuchCoin(coin), - } - } -} - -impl From for OpenChannelError { - fn from(e: BalanceError) -> Self { OpenChannelError::BalanceError(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: NumConversError) -> Self { OpenChannelError::InternalError(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: GenerateTxError) -> Self { OpenChannelError::GenerateTxErr(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: UtxoRpcError) -> Self { OpenChannelError::RpcError(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: UnexpectedDerivationMethod) -> Self { OpenChannelError::InternalError(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: UtxoSignWithKeyPairError) -> Self { OpenChannelError::InternalError(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(e: PrivKeyNotAllowed) -> Self { OpenChannelError::PrivKeyNotAllowed(e.to_string()) } -} - -impl From for OpenChannelError { - fn from(err: std::io::Error) -> OpenChannelError { OpenChannelError::IOError(err.to_string()) } -} - -impl From for OpenChannelError { - fn from(err: SqlError) -> OpenChannelError { OpenChannelError::DbError(err.to_string()) } -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum UpdateChannelError { diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index 114f8a12cb..a6b0d0f411 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -179,19 +179,27 @@ pub async fn init_events_abort_handlers( Ok(abort_handlers) } +#[derive(Display)] +pub enum SignFundingTransactionError { + #[display(fmt = "Internal error: {}", _0)] + Internal(String), + #[display(fmt = "Error converting transaction: {}", _0)] + ConvertTxErr(String), +} + // Generates the raw funding transaction with one output equal to the channel value. fn sign_funding_transaction( user_channel_id: u64, output_script: &Script, platform: Arc, -) -> OpenChannelResult { +) -> Result { let coin = &platform.coin; let mut unsigned = { let unsigned_funding_txs = platform.unsigned_funding_txs.lock(); unsigned_funding_txs .get(&user_channel_id) .ok_or_else(|| { - OpenChannelError::InternalError(format!( + SignFundingTransactionError::Internal(format!( "Unsigned funding tx not found for internal channel id: {}", user_channel_id )) @@ -200,8 +208,16 @@ fn sign_funding_transaction( }; unsigned.outputs[0].script_pubkey = output_script.to_bytes().into(); - let my_address = coin.as_ref().derivation_method.iguana_or_err()?; - let key_pair = coin.as_ref().priv_key_policy.key_pair_or_err()?; + let my_address = coin + .as_ref() + .derivation_method + .iguana_or_err() + .map_err(|e| SignFundingTransactionError::Internal(e.to_string()))?; + let key_pair = coin + .as_ref() + .priv_key_policy + .key_pair_or_err() + .map_err(|e| SignFundingTransactionError::Internal(e.to_string()))?; let prev_script = Builder::build_p2pkh(&my_address.hash); let signed = sign_tx( @@ -210,9 +226,10 @@ fn sign_funding_transaction( prev_script, SignatureVersion::WitnessV0, coin.as_ref().conf.fork_id, - )?; + ) + .map_err(|e| SignFundingTransactionError::Internal(e.to_string()))?; - Transaction::try_from(signed).map_to_mm(|e| OpenChannelError::ConvertTxErr(e.to_string())) + Transaction::try_from(signed).map_err(|e| SignFundingTransactionError::ConvertTxErr(e.to_string())) } async fn save_channel_closing_details( diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 1d9318d9ed..2d0391c0e2 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1 +1,2 @@ pub mod connect_to_lightning_node; +pub mod open_channel; diff --git a/mm2src/coins/rpc_command/lightning/open_channel.rs b/mm2src/coins/rpc_command/lightning/open_channel.rs new file mode 100644 index 0000000000..9dbeb3a495 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/open_channel.rs @@ -0,0 +1,237 @@ +use crate::lightning::ln_conf::{ChannelOptions, OurChannelsConfigs}; +use crate::lightning::ln_db::{DBChannelDetails, LightningDB}; +use crate::lightning::ln_p2p::{connect_to_node, ConnectionError}; +use crate::lightning::ln_serialization::NodeAddress; +use crate::lightning::ln_storage::LightningStorage; +use crate::utxo::utxo_common::UtxoTxBuilder; +use crate::utxo::{sat_from_big_decimal, FeePolicy, GetUtxoListOps, UtxoTxGenerationOps}; +use crate::{lp_coinfind_or_err, BalanceError, CoinFindError, GenerateTxError, MmCoinEnum, NumConversError, + UnexpectedDerivationMethod, UtxoRpcError}; +use chain::TransactionOutput; +use common::log::error; +use common::{async_blocking, HttpStatusCode}; +use db_common::sqlite::rusqlite::Error as SqlError; +use http::StatusCode; +use keys::AddressHashEnum; +use lightning::util::config::UserConfig; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; +use mm2_number::BigDecimal; +use script::Builder; + +pub type OpenChannelResult = Result>; + +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(tag = "type", content = "value")] +pub enum ChannelOpenAmount { + Exact(BigDecimal), + Max, +} + +#[derive(Deserialize)] +pub struct OpenChannelRequest { + pub coin: String, + pub node_address: NodeAddress, + pub amount: ChannelOpenAmount, + /// The amount to push to the counterparty as part of the open, in milli-satoshi. Creates inbound liquidity for the channel. + /// By setting push_msat to a value, opening channel request will be equivalent to opening a channel then sending a payment with + /// the push_msat amount. + #[serde(default)] + pub push_msat: u64, + pub channel_options: Option, + pub channel_configs: Option, +} + +#[derive(Serialize)] +pub struct OpenChannelResponse { + rpc_channel_id: u64, + node_address: NodeAddress, +} + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum OpenChannelError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "Balance Error {}", _0)] + BalanceError(String), + #[display(fmt = "Invalid path: {}", _0)] + InvalidPath(String), + #[display(fmt = "Failure to open channel with node {}: {}", _0, _1)] + FailureToOpenChannel(String, String), + #[display(fmt = "RPC error {}", _0)] + RpcError(String), + #[display(fmt = "Internal error: {}", _0)] + InternalError(String), + #[display(fmt = "I/O error {}", _0)] + IOError(String), + #[display(fmt = "DB error {}", _0)] + DbError(String), + ConnectToNodeError(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "Generate Tx Error {}", _0)] + GenerateTxErr(String), +} + +impl HttpStatusCode for OpenChannelError { + fn status_code(&self) -> StatusCode { + match self { + OpenChannelError::UnsupportedCoin(_) | OpenChannelError::RpcError(_) => StatusCode::BAD_REQUEST, + OpenChannelError::FailureToOpenChannel(_, _) + | OpenChannelError::ConnectToNodeError(_) + | OpenChannelError::InternalError(_) + | OpenChannelError::GenerateTxErr(_) + | OpenChannelError::IOError(_) + | OpenChannelError::DbError(_) + | OpenChannelError::InvalidPath(_) + | OpenChannelError::BalanceError(_) => StatusCode::INTERNAL_SERVER_ERROR, + OpenChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + } + } +} + +impl From for OpenChannelError { + fn from(err: ConnectionError) -> OpenChannelError { OpenChannelError::ConnectToNodeError(err.to_string()) } +} + +impl From for OpenChannelError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => OpenChannelError::NoSuchCoin(coin), + } + } +} + +impl From for OpenChannelError { + fn from(e: BalanceError) -> Self { OpenChannelError::BalanceError(e.to_string()) } +} + +impl From for OpenChannelError { + fn from(e: NumConversError) -> Self { OpenChannelError::InternalError(e.to_string()) } +} + +impl From for OpenChannelError { + fn from(e: GenerateTxError) -> Self { OpenChannelError::GenerateTxErr(e.to_string()) } +} + +impl From for OpenChannelError { + fn from(e: UtxoRpcError) -> Self { OpenChannelError::RpcError(e.to_string()) } +} + +impl From for OpenChannelError { + fn from(e: UnexpectedDerivationMethod) -> Self { OpenChannelError::InternalError(e.to_string()) } +} + +impl From for OpenChannelError { + fn from(err: std::io::Error) -> OpenChannelError { OpenChannelError::IOError(err.to_string()) } +} + +impl From for OpenChannelError { + fn from(err: SqlError) -> OpenChannelError { OpenChannelError::DbError(err.to_string()) } +} + +/// Opens a channel on the lightning network. +pub async fn open_channel(ctx: MmArc, req: OpenChannelRequest) -> OpenChannelResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(OpenChannelError::UnsupportedCoin(e.ticker().to_string())), + }; + + // Making sure that the node data is correct and that we can connect to it before doing more operations + let node_pubkey = req.node_address.pubkey; + let node_addr = req.node_address.addr; + connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; + + let platform_coin = ln_coin.platform_coin().clone(); + let decimals = platform_coin.as_ref().decimals; + let my_address = platform_coin.as_ref().derivation_method.iguana_or_err()?; + let (unspents, _) = platform_coin.get_unspent_ordered_list(my_address).await?; + let (value, fee_policy) = match req.amount.clone() { + ChannelOpenAmount::Max => ( + unspents.iter().fold(0, |sum, unspent| sum + unspent.value), + FeePolicy::DeductFromOutput(0), + ), + ChannelOpenAmount::Exact(v) => { + let value = sat_from_big_decimal(&v, decimals)?; + (value, FeePolicy::SendExact) + }, + }; + + // The actual script_pubkey will replace this before signing the transaction after receiving the required + // output script from the other node when the channel is accepted + let script_pubkey = + Builder::build_witness_script(&AddressHashEnum::WitnessScriptHash(Default::default())).to_bytes(); + let outputs = vec![TransactionOutput { value, script_pubkey }]; + + let mut tx_builder = UtxoTxBuilder::new(&platform_coin) + .add_available_inputs(unspents) + .add_outputs(outputs) + .with_fee_policy(fee_policy); + + let fee = platform_coin + .get_tx_fee() + .await + .map_err(|e| OpenChannelError::RpcError(e.to_string()))?; + tx_builder = tx_builder.with_fee(fee); + + let (unsigned, _) = tx_builder.build().await?; + + let amount_in_sat = unsigned.outputs[0].value; + let push_msat = req.push_msat; + let channel_manager = ln_coin.channel_manager.clone(); + + let mut conf = ln_coin.conf.clone(); + if let Some(options) = req.channel_options { + match conf.channel_options.as_mut() { + Some(o) => o.update_according_to(options), + None => conf.channel_options = Some(options), + } + } + if let Some(configs) = req.channel_configs { + match conf.our_channels_configs.as_mut() { + Some(o) => o.update_according_to(configs), + None => conf.our_channels_configs = Some(configs), + } + } + drop_mutability!(conf); + let user_config: UserConfig = conf.into(); + + let rpc_channel_id = ln_coin.db.get_last_channel_rpc_id().await? as u64 + 1; + + let temp_channel_id = async_blocking(move || { + channel_manager + .create_channel(node_pubkey, amount_in_sat, push_msat, rpc_channel_id, Some(user_config)) + .map_to_mm(|e| OpenChannelError::FailureToOpenChannel(node_pubkey.to_string(), format!("{:?}", e))) + }) + .await?; + + { + let mut unsigned_funding_txs = ln_coin.platform.unsigned_funding_txs.lock(); + unsigned_funding_txs.insert(rpc_channel_id, unsigned); + } + + let pending_channel_details = DBChannelDetails::new( + rpc_channel_id, + temp_channel_id, + node_pubkey, + true, + user_config.channel_handshake_config.announced_channel, + ); + + // Saving node data to reconnect to it on restart + ln_coin.open_channels_nodes.lock().insert(node_pubkey, node_addr); + ln_coin + .persister + .save_nodes_addresses(ln_coin.open_channels_nodes) + .await?; + + if let Err(e) = ln_coin.db.add_channel_to_db(pending_channel_details).await { + error!("Unable to add new outbound channel {} to db: {}", rpc_channel_id, e); + } + + Ok(OpenChannelResponse { + rpc_channel_id, + node_address: req.node_address, + }) +} diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index d0e321905a..852aab9643 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -45,9 +45,10 @@ use std::net::SocketAddr; cfg_native! { use coins::lightning::{add_trusted_node, close_channel, generate_invoice, get_channel_details, get_claimable_balances, get_payment_details, list_closed_channels_by_filter, list_open_channels_by_filter, - list_payments_by_filter, list_trusted_nodes, open_channel, remove_trusted_node, send_payment, update_channel, + list_payments_by_filter, list_trusted_nodes, remove_trusted_node, send_payment, update_channel, LightningCoin}; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; + use coins::rpc_command::lightning::open_channel::open_channel; use coins::z_coin::ZCoin; } From a16cbbe94a8c8be1205ee0dc968a69f8b88fc5e7 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 17:10:21 +0200 Subject: [PATCH 04/36] move update_channel to rpc_command --- mm2src/coins/lightning.rs | 50 +---------- mm2src/coins/lightning/ln_errors.rs | 33 -------- mm2src/coins/rpc_command/lightning/mod.rs | 1 + .../rpc_command/lightning/update_channel.rs | 84 +++++++++++++++++++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- 5 files changed, 90 insertions(+), 81 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/update_channel.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 7cef46b761..d594e65a4e 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -11,7 +11,7 @@ pub(crate) mod ln_storage; mod ln_utils; use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum}; -use crate::lightning::ln_errors::{TrustedNodeError, TrustedNodeResult, UpdateChannelError, UpdateChannelResult}; +use crate::lightning::ln_errors::{TrustedNodeError, TrustedNodeResult}; use crate::lightning::ln_events::init_events_abort_handlers; use crate::lightning::ln_serialization::PublicKeyForRPC; use crate::lightning::ln_sql::SqliteLightningDB; @@ -46,7 +46,7 @@ use lightning_background_processor::{BackgroundProcessor, GossipSync}; use lightning_invoice::payment; use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter}; use lightning_invoice::{Invoice, InvoiceDescription}; -use ln_conf::{ChannelOptions, LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; +use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; use ln_db::{ClosedChannelsFilter, DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, PaymentType}; use ln_errors::{ClaimableBalancesError, ClaimableBalancesResult, CloseChannelError, CloseChannelResult, @@ -139,7 +139,7 @@ impl LightningCoin { }) } - async fn get_channel_by_rpc_id(&self, rpc_id: u64) -> Option { + pub(crate) async fn get_channel_by_rpc_id(&self, rpc_id: u64) -> Option { self.list_channels() .await .into_iter() @@ -794,50 +794,6 @@ pub async fn start_lightning( }) } -#[derive(Deserialize)] -pub struct UpdateChannelReq { - pub coin: String, - pub rpc_channel_id: u64, - pub channel_options: ChannelOptions, -} - -#[derive(Serialize)] -pub struct UpdateChannelResponse { - channel_options: ChannelOptions, -} - -/// Updates configuration for an open channel. -pub async fn update_channel(ctx: MmArc, req: UpdateChannelReq) -> UpdateChannelResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(UpdateChannelError::UnsupportedCoin(e.ticker().to_string())), - }; - - let channel_details = ln_coin - .get_channel_by_rpc_id(req.rpc_channel_id) - .await - .ok_or(UpdateChannelError::NoSuchChannel(req.rpc_channel_id))?; - - async_blocking(move || { - let mut channel_options = ln_coin - .conf - .channel_options - .unwrap_or_else(|| req.channel_options.clone()); - if channel_options != req.channel_options { - channel_options.update_according_to(req.channel_options.clone()); - } - drop_mutability!(channel_options); - let channel_ids = &[channel_details.channel_id]; - let counterparty_node_id = channel_details.counterparty.node_id; - ln_coin - .channel_manager - .update_channel_config(&counterparty_node_id, channel_ids, &channel_options.clone().into()) - .map_to_mm(|e| UpdateChannelError::FailureToUpdateChannel(req.rpc_channel_id, format!("{:?}", e)))?; - Ok(UpdateChannelResponse { channel_options }) - }) - .await -} - #[derive(Deserialize)] pub struct OpenChannelsFilter { pub channel_id: Option, diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index 68ba0af987..f63bc873dd 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -10,7 +10,6 @@ use rpc::v1::types::H256 as H256Json; use std::num::TryFromIntError; pub type EnableLightningResult = Result>; -pub type UpdateChannelResult = Result>; pub type ListChannelsResult = Result>; pub type GetChannelDetailsResult = Result>; pub type GenerateInvoiceResult = Result>; @@ -74,38 +73,6 @@ impl From for EnableLightningError { fn from(e: UtxoRpcError) -> Self { EnableLightningError::RpcError(e.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum UpdateChannelError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "No such channel with rpc_channel_id {}", _0)] - NoSuchChannel(u64), - #[display(fmt = "Failure to channel {}: {}", _0, _1)] - FailureToUpdateChannel(u64, String), -} - -impl HttpStatusCode for UpdateChannelError { - fn status_code(&self) -> StatusCode { - match self { - UpdateChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - UpdateChannelError::NoSuchChannel(_) => StatusCode::NOT_FOUND, - UpdateChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - UpdateChannelError::FailureToUpdateChannel(_, _) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for UpdateChannelError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => UpdateChannelError::NoSuchCoin(coin), - } - } -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum ListChannelsError { diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 2d0391c0e2..9e59f26709 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1,2 +1,3 @@ pub mod connect_to_lightning_node; pub mod open_channel; +pub mod update_channel; diff --git a/mm2src/coins/rpc_command/lightning/update_channel.rs b/mm2src/coins/rpc_command/lightning/update_channel.rs new file mode 100644 index 0000000000..9bc9fd01b8 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/update_channel.rs @@ -0,0 +1,84 @@ +use crate::lightning::ln_conf::ChannelOptions; +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::{async_blocking, HttpStatusCode}; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +pub type UpdateChannelResult = Result>; + +#[derive(Deserialize)] +pub struct UpdateChannelReq { + pub coin: String, + pub rpc_channel_id: u64, + pub channel_options: ChannelOptions, +} + +#[derive(Serialize)] +pub struct UpdateChannelResponse { + channel_options: ChannelOptions, +} + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum UpdateChannelError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "No such channel with rpc_channel_id {}", _0)] + NoSuchChannel(u64), + #[display(fmt = "Failure to channel {}: {}", _0, _1)] + FailureToUpdateChannel(u64, String), +} + +impl HttpStatusCode for UpdateChannelError { + fn status_code(&self) -> StatusCode { + match self { + UpdateChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + UpdateChannelError::NoSuchChannel(_) => StatusCode::NOT_FOUND, + UpdateChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + UpdateChannelError::FailureToUpdateChannel(_, _) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for UpdateChannelError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => UpdateChannelError::NoSuchCoin(coin), + } + } +} + +/// Updates configuration for an open channel. +pub async fn update_channel(ctx: MmArc, req: UpdateChannelReq) -> UpdateChannelResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(UpdateChannelError::UnsupportedCoin(e.ticker().to_string())), + }; + + let channel_details = ln_coin + .get_channel_by_rpc_id(req.rpc_channel_id) + .await + .ok_or(UpdateChannelError::NoSuchChannel(req.rpc_channel_id))?; + + async_blocking(move || { + let mut channel_options = ln_coin + .conf + .channel_options + .unwrap_or_else(|| req.channel_options.clone()); + if channel_options != req.channel_options { + channel_options.update_according_to(req.channel_options.clone()); + } + drop_mutability!(channel_options); + let channel_ids = &[channel_details.channel_id]; + let counterparty_node_id = channel_details.counterparty.node_id; + ln_coin + .channel_manager + .update_channel_config(&counterparty_node_id, channel_ids, &channel_options.clone().into()) + .map_to_mm(|e| UpdateChannelError::FailureToUpdateChannel(req.rpc_channel_id, format!("{:?}", e)))?; + Ok(UpdateChannelResponse { channel_options }) + }) + .await +} diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 852aab9643..c0313dafe5 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -45,10 +45,11 @@ use std::net::SocketAddr; cfg_native! { use coins::lightning::{add_trusted_node, close_channel, generate_invoice, get_channel_details, get_claimable_balances, get_payment_details, list_closed_channels_by_filter, list_open_channels_by_filter, - list_payments_by_filter, list_trusted_nodes, remove_trusted_node, send_payment, update_channel, + list_payments_by_filter, list_trusted_nodes, remove_trusted_node, send_payment, LightningCoin}; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; use coins::rpc_command::lightning::open_channel::open_channel; + use coins::rpc_command::lightning::update_channel::update_channel; use coins::z_coin::ZCoin; } From df6ae9ba093366a3fd910fecb53466ad418f0236 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 17:45:40 +0200 Subject: [PATCH 05/36] move list channels rpcs to rpc_command --- mm2src/coins/lightning.rs | 311 +++++------------- mm2src/coins/lightning/ln_errors.rs | 34 -- mm2src/coins/lightning/ln_serialization.rs | 46 +++ .../lightning/connect_to_lightning_node.rs | 12 +- .../rpc_command/lightning/list_channels.rs | 130 ++++++++ mm2src/coins/rpc_command/lightning/mod.rs | 1 + .../rpc_command/lightning/open_channel.rs | 56 ++-- .../rpc_command/lightning/update_channel.rs | 26 +- .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- 9 files changed, 317 insertions(+), 302 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/list_channels.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index d594e65a4e..3b2977cbad 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -13,7 +13,7 @@ mod ln_utils; use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum}; use crate::lightning::ln_errors::{TrustedNodeError, TrustedNodeResult}; use crate::lightning::ln_events::init_events_abort_handlers; -use crate::lightning::ln_serialization::PublicKeyForRPC; +use crate::lightning::ln_serialization::{ChannelDetailsForRPC, PublicKeyForRPC}; use crate::lightning::ln_sql::SqliteLightningDB; use crate::lightning::ln_storage::{NetworkGraph, TrustedNodesShared}; use crate::utxo::rpc_clients::UtxoRpcClientEnum; @@ -47,17 +47,15 @@ use lightning_invoice::payment; use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter}; use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; -use ln_db::{ClosedChannelsFilter, DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, - PaymentType}; +use ln_db::{DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, PaymentType}; use ln_errors::{ClaimableBalancesError, ClaimableBalancesResult, CloseChannelError, CloseChannelResult, EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, GetChannelDetailsError, GetChannelDetailsResult, GetPaymentDetailsError, GetPaymentDetailsResult, - ListChannelsError, ListChannelsResult, ListPaymentsError, ListPaymentsResult, SendPaymentError, - SendPaymentResult}; + ListPaymentsError, ListPaymentsResult, SendPaymentError, SendPaymentResult}; use ln_events::LightningEventHandler; use ln_filesystem_persister::LightningFilesystemPersister; use ln_p2p::{connect_to_node, PeerManager}; -use ln_platform::{h256_json_from_txid, Platform}; +use ln_platform::Platform; use ln_storage::{LightningStorage, NodesAddressesMapShared, Scorer}; use ln_utils::{ChainMonitor, ChannelManager}; use mm2_core::mm_ctx::MmArc; @@ -112,6 +110,31 @@ impl fmt::Debug for LightningCoin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "LightningCoin {{ conf: {:?} }}", self.conf) } } +#[derive(Deserialize)] +pub struct OpenChannelsFilter { + pub channel_id: Option, + pub counterparty_node_id: Option, + pub funding_tx: Option, + pub from_funding_value_sats: Option, + pub to_funding_value_sats: Option, + pub is_outbound: Option, + pub from_balance_msat: Option, + pub to_balance_msat: Option, + pub from_outbound_capacity_msat: Option, + pub to_outbound_capacity_msat: Option, + pub from_inbound_capacity_msat: Option, + pub to_inbound_capacity_msat: Option, + pub is_ready: Option, + pub is_usable: Option, + pub is_public: Option, +} + +pub(crate) struct GetOpenChannelsResult { + pub channels: Vec, + pub skipped: usize, + pub total: usize, +} + impl LightningCoin { pub fn platform_coin(&self) -> &UtxoStandardCoin { &self.platform.coin } @@ -221,12 +244,70 @@ impl LightningCoin { }) } - async fn get_open_channels_by_filter( + pub(crate) async fn get_open_channels_by_filter( &self, filter: Option, paging: PagingOptionsEnum, limit: usize, - ) -> ListChannelsResult { + ) -> GetOpenChannelsResult { + fn apply_open_channel_filter(channel_details: &ChannelDetailsForRPC, filter: &OpenChannelsFilter) -> bool { + let is_channel_id = + filter.channel_id.is_none() || Some(&channel_details.channel_id) == filter.channel_id.as_ref(); + + let is_counterparty_node_id = filter.counterparty_node_id.is_none() + || Some(&channel_details.counterparty_node_id) == filter.counterparty_node_id.as_ref(); + + let is_funding_tx = filter.funding_tx.is_none() || channel_details.funding_tx == filter.funding_tx; + + let is_from_funding_value_sats = + Some(&channel_details.funding_tx_value_sats) >= filter.from_funding_value_sats.as_ref(); + + let is_to_funding_value_sats = filter.to_funding_value_sats.is_none() + || Some(&channel_details.funding_tx_value_sats) <= filter.to_funding_value_sats.as_ref(); + + let is_outbound = + filter.is_outbound.is_none() || Some(&channel_details.is_outbound) == filter.is_outbound.as_ref(); + + let is_from_balance_msat = Some(&channel_details.balance_msat) >= filter.from_balance_msat.as_ref(); + + let is_to_balance_msat = filter.to_balance_msat.is_none() + || Some(&channel_details.balance_msat) <= filter.to_balance_msat.as_ref(); + + let is_from_outbound_capacity_msat = + Some(&channel_details.outbound_capacity_msat) >= filter.from_outbound_capacity_msat.as_ref(); + + let is_to_outbound_capacity_msat = filter.to_outbound_capacity_msat.is_none() + || Some(&channel_details.outbound_capacity_msat) <= filter.to_outbound_capacity_msat.as_ref(); + + let is_from_inbound_capacity_msat = + Some(&channel_details.inbound_capacity_msat) >= filter.from_inbound_capacity_msat.as_ref(); + + let is_to_inbound_capacity_msat = filter.to_inbound_capacity_msat.is_none() + || Some(&channel_details.inbound_capacity_msat) <= filter.to_inbound_capacity_msat.as_ref(); + + let is_confirmed = filter.is_ready.is_none() || Some(&channel_details.is_ready) == filter.is_ready.as_ref(); + + let is_usable = filter.is_usable.is_none() || Some(&channel_details.is_usable) == filter.is_usable.as_ref(); + + let is_public = filter.is_public.is_none() || Some(&channel_details.is_public) == filter.is_public.as_ref(); + + is_channel_id + && is_counterparty_node_id + && is_funding_tx + && is_from_funding_value_sats + && is_to_funding_value_sats + && is_outbound + && is_from_balance_msat + && is_to_balance_msat + && is_from_outbound_capacity_msat + && is_to_outbound_capacity_msat + && is_from_inbound_capacity_msat + && is_to_inbound_capacity_msat + && is_confirmed + && is_usable + && is_public + } + let mut total_open_channels: Vec = self.list_channels().await.into_iter().map(From::from).collect(); @@ -259,11 +340,11 @@ impl LightningCoin { open_channels_filtered[offset..].to_vec() }; - Ok(GetOpenChannelsResult { + GetOpenChannelsResult { channels, skipped: offset, total, - }) + } } } @@ -794,216 +875,6 @@ pub async fn start_lightning( }) } -#[derive(Deserialize)] -pub struct OpenChannelsFilter { - pub channel_id: Option, - pub counterparty_node_id: Option, - pub funding_tx: Option, - pub from_funding_value_sats: Option, - pub to_funding_value_sats: Option, - pub is_outbound: Option, - pub from_balance_msat: Option, - pub to_balance_msat: Option, - pub from_outbound_capacity_msat: Option, - pub to_outbound_capacity_msat: Option, - pub from_inbound_capacity_msat: Option, - pub to_inbound_capacity_msat: Option, - pub is_ready: Option, - pub is_usable: Option, - pub is_public: Option, -} - -fn apply_open_channel_filter(channel_details: &ChannelDetailsForRPC, filter: &OpenChannelsFilter) -> bool { - let is_channel_id = filter.channel_id.is_none() || Some(&channel_details.channel_id) == filter.channel_id.as_ref(); - - let is_counterparty_node_id = filter.counterparty_node_id.is_none() - || Some(&channel_details.counterparty_node_id) == filter.counterparty_node_id.as_ref(); - - let is_funding_tx = filter.funding_tx.is_none() || channel_details.funding_tx == filter.funding_tx; - - let is_from_funding_value_sats = - Some(&channel_details.funding_tx_value_sats) >= filter.from_funding_value_sats.as_ref(); - - let is_to_funding_value_sats = filter.to_funding_value_sats.is_none() - || Some(&channel_details.funding_tx_value_sats) <= filter.to_funding_value_sats.as_ref(); - - let is_outbound = filter.is_outbound.is_none() || Some(&channel_details.is_outbound) == filter.is_outbound.as_ref(); - - let is_from_balance_msat = Some(&channel_details.balance_msat) >= filter.from_balance_msat.as_ref(); - - let is_to_balance_msat = - filter.to_balance_msat.is_none() || Some(&channel_details.balance_msat) <= filter.to_balance_msat.as_ref(); - - let is_from_outbound_capacity_msat = - Some(&channel_details.outbound_capacity_msat) >= filter.from_outbound_capacity_msat.as_ref(); - - let is_to_outbound_capacity_msat = filter.to_outbound_capacity_msat.is_none() - || Some(&channel_details.outbound_capacity_msat) <= filter.to_outbound_capacity_msat.as_ref(); - - let is_from_inbound_capacity_msat = - Some(&channel_details.inbound_capacity_msat) >= filter.from_inbound_capacity_msat.as_ref(); - - let is_to_inbound_capacity_msat = filter.to_inbound_capacity_msat.is_none() - || Some(&channel_details.inbound_capacity_msat) <= filter.to_inbound_capacity_msat.as_ref(); - - let is_confirmed = filter.is_ready.is_none() || Some(&channel_details.is_ready) == filter.is_ready.as_ref(); - - let is_usable = filter.is_usable.is_none() || Some(&channel_details.is_usable) == filter.is_usable.as_ref(); - - let is_public = filter.is_public.is_none() || Some(&channel_details.is_public) == filter.is_public.as_ref(); - - is_channel_id - && is_counterparty_node_id - && is_funding_tx - && is_from_funding_value_sats - && is_to_funding_value_sats - && is_outbound - && is_from_balance_msat - && is_to_balance_msat - && is_from_outbound_capacity_msat - && is_to_outbound_capacity_msat - && is_from_inbound_capacity_msat - && is_to_inbound_capacity_msat - && is_confirmed - && is_usable - && is_public -} - -#[derive(Deserialize)] -pub struct ListOpenChannelsRequest { - pub coin: String, - pub filter: Option, - #[serde(default = "ten")] - limit: usize, - #[serde(default)] - paging_options: PagingOptionsEnum, -} - -#[derive(Clone, Serialize)] -pub struct ChannelDetailsForRPC { - pub rpc_channel_id: u64, - pub channel_id: H256Json, - pub counterparty_node_id: PublicKeyForRPC, - pub funding_tx: Option, - pub funding_tx_output_index: Option, - pub funding_tx_value_sats: u64, - /// True if the channel was initiated (and thus funded) by us. - pub is_outbound: bool, - pub balance_msat: u64, - pub outbound_capacity_msat: u64, - pub inbound_capacity_msat: u64, - // Channel is confirmed onchain, this means that funding_locked messages have been exchanged, - // the channel is not currently being shut down, and the required confirmation count has been reached. - pub is_ready: bool, - // Channel is confirmed and channel_ready messages have been exchanged, the peer is connected, - // and the channel is not currently negotiating a shutdown. - pub is_usable: bool, - // A publicly-announced channel. - pub is_public: bool, -} - -impl From for ChannelDetailsForRPC { - fn from(details: ChannelDetails) -> ChannelDetailsForRPC { - ChannelDetailsForRPC { - rpc_channel_id: details.user_channel_id, - channel_id: details.channel_id.into(), - counterparty_node_id: PublicKeyForRPC(details.counterparty.node_id), - funding_tx: details.funding_txo.map(|tx| h256_json_from_txid(tx.txid)), - funding_tx_output_index: details.funding_txo.map(|tx| tx.index), - funding_tx_value_sats: details.channel_value_satoshis, - is_outbound: details.is_outbound, - balance_msat: details.balance_msat, - outbound_capacity_msat: details.outbound_capacity_msat, - inbound_capacity_msat: details.inbound_capacity_msat, - is_ready: details.is_channel_ready, - is_usable: details.is_usable, - is_public: details.is_public, - } - } -} - -struct GetOpenChannelsResult { - pub channels: Vec, - pub skipped: usize, - pub total: usize, -} - -#[derive(Serialize)] -pub struct ListOpenChannelsResponse { - open_channels: Vec, - limit: usize, - skipped: usize, - total: usize, - total_pages: usize, - paging_options: PagingOptionsEnum, -} - -pub async fn list_open_channels_by_filter( - ctx: MmArc, - req: ListOpenChannelsRequest, -) -> ListChannelsResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(ListChannelsError::UnsupportedCoin(e.ticker().to_string())), - }; - - let result = ln_coin - .get_open_channels_by_filter(req.filter, req.paging_options.clone(), req.limit) - .await?; - - Ok(ListOpenChannelsResponse { - open_channels: result.channels, - limit: req.limit, - skipped: result.skipped, - total: result.total, - total_pages: calc_total_pages(result.total, req.limit), - paging_options: req.paging_options, - }) -} - -#[derive(Deserialize)] -pub struct ListClosedChannelsRequest { - pub coin: String, - pub filter: Option, - #[serde(default = "ten")] - limit: usize, - #[serde(default)] - paging_options: PagingOptionsEnum, -} - -#[derive(Serialize)] -pub struct ListClosedChannelsResponse { - closed_channels: Vec, - limit: usize, - skipped: usize, - total: usize, - total_pages: usize, - paging_options: PagingOptionsEnum, -} - -pub async fn list_closed_channels_by_filter( - ctx: MmArc, - req: ListClosedChannelsRequest, -) -> ListChannelsResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(ListChannelsError::UnsupportedCoin(e.ticker().to_string())), - }; - let closed_channels_res = ln_coin - .db - .get_closed_channels_by_filter(req.filter, req.paging_options.clone(), req.limit) - .await?; - - Ok(ListClosedChannelsResponse { - closed_channels: closed_channels_res.channels, - limit: req.limit, - skipped: closed_channels_res.skipped, - total: closed_channels_res.total, - total_pages: calc_total_pages(closed_channels_res.total, req.limit), - paging_options: req.paging_options, - }) -} - #[derive(Deserialize)] pub struct GetChannelDetailsRequest { pub coin: String, diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index f63bc873dd..4d58bb9eca 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -10,7 +10,6 @@ use rpc::v1::types::H256 as H256Json; use std::num::TryFromIntError; pub type EnableLightningResult = Result>; -pub type ListChannelsResult = Result>; pub type GetChannelDetailsResult = Result>; pub type GenerateInvoiceResult = Result>; pub type SendPaymentResult = Result>; @@ -73,39 +72,6 @@ impl From for EnableLightningError { fn from(e: UtxoRpcError) -> Self { EnableLightningError::RpcError(e.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum ListChannelsError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "DB error {}", _0)] - DbError(String), -} - -impl HttpStatusCode for ListChannelsError { - fn status_code(&self) -> StatusCode { - match self { - ListChannelsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - ListChannelsError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - ListChannelsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for ListChannelsError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => ListChannelsError::NoSuchCoin(coin), - } - } -} - -impl From for ListChannelsError { - fn from(err: SqlError) -> ListChannelsError { ListChannelsError::DbError(err.to_string()) } -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum GetChannelDetailsError { diff --git a/mm2src/coins/lightning/ln_serialization.rs b/mm2src/coins/lightning/ln_serialization.rs index 01b99d0bd0..d5ece746b6 100644 --- a/mm2src/coins/lightning/ln_serialization.rs +++ b/mm2src/coins/lightning/ln_serialization.rs @@ -1,3 +1,6 @@ +use crate::lightning::ln_platform::h256_json_from_txid; +use crate::H256Json; +use lightning::ln::channelmanager::ChannelDetails; use secp256k1v22::PublicKey; use serde::{de, Serialize, Serializer}; use std::fmt; @@ -94,6 +97,49 @@ impl<'de> de::Deserialize<'de> for PublicKeyForRPC { } } +#[derive(Clone, Serialize)] +pub struct ChannelDetailsForRPC { + pub rpc_channel_id: u64, + pub channel_id: H256Json, + pub counterparty_node_id: PublicKeyForRPC, + pub funding_tx: Option, + pub funding_tx_output_index: Option, + pub funding_tx_value_sats: u64, + /// True if the channel was initiated (and thus funded) by us. + pub is_outbound: bool, + pub balance_msat: u64, + pub outbound_capacity_msat: u64, + pub inbound_capacity_msat: u64, + // Channel is confirmed onchain, this means that funding_locked messages have been exchanged, + // the channel is not currently being shut down, and the required confirmation count has been reached. + pub is_ready: bool, + // Channel is confirmed and channel_ready messages have been exchanged, the peer is connected, + // and the channel is not currently negotiating a shutdown. + pub is_usable: bool, + // A publicly-announced channel. + pub is_public: bool, +} + +impl From for ChannelDetailsForRPC { + fn from(details: ChannelDetails) -> ChannelDetailsForRPC { + ChannelDetailsForRPC { + rpc_channel_id: details.user_channel_id, + channel_id: details.channel_id.into(), + counterparty_node_id: PublicKeyForRPC(details.counterparty.node_id), + funding_tx: details.funding_txo.map(|tx| h256_json_from_txid(tx.txid)), + funding_tx_output_index: details.funding_txo.map(|tx| tx.index), + funding_tx_value_sats: details.channel_value_satoshis, + is_outbound: details.is_outbound, + balance_msat: details.balance_msat, + outbound_capacity_msat: details.outbound_capacity_msat, + inbound_capacity_msat: details.inbound_capacity_msat, + is_ready: details.is_channel_ready, + is_usable: details.is_usable, + is_public: details.is_public, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs b/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs index abf3261c1f..0c9a71c561 100644 --- a/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs +++ b/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs @@ -11,12 +11,6 @@ use std::collections::hash_map::Entry; type ConnectToNodeResult = Result>; -#[derive(Deserialize)] -pub struct ConnectToNodeRequest { - pub coin: String, - pub node_address: NodeAddress, -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum ConnectToNodeError { @@ -66,6 +60,12 @@ impl From for ConnectToNodeError { fn from(err: ConnectionError) -> ConnectToNodeError { ConnectToNodeError::ConnectionError(err.to_string()) } } +#[derive(Deserialize)] +pub struct ConnectToNodeRequest { + pub coin: String, + pub node_address: NodeAddress, +} + /// Connect to a certain node on the lightning network. pub async fn connect_to_lightning_node(ctx: MmArc, req: ConnectToNodeRequest) -> ConnectToNodeResult { let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { diff --git a/mm2src/coins/rpc_command/lightning/list_channels.rs b/mm2src/coins/rpc_command/lightning/list_channels.rs new file mode 100644 index 0000000000..a96106a99f --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/list_channels.rs @@ -0,0 +1,130 @@ +use crate::lightning::ln_db::{ClosedChannelsFilter, DBChannelDetails, LightningDB}; +use crate::lightning::ln_serialization::ChannelDetailsForRPC; +use crate::lightning::OpenChannelsFilter; +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::{calc_total_pages, ten, HttpStatusCode, PagingOptionsEnum}; +use db_common::sqlite::rusqlite::Error as SqlError; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +type ListChannelsResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum ListChannelsError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl HttpStatusCode for ListChannelsError { + fn status_code(&self) -> StatusCode { + match self { + ListChannelsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + ListChannelsError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + ListChannelsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for ListChannelsError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => ListChannelsError::NoSuchCoin(coin), + } + } +} + +impl From for ListChannelsError { + fn from(err: SqlError) -> ListChannelsError { ListChannelsError::DbError(err.to_string()) } +} + +#[derive(Deserialize)] +pub struct ListOpenChannelsRequest { + pub coin: String, + pub filter: Option, + #[serde(default = "ten")] + limit: usize, + #[serde(default)] + paging_options: PagingOptionsEnum, +} + +#[derive(Serialize)] +pub struct ListOpenChannelsResponse { + open_channels: Vec, + limit: usize, + skipped: usize, + total: usize, + total_pages: usize, + paging_options: PagingOptionsEnum, +} + +pub async fn list_open_channels_by_filter( + ctx: MmArc, + req: ListOpenChannelsRequest, +) -> ListChannelsResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(ListChannelsError::UnsupportedCoin(e.ticker().to_string())), + }; + + let result = ln_coin + .get_open_channels_by_filter(req.filter, req.paging_options.clone(), req.limit) + .await; + + Ok(ListOpenChannelsResponse { + open_channels: result.channels, + limit: req.limit, + skipped: result.skipped, + total: result.total, + total_pages: calc_total_pages(result.total, req.limit), + paging_options: req.paging_options, + }) +} + +#[derive(Deserialize)] +pub struct ListClosedChannelsRequest { + pub coin: String, + pub filter: Option, + #[serde(default = "ten")] + limit: usize, + #[serde(default)] + paging_options: PagingOptionsEnum, +} + +#[derive(Serialize)] +pub struct ListClosedChannelsResponse { + closed_channels: Vec, + limit: usize, + skipped: usize, + total: usize, + total_pages: usize, + paging_options: PagingOptionsEnum, +} + +pub async fn list_closed_channels_by_filter( + ctx: MmArc, + req: ListClosedChannelsRequest, +) -> ListChannelsResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(ListChannelsError::UnsupportedCoin(e.ticker().to_string())), + }; + let closed_channels_res = ln_coin + .db + .get_closed_channels_by_filter(req.filter, req.paging_options.clone(), req.limit) + .await?; + + Ok(ListClosedChannelsResponse { + closed_channels: closed_channels_res.channels, + limit: req.limit, + skipped: closed_channels_res.skipped, + total: closed_channels_res.total, + total_pages: calc_total_pages(closed_channels_res.total, req.limit), + paging_options: req.paging_options, + }) +} diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 9e59f26709..2214df2e3c 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1,3 +1,4 @@ pub mod connect_to_lightning_node; +pub mod list_channels; pub mod open_channel; pub mod update_channel; diff --git a/mm2src/coins/rpc_command/lightning/open_channel.rs b/mm2src/coins/rpc_command/lightning/open_channel.rs index 9dbeb3a495..e312f7c2fe 100644 --- a/mm2src/coins/rpc_command/lightning/open_channel.rs +++ b/mm2src/coins/rpc_command/lightning/open_channel.rs @@ -19,34 +19,7 @@ use mm2_err_handle::prelude::*; use mm2_number::BigDecimal; use script::Builder; -pub type OpenChannelResult = Result>; - -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(tag = "type", content = "value")] -pub enum ChannelOpenAmount { - Exact(BigDecimal), - Max, -} - -#[derive(Deserialize)] -pub struct OpenChannelRequest { - pub coin: String, - pub node_address: NodeAddress, - pub amount: ChannelOpenAmount, - /// The amount to push to the counterparty as part of the open, in milli-satoshi. Creates inbound liquidity for the channel. - /// By setting push_msat to a value, opening channel request will be equivalent to opening a channel then sending a payment with - /// the push_msat amount. - #[serde(default)] - pub push_msat: u64, - pub channel_options: Option, - pub channel_configs: Option, -} - -#[derive(Serialize)] -pub struct OpenChannelResponse { - rpc_channel_id: u64, - node_address: NodeAddress, -} +type OpenChannelResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] @@ -131,6 +104,33 @@ impl From for OpenChannelError { fn from(err: SqlError) -> OpenChannelError { OpenChannelError::DbError(err.to_string()) } } +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(tag = "type", content = "value")] +pub enum ChannelOpenAmount { + Exact(BigDecimal), + Max, +} + +#[derive(Deserialize)] +pub struct OpenChannelRequest { + pub coin: String, + pub node_address: NodeAddress, + pub amount: ChannelOpenAmount, + /// The amount to push to the counterparty as part of the open, in milli-satoshi. Creates inbound liquidity for the channel. + /// By setting push_msat to a value, opening channel request will be equivalent to opening a channel then sending a payment with + /// the push_msat amount. + #[serde(default)] + pub push_msat: u64, + pub channel_options: Option, + pub channel_configs: Option, +} + +#[derive(Serialize)] +pub struct OpenChannelResponse { + rpc_channel_id: u64, + node_address: NodeAddress, +} + /// Opens a channel on the lightning network. pub async fn open_channel(ctx: MmArc, req: OpenChannelRequest) -> OpenChannelResult { let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { diff --git a/mm2src/coins/rpc_command/lightning/update_channel.rs b/mm2src/coins/rpc_command/lightning/update_channel.rs index 9bc9fd01b8..93342c1e3a 100644 --- a/mm2src/coins/rpc_command/lightning/update_channel.rs +++ b/mm2src/coins/rpc_command/lightning/update_channel.rs @@ -5,19 +5,7 @@ use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -pub type UpdateChannelResult = Result>; - -#[derive(Deserialize)] -pub struct UpdateChannelReq { - pub coin: String, - pub rpc_channel_id: u64, - pub channel_options: ChannelOptions, -} - -#[derive(Serialize)] -pub struct UpdateChannelResponse { - channel_options: ChannelOptions, -} +type UpdateChannelResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] @@ -51,6 +39,18 @@ impl From for UpdateChannelError { } } +#[derive(Deserialize)] +pub struct UpdateChannelReq { + pub coin: String, + pub rpc_channel_id: u64, + pub channel_options: ChannelOptions, +} + +#[derive(Serialize)] +pub struct UpdateChannelResponse { + channel_options: ChannelOptions, +} + /// Updates configuration for an open channel. pub async fn update_channel(ctx: MmArc, req: UpdateChannelReq) -> UpdateChannelResult { let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index c0313dafe5..59bf0a88a2 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -44,10 +44,11 @@ use std::net::SocketAddr; cfg_native! { use coins::lightning::{add_trusted_node, close_channel, generate_invoice, get_channel_details, - get_claimable_balances, get_payment_details, list_closed_channels_by_filter, list_open_channels_by_filter, + get_claimable_balances, get_payment_details, list_payments_by_filter, list_trusted_nodes, remove_trusted_node, send_payment, LightningCoin}; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; + use coins::rpc_command::lightning::list_channels::{list_closed_channels_by_filter, list_open_channels_by_filter}; use coins::rpc_command::lightning::open_channel::open_channel; use coins::rpc_command::lightning::update_channel::update_channel; use coins::z_coin::ZCoin; From 48d748e368f8d941b708c78b12f6a90efb352ab2 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 18:45:55 +0200 Subject: [PATCH 06/36] move get_channel_details, get_claimable_balances, trusted nodes rpcs to rpc_command --- mm2src/coins/lightning.rs | 248 +----------------- mm2src/coins/lightning/ln_errors.rs | 97 ------- mm2src/coins/lightning/ln_serialization.rs | 83 ++++++ .../lightning/get_channel_details.rs | 81 ++++++ .../lightning/get_claimable_balances.rs | 67 +++++ mm2src/coins/rpc_command/lightning/mod.rs | 3 + .../rpc_command/lightning/trusted_nodes.rs | 120 +++++++++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 9 +- 8 files changed, 367 insertions(+), 341 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/get_channel_details.rs create mode 100644 mm2src/coins/rpc_command/lightning/get_claimable_balances.rs create mode 100644 mm2src/coins/rpc_command/lightning/trusted_nodes.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 3b2977cbad..666436b6a2 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -11,11 +11,6 @@ pub(crate) mod ln_storage; mod ln_utils; use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum}; -use crate::lightning::ln_errors::{TrustedNodeError, TrustedNodeResult}; -use crate::lightning::ln_events::init_events_abort_handlers; -use crate::lightning::ln_serialization::{ChannelDetailsForRPC, PublicKeyForRPC}; -use crate::lightning::ln_sql::SqliteLightningDB; -use crate::lightning::ln_storage::{NetworkGraph, TrustedNodesShared}; use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::BlockchainNetwork; @@ -36,7 +31,6 @@ use common::{async_blocking, calc_total_pages, log, now_ms, ten, PagingOptionsEn use futures::{FutureExt, TryFutureExt}; use futures01::Future; use keys::{hash::H256, CompactSignature, KeyPair, Private, Public}; -use lightning::chain::channelmonitor::Balance; use lightning::chain::keysinterface::{KeysInterface, KeysManager, Recipient}; use lightning::chain::Access; use lightning::ln::channelmanager::{ChannelDetails, MIN_FINAL_CLTV_EXPIRY}; @@ -48,15 +42,16 @@ use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; use ln_db::{DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, PaymentType}; -use ln_errors::{ClaimableBalancesError, ClaimableBalancesResult, CloseChannelError, CloseChannelResult, - EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, - GetChannelDetailsError, GetChannelDetailsResult, GetPaymentDetailsError, GetPaymentDetailsResult, +use ln_errors::{CloseChannelError, CloseChannelResult, EnableLightningError, EnableLightningResult, + GenerateInvoiceError, GenerateInvoiceResult, GetPaymentDetailsError, GetPaymentDetailsResult, ListPaymentsError, ListPaymentsResult, SendPaymentError, SendPaymentResult}; -use ln_events::LightningEventHandler; +use ln_events::{init_events_abort_handlers, LightningEventHandler}; use ln_filesystem_persister::LightningFilesystemPersister; use ln_p2p::{connect_to_node, PeerManager}; use ln_platform::Platform; -use ln_storage::{LightningStorage, NodesAddressesMapShared, Scorer}; +use ln_serialization::{ChannelDetailsForRPC, PublicKeyForRPC}; +use ln_sql::SqliteLightningDB; +use ln_storage::{LightningStorage, NetworkGraph, NodesAddressesMapShared, Scorer, TrustedNodesShared}; use ln_utils::{ChainMonitor, ChannelManager}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; @@ -141,7 +136,7 @@ impl LightningCoin { #[inline] fn my_node_id(&self) -> String { self.channel_manager.get_our_node_id().to_string() } - async fn list_channels(&self) -> Vec { + pub(crate) async fn list_channels(&self) -> Vec { let channel_manager = self.channel_manager.clone(); async_blocking(move || channel_manager.list_channels()).await } @@ -875,42 +870,6 @@ pub async fn start_lightning( }) } -#[derive(Deserialize)] -pub struct GetChannelDetailsRequest { - pub coin: String, - pub rpc_channel_id: u64, -} - -#[derive(Serialize)] -#[serde(tag = "status", content = "details")] -pub enum GetChannelDetailsResponse { - Open(ChannelDetailsForRPC), - Closed(DBChannelDetails), -} - -pub async fn get_channel_details( - ctx: MmArc, - req: GetChannelDetailsRequest, -) -> GetChannelDetailsResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(GetChannelDetailsError::UnsupportedCoin(e.ticker().to_string())), - }; - - let channel_details = match ln_coin.get_channel_by_rpc_id(req.rpc_channel_id).await { - Some(details) => GetChannelDetailsResponse::Open(details.into()), - None => GetChannelDetailsResponse::Closed( - ln_coin - .db - .get_channel_from_db(req.rpc_channel_id) - .await? - .ok_or(GetChannelDetailsError::NoSuchChannel(req.rpc_channel_id))?, - ), - }; - - Ok(channel_details) -} - #[derive(Deserialize)] pub struct GenerateInvoiceRequest { pub coin: String, @@ -1254,196 +1213,3 @@ pub async fn close_channel(ctx: MmArc, req: CloseChannelReq) -> CloseChannelResu req.rpc_channel_id )) } - -/// Details about the balance(s) available for spending once the channel appears on chain. -#[derive(Serialize)] -pub enum ClaimableBalance { - /// The channel is not yet closed (or the commitment or closing transaction has not yet - /// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is - /// force-closed now. - ClaimableOnChannelClose { - /// The amount available to claim, in satoshis, excluding the on-chain fees which will be - /// required to do so. - claimable_amount_satoshis: u64, - }, - /// The channel has been closed, and the given balance is ours but awaiting confirmations until - /// we consider it spendable. - ClaimableAwaitingConfirmations { - /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which - /// were spent in broadcasting the transaction. - claimable_amount_satoshis: u64, - /// The height at which an [`Event::SpendableOutputs`] event will be generated for this - /// amount. - confirmation_height: u32, - }, - /// The channel has been closed, and the given balance should be ours but awaiting spending - /// transaction confirmation. If the spending transaction does not confirm in time, it is - /// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain. - /// - /// Once the spending transaction confirms, before it has reached enough confirmations to be - /// considered safe from chain reorganizations, the balance will instead be provided via - /// [`Balance::ClaimableAwaitingConfirmations`]. - ContentiousClaimable { - /// The amount available to claim, in satoshis, excluding the on-chain fees which will be - /// required to do so. - claimable_amount_satoshis: u64, - /// The height at which the counterparty may be able to claim the balance if we have not - /// done so. - timeout_height: u32, - }, - /// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain - /// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat - /// likely to be claimed by our counterparty before we do. - MaybeClaimableHTLCAwaitingTimeout { - /// The amount available to claim, in satoshis, excluding the on-chain fees which will be - /// required to do so. - claimable_amount_satoshis: u64, - /// The height at which we will be able to claim the balance if our counterparty has not - /// done so. - claimable_height: u32, - }, -} - -impl From for ClaimableBalance { - fn from(balance: Balance) -> Self { - match balance { - Balance::ClaimableOnChannelClose { - claimable_amount_satoshis, - } => ClaimableBalance::ClaimableOnChannelClose { - claimable_amount_satoshis, - }, - Balance::ClaimableAwaitingConfirmations { - claimable_amount_satoshis, - confirmation_height, - } => ClaimableBalance::ClaimableAwaitingConfirmations { - claimable_amount_satoshis, - confirmation_height, - }, - Balance::ContentiousClaimable { - claimable_amount_satoshis, - timeout_height, - } => ClaimableBalance::ContentiousClaimable { - claimable_amount_satoshis, - timeout_height, - }, - Balance::MaybeClaimableHTLCAwaitingTimeout { - claimable_amount_satoshis, - claimable_height, - } => ClaimableBalance::MaybeClaimableHTLCAwaitingTimeout { - claimable_amount_satoshis, - claimable_height, - }, - } - } -} - -#[derive(Deserialize)] -pub struct ClaimableBalancesReq { - pub coin: String, - #[serde(default)] - pub include_open_channels_balances: bool, -} - -pub async fn get_claimable_balances( - ctx: MmArc, - req: ClaimableBalancesReq, -) -> ClaimableBalancesResult> { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(ClaimableBalancesError::UnsupportedCoin(e.ticker().to_string())), - }; - let ignored_channels = if req.include_open_channels_balances { - Vec::new() - } else { - ln_coin.list_channels().await - }; - let claimable_balances = async_blocking(move || { - ln_coin - .chain_monitor - .get_claimable_balances(&ignored_channels.iter().collect::>()[..]) - .into_iter() - .map(From::from) - .collect() - }) - .await; - - Ok(claimable_balances) -} - -#[derive(Deserialize)] -pub struct AddTrustedNodeReq { - pub coin: String, - pub node_id: PublicKeyForRPC, -} - -#[derive(Serialize)] -pub struct AddTrustedNodeResponse { - pub added_node: PublicKeyForRPC, -} - -pub async fn add_trusted_node(ctx: MmArc, req: AddTrustedNodeReq) -> TrustedNodeResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())), - }; - - if ln_coin.trusted_nodes.lock().insert(req.node_id.clone().into()) { - ln_coin.persister.save_trusted_nodes(ln_coin.trusted_nodes).await?; - } - - Ok(AddTrustedNodeResponse { - added_node: req.node_id, - }) -} - -#[derive(Deserialize)] -pub struct RemoveTrustedNodeReq { - pub coin: String, - pub node_id: PublicKeyForRPC, -} - -#[derive(Serialize)] -pub struct RemoveTrustedNodeResponse { - pub removed_node: PublicKeyForRPC, -} - -pub async fn remove_trusted_node( - ctx: MmArc, - req: RemoveTrustedNodeReq, -) -> TrustedNodeResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())), - }; - - if ln_coin.trusted_nodes.lock().remove(&req.node_id.clone().into()) { - ln_coin.persister.save_trusted_nodes(ln_coin.trusted_nodes).await?; - } - - Ok(RemoveTrustedNodeResponse { - removed_node: req.node_id, - }) -} - -#[derive(Deserialize)] -pub struct ListTrustedNodesReq { - pub coin: String, -} - -#[derive(Serialize)] -pub struct ListTrustedNodesResponse { - trusted_nodes: Vec, -} - -pub async fn list_trusted_nodes(ctx: MmArc, req: ListTrustedNodesReq) -> TrustedNodeResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())), - }; - - let trusted_nodes = ln_coin.trusted_nodes.lock().clone(); - - Ok(ListTrustedNodesResponse { - trusted_nodes: trusted_nodes.into_iter().map(PublicKeyForRPC).collect(), - }) -} diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index 4d58bb9eca..9782ef786e 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -10,15 +10,12 @@ use rpc::v1::types::H256 as H256Json; use std::num::TryFromIntError; pub type EnableLightningResult = Result>; -pub type GetChannelDetailsResult = Result>; pub type GenerateInvoiceResult = Result>; pub type SendPaymentResult = Result>; pub type ListPaymentsResult = Result>; pub type GetPaymentDetailsResult = Result>; pub type CloseChannelResult = Result>; -pub type ClaimableBalancesResult = Result>; pub type SaveChannelClosingResult = Result>; -pub type TrustedNodeResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] @@ -72,41 +69,6 @@ impl From for EnableLightningError { fn from(e: UtxoRpcError) -> Self { EnableLightningError::RpcError(e.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum GetChannelDetailsError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "Channel with rpc id: {} is not found", _0)] - NoSuchChannel(u64), - #[display(fmt = "DB error {}", _0)] - DbError(String), -} - -impl HttpStatusCode for GetChannelDetailsError { - fn status_code(&self) -> StatusCode { - match self { - GetChannelDetailsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - GetChannelDetailsError::NoSuchCoin(_) | GetChannelDetailsError::NoSuchChannel(_) => StatusCode::NOT_FOUND, - GetChannelDetailsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for GetChannelDetailsError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => GetChannelDetailsError::NoSuchCoin(coin), - } - } -} - -impl From for GetChannelDetailsError { - fn from(err: SqlError) -> GetChannelDetailsError { GetChannelDetailsError::DbError(err.to_string()) } -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum GenerateInvoiceError { @@ -290,32 +252,6 @@ impl From for CloseChannelError { } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum ClaimableBalancesError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), -} - -impl HttpStatusCode for ClaimableBalancesError { - fn status_code(&self) -> StatusCode { - match self { - ClaimableBalancesError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - ClaimableBalancesError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - } - } -} - -impl From for ClaimableBalancesError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => ClaimableBalancesError::NoSuchCoin(coin), - } - } -} - #[derive(Display, PartialEq)] pub enum SaveChannelClosingError { #[display(fmt = "DB error: {}", _0)] @@ -341,36 +277,3 @@ impl From for SaveChannelClosingError { impl From for SaveChannelClosingError { fn from(err: TryFromIntError) -> SaveChannelClosingError { SaveChannelClosingError::ConversionError(err) } } - -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum TrustedNodeError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "I/O error {}", _0)] - IOError(String), -} - -impl HttpStatusCode for TrustedNodeError { - fn status_code(&self) -> StatusCode { - match self { - TrustedNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - TrustedNodeError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - TrustedNodeError::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for TrustedNodeError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => TrustedNodeError::NoSuchCoin(coin), - } - } -} - -impl From for TrustedNodeError { - fn from(err: std::io::Error) -> TrustedNodeError { TrustedNodeError::IOError(err.to_string()) } -} diff --git a/mm2src/coins/lightning/ln_serialization.rs b/mm2src/coins/lightning/ln_serialization.rs index d5ece746b6..f7b7c16655 100644 --- a/mm2src/coins/lightning/ln_serialization.rs +++ b/mm2src/coins/lightning/ln_serialization.rs @@ -1,5 +1,6 @@ use crate::lightning::ln_platform::h256_json_from_txid; use crate::H256Json; +use lightning::chain::channelmonitor::Balance; use lightning::ln::channelmanager::ChannelDetails; use secp256k1v22::PublicKey; use serde::{de, Serialize, Serializer}; @@ -140,6 +141,88 @@ impl From for ChannelDetailsForRPC { } } +/// Details about the balance(s) available for spending once the channel appears on chain. +#[derive(Serialize)] +pub enum ClaimableBalance { + /// The channel is not yet closed (or the commitment or closing transaction has not yet + /// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is + /// force-closed now. + ClaimableOnChannelClose { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + }, + /// The channel has been closed, and the given balance is ours but awaiting confirmations until + /// we consider it spendable. + ClaimableAwaitingConfirmations { + /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which + /// were spent in broadcasting the transaction. + claimable_amount_satoshis: u64, + /// The height at which an [`Event::SpendableOutputs`] event will be generated for this + /// amount. + confirmation_height: u32, + }, + /// The channel has been closed, and the given balance should be ours but awaiting spending + /// transaction confirmation. If the spending transaction does not confirm in time, it is + /// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain. + /// + /// Once the spending transaction confirms, before it has reached enough confirmations to be + /// considered safe from chain reorganizations, the balance will instead be provided via + /// [`Balance::ClaimableAwaitingConfirmations`]. + ContentiousClaimable { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + /// The height at which the counterparty may be able to claim the balance if we have not + /// done so. + timeout_height: u32, + }, + /// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain + /// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat + /// likely to be claimed by our counterparty before we do. + MaybeClaimableHTLCAwaitingTimeout { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + /// The height at which we will be able to claim the balance if our counterparty has not + /// done so. + claimable_height: u32, + }, +} + +impl From for ClaimableBalance { + fn from(balance: Balance) -> Self { + match balance { + Balance::ClaimableOnChannelClose { + claimable_amount_satoshis, + } => ClaimableBalance::ClaimableOnChannelClose { + claimable_amount_satoshis, + }, + Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis, + confirmation_height, + } => ClaimableBalance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis, + confirmation_height, + }, + Balance::ContentiousClaimable { + claimable_amount_satoshis, + timeout_height, + } => ClaimableBalance::ContentiousClaimable { + claimable_amount_satoshis, + timeout_height, + }, + Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis, + claimable_height, + } => ClaimableBalance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis, + claimable_height, + }, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/mm2src/coins/rpc_command/lightning/get_channel_details.rs b/mm2src/coins/rpc_command/lightning/get_channel_details.rs new file mode 100644 index 0000000000..f6c18ae633 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/get_channel_details.rs @@ -0,0 +1,81 @@ +use crate::lightning::ln_db::{DBChannelDetails, LightningDB}; +use crate::lightning::ln_serialization::ChannelDetailsForRPC; +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::HttpStatusCode; +use db_common::sqlite::rusqlite::Error as SqlError; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +type GetChannelDetailsResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum GetChannelDetailsError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "Channel with rpc id: {} is not found", _0)] + NoSuchChannel(u64), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl HttpStatusCode for GetChannelDetailsError { + fn status_code(&self) -> StatusCode { + match self { + GetChannelDetailsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + GetChannelDetailsError::NoSuchCoin(_) | GetChannelDetailsError::NoSuchChannel(_) => StatusCode::NOT_FOUND, + GetChannelDetailsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for GetChannelDetailsError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => GetChannelDetailsError::NoSuchCoin(coin), + } + } +} + +impl From for GetChannelDetailsError { + fn from(err: SqlError) -> GetChannelDetailsError { GetChannelDetailsError::DbError(err.to_string()) } +} + +#[derive(Deserialize)] +pub struct GetChannelDetailsRequest { + pub coin: String, + pub rpc_channel_id: u64, +} + +#[derive(Serialize)] +#[serde(tag = "status", content = "details")] +pub enum GetChannelDetailsResponse { + Open(ChannelDetailsForRPC), + Closed(DBChannelDetails), +} + +pub async fn get_channel_details( + ctx: MmArc, + req: GetChannelDetailsRequest, +) -> GetChannelDetailsResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(GetChannelDetailsError::UnsupportedCoin(e.ticker().to_string())), + }; + + let channel_details = match ln_coin.get_channel_by_rpc_id(req.rpc_channel_id).await { + Some(details) => GetChannelDetailsResponse::Open(details.into()), + None => GetChannelDetailsResponse::Closed( + ln_coin + .db + .get_channel_from_db(req.rpc_channel_id) + .await? + .ok_or(GetChannelDetailsError::NoSuchChannel(req.rpc_channel_id))?, + ), + }; + + Ok(channel_details) +} diff --git a/mm2src/coins/rpc_command/lightning/get_claimable_balances.rs b/mm2src/coins/rpc_command/lightning/get_claimable_balances.rs new file mode 100644 index 0000000000..5d1b84dad1 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/get_claimable_balances.rs @@ -0,0 +1,67 @@ +use crate::lightning::ln_serialization::ClaimableBalance; +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::{async_blocking, HttpStatusCode}; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +type ClaimableBalancesResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum ClaimableBalancesError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), +} + +impl HttpStatusCode for ClaimableBalancesError { + fn status_code(&self) -> StatusCode { + match self { + ClaimableBalancesError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + ClaimableBalancesError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + } + } +} + +impl From for ClaimableBalancesError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => ClaimableBalancesError::NoSuchCoin(coin), + } + } +} + +#[derive(Deserialize)] +pub struct ClaimableBalancesReq { + pub coin: String, + #[serde(default)] + pub include_open_channels_balances: bool, +} + +pub async fn get_claimable_balances( + ctx: MmArc, + req: ClaimableBalancesReq, +) -> ClaimableBalancesResult> { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(ClaimableBalancesError::UnsupportedCoin(e.ticker().to_string())), + }; + let ignored_channels = if req.include_open_channels_balances { + Vec::new() + } else { + ln_coin.list_channels().await + }; + let claimable_balances = async_blocking(move || { + ln_coin + .chain_monitor + .get_claimable_balances(&ignored_channels.iter().collect::>()[..]) + .into_iter() + .map(From::from) + .collect() + }) + .await; + + Ok(claimable_balances) +} diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 2214df2e3c..dc2ce13fb7 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1,4 +1,7 @@ pub mod connect_to_lightning_node; +pub mod get_channel_details; +pub mod get_claimable_balances; pub mod list_channels; pub mod open_channel; +pub mod trusted_nodes; pub mod update_channel; diff --git a/mm2src/coins/rpc_command/lightning/trusted_nodes.rs b/mm2src/coins/rpc_command/lightning/trusted_nodes.rs new file mode 100644 index 0000000000..6f0f2fd36e --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/trusted_nodes.rs @@ -0,0 +1,120 @@ +use crate::lightning::ln_serialization::PublicKeyForRPC; +use crate::lightning::ln_storage::LightningStorage; +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::HttpStatusCode; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +type TrustedNodeResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum TrustedNodeError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "I/O error {}", _0)] + IOError(String), +} + +impl HttpStatusCode for TrustedNodeError { + fn status_code(&self) -> StatusCode { + match self { + TrustedNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + TrustedNodeError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + TrustedNodeError::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for TrustedNodeError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => TrustedNodeError::NoSuchCoin(coin), + } + } +} + +impl From for TrustedNodeError { + fn from(err: std::io::Error) -> TrustedNodeError { TrustedNodeError::IOError(err.to_string()) } +} + +#[derive(Deserialize)] +pub struct AddTrustedNodeReq { + pub coin: String, + pub node_id: PublicKeyForRPC, +} + +#[derive(Serialize)] +pub struct AddTrustedNodeResponse { + pub added_node: PublicKeyForRPC, +} + +pub async fn add_trusted_node(ctx: MmArc, req: AddTrustedNodeReq) -> TrustedNodeResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())), + }; + + if ln_coin.trusted_nodes.lock().insert(req.node_id.clone().into()) { + ln_coin.persister.save_trusted_nodes(ln_coin.trusted_nodes).await?; + } + + Ok(AddTrustedNodeResponse { + added_node: req.node_id, + }) +} + +#[derive(Deserialize)] +pub struct RemoveTrustedNodeReq { + pub coin: String, + pub node_id: PublicKeyForRPC, +} + +#[derive(Serialize)] +pub struct RemoveTrustedNodeResponse { + pub removed_node: PublicKeyForRPC, +} + +pub async fn remove_trusted_node( + ctx: MmArc, + req: RemoveTrustedNodeReq, +) -> TrustedNodeResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())), + }; + + if ln_coin.trusted_nodes.lock().remove(&req.node_id.clone().into()) { + ln_coin.persister.save_trusted_nodes(ln_coin.trusted_nodes).await?; + } + + Ok(RemoveTrustedNodeResponse { + removed_node: req.node_id, + }) +} + +#[derive(Deserialize)] +pub struct ListTrustedNodesReq { + pub coin: String, +} + +#[derive(Serialize)] +pub struct ListTrustedNodesResponse { + trusted_nodes: Vec, +} + +pub async fn list_trusted_nodes(ctx: MmArc, req: ListTrustedNodesReq) -> TrustedNodeResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())), + }; + + let trusted_nodes = ln_coin.trusted_nodes.lock().clone(); + + Ok(ListTrustedNodesResponse { + trusted_nodes: trusted_nodes.into_iter().map(PublicKeyForRPC).collect(), + }) +} diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 59bf0a88a2..744cdbf6b2 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -43,13 +43,16 @@ use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; cfg_native! { - use coins::lightning::{add_trusted_node, close_channel, generate_invoice, get_channel_details, - get_claimable_balances, get_payment_details, - list_payments_by_filter, list_trusted_nodes, remove_trusted_node, send_payment, + use coins::lightning::{close_channel, generate_invoice, + get_payment_details, + list_payments_by_filter, send_payment, LightningCoin}; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; + use coins::rpc_command::lightning::get_channel_details::get_channel_details; + use coins::rpc_command::lightning::get_claimable_balances::get_claimable_balances; use coins::rpc_command::lightning::list_channels::{list_closed_channels_by_filter, list_open_channels_by_filter}; use coins::rpc_command::lightning::open_channel::open_channel; + use coins::rpc_command::lightning::trusted_nodes::{add_trusted_node, list_trusted_nodes, remove_trusted_node}; use coins::rpc_command::lightning::update_channel::update_channel; use coins::z_coin::ZCoin; } From 26806bed41b3cf42dad8cdd6491b0ab7444f4322 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 18:50:14 +0200 Subject: [PATCH 07/36] move close_channel to rpc_command --- mm2src/coins/lightning.rs | 51 +----------- mm2src/coins/lightning/ln_errors.rs | 32 ------- .../rpc_command/lightning/close_channel.rs | 83 +++++++++++++++++++ mm2src/coins/rpc_command/lightning/mod.rs | 1 + .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- 5 files changed, 89 insertions(+), 81 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/close_channel.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 666436b6a2..e03d5ee348 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -42,9 +42,9 @@ use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; use ln_db::{DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, PaymentType}; -use ln_errors::{CloseChannelError, CloseChannelResult, EnableLightningError, EnableLightningResult, - GenerateInvoiceError, GenerateInvoiceResult, GetPaymentDetailsError, GetPaymentDetailsResult, - ListPaymentsError, ListPaymentsResult, SendPaymentError, SendPaymentResult}; +use ln_errors::{EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, + GetPaymentDetailsError, GetPaymentDetailsResult, ListPaymentsError, ListPaymentsResult, + SendPaymentError, SendPaymentResult}; use ln_events::{init_events_abort_handlers, LightningEventHandler}; use ln_filesystem_persister::LightningFilesystemPersister; use ln_p2p::{connect_to_node, PeerManager}; @@ -1168,48 +1168,3 @@ pub async fn get_payment_details( MmError::err(GetPaymentDetailsError::NoSuchPayment(req.payment_hash)) } - -#[derive(Deserialize)] -pub struct CloseChannelReq { - pub coin: String, - pub rpc_channel_id: u64, - #[serde(default)] - pub force_close: bool, -} - -pub async fn close_channel(ctx: MmArc, req: CloseChannelReq) -> CloseChannelResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(CloseChannelError::UnsupportedCoin(e.ticker().to_string())), - }; - - let channel_details = ln_coin - .get_channel_by_rpc_id(req.rpc_channel_id) - .await - .ok_or(CloseChannelError::NoSuchChannel(req.rpc_channel_id))?; - let channel_id = channel_details.channel_id; - let counterparty_node_id = channel_details.counterparty.node_id; - - if req.force_close { - async_blocking(move || { - ln_coin - .channel_manager - .force_close_broadcasting_latest_txn(&channel_id, &counterparty_node_id) - .map_to_mm(|e| CloseChannelError::CloseChannelError(format!("{:?}", e))) - }) - .await?; - } else { - async_blocking(move || { - ln_coin - .channel_manager - .close_channel(&channel_id, &counterparty_node_id) - .map_to_mm(|e| CloseChannelError::CloseChannelError(format!("{:?}", e))) - }) - .await?; - } - - Ok(format!( - "Initiated closing of channel with rpc_channel_id: {}", - req.rpc_channel_id - )) -} diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index 9782ef786e..0b5785e528 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -14,7 +14,6 @@ pub type GenerateInvoiceResult = Result>; pub type SendPaymentResult = Result>; pub type ListPaymentsResult = Result>; pub type GetPaymentDetailsResult = Result>; -pub type CloseChannelResult = Result>; pub type SaveChannelClosingResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] @@ -221,37 +220,6 @@ impl From for GetPaymentDetailsError { fn from(err: SqlError) -> GetPaymentDetailsError { GetPaymentDetailsError::DbError(err.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum CloseChannelError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "No such channel with rpc_channel_id {}", _0)] - NoSuchChannel(u64), - #[display(fmt = "Closing channel error: {}", _0)] - CloseChannelError(String), -} - -impl HttpStatusCode for CloseChannelError { - fn status_code(&self) -> StatusCode { - match self { - CloseChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - CloseChannelError::NoSuchChannel(_) | CloseChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - CloseChannelError::CloseChannelError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for CloseChannelError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => CloseChannelError::NoSuchCoin(coin), - } - } -} - #[derive(Display, PartialEq)] pub enum SaveChannelClosingError { #[display(fmt = "DB error: {}", _0)] diff --git a/mm2src/coins/rpc_command/lightning/close_channel.rs b/mm2src/coins/rpc_command/lightning/close_channel.rs new file mode 100644 index 0000000000..5fb1497911 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/close_channel.rs @@ -0,0 +1,83 @@ +use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; +use common::{async_blocking, HttpStatusCode}; +use http::StatusCode; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +pub type CloseChannelResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum CloseChannelError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "No such channel with rpc_channel_id {}", _0)] + NoSuchChannel(u64), + #[display(fmt = "Closing channel error: {}", _0)] + CloseChannelError(String), +} + +impl HttpStatusCode for CloseChannelError { + fn status_code(&self) -> StatusCode { + match self { + CloseChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + CloseChannelError::NoSuchChannel(_) | CloseChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + CloseChannelError::CloseChannelError(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for CloseChannelError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => CloseChannelError::NoSuchCoin(coin), + } + } +} + +#[derive(Deserialize)] +pub struct CloseChannelReq { + pub coin: String, + pub rpc_channel_id: u64, + #[serde(default)] + pub force_close: bool, +} + +pub async fn close_channel(ctx: MmArc, req: CloseChannelReq) -> CloseChannelResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(CloseChannelError::UnsupportedCoin(e.ticker().to_string())), + }; + + let channel_details = ln_coin + .get_channel_by_rpc_id(req.rpc_channel_id) + .await + .ok_or(CloseChannelError::NoSuchChannel(req.rpc_channel_id))?; + let channel_id = channel_details.channel_id; + let counterparty_node_id = channel_details.counterparty.node_id; + + if req.force_close { + async_blocking(move || { + ln_coin + .channel_manager + .force_close_broadcasting_latest_txn(&channel_id, &counterparty_node_id) + .map_to_mm(|e| CloseChannelError::CloseChannelError(format!("{:?}", e))) + }) + .await?; + } else { + async_blocking(move || { + ln_coin + .channel_manager + .close_channel(&channel_id, &counterparty_node_id) + .map_to_mm(|e| CloseChannelError::CloseChannelError(format!("{:?}", e))) + }) + .await?; + } + + Ok(format!( + "Initiated closing of channel with rpc_channel_id: {}", + req.rpc_channel_id + )) +} diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index dc2ce13fb7..270c460acd 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1,3 +1,4 @@ +pub mod close_channel; pub mod connect_to_lightning_node; pub mod get_channel_details; pub mod get_claimable_balances; diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 744cdbf6b2..7f1e250536 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -43,10 +43,11 @@ use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; cfg_native! { - use coins::lightning::{close_channel, generate_invoice, + use coins::lightning::{generate_invoice, get_payment_details, list_payments_by_filter, send_payment, LightningCoin}; + use coins::rpc_command::lightning::close_channel::close_channel; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; use coins::rpc_command::lightning::get_channel_details::get_channel_details; use coins::rpc_command::lightning::get_claimable_balances::get_claimable_balances; From 37320505074b6fc5d1e507f1e4259779e87f7c5a Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 19:00:37 +0200 Subject: [PATCH 08/36] move get_payment_details, list_payments_by_filter to rpc_command --- mm2src/coins/lightning.rs | 176 +----------------- mm2src/coins/lightning/ln_errors.rs | 72 ------- mm2src/coins/lightning/ln_serialization.rs | 99 ++++++++++ .../lightning/get_payment_details.rs | 76 ++++++++ .../lightning/list_payments_by_filter.rs | 88 +++++++++ mm2src/coins/rpc_command/lightning/mod.rs | 2 + .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 7 +- 7 files changed, 270 insertions(+), 250 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/get_payment_details.rs create mode 100644 mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index e03d5ee348..0263dec0a2 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -27,7 +27,7 @@ use bitcrypto::dhash256; use bitcrypto::ChecksumType; use common::executor::spawn; use common::log::{LogOnError, LogState}; -use common::{async_blocking, calc_total_pages, log, now_ms, ten, PagingOptionsEnum}; +use common::{async_blocking, log, now_ms, PagingOptionsEnum}; use futures::{FutureExt, TryFutureExt}; use futures01::Future; use keys::{hash::H256, CompactSignature, KeyPair, Private, Public}; @@ -41,9 +41,8 @@ use lightning_invoice::payment; use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter}; use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; -use ln_db::{DBChannelDetails, DBPaymentInfo, DBPaymentsFilter, HTLCStatus, LightningDB, PaymentType}; +use ln_db::{DBChannelDetails, DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; use ln_errors::{EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, - GetPaymentDetailsError, GetPaymentDetailsResult, ListPaymentsError, ListPaymentsResult, SendPaymentError, SendPaymentResult}; use ln_events::{init_events_abort_handlers, LightningEventHandler}; use ln_filesystem_persister::LightningFilesystemPersister; @@ -997,174 +996,3 @@ pub async fn send_payment(ctx: MmArc, req: SendPaymentReq) -> SendPaymentResult< payment_hash: payment_info.payment_hash.0.into(), }) } - -#[derive(Deserialize)] -pub struct PaymentsFilterForRPC { - pub payment_type: Option, - pub description: Option, - pub status: Option, - pub from_amount_msat: Option, - pub to_amount_msat: Option, - pub from_fee_paid_msat: Option, - pub to_fee_paid_msat: Option, - pub from_timestamp: Option, - pub to_timestamp: Option, -} - -impl From for DBPaymentsFilter { - fn from(filter: PaymentsFilterForRPC) -> Self { - let (is_outbound, destination) = if let Some(payment_type) = filter.payment_type { - match payment_type { - PaymentTypeForRPC::OutboundPayment { destination } => (Some(true), Some(destination.0.to_string())), - PaymentTypeForRPC::InboundPayment => (Some(false), None), - } - } else { - (None, None) - }; - DBPaymentsFilter { - is_outbound, - destination, - description: filter.description, - status: filter.status.map(|s| s.to_string()), - from_amount_msat: filter.from_amount_msat.map(|a| a as i64), - to_amount_msat: filter.to_amount_msat.map(|a| a as i64), - from_fee_paid_msat: filter.from_fee_paid_msat.map(|f| f as i64), - to_fee_paid_msat: filter.to_fee_paid_msat.map(|f| f as i64), - from_timestamp: filter.from_timestamp.map(|f| f as i64), - to_timestamp: filter.to_timestamp.map(|f| f as i64), - } - } -} - -#[derive(Deserialize)] -pub struct ListPaymentsReq { - pub coin: String, - pub filter: Option, - #[serde(default = "ten")] - limit: usize, - #[serde(default)] - paging_options: PagingOptionsEnum, -} - -#[derive(Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum PaymentTypeForRPC { - #[serde(rename = "Outbound Payment")] - OutboundPayment { destination: PublicKeyForRPC }, - #[serde(rename = "Inbound Payment")] - InboundPayment, -} - -impl From for PaymentTypeForRPC { - fn from(payment_type: PaymentType) -> Self { - match payment_type { - PaymentType::OutboundPayment { destination } => PaymentTypeForRPC::OutboundPayment { - destination: PublicKeyForRPC(destination), - }, - PaymentType::InboundPayment => PaymentTypeForRPC::InboundPayment, - } - } -} - -impl From for PaymentType { - fn from(payment_type: PaymentTypeForRPC) -> Self { - match payment_type { - PaymentTypeForRPC::OutboundPayment { destination } => PaymentType::OutboundPayment { - destination: destination.into(), - }, - PaymentTypeForRPC::InboundPayment => PaymentType::InboundPayment, - } - } -} - -#[derive(Serialize)] -pub struct PaymentInfoForRPC { - payment_hash: H256Json, - payment_type: PaymentTypeForRPC, - description: String, - #[serde(skip_serializing_if = "Option::is_none")] - amount_in_msat: Option, - #[serde(skip_serializing_if = "Option::is_none")] - fee_paid_msat: Option, - status: HTLCStatus, - created_at: i64, - last_updated: i64, -} - -impl From for PaymentInfoForRPC { - fn from(info: DBPaymentInfo) -> Self { - PaymentInfoForRPC { - payment_hash: info.payment_hash.0.into(), - payment_type: info.payment_type.into(), - description: info.description, - amount_in_msat: info.amt_msat, - fee_paid_msat: info.fee_paid_msat, - status: info.status, - created_at: info.created_at, - last_updated: info.last_updated, - } - } -} - -#[derive(Serialize)] -pub struct ListPaymentsResponse { - payments: Vec, - limit: usize, - skipped: usize, - total: usize, - total_pages: usize, - paging_options: PagingOptionsEnum, -} - -pub async fn list_payments_by_filter(ctx: MmArc, req: ListPaymentsReq) -> ListPaymentsResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(ListPaymentsError::UnsupportedCoin(e.ticker().to_string())), - }; - let get_payments_res = ln_coin - .db - .get_payments_by_filter( - req.filter.map(From::from), - req.paging_options.clone().map(|h| PaymentHash(h.0)), - req.limit, - ) - .await?; - - Ok(ListPaymentsResponse { - payments: get_payments_res.payments.into_iter().map(From::from).collect(), - limit: req.limit, - skipped: get_payments_res.skipped, - total: get_payments_res.total, - total_pages: calc_total_pages(get_payments_res.total, req.limit), - paging_options: req.paging_options, - }) -} - -#[derive(Deserialize)] -pub struct GetPaymentDetailsRequest { - pub coin: String, - pub payment_hash: H256Json, -} - -#[derive(Serialize)] -pub struct GetPaymentDetailsResponse { - payment_details: PaymentInfoForRPC, -} - -pub async fn get_payment_details( - ctx: MmArc, - req: GetPaymentDetailsRequest, -) -> GetPaymentDetailsResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(GetPaymentDetailsError::UnsupportedCoin(e.ticker().to_string())), - }; - - if let Some(payment_info) = ln_coin.db.get_payment_from_db(PaymentHash(req.payment_hash.0)).await? { - return Ok(GetPaymentDetailsResponse { - payment_details: payment_info.into(), - }); - } - - MmError::err(GetPaymentDetailsError::NoSuchPayment(req.payment_hash)) -} diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index 0b5785e528..5711b3003d 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -6,14 +6,11 @@ use derive_more::Display; use http::StatusCode; use lightning_invoice::SignOrCreationError; use mm2_err_handle::prelude::*; -use rpc::v1::types::H256 as H256Json; use std::num::TryFromIntError; pub type EnableLightningResult = Result>; pub type GenerateInvoiceResult = Result>; pub type SendPaymentResult = Result>; -pub type ListPaymentsResult = Result>; -pub type GetPaymentDetailsResult = Result>; pub type SaveChannelClosingResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] @@ -151,75 +148,6 @@ impl From for SendPaymentError { fn from(err: SqlError) -> SendPaymentError { SendPaymentError::DbError(err.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum ListPaymentsError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "DB error {}", _0)] - DbError(String), -} - -impl HttpStatusCode for ListPaymentsError { - fn status_code(&self) -> StatusCode { - match self { - ListPaymentsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - ListPaymentsError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - ListPaymentsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for ListPaymentsError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => ListPaymentsError::NoSuchCoin(coin), - } - } -} - -impl From for ListPaymentsError { - fn from(err: SqlError) -> ListPaymentsError { ListPaymentsError::DbError(err.to_string()) } -} - -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum GetPaymentDetailsError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "Payment with hash: {:?} is not found", _0)] - NoSuchPayment(H256Json), - #[display(fmt = "DB error {}", _0)] - DbError(String), -} - -impl HttpStatusCode for GetPaymentDetailsError { - fn status_code(&self) -> StatusCode { - match self { - GetPaymentDetailsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - GetPaymentDetailsError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - GetPaymentDetailsError::NoSuchPayment(_) => StatusCode::NOT_FOUND, - GetPaymentDetailsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for GetPaymentDetailsError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => GetPaymentDetailsError::NoSuchCoin(coin), - } - } -} - -impl From for GetPaymentDetailsError { - fn from(err: SqlError) -> GetPaymentDetailsError { GetPaymentDetailsError::DbError(err.to_string()) } -} - #[derive(Display, PartialEq)] pub enum SaveChannelClosingError { #[display(fmt = "DB error: {}", _0)] diff --git a/mm2src/coins/lightning/ln_serialization.rs b/mm2src/coins/lightning/ln_serialization.rs index f7b7c16655..857cae2905 100644 --- a/mm2src/coins/lightning/ln_serialization.rs +++ b/mm2src/coins/lightning/ln_serialization.rs @@ -1,3 +1,4 @@ +use crate::lightning::ln_db::{DBPaymentInfo, DBPaymentsFilter, HTLCStatus, PaymentType}; use crate::lightning::ln_platform::h256_json_from_txid; use crate::H256Json; use lightning::chain::channelmonitor::Balance; @@ -141,6 +142,104 @@ impl From for ChannelDetailsForRPC { } } +#[derive(Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum PaymentTypeForRPC { + #[serde(rename = "Outbound Payment")] + OutboundPayment { destination: PublicKeyForRPC }, + #[serde(rename = "Inbound Payment")] + InboundPayment, +} + +impl From for PaymentTypeForRPC { + fn from(payment_type: PaymentType) -> Self { + match payment_type { + PaymentType::OutboundPayment { destination } => PaymentTypeForRPC::OutboundPayment { + destination: PublicKeyForRPC(destination), + }, + PaymentType::InboundPayment => PaymentTypeForRPC::InboundPayment, + } + } +} + +impl From for PaymentType { + fn from(payment_type: PaymentTypeForRPC) -> Self { + match payment_type { + PaymentTypeForRPC::OutboundPayment { destination } => PaymentType::OutboundPayment { + destination: destination.into(), + }, + PaymentTypeForRPC::InboundPayment => PaymentType::InboundPayment, + } + } +} + +#[derive(Serialize)] +pub struct PaymentInfoForRPC { + payment_hash: H256Json, + payment_type: PaymentTypeForRPC, + description: String, + #[serde(skip_serializing_if = "Option::is_none")] + amount_in_msat: Option, + #[serde(skip_serializing_if = "Option::is_none")] + fee_paid_msat: Option, + status: HTLCStatus, + created_at: i64, + last_updated: i64, +} + +impl From for PaymentInfoForRPC { + fn from(info: DBPaymentInfo) -> Self { + PaymentInfoForRPC { + payment_hash: info.payment_hash.0.into(), + payment_type: info.payment_type.into(), + description: info.description, + amount_in_msat: info.amt_msat, + fee_paid_msat: info.fee_paid_msat, + status: info.status, + created_at: info.created_at, + last_updated: info.last_updated, + } + } +} + +#[derive(Deserialize)] +pub struct PaymentsFilterForRPC { + pub payment_type: Option, + pub description: Option, + pub status: Option, + pub from_amount_msat: Option, + pub to_amount_msat: Option, + pub from_fee_paid_msat: Option, + pub to_fee_paid_msat: Option, + pub from_timestamp: Option, + pub to_timestamp: Option, +} + +impl From for DBPaymentsFilter { + fn from(filter: PaymentsFilterForRPC) -> Self { + let (is_outbound, destination) = if let Some(payment_type) = filter.payment_type { + match payment_type { + PaymentTypeForRPC::OutboundPayment { destination } => (Some(true), Some(destination.0.to_string())), + PaymentTypeForRPC::InboundPayment => (Some(false), None), + } + } else { + (None, None) + }; + DBPaymentsFilter { + is_outbound, + destination, + description: filter.description, + status: filter.status.map(|s| s.to_string()), + from_amount_msat: filter.from_amount_msat.map(|a| a as i64), + to_amount_msat: filter.to_amount_msat.map(|a| a as i64), + from_fee_paid_msat: filter.from_fee_paid_msat.map(|f| f as i64), + to_fee_paid_msat: filter.to_fee_paid_msat.map(|f| f as i64), + from_timestamp: filter.from_timestamp.map(|f| f as i64), + to_timestamp: filter.to_timestamp.map(|f| f as i64), + } + } +} + /// Details about the balance(s) available for spending once the channel appears on chain. #[derive(Serialize)] pub enum ClaimableBalance { diff --git a/mm2src/coins/rpc_command/lightning/get_payment_details.rs b/mm2src/coins/rpc_command/lightning/get_payment_details.rs new file mode 100644 index 0000000000..55acca175d --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/get_payment_details.rs @@ -0,0 +1,76 @@ +use crate::lightning::ln_db::LightningDB; +use crate::lightning::ln_serialization::PaymentInfoForRPC; +use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; +use common::HttpStatusCode; +use db_common::sqlite::rusqlite::Error as SqlError; +use http::StatusCode; +use lightning::ln::PaymentHash; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +pub type GetPaymentDetailsResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum GetPaymentDetailsError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "Payment with hash: {:?} is not found", _0)] + NoSuchPayment(H256Json), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl HttpStatusCode for GetPaymentDetailsError { + fn status_code(&self) -> StatusCode { + match self { + GetPaymentDetailsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + GetPaymentDetailsError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + GetPaymentDetailsError::NoSuchPayment(_) => StatusCode::NOT_FOUND, + GetPaymentDetailsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for GetPaymentDetailsError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => GetPaymentDetailsError::NoSuchCoin(coin), + } + } +} + +impl From for GetPaymentDetailsError { + fn from(err: SqlError) -> GetPaymentDetailsError { GetPaymentDetailsError::DbError(err.to_string()) } +} + +#[derive(Deserialize)] +pub struct GetPaymentDetailsRequest { + pub coin: String, + pub payment_hash: H256Json, +} + +#[derive(Serialize)] +pub struct GetPaymentDetailsResponse { + payment_details: PaymentInfoForRPC, +} + +pub async fn get_payment_details( + ctx: MmArc, + req: GetPaymentDetailsRequest, +) -> GetPaymentDetailsResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(GetPaymentDetailsError::UnsupportedCoin(e.ticker().to_string())), + }; + + if let Some(payment_info) = ln_coin.db.get_payment_from_db(PaymentHash(req.payment_hash.0)).await? { + return Ok(GetPaymentDetailsResponse { + payment_details: payment_info.into(), + }); + } + + MmError::err(GetPaymentDetailsError::NoSuchPayment(req.payment_hash)) +} diff --git a/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs b/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs new file mode 100644 index 0000000000..6dfb94416d --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs @@ -0,0 +1,88 @@ +use crate::lightning::ln_db::LightningDB; +use crate::lightning::ln_serialization::{PaymentInfoForRPC, PaymentsFilterForRPC}; +use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; +use common::{calc_total_pages, ten, HttpStatusCode, PagingOptionsEnum}; +use db_common::sqlite::rusqlite::Error as SqlError; +use http::StatusCode; +use lightning::ln::PaymentHash; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +pub type ListPaymentsResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum ListPaymentsError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl HttpStatusCode for ListPaymentsError { + fn status_code(&self) -> StatusCode { + match self { + ListPaymentsError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + ListPaymentsError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + ListPaymentsError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl From for ListPaymentsError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => ListPaymentsError::NoSuchCoin(coin), + } + } +} + +impl From for ListPaymentsError { + fn from(err: SqlError) -> ListPaymentsError { ListPaymentsError::DbError(err.to_string()) } +} + +#[derive(Deserialize)] +pub struct ListPaymentsReq { + pub coin: String, + pub filter: Option, + #[serde(default = "ten")] + limit: usize, + #[serde(default)] + paging_options: PagingOptionsEnum, +} + +#[derive(Serialize)] +pub struct ListPaymentsResponse { + payments: Vec, + limit: usize, + skipped: usize, + total: usize, + total_pages: usize, + paging_options: PagingOptionsEnum, +} + +pub async fn list_payments_by_filter(ctx: MmArc, req: ListPaymentsReq) -> ListPaymentsResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(ListPaymentsError::UnsupportedCoin(e.ticker().to_string())), + }; + let get_payments_res = ln_coin + .db + .get_payments_by_filter( + req.filter.map(From::from), + req.paging_options.clone().map(|h| PaymentHash(h.0)), + req.limit, + ) + .await?; + + Ok(ListPaymentsResponse { + payments: get_payments_res.payments.into_iter().map(From::from).collect(), + limit: req.limit, + skipped: get_payments_res.skipped, + total: get_payments_res.total, + total_pages: calc_total_pages(get_payments_res.total, req.limit), + paging_options: req.paging_options, + }) +} diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 270c460acd..efff53591f 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -2,7 +2,9 @@ pub mod close_channel; pub mod connect_to_lightning_node; pub mod get_channel_details; pub mod get_claimable_balances; +pub mod get_payment_details; pub mod list_channels; +pub mod list_payments_by_filter; pub mod open_channel; pub mod trusted_nodes; pub mod update_channel; diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 7f1e250536..58360a63db 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -43,15 +43,14 @@ use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; cfg_native! { - use coins::lightning::{generate_invoice, - get_payment_details, - list_payments_by_filter, send_payment, - LightningCoin}; + use coins::lightning::{generate_invoice, send_payment, LightningCoin}; use coins::rpc_command::lightning::close_channel::close_channel; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; use coins::rpc_command::lightning::get_channel_details::get_channel_details; use coins::rpc_command::lightning::get_claimable_balances::get_claimable_balances; + use coins::rpc_command::lightning::get_payment_details::get_payment_details; use coins::rpc_command::lightning::list_channels::{list_closed_channels_by_filter, list_open_channels_by_filter}; + use coins::rpc_command::lightning::list_payments_by_filter::list_payments_by_filter; use coins::rpc_command::lightning::open_channel::open_channel; use coins::rpc_command::lightning::trusted_nodes::{add_trusted_node, list_trusted_nodes, remove_trusted_node}; use coins::rpc_command::lightning::update_channel::update_channel; From 2322b251066403f78688f223552231c7f62c6326 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 19:17:00 +0200 Subject: [PATCH 09/36] move send_payment to rpc_command --- mm2src/coins/lightning.rs | 85 +++---------- mm2src/coins/lightning/ln_errors.rs | 43 ------- .../rpc_command/lightning/close_channel.rs | 2 +- .../lightning/get_payment_details.rs | 2 +- .../lightning/list_payments_by_filter.rs | 2 +- mm2src/coins/rpc_command/lightning/mod.rs | 1 + .../rpc_command/lightning/send_payment.rs | 114 ++++++++++++++++++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- 8 files changed, 137 insertions(+), 115 deletions(-) create mode 100644 mm2src/coins/rpc_command/lightning/send_payment.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 0263dec0a2..773ebe93b4 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -42,8 +42,7 @@ use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; use ln_db::{DBChannelDetails, DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; -use ln_errors::{EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult, - SendPaymentError, SendPaymentResult}; +use ln_errors::{EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult}; use ln_events::{init_events_abort_handlers, LightningEventHandler}; use ln_filesystem_persister::LightningFilesystemPersister; use ln_p2p::{connect_to_node, PeerManager}; @@ -129,6 +128,16 @@ pub(crate) struct GetOpenChannelsResult { pub total: usize, } +#[derive(Display)] +pub(crate) enum PaymentError { + #[display(fmt = "Final cltv expiry delta {} is below the required minimum of {}", _0, _1)] + CLTVExpiry(u32, u32), + #[display(fmt = "Error paying invoice: {}", _0)] + Invoice(String), + #[display(fmt = "Keysend error: {}", _0)] + Keysend(String), +} + impl LightningCoin { pub fn platform_coin(&self) -> &UtxoStandardCoin { &self.platform.coin } @@ -163,7 +172,7 @@ impl LightningCoin { .find(|chan| chan.user_channel_id == rpc_id) } - async fn pay_invoice(&self, invoice: Invoice) -> SendPaymentResult { + pub(crate) async fn pay_invoice(&self, invoice: Invoice) -> Result { let payment_hash = PaymentHash((invoice.payment_hash()).into_inner()); let payment_type = PaymentType::OutboundPayment { destination: *invoice.payee_pub_key().unwrap_or(&invoice.recover_payee_pub_key()), @@ -180,7 +189,7 @@ impl LightningCoin { selfi .invoice_payer .pay_invoice(&invoice) - .map_to_mm(|e| SendPaymentError::PaymentError(format!("{:?}", e))) + .map_err(|e| PaymentError::Invoice(format!("{:?}", e))) }) .await?; @@ -198,17 +207,14 @@ impl LightningCoin { }) } - async fn keysend( + pub(crate) async fn keysend( &self, destination: PublicKey, amount_msat: u64, final_cltv_expiry_delta: u32, - ) -> SendPaymentResult { + ) -> Result { if final_cltv_expiry_delta < MIN_FINAL_CLTV_EXPIRY { - return MmError::err(SendPaymentError::CLTVExpiryError( - final_cltv_expiry_delta, - MIN_FINAL_CLTV_EXPIRY, - )); + return Err(PaymentError::CLTVExpiry(final_cltv_expiry_delta, MIN_FINAL_CLTV_EXPIRY)); } let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes()); @@ -217,7 +223,7 @@ impl LightningCoin { selfi .invoice_payer .pay_pubkey(destination, payment_preimage, amount_msat, final_cltv_expiry_delta) - .map_to_mm(|e| SendPaymentError::PaymentError(format!("{:?}", e))) + .map_err(|e| PaymentError::Keysend(format!("{:?}", e))) }) .await?; @@ -939,60 +945,3 @@ pub async fn generate_invoice( invoice, }) } - -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(tag = "type")] -pub enum Payment { - #[serde(rename = "invoice")] - Invoice { invoice: Invoice }, - #[serde(rename = "keysend")] - Keysend { - // The recieving node pubkey (node ID) - destination: PublicKeyForRPC, - // Amount to send in millisatoshis - amount_in_msat: u64, - // The number of blocks the payment will be locked for if not claimed by the destination, - // It's can be assumed that 6 blocks = 1 hour. We can claim the payment amount back after this cltv expires. - // Minmum value allowed is MIN_FINAL_CLTV_EXPIRY which is currently 24 for rust-lightning. - expiry: u32, - }, -} - -#[derive(Deserialize)] -pub struct SendPaymentReq { - pub coin: String, - pub payment: Payment, -} - -#[derive(Serialize)] -pub struct SendPaymentResponse { - payment_hash: H256Json, -} - -pub async fn send_payment(ctx: MmArc, req: SendPaymentReq) -> SendPaymentResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(SendPaymentError::UnsupportedCoin(e.ticker().to_string())), - }; - let open_channels_nodes = ln_coin.open_channels_nodes.lock().clone(); - for (node_pubkey, node_addr) in open_channels_nodes { - connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()) - .await - .error_log_with_msg(&format!( - "Channel with node: {} can't be used to route this payment due to connection error.", - node_pubkey - )); - } - let payment_info = match req.payment { - Payment::Invoice { invoice } => ln_coin.pay_invoice(invoice).await?, - Payment::Keysend { - destination, - amount_in_msat, - expiry, - } => ln_coin.keysend(destination.into(), amount_in_msat, expiry).await?, - }; - ln_coin.db.add_or_update_payment_in_db(payment_info.clone()).await?; - Ok(SendPaymentResponse { - payment_hash: payment_info.payment_hash.0.into(), - }) -} diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index 5711b3003d..5f8c5ed675 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -10,7 +10,6 @@ use std::num::TryFromIntError; pub type EnableLightningResult = Result>; pub type GenerateInvoiceResult = Result>; -pub type SendPaymentResult = Result>; pub type SaveChannelClosingResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] @@ -106,48 +105,6 @@ impl From for GenerateInvoiceError { fn from(err: SqlError) -> GenerateInvoiceError { GenerateInvoiceError::DbError(err.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum SendPaymentError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "Couldn't parse destination pubkey: {}", _0)] - NoRouteFound(String), - #[display(fmt = "Payment error: {}", _0)] - PaymentError(String), - #[display(fmt = "Final cltv expiry delta {} is below the required minimum of {}", _0, _1)] - CLTVExpiryError(u32, u32), - #[display(fmt = "DB error {}", _0)] - DbError(String), -} - -impl HttpStatusCode for SendPaymentError { - fn status_code(&self) -> StatusCode { - match self { - SendPaymentError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - SendPaymentError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - SendPaymentError::PaymentError(_) - | SendPaymentError::NoRouteFound(_) - | SendPaymentError::CLTVExpiryError(_, _) - | SendPaymentError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for SendPaymentError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => SendPaymentError::NoSuchCoin(coin), - } - } -} - -impl From for SendPaymentError { - fn from(err: SqlError) -> SendPaymentError { SendPaymentError::DbError(err.to_string()) } -} - #[derive(Display, PartialEq)] pub enum SaveChannelClosingError { #[display(fmt = "DB error: {}", _0)] diff --git a/mm2src/coins/rpc_command/lightning/close_channel.rs b/mm2src/coins/rpc_command/lightning/close_channel.rs index 5fb1497911..a3fa05644c 100644 --- a/mm2src/coins/rpc_command/lightning/close_channel.rs +++ b/mm2src/coins/rpc_command/lightning/close_channel.rs @@ -4,7 +4,7 @@ use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -pub type CloseChannelResult = Result>; +type CloseChannelResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] diff --git a/mm2src/coins/rpc_command/lightning/get_payment_details.rs b/mm2src/coins/rpc_command/lightning/get_payment_details.rs index 55acca175d..0e7a4868c2 100644 --- a/mm2src/coins/rpc_command/lightning/get_payment_details.rs +++ b/mm2src/coins/rpc_command/lightning/get_payment_details.rs @@ -8,7 +8,7 @@ use lightning::ln::PaymentHash; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -pub type GetPaymentDetailsResult = Result>; +type GetPaymentDetailsResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] diff --git a/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs b/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs index 6dfb94416d..0a6d0d3308 100644 --- a/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs +++ b/mm2src/coins/rpc_command/lightning/list_payments_by_filter.rs @@ -8,7 +8,7 @@ use lightning::ln::PaymentHash; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -pub type ListPaymentsResult = Result>; +type ListPaymentsResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index efff53591f..1d6dab1d36 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -6,5 +6,6 @@ pub mod get_payment_details; pub mod list_channels; pub mod list_payments_by_filter; pub mod open_channel; +pub mod send_payment; pub mod trusted_nodes; pub mod update_channel; diff --git a/mm2src/coins/rpc_command/lightning/send_payment.rs b/mm2src/coins/rpc_command/lightning/send_payment.rs new file mode 100644 index 0000000000..995c85ca56 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/send_payment.rs @@ -0,0 +1,114 @@ +use crate::lightning::ln_db::LightningDB; +use crate::lightning::ln_p2p::connect_to_node; +use crate::lightning::ln_serialization::PublicKeyForRPC; +use crate::lightning::PaymentError; +use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; +use common::log::LogOnError; +use common::HttpStatusCode; +use db_common::sqlite::rusqlite::Error as SqlError; +use http::StatusCode; +use lightning_invoice::Invoice; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +type SendPaymentResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum SendPaymentError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "Couldn't parse destination pubkey: {}", _0)] + NoRouteFound(String), + #[display(fmt = "Payment error: {}", _0)] + PaymentError(String), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl HttpStatusCode for SendPaymentError { + fn status_code(&self) -> StatusCode { + match self { + SendPaymentError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + SendPaymentError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + SendPaymentError::PaymentError(_) | SendPaymentError::NoRouteFound(_) | SendPaymentError::DbError(_) => { + StatusCode::INTERNAL_SERVER_ERROR + }, + } + } +} + +impl From for SendPaymentError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => SendPaymentError::NoSuchCoin(coin), + } + } +} + +impl From for SendPaymentError { + fn from(err: SqlError) -> SendPaymentError { SendPaymentError::DbError(err.to_string()) } +} + +impl From for SendPaymentError { + fn from(err: PaymentError) -> SendPaymentError { SendPaymentError::PaymentError(err.to_string()) } +} + +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(tag = "type")] +pub enum Payment { + #[serde(rename = "invoice")] + Invoice { invoice: Invoice }, + #[serde(rename = "keysend")] + Keysend { + // The recieving node pubkey (node ID) + destination: PublicKeyForRPC, + // Amount to send in millisatoshis + amount_in_msat: u64, + // The number of blocks the payment will be locked for if not claimed by the destination, + // It's can be assumed that 6 blocks = 1 hour. We can claim the payment amount back after this cltv expires. + // Minmum value allowed is MIN_FINAL_CLTV_EXPIRY which is currently 24 for rust-lightning. + expiry: u32, + }, +} + +#[derive(Deserialize)] +pub struct SendPaymentReq { + pub coin: String, + pub payment: Payment, +} + +#[derive(Serialize)] +pub struct SendPaymentResponse { + payment_hash: H256Json, +} + +pub async fn send_payment(ctx: MmArc, req: SendPaymentReq) -> SendPaymentResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(SendPaymentError::UnsupportedCoin(e.ticker().to_string())), + }; + let open_channels_nodes = ln_coin.open_channels_nodes.lock().clone(); + for (node_pubkey, node_addr) in open_channels_nodes { + connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()) + .await + .error_log_with_msg(&format!( + "Channel with node: {} can't be used to route this payment due to connection error.", + node_pubkey + )); + } + let payment_info = match req.payment { + Payment::Invoice { invoice } => ln_coin.pay_invoice(invoice).await?, + Payment::Keysend { + destination, + amount_in_msat, + expiry, + } => ln_coin.keysend(destination.into(), amount_in_msat, expiry).await?, + }; + ln_coin.db.add_or_update_payment_in_db(payment_info.clone()).await?; + Ok(SendPaymentResponse { + payment_hash: payment_info.payment_hash.0.into(), + }) +} diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 58360a63db..32985cc148 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -43,7 +43,7 @@ use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; cfg_native! { - use coins::lightning::{generate_invoice, send_payment, LightningCoin}; + use coins::lightning::{generate_invoice, LightningCoin}; use coins::rpc_command::lightning::close_channel::close_channel; use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; use coins::rpc_command::lightning::get_channel_details::get_channel_details; @@ -52,6 +52,7 @@ cfg_native! { use coins::rpc_command::lightning::list_channels::{list_closed_channels_by_filter, list_open_channels_by_filter}; use coins::rpc_command::lightning::list_payments_by_filter::list_payments_by_filter; use coins::rpc_command::lightning::open_channel::open_channel; + use coins::rpc_command::lightning::send_payment::send_payment; use coins::rpc_command::lightning::trusted_nodes::{add_trusted_node, list_trusted_nodes, remove_trusted_node}; use coins::rpc_command::lightning::update_channel::update_channel; use coins::z_coin::ZCoin; From b638bb7d9806fac90bf5f2c16139439a0ed1d03d Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 16 Sep 2022 21:02:12 +0200 Subject: [PATCH 10/36] move the rest of lightning RPCs to rpc_command, use lightning:: prefix for lightning methods --- mm2src/coins/lightning.rs | 81 +---------- mm2src/coins/lightning/ln_errors.rs | 44 ------ mm2src/coins/lightning/ln_p2p.rs | 6 +- ...o_lightning_node.rs => connect_to_node.rs} | 6 +- .../rpc_command/lightning/generate_invoice.rs | 129 ++++++++++++++++++ mm2src/coins/rpc_command/lightning/mod.rs | 3 +- .../rpc_command/lightning/open_channel.rs | 4 +- .../rpc_command/lightning/send_payment.rs | 4 +- .../mm2_main/src/mm2_tests/lightning_tests.rs | 54 +++++--- .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 94 +++++++++---- 10 files changed, 248 insertions(+), 177 deletions(-) rename mm2src/coins/rpc_command/lightning/{connect_to_lightning_node.rs => connect_to_node.rs} (91%) create mode 100644 mm2src/coins/rpc_command/lightning/generate_invoice.rs diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 773ebe93b4..0076394a5f 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -10,7 +10,7 @@ mod ln_sql; pub(crate) mod ln_storage; mod ln_utils; -use super::{lp_coinfind_or_err, DerivationMethod, MmCoinEnum}; +use super::DerivationMethod; use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::BlockchainNetwork; @@ -38,14 +38,14 @@ use lightning::ln::{PaymentHash, PaymentPreimage}; use lightning::routing::gossip; use lightning_background_processor::{BackgroundProcessor, GossipSync}; use lightning_invoice::payment; -use lightning_invoice::utils::{create_invoice_from_channelmanager, DefaultRouter}; +use lightning_invoice::utils::DefaultRouter; use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; use ln_db::{DBChannelDetails, DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; -use ln_errors::{EnableLightningError, EnableLightningResult, GenerateInvoiceError, GenerateInvoiceResult}; +use ln_errors::{EnableLightningError, EnableLightningResult}; use ln_events::{init_events_abort_handlers, LightningEventHandler}; use ln_filesystem_persister::LightningFilesystemPersister; -use ln_p2p::{connect_to_node, PeerManager}; +use ln_p2p::PeerManager; use ln_platform::Platform; use ln_serialization::{ChannelDetailsForRPC, PublicKeyForRPC}; use ln_sql::SqliteLightningDB; @@ -847,7 +847,7 @@ pub async fn start_lightning( let open_channels_nodes = Arc::new(PaMutex::new( ln_utils::get_open_channels_nodes_addresses(persister.clone(), channel_manager.clone()).await?, )); - spawn(ln_p2p::connect_to_nodes_loop( + spawn(ln_p2p::connect_to_ln_nodes_loop( open_channels_nodes.clone(), peer_manager.clone(), )); @@ -874,74 +874,3 @@ pub async fn start_lightning( trusted_nodes, }) } - -#[derive(Deserialize)] -pub struct GenerateInvoiceRequest { - pub coin: String, - pub amount_in_msat: Option, - pub description: String, - pub expiry: Option, -} - -#[derive(Serialize)] -pub struct GenerateInvoiceResponse { - payment_hash: H256Json, - invoice: Invoice, -} - -/// Generates an invoice (request for payment) that can be paid on the lightning network by another node using send_payment. -pub async fn generate_invoice( - ctx: MmArc, - req: GenerateInvoiceRequest, -) -> GenerateInvoiceResult { - let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { - MmCoinEnum::LightningCoin(c) => c, - e => return MmError::err(GenerateInvoiceError::UnsupportedCoin(e.ticker().to_string())), - }; - let open_channels_nodes = ln_coin.open_channels_nodes.lock().clone(); - for (node_pubkey, node_addr) in open_channels_nodes { - connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()) - .await - .error_log_with_msg(&format!( - "Channel with node: {} can't be used for invoice routing hints due to connection error.", - node_pubkey - )); - } - - let network = ln_coin.platform.network.clone().into(); - let channel_manager = ln_coin.channel_manager.clone(); - let keys_manager = ln_coin.keys_manager.clone(); - let amount_in_msat = req.amount_in_msat; - let description = req.description.clone(); - let expiry = req.expiry.unwrap_or(DEFAULT_INVOICE_EXPIRY); - let invoice = async_blocking(move || { - create_invoice_from_channelmanager( - &channel_manager, - keys_manager, - network, - amount_in_msat, - description, - expiry, - ) - }) - .await?; - - let payment_hash = invoice.payment_hash().into_inner(); - let payment_info = DBPaymentInfo { - payment_hash: PaymentHash(payment_hash), - payment_type: PaymentType::InboundPayment, - description: req.description, - preimage: None, - secret: Some(*invoice.payment_secret()), - amt_msat: req.amount_in_msat.map(|a| a as i64), - fee_paid_msat: None, - status: HTLCStatus::Pending, - created_at: (now_ms() / 1000) as i64, - last_updated: (now_ms() / 1000) as i64, - }; - ln_coin.db.add_or_update_payment_in_db(payment_info).await?; - Ok(GenerateInvoiceResponse { - payment_hash: payment_hash.into(), - invoice, - }) -} diff --git a/mm2src/coins/lightning/ln_errors.rs b/mm2src/coins/lightning/ln_errors.rs index 5f8c5ed675..de92a5dd81 100644 --- a/mm2src/coins/lightning/ln_errors.rs +++ b/mm2src/coins/lightning/ln_errors.rs @@ -1,15 +1,12 @@ use crate::utxo::rpc_clients::UtxoRpcError; -use crate::CoinFindError; use common::HttpStatusCode; use db_common::sqlite::rusqlite::Error as SqlError; use derive_more::Display; use http::StatusCode; -use lightning_invoice::SignOrCreationError; use mm2_err_handle::prelude::*; use std::num::TryFromIntError; pub type EnableLightningResult = Result>; -pub type GenerateInvoiceResult = Result>; pub type SaveChannelClosingResult = Result>; #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] @@ -64,47 +61,6 @@ impl From for EnableLightningError { fn from(e: UtxoRpcError) -> Self { EnableLightningError::RpcError(e.to_string()) } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] -#[serde(tag = "error_type", content = "error_data")] -pub enum GenerateInvoiceError { - #[display(fmt = "Lightning network is not supported for {}", _0)] - UnsupportedCoin(String), - #[display(fmt = "No such coin {}", _0)] - NoSuchCoin(String), - #[display(fmt = "Invoice signing or creation error: {}", _0)] - SignOrCreationError(String), - #[display(fmt = "DB error {}", _0)] - DbError(String), -} - -impl HttpStatusCode for GenerateInvoiceError { - fn status_code(&self) -> StatusCode { - match self { - GenerateInvoiceError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - GenerateInvoiceError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - GenerateInvoiceError::SignOrCreationError(_) | GenerateInvoiceError::DbError(_) => { - StatusCode::INTERNAL_SERVER_ERROR - }, - } - } -} - -impl From for GenerateInvoiceError { - fn from(e: CoinFindError) -> Self { - match e { - CoinFindError::NoSuchCoin { coin } => GenerateInvoiceError::NoSuchCoin(coin), - } - } -} - -impl From for GenerateInvoiceError { - fn from(e: SignOrCreationError) -> Self { GenerateInvoiceError::SignOrCreationError(e.to_string()) } -} - -impl From for GenerateInvoiceError { - fn from(err: SqlError) -> GenerateInvoiceError { GenerateInvoiceError::DbError(err.to_string()) } -} - #[derive(Display, PartialEq)] pub enum SaveChannelClosingError { #[display(fmt = "DB error: {}", _0)] diff --git a/mm2src/coins/lightning/ln_p2p.rs b/mm2src/coins/lightning/ln_p2p.rs index 95d266ad51..9257bfc71e 100644 --- a/mm2src/coins/lightning/ln_p2p.rs +++ b/mm2src/coins/lightning/ln_p2p.rs @@ -35,7 +35,7 @@ pub enum ConnectionError { ConnectionError(String), } -pub async fn connect_to_node( +pub async fn connect_to_ln_node( pubkey: PublicKey, node_addr: SocketAddr, peer_manager: Arc, @@ -82,12 +82,12 @@ pub async fn connect_to_node( Ok(ConnectToNodeRes::ConnectedSuccessfully { pubkey, node_addr }) } -pub async fn connect_to_nodes_loop(open_channels_nodes: NodesAddressesMapShared, peer_manager: Arc) { +pub async fn connect_to_ln_nodes_loop(open_channels_nodes: NodesAddressesMapShared, peer_manager: Arc) { loop { let open_channels_nodes = open_channels_nodes.lock().clone(); for (pubkey, node_addr) in open_channels_nodes { let peer_manager = peer_manager.clone(); - match connect_to_node(pubkey, node_addr, peer_manager.clone()).await { + match connect_to_ln_node(pubkey, node_addr, peer_manager.clone()).await { Ok(res) => { if let ConnectToNodeRes::ConnectedSuccessfully { .. } = res { log::info!("{}", res.to_string()); diff --git a/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs b/mm2src/coins/rpc_command/lightning/connect_to_node.rs similarity index 91% rename from mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs rename to mm2src/coins/rpc_command/lightning/connect_to_node.rs index 0c9a71c561..1e0e4258fa 100644 --- a/mm2src/coins/rpc_command/lightning/connect_to_lightning_node.rs +++ b/mm2src/coins/rpc_command/lightning/connect_to_node.rs @@ -1,5 +1,5 @@ use crate::lightning::ln_errors::EnableLightningError; -use crate::lightning::ln_p2p::{connect_to_node, ConnectToNodeRes, ConnectionError}; +use crate::lightning::ln_p2p::{connect_to_ln_node, ConnectToNodeRes, ConnectionError}; use crate::lightning::ln_serialization::NodeAddress; use crate::lightning::ln_storage::LightningStorage; use crate::{lp_coinfind_or_err, CoinFindError, MmCoinEnum}; @@ -67,7 +67,7 @@ pub struct ConnectToNodeRequest { } /// Connect to a certain node on the lightning network. -pub async fn connect_to_lightning_node(ctx: MmArc, req: ConnectToNodeRequest) -> ConnectToNodeResult { +pub async fn connect_to_node(ctx: MmArc, req: ConnectToNodeRequest) -> ConnectToNodeResult { let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { MmCoinEnum::LightningCoin(c) => c, e => return MmError::err(ConnectToNodeError::UnsupportedCoin(e.ticker().to_string())), @@ -75,7 +75,7 @@ pub async fn connect_to_lightning_node(ctx: MmArc, req: ConnectToNodeRequest) -> let node_pubkey = req.node_address.pubkey; let node_addr = req.node_address.addr; - let res = connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; + let res = connect_to_ln_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; // If a node that we have an open channel with changed it's address, "connect_to_lightning_node" // can be used to reconnect to the new address while saving this new address for reconnections. diff --git a/mm2src/coins/rpc_command/lightning/generate_invoice.rs b/mm2src/coins/rpc_command/lightning/generate_invoice.rs new file mode 100644 index 0000000000..90d5d6a5d7 --- /dev/null +++ b/mm2src/coins/rpc_command/lightning/generate_invoice.rs @@ -0,0 +1,129 @@ +use crate::lightning::ln_db::{DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; +use crate::lightning::ln_p2p::connect_to_ln_node; +use crate::lightning::DEFAULT_INVOICE_EXPIRY; +use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; +use bitcoin_hashes::Hash; +use common::log::LogOnError; +use common::{async_blocking, HttpStatusCode}; +use db_common::sqlite::rusqlite::Error as SqlError; +use gstuff::now_ms; +use http::StatusCode; +use lightning::ln::PaymentHash; +use lightning_invoice::utils::create_invoice_from_channelmanager; +use lightning_invoice::{Invoice, SignOrCreationError}; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::*; + +type GenerateInvoiceResult = Result>; + +#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum GenerateInvoiceError { + #[display(fmt = "Lightning network is not supported for {}", _0)] + UnsupportedCoin(String), + #[display(fmt = "No such coin {}", _0)] + NoSuchCoin(String), + #[display(fmt = "Invoice signing or creation error: {}", _0)] + SignOrCreationError(String), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl HttpStatusCode for GenerateInvoiceError { + fn status_code(&self) -> StatusCode { + match self { + GenerateInvoiceError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, + GenerateInvoiceError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + GenerateInvoiceError::SignOrCreationError(_) | GenerateInvoiceError::DbError(_) => { + StatusCode::INTERNAL_SERVER_ERROR + }, + } + } +} + +impl From for GenerateInvoiceError { + fn from(e: CoinFindError) -> Self { + match e { + CoinFindError::NoSuchCoin { coin } => GenerateInvoiceError::NoSuchCoin(coin), + } + } +} + +impl From for GenerateInvoiceError { + fn from(e: SignOrCreationError) -> Self { GenerateInvoiceError::SignOrCreationError(e.to_string()) } +} + +impl From for GenerateInvoiceError { + fn from(err: SqlError) -> GenerateInvoiceError { GenerateInvoiceError::DbError(err.to_string()) } +} + +#[derive(Deserialize)] +pub struct GenerateInvoiceRequest { + pub coin: String, + pub amount_in_msat: Option, + pub description: String, + pub expiry: Option, +} + +#[derive(Serialize)] +pub struct GenerateInvoiceResponse { + payment_hash: H256Json, + invoice: Invoice, +} + +/// Generates an invoice (request for payment) that can be paid on the lightning network by another node using send_payment. +pub async fn generate_invoice( + ctx: MmArc, + req: GenerateInvoiceRequest, +) -> GenerateInvoiceResult { + let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? { + MmCoinEnum::LightningCoin(c) => c, + e => return MmError::err(GenerateInvoiceError::UnsupportedCoin(e.ticker().to_string())), + }; + let open_channels_nodes = ln_coin.open_channels_nodes.lock().clone(); + for (node_pubkey, node_addr) in open_channels_nodes { + connect_to_ln_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()) + .await + .error_log_with_msg(&format!( + "Channel with node: {} can't be used for invoice routing hints due to connection error.", + node_pubkey + )); + } + + let network = ln_coin.platform.network.clone().into(); + let channel_manager = ln_coin.channel_manager.clone(); + let keys_manager = ln_coin.keys_manager.clone(); + let amount_in_msat = req.amount_in_msat; + let description = req.description.clone(); + let expiry = req.expiry.unwrap_or(DEFAULT_INVOICE_EXPIRY); + let invoice = async_blocking(move || { + create_invoice_from_channelmanager( + &channel_manager, + keys_manager, + network, + amount_in_msat, + description, + expiry, + ) + }) + .await?; + + let payment_hash = invoice.payment_hash().into_inner(); + let payment_info = DBPaymentInfo { + payment_hash: PaymentHash(payment_hash), + payment_type: PaymentType::InboundPayment, + description: req.description, + preimage: None, + secret: Some(*invoice.payment_secret()), + amt_msat: req.amount_in_msat.map(|a| a as i64), + fee_paid_msat: None, + status: HTLCStatus::Pending, + created_at: (now_ms() / 1000) as i64, + last_updated: (now_ms() / 1000) as i64, + }; + ln_coin.db.add_or_update_payment_in_db(payment_info).await?; + Ok(GenerateInvoiceResponse { + payment_hash: payment_hash.into(), + invoice, + }) +} diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 1d6dab1d36..431aeb7977 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1,5 +1,6 @@ pub mod close_channel; -pub mod connect_to_lightning_node; +pub mod connect_to_node; +pub mod generate_invoice; pub mod get_channel_details; pub mod get_claimable_balances; pub mod get_payment_details; diff --git a/mm2src/coins/rpc_command/lightning/open_channel.rs b/mm2src/coins/rpc_command/lightning/open_channel.rs index e312f7c2fe..bf9ccec931 100644 --- a/mm2src/coins/rpc_command/lightning/open_channel.rs +++ b/mm2src/coins/rpc_command/lightning/open_channel.rs @@ -1,6 +1,6 @@ use crate::lightning::ln_conf::{ChannelOptions, OurChannelsConfigs}; use crate::lightning::ln_db::{DBChannelDetails, LightningDB}; -use crate::lightning::ln_p2p::{connect_to_node, ConnectionError}; +use crate::lightning::ln_p2p::{connect_to_ln_node, ConnectionError}; use crate::lightning::ln_serialization::NodeAddress; use crate::lightning::ln_storage::LightningStorage; use crate::utxo::utxo_common::UtxoTxBuilder; @@ -141,7 +141,7 @@ pub async fn open_channel(ctx: MmArc, req: OpenChannelRequest) -> OpenChannelRes // Making sure that the node data is correct and that we can connect to it before doing more operations let node_pubkey = req.node_address.pubkey; let node_addr = req.node_address.addr; - connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; + connect_to_ln_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; let platform_coin = ln_coin.platform_coin().clone(); let decimals = platform_coin.as_ref().decimals; diff --git a/mm2src/coins/rpc_command/lightning/send_payment.rs b/mm2src/coins/rpc_command/lightning/send_payment.rs index 995c85ca56..c156589839 100644 --- a/mm2src/coins/rpc_command/lightning/send_payment.rs +++ b/mm2src/coins/rpc_command/lightning/send_payment.rs @@ -1,5 +1,5 @@ use crate::lightning::ln_db::LightningDB; -use crate::lightning::ln_p2p::connect_to_node; +use crate::lightning::ln_p2p::connect_to_ln_node; use crate::lightning::ln_serialization::PublicKeyForRPC; use crate::lightning::PaymentError; use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; @@ -92,7 +92,7 @@ pub async fn send_payment(ctx: MmArc, req: SendPaymentReq) -> SendPaymentResult< }; let open_channels_nodes = ln_coin.open_channels_nodes.lock().clone(); for (node_pubkey, node_addr) in open_channels_nodes { - connect_to_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()) + connect_to_ln_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()) .await .error_log_with_msg(&format!( "Channel with node: {} can't be used to route this payment due to connection error.", diff --git a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs index 19711a08fc..52ea8a1025 100644 --- a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs +++ b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs @@ -188,21 +188,21 @@ fn test_enable_lightning() { #[test] #[cfg(not(target_arch = "wasm32"))] -fn test_connect_to_lightning_node() { +fn test_connect_to_node() { let (mm_node_1, mm_node_2, node_1_id, _) = start_lightning_nodes(false); let node_1_address = format!("{}@{}:9735", node_1_id, mm_node_1.ip.to_string()); let connect = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "connect_to_lightning_node", + "method": "lightning::connect_to_node", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, }, }))) .unwrap(); - assert!(connect.0.is_success(), "!connect_to_lightning_node: {}", connect.1); + assert!(connect.0.is_success(), "!lightning::connect_to_node: {}", connect.1); let connect_res: Json = json::from_str(&connect.1).unwrap(); let expected = format!("Connected successfully to node : {}", node_1_address); assert_eq!(connect_res["result"], expected); @@ -222,7 +222,7 @@ fn test_open_channel() { let open_channel = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "open_channel", + "method": "lightning::open_channel", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, @@ -233,14 +233,18 @@ fn test_open_channel() { }, }))) .unwrap(); - assert!(open_channel.0.is_success(), "!open_channel: {}", open_channel.1); + assert!( + open_channel.0.is_success(), + "!lightning::open_channel: {}", + open_channel.1 + ); block_on(mm_node_2.wait_for_log(60., |log| log.contains("Transaction broadcasted successfully"))).unwrap(); let list_channels_node_1 = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "list_open_channels_by_filter", + "method": "lightning::list_open_channels_by_filter", "params": { "coin": "tBTC-TEST-lightning", }, @@ -248,7 +252,7 @@ fn test_open_channel() { .unwrap(); assert!( list_channels_node_1.0.is_success(), - "!list_channels: {}", + "!lightning::list_open_channels_by_filter: {}", list_channels_node_1.1 ); let list_channels_node_1_res: Json = json::from_str(&list_channels_node_1.1).unwrap(); @@ -269,7 +273,7 @@ fn test_open_channel() { let list_channels_node_2 = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "list_open_channels_by_filter", + "method": "lightning::list_open_channels_by_filter", "params": { "coin": "tBTC-TEST-lightning", }, @@ -277,7 +281,7 @@ fn test_open_channel() { .unwrap(); assert!( list_channels_node_2.0.is_success(), - "!list_channels: {}", + "!lightning::list_open_channels_by_filter: {}", list_channels_node_2.1 ); let list_channels_node_2_res: Json = json::from_str(&list_channels_node_2.1).unwrap(); @@ -310,19 +314,23 @@ fn test_send_payment() { let add_trusted_node = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "add_trusted_node", + "method": "lightning::add_trusted_node", "params": { "coin": "tBTC-TEST-lightning", "node_id": node_2_id }, }))) .unwrap(); - assert!(add_trusted_node.0.is_success(), "!open_channel: {}", add_trusted_node.1); + assert!( + add_trusted_node.0.is_success(), + "!lightning::add_trusted_node: {}", + add_trusted_node.1 + ); let open_channel = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "open_channel", + "method": "lightning::open_channel", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, @@ -333,14 +341,18 @@ fn test_send_payment() { }, }))) .unwrap(); - assert!(open_channel.0.is_success(), "!open_channel: {}", open_channel.1); + assert!( + open_channel.0.is_success(), + "!lightning::open_channel: {}", + open_channel.1 + ); block_on(mm_node_2.wait_for_log(60., |log| log.contains("Received message ChannelReady"))).unwrap(); let send_payment = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "send_payment", + "method": "lightning::send_payment", "params": { "coin": "tBTC-TEST-lightning", "payment": { @@ -352,7 +364,11 @@ fn test_send_payment() { }, }))) .unwrap(); - assert!(send_payment.0.is_success(), "!send_payment: {}", send_payment.1); + assert!( + send_payment.0.is_success(), + "!lightning::send_payment: {}", + send_payment.1 + ); let send_payment_res: Json = json::from_str(&send_payment.1).unwrap(); log!("send_payment_res {:?}", send_payment_res); @@ -364,7 +380,7 @@ fn test_send_payment() { let get_payment_details = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "get_payment_details", + "method": "lightning::get_payment_details", "params": { "coin": "tBTC-TEST-lightning", "payment_hash": payment_hash @@ -373,7 +389,7 @@ fn test_send_payment() { .unwrap(); assert!( get_payment_details.0.is_success(), - "!get_payment_details: {}", + "!lightning::get_payment_details: {}", get_payment_details.1 ); @@ -387,7 +403,7 @@ fn test_send_payment() { let get_payment_details = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "get_payment_details", + "method": "lightning::get_payment_details", "params": { "coin": "tBTC-TEST-lightning", "payment_hash": payment_hash @@ -396,7 +412,7 @@ fn test_send_payment() { .unwrap(); assert!( get_payment_details.0.is_success(), - "!get_payment_details: {}", + "!lightning::get_payment_details: {}", get_payment_details.1 ); diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 32985cc148..f390f7a3a2 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -43,18 +43,7 @@ use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; cfg_native! { - use coins::lightning::{generate_invoice, LightningCoin}; - use coins::rpc_command::lightning::close_channel::close_channel; - use coins::rpc_command::lightning::connect_to_lightning_node::connect_to_lightning_node; - use coins::rpc_command::lightning::get_channel_details::get_channel_details; - use coins::rpc_command::lightning::get_claimable_balances::get_claimable_balances; - use coins::rpc_command::lightning::get_payment_details::get_payment_details; - use coins::rpc_command::lightning::list_channels::{list_closed_channels_by_filter, list_open_channels_by_filter}; - use coins::rpc_command::lightning::list_payments_by_filter::list_payments_by_filter; - use coins::rpc_command::lightning::open_channel::open_channel; - use coins::rpc_command::lightning::send_payment::send_payment; - use coins::rpc_command::lightning::trusted_nodes::{add_trusted_node, list_trusted_nodes, remove_trusted_node}; - use coins::rpc_command::lightning::update_channel::update_channel; + use coins::lightning::LightningCoin; use coins::z_coin::ZCoin; } @@ -144,6 +133,12 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, account_balance).await, "add_delegation" => handle_mmrpc(ctx, request, add_delegation).await, @@ -179,9 +174,6 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, withdraw).await, #[cfg(not(target_arch = "wasm32"))] native_only_methods => match native_only_methods { - "add_trusted_node" => handle_mmrpc(ctx, request, add_trusted_node).await, - "close_channel" => handle_mmrpc(ctx, request, close_channel).await, - "connect_to_lightning_node" => handle_mmrpc(ctx, request, connect_to_lightning_node).await, "enable_lightning" => handle_mmrpc(ctx, request, enable_l2::).await, #[cfg(all(not(target_os = "ios"), not(target_os = "android")))] "enable_solana_with_tokens" => { @@ -189,18 +181,6 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, enable_token::).await, - "generate_invoice" => handle_mmrpc(ctx, request, generate_invoice).await, - "get_channel_details" => handle_mmrpc(ctx, request, get_channel_details).await, - "get_claimable_balances" => handle_mmrpc(ctx, request, get_claimable_balances).await, - "get_payment_details" => handle_mmrpc(ctx, request, get_payment_details).await, - "list_closed_channels_by_filter" => handle_mmrpc(ctx, request, list_closed_channels_by_filter).await, - "list_open_channels_by_filter" => handle_mmrpc(ctx, request, list_open_channels_by_filter).await, - "list_payments_by_filter" => handle_mmrpc(ctx, request, list_payments_by_filter).await, - "list_trusted_nodes" => handle_mmrpc(ctx, request, list_trusted_nodes).await, - "open_channel" => handle_mmrpc(ctx, request, open_channel).await, - "remove_trusted_node" => handle_mmrpc(ctx, request, remove_trusted_node).await, - "send_payment" => handle_mmrpc(ctx, request, send_payment).await, - "update_channel" => handle_mmrpc(ctx, request, update_channel).await, "z_coin_tx_history" => handle_mmrpc(ctx, request, coins::my_tx_history_v2::z_coin_tx_history_rpc).await, _ => MmError::err(DispatcherError::NoSuchMethod), }, @@ -290,3 +270,63 @@ async fn gui_storage_dispatcher( _ => MmError::err(DispatcherError::NoSuchMethod), } } + +/// `lightning` dispatcher. +/// +/// # Note +/// +/// `gui_storage_method` is a method name with the `lightning::` prefix removed. +#[cfg(not(target_arch = "wasm32"))] +async fn lightning_dispatcher( + request: MmRpcRequest, + ctx: MmArc, + lightning_method: &str, +) -> DispatcherResult>> { + use coins::rpc_command::lightning as lightning_rpc; + + match lightning_method { + "add_trusted_node" => handle_mmrpc(ctx, request, lightning_rpc::trusted_nodes::add_trusted_node).await, + "close_channel" => handle_mmrpc(ctx, request, lightning_rpc::close_channel::close_channel).await, + "connect_to_node" => handle_mmrpc(ctx, request, lightning_rpc::connect_to_node::connect_to_node).await, + "generate_invoice" => handle_mmrpc(ctx, request, lightning_rpc::generate_invoice::generate_invoice).await, + "get_channel_details" => { + handle_mmrpc(ctx, request, lightning_rpc::get_channel_details::get_channel_details).await + }, + "get_claimable_balances" => { + handle_mmrpc( + ctx, + request, + lightning_rpc::get_claimable_balances::get_claimable_balances, + ) + .await + }, + "get_payment_details" => { + handle_mmrpc(ctx, request, lightning_rpc::get_payment_details::get_payment_details).await + }, + "list_closed_channels_by_filter" => { + handle_mmrpc( + ctx, + request, + lightning_rpc::list_channels::list_closed_channels_by_filter, + ) + .await + }, + "list_open_channels_by_filter" => { + handle_mmrpc(ctx, request, lightning_rpc::list_channels::list_open_channels_by_filter).await + }, + "list_payments_by_filter" => { + handle_mmrpc( + ctx, + request, + lightning_rpc::list_payments_by_filter::list_payments_by_filter, + ) + .await + }, + "list_trusted_nodes" => handle_mmrpc(ctx, request, lightning_rpc::trusted_nodes::list_trusted_nodes).await, + "open_channel" => handle_mmrpc(ctx, request, lightning_rpc::open_channel::open_channel).await, + "remove_trusted_node" => handle_mmrpc(ctx, request, lightning_rpc::trusted_nodes::remove_trusted_node).await, + "send_payment" => handle_mmrpc(ctx, request, lightning_rpc::send_payment::send_payment).await, + "update_channel" => handle_mmrpc(ctx, request, lightning_rpc::update_channel::update_channel).await, + _ => MmError::err(DispatcherError::NoSuchMethod), + } +} From abfb9a87ef265ae2542a9d58104a4758abbf267d Mon Sep 17 00:00:00 2001 From: shamardy Date: Wed, 21 Sep 2022 23:02:42 +0200 Subject: [PATCH 11/36] edit SwapMsg MakerPayment and TakerPayment to allow sending payment instructions to the other side --- mm2src/coins/lp_coins.rs | 3 + mm2src/mm2_main/src/docker_tests.rs | 3 - mm2src/mm2_main/src/lp_swap.rs | 132 ++++++++++++++++++---- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 28 +++-- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 30 +++-- mm2src/mm2_main/src/mm2_bin.rs | 1 - mm2src/mm2_main/src/mm2_lib.rs | 1 - 7 files changed, 152 insertions(+), 46 deletions(-) diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index bb5367b059..bb464decd5 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -577,6 +577,9 @@ pub trait SwapOps { ) -> Result, MmError>; fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair; + + // Todo: implement this for all coins + fn other_side_instructions(&self) -> Option> { None } } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/mm2_main/src/docker_tests.rs b/mm2src/mm2_main/src/docker_tests.rs index 91cdd47d47..c1729387a9 100644 --- a/mm2src/mm2_main/src/docker_tests.rs +++ b/mm2src/mm2_main/src/docker_tests.rs @@ -26,9 +26,6 @@ extern crate serde_json; extern crate serde_derive; #[cfg(test)] #[macro_use] -extern crate serialization_derive; -#[cfg(test)] -#[macro_use] extern crate ser_error_derive; #[cfg(test)] extern crate test; diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 8456b5e007..b222df7151 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -71,7 +71,6 @@ use mm2_core::mm_ctx::{from_ctx, MmArc}; use mm2_err_handle::prelude::*; use mm2_libp2p::{decode_signed, encode_and_sign, pub_sub_topic, TopicPrefix}; use mm2_number::{BigDecimal, BigRational, MmNumber}; -use primitives::hash::{H160, H264}; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; use serde_json::{self as json, Value as Json}; use std::collections::{HashMap, HashSet}; @@ -126,14 +125,14 @@ cfg_wasm32! { pub type SwapDbLocked<'a> = DbLocked<'a, SwapDb>; } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] pub enum SwapMsg { Negotiation(NegotiationDataMsg), NegotiationReply(NegotiationDataMsg), Negotiated(bool), TakerFee(Vec), - MakerPayment(Vec), - TakerPayment(Vec), + MakerPayment(PaymentDataMsg), + TakerPayment(PaymentDataMsg), } #[derive(Debug, Default)] @@ -142,8 +141,8 @@ pub struct SwapMsgStore { negotiation_reply: Option, negotiated: Option, taker_fee: Option>, - maker_payment: Option>, - taker_payment: Option>, + maker_payment: Option, + taker_payment: Option, accept_only_from: bits256, } @@ -232,8 +231,8 @@ pub async fn process_msg(ctx: MmArc, topic: &str, msg: &[u8]) { SwapMsg::NegotiationReply(data) => msg_store.negotiation_reply = Some(data), SwapMsg::Negotiated(negotiated) => msg_store.negotiated = Some(negotiated), SwapMsg::TakerFee(taker_fee) => msg_store.taker_fee = Some(taker_fee), - SwapMsg::MakerPayment(maker_payment) => msg_store.maker_payment = Some(maker_payment), - SwapMsg::TakerPayment(taker_payment) => msg_store.taker_payment = Some(taker_payment), + SwapMsg::MakerPayment(data) => msg_store.maker_payment = Some(data), + SwapMsg::TakerPayment(data) => msg_store.taker_payment = Some(data), } } else { warn!("Received message from unexpected sender for swap {}", uuid); @@ -689,13 +688,38 @@ impl NegotiationDataMsg { } } -/// Data to be exchanged and validated on swap start, the replacement of LP_pubkeys_data, LP_choosei_data, etc. -#[derive(Debug, Default, Deserializable, Eq, PartialEq, Serializable)] -struct SwapNegotiationData { - started_at: u64, - payment_locktime: u64, - secret_hash: H160, - persistent_pubkey: H264, +#[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] +pub struct PaymentDataV2 { + payment_data: Vec, + // Instructions for the other side whether taker or maker on how to make it's payment. + // An example for this is a maker/taker sending the taker/maker a lightning invoice to be payed. + other_side_instructions: Vec, +} + +#[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] +#[serde(untagged)] +pub enum PaymentDataMsg { + V1(Vec), + V2(PaymentDataV2), +} + +impl PaymentDataMsg { + #[inline] + pub fn payment_data(&self) -> &[u8] { + match self { + PaymentDataMsg::V1(v1) => v1, + PaymentDataMsg::V2(v2) => &v2.payment_data, + } + } + + #[allow(dead_code)] + #[inline] + pub fn other_side_instructions(&self) -> Option<&[u8]> { + match self { + PaymentDataMsg::V1(_) => None, + PaymentDataMsg::V2(v2) => Some(&v2.other_side_instructions), + } + } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -1242,7 +1266,6 @@ mod lp_swap_tests { use crypto::privkey::key_pair_from_seed; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_test_helpers::for_tests::{morty_conf, rick_conf, MORTY_ELECTRUM_ADDRS, RICK_ELECTRUM_ADDRS}; - use serialization::{deserialize, serialize}; #[test] fn test_dex_fee_amount() { @@ -1276,14 +1299,6 @@ mod lp_swap_tests { assert_eq!(dex_fee_threshold, actual_fee); } - #[test] - fn test_serde_swap_negotiation_data() { - let data = SwapNegotiationData::default(); - let bytes = serialize(&data); - let deserialized = deserialize(bytes.as_slice()).unwrap(); - assert_eq!(data, deserialized); - } - #[test] fn test_lp_atomic_locktime() { let maker_coin = "KMD"; @@ -1535,6 +1550,75 @@ mod lp_swap_tests { assert_eq!(deserialized, v3); } + #[test] + fn check_payment_data_serde() { + #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] + enum SwapMsgOld { + Negotiation(NegotiationDataMsg), + NegotiationReply(NegotiationDataMsg), + Negotiated(bool), + TakerFee(Vec), + MakerPayment(Vec), + TakerPayment(Vec), + } + + // old message format should be deserialized to PaymentDataMsg::V1 + let old = SwapMsgOld::MakerPayment(vec![1; 300]); + + let expected = SwapMsg::MakerPayment(PaymentDataMsg::V1(vec![1; 300])); + + let serialized = rmp_serde::to_vec(&old).unwrap(); + + let deserialized: SwapMsg = rmp_serde::from_read_ref(serialized.as_slice()).unwrap(); + + assert_eq!(deserialized, expected); + + // PaymentDataMsg::V1 should be deserialized to old message format + let v1 = SwapMsg::MakerPayment(PaymentDataMsg::V1(vec![1; 300])); + + let expected = old; + + let serialized = rmp_serde::to_vec(&v1).unwrap(); + + let deserialized: SwapMsgOld = rmp_serde::from_read_ref(serialized.as_slice()).unwrap(); + + assert_eq!(deserialized, expected); + + // PaymentDataMsg::V1 should be deserialized to PaymentDataMsg::V1 + let v1 = SwapMsg::MakerPayment(PaymentDataMsg::V1(vec![1; 300])); + + let serialized = rmp_serde::to_vec(&v1).unwrap(); + + let deserialized: SwapMsg = rmp_serde::from_read_ref(serialized.as_slice()).unwrap(); + + assert_eq!(deserialized, v1); + + // PaymentDataMsg::V2 should be deserialized to PaymentDataMsg::V2 + let v2 = SwapMsg::MakerPayment(PaymentDataMsg::V2(PaymentDataV2 { + payment_data: vec![1; 300], + other_side_instructions: vec![1; 300], + })); + + let serialized = rmp_serde::to_vec(&v2).unwrap(); + + let deserialized: SwapMsg = rmp_serde::from_read_ref(serialized.as_slice()).unwrap(); + + assert_eq!(deserialized, v2); + + // PaymentDataMsg::V2 shouldn't be deserialized to old message format, new nodes with payment instructions can't swap with old nodes without it. + let v2 = SwapMsg::MakerPayment(PaymentDataMsg::V2(PaymentDataV2 { + payment_data: vec![1; 300], + other_side_instructions: vec![1; 300], + })); + + let serialized = rmp_serde::to_vec(&v2).unwrap(); + + let deserialized: Result = + rmp_serde::from_read_ref(serialized.as_slice()); + + assert!(deserialized.is_err()); + } + fn utxo_activation_params(electrums: &[&str]) -> UtxoActivationParams { UtxoActivationParams { mode: UtxoRpcMode::Electrum { diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 02480e56b9..aff0c9cf04 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -3,16 +3,16 @@ use super::check_balance::{check_base_coin_balance_for_swap, check_my_coin_balan use super::pubkey_banning::ban_pubkey_on_failed_swap; use super::swap_lock::{SwapLock, SwapLockOps}; use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePreimageRpcResult}; -use super::{broadcast_my_swap_status, broadcast_swap_message_every, check_other_coin_balance_for_swap, - dex_fee_amount_from_taker_coin, get_locked_amount, recv_swap_msg, swap_topic, AtomicSwap, LockedAmount, - MySwapInfo, NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, - SavedSwap, SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapsContext, +use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_message_every, + check_other_coin_balance_for_swap, dex_fee_amount_from_taker_coin, get_locked_amount, recv_swap_msg, + swap_topic, tx_helper_topic, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, NegotiationDataV2, + NegotiationDataV3, PaymentDataMsg, PaymentDataV2, RecoveredSwap, RecoveredSwapAction, SavedSwap, + SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; use crate::mm2::lp_price::fetch_swap_coins_price; -use crate::mm2::lp_swap::{broadcast_p2p_tx_msg, tx_helper_topic}; use crate::mm2::MM_VERSION; use bitcrypto::dhash160; use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, SearchForSwapTxSpendInput, TradeFee, @@ -401,6 +401,18 @@ impl MakerSwap { } } + fn get_my_payment_data(&self) -> PaymentDataMsg { + let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); + if let Some(other_side_instructions) = self.taker_coin.other_side_instructions() { + PaymentDataMsg::V2(PaymentDataV2 { + payment_data, + other_side_instructions, + }) + } else { + PaymentDataMsg::V1(payment_data) + } + } + async fn start(&self) -> Result<(Option, Vec), String> { // do not use self.r().data here as it is not initialized at this step yet let preimage_value = TradePreimageValue::Exact(self.maker_amount.clone()); @@ -741,8 +753,8 @@ impl MakerSwap { } async fn wait_for_taker_payment(&self) -> Result<(Option, Vec), String> { - let maker_payment_hex = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); - let msg = SwapMsg::MakerPayment(maker_payment_hex); + let payment_data_msg = self.get_my_payment_data(); + let msg = SwapMsg::MakerPayment(payment_data_msg); let abort_send_handle = broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); @@ -786,7 +798,7 @@ impl MakerSwap { }; drop(abort_send_handle); - let taker_payment = match self.taker_coin.tx_enum_from_bytes(&payload) { + let taker_payment = match self.taker_coin.tx_enum_from_bytes(payload.payment_data()) { Ok(tx) => tx, Err(err) => { return Ok((Some(MakerSwapCommand::RefundMakerPayment), vec![ diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 48e4a55a4c..af675c2d45 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -3,15 +3,15 @@ use super::check_balance::{check_my_coin_balance_for_swap, CheckBalanceError, Ch use super::pubkey_banning::ban_pubkey_on_failed_swap; use super::swap_lock::{SwapLock, SwapLockOps}; use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePreimageRpcResult}; -use super::{broadcast_my_swap_status, broadcast_swap_message_every, check_other_coin_balance_for_swap, - dex_fee_amount_from_taker_coin, dex_fee_rate, dex_fee_threshold, get_locked_amount, recv_swap_msg, - swap_topic, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, NegotiationDataV2, - NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, - SwapConfirmationsSettings, SwapError, SwapMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; +use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_message_every, + check_other_coin_balance_for_swap, dex_fee_amount_from_taker_coin, dex_fee_rate, dex_fee_threshold, + get_locked_amount, recv_swap_msg, swap_topic, tx_helper_topic, AtomicSwap, LockedAmount, MySwapInfo, + NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, PaymentDataMsg, PaymentDataV2, RecoveredSwap, + RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, + SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MatchBy, OrderConfirmationsSettings, TakerAction, TakerOrderBuilder}; use crate::mm2::lp_price::fetch_swap_coins_price; -use crate::mm2::lp_swap::{broadcast_p2p_tx_msg, tx_helper_topic}; use crate::mm2::MM_VERSION; use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, ValidatePaymentInput}; @@ -810,6 +810,18 @@ impl TakerSwap { } } + fn get_my_payment_data(&self) -> PaymentDataMsg { + let payment_data = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); + if let Some(other_side_instructions) = self.maker_coin.other_side_instructions() { + PaymentDataMsg::V2(PaymentDataV2 { + payment_data, + other_side_instructions, + }) + } else { + PaymentDataMsg::V1(payment_data) + } + } + async fn start(&self) -> Result<(Option, Vec), String> { // do not use self.r().data here as it is not initialized at this step yet let stage = FeeApproxStage::StartSwap; @@ -1111,7 +1123,7 @@ impl TakerSwap { }, }; drop(abort_send_handle); - let maker_payment = match self.maker_coin.tx_enum_from_bytes(&payload) { + let maker_payment = match self.maker_coin.tx_enum_from_bytes(payload.payment_data()) { Ok(p) => p, Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ @@ -1248,8 +1260,8 @@ impl TakerSwap { } async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { - let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); - let msg = SwapMsg::TakerPayment(tx_hex); + let payment_data_msg = self.get_my_payment_data(); + let msg = SwapMsg::TakerPayment(payment_data_msg); let send_abort_handle = broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); diff --git a/mm2src/mm2_main/src/mm2_bin.rs b/mm2src/mm2_main/src/mm2_bin.rs index 81ed8097ad..0b4512f806 100644 --- a/mm2src/mm2_main/src/mm2_bin.rs +++ b/mm2src/mm2_main/src/mm2_bin.rs @@ -10,7 +10,6 @@ #[macro_use] extern crate gstuff; #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; -#[macro_use] extern crate serialization_derive; #[macro_use] extern crate ser_error_derive; #[path = "mm2.rs"] mod mm2; diff --git a/mm2src/mm2_main/src/mm2_lib.rs b/mm2src/mm2_main/src/mm2_lib.rs index b434c474a7..0afd80b500 100644 --- a/mm2src/mm2_main/src/mm2_lib.rs +++ b/mm2src/mm2_main/src/mm2_lib.rs @@ -11,7 +11,6 @@ #[macro_use] extern crate gstuff; #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; -#[macro_use] extern crate serialization_derive; #[macro_use] extern crate ser_error_derive; #[path = "mm2.rs"] mod mm2; From d93b834700056687428c7c727e79b51f3f93e100 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 22 Sep 2022 18:56:57 +0200 Subject: [PATCH 12/36] create_invoice_for_hash fn, impl other_side_instructions for LightningCoin, copy needed parts of SecretHashAlgo from iris-swap-poc branch --- mm2src/coins/lightning.rs | 86 ++++++++++++++++++- mm2src/coins/lightning/ln_utils.rs | 76 ++++++++++++++++ mm2src/coins/lp_coins.rs | 3 +- mm2src/mm2_main/src/lp_swap.rs | 41 +++++++++ mm2src/mm2_main/src/lp_swap/maker_swap.rs | 32 ++++--- .../src/lp_swap/recreate_swap_data.rs | 6 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 17 ++-- 7 files changed, 234 insertions(+), 27 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 0076394a5f..63903d110f 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -11,9 +11,10 @@ pub(crate) mod ln_storage; mod ln_utils; use super::DerivationMethod; +use crate::lightning::ln_utils::filter_channels; use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; -use crate::utxo::BlockchainNetwork; +use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, @@ -21,6 +22,7 @@ use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySy UtxoStandardCoin, ValidateAddressResult, ValidatePaymentInput, VerificationError, VerificationResult, WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; +use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; use bitcoin_hashes::sha256::Hash as Sha256; use bitcrypto::dhash256; @@ -37,8 +39,8 @@ use lightning::ln::channelmanager::{ChannelDetails, MIN_FINAL_CLTV_EXPIRY}; use lightning::ln::{PaymentHash, PaymentPreimage}; use lightning::routing::gossip; use lightning_background_processor::{BackgroundProcessor, GossipSync}; -use lightning_invoice::payment; use lightning_invoice::utils::DefaultRouter; +use lightning_invoice::{payment, CreationError, InvoiceBuilder, SignOrCreationError}; use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; use ln_db::{DBChannelDetails, DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; @@ -66,6 +68,7 @@ use std::fmt; use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; +use std::time::SystemTime; pub const DEFAULT_INVOICE_EXPIRY: u32 = 3600; @@ -346,6 +349,61 @@ impl LightningCoin { total, } } + + fn create_invoice_for_hash( + &self, + payment_hash: PaymentHash, + amt_msat: Option, + description: String, + invoice_expiry_delta_secs: u32, + ) -> Result> { + // Todo: maybe remove the expect? + let duration = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("for the foreseeable future this shouldn't happen"); + + let route_hints = filter_channels(self.channel_manager.list_usable_channels(), amt_msat); + + // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin + // supply. + let payment_secret = self + .channel_manager + .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs) + .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; + let our_node_pubkey = self.channel_manager.get_our_node_id(); + + let mut invoice = InvoiceBuilder::new(self.platform.network.clone().into()) + .description(description) + .duration_since_epoch(duration) + .payee_pub_key(our_node_pubkey) + .payment_hash(Hash::from_slice(&payment_hash.0).unwrap()) + .payment_secret(payment_secret) + .basic_mpp() + .min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into()) + .expiry_time(core::time::Duration::from_secs(invoice_expiry_delta_secs.into())); + if let Some(amt) = amt_msat { + invoice = invoice.amount_milli_satoshis(amt); + } + for hint in route_hints { + invoice = invoice.private_route(hint); + } + + let raw_invoice = match invoice.build_raw() { + Ok(inv) => inv, + Err(e) => return Err(SignOrCreationError::CreationError(e)), + }; + let hrp_str = raw_invoice.hrp.to_string(); + let hrp_bytes = hrp_str.as_bytes(); + let data_without_signature = raw_invoice.data.to_base32(); + let signed_raw_invoice = raw_invoice.sign(|_| { + self.keys_manager + .sign_invoice(hrp_bytes, &data_without_signature, Recipient::Node) + }); + match signed_raw_invoice { + Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()), + Err(e) => Err(SignOrCreationError::SignError(e)), + } + } } #[async_trait] @@ -487,6 +545,30 @@ impl SwapOps for LightningCoin { } fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } + + fn other_side_instructions(&self, secret_hash: &[u8], other_side_amount: &BigDecimal) -> Option> { + // Todo: lightning decimals should be 11 in config because it's up to msats but the function name is not good + // Todo: remove unwrap + let amt_msat = sat_from_big_decimal(other_side_amount, self.decimals()).unwrap(); + + if secret_hash.len() != 32 { + // return error here + } + let mut payment_hash = [b' '; 32]; + payment_hash.copy_from_slice(secret_hash); + + // Todo: maybe get invoice_expiry_delta_secs from locktime (not sure if needed), and maybe description can be the swap uuid + // Todo: remove unwrap + let invoice = self + .create_invoice_for_hash( + PaymentHash(payment_hash), + Some(amt_msat), + "".into(), + DEFAULT_INVOICE_EXPIRY, + ) + .unwrap(); + Some(invoice.to_string().into_bytes()) + } } impl MarketCoinOps for LightningCoin { diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index c08c59035c..1ad2fbccd3 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -11,9 +11,12 @@ use common::log::LogState; use lightning::chain::keysinterface::{InMemorySigner, KeysManager}; use lightning::chain::{chainmonitor, BestBlock, Watch}; use lightning::ln::channelmanager::{ChainParameters, ChannelManagerReadArgs, SimpleArcChannelManager}; +use lightning::routing::gossip::RoutingFees; +use lightning::routing::router::{RouteHint, RouteHintHop}; use lightning::util::config::UserConfig; use lightning::util::ser::ReadableArgs; use mm2_core::mm_ctx::MmArc; +use std::collections::hash_map::Entry; use std::fs::File; use std::path::PathBuf; use std::sync::Arc; @@ -241,3 +244,76 @@ pub async fn get_open_channels_nodes_addresses( }); Ok(nodes_addresses) } + +// Todo: Make this public by opening a PR in rust-lightning instead of importing it here +// Todo: revise this +/// Filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include +/// in the invoice. +/// +/// The filtering is based on the following criteria: +/// * Only one channel per counterparty node +/// * Always select the channel with the highest inbound capacity per counterparty node +/// * Filter out channels with a lower inbound capacity than `min_inbound_capacity_msat`, if any +/// channel with a higher or equal inbound capacity than `min_inbound_capacity_msat` exists +/// * If any public channel exists, the returned `RouteHint`s will be empty, and the sender will +/// need to find the path by looking at the public channels instead +pub(crate) fn filter_channels(channels: Vec, min_inbound_capacity_msat: Option) -> Vec { + let mut filtered_channels: HashMap = HashMap::new(); + let min_inbound_capacity = min_inbound_capacity_msat.unwrap_or(0); + let mut min_capacity_channel_exists = false; + + for channel in channels.iter() { + if channel.get_inbound_payment_scid().is_none() || channel.counterparty.forwarding_info.is_none() { + continue; + } + + // Todo: maybe check for inbound_capacity_msat first?? + if channel.is_public { + // If any public channel exists, return no hints and let the sender + // look at the public channels instead. + return vec![]; + } + + if channel.inbound_capacity_msat >= min_inbound_capacity { + min_capacity_channel_exists = true; + }; + match filtered_channels.entry(channel.counterparty.node_id) { + Entry::Occupied(mut entry) => { + let current_max_capacity = entry.get().inbound_capacity_msat; + if channel.inbound_capacity_msat < current_max_capacity { + continue; + } + entry.insert(channel); + }, + Entry::Vacant(entry) => { + entry.insert(channel); + }, + } + } + + let route_hint_from_channel = |channel: &ChannelDetails| { + let forwarding_info = channel.counterparty.forwarding_info.as_ref().unwrap(); + RouteHint(vec![RouteHintHop { + src_node_id: channel.counterparty.node_id, + short_channel_id: channel.get_inbound_payment_scid().unwrap(), + fees: RoutingFees { + base_msat: forwarding_info.fee_base_msat, + proportional_millionths: forwarding_info.fee_proportional_millionths, + }, + cltv_expiry_delta: forwarding_info.cltv_expiry_delta, + htlc_minimum_msat: channel.inbound_htlc_minimum_msat, + htlc_maximum_msat: channel.inbound_htlc_maximum_msat, + }]) + }; + // If all channels are private, return the route hint for the highest inbound capacity channel + // per counterparty node. If channels with an higher inbound capacity than the + // min_inbound_capacity exists, filter out the channels with a lower capacity than that. + filtered_channels + .into_iter() + .filter(|(_counterparty_id, channel)| { + // Todo: can this be channel.inbound_capacity_msat >= min_inbound_capacity only check min_capacity_channel_exists + !min_capacity_channel_exists || channel.inbound_capacity_msat >= min_inbound_capacity + }) + .map(|(_counterparty_id, channel)| route_hint_from_channel(channel)) + .collect::>() +} diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index bb464decd5..08bdeb08f8 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -578,8 +578,7 @@ pub trait SwapOps { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair; - // Todo: implement this for all coins - fn other_side_instructions(&self) -> Option> { None } + fn other_side_instructions(&self, _secret_hash: &[u8], _other_side_amount: &BigDecimal) -> Option> { None } } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index b222df7151..b6f808ca04 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -80,6 +80,7 @@ use std::str::FromStr; use std::sync::{Arc, Mutex, Weak}; use uuid::Uuid; +use bitcrypto::{dhash160, sha256}; #[cfg(feature = "custom-swap-locktime")] use std::sync::atomic::{AtomicU64, Ordering}; @@ -1253,6 +1254,46 @@ pub async fn active_swaps_rpc(ctx: MmArc, req: Json) -> Result> Ok(try_s!(Response::builder().body(res))) } +enum SecretHashAlgo { + /// ripemd160(sha256(secret)) + DHASH160, + /// sha256(secret) + SHA256, +} + +impl Default for SecretHashAlgo { + fn default() -> Self { SecretHashAlgo::DHASH160 } +} + +impl SecretHashAlgo { + fn hash_secret(&self, secret: &[u8]) -> Vec { + match self { + SecretHashAlgo::DHASH160 => dhash160(secret).take().into(), + SecretHashAlgo::SHA256 => sha256(secret).take().into(), + } + } +} + +// Todo: Maybe add a secret_hash_algo method to the SwapOps trait instead +#[cfg(not(target_arch = "wasm32"))] +fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> SecretHashAlgo { + match (maker_coin, taker_coin) { + (MmCoinEnum::Tendermint(_) | MmCoinEnum::LightningCoin(_), _) => SecretHashAlgo::SHA256, + (_, MmCoinEnum::Tendermint(_) | MmCoinEnum::LightningCoin(_)) => SecretHashAlgo::SHA256, + (_, _) => SecretHashAlgo::DHASH160, + } +} + +// Todo: Maybe add a secret_hash_algo method to the SwapOps trait instead +#[cfg(target_arch = "wasm32")] +fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> SecretHashAlgo { + match (maker_coin, taker_coin) { + (MmCoinEnum::Tendermint(_), _) => SecretHashAlgo::SHA256, + (_, MmCoinEnum::Tendermint(_)) => SecretHashAlgo::SHA256, + (_, _) => SecretHashAlgo::DHASH160, + } +} + #[cfg(all(test, not(target_arch = "wasm32")))] mod lp_swap_tests { use super::*; diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index aff0c9cf04..8e6c10f985 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -4,17 +4,16 @@ use super::pubkey_banning::ban_pubkey_on_failed_swap; use super::swap_lock::{SwapLock, SwapLockOps}; use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePreimageRpcResult}; use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_message_every, - check_other_coin_balance_for_swap, dex_fee_amount_from_taker_coin, get_locked_amount, recv_swap_msg, - swap_topic, tx_helper_topic, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, NegotiationDataV2, - NegotiationDataV3, PaymentDataMsg, PaymentDataV2, RecoveredSwap, RecoveredSwapAction, SavedSwap, - SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapsContext, - TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; + check_other_coin_balance_for_swap, detect_secret_hash_algo, dex_fee_amount_from_taker_coin, + get_locked_amount, recv_swap_msg, swap_topic, tx_helper_topic, AtomicSwap, LockedAmount, MySwapInfo, + NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, PaymentDataMsg, PaymentDataV2, RecoveredSwap, + RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, SecretHashAlgo, SwapConfirmationsSettings, + SwapError, SwapMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::MM_VERSION; -use bitcrypto::dhash160; use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, TransactionEnum, ValidatePaymentInput}; use common::log::{debug, error, info, warn}; @@ -26,9 +25,9 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; use parking_lot::Mutex as PaMutex; -use primitives::hash::{H160, H256, H264}; +use primitives::hash::{H256, H264}; use rand::Rng; -use rpc::v1::types::{Bytes as BytesJson, H160 as H160Json, H256 as H256Json, H264 as H264Json}; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; use std::any::TypeId; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -129,7 +128,7 @@ pub struct MakerSwapData { pub maker_coin: String, pub taker: H256Json, pub secret: H256Json, - pub secret_hash: Option, + pub secret_hash: Option, pub my_persistent_pub: H264Json, pub lock_duration: u64, pub maker_amount: BigDecimal, @@ -213,6 +212,7 @@ pub struct MakerSwap { /// Temporary privkey used to sign P2P messages when applicable p2p_privkey: Option, secret: H256, + secret_hash_algo: SecretHashAlgo, #[cfg(test)] pub(super) fail_at: Option, } @@ -228,12 +228,13 @@ impl MakerSwap { pub fn generate_secret() -> [u8; 32] { rand::thread_rng().gen() } #[inline] - fn secret_hash(&self) -> H160 { + fn secret_hash(&self) -> Vec { self.r() .data .secret_hash - .map(H160Json::into) - .unwrap_or_else(|| dhash160(self.secret.as_slice())) + .as_ref() + .map(|bytes| bytes.0.clone()) + .unwrap_or_else(|| self.secret_hash_algo.hash_secret(self.secret.as_slice())) } #[inline] @@ -333,6 +334,7 @@ impl MakerSwap { p2p_privkey: Option, secret: H256, ) -> Self { + let secret_hash_algo = detect_secret_hash_algo(&maker_coin, &taker_coin); MakerSwap { maker_coin, taker_coin, @@ -362,6 +364,7 @@ impl MakerSwap { }), ctx, secret, + secret_hash_algo, #[cfg(test)] fail_at: None, } @@ -403,7 +406,10 @@ impl MakerSwap { fn get_my_payment_data(&self) -> PaymentDataMsg { let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); - if let Some(other_side_instructions) = self.taker_coin.other_side_instructions() { + if let Some(other_side_instructions) = self + .taker_coin + .other_side_instructions(&self.secret_hash(), &self.taker_amount) + { PaymentDataMsg::V2(PaymentDataV2 { payment_data, other_side_instructions, 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 ecb3cd8db9..18af44eb74 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -9,7 +9,7 @@ use common::{HttpStatusCode, StatusCode}; use derive_more::Display; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use rpc::v1::types::{H160 as H160Json, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; pub type RecreateSwapResult = Result>; @@ -354,7 +354,7 @@ async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> Recreate let taker_negotiated_event = TakerSwapEvent::Negotiated(MakerNegotiationData { maker_payment_locktime: started_event.maker_payment_lock, maker_pubkey: started_event.my_persistent_pub, - secret_hash, + secret_hash: secret_hash.clone(), maker_coin_swap_contract_addr: negotiated_event.maker_coin_swap_contract_addr, taker_coin_swap_contract_addr: negotiated_event.taker_coin_swap_contract_addr, maker_coin_htlc_pubkey: started_event.maker_coin_htlc_pubkey, @@ -394,7 +394,7 @@ async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> Recreate fn convert_maker_to_taker_events( event_it: impl Iterator, maker_coin: MmCoinEnum, - secret_hash: H160Json, + secret_hash: BytesJson, wait_refund_until: u64, ) -> Vec { let mut events = Vec::new(); diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index af675c2d45..979b531277 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -27,7 +27,7 @@ use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; use parking_lot::Mutex as PaMutex; use primitives::hash::H264; -use rpc::v1::types::{Bytes as BytesJson, H160 as H160Json, H256 as H256Json, H264 as H264Json}; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; use serde_json::{self as json, Value as Json}; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -465,7 +465,7 @@ pub struct TakerSwapMut { maker_payment_spend: Option, taker_payment_spend: Option, taker_payment_refund: Option, - secret_hash: H160Json, + secret_hash: BytesJson, secret: H256Json, } @@ -521,7 +521,7 @@ pub struct TakerPaymentSpentData { pub struct MakerNegotiationData { pub maker_payment_locktime: u64, pub maker_pubkey: H264Json, - pub secret_hash: H160Json, + pub secret_hash: BytesJson, pub maker_coin_swap_contract_addr: Option, pub taker_coin_swap_contract_addr: Option, pub maker_coin_htlc_pubkey: Option, @@ -772,7 +772,7 @@ impl TakerSwap { taker_payment_spend: None, maker_payment_spend: None, taker_payment_refund: None, - secret_hash: H160Json::default(), + secret_hash: BytesJson::default(), secret: H256Json::default(), }), ctx, @@ -811,8 +811,11 @@ impl TakerSwap { } fn get_my_payment_data(&self) -> PaymentDataMsg { - let payment_data = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); - if let Some(other_side_instructions) = self.maker_coin.other_side_instructions() { + let r = self.r(); + let payment_data = r.taker_payment.as_ref().unwrap().tx_hex.0.clone(); + let maker_amount = self.maker_amount.clone().into(); + if let Some(other_side_instructions) = self.maker_coin.other_side_instructions(&r.secret_hash.0, &maker_amount) + { PaymentDataMsg::V2(PaymentDataV2 { payment_data, other_side_instructions, @@ -1548,7 +1551,7 @@ impl TakerSwap { // so it can't be used across await let other_maker_coin_htlc_pub = self.r().other_maker_coin_htlc_pub; let other_taker_coin_htlc_pub = self.r().other_taker_coin_htlc_pub; - let secret_hash = self.r().secret_hash.0; + let secret_hash = self.r().secret_hash.0.clone(); let maker_coin_start_block = self.r().data.maker_coin_start_block; let maker_coin_swap_contract_address = self.r().data.maker_coin_swap_contract_address.clone(); From 62d5a9ad5f61cdd83407356bc82f98e5072d5d3b Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 23 Sep 2022 16:40:29 +0200 Subject: [PATCH 13/36] check connection to nodes before generating route hints, refactors --- mm2src/coins/eth.rs | 23 +++++++---- mm2src/coins/lightning.rs | 46 ++++++++++++++-------- mm2src/coins/lightning/ln_utils.rs | 7 ++-- mm2src/coins/lp_coins.rs | 16 +++++++- mm2src/coins/qrc20.rs | 20 +++++++--- mm2src/coins/solana.rs | 16 ++++++-- mm2src/coins/solana/spl.rs | 16 ++++++-- mm2src/coins/tendermint/tendermint_coin.rs | 18 ++++++--- mm2src/coins/test_coin.rs | 14 +++++-- mm2src/coins/utxo/bch.rs | 15 +++++-- mm2src/coins/utxo/qtum.rs | 16 ++++++-- mm2src/coins/utxo/slp.rs | 10 ++++- mm2src/coins/utxo/utxo_standard.rs | 14 +++++-- mm2src/coins/z_coin.rs | 19 ++++++--- mm2src/mm2_main/src/lp_swap.rs | 4 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 25 ++++++++---- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 33 +++++++++++----- 17 files changed, 228 insertions(+), 84 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index a3f523f98b..93b4a72b6d 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -61,13 +61,14 @@ use web3_transport::{EthFeeHistoryNamespace, Web3Transport, Web3TransportNode}; use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CoinBalance, CoinProtocol, CoinTransportMetrics, CoinsContext, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, NumConversError, NumConversResult, RawTransactionError, RawTransactionFut, - RawTransactionRequest, RawTransactionRes, RawTransactionResult, RpcClientType, RpcTransportEventHandler, - RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, - TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, - TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, VerificationError, - VerificationResult, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + NegotiateSwapContractAddrErr, NumConversError, NumConversResult, OtherInstructionsErr, + RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RawTransactionResult, + RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, + SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, + TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr, + TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, + VerificationError, VerificationResult, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, + WithdrawResult}; pub use rlp; @@ -1111,6 +1112,14 @@ impl SwapOps for EthCoin { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> keys::KeyPair { key_pair_from_secret(self.key_pair.secret()).expect("valid key") } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } #[cfg_attr(test, mockable)] diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 63903d110f..a097a81d92 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -16,11 +16,11 @@ use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, - SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, - TradePreimageValue, TransactionEnum, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, - UtxoStandardCoin, ValidateAddressResult, ValidatePaymentInput, VerificationError, VerificationResult, - WithdrawError, WithdrawFut, WithdrawRequest}; + NegotiateSwapContractAddrErr, OtherInstructionsErr, RawTransactionFut, RawTransactionRequest, + SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TransactionEnum, TransactionFut, TxMarshalingErr, + UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidatePaymentInput, + VerificationError, VerificationResult, WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -350,18 +350,27 @@ impl LightningCoin { } } - fn create_invoice_for_hash( + async fn create_invoice_for_hash( &self, payment_hash: PaymentHash, amt_msat: Option, description: String, invoice_expiry_delta_secs: u32, ) -> Result> { - // Todo: maybe remove the expect? let duration = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("for the foreseeable future this shouldn't happen"); + let open_channels_nodes = self.open_channels_nodes.lock().clone(); + for (node_pubkey, node_addr) in open_channels_nodes { + ln_p2p::connect_to_ln_node(node_pubkey, node_addr, self.peer_manager.clone()) + .await + .error_log_with_msg(&format!( + "Channel with node: {} can't be used for invoice routing hints due to connection error.", + node_pubkey + )); + } + let route_hints = filter_channels(self.channel_manager.list_usable_channels(), amt_msat); // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin @@ -376,7 +385,7 @@ impl LightningCoin { .description(description) .duration_since_epoch(duration) .payee_pub_key(our_node_pubkey) - .payment_hash(Hash::from_slice(&payment_hash.0).unwrap()) + .payment_hash(Hash::from_inner(payment_hash.0)) .payment_secret(payment_secret) .basic_mpp() .min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into()) @@ -400,7 +409,7 @@ impl LightningCoin { .sign_invoice(hrp_bytes, &data_without_signature, Recipient::Node) }); match signed_raw_invoice { - Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()), + Ok(inv) => Ok(Invoice::from_signed(inv).map_err(|_| SignOrCreationError::SignError(()))?), Err(e) => Err(SignOrCreationError::SignError(e)), } } @@ -546,10 +555,13 @@ impl SwapOps for LightningCoin { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } - fn other_side_instructions(&self, secret_hash: &[u8], other_side_amount: &BigDecimal) -> Option> { - // Todo: lightning decimals should be 11 in config because it's up to msats but the function name is not good - // Todo: remove unwrap - let amt_msat = sat_from_big_decimal(other_side_amount, self.decimals()).unwrap(); + async fn other_side_instructions( + &self, + secret_hash: &[u8], + other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + // lightning decimals should be 11 in config since the smallest divisible unit in lightning coin is msat + let amt_msat = sat_from_big_decimal(other_side_amount, self.decimals())?; if secret_hash.len() != 32 { // return error here @@ -557,8 +569,7 @@ impl SwapOps for LightningCoin { let mut payment_hash = [b' '; 32]; payment_hash.copy_from_slice(secret_hash); - // Todo: maybe get invoice_expiry_delta_secs from locktime (not sure if needed), and maybe description can be the swap uuid - // Todo: remove unwrap + // Todo: Maybe the description can be the swap uuid let invoice = self .create_invoice_for_hash( PaymentHash(payment_hash), @@ -566,8 +577,9 @@ impl SwapOps for LightningCoin { "".into(), DEFAULT_INVOICE_EXPIRY, ) - .unwrap(); - Some(invoice.to_string().into_bytes()) + .await + .map_to_mm(|e| OtherInstructionsErr::LightningInvoiceErr(e.to_string()))?; + Ok(Some(invoice.to_string().into_bytes())) } } diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index 1ad2fbccd3..eef2a81de7 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -246,7 +246,7 @@ pub async fn get_open_channels_nodes_addresses( } // Todo: Make this public by opening a PR in rust-lightning instead of importing it here -// Todo: revise this +// Todo: can a node that has all private channels send wrong data (we need to validate the invoice on the receiver side, if no public channels then trust the sender??) /// Filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include /// in the invoice. /// @@ -267,7 +267,7 @@ pub(crate) fn filter_channels(channels: Vec, min_inbound_capacit continue; } - // Todo: maybe check for inbound_capacity_msat first?? + // Todo: maybe check for inbound_capacity_msat first?? can this be used for probing?? I don't think so but how can it be avoided?? if channel.is_public { // If any public channel exists, return no hints and let the sender // look at the public channels instead. @@ -292,9 +292,11 @@ pub(crate) fn filter_channels(channels: Vec, min_inbound_capacit } let route_hint_from_channel = |channel: &ChannelDetails| { + // It's safe to unwrap here since all filtered_channels have forwarding_info let forwarding_info = channel.counterparty.forwarding_info.as_ref().unwrap(); RouteHint(vec![RouteHintHop { src_node_id: channel.counterparty.node_id, + // It's safe to unwrap here since all filtered_channels have inbound_payment_scid short_channel_id: channel.get_inbound_payment_scid().unwrap(), fees: RoutingFees { base_msat: forwarding_info.fee_base_msat, @@ -311,7 +313,6 @@ pub(crate) fn filter_channels(channels: Vec, min_inbound_capacit filtered_channels .into_iter() .filter(|(_counterparty_id, channel)| { - // Todo: can this be channel.inbound_capacity_msat >= min_inbound_capacity only check min_capacity_channel_exists !min_capacity_channel_exists || channel.inbound_capacity_msat >= min_inbound_capacity }) .map(|(_counterparty_id, channel)| route_hint_from_channel(channel)) diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 08bdeb08f8..35f09dcb0e 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -457,6 +457,16 @@ pub struct SearchForSwapTxSpendInput<'a> { pub swap_unique_data: &'a [u8], } +#[derive(Display)] +pub enum OtherInstructionsErr { + LightningInvoiceErr(String), + InternalError(String), +} + +impl From for OtherInstructionsErr { + fn from(e: NumConversError) -> Self { OtherInstructionsErr::InternalError(e.to_string()) } +} + /// Swap operations (mostly based on the Hash/Time locked transactions implemented by coin wallets). #[async_trait] pub trait SwapOps { @@ -578,7 +588,11 @@ pub trait SwapOps { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair; - fn other_side_instructions(&self, _secret_hash: &[u8], _other_side_amount: &BigDecimal) -> Option> { None } + async fn other_side_instructions( + &self, + secret_hash: &[u8], + other_side_amount: &BigDecimal, + ) -> Result>, MmError>; } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index d137e75db4..21923c7e0c 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -14,12 +14,12 @@ use crate::utxo::{qtum, ActualTxFee, AdditionalTxData, BroadcastTxErr, FeePolicy UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK}; use crate::{BalanceError, BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, - MmCoin, NegotiateSwapContractAddrErr, PrivKeyNotAllowed, RawTransactionFut, RawTransactionRequest, - SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, - TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, - TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidatePaymentInput, VerificationResult, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, - WithdrawResult}; + MmCoin, NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyNotAllowed, RawTransactionFut, + RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageError, + TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, + TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidatePaymentInput, VerificationResult, WithdrawError, WithdrawFee, WithdrawFut, + WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -1002,6 +1002,14 @@ impl SwapOps for Qrc20Coin { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } impl MarketCoinOps for Qrc20Coin { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 5a487851b6..8adc951149 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -2,10 +2,10 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, Trade use crate::solana::solana_common::{lamports_to_sol, PrepareTransferData, SufficientBalanceError}; use crate::solana::spl::SplTokenInfo; use crate::{BalanceError, BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, - RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, - TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionType, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, - VerificationResult, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; + OtherInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, + SignatureResult, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, + TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidatePaymentInput, VerificationResult, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -581,6 +581,14 @@ impl SwapOps for SolanaCoin { } fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { todo!() } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + unimplemented!() + } } #[allow(clippy::forget_ref, clippy::forget_copy, clippy::cast_ref_to_mut)] diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index de7639647c..fdbff3e4e2 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -1,10 +1,10 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, TradeFee, TransactionEnum}; use crate::solana::solana_common::{ui_amount_to_amount, PrepareTransferData, SufficientBalanceError}; use crate::solana::{solana_common, AccountError, SolanaCommonOps, SolanaFeeDetails}; -use crate::{BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, RawTransactionFut, - RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SolanaCoin, TradePreimageFut, - TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionType, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, +use crate::{BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, OtherInstructionsErr, + RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SolanaCoin, + TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, + TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, VerificationResult, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bincode::serialize; @@ -415,6 +415,14 @@ impl SwapOps for SplToken { } fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { todo!() } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + unimplemented!() + } } #[allow(clippy::forget_ref, clippy::forget_copy, clippy::cast_ref_to_mut)] diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 7cc9cb3d2a..f661c1cf40 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -6,11 +6,11 @@ use crate::tendermint::htlc::MsgClaimHtlc; use crate::utxo::sat_from_big_decimal; use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, - RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, - TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidatePaymentInput, VerificationResult, WithdrawError, WithdrawFut, - WithdrawRequest}; + OtherInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, + SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, + TransactionDetails, TransactionEnum, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, VerificationResult, + WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcrypto::sha256; use common::{get_utc_timestamp, Future01CompatExt}; @@ -751,6 +751,14 @@ impl SwapOps for TendermintCoin { } fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { todo!() } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } #[cfg(test)] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index d6ff8a1d67..0123015802 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -1,9 +1,9 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, SwapOps, TradeFee, TransactionEnum, TransactionFut}; use crate::{BalanceFut, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, - SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, TradePreimageValue, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, - VerificationResult, WithdrawFut, WithdrawRequest}; + OtherInstructionsErr, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, + TradePreimageValue, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidatePaymentInput, VerificationResult, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use futures01::Future; use keys::KeyPair; @@ -237,6 +237,14 @@ impl SwapOps for TestCoin { fn can_refund_htlc(&self, locktime: u64) -> Box + Send + '_> { unimplemented!() } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + unimplemented!() + } } #[async_trait] diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 5b7de796c3..6d494b3033 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -7,9 +7,10 @@ use crate::utxo::slp::{parse_slp_script, ParseSlpScriptError, SlpGenesisParams, use crate::utxo::utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::{BlockHeightAndTime, CanRefundHtlc, CoinBalance, CoinProtocol, NegotiateSwapContractAddrErr, - PrivKeyBuildPolicy, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, - SwapOps, TradePreimageValue, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, VerificationResult, WithdrawFut}; + OtherInstructionsErr, PrivKeyBuildPolicy, RawTransactionFut, RawTransactionRequest, + SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradePreimageValue, TransactionFut, TransactionType, + TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, + VerificationResult, WithdrawFut}; use common::log::warn; use derive_more::Display; use futures::{FutureExt, TryFutureExt}; @@ -1044,6 +1045,14 @@ impl SwapOps for BchCoin { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } fn total_unspent_value<'a>(unspents: impl IntoIterator) -> u64 { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 28fb1bb77c..64a979ad4f 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -18,10 +18,10 @@ use crate::utxo::utxo_builder::{BlockHeaderUtxoArcOps, MergeUtxoArcOps, UtxoCoin UtxoCoinBuilderCommonOps, UtxoFieldsWithHardwareWalletBuilder, UtxoFieldsWithIguanaPrivKeyBuilder}; use crate::{eth, CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, DelegationError, DelegationFut, - GetWithdrawSenderAddress, NegotiateSwapContractAddrErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, - SignatureResult, StakingInfosFut, SwapOps, TradePreimageValue, TransactionFut, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, VerificationResult, WithdrawFut, - WithdrawSenderAddress}; + GetWithdrawSenderAddress, NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyBuildPolicy, + SearchForSwapTxSpendInput, SignatureResult, StakingInfosFut, SwapOps, TradePreimageValue, TransactionFut, + TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, + VerificationResult, WithdrawFut, WithdrawSenderAddress}; use crypto::Bip44Chain; use ethereum_types::H160; use futures::{FutureExt, TryFutureExt}; @@ -703,6 +703,14 @@ impl SwapOps for QtumCoin { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } impl MarketCoinOps for QtumCoin { diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 0112508cd8..5c730fe4eb 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -13,7 +13,7 @@ use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualTxFee, Addit FeePolicy, GenerateTxError, RecentlySpentOutPointsGuard, UtxoCoinConf, UtxoCoinFields, UtxoCommonOps, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, NumConversError, PrivKeyNotAllowed, RawTransactionFut, + NegotiateSwapContractAddrErr, NumConversError, OtherInstructionsErr, PrivKeyNotAllowed, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, @@ -1440,6 +1440,14 @@ impl SwapOps for SlpToken { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { utxo_common::derive_htlc_key_pair(self.platform_coin.as_ref(), swap_unique_data) } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } impl From for TradePreimageError { diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 06f2623f17..a417a6d5ad 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -16,9 +16,9 @@ use crate::rpc_command::init_scan_for_new_addresses::{self, InitScanAddressesRpc use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawTaskHandle}; use crate::utxo::utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}; use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, GetWithdrawSenderAddress, - NegotiateSwapContractAddrErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, SignatureResult, SwapOps, - TradePreimageValue, TransactionFut, TxMarshalingErr, ValidateAddressResult, ValidatePaymentInput, - VerificationResult, WithdrawFut, WithdrawSenderAddress}; + NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, + SignatureResult, SwapOps, TradePreimageValue, TransactionFut, TxMarshalingErr, ValidateAddressResult, + ValidatePaymentInput, VerificationResult, WithdrawFut, WithdrawSenderAddress}; use crypto::Bip44Chain; use futures::{FutureExt, TryFutureExt}; use mm2_metrics::MetricsArc; @@ -464,6 +464,14 @@ impl SwapOps for UtxoStandardCoin { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } impl MarketCoinOps for UtxoStandardCoin { diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 7e9f0dc536..930af5d633 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -12,11 +12,12 @@ use crate::utxo::{sat_from_big_decimal, utxo_common, ActualTxFee, AdditionalTxDa UtxoCommonOps, UtxoFeeDetails, UtxoRpcMode, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom}; use crate::{BalanceError, BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, - MmCoin, NegotiateSwapContractAddrErr, NumConversError, PrivKeyActivationPolicy, RawTransactionFut, - RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, - TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidatePaymentInput, VerificationError, VerificationResult, WithdrawFut, WithdrawRequest}; + MmCoin, NegotiateSwapContractAddrErr, NumConversError, OtherInstructionsErr, PrivKeyActivationPolicy, + RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, + SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, + TransactionEnum, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidatePaymentInput, VerificationError, VerificationResult, WithdrawFut, + WithdrawRequest}; use crate::{Transaction, WithdrawError}; use async_trait::async_trait; use bitcrypto::{dhash160, dhash256}; @@ -1370,6 +1371,14 @@ impl SwapOps for ZCoin { let key = secp_privkey_from_hash(dhash256(&signature)); key_pair_from_secret(key.as_slice()).expect("valid privkey") } + + async fn other_side_instructions( + &self, + _secret_hash: &[u8], + _other_side_amount: &BigDecimal, + ) -> Result>, MmError> { + Ok(None) + } } #[async_trait] diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index b6f808ca04..c77c6455ea 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1275,11 +1275,13 @@ impl SecretHashAlgo { } // Todo: Maybe add a secret_hash_algo method to the SwapOps trait instead +// Todo: both sides don't need to use SHA256 in the payment script if only one side requires it (check send_taker_payment if SecretHashAlgo::SHA256) #[cfg(not(target_arch = "wasm32"))] fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> SecretHashAlgo { match (maker_coin, taker_coin) { (MmCoinEnum::Tendermint(_) | MmCoinEnum::LightningCoin(_), _) => SecretHashAlgo::SHA256, - (_, MmCoinEnum::Tendermint(_) | MmCoinEnum::LightningCoin(_)) => SecretHashAlgo::SHA256, + // If taker is lightning coin the SHA256 of the secret will be sent as part of the maker signed invoice + (_, MmCoinEnum::Tendermint(_)) => SecretHashAlgo::SHA256, (_, _) => SecretHashAlgo::DHASH160, } } diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 8e6c10f985..86b1c43153 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -14,8 +14,8 @@ use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::MM_VERSION; -use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, SearchForSwapTxSpendInput, TradeFee, - TradePreimageValue, TransactionEnum, ValidatePaymentInput}; +use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, OtherInstructionsErr, + SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, TransactionEnum, ValidatePaymentInput}; use common::log::{debug, error, info, warn}; use common::{bits256, executor::Timer, now_ms, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::SerializableSecp256k1Keypair; @@ -404,18 +404,19 @@ impl MakerSwap { } } - fn get_my_payment_data(&self) -> PaymentDataMsg { + async fn get_my_payment_data(&self) -> Result> { let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); if let Some(other_side_instructions) = self .taker_coin .other_side_instructions(&self.secret_hash(), &self.taker_amount) + .await? { - PaymentDataMsg::V2(PaymentDataV2 { + Ok(PaymentDataMsg::V2(PaymentDataV2 { payment_data, other_side_instructions, - }) + })) } else { - PaymentDataMsg::V1(payment_data) + Ok(PaymentDataMsg::V1(payment_data)) } } @@ -759,7 +760,17 @@ impl MakerSwap { } async fn wait_for_taker_payment(&self) -> Result<(Option, Vec), String> { - let payment_data_msg = self.get_my_payment_data(); + let payment_data_msg = match self.get_my_payment_data().await { + Ok(data) => data, + Err(e) => { + return Ok((Some(MakerSwapCommand::RefundMakerPayment), vec![ + MakerSwapEvent::MakerPaymentDataSendFailed(e.to_string().into()), + MakerSwapEvent::MakerPaymentWaitRefundStarted { + wait_until: self.wait_refund_until(), + }, + ])) + }, + }; let msg = SwapMsg::MakerPayment(payment_data_msg); let abort_send_handle = broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 979b531277..a399ba3602 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -13,8 +13,8 @@ use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MatchBy, OrderConfirmationsSettings, TakerAction, TakerOrderBuilder}; use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::MM_VERSION; -use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, SearchForSwapTxSpendInput, - TradeFee, TradePreimageValue, ValidatePaymentInput}; +use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, OtherInstructionsErr, + SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, ValidatePaymentInput}; use common::executor::Timer; use common::log::{debug, error, info, warn}; use common::{bits256, now_ms, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -810,18 +810,21 @@ impl TakerSwap { } } - fn get_my_payment_data(&self) -> PaymentDataMsg { - let r = self.r(); - let payment_data = r.taker_payment.as_ref().unwrap().tx_hex.0.clone(); + async fn get_my_payment_data(&self) -> Result> { + let payment_data = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); + let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); - if let Some(other_side_instructions) = self.maker_coin.other_side_instructions(&r.secret_hash.0, &maker_amount) + if let Some(other_side_instructions) = self + .maker_coin + .other_side_instructions(&secret_hash, &maker_amount) + .await? { - PaymentDataMsg::V2(PaymentDataV2 { + Ok(PaymentDataMsg::V2(PaymentDataV2 { payment_data, other_side_instructions, - }) + })) } else { - PaymentDataMsg::V1(payment_data) + Ok(PaymentDataMsg::V1(payment_data)) } } @@ -1263,7 +1266,17 @@ impl TakerSwap { } async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { - let payment_data_msg = self.get_my_payment_data(); + let payment_data_msg = match self.get_my_payment_data().await { + Ok(data) => data, + Err(e) => { + return Ok((Some(TakerSwapCommand::RefundTakerPayment), vec![ + TakerSwapEvent::TakerPaymentDataSendFailed(e.to_string().into()), + TakerSwapEvent::TakerPaymentWaitRefundStarted { + wait_until: self.wait_refund_until(), + }, + ])) + }, + }; let msg = SwapMsg::TakerPayment(payment_data_msg); let send_abort_handle = broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); From 3bb77df219b465ea5d71a171d92732b8e4169e97 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 27 Sep 2022 13:37:15 +0200 Subject: [PATCH 14/36] fix TakerFee msg to include PaymentDataMsg (lightning invoice to be paid by maker to taker) instead of TakerPayment msg --- mm2src/mm2_main/src/lp_swap.rs | 12 +++++----- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 7 ++++-- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 28 +++++++++++------------ 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index c77c6455ea..f08ac34be9 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -131,9 +131,9 @@ pub enum SwapMsg { Negotiation(NegotiationDataMsg), NegotiationReply(NegotiationDataMsg), Negotiated(bool), - TakerFee(Vec), + TakerFee(PaymentDataMsg), MakerPayment(PaymentDataMsg), - TakerPayment(PaymentDataMsg), + TakerPayment(Vec), } #[derive(Debug, Default)] @@ -141,9 +141,9 @@ pub struct SwapMsgStore { negotiation: Option, negotiation_reply: Option, negotiated: Option, - taker_fee: Option>, + taker_fee: Option, maker_payment: Option, - taker_payment: Option, + taker_payment: Option>, accept_only_from: bits256, } @@ -231,9 +231,9 @@ pub async fn process_msg(ctx: MmArc, topic: &str, msg: &[u8]) { SwapMsg::Negotiation(data) => msg_store.negotiation = Some(data), SwapMsg::NegotiationReply(data) => msg_store.negotiation_reply = Some(data), SwapMsg::Negotiated(negotiated) => msg_store.negotiated = Some(negotiated), - SwapMsg::TakerFee(taker_fee) => msg_store.taker_fee = Some(taker_fee), + SwapMsg::TakerFee(data) => msg_store.taker_fee = Some(data), SwapMsg::MakerPayment(data) => msg_store.maker_payment = Some(data), - SwapMsg::TakerPayment(data) => msg_store.taker_payment = Some(data), + SwapMsg::TakerPayment(taker_payment) => msg_store.taker_payment = Some(taker_payment), } } else { warn!("Received message from unexpected sender for swap {}", uuid); diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 86b1c43153..2e3ae5cef3 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -635,7 +635,8 @@ impl MakerSwap { }, }; drop(send_abort_handle); - let taker_fee = match self.taker_coin.tx_enum_from_bytes(&payload) { + // Todo: validate invoice here, or after? should I revalidate on restart? + let taker_fee = match self.taker_coin.tx_enum_from_bytes(payload.payment_data()) { Ok(tx) => tx, Err(e) => { return Ok((Some(MakerSwapCommand::Finish), vec![ @@ -702,6 +703,7 @@ impl MakerSwap { let secret_hash = self.secret_hash(); let unique_data = self.unique_swap_data(); + // Todo: For lightning do this check if payment is sent/failed/pending??? what to do in each case?, should I test payments/events across restarts??? let transaction_f = self .maker_coin .check_if_my_payment_sent( @@ -718,6 +720,7 @@ impl MakerSwap { Ok(res) => match res { Some(tx) => tx, None => { + // Todo: For lightning this pays the invoice let payment_fut = self.maker_coin.send_maker_payment( self.r().data.maker_payment_lock as u32, &*self.r().other_maker_coin_htlc_pub, @@ -815,7 +818,7 @@ impl MakerSwap { }; drop(abort_send_handle); - let taker_payment = match self.taker_coin.tx_enum_from_bytes(payload.payment_data()) { + let taker_payment = match self.taker_coin.tx_enum_from_bytes(&payload) { Ok(tx) => tx, Err(err) => { return Ok((Some(MakerSwapCommand::RefundMakerPayment), vec![ diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index a399ba3602..1c6acc9e4b 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1102,8 +1102,15 @@ impl TakerSwap { async fn wait_for_maker_payment(&self) -> Result<(Option, Vec), String> { const MAKER_PAYMENT_WAIT_TIMEOUT: u64 = 600; - let tx_hex = self.r().taker_fee.as_ref().unwrap().tx_hex.0.clone(); - let msg = SwapMsg::TakerFee(tx_hex); + let payment_data_msg = match self.get_my_payment_data().await { + Ok(data) => data, + Err(e) => { + return Ok((Some(TakerSwapCommand::Finish), vec![ + TakerSwapEvent::TakerFeeSendFailed(e.to_string().into()), + ])) + }, + }; + let msg = SwapMsg::TakerFee(payment_data_msg); let abort_send_handle = broadcast_swap_message_every( self.ctx.clone(), swap_topic(&self.uuid), @@ -1154,6 +1161,7 @@ impl TakerSwap { } async fn validate_maker_payment(&self) -> Result<(Option, Vec), String> { + // Todo: validate invoice here, or before? should I revalidate on restart? it's ok to do so info!("Before wait confirm"); let confirmations = self.r().data.maker_payment_confirmations; let f = self.maker_coin.wait_for_confirmations( @@ -1213,6 +1221,7 @@ impl TakerSwap { } let unique_data = self.unique_swap_data(); + // Todo: For lightning do this check if payment is sent/failed/pending??? what to do in each case?, should I test payments/events across restarts??? let f = self.taker_coin.check_if_my_payment_sent( self.r().data.taker_payment_lock as u32, self.r().other_taker_coin_htlc_pub.as_slice(), @@ -1225,6 +1234,7 @@ impl TakerSwap { Ok(res) => match res { Some(tx) => tx, None => { + // Todo: For lightning this pays the invoice let payment_fut = self.taker_coin.send_taker_payment( self.r().data.taker_payment_lock as u32, &*self.r().other_taker_coin_htlc_pub, @@ -1266,18 +1276,8 @@ impl TakerSwap { } async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { - let payment_data_msg = match self.get_my_payment_data().await { - Ok(data) => data, - Err(e) => { - return Ok((Some(TakerSwapCommand::RefundTakerPayment), vec![ - TakerSwapEvent::TakerPaymentDataSendFailed(e.to_string().into()), - TakerSwapEvent::TakerPaymentWaitRefundStarted { - wait_until: self.wait_refund_until(), - }, - ])) - }, - }; - let msg = SwapMsg::TakerPayment(payment_data_msg); + let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); + let msg = SwapMsg::TakerPayment(tx_hex); let send_abort_handle = broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); From a436358f1df314ec41495e2c1dee3e9420584562 Mon Sep 17 00:00:00 2001 From: shamardy Date: Wed, 28 Sep 2022 23:02:21 +0200 Subject: [PATCH 15/36] Validate invoice received from swap messages wip --- mm2src/coins/eth.rs | 9 ++-- mm2src/coins/lightning.rs | 26 +++++++---- mm2src/coins/lp_coins.rs | 23 +++++++--- mm2src/coins/qrc20.rs | 12 ++--- .../rpc_command/lightning/generate_invoice.rs | 1 + mm2src/coins/solana.rs | 14 +++--- mm2src/coins/solana/spl.rs | 16 ++++--- mm2src/coins/tendermint/tendermint_coin.rs | 12 ++--- mm2src/coins/test_coin.rs | 13 ++++-- mm2src/coins/utxo/bch.rs | 11 +++-- mm2src/coins/utxo/qtum.rs | 12 ++--- mm2src/coins/utxo/slp.rs | 19 ++++---- mm2src/coins/utxo/utxo_standard.rs | 11 +++-- mm2src/coins/z_coin.rs | 12 ++--- mm2src/mm2_main/src/lp_swap.rs | 21 +++++---- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 15 ++++--- .../src/lp_swap/recreate_swap_data.rs | 4 ++ mm2src/mm2_main/src/lp_swap/saved_swap.rs | 1 + mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 1 + mm2src/mm2_main/src/lp_swap/taker_swap.rs | 45 +++++++++++++------ 20 files changed, 180 insertions(+), 98 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 3caf116979..85e3361f79 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -61,7 +61,7 @@ use web3_transport::{EthFeeHistoryNamespace, Web3Transport, Web3TransportNode}; use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CoinBalance, CoinProtocol, CoinTransportMetrics, CoinsContext, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, NumConversError, NumConversResult, OtherInstructionsErr, + NegotiateSwapContractAddrErr, NumConversError, NumConversResult, PaymentInstructionsErr, RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RawTransactionResult, RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, @@ -70,6 +70,7 @@ use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CoinBalance, CoinPr VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; +use crate::ValidateInstructionsErr; pub use rlp; #[cfg(test)] mod eth_tests; @@ -1135,13 +1136,15 @@ impl SwapOps for EthCoin { key_pair_from_secret(self.key_pair.secret()).expect("valid key") } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } #[cfg_attr(test, mockable)] diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index fa41a9090c..e9a2576d83 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -16,12 +16,12 @@ use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, OtherInstructionsErr, RawTransactionFut, RawTransactionRequest, + NegotiateSwapContractAddrErr, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionEnum, TransactionFut, TxMarshalingErr, - UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidatePaymentInput, - VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, - WithdrawRequest}; + UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateInstructionsErr, + ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, + WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -378,6 +378,7 @@ impl LightningCoin { // supply. let payment_secret = self .channel_manager + // Todo: review secret creation process .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs) .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; let our_node_pubkey = self.channel_manager.get_our_node_id(); @@ -390,6 +391,7 @@ impl LightningCoin { .payment_secret(payment_secret) .basic_mpp() .min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into()) + // Todo: this should be the locktime probably and it should be validated by the other side, what about min_final_cltv_expiry?? .expiry_time(core::time::Duration::from_secs(invoice_expiry_delta_secs.into())); if let Some(amt) = amt_msat { invoice = invoice.amount_milli_satoshis(amt); @@ -578,11 +580,11 @@ impl SwapOps for LightningCoin { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } - async fn other_side_instructions( + async fn payment_instructions( &self, secret_hash: &[u8], other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { // lightning decimals should be 11 in config since the smallest divisible unit in lightning coin is msat let amt_msat = sat_from_big_decimal(other_side_amount, self.decimals())?; @@ -601,8 +603,16 @@ impl SwapOps for LightningCoin { DEFAULT_INVOICE_EXPIRY, ) .await - .map_to_mm(|e| OtherInstructionsErr::LightningInvoiceErr(e.to_string()))?; - Ok(Some(invoice.to_string().into_bytes())) + .map_to_mm(|e| PaymentInstructionsErr::LightningInvoiceErr(e.to_string()))?; + Ok(Some(hex::decode(invoice.to_string()).map_to_mm(|e| { + PaymentInstructionsErr::LightningInvoiceErr(e.to_string()) + })?)) + } + + fn validate_instructions(&self, instructions: &[u8]) -> Result<(), MmError> { + let _invoice = Invoice::from_str(&hex::encode(instructions))?; + // Todo: continue validation here by comparing (payment_hash of invoice with secret_hash, invoice_amount with maker/taker amount, locktime, etc..) + Ok(()) } } diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 81e79cbc17..859d132f7a 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -69,6 +69,7 @@ cfg_native! { use crate::lightning::ln_conf::PlatformCoinConfirmationTargets; use async_std::fs; use futures::AsyncWriteExt; + use lightning_invoice::ParseOrSemanticError; use std::io; use zcash_primitives::transaction::Transaction as ZTransaction; use z_coin::ZcoinProtocolInfo; @@ -470,13 +471,23 @@ pub struct SearchForSwapTxSpendInput<'a> { } #[derive(Display)] -pub enum OtherInstructionsErr { +pub enum PaymentInstructionsErr { LightningInvoiceErr(String), InternalError(String), } -impl From for OtherInstructionsErr { - fn from(e: NumConversError) -> Self { OtherInstructionsErr::InternalError(e.to_string()) } +impl From for PaymentInstructionsErr { + fn from(e: NumConversError) -> Self { PaymentInstructionsErr::InternalError(e.to_string()) } +} + +#[derive(Display)] +pub enum ValidateInstructionsErr { + ValidateLightningInvoiceErr(String), +} + +#[cfg(not(target_arch = "wasm32"))] +impl From for ValidateInstructionsErr { + fn from(e: ParseOrSemanticError) -> Self { ValidateInstructionsErr::ValidateLightningInvoiceErr(e.to_string()) } } /// Swap operations (mostly based on the Hash/Time locked transactions implemented by coin wallets). @@ -616,11 +627,13 @@ pub trait SwapOps { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair; - async fn other_side_instructions( + async fn payment_instructions( &self, secret_hash: &[u8], other_side_amount: &BigDecimal, - ) -> Result>, MmError>; + ) -> Result>, MmError>; + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError>; } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 2bfa33dc45..09d84e14d9 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -14,12 +14,12 @@ use crate::utxo::{qtum, ActualTxFee, AdditionalTxData, AddrFromStrError, Broadca UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK}; use crate::{BalanceError, BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, - MmCoin, NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyNotAllowed, RawTransactionFut, + MmCoin, NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyNotAllowed, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, - WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, + WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -1025,13 +1025,15 @@ impl SwapOps for Qrc20Coin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } impl MarketCoinOps for Qrc20Coin { diff --git a/mm2src/coins/rpc_command/lightning/generate_invoice.rs b/mm2src/coins/rpc_command/lightning/generate_invoice.rs index 90d5d6a5d7..68bb18ade6 100644 --- a/mm2src/coins/rpc_command/lightning/generate_invoice.rs +++ b/mm2src/coins/rpc_command/lightning/generate_invoice.rs @@ -108,6 +108,7 @@ pub async fn generate_invoice( }) .await?; + // Todo: Should remove adding payment to db step since the preimage can be recreated from the keymanager and the invoice secret (Do I need to check that received amount equals the requested amount?) let payment_hash = invoice.payment_hash().into_inner(); let payment_info = DBPaymentInfo { payment_hash: PaymentHash(payment_hash), diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 6f1dc3c499..2f68596858 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -2,11 +2,11 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, Trade use crate::solana::solana_common::{lamports_to_sol, PrepareTransferData, SufficientBalanceError}; use crate::solana::spl::SplTokenInfo; use crate::{BalanceError, BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, - OtherInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, + PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, - WithdrawRequest, WithdrawResult}; + ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, + WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -605,11 +605,15 @@ impl SwapOps for SolanaCoin { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { todo!() } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { + unimplemented!() + } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { unimplemented!() } } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index b3f65848c3..59669c68f6 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -1,12 +1,12 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, TradeFee, TransactionEnum}; use crate::solana::solana_common::{ui_amount_to_amount, PrepareTransferData, SufficientBalanceError}; use crate::solana::{solana_common, AccountError, SolanaCommonOps, SolanaFeeDetails}; -use crate::{BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, OtherInstructionsErr, +use crate::{BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SolanaCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, - TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, - VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest, - WithdrawResult}; + TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, + WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bincode::serialize; use common::{async_blocking, now_ms}; @@ -439,11 +439,15 @@ impl SwapOps for SplToken { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { todo!() } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { + unimplemented!() + } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { unimplemented!() } } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 79fd980567..4acee1df49 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -6,11 +6,11 @@ use crate::tendermint::htlc::MsgClaimHtlc; use crate::utxo::sat_from_big_decimal; use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, - OtherInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, + PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, VerificationResult, - WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest}; + UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, + VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcrypto::sha256; use common::{get_utc_timestamp, Future01CompatExt}; @@ -774,13 +774,15 @@ impl SwapOps for TendermintCoin { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { todo!() } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } #[cfg(test)] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 8f801c8431..e4ed3b1735 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -1,9 +1,10 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, SwapOps, TradeFee, TransactionEnum, TransactionFut}; use crate::{BalanceFut, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, - OtherInstructionsErr, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, + PaymentInstructionsErr, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, TradePreimageValue, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawRequest}; + ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, + WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use futures01::Future; use keys::KeyPair; @@ -260,11 +261,15 @@ impl SwapOps for TestCoin { unimplemented!() } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { + unimplemented!() + } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { unimplemented!() } } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 63da5d9591..a93484ffc9 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -9,10 +9,11 @@ use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{BlockHeightAndTime, CanRefundHtlc, CoinBalance, CoinProtocol, CoinWithDerivationMethod, - NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyBuildPolicy, RawTransactionFut, + NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradePreimageValue, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawFut}; + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, + WatcherValidatePaymentInput, WithdrawFut}; use common::log::warn; use derive_more::Display; use futures::{FutureExt, TryFutureExt}; @@ -1039,13 +1040,15 @@ impl SwapOps for BchCoin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } fn total_unspent_value<'a>(unspents: impl IntoIterator) -> u64 { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 154a85ceee..487ad9f117 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -22,10 +22,10 @@ use crate::utxo::utxo_builder::{BlockHeaderUtxoArcOps, MergeUtxoArcOps, UtxoCoin use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{eth, CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, DelegationError, DelegationFut, - GetWithdrawSenderAddress, NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyBuildPolicy, + GetWithdrawSenderAddress, NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, SignatureResult, StakingInfosFut, SwapOps, TradePreimageValue, TransactionFut, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentInput, - VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawSenderAddress}; + TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, + ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawSenderAddress}; use crypto::Bip44Chain; use ethereum_types::H160; use futures::{FutureExt, TryFutureExt}; @@ -737,13 +737,15 @@ impl SwapOps for QtumCoin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } impl MarketCoinOps for QtumCoin { diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 5f819bea09..ddb84534ed 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -13,12 +13,13 @@ use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualTxFee, Addit FeePolicy, GenerateTxError, RecentlySpentOutPointsGuard, UtxoCoinConf, UtxoCoinFields, UtxoCommonOps, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, NumConversError, OtherInstructionsErr, PrivKeyNotAllowed, RawTransactionFut, - RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageError, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, - TransactionErr, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidatePaymentInput, VerificationError, VerificationResult, - WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; + NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionsErr, PrivKeyNotAllowed, + RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, + TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, + TransactionEnum, TransactionErr, TransactionFut, TxFeeDetails, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, + VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, + WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcrypto::dhash160; use chain::constants::SEQUENCE_FINAL; @@ -1463,13 +1464,15 @@ impl SwapOps for SlpToken { utxo_common::derive_htlc_key_pair(self.platform_coin.as_ref(), swap_unique_data) } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } impl From for TradePreimageError { diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index f29234c7b3..cd28ca8f56 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -20,9 +20,10 @@ use crate::utxo::utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, GetWithdrawSenderAddress, - NegotiateSwapContractAddrErr, OtherInstructionsErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, + NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradePreimageValue, TransactionFut, TxMarshalingErr, ValidateAddressResult, - ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawSenderAddress}; + ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, + WithdrawFut, WithdrawSenderAddress}; use crypto::Bip44Chain; use futures::{FutureExt, TryFutureExt}; use mm2_metrics::MetricsArc; @@ -498,13 +499,15 @@ impl SwapOps for UtxoStandardCoin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } impl MarketCoinOps for UtxoStandardCoin { diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index a2bad32245..1a95df063f 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -12,12 +12,12 @@ use crate::utxo::{sat_from_big_decimal, utxo_common, ActualTxFee, AdditionalTxDa UtxoCommonOps, UtxoFeeDetails, UtxoRpcMode, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom}; use crate::{BalanceError, BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, - MmCoin, NegotiateSwapContractAddrErr, NumConversError, OtherInstructionsErr, PrivKeyActivationPolicy, + MmCoin, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionsErr, PrivKeyActivationPolicy, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidatePaymentInput, VerificationError, VerificationResult, - WatcherValidatePaymentInput, WithdrawFut, WithdrawRequest}; + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, VerificationError, + VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawRequest}; use crate::{Transaction, WithdrawError}; use async_trait::async_trait; use bitcrypto::{dhash160, dhash256}; @@ -1395,13 +1395,15 @@ impl SwapOps for ZCoin { key_pair_from_secret(key.as_slice()).expect("valid privkey") } - async fn other_side_instructions( + async fn payment_instructions( &self, _secret_hash: &[u8], _other_side_amount: &BigDecimal, - ) -> Result>, MmError> { + ) -> Result>, MmError> { Ok(None) } + + fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } } #[async_trait] diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 5d6facf380..af07ff3470 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -698,10 +698,10 @@ impl NegotiationDataMsg { #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] pub struct PaymentDataV2 { - payment_data: Vec, + data: Vec, // Instructions for the other side whether taker or maker on how to make it's payment. // An example for this is a maker/taker sending the taker/maker a lightning invoice to be payed. - other_side_instructions: Vec, + instructions: Vec, } #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] @@ -713,19 +713,18 @@ pub enum PaymentDataMsg { impl PaymentDataMsg { #[inline] - pub fn payment_data(&self) -> &[u8] { + pub fn data(&self) -> &[u8] { match self { PaymentDataMsg::V1(v1) => v1, - PaymentDataMsg::V2(v2) => &v2.payment_data, + PaymentDataMsg::V2(v2) => &v2.data, } } - #[allow(dead_code)] #[inline] - pub fn other_side_instructions(&self) -> Option<&[u8]> { + pub fn instructions(&self) -> Option<&[u8]> { match self { PaymentDataMsg::V1(_) => None, - PaymentDataMsg::V2(v2) => Some(&v2.other_side_instructions), + PaymentDataMsg::V2(v2) => Some(&v2.instructions), } } } @@ -1645,8 +1644,8 @@ mod lp_swap_tests { // PaymentDataMsg::V2 should be deserialized to PaymentDataMsg::V2 let v2 = SwapMsg::MakerPayment(PaymentDataMsg::V2(PaymentDataV2 { - payment_data: vec![1; 300], - other_side_instructions: vec![1; 300], + data: vec![1; 300], + instructions: vec![1; 300], })); let serialized = rmp_serde::to_vec(&v2).unwrap(); @@ -1657,8 +1656,8 @@ mod lp_swap_tests { // PaymentDataMsg::V2 shouldn't be deserialized to old message format, new nodes with payment instructions can't swap with old nodes without it. let v2 = SwapMsg::MakerPayment(PaymentDataMsg::V2(PaymentDataV2 { - payment_data: vec![1; 300], - other_side_instructions: vec![1; 300], + data: vec![1; 300], + instructions: vec![1; 300], })); let serialized = rmp_serde::to_vec(&v2).unwrap(); diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 2e3ae5cef3..faa1a1522c 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -14,7 +14,7 @@ use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::MM_VERSION; -use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, OtherInstructionsErr, +use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionsErr, SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, TransactionEnum, ValidatePaymentInput}; use common::log::{debug, error, info, warn}; use common::{bits256, executor::Timer, now_ms, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -404,16 +404,16 @@ impl MakerSwap { } } - async fn get_my_payment_data(&self) -> Result> { + async fn get_my_payment_data(&self) -> Result> { let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); - if let Some(other_side_instructions) = self + if let Some(instructions) = self .taker_coin - .other_side_instructions(&self.secret_hash(), &self.taker_amount) + .payment_instructions(&self.secret_hash(), &self.taker_amount) .await? { Ok(PaymentDataMsg::V2(PaymentDataV2 { - payment_data, - other_side_instructions, + data: payment_data, + instructions, })) } else { Ok(PaymentDataMsg::V1(payment_data)) @@ -636,7 +636,7 @@ impl MakerSwap { }; drop(send_abort_handle); // Todo: validate invoice here, or after? should I revalidate on restart? - let taker_fee = match self.taker_coin.tx_enum_from_bytes(payload.payment_data()) { + let taker_fee = match self.taker_coin.tx_enum_from_bytes(payload.data()) { Ok(tx) => tx, Err(e) => { return Ok((Some(MakerSwapCommand::Finish), vec![ @@ -1386,6 +1386,7 @@ pub enum MakerSwapCommand { Finish, } +// Todo: mirror what's implemented in taker swap #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "type", content = "data")] #[allow(clippy::large_enum_variant)] 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 eb71dccebe..b8eec66f8f 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -212,6 +212,7 @@ fn convert_taker_to_maker_events( // Finish processing Taker events. return events; }, + // Todo: does the taker need to retrieve the invoice from here?? also look in maker_swap.rs for mirror TakerSwapEvent::MakerPaymentReceived(tx_ident) => { if let Some(taker_fee_ident) = taker_fee_ident.take() { push_event!(MakerSwapEvent::TakerFeeValidated(taker_fee_ident)); @@ -260,6 +261,8 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::StartFailed(_) | TakerSwapEvent::Negotiated(_) | TakerSwapEvent::NegotiateFailed(_) + // Todo: add a note here to describe why this is not needed for lightning and that it might be needed if instructions is used later depending on the situation + | TakerSwapEvent::TakerPaymentInstructionsReceived(_) | TakerSwapEvent::MakerPaymentWaitConfirmStarted | TakerSwapEvent::MakerPaymentValidatedAndConfirmed | TakerSwapEvent::MakerPaymentSpent(_) @@ -422,6 +425,7 @@ fn convert_maker_to_taker_events( return events; }, MakerSwapEvent::MakerPaymentSent(tx_ident) => { + // Todo: check this if a new event is added for other side instructions, also look in the mirror maker_swap.rs push_event!(TakerSwapEvent::MakerPaymentReceived(tx_ident)); // Please note we have not to push `MakerPaymentValidatedAndConfirmed` since we could actually decline it. push_event!(TakerSwapEvent::MakerPaymentWaitConfirmStarted); diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index a4afb8dafe..c8e4cd13ef 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -26,6 +26,7 @@ pub enum SavedSwapError { InternalError(String), } +// Todo: check backward compatibility for loading swaps from files, etc.. #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum SavedSwap { diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 1d7325c92b..e4e95b895c 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -39,6 +39,7 @@ pub struct WatcherMut { secret: H256Json, } +// Todo: how to use payment instructions with watchers?? is it even needed?? what about lightning payments?? (it's instant to pay for the taker) #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct TakerSwapWatcherData { pub uuid: Uuid, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 6496551948..c799b6e0c8 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -15,7 +15,7 @@ use crate::mm2::lp_ordermatch::{MatchBy, OrderConfirmationsSettings, TakerAction use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::lp_swap::{broadcast_p2p_tx_msg, tx_helper_topic, TakerSwapWatcherData}; use crate::mm2::MM_VERSION; -use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, OtherInstructionsErr, +use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionsErr, SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, ValidatePaymentInput}; use common::executor::Timer; use common::log::{debug, error, info, warn}; @@ -141,6 +141,7 @@ impl TakerSavedEvent { TakerSwapEvent::NegotiateFailed(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerFeeSent(_) => Some(TakerSwapCommand::WaitForMakerPayment), TakerSwapEvent::TakerFeeSendFailed(_) => Some(TakerSwapCommand::Finish), + TakerSwapEvent::TakerPaymentInstructionsReceived(_) => Some(TakerSwapCommand::ValidateMakerPayment), TakerSwapEvent::MakerPaymentReceived(_) => Some(TakerSwapCommand::ValidateMakerPayment), TakerSwapEvent::MakerPaymentWaitConfirmStarted => Some(TakerSwapCommand::ValidateMakerPayment), TakerSwapEvent::MakerPaymentValidatedAndConfirmed => Some(TakerSwapCommand::SendTakerPayment), @@ -491,6 +492,7 @@ pub struct TakerSwapMut { taker_payment_refund: Option, secret_hash: BytesJson, secret: H256Json, + payment_instructions: Option>, } #[cfg(test)] @@ -568,6 +570,7 @@ pub enum TakerSwapEvent { NegotiateFailed(SwapError), TakerFeeSent(TransactionIdentifier), TakerFeeSendFailed(SwapError), + TakerPaymentInstructionsReceived(Vec), MakerPaymentReceived(TransactionIdentifier), MakerPaymentWaitConfirmStarted, MakerPaymentValidatedAndConfirmed, @@ -597,6 +600,7 @@ impl TakerSwapEvent { TakerSwapEvent::NegotiateFailed(_) => "Negotiate failed...".to_owned(), TakerSwapEvent::TakerFeeSent(_) => "Taker fee sent...".to_owned(), TakerSwapEvent::TakerFeeSendFailed(_) => "Taker fee send failed...".to_owned(), + TakerSwapEvent::TakerPaymentInstructionsReceived(_) => "Taker payment instructions received...".to_owned(), TakerSwapEvent::MakerPaymentReceived(_) => "Maker payment received...".to_owned(), TakerSwapEvent::MakerPaymentWaitConfirmStarted => "Maker payment wait confirm started...".to_owned(), TakerSwapEvent::MakerPaymentValidatedAndConfirmed => "Maker payment validated and confirmed...".to_owned(), @@ -715,6 +719,9 @@ impl TakerSwap { TakerSwapEvent::NegotiateFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerFeeSent(tx) => self.w().taker_fee = Some(tx), TakerSwapEvent::TakerFeeSendFailed(err) => self.errors.lock().push(err), + TakerSwapEvent::TakerPaymentInstructionsReceived(instructions) => { + self.w().payment_instructions = Some(instructions) + }, TakerSwapEvent::MakerPaymentReceived(tx) => self.w().maker_payment = Some(tx), TakerSwapEvent::MakerPaymentWaitConfirmStarted => (), TakerSwapEvent::MakerPaymentValidatedAndConfirmed => { @@ -805,6 +812,7 @@ impl TakerSwap { taker_payment_refund: None, secret_hash: BytesJson::default(), secret: H256Json::default(), + payment_instructions: None, }), ctx, #[cfg(test)] @@ -841,18 +849,18 @@ impl TakerSwap { } } - async fn get_my_payment_data(&self) -> Result> { + async fn get_my_payment_data(&self) -> Result> { let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex.0.clone(); let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); - if let Some(other_side_instructions) = self + if let Some(instructions) = self .maker_coin - .other_side_instructions(&secret_hash, &maker_amount) + .payment_instructions(&secret_hash, &maker_amount) .await? { Ok(PaymentDataMsg::V2(PaymentDataV2 { - payment_data, - other_side_instructions, + data: payment_data, + instructions, })) } else { Ok(PaymentDataMsg::V1(payment_data)) @@ -1133,14 +1141,16 @@ impl TakerSwap { async fn wait_for_maker_payment(&self) -> Result<(Option, Vec), String> { const MAKER_PAYMENT_WAIT_TIMEOUT: u64 = 600; + let payment_data_msg = match self.get_my_payment_data().await { Ok(data) => data, Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ - TakerSwapEvent::TakerFeeSendFailed(e.to_string().into()), + TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), ])) }, }; + let msg = SwapMsg::TakerFee(payment_data_msg); let abort_send_handle = broadcast_swap_message_every( self.ctx.clone(), @@ -1167,7 +1177,7 @@ impl TakerSwap { }, }; drop(abort_send_handle); - let maker_payment = match self.maker_coin.tx_enum_from_bytes(payload.payment_data()) { + let maker_payment = match self.maker_coin.tx_enum_from_bytes(payload.data()) { Ok(p) => p, Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ @@ -1185,14 +1195,23 @@ impl TakerSwap { tx_hash, }; - Ok((Some(TakerSwapCommand::ValidateMakerPayment), vec![ - TakerSwapEvent::MakerPaymentReceived(tx_ident), - TakerSwapEvent::MakerPaymentWaitConfirmStarted, - ])) + let mut swap_events = vec![]; + if let Some(instructions) = payload.instructions() { + if let Err(e) = self.taker_coin.validate_instructions(instructions) { + return Ok((Some(TakerSwapCommand::Finish), vec![ + // Todo: maybe add a different event for this?? + TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), + ])); + } + swap_events.push(TakerSwapEvent::TakerPaymentInstructionsReceived(instructions.to_vec())); + } + swap_events.push(TakerSwapEvent::MakerPaymentReceived(tx_ident)); + swap_events.push(TakerSwapEvent::MakerPaymentWaitConfirmStarted); + + Ok((Some(TakerSwapCommand::ValidateMakerPayment), swap_events)) } async fn validate_maker_payment(&self) -> Result<(Option, Vec), String> { - // Todo: validate invoice here, or before? should I revalidate on restart? it's ok to do so info!("Before wait confirm"); let confirmations = self.r().data.maker_payment_confirmations; let f = self.maker_coin.wait_for_confirmations( From a7af8c4c77cb713ddc04ce9997347beb78588101 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 29 Sep 2022 18:54:58 +0200 Subject: [PATCH 16/36] Validate invoice on maker side if maker is lightning coin --- mm2src/coins/eth.rs | 11 +++++- mm2src/coins/lightning.rs | 38 ++++++++++++++----- mm2src/coins/lightning/ln_utils.rs | 1 + mm2src/coins/lp_coins.rs | 9 ++++- mm2src/coins/qrc20.rs | 11 +++++- mm2src/coins/solana.rs | 9 ++++- mm2src/coins/solana/spl.rs | 9 ++++- mm2src/coins/tendermint/tendermint_coin.rs | 11 +++++- mm2src/coins/test_coin.rs | 9 ++++- mm2src/coins/utxo/bch.rs | 11 +++++- mm2src/coins/utxo/qtum.rs | 11 +++++- mm2src/coins/utxo/slp.rs | 11 +++++- mm2src/coins/utxo/utxo_standard.rs | 11 +++++- mm2src/coins/z_coin.rs | 11 +++++- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 28 ++++++++++++-- .../src/lp_swap/recreate_swap_data.rs | 2 + mm2src/mm2_main/src/lp_swap/taker_swap.rs | 6 ++- 17 files changed, 161 insertions(+), 38 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 85e3361f79..c8462720c4 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1139,12 +1139,19 @@ impl SwapOps for EthCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } #[cfg_attr(test, mockable)] diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index e9a2576d83..ccd9fd845e 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -13,7 +13,7 @@ mod ln_utils; use super::DerivationMethod; use crate::lightning::ln_utils::filter_channels; use crate::utxo::rpc_clients::UtxoRpcClientEnum; -use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; +use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsigned}; use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, @@ -26,8 +26,8 @@ use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; use bitcoin_hashes::sha256::Hash as Sha256; -use bitcrypto::dhash256; use bitcrypto::ChecksumType; +use bitcrypto::{dhash256, ripemd160}; use common::executor::spawn; use common::log::{LogOnError, LogState}; use common::{async_blocking, log, now_ms, PagingOptionsEnum}; @@ -583,13 +583,15 @@ impl SwapOps for LightningCoin { async fn payment_instructions( &self, secret_hash: &[u8], - other_side_amount: &BigDecimal, + amount: &BigDecimal, ) -> Result>, MmError> { // lightning decimals should be 11 in config since the smallest divisible unit in lightning coin is msat - let amt_msat = sat_from_big_decimal(other_side_amount, self.decimals())?; + let amt_msat = sat_from_big_decimal(amount, self.decimals())?; - if secret_hash.len() != 32 { - // return error here + let secret_hash_length = secret_hash.len(); + if secret_hash_length != 32 { + let error = format!("Invalid secret_hash length {}", secret_hash_length); + return Err(PaymentInstructionsErr::InternalError(error).into()); } let mut payment_hash = [b' '; 32]; payment_hash.copy_from_slice(secret_hash); @@ -609,9 +611,27 @@ impl SwapOps for LightningCoin { })?)) } - fn validate_instructions(&self, instructions: &[u8]) -> Result<(), MmError> { - let _invoice = Invoice::from_str(&hex::encode(instructions))?; - // Todo: continue validation here by comparing (payment_hash of invoice with secret_hash, invoice_amount with maker/taker amount, locktime, etc..) + fn validate_instructions( + &self, + instructions: &[u8], + secret_hash: &[u8], + amount: BigDecimal, + ) -> Result<(), MmError> { + let invoice = Invoice::from_str(&hex::encode(instructions))?; + if (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) + || (secret_hash.len() == 32 && invoice.payment_hash().as_inner() != secret_hash) + { + return Err( + ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice payment hash!".into()).into(), + ); + } + let invoice_amount = invoice + .amount_milli_satoshis() + .ok_or_else(|| ValidateInstructionsErr::ValidateLightningInvoiceErr("No invoice amount!".into()))?; + if big_decimal_from_sat(invoice_amount as i64, self.decimals()) != amount { + return Err(ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice amount!".into()).into()); + } + // Todo: continue validation here by comparing (locktime, etc..) Ok(()) } } diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index eef2a81de7..b3038a5550 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -268,6 +268,7 @@ pub(crate) fn filter_channels(channels: Vec, min_inbound_capacit } // Todo: maybe check for inbound_capacity_msat first?? can this be used for probing?? I don't think so but how can it be avoided?? + // Todo: we need the side who is revealing the private channel to check that the other side has the balance on-chain and not someone trying to probe for private channels if channel.is_public { // If any public channel exists, return no hints and let the sender // look at the public channels instead. diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 859d132f7a..488a9a6d54 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -630,10 +630,15 @@ pub trait SwapOps { async fn payment_instructions( &self, secret_hash: &[u8], - other_side_amount: &BigDecimal, + amount: &BigDecimal, ) -> Result>, MmError>; - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError>; + fn validate_instructions( + &self, + instructions: &[u8], + secret_hash: &[u8], + amount: BigDecimal, + ) -> Result<(), MmError>; } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 09d84e14d9..4c92b1784f 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1028,12 +1028,19 @@ impl SwapOps for Qrc20Coin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } impl MarketCoinOps for Qrc20Coin { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 2f68596858..23ad6a4a71 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -608,12 +608,17 @@ impl SwapOps for SolanaCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { unimplemented!() } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { unimplemented!() } } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 59669c68f6..5adf31e526 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -442,12 +442,17 @@ impl SwapOps for SplToken { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { unimplemented!() } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { unimplemented!() } } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 4acee1df49..2035188222 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -777,12 +777,19 @@ impl SwapOps for TendermintCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } #[cfg(test)] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index e4ed3b1735..6533c45040 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -264,12 +264,17 @@ impl SwapOps for TestCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { unimplemented!() } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { unimplemented!() } } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index a93484ffc9..69523b5b2f 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1043,12 +1043,19 @@ impl SwapOps for BchCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } fn total_unspent_value<'a>(unspents: impl IntoIterator) -> u64 { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 487ad9f117..06fa130eed 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -740,12 +740,19 @@ impl SwapOps for QtumCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } impl MarketCoinOps for QtumCoin { diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index ddb84534ed..393f7d1b1e 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1467,12 +1467,19 @@ impl SwapOps for SlpToken { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } impl From for TradePreimageError { diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index cd28ca8f56..e4e477a577 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -502,12 +502,19 @@ impl SwapOps for UtxoStandardCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } impl MarketCoinOps for UtxoStandardCoin { diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 1a95df063f..fcd3d5df06 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1398,12 +1398,19 @@ impl SwapOps for ZCoin { async fn payment_instructions( &self, _secret_hash: &[u8], - _other_side_amount: &BigDecimal, + _amount: &BigDecimal, ) -> Result>, MmError> { Ok(None) } - fn validate_instructions(&self, _instructions: &[u8]) -> Result<(), MmError> { Ok(()) } + fn validate_instructions( + &self, + _instructions: &[u8], + _secret_hash: &[u8], + _amount: BigDecimal, + ) -> Result<(), MmError> { + Ok(()) + } } #[async_trait] diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index faa1a1522c..f4917b8a12 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -172,6 +172,7 @@ pub struct MakerSwapMut { taker_payment_spend: Option, taker_payment_spend_confirmed: bool, maker_payment_refund: Option, + payment_instructions: Option>, } #[cfg(test)] @@ -275,6 +276,9 @@ impl MakerSwap { } }, MakerSwapEvent::NegotiateFailed(err) => self.errors.lock().push(err), + MakerSwapEvent::MakerPaymentInstructionsReceived(instructions) => { + self.w().payment_instructions = Some(instructions) + }, MakerSwapEvent::TakerFeeValidated(tx) => self.w().taker_fee = Some(tx), MakerSwapEvent::TakerFeeValidateFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentSent(tx) => self.w().maker_payment = Some(tx), @@ -361,6 +365,7 @@ impl MakerSwap { taker_payment_spend: None, maker_payment_refund: None, taker_payment_spend_confirmed: false, + payment_instructions: None, }), ctx, secret, @@ -635,7 +640,20 @@ impl MakerSwap { }, }; drop(send_abort_handle); - // Todo: validate invoice here, or after? should I revalidate on restart? + let mut swap_events = vec![]; + if let Some(instructions) = payload.instructions() { + if let Err(e) = + self.maker_coin + .validate_instructions(instructions, &self.secret_hash(), self.maker_amount.clone()) + { + return Ok((Some(MakerSwapCommand::Finish), vec![ + // Todo: maybe add a different event for this?? + MakerSwapEvent::TakerFeeValidateFailed(e.to_string().into()), + ])); + } + swap_events.push(MakerSwapEvent::MakerPaymentInstructionsReceived(instructions.to_vec())); + } + let taker_fee = match self.taker_coin.tx_enum_from_bytes(payload.data()) { Ok(tx) => tx, Err(e) => { @@ -686,10 +704,9 @@ impl MakerSwap { tx_hex: taker_fee.tx_hex().into(), tx_hash: hash, }; + swap_events.push(MakerSwapEvent::TakerFeeValidated(fee_ident)); - Ok((Some(MakerSwapCommand::SendPayment), vec![ - MakerSwapEvent::TakerFeeValidated(fee_ident), - ])) + Ok((Some(MakerSwapCommand::SendPayment), swap_events)) } async fn maker_payment(&self) -> Result<(Option, Vec), String> { @@ -1395,6 +1412,7 @@ pub enum MakerSwapEvent { StartFailed(SwapError), Negotiated(TakerNegotiationData), NegotiateFailed(SwapError), + MakerPaymentInstructionsReceived(Vec), TakerFeeValidated(TransactionIdentifier), TakerFeeValidateFailed(SwapError), MakerPaymentSent(TransactionIdentifier), @@ -1424,6 +1442,7 @@ impl MakerSwapEvent { MakerSwapEvent::StartFailed(_) => "Start failed...".to_owned(), MakerSwapEvent::Negotiated(_) => "Negotiated...".to_owned(), MakerSwapEvent::NegotiateFailed(_) => "Negotiate failed...".to_owned(), + MakerSwapEvent::MakerPaymentInstructionsReceived(_) => "Maker payment instructions received...".to_owned(), MakerSwapEvent::TakerFeeValidated(_) => "Taker fee validated...".to_owned(), MakerSwapEvent::TakerFeeValidateFailed(_) => "Taker fee validate failed...".to_owned(), MakerSwapEvent::MakerPaymentSent(_) => "Maker payment sent...".to_owned(), @@ -1493,6 +1512,7 @@ impl MakerSavedEvent { MakerSwapEvent::Started(_) => Some(MakerSwapCommand::Negotiate), MakerSwapEvent::StartFailed(_) => Some(MakerSwapCommand::Finish), MakerSwapEvent::Negotiated(_) => Some(MakerSwapCommand::WaitForTakerFee), + MakerSwapEvent::MakerPaymentInstructionsReceived(_) => Some(MakerSwapCommand::WaitForTakerFee), MakerSwapEvent::NegotiateFailed(_) => Some(MakerSwapCommand::Finish), MakerSwapEvent::TakerFeeValidated(_) => Some(MakerSwapCommand::SendPayment), MakerSwapEvent::TakerFeeValidateFailed(_) => Some(MakerSwapCommand::Finish), 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 b8eec66f8f..75b71227b4 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -470,6 +470,8 @@ fn convert_maker_to_taker_events( | MakerSwapEvent::StartFailed(_) | MakerSwapEvent::Negotiated(_) | MakerSwapEvent::NegotiateFailed(_) + // Todo: add a note here to describe why this is not needed for lightning and that it might be needed if instructions is used later depending on the situation + | MakerSwapEvent::MakerPaymentInstructionsReceived(_) | MakerSwapEvent::TakerPaymentWaitConfirmStarted | MakerSwapEvent::TakerPaymentValidatedAndConfirmed | MakerSwapEvent::TakerPaymentSpendConfirmStarted diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index c799b6e0c8..a573c238ce 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1197,7 +1197,11 @@ impl TakerSwap { let mut swap_events = vec![]; if let Some(instructions) = payload.instructions() { - if let Err(e) = self.taker_coin.validate_instructions(instructions) { + if let Err(e) = self.taker_coin.validate_instructions( + instructions, + &self.r().secret_hash.0, + self.taker_amount.clone().into(), + ) { return Ok((Some(TakerSwapCommand::Finish), vec![ // Todo: maybe add a different event for this?? TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), From 9e67c4153cf8c7036b02a6e07b5951748409db18 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 29 Sep 2022 21:32:12 +0200 Subject: [PATCH 17/36] add optional PaymentInstructions parameter to send_maker_payment, send_taker_payment functions --- mm2src/coins/eth.rs | 15 +++++---- mm2src/coins/eth/eth_tests.rs | 2 ++ mm2src/coins/eth/eth_wasm_tests.rs | 1 + mm2src/coins/lightning.rs | 16 ++++++---- mm2src/coins/lp_coins.rs | 16 ++++++++-- mm2src/coins/qrc20.rs | 20 ++++++------ mm2src/coins/solana.rs | 15 +++++---- mm2src/coins/solana/spl.rs | 17 +++++----- mm2src/coins/tendermint/tendermint_coin.rs | 18 ++++++----- mm2src/coins/test_coin.rs | 12 ++++--- mm2src/coins/utxo/bch.rs | 16 +++++----- mm2src/coins/utxo/qtum.rs | 16 +++++----- mm2src/coins/utxo/slp.rs | 14 +++++---- mm2src/coins/utxo/utxo_standard.rs | 14 +++++---- mm2src/coins/z_coin.rs | 19 +++++++----- mm2src/coins/z_coin/z_coin_native_tests.rs | 2 ++ mm2src/mm2_main/src/docker_tests.rs | 25 ++++++++++++--- .../mm2_main/src/docker_tests/qrc20_tests.rs | 14 +++++++-- mm2src/mm2_main/src/lp_swap.rs | 2 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 31 +++++++++++-------- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 27 +++++++++------- 21 files changed, 198 insertions(+), 114 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index b31814bb9f..76f989e207 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -66,11 +66,12 @@ use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CoinBalance, CoinPr RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr, - TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidatePaymentError, - ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, - WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidateInstructionsErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, + VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, + WithdrawFut, WithdrawRequest, WithdrawResult}; -use crate::ValidateInstructionsErr; +use crate::PaymentInstructions; pub use rlp; #[cfg(test)] mod eth_tests; @@ -732,6 +733,7 @@ impl SwapOps for EthCoin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let taker_addr = try_tx_fus!(addr_from_raw_pubkey(taker_pub)); let swap_contract_address = try_tx_fus!(swap_contract_address.try_to_address()); @@ -757,6 +759,7 @@ impl SwapOps for EthCoin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let maker_addr = try_tx_fus!(addr_from_raw_pubkey(maker_pub)); let swap_contract_address = try_tx_fus!(swap_contract_address.try_to_address()); @@ -1155,8 +1158,8 @@ impl SwapOps for EthCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 4cd66b4457..f5edfe2875 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -254,6 +254,7 @@ fn send_and_refund_erc20_payment() { "0.001".parse().unwrap(), &coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -322,6 +323,7 @@ fn send_and_refund_eth_payment() { "0.001".parse().unwrap(), &coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); diff --git a/mm2src/coins/eth/eth_wasm_tests.rs b/mm2src/coins/eth/eth_wasm_tests.rs index 55d02ce462..b40627a1b3 100644 --- a/mm2src/coins/eth/eth_wasm_tests.rs +++ b/mm2src/coins/eth/eth_wasm_tests.rs @@ -56,6 +56,7 @@ async fn test_send() { "0.001".parse().unwrap(), &None, &[], + &None, ) .compat() .await; diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 67d1666a4c..b927954b56 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -16,10 +16,10 @@ use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsigned}; use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - MyAddressError, NegotiateSwapContractAddrErr, PaymentInstructionsErr, RawTransactionFut, - RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionEnum, TransactionFut, - TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, + MyAddressError, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, + RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, + SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionEnum, + TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest}; @@ -432,7 +432,9 @@ impl SwapOps for LightningCoin { _amount: BigDecimal, _swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { + // Todo: Pay invoice in PaymentInstructions here unimplemented!() } @@ -444,7 +446,9 @@ impl SwapOps for LightningCoin { _amount: BigDecimal, _swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { + // Todo: Pay invoice in PaymentInstructions here unimplemented!() } @@ -607,7 +611,7 @@ impl SwapOps for LightningCoin { instructions: &[u8], secret_hash: &[u8], amount: BigDecimal, - ) -> Result<(), MmError> { + ) -> Result, MmError> { let invoice = Invoice::from_str(&hex::encode(instructions))?; if (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) || (secret_hash.len() == 32 && invoice.payment_hash().as_inner() != secret_hash) @@ -623,7 +627,7 @@ impl SwapOps for LightningCoin { return Err(ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice amount!".into()).into()); } // Todo: continue validation here by comparing (locktime, etc..) - Ok(()) + Ok(Some(PaymentInstructions::Lightning(invoice))) } } diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 40bbc3d304..490192015f 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -69,7 +69,7 @@ cfg_native! { use crate::lightning::ln_conf::PlatformCoinConfirmationTargets; use async_std::fs; use futures::AsyncWriteExt; - use lightning_invoice::ParseOrSemanticError; + use lightning_invoice::{Invoice, ParseOrSemanticError}; use std::io; use zcash_primitives::transaction::Transaction as ZTransaction; use z_coin::ZcoinProtocolInfo; @@ -474,6 +474,12 @@ pub struct SearchForSwapTxSpendInput<'a> { pub swap_unique_data: &'a [u8], } +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub enum PaymentInstructions { + #[cfg(not(target_arch = "wasm32"))] + Lightning(Invoice), +} + #[derive(Display)] pub enum PaymentInstructionsErr { LightningInvoiceErr(String), @@ -499,6 +505,8 @@ impl From for ValidateInstructionsErr { pub trait SwapOps { fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut; + // Todo: maybe make a struct for the arguments + #[allow(clippy::too_many_arguments)] fn send_maker_payment( &self, time_lock: u32, @@ -507,8 +515,11 @@ pub trait SwapOps { amount: BigDecimal, swap_contract_address: &Option, swap_unique_data: &[u8], + payment_instructions: &Option, ) -> TransactionFut; + // Todo: maybe make a struct for the arguments + #[allow(clippy::too_many_arguments)] fn send_taker_payment( &self, time_lock: u32, @@ -517,6 +528,7 @@ pub trait SwapOps { amount: BigDecimal, swap_contract_address: &Option, swap_unique_data: &[u8], + payment_instructions: &Option, ) -> TransactionFut; fn send_maker_spends_taker_payment( @@ -642,7 +654,7 @@ pub trait SwapOps { instructions: &[u8], secret_hash: &[u8], amount: BigDecimal, - ) -> Result<(), MmError>; + ) -> Result, MmError>; } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 493d7be8a6..1b648a696d 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -15,13 +15,13 @@ use crate::utxo::{qtum, ActualTxFee, AdditionalTxData, AddrFromStrError, Broadca UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK}; use crate::{BalanceError, BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, - MmCoin, NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyNotAllowed, RawTransactionFut, - RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageError, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, - TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, - VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, - WithdrawResult}; + MmCoin, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, PrivKeyNotAllowed, + RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, + TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, + TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, + ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, + WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -737,6 +737,7 @@ impl SwapOps for Qrc20Coin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let taker_addr = try_tx_fus!(self.contract_address_from_raw_pubkey(taker_pub)); let id = qrc20_swap_id(time_lock, secret_hash); @@ -761,6 +762,7 @@ impl SwapOps for Qrc20Coin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let maker_addr = try_tx_fus!(self.contract_address_from_raw_pubkey(maker_pub)); let id = qrc20_swap_id(time_lock, secret_hash); @@ -1054,8 +1056,8 @@ impl SwapOps for Qrc20Coin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index fbfc362116..3c115466a9 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -3,11 +3,12 @@ use crate::coin_errors::{MyAddressError, ValidatePaymentError}; use crate::solana::solana_common::{lamports_to_sol, PrepareTransferData, SufficientBalanceError}; use crate::solana::spl::SplTokenInfo; use crate::{BalanceError, BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, - PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, - SignatureResult, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, - TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, - WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; + PaymentInstructions, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, + SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, TradePreimageValue, + TransactionDetails, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, + VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest, + WithdrawResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -463,6 +464,7 @@ impl SwapOps for SolanaCoin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { unimplemented!() } @@ -475,6 +477,7 @@ impl SwapOps for SolanaCoin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { unimplemented!() } @@ -615,7 +618,7 @@ impl SwapOps for SolanaCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { + ) -> Result, MmError> { unimplemented!() } } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 0b2feed1be..4587047fd0 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -2,12 +2,13 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, Trade use crate::coin_errors::{MyAddressError, ValidatePaymentError}; use crate::solana::solana_common::{ui_amount_to_amount, PrepareTransferData, SufficientBalanceError}; use crate::solana::{solana_common, AccountError, SolanaCommonOps, SolanaFeeDetails}; -use crate::{BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, PaymentInstructionsErr, - RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SolanaCoin, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, - TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, - WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; +use crate::{BalanceFut, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, PaymentInstructions, + PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, + SignatureResult, SolanaCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, + TransactionDetails, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, + VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest, + WithdrawResult}; use async_trait::async_trait; use bincode::serialize; use common::{async_blocking, now_ms}; @@ -297,6 +298,7 @@ impl SwapOps for SplToken { amount: BigDecimal, swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { unimplemented!() } @@ -309,6 +311,7 @@ impl SwapOps for SplToken { amount: BigDecimal, swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { unimplemented!() } @@ -449,7 +452,7 @@ impl SwapOps for SplToken { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { + ) -> Result, MmError> { unimplemented!() } } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index a8e8597abe..8191fb4d14 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -7,12 +7,12 @@ use crate::tendermint::htlc::MsgClaimHtlc; use crate::utxo::sat_from_big_decimal; use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, - PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, - SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, - TransactionDetails, TransactionEnum, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, - ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, - WithdrawRequest}; + PaymentInstructions, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, + SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, + TradePreimageValue, TransactionDetails, TransactionEnum, TransactionFut, TransactionType, TxFeeDetails, + TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, + ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawError, + WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcrypto::sha256; use common::{get_utc_timestamp, Future01CompatExt}; @@ -633,6 +633,7 @@ impl SwapOps for TendermintCoin { amount: BigDecimal, swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { todo!() } @@ -645,6 +646,7 @@ impl SwapOps for TendermintCoin { amount: BigDecimal, swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { todo!() } @@ -785,8 +787,8 @@ impl SwapOps for TendermintCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 6320795fc0..9f914ea4d9 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -2,10 +2,10 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransaction TradeFee, TransactionEnum, TransactionFut}; use crate::{coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentFut}, BalanceFut, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, NegotiateSwapContractAddrErr, - PaymentInstructionsErr, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, TradePreimageResult, - TradePreimageValue, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, - WithdrawFut, WithdrawRequest}; + PaymentInstructions, PaymentInstructionsErr, SearchForSwapTxSpendInput, SignatureResult, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, VerificationResult, + WatcherValidatePaymentInput, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use futures01::Future; use keys::KeyPair; @@ -109,6 +109,7 @@ impl SwapOps for TestCoin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { unimplemented!() } @@ -121,6 +122,7 @@ impl SwapOps for TestCoin { amount: BigDecimal, swap_contract_address: &Option, _swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { unimplemented!() } @@ -265,7 +267,7 @@ impl SwapOps for TestCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { + ) -> Result, MmError> { unimplemented!() } } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index cd51e2a34d..3c8ec5b1aa 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -10,11 +10,11 @@ use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{BlockHeightAndTime, CanRefundHtlc, CoinBalance, CoinProtocol, CoinWithDerivationMethod, - NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionFut, - RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradePreimageValue, - TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, - VerificationResult, WatcherValidatePaymentInput, WithdrawFut}; + NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, + RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, + TradePreimageValue, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, + ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawFut}; use common::log::warn; use derive_more::Display; use futures::{FutureExt, TryFutureExt}; @@ -824,6 +824,7 @@ impl SwapOps for BchCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { utxo_common::send_maker_payment( self.clone(), @@ -843,6 +844,7 @@ impl SwapOps for BchCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { utxo_common::send_taker_payment( self.clone(), @@ -1054,8 +1056,8 @@ impl SwapOps for BchCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 5180d37c2b..97bf34ed1c 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -23,11 +23,11 @@ use crate::utxo::utxo_builder::{BlockHeaderUtxoArcOps, MergeUtxoArcOps, UtxoCoin use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{eth, CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, DelegationError, DelegationFut, - GetWithdrawSenderAddress, NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyBuildPolicy, - SearchForSwapTxSpendInput, SignatureResult, StakingInfosFut, SwapOps, TradePreimageValue, TransactionFut, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, - ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WatcherValidatePaymentInput, WithdrawFut, - WithdrawSenderAddress}; + GetWithdrawSenderAddress, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, + PrivKeyBuildPolicy, SearchForSwapTxSpendInput, SignatureResult, StakingInfosFut, SwapOps, + TradePreimageValue, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, + WatcherValidatePaymentInput, WithdrawFut, WithdrawSenderAddress}; use crypto::Bip44Chain; use ethereum_types::H160; use futures::{FutureExt, TryFutureExt}; @@ -522,6 +522,7 @@ impl SwapOps for QtumCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { utxo_common::send_maker_payment( self.clone(), @@ -541,6 +542,7 @@ impl SwapOps for QtumCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { utxo_common::send_taker_payment( self.clone(), @@ -752,8 +754,8 @@ impl SwapOps for QtumCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 305bf7a34d..ec085a2796 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -14,10 +14,10 @@ use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualTxFee, Addit FeePolicy, GenerateTxError, RecentlySpentOutPointsGuard, UtxoCoinConf, UtxoCoinFields, UtxoCommonOps, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, - NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionsErr, PrivKeyNotAllowed, - RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradeFee, - TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, - TransactionEnum, TransactionErr, TransactionFut, TxFeeDetails, TxMarshalingErr, + NegotiateSwapContractAddrErr, NumConversError, PaymentInstructions, PaymentInstructionsErr, + PrivKeyNotAllowed, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureResult, + SwapOps, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, + TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; @@ -1194,6 +1194,7 @@ impl SwapOps for SlpToken { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let taker_pub = try_tx_fus!(Public::from_slice(taker_pub)); let amount = try_tx_fus!(sat_from_big_decimal(&amount, self.decimals())); @@ -1219,6 +1220,7 @@ impl SwapOps for SlpToken { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let maker_pub = try_tx_fus!(Public::from_slice(maker_pub)); let amount = try_tx_fus!(sat_from_big_decimal(&amount, self.decimals())); @@ -1465,8 +1467,8 @@ impl SwapOps for SlpToken { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 0706c45155..1ba946ef42 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -21,10 +21,10 @@ use crate::utxo::utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, GetWithdrawSenderAddress, - NegotiateSwapContractAddrErr, PaymentInstructionsErr, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, - SignatureResult, SwapOps, TradePreimageValue, TransactionFut, TxMarshalingErr, ValidateAddressResult, - ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, - WatcherValidatePaymentInput, WithdrawFut, WithdrawSenderAddress}; + NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, + SearchForSwapTxSpendInput, SignatureResult, SwapOps, TradePreimageValue, TransactionFut, TxMarshalingErr, + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, + VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawSenderAddress}; use crypto::Bip44Chain; use futures::{FutureExt, TryFutureExt}; use mm2_metrics::MetricsArc; @@ -283,6 +283,7 @@ impl SwapOps for UtxoStandardCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { utxo_common::send_maker_payment( self.clone(), @@ -302,6 +303,7 @@ impl SwapOps for UtxoStandardCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { utxo_common::send_taker_payment( self.clone(), @@ -513,8 +515,8 @@ impl SwapOps for UtxoStandardCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 76e8b71fbe..965ecff002 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -13,12 +13,13 @@ use crate::utxo::{sat_from_big_decimal, utxo_common, ActualTxFee, AdditionalTxDa UtxoCommonOps, UtxoFeeDetails, UtxoRpcMode, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom}; use crate::{BalanceError, BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, - MmCoin, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionsErr, PrivKeyActivationPolicy, - RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, - SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, - TransactionEnum, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, ValidatePaymentInput, - VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawFut, WithdrawRequest}; + MmCoin, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructions, PaymentInstructionsErr, + PrivKeyActivationPolicy, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, + SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, + TradePreimageValue, TransactionDetails, TransactionEnum, TransactionFut, TxFeeDetails, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentFut, + ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawFut, + WithdrawRequest}; use crate::{Transaction, WithdrawError}; use async_trait::async_trait; use bitcrypto::{dhash160, dhash256}; @@ -1038,6 +1039,7 @@ impl SwapOps for ZCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let selfi = self.clone(); let maker_key_pair = self.derive_htlc_key_pair(swap_unique_data); @@ -1068,6 +1070,7 @@ impl SwapOps for ZCoin { amount: BigDecimal, _swap_contract_address: &Option, swap_unique_data: &[u8], + _payment_instructions: &Option, ) -> TransactionFut { let selfi = self.clone(); let taker_keypair = self.derive_htlc_key_pair(swap_unique_data); @@ -1409,8 +1412,8 @@ impl SwapOps for ZCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result<(), MmError> { - Ok(()) + ) -> Result, MmError> { + Ok(None) } } diff --git a/mm2src/coins/z_coin/z_coin_native_tests.rs b/mm2src/coins/z_coin/z_coin_native_tests.rs index 51957d8218..7c51a21ec6 100644 --- a/mm2src/coins/z_coin/z_coin_native_tests.rs +++ b/mm2src/coins/z_coin/z_coin_native_tests.rs @@ -43,6 +43,7 @@ fn zombie_coin_send_and_refund_maker_payment() { &secret_hash, "0.01".parse().unwrap(), &None, + &None, ) .wait() .unwrap(); @@ -93,6 +94,7 @@ fn zombie_coin_send_and_spend_maker_payment() { &*secret_hash, "0.01".parse().unwrap(), &None, + &None, ) .wait() .unwrap(); diff --git a/mm2src/mm2_main/src/docker_tests.rs b/mm2src/mm2_main/src/docker_tests.rs index 3bc1070b99..93b47bd520 100644 --- a/mm2src/mm2_main/src/docker_tests.rs +++ b/mm2src/mm2_main/src/docker_tests.rs @@ -394,7 +394,7 @@ mod docker_tests { let time_lock = (now_ms() / 1000) as u32 - 3600; let tx = coin - .send_taker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[]) + .send_taker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[], &None) .wait() .unwrap(); @@ -451,7 +451,7 @@ mod docker_tests { let time_lock = (now_ms() / 1000) as u32 - 3600; let tx = coin - .send_maker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[]) + .send_maker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[], &None) .wait() .unwrap(); @@ -492,7 +492,15 @@ mod docker_tests { let time_lock = (now_ms() / 1000) as u32 - 3600; let tx = coin - .send_taker_payment(time_lock, my_pubkey, &*dhash160(&secret), 1u64.into(), &None, &[]) + .send_taker_payment( + time_lock, + my_pubkey, + &*dhash160(&secret), + 1u64.into(), + &None, + &[], + &None, + ) .wait() .unwrap(); @@ -533,7 +541,15 @@ mod docker_tests { let time_lock = (now_ms() / 1000) as u32 - 3600; let tx = coin - .send_maker_payment(time_lock, my_pubkey, &*dhash160(&secret), 1u64.into(), &None, &[]) + .send_maker_payment( + time_lock, + my_pubkey, + &*dhash160(&secret), + 1u64.into(), + &None, + &[], + &None, + ) .wait() .unwrap(); @@ -585,6 +601,7 @@ mod docker_tests { 1.into(), &coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); diff --git a/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs index 4d2e22f0ec..4973246654 100644 --- a/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs @@ -184,6 +184,7 @@ fn test_taker_spends_maker_payment() { amount.clone(), &maker_coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -276,6 +277,7 @@ fn test_maker_spends_taker_payment() { amount.clone(), &taker_coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -357,6 +359,7 @@ fn test_maker_refunds_payment() { amount.clone(), &coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -418,6 +421,7 @@ fn test_taker_refunds_payment() { amount.clone(), &coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -476,6 +480,7 @@ fn test_check_if_my_payment_sent() { amount, &coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -527,6 +532,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { amount, &maker_coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -597,6 +603,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { amount, &maker_coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -667,6 +674,7 @@ fn test_search_for_swap_tx_spend_not_spent() { amount, &maker_coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -718,6 +726,7 @@ fn test_wait_for_tx_spend() { amount, &maker_coin.swap_contract_address(), &[], + &None, ) .wait() .unwrap(); @@ -1029,6 +1038,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ expected_max_taker_vol.to_decimal(), &None, &[], + &None, ) .wait() .expect("!send_taker_payment"); @@ -1416,7 +1426,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { let time_lock = (now_ms() / 1000) as u32 - 3600; let tx = coin - .send_maker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[]) + .send_maker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[], &None) .wait() .unwrap(); @@ -1457,7 +1467,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { let time_lock = (now_ms() / 1000) as u32 - 3600; let tx = coin - .send_taker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[]) + .send_taker_payment(time_lock, my_public_key, &[0; 20], 1u64.into(), &None, &[], &None) .wait() .unwrap(); diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index af07ff3470..354df57d74 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -59,6 +59,7 @@ use crate::mm2::lp_network::{broadcast_p2p_msg, Libp2pPeerId}; use async_std::sync as async_std_sync; +use bitcrypto::{dhash160, sha256}; use coins::{lp_coinfind, MmCoinEnum, TradeFee, TransactionEnum}; use common::log::{debug, warn}; use common::{bits256, calc_total_pages, @@ -82,7 +83,6 @@ use std::str::FromStr; use std::sync::{Arc, Mutex, Weak}; use uuid::Uuid; -use bitcrypto::{dhash160, sha256}; #[cfg(feature = "custom-swap-locktime")] use std::sync::atomic::{AtomicU64, Ordering}; diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index f4917b8a12..cf17350037 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -14,7 +14,7 @@ use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::MM_VERSION; -use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionsErr, +use coins::{CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructions, PaymentInstructionsErr, SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, TransactionEnum, ValidatePaymentInput}; use common::log::{debug, error, info, warn}; use common::{bits256, executor::Timer, now_ms, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -172,7 +172,7 @@ pub struct MakerSwapMut { taker_payment_spend: Option, taker_payment_spend_confirmed: bool, maker_payment_refund: Option, - payment_instructions: Option>, + payment_instructions: Option, } #[cfg(test)] @@ -642,16 +642,21 @@ impl MakerSwap { drop(send_abort_handle); let mut swap_events = vec![]; if let Some(instructions) = payload.instructions() { - if let Err(e) = - self.maker_coin - .validate_instructions(instructions, &self.secret_hash(), self.maker_amount.clone()) + match self + .maker_coin + .validate_instructions(instructions, &self.secret_hash(), self.maker_amount.clone()) { - return Ok((Some(MakerSwapCommand::Finish), vec![ - // Todo: maybe add a different event for this?? - MakerSwapEvent::TakerFeeValidateFailed(e.to_string().into()), - ])); - } - swap_events.push(MakerSwapEvent::MakerPaymentInstructionsReceived(instructions.to_vec())); + Ok(Some(instructions)) => { + swap_events.push(MakerSwapEvent::MakerPaymentInstructionsReceived(instructions)) + }, + Ok(None) => (), + Err(e) => { + return Ok((Some(MakerSwapCommand::Finish), vec![ + // Todo: maybe add a different event for this?? + MakerSwapEvent::TakerFeeValidateFailed(e.to_string().into()), + ])); + }, + }; } let taker_fee = match self.taker_coin.tx_enum_from_bytes(payload.data()) { @@ -737,7 +742,6 @@ impl MakerSwap { Ok(res) => match res { Some(tx) => tx, None => { - // Todo: For lightning this pays the invoice let payment_fut = self.maker_coin.send_maker_payment( self.r().data.maker_payment_lock as u32, &*self.r().other_maker_coin_htlc_pub, @@ -745,6 +749,7 @@ impl MakerSwap { self.maker_amount.clone(), &self.r().data.maker_coin_swap_contract_address, &unique_data, + &self.r().payment_instructions, ); match payment_fut.compat().await { @@ -1412,7 +1417,7 @@ pub enum MakerSwapEvent { StartFailed(SwapError), Negotiated(TakerNegotiationData), NegotiateFailed(SwapError), - MakerPaymentInstructionsReceived(Vec), + MakerPaymentInstructionsReceived(PaymentInstructions), TakerFeeValidated(TransactionIdentifier), TakerFeeValidateFailed(SwapError), MakerPaymentSent(TransactionIdentifier), diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index a573c238ce..2b46a0a570 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -15,8 +15,8 @@ use crate::mm2::lp_ordermatch::{MatchBy, OrderConfirmationsSettings, TakerAction use crate::mm2::lp_price::fetch_swap_coins_price; use crate::mm2::lp_swap::{broadcast_p2p_tx_msg, tx_helper_topic, TakerSwapWatcherData}; use crate::mm2::MM_VERSION; -use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionsErr, - SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, ValidatePaymentInput}; +use coins::{lp_coinfind, CanRefundHtlc, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructions, + PaymentInstructionsErr, SearchForSwapTxSpendInput, TradeFee, TradePreimageValue, ValidatePaymentInput}; use common::executor::Timer; use common::log::{debug, error, info, warn}; use common::{bits256, now_ms, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -492,7 +492,7 @@ pub struct TakerSwapMut { taker_payment_refund: Option, secret_hash: BytesJson, secret: H256Json, - payment_instructions: Option>, + payment_instructions: Option, } #[cfg(test)] @@ -570,7 +570,7 @@ pub enum TakerSwapEvent { NegotiateFailed(SwapError), TakerFeeSent(TransactionIdentifier), TakerFeeSendFailed(SwapError), - TakerPaymentInstructionsReceived(Vec), + TakerPaymentInstructionsReceived(PaymentInstructions), MakerPaymentReceived(TransactionIdentifier), MakerPaymentWaitConfirmStarted, MakerPaymentValidatedAndConfirmed, @@ -1197,17 +1197,22 @@ impl TakerSwap { let mut swap_events = vec![]; if let Some(instructions) = payload.instructions() { - if let Err(e) = self.taker_coin.validate_instructions( + match self.taker_coin.validate_instructions( instructions, &self.r().secret_hash.0, self.taker_amount.clone().into(), ) { - return Ok((Some(TakerSwapCommand::Finish), vec![ - // Todo: maybe add a different event for this?? - TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), - ])); + Ok(Some(instructions)) => { + swap_events.push(TakerSwapEvent::TakerPaymentInstructionsReceived(instructions)) + }, + Ok(None) => (), + Err(e) => { + return Ok((Some(TakerSwapCommand::Finish), vec![ + // Todo: maybe add a different event for this?? + TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), + ])); + }, } - swap_events.push(TakerSwapEvent::TakerPaymentInstructionsReceived(instructions.to_vec())); } swap_events.push(TakerSwapEvent::MakerPaymentReceived(tx_ident)); swap_events.push(TakerSwapEvent::MakerPaymentWaitConfirmStarted); @@ -1312,7 +1317,6 @@ impl TakerSwap { Ok(res) => match res { Some(tx) => tx, None => { - // Todo: For lightning this pays the invoice let payment_fut = self.taker_coin.send_taker_payment( self.r().data.taker_payment_lock as u32, &*self.r().other_taker_coin_htlc_pub, @@ -1320,6 +1324,7 @@ impl TakerSwap { self.taker_amount.to_decimal(), &self.r().data.taker_coin_swap_contract_address, &unique_data, + &self.r().payment_instructions, ); match payment_fut.compat().await { From 2a94c2305c49397ab7778f7773d0df16c2fd80be Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 30 Sep 2022 21:44:00 +0200 Subject: [PATCH 18/36] implement send_taker_payment and send_maker_payment for LightningCoin --- mm2src/coins/lightning.rs | 59 +++++++++++++------ mm2src/coins/lightning/ln_db.rs | 8 +-- mm2src/coins/lightning/ln_events.rs | 2 +- mm2src/coins/lightning/ln_serialization.rs | 6 +- mm2src/coins/lightning/ln_sql.rs | 24 ++++---- mm2src/coins/lp_coins.rs | 8 ++- .../rpc_command/lightning/generate_invoice.rs | 4 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 1 + mm2src/mm2_main/src/lp_swap/taker_swap.rs | 1 + 9 files changed, 73 insertions(+), 40 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index b927954b56..4a7034bb1b 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -18,11 +18,11 @@ use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, MyAddressError, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, - SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionEnum, - TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, - ValidateInstructionsErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, - WithdrawRequest}; + SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, + TransactionEnum, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, + ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentError, ValidatePaymentFut, + ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, + WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -45,7 +45,7 @@ use lightning_invoice::utils::DefaultRouter; use lightning_invoice::{payment, CreationError, InvoiceBuilder, SignOrCreationError}; use lightning_invoice::{Invoice, InvoiceDescription}; use ln_conf::{LightningCoinConf, LightningProtocolConf, PlatformCoinConfirmationTargets}; -use ln_db::{DBChannelDetails, DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; +use ln_db::{DBChannelDetails, HTLCStatus, LightningDB, PaymentInfo, PaymentType}; use ln_errors::{EnableLightningError, EnableLightningResult}; use ln_events::{init_events_abort_handlers, LightningEventHandler}; use ln_filesystem_persister::LightningFilesystemPersister; @@ -133,7 +133,7 @@ pub(crate) struct GetOpenChannelsResult { pub total: usize, } -#[derive(Display)] +#[derive(Debug, Display)] pub(crate) enum PaymentError { #[display(fmt = "Final cltv expiry delta {} is below the required minimum of {}", _0, _1)] CLTVExpiry(u32, u32), @@ -143,6 +143,15 @@ pub(crate) enum PaymentError { Keysend(String), } +impl Transaction for PaymentInfo { + fn tx_hex(&self) -> Vec { + // Todo: should this be an empty vec or an option or the payment_hash + Vec::new() + } + + fn tx_hash(&self) -> BytesJson { self.payment_hash.0.to_vec().into() } +} + impl LightningCoin { pub fn platform_coin(&self) -> &UtxoStandardCoin { &self.platform.coin } @@ -177,7 +186,7 @@ impl LightningCoin { .find(|chan| chan.user_channel_id == rpc_id) } - pub(crate) async fn pay_invoice(&self, invoice: Invoice) -> Result { + pub(crate) async fn pay_invoice(&self, invoice: Invoice) -> Result { let payment_hash = PaymentHash((invoice.payment_hash()).into_inner()); let payment_type = PaymentType::OutboundPayment { destination: *invoice.payee_pub_key().unwrap_or(&invoice.recover_payee_pub_key()), @@ -198,7 +207,7 @@ impl LightningCoin { }) .await?; - Ok(DBPaymentInfo { + Ok(PaymentInfo { payment_hash, payment_type, description, @@ -217,7 +226,7 @@ impl LightningCoin { destination: PublicKey, amount_msat: u64, final_cltv_expiry_delta: u32, - ) -> Result { + ) -> Result { if final_cltv_expiry_delta < MIN_FINAL_CLTV_EXPIRY { return Err(PaymentError::CLTVExpiry(final_cltv_expiry_delta, MIN_FINAL_CLTV_EXPIRY)); } @@ -235,7 +244,7 @@ impl LightningCoin { let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner()); let payment_type = PaymentType::OutboundPayment { destination }; - Ok(DBPaymentInfo { + Ok(PaymentInfo { payment_hash, payment_type, description: "".into(), @@ -432,10 +441,18 @@ impl SwapOps for LightningCoin { _amount: BigDecimal, _swap_contract_address: &Option, _swap_unique_data: &[u8], - _payment_instructions: &Option, + payment_instructions: &Option, ) -> TransactionFut { - // Todo: Pay invoice in PaymentInstructions here - unimplemented!() + let PaymentInstructions::Lightning(invoice) = payment_instructions + .clone() + .expect("payment_instructions can't be None"); + let this = self.clone(); + let fut = async move { + let payment = try_tx_s!(this.pay_invoice(invoice).await); + // Todo: are there more steps after pay_invoice?? + Ok(payment.into()) + }; + Box::new(fut.boxed().compat()) } fn send_taker_payment( @@ -446,10 +463,18 @@ impl SwapOps for LightningCoin { _amount: BigDecimal, _swap_contract_address: &Option, _swap_unique_data: &[u8], - _payment_instructions: &Option, + payment_instructions: &Option, ) -> TransactionFut { - // Todo: Pay invoice in PaymentInstructions here - unimplemented!() + let PaymentInstructions::Lightning(invoice) = payment_instructions + .clone() + .expect("payment_instructions can't be None"); + let this = self.clone(); + let fut = async move { + let payment = try_tx_s!(this.pay_invoice(invoice).await); + // Todo: are there more steps after pay_invoice?? + Ok(payment.into()) + }; + Box::new(fut.boxed().compat()) } fn send_maker_spends_taker_payment( diff --git a/mm2src/coins/lightning/ln_db.rs b/mm2src/coins/lightning/ln_db.rs index 248c164b06..b3b4215491 100644 --- a/mm2src/coins/lightning/ln_db.rs +++ b/mm2src/coins/lightning/ln_db.rs @@ -125,7 +125,7 @@ pub enum PaymentType { } #[derive(Clone, Debug, PartialEq)] -pub struct DBPaymentInfo { +pub struct PaymentInfo { pub payment_hash: PaymentHash, pub payment_type: PaymentType, pub description: String, @@ -153,7 +153,7 @@ pub struct DBPaymentsFilter { } pub struct GetPaymentsResult { - pub payments: Vec, + pub payments: Vec, pub skipped: usize, pub total: usize, } @@ -228,10 +228,10 @@ pub trait LightningDB { ) -> Result; /// Inserts or updates a new payment record in the DB. - async fn add_or_update_payment_in_db(&self, info: DBPaymentInfo) -> Result<(), Self::Error>; + async fn add_or_update_payment_in_db(&self, info: PaymentInfo) -> Result<(), Self::Error>; /// Gets a payment's record from DB by the payment's hash. - async fn get_payment_from_db(&self, hash: PaymentHash) -> Result, Self::Error>; + async fn get_payment_from_db(&self, hash: PaymentHash) -> Result, Self::Error>; /// Gets the list of payments that match the provided filter criteria. The number of requested records is specified /// by the limit parameter, the starting record to list from is specified by the paging parameter. The total number diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index a6b0d0f411..92fbfbe463 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -375,7 +375,7 @@ impl LightningEventHandler { } }), PaymentPurpose::SpontaneousPayment(payment_preimage) => { - let payment_info = DBPaymentInfo { + let payment_info = PaymentInfo { payment_hash, payment_type: PaymentType::InboundPayment, description: "".into(), diff --git a/mm2src/coins/lightning/ln_serialization.rs b/mm2src/coins/lightning/ln_serialization.rs index 857cae2905..511522c328 100644 --- a/mm2src/coins/lightning/ln_serialization.rs +++ b/mm2src/coins/lightning/ln_serialization.rs @@ -1,4 +1,4 @@ -use crate::lightning::ln_db::{DBPaymentInfo, DBPaymentsFilter, HTLCStatus, PaymentType}; +use crate::lightning::ln_db::{DBPaymentsFilter, HTLCStatus, PaymentInfo, PaymentType}; use crate::lightning::ln_platform::h256_json_from_txid; use crate::H256Json; use lightning::chain::channelmonitor::Balance; @@ -187,8 +187,8 @@ pub struct PaymentInfoForRPC { last_updated: i64, } -impl From for PaymentInfoForRPC { - fn from(info: DBPaymentInfo) -> Self { +impl From for PaymentInfoForRPC { + fn from(info: PaymentInfo) -> Self { PaymentInfoForRPC { payment_hash: info.payment_hash.0.into(), payment_type: info.payment_type.into(), diff --git a/mm2src/coins/lightning/ln_sql.rs b/mm2src/coins/lightning/ln_sql.rs index ac21b6351e..7809c3e85c 100644 --- a/mm2src/coins/lightning/ln_sql.rs +++ b/mm2src/coins/lightning/ln_sql.rs @@ -1,6 +1,6 @@ -use crate::lightning::ln_db::{ChannelType, ChannelVisibility, ClosedChannelsFilter, DBChannelDetails, DBPaymentInfo, +use crate::lightning::ln_db::{ChannelType, ChannelVisibility, ClosedChannelsFilter, DBChannelDetails, DBPaymentsFilter, GetClosedChannelsResult, GetPaymentsResult, HTLCStatus, LightningDB, - PaymentType}; + PaymentInfo, PaymentType}; use async_trait::async_trait; use common::{async_blocking, PagingOptionsEnum}; use db_common::sqlite::rusqlite::{Error as SqlError, Row, ToSql, NO_PARAMS}; @@ -198,7 +198,7 @@ fn channel_details_from_row(row: &Row<'_>) -> Result Ok(channel_details) } -fn payment_info_from_row(row: &Row<'_>) -> Result { +fn payment_info_from_row(row: &Row<'_>) -> Result { let is_outbound = row.get::<_, bool>(8)?; let payment_type = if is_outbound { PaymentType::OutboundPayment { @@ -208,7 +208,7 @@ fn payment_info_from_row(row: &Row<'_>) -> Result { PaymentType::InboundPayment }; - let payment_info = DBPaymentInfo { + let payment_info = PaymentInfo { payment_hash: PaymentHash(h256_slice_from_row::(row, 0)?), payment_type, description: row.get(2)?, @@ -768,7 +768,7 @@ impl LightningDB for SqliteLightningDB { .await } - async fn add_or_update_payment_in_db(&self, info: DBPaymentInfo) -> Result<(), Self::Error> { + async fn add_or_update_payment_in_db(&self, info: PaymentInfo) -> Result<(), Self::Error> { let for_coin = self.db_ticker.clone(); let payment_hash = hex::encode(info.payment_hash.0); let (is_outbound, destination) = match info.payment_type { @@ -803,7 +803,7 @@ impl LightningDB for SqliteLightningDB { .await } - async fn get_payment_from_db(&self, hash: PaymentHash) -> Result, Self::Error> { + async fn get_payment_from_db(&self, hash: PaymentHash) -> Result, Self::Error> { let params = [hex::encode(hash.0)]; let sql = select_payment_by_hash_sql(self.db_ticker.as_str())?; @@ -946,7 +946,7 @@ mod tests { channels } - fn generate_random_payments(num: u64) -> Vec { + fn generate_random_payments(num: u64) -> Vec { let mut rng = rand::thread_rng(); let mut payments = vec![]; let s = Secp256k1::new(); @@ -970,7 +970,7 @@ mod tests { HTLCStatus::Failed }; let description: String = rng.sample_iter(&Alphanumeric).take(30).map(char::from).collect(); - let info = DBPaymentInfo { + let info = PaymentInfo { payment_hash: { rng.fill_bytes(&mut bytes); PaymentHash(bytes) @@ -1143,7 +1143,7 @@ mod tests { let payment = block_on(db.get_payment_from_db(PaymentHash([0; 32]))).unwrap(); assert!(payment.is_none()); - let mut expected_payment_info = DBPaymentInfo { + let mut expected_payment_info = PaymentInfo { payment_hash: PaymentHash([0; 32]), payment_type: PaymentType::InboundPayment, description: "test payment".into(), @@ -1242,7 +1242,7 @@ mod tests { let limit = 10; let result = block_on(db.get_payments_by_filter(Some(filter.clone()), paging.clone(), limit)).unwrap(); - let expected_payments_vec: Vec = payments + let expected_payments_vec: Vec = payments .iter() .map(|p| p.clone()) .filter(|p| p.payment_type == PaymentType::InboundPayment) @@ -1258,7 +1258,7 @@ mod tests { filter.status = Some(HTLCStatus::Succeeded.to_string()); let result = block_on(db.get_payments_by_filter(Some(filter.clone()), paging.clone(), limit)).unwrap(); - let expected_payments_vec: Vec = expected_payments_vec + let expected_payments_vec: Vec = expected_payments_vec .iter() .map(|p| p.clone()) .filter(|p| p.status == HTLCStatus::Succeeded) @@ -1279,7 +1279,7 @@ mod tests { filter.status = None; filter.description = Some(substr.to_string()); let result = block_on(db.get_payments_by_filter(Some(filter), paging, limit)).unwrap(); - let expected_payments_vec: Vec = payments + let expected_payments_vec: Vec = payments .iter() .map(|p| p.clone()) .filter(|p| p.description.contains(&substr)) diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 490192015f..277e4b75a0 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -66,6 +66,7 @@ use utxo_signer::with_key_pair::UtxoSignWithKeyPairError; cfg_native! { use crate::lightning::LightningCoin; + use crate::lightning::ln_db::PaymentInfo as LightningPayment; use crate::lightning::ln_conf::PlatformCoinConfirmationTargets; use async_std::fs; use futures::AsyncWriteExt; @@ -79,7 +80,6 @@ cfg_wasm32! { use mm2_db::indexed_db::{ConstructibleDb, DbLocked, SharedDb}; use hd_wallet_storage::HDWalletDb; use tx_history_storage::wasm::{clear_tx_history, load_tx_history, save_tx_history, TxHistoryDb}; - pub type TxHistoryDbLocked<'a> = DbLocked<'a, TxHistoryDb>; } @@ -358,12 +358,16 @@ pub enum TransactionEnum { SignedEthTx(SignedEthTx), #[cfg(not(target_arch = "wasm32"))] ZTransaction(ZTransaction), + #[cfg(not(target_arch = "wasm32"))] + LightningPayment(LightningPayment), } ifrom!(TransactionEnum, UtxoTx); ifrom!(TransactionEnum, SignedEthTx); #[cfg(not(target_arch = "wasm32"))] ifrom!(TransactionEnum, ZTransaction); +#[cfg(not(target_arch = "wasm32"))] +ifrom!(TransactionEnum, LightningPayment); // NB: When stable and groked by IDEs, `enum_dispatch` can be used instead of `Deref` to speed things up. impl Deref for TransactionEnum { @@ -374,6 +378,8 @@ impl Deref for TransactionEnum { TransactionEnum::SignedEthTx(ref t) => t, #[cfg(not(target_arch = "wasm32"))] TransactionEnum::ZTransaction(ref t) => t, + #[cfg(not(target_arch = "wasm32"))] + TransactionEnum::LightningPayment(ref p) => p, } } } diff --git a/mm2src/coins/rpc_command/lightning/generate_invoice.rs b/mm2src/coins/rpc_command/lightning/generate_invoice.rs index 68bb18ade6..0959cb0b9f 100644 --- a/mm2src/coins/rpc_command/lightning/generate_invoice.rs +++ b/mm2src/coins/rpc_command/lightning/generate_invoice.rs @@ -1,4 +1,4 @@ -use crate::lightning::ln_db::{DBPaymentInfo, HTLCStatus, LightningDB, PaymentType}; +use crate::lightning::ln_db::{HTLCStatus, LightningDB, PaymentInfo, PaymentType}; use crate::lightning::ln_p2p::connect_to_ln_node; use crate::lightning::DEFAULT_INVOICE_EXPIRY; use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; @@ -110,7 +110,7 @@ pub async fn generate_invoice( // Todo: Should remove adding payment to db step since the preimage can be recreated from the keymanager and the invoice secret (Do I need to check that received amount equals the requested amount?) let payment_hash = invoice.payment_hash().into_inner(); - let payment_info = DBPaymentInfo { + let payment_info = PaymentInfo { payment_hash: PaymentHash(payment_hash), payment_type: PaymentType::InboundPayment, description: req.description, diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index cf17350037..1f5a64430b 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -410,6 +410,7 @@ impl MakerSwap { } async fn get_my_payment_data(&self) -> Result> { + // Todo: this will send the payment hash of the maker_payment to the taker if maker is lightning, maybe it's not needed let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); if let Some(instructions) = self .taker_coin diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 2b46a0a570..b99f3d2e4c 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -850,6 +850,7 @@ impl TakerSwap { } async fn get_my_payment_data(&self) -> Result> { + // Todo: this will send the payment hash of the taker_fee to the maker if taker is lightning, maybe it's not needed let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex.0.clone(); let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); From ac6b7a998b9c5ea19c529918e4e578a0e7e6dd2a Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 3 Oct 2022 20:57:06 +0200 Subject: [PATCH 19/36] change tx_enum_from_bytes to return none for lightning coin --- mm2src/coins/eth.rs | 4 +- mm2src/coins/lightning.rs | 30 ++--- mm2src/coins/lightning/ln_events.rs | 1 + mm2src/coins/lp_coins.rs | 2 +- mm2src/coins/qrc20.rs | 4 +- mm2src/coins/solana.rs | 2 +- mm2src/coins/solana/spl.rs | 2 +- mm2src/coins/tendermint/tendermint_coin.rs | 2 +- mm2src/coins/test_coin.rs | 2 +- mm2src/coins/utxo/bch.rs | 4 +- mm2src/coins/utxo/qtum.rs | 4 +- mm2src/coins/utxo/slp.rs | 2 +- mm2src/coins/utxo/utxo_standard.rs | 4 +- mm2src/coins/utxo/utxo_tests.rs | 6 +- mm2src/coins/z_coin.rs | 4 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 105 +++++++++++------- .../src/lp_swap/recreate_swap_data.rs | 15 ++- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 59 ++++++---- 18 files changed, 148 insertions(+), 104 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 76f989e207..b265f9b925 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1428,9 +1428,9 @@ impl MarketCoinOps for EthCoin { Box::new(fut.boxed().compat()) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { signed_eth_tx_from_bytes(bytes) - .map(TransactionEnum::from) + .map(|tx| Some(tx.into())) .map_to_mm(TxMarshalingErr::InvalidInput) } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 4a7034bb1b..3d5da538ac 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -17,12 +17,12 @@ use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsign use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, MyAddressError, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, - RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, - SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, - TransactionEnum, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, - ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, - WithdrawFut, WithdrawRequest}; + RawTransactionError, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, + SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, + Transaction, TransactionEnum, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, + UtxoStandardCoin, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentError, + ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, + WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -595,7 +595,7 @@ impl SwapOps for LightningCoin { &self, _other_side_address: Option<&[u8]>, ) -> Result, MmError> { - unimplemented!() + Ok(None) } fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } @@ -757,11 +757,8 @@ impl MarketCoinOps for LightningCoin { unimplemented!() } - // Todo: Implement this when implementing swaps for lightning as it's is used mainly for swaps - fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { - MmError::err(TxMarshalingErr::NotSupported( - "tx_enum_from_bytes is not supported for Lightning yet.".to_string(), - )) + fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result, MmError> { + Ok(None) } fn current_block(&self) -> Box + Send> { Box::new(futures01::future::ok(0)) } @@ -786,8 +783,13 @@ impl MarketCoinOps for LightningCoin { impl MmCoin for LightningCoin { fn is_asset_chain(&self) -> bool { false } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { - Box::new(self.platform_coin().get_raw_transaction(req)) + fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { + let fut = async move { + MmError::err(RawTransactionError::InternalError( + "get_raw_transaction method is not supported for lightning, please use get_payment_details method instead.".into(), + )) + }; + Box::new(fut.boxed().compat()) } fn withdraw(&self, _req: WithdrawRequest) -> WithdrawFut { diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index 92fbfbe463..4f2a7417a7 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -346,6 +346,7 @@ impl LightningEventHandler { None => { // Free the htlc immediately if we don't have the preimage required to claim the payment // to allow for this inbound liquidity to be used for other inbound payments. + // Todo: this should be avoided for swaps except on refund self.channel_manager.fail_htlc_backwards(payment_hash); return; }, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 277e4b75a0..038f817e54 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -722,7 +722,7 @@ pub trait MarketCoinOps { swap_contract_address: &Option, ) -> TransactionFut; - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result>; + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError>; fn current_block(&self) -> Box + Send>; diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 1b648a696d..4547c5b91e 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1168,8 +1168,8 @@ impl MarketCoinOps for Qrc20Coin { Box::new(fut.boxed().compat()) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 3c115466a9..a4dec2faac 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -432,7 +432,7 @@ impl MarketCoinOps for SolanaCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Solana yet.".to_string(), )) diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 4587047fd0..5fc84d437b 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -270,7 +270,7 @@ impl MarketCoinOps for SplToken { unimplemented!() } - fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Spl yet.".to_string(), )) diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 8191fb4d14..f9a6428f9f 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -598,7 +598,7 @@ impl MarketCoinOps for TendermintCoin { todo!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Tendermint yet.".to_string(), )) diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 9f914ea4d9..46f79a129c 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -80,7 +80,7 @@ impl MarketCoinOps for TestCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Test coin yet.".to_string(), )) diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 3c8ec5b1aa..c133969136 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1145,8 +1145,8 @@ impl MarketCoinOps for BchCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 97bf34ed1c..d1675c41ef 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -831,8 +831,8 @@ impl MarketCoinOps for QtumCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index ec085a2796..05cc9c05df 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1149,7 +1149,7 @@ impl MarketCoinOps for SlpToken { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { self.platform_coin.tx_enum_from_bytes(bytes) } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 1ba946ef42..8531197a57 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -592,8 +592,8 @@ impl MarketCoinOps for UtxoStandardCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index bcc0910483..0fb8219b18 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -2485,7 +2485,7 @@ fn test_validate_fee_wrong_sender() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Electrum(rpc_client), None, false); // https://morty.explorer.dexstats.info/tx/fe4b0e1c4537e22f2956b5b74513fc936ebd87ada21513e850899cb07a45d475 let tx_bytes = hex::decode("0400008085202f890199cc492c24cc617731d13cff0ef22e7b0c277a64e7368a615b46214424a1c894020000006a473044022071edae37cf518e98db3f7637b9073a7a980b957b0c7b871415dbb4898ec3ebdc022031b402a6b98e64ffdf752266449ca979a9f70144dba77ed7a6a25bfab11648f6012103ad6f89abc2e5beaa8a3ac28e22170659b3209fe2ddf439681b4b8f31508c36faffffffff0202290200000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac8a96e70b000000001976a914d55f0df6cb82630ad21a4e6049522a6f2b6c9d4588ac8afb2c60000000000000000000000000000000").unwrap(); - let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap(); + let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap().unwrap(); let amount: BigDecimal = "0.0014157".parse().unwrap(); let validate_err = coin .validate_fee( @@ -2511,7 +2511,7 @@ fn test_validate_fee_min_block() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Electrum(rpc_client), None, false); // https://morty.explorer.dexstats.info/tx/fe4b0e1c4537e22f2956b5b74513fc936ebd87ada21513e850899cb07a45d475 let tx_bytes = hex::decode("0400008085202f890199cc492c24cc617731d13cff0ef22e7b0c277a64e7368a615b46214424a1c894020000006a473044022071edae37cf518e98db3f7637b9073a7a980b957b0c7b871415dbb4898ec3ebdc022031b402a6b98e64ffdf752266449ca979a9f70144dba77ed7a6a25bfab11648f6012103ad6f89abc2e5beaa8a3ac28e22170659b3209fe2ddf439681b4b8f31508c36faffffffff0202290200000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac8a96e70b000000001976a914d55f0df6cb82630ad21a4e6049522a6f2b6c9d4588ac8afb2c60000000000000000000000000000000").unwrap(); - let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap(); + let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap().unwrap(); let amount: BigDecimal = "0.0014157".parse().unwrap(); let sender_pub = hex::decode("03ad6f89abc2e5beaa8a3ac28e22170659b3209fe2ddf439681b4b8f31508c36fa").unwrap(); let validate_err = coin @@ -2539,7 +2539,7 @@ fn test_validate_fee_bch_70_bytes_signature() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Electrum(rpc_client), None, false); // https://blockchair.com/bitcoin-cash/transaction/ccee05a6b5bbc6f50d2a65a5a3a04690d3e2d81082ad57d3ab471189f53dd70d let tx_bytes = hex::decode("0100000002cae89775f264e50f14238be86a7184b7f77bfe26f54067b794c546ec5eb9c91a020000006b483045022100d6ed080f722a0637a37552382f462230cc438984bc564bdb4b7094f06cfa38fa022062304a52602df1fbb3bebac4f56e1632ad456f62d9031f4983f07e546c8ec4d8412102ae7dc4ef1b49aadeff79cfad56664105f4d114e1716bc4f930cb27dbd309e521ffffffff11f386a6fe8f0431cb84f549b59be00f05e78f4a8a926c5e023a0d5f9112e8200000000069463043021f17eb93ed20a6f2cd357eabb41a4ec6329000ddc6d5b42ecbe642c5d41b206a022026bc4920c4ce3af751283574baa8e4a3efd4dad0d8fe6ba3ddf5d75628d36fda412102ae7dc4ef1b49aadeff79cfad56664105f4d114e1716bc4f930cb27dbd309e521ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac57481c00000000001976a914bac11ce4cd2b1df2769c470d09b54f86df737e3c88ac035b4a60").unwrap(); - let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap(); + let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap().unwrap(); let amount: BigDecimal = "0.0001".parse().unwrap(); let sender_pub = hex::decode("02ae7dc4ef1b49aadeff79cfad56664105f4d114e1716bc4f930cb27dbd309e521").unwrap(); coin.validate_fee(&taker_fee_tx, &sender_pub, &*DEX_FEE_ADDR_RAW_PUBKEY, &amount, 0, &[]) diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 965ecff002..9769d39b60 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -995,9 +995,9 @@ impl MarketCoinOps for ZCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { ZTransaction::read(bytes) - .map(TransactionEnum::from) + .map(|tx| Some(tx.into())) .map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string())) } diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 1f5a64430b..0d35f9f9b9 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -279,13 +279,14 @@ impl MakerSwap { MakerSwapEvent::MakerPaymentInstructionsReceived(instructions) => { self.w().payment_instructions = Some(instructions) }, - MakerSwapEvent::TakerFeeValidated(tx) => self.w().taker_fee = Some(tx), + // Todo: should we have the hash for the taker fee for lightning here not None? + MakerSwapEvent::TakerFeeValidated(tx) => self.w().taker_fee = tx, MakerSwapEvent::TakerFeeValidateFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentSent(tx) => self.w().maker_payment = Some(tx), MakerSwapEvent::MakerPaymentTransactionFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentDataSendFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentWaitConfirmFailed(err) => self.errors.lock().push(err), - MakerSwapEvent::TakerPaymentReceived(tx) => self.w().taker_payment = Some(tx), + MakerSwapEvent::TakerPaymentReceived(tx) => self.w().taker_payment = tx, MakerSwapEvent::TakerPaymentWaitConfirmStarted => (), MakerSwapEvent::TakerPaymentValidatedAndConfirmed => { self.taker_payment_confirmed.store(true, Ordering::Relaxed) @@ -669,47 +670,58 @@ impl MakerSwap { }, }; - let hash = taker_fee.tx_hash(); - info!("Taker fee tx {:02x}", hash); + let fee_ident = match &taker_fee { + Some(fee) => { + let hash = fee.tx_hash(); + info!("Taker fee tx {:02x}", hash); + Some(TransactionIdentifier { + tx_hex: fee.tx_hex().into(), + tx_hash: hash, + }) + }, + None => { + // Todo: when implementing fee for lightning, should add a log for taker fee payment hash + None + }, + }; let taker_amount = MmNumber::from(self.taker_amount.clone()); let fee_amount = dex_fee_amount_from_taker_coin(&self.taker_coin, &self.r().data.maker_coin, &taker_amount); let other_taker_coin_htlc_pub = self.r().other_taker_coin_htlc_pub; let taker_coin_start_block = self.r().data.taker_coin_start_block; - let mut attempts = 0; - loop { - match self - .taker_coin - .validate_fee( - &taker_fee, - &*other_taker_coin_htlc_pub, - &DEX_FEE_ADDR_RAW_PUBKEY, - &fee_amount.clone().into(), - taker_coin_start_block, - self.uuid.as_bytes(), - ) - .compat() - .await - { - Ok(_) => break, - Err(err) => { - if attempts >= 3 { - return Ok((Some(MakerSwapCommand::Finish), vec![ - MakerSwapEvent::TakerFeeValidateFailed(ERRL!("{}", err).into()), - ])); - } else { - attempts += 1; - Timer::sleep(10.).await; - } - }, - }; + // Todo: when implementing fee for lightning this will be changed + if let Some(fee) = &taker_fee { + let mut attempts = 0; + loop { + match self + .taker_coin + .validate_fee( + fee, + &*other_taker_coin_htlc_pub, + &DEX_FEE_ADDR_RAW_PUBKEY, + &fee_amount.clone().into(), + taker_coin_start_block, + self.uuid.as_bytes(), + ) + .compat() + .await + { + Ok(_) => break, + Err(err) => { + if attempts >= 3 { + return Ok((Some(MakerSwapCommand::Finish), vec![ + MakerSwapEvent::TakerFeeValidateFailed(ERRL!("{}", err).into()), + ])); + } else { + attempts += 1; + Timer::sleep(10.).await; + } + }, + }; + } } - let fee_ident = TransactionIdentifier { - tx_hex: taker_fee.tx_hex().into(), - tx_hash: hash, - }; swap_events.push(MakerSwapEvent::TakerFeeValidated(fee_ident)); Ok((Some(MakerSwapCommand::SendPayment), swap_events)) @@ -828,6 +840,7 @@ impl MakerSwap { &self.uuid, wait_duration, ); + // Todo: taker_payment should be a message on lightning network not a swap message let payload = match recv_fut.await { Ok(p) => p, Err(e) => { @@ -855,11 +868,19 @@ impl MakerSwap { }, }; - let tx_hash = taker_payment.tx_hash(); - info!("Taker payment tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: taker_payment.tx_hex().into(), - tx_hash, + let tx_ident = match taker_payment { + Some(tx) => { + let tx_hash = tx.tx_hash(); + info!("Taker payment tx {:02x}", tx_hash); + Some(TransactionIdentifier { + tx_hex: tx.tx_hex().into(), + tx_hash, + }) + }, + None => { + info!("Taker payment tx {:02x}", BytesJson::new(self.secret_hash())); + None + }, }; Ok((Some(MakerSwapCommand::ValidateTakerPayment), vec![ @@ -1419,13 +1440,13 @@ pub enum MakerSwapEvent { Negotiated(TakerNegotiationData), NegotiateFailed(SwapError), MakerPaymentInstructionsReceived(PaymentInstructions), - TakerFeeValidated(TransactionIdentifier), + TakerFeeValidated(Option), TakerFeeValidateFailed(SwapError), MakerPaymentSent(TransactionIdentifier), MakerPaymentTransactionFailed(SwapError), MakerPaymentDataSendFailed(SwapError), MakerPaymentWaitConfirmFailed(SwapError), - TakerPaymentReceived(TransactionIdentifier), + TakerPaymentReceived(Option), TakerPaymentWaitConfirmStarted, TakerPaymentValidatedAndConfirmed, TakerPaymentValidateFailed(SwapError), 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 75b71227b4..b2adb9a744 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -215,9 +215,10 @@ fn convert_taker_to_maker_events( // Todo: does the taker need to retrieve the invoice from here?? also look in maker_swap.rs for mirror TakerSwapEvent::MakerPaymentReceived(tx_ident) => { if let Some(taker_fee_ident) = taker_fee_ident.take() { - push_event!(MakerSwapEvent::TakerFeeValidated(taker_fee_ident)); + push_event!(MakerSwapEvent::TakerFeeValidated(Some(taker_fee_ident))); } - push_event!(MakerSwapEvent::MakerPaymentSent(tx_ident)) + // Todo: remove unwrap, we can + push_event!(MakerSwapEvent::MakerPaymentSent(tx_ident.unwrap())) }, TakerSwapEvent::MakerPaymentValidateFailed(_) | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) @@ -234,7 +235,7 @@ fn convert_taker_to_maker_events( return events; }, TakerSwapEvent::TakerPaymentSent(tx_ident) => { - push_event!(MakerSwapEvent::TakerPaymentReceived(tx_ident)); + push_event!(MakerSwapEvent::TakerPaymentReceived(Some(tx_ident))); // Please note we have not to push `TakerPaymentValidatedAndConfirmed` since we could actually decline it. push_event!(MakerSwapEvent::TakerPaymentWaitConfirmStarted); }, @@ -417,7 +418,8 @@ fn convert_maker_to_taker_events( error: format!("Origin Maker error event: {:?}", event), }; match event { - MakerSwapEvent::TakerFeeValidated(tx_ident) => push_event!(TakerSwapEvent::TakerFeeSent(tx_ident)), + // Todo: remove unwrap by making tx_ident and enum + MakerSwapEvent::TakerFeeValidated(tx_ident) => push_event!(TakerSwapEvent::TakerFeeSent(tx_ident.unwrap())), MakerSwapEvent::TakerFeeValidateFailed(_) => { push_event!(TakerSwapEvent::MakerPaymentValidateFailed(swap_error)); push_event!(TakerSwapEvent::Finished); @@ -426,7 +428,7 @@ fn convert_maker_to_taker_events( }, MakerSwapEvent::MakerPaymentSent(tx_ident) => { // Todo: check this if a new event is added for other side instructions, also look in the mirror maker_swap.rs - push_event!(TakerSwapEvent::MakerPaymentReceived(tx_ident)); + push_event!(TakerSwapEvent::MakerPaymentReceived(Some(tx_ident))); // Please note we have not to push `MakerPaymentValidatedAndConfirmed` since we could actually decline it. push_event!(TakerSwapEvent::MakerPaymentWaitConfirmStarted); }, @@ -441,7 +443,8 @@ fn convert_maker_to_taker_events( }, MakerSwapEvent::TakerPaymentReceived(tx_ident) => { push_event!(TakerSwapEvent::MakerPaymentValidatedAndConfirmed); - push_event!(TakerSwapEvent::TakerPaymentSent(tx_ident.clone())); + // Todo: remove unwrap by making tx_ident an enum + push_event!(TakerSwapEvent::TakerPaymentSent(tx_ident.clone().unwrap())); }, MakerSwapEvent::TakerPaymentValidateFailed(_) | MakerSwapEvent::TakerPaymentSpendFailed(_) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index b99f3d2e4c..c5cd5e6a41 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -571,7 +571,7 @@ pub enum TakerSwapEvent { TakerFeeSent(TransactionIdentifier), TakerFeeSendFailed(SwapError), TakerPaymentInstructionsReceived(PaymentInstructions), - MakerPaymentReceived(TransactionIdentifier), + MakerPaymentReceived(Option), MakerPaymentWaitConfirmStarted, MakerPaymentValidatedAndConfirmed, MakerPaymentValidateFailed(SwapError), @@ -722,7 +722,7 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentInstructionsReceived(instructions) => { self.w().payment_instructions = Some(instructions) }, - TakerSwapEvent::MakerPaymentReceived(tx) => self.w().maker_payment = Some(tx), + TakerSwapEvent::MakerPaymentReceived(tx) => self.w().maker_payment = tx, TakerSwapEvent::MakerPaymentWaitConfirmStarted => (), TakerSwapEvent::MakerPaymentValidatedAndConfirmed => { self.maker_payment_confirmed.store(true, Ordering::Relaxed) @@ -850,7 +850,8 @@ impl TakerSwap { } async fn get_my_payment_data(&self) -> Result> { - // Todo: this will send the payment hash of the taker_fee to the maker if taker is lightning, maybe it's not needed + // Todo: this can be used to send the payment hash of the taker_fee to the maker if taker is lightning, if it's needed + // Todo: What about if the 2 coins are lightning do we need any of these messages?? let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex.0.clone(); let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); @@ -1178,24 +1179,6 @@ impl TakerSwap { }, }; drop(abort_send_handle); - let maker_payment = match self.maker_coin.tx_enum_from_bytes(payload.data()) { - Ok(p) => p, - Err(e) => { - return Ok((Some(TakerSwapCommand::Finish), vec![ - TakerSwapEvent::MakerPaymentValidateFailed( - ERRL!("Error parsing the 'maker-payment': {:?}", e).into(), - ), - ])) - }, - }; - - let tx_hash = maker_payment.tx_hash(); - info!("Got maker payment {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: maker_payment.tx_hex().into(), - tx_hash, - }; - let mut swap_events = vec![]; if let Some(instructions) = payload.instructions() { match self.taker_coin.validate_instructions( @@ -1215,6 +1198,34 @@ impl TakerSwap { }, } } + + let maker_payment = match self.maker_coin.tx_enum_from_bytes(payload.data()) { + Ok(p) => p, + Err(e) => { + return Ok((Some(TakerSwapCommand::Finish), vec![ + TakerSwapEvent::MakerPaymentValidateFailed( + ERRL!("Error parsing the 'maker-payment': {:?}", e).into(), + ), + ])) + }, + }; + + let tx_ident = match maker_payment { + Some(tx) => { + let tx_hash = tx.tx_hash(); + info!("Got maker payment {:02x}", tx_hash); + Some(TransactionIdentifier { + tx_hex: tx.tx_hex().into(), + tx_hash, + }) + }, + None => { + // secret_hash is the hash for the lightning payment + info!("Got maker payment {:02x}", &self.r().secret_hash); + None + }, + }; + swap_events.push(TakerSwapEvent::MakerPaymentReceived(tx_ident)); swap_events.push(TakerSwapEvent::MakerPaymentWaitConfirmStarted); @@ -1225,6 +1236,7 @@ impl TakerSwap { info!("Before wait confirm"); let confirmations = self.r().data.maker_payment_confirmations; let f = self.maker_coin.wait_for_confirmations( + // Todo: remove unwrap since maker_payment is none for lightning &self.r().maker_payment.clone().unwrap().tx_hex, confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), @@ -1241,6 +1253,7 @@ impl TakerSwap { info!("After wait confirm"); let validate_input = ValidatePaymentInput { + // Todo: remove unwrap since maker_payment is None for lightning payment_tx: self.r().maker_payment.clone().unwrap().tx_hex.0, time_lock: self.maker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_maker_coin_htlc_pub.to_vec(), @@ -1357,6 +1370,7 @@ impl TakerSwap { let mut swap_events = vec![TakerSwapEvent::TakerPaymentSent(tx_ident)]; if self.ctx.use_watchers() { let preimage_fut = self.taker_coin.create_taker_spends_maker_payment_preimage( + // Todo: remove unwrap since maker_payment is None for lightning &self.r().maker_payment.as_ref().unwrap().tx_hex, self.maker_payment_lock.load(Ordering::Relaxed) as u32, self.r().other_maker_coin_htlc_pub.as_slice(), @@ -1403,6 +1417,7 @@ impl TakerSwap { )); } } + // Todo: should not be sent for lightning since tx_hex is empty anyways let msg = SwapMsg::TakerPayment(tx_hex); let send_abort_handle = broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); @@ -1483,6 +1498,7 @@ impl TakerSwap { ])); } let spend_fut = self.maker_coin.send_taker_spends_maker_payment( + // Todo: remove unwrap since maker_payment is None for lightning &self.r().maker_payment.clone().unwrap().tx_hex, self.maker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_maker_coin_htlc_pub, @@ -1684,6 +1700,7 @@ impl TakerSwap { let maker_payment = match &self.r().maker_payment { Some(tx) => tx.tx_hex.0.clone(), + // Todo: for lightning maker_payment can be None None => return ERR!("No info about maker payment, swap is not recoverable"), }; From e8ce302ba0fef371904ea6c5918162786c6dc373 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 4 Oct 2022 20:44:01 +0200 Subject: [PATCH 20/36] Some refactors and rollbacks --- mm2src/coins/eth.rs | 6 +- mm2src/coins/eth/eth_tests.rs | 4 +- mm2src/coins/lightning.rs | 25 +-- mm2src/coins/lp_coins.rs | 7 +- mm2src/coins/my_tx_history_v2.rs | 3 +- mm2src/coins/qrc20.rs | 4 +- mm2src/coins/solana.rs | 22 +-- mm2src/coins/solana/spl.rs | 26 +-- mm2src/coins/tendermint/tendermint_coin.rs | 14 +- mm2src/coins/test_coin.rs | 22 +-- mm2src/coins/utxo.rs | 6 +- mm2src/coins/utxo/bch.rs | 4 +- mm2src/coins/utxo/qtum.rs | 4 +- mm2src/coins/utxo/slp.rs | 2 +- mm2src/coins/utxo/utxo_common.rs | 5 +- mm2src/coins/utxo/utxo_standard.rs | 4 +- mm2src/coins/utxo/utxo_tests.rs | 6 +- mm2src/coins/z_coin.rs | 8 +- mm2src/coins/z_coin/z_coin_native_tests.rs | 9 +- mm2src/mm2_main/src/docker_tests.rs | 36 ++-- .../mm2_main/src/docker_tests/qrc20_tests.rs | 46 ++--- mm2src/mm2_main/src/lp_swap.rs | 16 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 135 +++++++------- .../src/lp_swap/recreate_swap_data.rs | 16 +- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 6 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 165 +++++++++--------- 26 files changed, 307 insertions(+), 294 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index b265f9b925..11cd86e5e3 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1428,9 +1428,9 @@ impl MarketCoinOps for EthCoin { Box::new(fut.boxed().compat()) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { signed_eth_tx_from_bytes(bytes) - .map(|tx| Some(tx.into())) + .map(TransactionEnum::from) .map_to_mm(TxMarshalingErr::InvalidInput) } @@ -3417,7 +3417,7 @@ pub fn wei_from_big_decimal(amount: &BigDecimal, decimals: u8) -> NumConversResu } impl Transaction for SignedEthTx { - fn tx_hex(&self) -> Vec { rlp::encode(self).to_vec() } + fn tx_hex(&self) -> Option> { Some(rlp::encode(self).to_vec()) } fn tx_hash(&self) -> BytesJson { self.hash.to_vec().into() } } diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index f5edfe2875..81bdbf1570 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -265,7 +265,7 @@ fn send_and_refund_erc20_payment() { let refund = coin .send_maker_refunds_payment( - &payment.tx_hex(), + &payment.tx_hex().unwrap(), (now_ms() / 1000) as u32 - 200, &DEX_FEE_ADDR_RAW_PUBKEY, &[1; 20], @@ -334,7 +334,7 @@ fn send_and_refund_eth_payment() { let refund = coin .send_maker_refunds_payment( - &payment.tx_hex(), + &payment.tx_hex().unwrap(), (now_ms() / 1000) as u32 - 200, &DEX_FEE_ADDR_RAW_PUBKEY, &[1; 20], diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 3d5da538ac..5ca8c9aa6d 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -143,13 +143,10 @@ pub(crate) enum PaymentError { Keysend(String), } -impl Transaction for PaymentInfo { - fn tx_hex(&self) -> Vec { - // Todo: should this be an empty vec or an option or the payment_hash - Vec::new() - } +impl Transaction for PaymentHash { + fn tx_hex(&self) -> Option> { None } - fn tx_hash(&self) -> BytesJson { self.payment_hash.0.to_vec().into() } + fn tx_hash(&self) -> BytesJson { self.0.to_vec().into() } } impl LightningCoin { @@ -450,7 +447,7 @@ impl SwapOps for LightningCoin { let fut = async move { let payment = try_tx_s!(this.pay_invoice(invoice).await); // Todo: are there more steps after pay_invoice?? - Ok(payment.into()) + Ok(payment.payment_hash.into()) }; Box::new(fut.boxed().compat()) } @@ -472,7 +469,7 @@ impl SwapOps for LightningCoin { let fut = async move { let payment = try_tx_s!(this.pay_invoice(invoice).await); // Todo: are there more steps after pay_invoice?? - Ok(payment.into()) + Ok(payment.payment_hash.into()) }; Box::new(fut.boxed().compat()) } @@ -757,8 +754,16 @@ impl MarketCoinOps for LightningCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result, MmError> { - Ok(None) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + // Todo: this block of code is repeated, maybe make it a function + let payment_hash_length = bytes.len(); + if payment_hash_length != 32 { + let error = format!("Invalid payment hash length {}", payment_hash_length); + return Err(TxMarshalingErr::InvalidInput(error).into()); + } + let mut payment_hash = [b' '; 32]; + payment_hash.copy_from_slice(bytes); + Ok(TransactionEnum::LightningPayment(PaymentHash(payment_hash))) } fn current_block(&self) -> Box + Send> { Box::new(futures01::future::ok(0)) } diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 038f817e54..4a2d6b5a43 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -66,8 +66,8 @@ use utxo_signer::with_key_pair::UtxoSignWithKeyPairError; cfg_native! { use crate::lightning::LightningCoin; - use crate::lightning::ln_db::PaymentInfo as LightningPayment; use crate::lightning::ln_conf::PlatformCoinConfirmationTargets; + use ::lightning::ln::PaymentHash as LightningPayment; use async_std::fs; use futures::AsyncWriteExt; use lightning_invoice::{Invoice, ParseOrSemanticError}; @@ -347,7 +347,7 @@ pub enum UnexpectedDerivationMethod { pub trait Transaction: fmt::Debug + 'static { /// Raw transaction bytes of the transaction - fn tx_hex(&self) -> Vec; + fn tx_hex(&self) -> Option>; /// Serializable representation of tx hash for displaying purpose fn tx_hash(&self) -> BytesJson; } @@ -705,6 +705,7 @@ pub trait MarketCoinOps { /// Receives raw transaction bytes as input and returns tx hash in hexadecimal format fn send_raw_tx_bytes(&self, tx: &[u8]) -> Box + Send>; + // Todo: If channel is closed for lightning we might need to wait for confirmations, should be done in the next PRs, move this note to a better place fn wait_for_confirmations( &self, tx: &[u8], @@ -722,7 +723,7 @@ pub trait MarketCoinOps { swap_contract_address: &Option, ) -> TransactionFut; - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError>; + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result>; fn current_block(&self) -> Box + Send>; diff --git a/mm2src/coins/my_tx_history_v2.rs b/mm2src/coins/my_tx_history_v2.rs index 7e7c97e616..c98466c5d5 100644 --- a/mm2src/coins/my_tx_history_v2.rs +++ b/mm2src/coins/my_tx_history_v2.rs @@ -222,7 +222,8 @@ impl<'a, Addr: Clone + DisplayAddress + Eq + std::hash::Hash, Tx: Transaction> T TransactionDetails { coin: self.coin, - tx_hex: self.tx.tx_hex().into(), + // Todo: remove unwrap, although it should be safe here + tx_hex: self.tx.tx_hex().unwrap().into(), tx_hash: tx_hash.to_tx_hash(), from, to, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 4547c5b91e..1b648a696d 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1168,8 +1168,8 @@ impl MarketCoinOps for Qrc20Coin { Box::new(fut.boxed().compat()) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index a4dec2faac..079799d503 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -432,7 +432,7 @@ impl MarketCoinOps for SolanaCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Solana yet.".to_string(), )) @@ -484,11 +484,11 @@ impl SwapOps for SolanaCoin { fn send_maker_spends_taker_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - taker_pub: &[u8], - secret: &[u8], - swap_contract_address: &Option, + _taker_payment_tx: &[u8], + _time_lock: u32, + _taker_pub: &[u8], + _secret: &[u8], + _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> TransactionFut { unimplemented!() @@ -523,11 +523,11 @@ impl SwapOps for SolanaCoin { fn send_taker_refunds_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - maker_pub: &[u8], - secret_hash: &[u8], - swap_contract_address: &Option, + _taker_payment_tx: &[u8], + _time_lock: u32, + _maker_pub: &[u8], + _secret_hash: &[u8], + _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> TransactionFut { unimplemented!() diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 5fc84d437b..083208d2cf 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -270,7 +270,7 @@ impl MarketCoinOps for SplToken { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Spl yet.".to_string(), )) @@ -318,12 +318,12 @@ impl SwapOps for SplToken { fn send_maker_spends_taker_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - taker_pub: &[u8], - secret: &[u8], - swap_contract_address: &Option, - swap_unique_data: &[u8], + _taker_payment_tx: &[u8], + _time_lock: u32, + _taker_pub: &[u8], + _secret: &[u8], + _swap_contract_address: &Option, + _swap_unique_data: &[u8], ) -> TransactionFut { unimplemented!() } @@ -357,12 +357,12 @@ impl SwapOps for SplToken { fn send_taker_refunds_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - maker_pub: &[u8], - secret_hash: &[u8], - swap_contract_address: &Option, - swap_unique_data: &[u8], + _taker_payment_tx: &[u8], + _time_lock: u32, + _maker_pub: &[u8], + _secret_hash: &[u8], + _swap_contract_address: &Option, + _swap_unique_data: &[u8], ) -> TransactionFut { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index f9a6428f9f..3519d355dc 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -598,7 +598,7 @@ impl MarketCoinOps for TendermintCoin { todo!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Tendermint yet.".to_string(), )) @@ -692,12 +692,12 @@ impl SwapOps for TendermintCoin { fn send_taker_refunds_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - maker_pub: &[u8], - secret_hash: &[u8], - swap_contract_address: &Option, - swap_unique_data: &[u8], + _taker_payment_tx: &[u8], + _time_lock: u32, + _maker_pub: &[u8], + _secret_hash: &[u8], + _swap_contract_address: &Option, + _swap_unique_data: &[u8], ) -> TransactionFut { todo!() } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 46f79a129c..dff56f4f30 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -80,7 +80,7 @@ impl MarketCoinOps for TestCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Test coin yet.".to_string(), )) @@ -129,11 +129,11 @@ impl SwapOps for TestCoin { fn send_maker_spends_taker_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - taker_pub: &[u8], - secret: &[u8], - swap_contract_address: &Option, + _taker_payment_tx: &[u8], + _time_lock: u32, + _taker_pub: &[u8], + _secret: &[u8], + _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> TransactionFut { unimplemented!() @@ -168,11 +168,11 @@ impl SwapOps for TestCoin { fn send_taker_refunds_payment( &self, - taker_payment_tx: &[u8], - time_lock: u32, - maker_pub: &[u8], - secret_hash: &[u8], - swap_contract_address: &Option, + _taker_payment_tx: &[u8], + _time_lock: u32, + _maker_pub: &[u8], + _secret_hash: &[u8], + _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> TransactionFut { unimplemented!() diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 59bdf81960..6cf1ff5fc3 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -165,11 +165,11 @@ fn get_special_folder_path() -> PathBuf { fn get_special_folder_path() -> PathBuf { panic!("!windows") } impl Transaction for UtxoTx { - fn tx_hex(&self) -> Vec { + fn tx_hex(&self) -> Option> { if self.has_witness() { - serialize_with_flags(self, SERIALIZE_TRANSACTION_WITNESS).into() + Some(serialize_with_flags(self, SERIALIZE_TRANSACTION_WITNESS).into()) } else { - serialize(self).into() + Some(serialize(self).into()) } } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index c133969136..3c8ec5b1aa 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1145,8 +1145,8 @@ impl MarketCoinOps for BchCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index d1675c41ef..97bf34ed1c 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -831,8 +831,8 @@ impl MarketCoinOps for QtumCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 05cc9c05df..ec085a2796 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1149,7 +1149,7 @@ impl MarketCoinOps for SlpToken { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { self.platform_coin.tx_enum_from_bytes(bytes) } diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index d2f7265675..131bf88b4e 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2092,12 +2092,13 @@ pub fn wait_for_output_spend( pub fn tx_enum_from_bytes(coin: &UtxoCoinFields, bytes: &[u8]) -> Result> { let mut transaction: UtxoTx = deserialize(bytes).map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string()))?; - let serialized_length = transaction.tx_hex().len(); + // Todo: remove unwrap, although it's safe to use here + let serialized_length = transaction.tx_hex().unwrap().len(); if bytes.len() != serialized_length { return MmError::err(TxMarshalingErr::CrossCheckFailed(format!( "Expected '{}' lenght of the serialized transaction, found '{}'", bytes.len(), - transaction.tx_hex().len() + serialized_length ))); } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 8531197a57..1ba946ef42 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -592,8 +592,8 @@ impl MarketCoinOps for UtxoStandardCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { - utxo_common::tx_enum_from_bytes(self.as_ref(), bytes).map(Some) + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + utxo_common::tx_enum_from_bytes(self.as_ref(), bytes) } fn current_block(&self) -> Box + Send> { diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index 0fb8219b18..bcc0910483 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -2485,7 +2485,7 @@ fn test_validate_fee_wrong_sender() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Electrum(rpc_client), None, false); // https://morty.explorer.dexstats.info/tx/fe4b0e1c4537e22f2956b5b74513fc936ebd87ada21513e850899cb07a45d475 let tx_bytes = hex::decode("0400008085202f890199cc492c24cc617731d13cff0ef22e7b0c277a64e7368a615b46214424a1c894020000006a473044022071edae37cf518e98db3f7637b9073a7a980b957b0c7b871415dbb4898ec3ebdc022031b402a6b98e64ffdf752266449ca979a9f70144dba77ed7a6a25bfab11648f6012103ad6f89abc2e5beaa8a3ac28e22170659b3209fe2ddf439681b4b8f31508c36faffffffff0202290200000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac8a96e70b000000001976a914d55f0df6cb82630ad21a4e6049522a6f2b6c9d4588ac8afb2c60000000000000000000000000000000").unwrap(); - let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap().unwrap(); + let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap(); let amount: BigDecimal = "0.0014157".parse().unwrap(); let validate_err = coin .validate_fee( @@ -2511,7 +2511,7 @@ fn test_validate_fee_min_block() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Electrum(rpc_client), None, false); // https://morty.explorer.dexstats.info/tx/fe4b0e1c4537e22f2956b5b74513fc936ebd87ada21513e850899cb07a45d475 let tx_bytes = hex::decode("0400008085202f890199cc492c24cc617731d13cff0ef22e7b0c277a64e7368a615b46214424a1c894020000006a473044022071edae37cf518e98db3f7637b9073a7a980b957b0c7b871415dbb4898ec3ebdc022031b402a6b98e64ffdf752266449ca979a9f70144dba77ed7a6a25bfab11648f6012103ad6f89abc2e5beaa8a3ac28e22170659b3209fe2ddf439681b4b8f31508c36faffffffff0202290200000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac8a96e70b000000001976a914d55f0df6cb82630ad21a4e6049522a6f2b6c9d4588ac8afb2c60000000000000000000000000000000").unwrap(); - let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap().unwrap(); + let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap(); let amount: BigDecimal = "0.0014157".parse().unwrap(); let sender_pub = hex::decode("03ad6f89abc2e5beaa8a3ac28e22170659b3209fe2ddf439681b4b8f31508c36fa").unwrap(); let validate_err = coin @@ -2539,7 +2539,7 @@ fn test_validate_fee_bch_70_bytes_signature() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Electrum(rpc_client), None, false); // https://blockchair.com/bitcoin-cash/transaction/ccee05a6b5bbc6f50d2a65a5a3a04690d3e2d81082ad57d3ab471189f53dd70d let tx_bytes = hex::decode("0100000002cae89775f264e50f14238be86a7184b7f77bfe26f54067b794c546ec5eb9c91a020000006b483045022100d6ed080f722a0637a37552382f462230cc438984bc564bdb4b7094f06cfa38fa022062304a52602df1fbb3bebac4f56e1632ad456f62d9031f4983f07e546c8ec4d8412102ae7dc4ef1b49aadeff79cfad56664105f4d114e1716bc4f930cb27dbd309e521ffffffff11f386a6fe8f0431cb84f549b59be00f05e78f4a8a926c5e023a0d5f9112e8200000000069463043021f17eb93ed20a6f2cd357eabb41a4ec6329000ddc6d5b42ecbe642c5d41b206a022026bc4920c4ce3af751283574baa8e4a3efd4dad0d8fe6ba3ddf5d75628d36fda412102ae7dc4ef1b49aadeff79cfad56664105f4d114e1716bc4f930cb27dbd309e521ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac57481c00000000001976a914bac11ce4cd2b1df2769c470d09b54f86df737e3c88ac035b4a60").unwrap(); - let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap().unwrap(); + let taker_fee_tx = coin.tx_enum_from_bytes(&tx_bytes).unwrap(); let amount: BigDecimal = "0.0001".parse().unwrap(); let sender_pub = hex::decode("02ae7dc4ef1b49aadeff79cfad56664105f4d114e1716bc4f930cb27dbd309e521").unwrap(); coin.validate_fee(&taker_fee_tx, &sender_pub, &*DEX_FEE_ADDR_RAW_PUBKEY, &amount, 0, &[]) diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 9769d39b60..b7e9bf537f 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -173,10 +173,10 @@ pub struct ZCoinFields { } impl Transaction for ZTransaction { - fn tx_hex(&self) -> Vec { + fn tx_hex(&self) -> Option> { let mut hex = Vec::with_capacity(1024); self.write(&mut hex).expect("Writing should not fail"); - hex + Some(hex) } fn tx_hash(&self) -> BytesJson { @@ -995,9 +995,9 @@ impl MarketCoinOps for ZCoin { ) } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result, MmError> { + fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { ZTransaction::read(bytes) - .map(|tx| Some(tx.into())) + .map(TransactionEnum::from) .map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string())) } diff --git a/mm2src/coins/z_coin/z_coin_native_tests.rs b/mm2src/coins/z_coin/z_coin_native_tests.rs index 7c51a21ec6..fca156d0f7 100644 --- a/mm2src/coins/z_coin/z_coin_native_tests.rs +++ b/mm2src/coins/z_coin/z_coin_native_tests.rs @@ -50,7 +50,14 @@ fn zombie_coin_send_and_refund_maker_payment() { println!("swap tx {}", hex::encode(&tx.tx_hash().0)); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex(), lock_time, &*taker_pub, &secret_hash, &priv_key, &None) + .send_maker_refunds_payment( + &tx.tx_hex().unwrap(), + lock_time, + &*taker_pub, + &secret_hash, + &priv_key, + &None, + ) .wait() .unwrap(); println!("refund tx {}", hex::encode(&refund_tx.tx_hash().0)); diff --git a/mm2src/mm2_main/src/docker_tests.rs b/mm2src/mm2_main/src/docker_tests.rs index 93b47bd520..7c4ecef87e 100644 --- a/mm2src/mm2_main/src/docker_tests.rs +++ b/mm2src/mm2_main/src/docker_tests.rs @@ -360,7 +360,7 @@ mod docker_tests { .wait() .unwrap(); self.coin - .wait_for_confirmations(&slp_genesis_tx.tx_hex(), 1, false, now_ms() / 1000 + 30, 1) + .wait_for_confirmations(&slp_genesis_tx.tx_hex().unwrap(), 1, false, now_ms() / 1000 + 30, 1) .wait() .unwrap(); @@ -374,7 +374,7 @@ mod docker_tests { let tx = block_on(adex_slp.send_slp_outputs(slp_outputs)).unwrap(); self.coin - .wait_for_confirmations(&tx.tx_hex(), 1, false, now_ms() / 1000 + 30, 1) + .wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, now_ms() / 1000 + 30, 1) .wait() .unwrap(); *SLP_TOKEN_OWNERS.lock().unwrap() = slp_privkeys; @@ -398,16 +398,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_taker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); @@ -415,7 +415,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex(), + tx: &tx.tx_hex().unwrap(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -455,16 +455,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); @@ -472,7 +472,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex(), + tx: &tx.tx_hex().unwrap(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -504,16 +504,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); let spend_tx = coin - .send_maker_spends_taker_payment(&tx.tx_hex(), time_lock, my_pubkey, &secret, &None, &[]) + .send_maker_spends_taker_payment(&tx.tx_hex().unwrap(), time_lock, my_pubkey, &secret, &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&spend_tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&spend_tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); @@ -521,7 +521,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &*dhash160(&secret), - tx: &tx.tx_hex(), + tx: &tx.tx_hex().unwrap(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -553,16 +553,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); let spend_tx = coin - .send_taker_spends_maker_payment(&tx.tx_hex(), time_lock, my_pubkey, &secret, &None, &[]) + .send_taker_spends_maker_payment(&tx.tx_hex().unwrap(), time_lock, my_pubkey, &secret, &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&spend_tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&spend_tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); @@ -570,7 +570,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &*dhash160(&secret), - tx: &tx.tx_hex(), + tx: &tx.tx_hex().unwrap(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], diff --git a/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs index 4973246654..3984b9be1b 100644 --- a/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs @@ -189,7 +189,7 @@ fn test_taker_spends_maker_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -226,7 +226,7 @@ fn test_taker_spends_maker_payment() { .wait() .unwrap(); let spend_tx_hash = spend.tx_hash(); - let spend_tx_hex = spend.tx_hex(); + let spend_tx_hex = spend.tx_hex().unwrap(); log!("Taker spends tx: {:?}", spend_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -282,7 +282,7 @@ fn test_maker_spends_taker_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Taker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -319,7 +319,7 @@ fn test_maker_spends_taker_payment() { .wait() .unwrap(); let spend_tx_hash = spend.tx_hash(); - let spend_tx_hex = spend.tx_hex(); + let spend_tx_hex = spend.tx_hex().unwrap(); log!("Maker spends tx: {:?}", spend_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -364,7 +364,7 @@ fn test_maker_refunds_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -390,7 +390,7 @@ fn test_maker_refunds_payment() { .wait() .unwrap(); let refund_tx_hash = refund.tx_hash(); - let refund_tx_hex = refund.tx_hex(); + let refund_tx_hex = refund.tx_hex().unwrap(); log!("Maker refunds payment: {:?}", refund_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -426,7 +426,7 @@ fn test_taker_refunds_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Taker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -452,7 +452,7 @@ fn test_taker_refunds_payment() { .wait() .unwrap(); let refund_tx_hash = refund.tx_hash(); - let refund_tx_hex = refund.tx_hex(); + let refund_tx_hex = refund.tx_hex().unwrap(); log!("Taker refunds payment: {:?}", refund_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -485,7 +485,7 @@ fn test_check_if_my_payment_sent() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 2; @@ -537,7 +537,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -561,7 +561,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { .wait() .unwrap(); let spend_tx_hash = spend.tx_hash(); - let spend_tx_hex = spend.tx_hex(); + let spend_tx_hex = spend.tx_hex().unwrap(); log!("Taker spends tx: {:?}", spend_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -608,7 +608,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -632,7 +632,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { .wait() .unwrap(); let refund_tx_hash = refund.tx_hash(); - let refund_tx_hex = refund.tx_hex(); + let refund_tx_hex = refund.tx_hex().unwrap(); log!("Maker refunds tx: {:?}", refund_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -679,7 +679,7 @@ fn test_search_for_swap_tx_spend_not_spent() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -731,7 +731,7 @@ fn test_wait_for_tx_spend() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex(); + let payment_tx_hex = payment.tx_hex().unwrap(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -1430,16 +1430,16 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); @@ -1447,7 +1447,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex(), + tx: &tx.tx_hex().unwrap(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -1471,16 +1471,16 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_taker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) .wait() .unwrap(); @@ -1488,7 +1488,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex(), + tx: &tx.tx_hex().unwrap(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 354df57d74..d938cf2db0 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -731,12 +731,21 @@ impl PaymentDataMsg { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct TransactionIdentifier { - /// Raw bytes of signed transaction in hexadecimal string, this should be sent as is to send_raw_transaction RPC to broadcast the transaction - tx_hex: BytesJson, + /// Raw bytes of signed transaction in hexadecimal string, this should be sent as is to send_raw_transaction RPC to broadcast the transaction. + /// Some payments like lightning payments don't have a tx_hex, this is why tx_hex is an option. + tx_hex: Option, /// Transaction hash in hexadecimal format tx_hash: BytesJson, } +impl TransactionIdentifier { + // Todo: rename this function + fn tx_hex(&self) -> BytesJson { + let tx_hash = self.tx_hash.clone(); + self.tx_hex.clone().unwrap_or(tx_hash) + } +} + pub fn my_swaps_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("MY") } pub fn my_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { my_swaps_dir(ctx).join(format!("{}.json", uuid)) } @@ -1181,7 +1190,8 @@ pub async fn recover_funds_of_swap(ctx: MmArc, req: Json) -> Result { self.w().payment_instructions = Some(instructions) }, - // Todo: should we have the hash for the taker fee for lightning here not None? - MakerSwapEvent::TakerFeeValidated(tx) => self.w().taker_fee = tx, + MakerSwapEvent::TakerFeeValidated(tx) => self.w().taker_fee = Some(tx), MakerSwapEvent::TakerFeeValidateFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentSent(tx) => self.w().maker_payment = Some(tx), MakerSwapEvent::MakerPaymentTransactionFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentDataSendFailed(err) => self.errors.lock().push(err), MakerSwapEvent::MakerPaymentWaitConfirmFailed(err) => self.errors.lock().push(err), - MakerSwapEvent::TakerPaymentReceived(tx) => self.w().taker_payment = tx, + MakerSwapEvent::TakerPaymentReceived(tx) => self.w().taker_payment = Some(tx), MakerSwapEvent::TakerPaymentWaitConfirmStarted => (), MakerSwapEvent::TakerPaymentValidatedAndConfirmed => { self.taker_payment_confirmed.store(true, Ordering::Relaxed) @@ -411,8 +410,10 @@ impl MakerSwap { } async fn get_my_payment_data(&self) -> Result> { - // Todo: this will send the payment hash of the maker_payment to the taker if maker is lightning, maybe it's not needed - let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); + // Todo: is it needed? the taker has the payment hash that was used in the invoice using the secret sent by the maker anyways, need to recheck this along with if secret_hash new implementation is needed?? + // Todo: recheck the other get_my_payment_data too + // If maker payment is a lightning payment the payment hash will be sent in the message + let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex().0; if let Some(instructions) = self .taker_coin .payment_instructions(&self.secret_hash(), &self.taker_amount) @@ -670,58 +671,47 @@ impl MakerSwap { }, }; - let fee_ident = match &taker_fee { - Some(fee) => { - let hash = fee.tx_hash(); - info!("Taker fee tx {:02x}", hash); - Some(TransactionIdentifier { - tx_hex: fee.tx_hex().into(), - tx_hash: hash, - }) - }, - None => { - // Todo: when implementing fee for lightning, should add a log for taker fee payment hash - None - }, - }; + let hash = taker_fee.tx_hash(); + info!("Taker fee tx {:02x}", hash); let taker_amount = MmNumber::from(self.taker_amount.clone()); let fee_amount = dex_fee_amount_from_taker_coin(&self.taker_coin, &self.r().data.maker_coin, &taker_amount); let other_taker_coin_htlc_pub = self.r().other_taker_coin_htlc_pub; let taker_coin_start_block = self.r().data.taker_coin_start_block; - // Todo: when implementing fee for lightning this will be changed - if let Some(fee) = &taker_fee { - let mut attempts = 0; - loop { - match self - .taker_coin - .validate_fee( - fee, - &*other_taker_coin_htlc_pub, - &DEX_FEE_ADDR_RAW_PUBKEY, - &fee_amount.clone().into(), - taker_coin_start_block, - self.uuid.as_bytes(), - ) - .compat() - .await - { - Ok(_) => break, - Err(err) => { - if attempts >= 3 { - return Ok((Some(MakerSwapCommand::Finish), vec![ - MakerSwapEvent::TakerFeeValidateFailed(ERRL!("{}", err).into()), - ])); - } else { - attempts += 1; - Timer::sleep(10.).await; - } - }, - }; - } + let mut attempts = 0; + loop { + match self + .taker_coin + .validate_fee( + &taker_fee, + &*other_taker_coin_htlc_pub, + &DEX_FEE_ADDR_RAW_PUBKEY, + &fee_amount.clone().into(), + taker_coin_start_block, + self.uuid.as_bytes(), + ) + .compat() + .await + { + Ok(_) => break, + Err(err) => { + if attempts >= 3 { + return Ok((Some(MakerSwapCommand::Finish), vec![ + MakerSwapEvent::TakerFeeValidateFailed(ERRL!("{}", err).into()), + ])); + } else { + attempts += 1; + Timer::sleep(10.).await; + } + }, + }; } + let fee_ident = TransactionIdentifier { + tx_hex: taker_fee.tx_hex().map(From::from), + tx_hash: hash, + }; swap_events.push(MakerSwapEvent::TakerFeeValidated(fee_ident)); Ok((Some(MakerSwapCommand::SendPayment), swap_events)) @@ -788,7 +778,7 @@ impl MakerSwap { info!("Maker payment tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; @@ -815,7 +805,7 @@ impl MakerSwap { let maker_payment_wait_confirm = self.r().data.started_at + (self.r().data.lock_duration * 2) / 5; let f = self.maker_coin.wait_for_confirmations( - &self.r().maker_payment.clone().unwrap().tx_hex, + &self.r().maker_payment.clone().unwrap().tx_hex(), self.r().data.maker_payment_confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), maker_payment_wait_confirm, @@ -868,19 +858,11 @@ impl MakerSwap { }, }; - let tx_ident = match taker_payment { - Some(tx) => { - let tx_hash = tx.tx_hash(); - info!("Taker payment tx {:02x}", tx_hash); - Some(TransactionIdentifier { - tx_hex: tx.tx_hex().into(), - tx_hash, - }) - }, - None => { - info!("Taker payment tx {:02x}", BytesJson::new(self.secret_hash())); - None - }, + let tx_hash = taker_payment.tx_hash(); + info!("Taker payment tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: taker_payment.tx_hex().map(From::from), + tx_hash, }; Ok((Some(MakerSwapCommand::ValidateTakerPayment), vec![ @@ -897,7 +879,7 @@ impl MakerSwap { let wait_f = self .taker_coin .wait_for_confirmations( - &self.r().taker_payment.clone().unwrap().tx_hex, + &self.r().taker_payment.clone().unwrap().tx_hex(), confirmations, self.r().data.taker_payment_requires_nota.unwrap_or(false), wait_taker_payment, @@ -916,7 +898,7 @@ impl MakerSwap { } let validate_input = ValidatePaymentInput { - payment_tx: self.r().taker_payment.clone().unwrap().tx_hex.0, + payment_tx: self.r().taker_payment.clone().unwrap().tx_hex().0, time_lock: self.taker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_taker_coin_htlc_pub.to_vec(), unique_swap_data: self.unique_swap_data(), @@ -964,7 +946,7 @@ impl MakerSwap { } let spend_fut = self.taker_coin.send_maker_spends_taker_payment( - &self.r().taker_payment.clone().unwrap().tx_hex, + &self.r().taker_payment.clone().unwrap().tx_hex(), self.taker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_taker_coin_htlc_pub, &self.r().data.secret.0, @@ -1009,7 +991,7 @@ impl MakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; @@ -1024,7 +1006,7 @@ impl MakerSwap { let confirmations = std::cmp::min(1, self.r().data.taker_payment_confirmations); let requires_nota = false; let wait_fut = self.taker_coin.wait_for_confirmations( - &self.r().taker_payment_spend.clone().unwrap().tx_hex, + &self.r().taker_payment_spend.clone().unwrap().tx_hex(), confirmations, requires_nota, self.wait_refund_until(), @@ -1067,7 +1049,7 @@ impl MakerSwap { } let spend_fut = self.maker_coin.send_maker_refunds_payment( - &self.r().maker_payment.clone().unwrap().tx_hex, + &self.r().maker_payment.clone().unwrap().tx_hex(), self.r().data.maker_payment_lock as u32, &*self.r().other_maker_coin_htlc_pub, self.secret_hash().as_slice(), @@ -1109,7 +1091,7 @@ impl MakerSwap { let tx_hash = transaction.tx_hash(); info!("Maker payment refund tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; @@ -1201,7 +1183,7 @@ impl MakerSwap { .taker_payment .clone() .ok_or(ERRL!("No info about taker payment, swap is not recoverable"))? - .tx_hex; + .tx_hex(); // have to do this because std::sync::RwLockReadGuard returned by r() is not Send, // so it can't be used across await @@ -1282,7 +1264,7 @@ impl MakerSwap { let maybe_maker_payment = self.r().maker_payment.clone(); let maker_payment = match maybe_maker_payment { - Some(tx) => tx.tx_hex.0.clone(), + Some(tx) => tx.tx_hex().0, None => { let maybe_maker_payment = try_s!( self.maker_coin @@ -1298,7 +1280,8 @@ impl MakerSwap { .await ); match maybe_maker_payment { - Some(tx) => tx.tx_hex(), + // Todo: remove this unwrap + Some(tx) => tx.tx_hex().unwrap(), None => return ERR!("Maker payment transaction was not found"), } }, @@ -1440,13 +1423,13 @@ pub enum MakerSwapEvent { Negotiated(TakerNegotiationData), NegotiateFailed(SwapError), MakerPaymentInstructionsReceived(PaymentInstructions), - TakerFeeValidated(Option), + TakerFeeValidated(TransactionIdentifier), TakerFeeValidateFailed(SwapError), MakerPaymentSent(TransactionIdentifier), MakerPaymentTransactionFailed(SwapError), MakerPaymentDataSendFailed(SwapError), MakerPaymentWaitConfirmFailed(SwapError), - TakerPaymentReceived(Option), + TakerPaymentReceived(TransactionIdentifier), TakerPaymentWaitConfirmStarted, TakerPaymentValidatedAndConfirmed, TakerPaymentValidateFailed(SwapError), 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 b2adb9a744..e52d034658 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -215,10 +215,9 @@ fn convert_taker_to_maker_events( // Todo: does the taker need to retrieve the invoice from here?? also look in maker_swap.rs for mirror TakerSwapEvent::MakerPaymentReceived(tx_ident) => { if let Some(taker_fee_ident) = taker_fee_ident.take() { - push_event!(MakerSwapEvent::TakerFeeValidated(Some(taker_fee_ident))); + push_event!(MakerSwapEvent::TakerFeeValidated(taker_fee_ident)); } - // Todo: remove unwrap, we can - push_event!(MakerSwapEvent::MakerPaymentSent(tx_ident.unwrap())) + push_event!(MakerSwapEvent::MakerPaymentSent(tx_ident)) }, TakerSwapEvent::MakerPaymentValidateFailed(_) | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) @@ -235,7 +234,7 @@ fn convert_taker_to_maker_events( return events; }, TakerSwapEvent::TakerPaymentSent(tx_ident) => { - push_event!(MakerSwapEvent::TakerPaymentReceived(Some(tx_ident))); + push_event!(MakerSwapEvent::TakerPaymentReceived(tx_ident)); // Please note we have not to push `TakerPaymentValidatedAndConfirmed` since we could actually decline it. push_event!(MakerSwapEvent::TakerPaymentWaitConfirmStarted); }, @@ -419,7 +418,7 @@ fn convert_maker_to_taker_events( }; match event { // Todo: remove unwrap by making tx_ident and enum - MakerSwapEvent::TakerFeeValidated(tx_ident) => push_event!(TakerSwapEvent::TakerFeeSent(tx_ident.unwrap())), + MakerSwapEvent::TakerFeeValidated(tx_ident) => push_event!(TakerSwapEvent::TakerFeeSent(tx_ident)), MakerSwapEvent::TakerFeeValidateFailed(_) => { push_event!(TakerSwapEvent::MakerPaymentValidateFailed(swap_error)); push_event!(TakerSwapEvent::Finished); @@ -428,7 +427,7 @@ fn convert_maker_to_taker_events( }, MakerSwapEvent::MakerPaymentSent(tx_ident) => { // Todo: check this if a new event is added for other side instructions, also look in the mirror maker_swap.rs - push_event!(TakerSwapEvent::MakerPaymentReceived(Some(tx_ident))); + push_event!(TakerSwapEvent::MakerPaymentReceived(tx_ident)); // Please note we have not to push `MakerPaymentValidatedAndConfirmed` since we could actually decline it. push_event!(TakerSwapEvent::MakerPaymentWaitConfirmStarted); }, @@ -443,8 +442,7 @@ fn convert_maker_to_taker_events( }, MakerSwapEvent::TakerPaymentReceived(tx_ident) => { push_event!(TakerSwapEvent::MakerPaymentValidatedAndConfirmed); - // Todo: remove unwrap by making tx_ident an enum - push_event!(TakerSwapEvent::TakerPaymentSent(tx_ident.clone().unwrap())); + push_event!(TakerSwapEvent::TakerPaymentSent(tx_ident.clone())); }, MakerSwapEvent::TakerPaymentValidateFailed(_) | MakerSwapEvent::TakerPaymentSpendFailed(_) @@ -455,7 +453,7 @@ fn convert_maker_to_taker_events( return events; }, MakerSwapEvent::TakerPaymentSpent(tx_ident) => { - let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex) { + let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()) { Ok(secret) => H256Json::from(secret.as_slice()), Err(e) => { push_event!(TakerSwapEvent::TakerPaymentWaitForSpendFailed(ERRL!("{}", e).into())); diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index e4e95b895c..368364c20f 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -271,13 +271,13 @@ impl Watcher { let tx_hash = tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: tx.tx_hex().into(), + tx_hex: tx.tx_hex().map(From::from), tx_hash, }; let secret = match self .taker_coin - .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex.0) + .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex()) { Ok(bytes) => H256Json::from(bytes.as_slice()), Err(e) => { @@ -324,7 +324,7 @@ impl Watcher { let tx_hash = transaction.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index c5cd5e6a41..635ae0dda7 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -571,7 +571,7 @@ pub enum TakerSwapEvent { TakerFeeSent(TransactionIdentifier), TakerFeeSendFailed(SwapError), TakerPaymentInstructionsReceived(PaymentInstructions), - MakerPaymentReceived(Option), + MakerPaymentReceived(TransactionIdentifier), MakerPaymentWaitConfirmStarted, MakerPaymentValidatedAndConfirmed, MakerPaymentValidateFailed(SwapError), @@ -722,7 +722,7 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentInstructionsReceived(instructions) => { self.w().payment_instructions = Some(instructions) }, - TakerSwapEvent::MakerPaymentReceived(tx) => self.w().maker_payment = tx, + TakerSwapEvent::MakerPaymentReceived(tx) => self.w().maker_payment = Some(tx), TakerSwapEvent::MakerPaymentWaitConfirmStarted => (), TakerSwapEvent::MakerPaymentValidatedAndConfirmed => { self.maker_payment_confirmed.store(true, Ordering::Relaxed) @@ -850,9 +850,8 @@ impl TakerSwap { } async fn get_my_payment_data(&self) -> Result> { - // Todo: this can be used to send the payment hash of the taker_fee to the maker if taker is lightning, if it's needed - // Todo: What about if the 2 coins are lightning do we need any of these messages?? - let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex.0.clone(); + // If taker fee is a lightning payment the payment hash will be sent in the message + let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex().0; let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); if let Some(instructions) = self @@ -1132,7 +1131,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker fee tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; @@ -1210,20 +1209,11 @@ impl TakerSwap { }, }; - let tx_ident = match maker_payment { - Some(tx) => { - let tx_hash = tx.tx_hash(); - info!("Got maker payment {:02x}", tx_hash); - Some(TransactionIdentifier { - tx_hex: tx.tx_hex().into(), - tx_hash, - }) - }, - None => { - // secret_hash is the hash for the lightning payment - info!("Got maker payment {:02x}", &self.r().secret_hash); - None - }, + let tx_hash = maker_payment.tx_hash(); + info!("Got maker payment {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: maker_payment.tx_hex().map(From::from), + tx_hash, }; swap_events.push(TakerSwapEvent::MakerPaymentReceived(tx_ident)); @@ -1236,8 +1226,7 @@ impl TakerSwap { info!("Before wait confirm"); let confirmations = self.r().data.maker_payment_confirmations; let f = self.maker_coin.wait_for_confirmations( - // Todo: remove unwrap since maker_payment is none for lightning - &self.r().maker_payment.clone().unwrap().tx_hex, + &self.r().maker_payment.clone().unwrap().tx_hex(), confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), self.r().data.maker_payment_wait, @@ -1253,8 +1242,7 @@ impl TakerSwap { info!("After wait confirm"); let validate_input = ValidatePaymentInput { - // Todo: remove unwrap since maker_payment is None for lightning - payment_tx: self.r().maker_payment.clone().unwrap().tx_hex.0, + payment_tx: self.r().maker_payment.clone().unwrap().tx_hex().0, time_lock: self.maker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_maker_coin_htlc_pub.to_vec(), secret_hash: self.r().secret_hash.0.to_vec(), @@ -1361,73 +1349,89 @@ impl TakerSwap { }; let tx_hash = transaction.tx_hash(); + let tx_hex = transaction.tx_hex().map(From::from); info!("Taker payment tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: tx_hex.clone(), tx_hash, }; let mut swap_events = vec![TakerSwapEvent::TakerPaymentSent(tx_ident)]; - if self.ctx.use_watchers() { - let preimage_fut = self.taker_coin.create_taker_spends_maker_payment_preimage( - // Todo: remove unwrap since maker_payment is None for lightning - &self.r().maker_payment.as_ref().unwrap().tx_hex, - self.maker_payment_lock.load(Ordering::Relaxed) as u32, - self.r().other_maker_coin_htlc_pub.as_slice(), - &self.r().secret_hash.0, - &self.unique_swap_data()[..], - ); - - match preimage_fut.compat().await { - Ok(preimage) => { - let watcher_data = self.create_watcher_data(transaction.tx_hex(), preimage.tx_hex()); - let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); - broadcast_swap_message( - &self.ctx, - watcher_topic(&self.r().data.taker_coin), - swpmsg_watcher, - &self.p2p_privkey, + // Watchers shouldn't be used with lightning payments for now + // Todo: refactor this and check if watcher can work in some cases with lightning + let maybe_maker_hex = self.r().maker_payment.as_ref().unwrap().tx_hex.clone(); + if let Some(maker_hex) = maybe_maker_hex { + if let Some(taker_hex) = tx_hex { + if self.ctx.use_watchers() { + let preimage_fut = self.taker_coin.create_taker_spends_maker_payment_preimage( + &maker_hex, + self.maker_payment_lock.load(Ordering::Relaxed) as u32, + self.r().other_maker_coin_htlc_pub.as_slice(), + &self.r().secret_hash.0, + &self.unique_swap_data()[..], ); - swap_events.push(TakerSwapEvent::WatcherMessageSent(Some(preimage.tx_hex()))) - }, - Err(e) => error!( + + match preimage_fut.compat().await { + Ok(preimage) => { + // Todo: revise if it's safe to unwrap here + let watcher_data = self.create_watcher_data(taker_hex.0, preimage.tx_hex().unwrap()); + let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); + broadcast_swap_message( + &self.ctx, + watcher_topic(&self.r().data.taker_coin), + swpmsg_watcher, + &self.p2p_privkey, + ); + // Todo: revise if it's safe to unwrap here + swap_events.push(TakerSwapEvent::WatcherMessageSent(Some(preimage.tx_hex().unwrap()))) + }, + Err(e) => error!( "The watcher message could not be sent, error creating the taker spends maker payment preimage: {}", e.get_plain_text_format() ), + } + } } } - Ok((Some(TakerSwapCommand::WaitForTakerPaymentSpend), swap_events)) } async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { - let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); + let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.clone(); let mut watcher_broadcast_abort_handle = None; - if self.ctx.use_watchers() { - let preimage_hex = self.r().taker_spends_maker_payment_preimage.clone(); - if let Some(preimage_hex) = preimage_hex { - let watcher_data = self.create_watcher_data(tx_hex.clone(), preimage_hex); - let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); - watcher_broadcast_abort_handle = Some(broadcast_swap_message_every( - self.ctx.clone(), - watcher_topic(&self.r().data.taker_coin), - swpmsg_watcher, - 600., - self.p2p_privkey, - )); + let mut send_abort_handle = None; + if let Some(hex) = tx_hex { + if self.ctx.use_watchers() { + let preimage_hex = self.r().taker_spends_maker_payment_preimage.clone(); + if let Some(preimage_hex) = preimage_hex { + let watcher_data = self.create_watcher_data(hex.0.clone(), preimage_hex); + let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); + watcher_broadcast_abort_handle = Some(broadcast_swap_message_every( + self.ctx.clone(), + watcher_topic(&self.r().data.taker_coin), + swpmsg_watcher, + 600., + self.p2p_privkey, + )); + } } + // Todo: The same should be done for MakerPayment (shouldn't be sent on some cases) + let msg = SwapMsg::TakerPayment(hex.0); + send_abort_handle = Some(broadcast_swap_message_every( + self.ctx.clone(), + swap_topic(&self.uuid), + msg, + 600., + self.p2p_privkey, + )); } - // Todo: should not be sent for lightning since tx_hex is empty anyways - let msg = SwapMsg::TakerPayment(tx_hex); - let send_abort_handle = - broadcast_swap_message_every(self.ctx.clone(), swap_topic(&self.uuid), msg, 600., self.p2p_privkey); let wait_duration = (self.r().data.lock_duration * 4) / 5; let wait_taker_payment = self.r().data.started_at + wait_duration; let wait_f = self .taker_coin .wait_for_confirmations( - &self.r().taker_payment.clone().unwrap().tx_hex, + &self.r().taker_payment.clone().unwrap().tx_hex(), self.r().data.taker_payment_confirmations, self.r().data.taker_payment_requires_nota.unwrap_or(false), wait_taker_payment, @@ -1446,7 +1450,7 @@ impl TakerSwap { } let f = self.taker_coin.wait_for_tx_spend( - &self.r().taker_payment.clone().unwrap().tx_hex, + &self.r().taker_payment.clone().unwrap().tx_hex(), self.r().data.taker_payment_lock, self.r().data.taker_coin_start_block, &self.r().data.taker_coin_swap_contract_address, @@ -1467,12 +1471,12 @@ impl TakerSwap { let tx_hash = tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: tx.tx_hex().into(), + tx_hex: tx.tx_hex().map(From::from), tx_hash, }; let secret = match self .taker_coin - .extract_secret(&self.r().secret_hash.0, &tx_ident.tx_hex.0) + .extract_secret(&self.r().secret_hash.0, &tx_ident.tx_hex()) { Ok(bytes) => H256Json::from(bytes.as_slice()), Err(e) => { @@ -1498,8 +1502,7 @@ impl TakerSwap { ])); } let spend_fut = self.maker_coin.send_taker_spends_maker_payment( - // Todo: remove unwrap since maker_payment is None for lightning - &self.r().maker_payment.clone().unwrap().tx_hex, + &self.r().maker_payment.clone().unwrap().tx_hex(), self.maker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_maker_coin_htlc_pub, &self.r().secret.0, @@ -1534,7 +1537,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; @@ -1564,7 +1567,7 @@ impl TakerSwap { } let refund_fut = self.taker_coin.send_taker_refunds_payment( - &self.r().taker_payment.clone().unwrap().tx_hex.0, + &self.r().taker_payment.clone().unwrap().tx_hex(), self.r().data.taker_payment_lock as u32, &*self.r().other_taker_coin_htlc_pub, &self.r().secret_hash.0, @@ -1590,6 +1593,7 @@ impl TakerSwap { }, }; + // Todo: check broadcast_p2p_tx_msg to not broadcast lightning payments broadcast_p2p_tx_msg( &self.ctx, tx_helper_topic(self.taker_coin.ticker()), @@ -1600,7 +1604,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().into(), + tx_hex: transaction.tx_hex().map(From::from), tx_hash, }; @@ -1699,8 +1703,7 @@ impl TakerSwap { } let maker_payment = match &self.r().maker_payment { - Some(tx) => tx.tx_hex.0.clone(), - // Todo: for lightning maker_payment can be None + Some(tx) => tx.tx_hex(), None => return ERR!("No info about maker payment, swap is not recoverable"), }; @@ -1753,7 +1756,7 @@ impl TakerSwap { let maybe_taker_payment = self.r().taker_payment.clone(); let taker_payment = match maybe_taker_payment { - Some(tx) => tx.tx_hex.0.clone(), + Some(tx) => tx.tx_hex().0, None => { let maybe_sent = try_s!( self.taker_coin @@ -1769,7 +1772,8 @@ impl TakerSwap { .await ); match maybe_sent { - Some(tx) => tx.tx_hex(), + // Todo: remove this unwrap + Some(tx) => tx.tx_hex().unwrap(), None => return ERR!("Taker payment is not found, swap is not recoverable"), } }, @@ -1830,7 +1834,10 @@ impl TakerSwap { Some(spend) => match spend { FoundSwapTxSpend::Spent(tx) => { check_maker_payment_is_not_spent!(); - let secret = try_s!(self.taker_coin.extract_secret(&self.r().secret_hash.0, &tx.tx_hex())); + let tx_hash = tx.tx_hash().0; + let secret = try_s!(self + .taker_coin + .extract_secret(&self.r().secret_hash.0, &tx.tx_hex().unwrap_or(tx_hash))); let fut = self.maker_coin.send_taker_spends_maker_payment( &maker_payment, From 474b0a6fb2c34f516632afee78da9abca5665d92 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 6 Oct 2022 22:28:41 +0200 Subject: [PATCH 21/36] impl more swap methods, do a unit test swap where taker is lightning --- mm2src/coins/eth.rs | 2 +- mm2src/coins/lightning.rs | 322 +++++++++++++++--- mm2src/coins/lightning/ln_db.rs | 2 + mm2src/coins/lightning/ln_events.rs | 29 +- mm2src/coins/lp_coins.rs | 3 +- mm2src/coins/qrc20.rs | 2 +- mm2src/coins/qrc20/qrc20_tests.rs | 4 +- .../rpc_command/lightning/generate_invoice.rs | 32 +- .../rpc_command/lightning/send_payment.rs | 3 +- mm2src/coins/solana.rs | 2 +- mm2src/coins/solana/spl.rs | 2 +- mm2src/coins/tendermint/tendermint_coin.rs | 2 +- mm2src/coins/test_coin.rs | 2 +- mm2src/coins/utxo/bch.rs | 2 +- mm2src/coins/utxo/qtum.rs | 2 +- mm2src/coins/utxo/rpc_clients.rs | 10 +- mm2src/coins/utxo/slp.rs | 2 +- mm2src/coins/utxo/utxo_standard.rs | 2 +- mm2src/coins/utxo/utxo_tests.rs | 2 +- mm2src/coins/z_coin.rs | 2 +- mm2src/mm2_main/src/docker_tests.rs | 2 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 8 +- .../src/lp_swap/recreate_swap_data.rs | 16 +- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 1 + mm2src/mm2_main/src/lp_swap/taker_swap.rs | 47 +-- .../mm2_main/src/mm2_tests/lightning_tests.rs | 82 ++++- 26 files changed, 442 insertions(+), 143 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 11cd86e5e3..aba3724eab 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1099,7 +1099,7 @@ impl SwapOps for EthCoin { .await } - fn extract_secret(&self, _secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, _secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { let unverified: UnverifiedTransaction = try_s!(rlp::decode(spend_tx)); let function = try_s!(SWAP_CONTRACT.function("receiverSpend")); let tokens = try_s!(function.decode_input(&unverified.data)); diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 5ca8c9aa6d..09ad781fab 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -14,12 +14,12 @@ use super::DerivationMethod; use crate::lightning::ln_utils::filter_channels; use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsigned}; -use crate::utxo::{sat_from_big_decimal, BlockchainNetwork}; +use crate::utxo::{sat_from_big_decimal, utxo_common, BlockchainNetwork}; use crate::{BalanceFut, CoinBalance, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, MyAddressError, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, RawTransactionError, RawTransactionFut, RawTransactionRequest, SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, - Transaction, TransactionEnum, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, + Transaction, TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateInstructionsErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, WithdrawFut, WithdrawRequest}; @@ -29,9 +29,10 @@ use bitcoin::hashes::Hash; use bitcoin_hashes::sha256::Hash as Sha256; use bitcrypto::ChecksumType; use bitcrypto::{dhash256, ripemd160}; -use common::executor::spawn; -use common::log::{LogOnError, LogState}; +use common::executor::{spawn, Timer}; +use common::log::{error, info, LogOnError, LogState}; use common::{async_blocking, log, now_ms, PagingOptionsEnum}; +use db_common::sqlite::rusqlite::Error as SqlError; use futures::{FutureExt, TryFutureExt}; use futures01::Future; use keys::{hash::H256, CompactSignature, KeyPair, Private, Public}; @@ -141,6 +142,12 @@ pub(crate) enum PaymentError { Invoice(String), #[display(fmt = "Keysend error: {}", _0)] Keysend(String), + #[display(fmt = "DB error {}", _0)] + DbError(String), +} + +impl From for PaymentError { + fn from(err: SqlError) -> PaymentError { PaymentError::DbError(err.to_string()) } } impl Transaction for PaymentHash { @@ -204,7 +211,7 @@ impl LightningCoin { }) .await?; - Ok(PaymentInfo { + let payment_info = PaymentInfo { payment_hash, payment_type, description, @@ -215,7 +222,9 @@ impl LightningCoin { status: HTLCStatus::Pending, created_at: (now_ms() / 1000) as i64, last_updated: (now_ms() / 1000) as i64, - }) + }; + self.db.add_or_update_payment_in_db(payment_info.clone()).await?; + Ok(payment_info) } pub(crate) async fn keysend( @@ -240,8 +249,7 @@ impl LightningCoin { let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner()); let payment_type = PaymentType::OutboundPayment { destination }; - - Ok(PaymentInfo { + let payment_info = PaymentInfo { payment_hash, payment_type, description: "".into(), @@ -252,7 +260,10 @@ impl LightningCoin { status: HTLCStatus::Pending, created_at: (now_ms() / 1000) as i64, last_updated: (now_ms() / 1000) as i64, - }) + }; + self.db.add_or_update_payment_in_db(payment_info.clone()).await?; + + Ok(payment_info) } pub(crate) async fn get_open_channels_by_filter( @@ -428,7 +439,11 @@ impl LightningCoin { #[async_trait] // Todo: Implement this when implementing swaps for lightning as it's is used only for swaps impl SwapOps for LightningCoin { - fn send_taker_fee(&self, _fee_addr: &[u8], _amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { unimplemented!() } + // Todo: This uses dummy data for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning + fn send_taker_fee(&self, _fee_addr: &[u8], _amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { + let fut = async move { Ok(TransactionEnum::LightningPayment(PaymentHash([1; 32]))) }; + Box::new(fut.boxed().compat()) + } fn send_maker_payment( &self, @@ -443,10 +458,9 @@ impl SwapOps for LightningCoin { let PaymentInstructions::Lightning(invoice) = payment_instructions .clone() .expect("payment_instructions can't be None"); - let this = self.clone(); + let coin = self.clone(); let fut = async move { - let payment = try_tx_s!(this.pay_invoice(invoice).await); - // Todo: are there more steps after pay_invoice?? + let payment = try_tx_s!(coin.pay_invoice(invoice).await); Ok(payment.payment_hash.into()) }; Box::new(fut.boxed().compat()) @@ -465,10 +479,9 @@ impl SwapOps for LightningCoin { let PaymentInstructions::Lightning(invoice) = payment_instructions .clone() .expect("payment_instructions can't be None"); - let this = self.clone(); + let coin = self.clone(); let fut = async move { - let payment = try_tx_s!(this.pay_invoice(invoice).await); - // Todo: are there more steps after pay_invoice?? + let payment = try_tx_s!(coin.pay_invoice(invoice).await); Ok(payment.payment_hash.into()) }; Box::new(fut.boxed().compat()) @@ -476,14 +489,32 @@ impl SwapOps for LightningCoin { fn send_maker_spends_taker_payment( &self, - _taker_payment_tx: &[u8], + taker_payment_tx: &[u8], _time_lock: u32, _taker_pub: &[u8], - _secret: &[u8], + secret: &[u8], _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> TransactionFut { - unimplemented!() + let payment_hash_length = taker_payment_tx.len(); + // Todo: do we need to do these checks every time (should be done at first only) + if payment_hash_length != 32 { + let error = format!("Invalid payment hash length {}", payment_hash_length); + return Box::new(futures01::future::err(TransactionErr::Plain(error))); + } + let mut payment_hash_array = [b' '; 32]; + payment_hash_array.copy_from_slice(taker_payment_tx); + let payment_hash = PaymentHash(payment_hash_array); + + let mut preimage = [b' '; 32]; + preimage.copy_from_slice(secret); + + let coin = self.clone(); + let fut = async move { + coin.channel_manager.claim_funds(PaymentPreimage(preimage)); + Ok(TransactionEnum::LightningPayment(payment_hash)) + }; + Box::new(fut.boxed().compat()) } fn create_taker_spends_maker_payment_preimage( @@ -537,6 +568,7 @@ impl SwapOps for LightningCoin { unimplemented!() } + // Todo: This validetes the dummy fee for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning fn validate_fee( &self, _fee_tx: &TransactionEnum, @@ -546,12 +578,54 @@ impl SwapOps for LightningCoin { _min_block_number: u64, _uuid: &[u8], ) -> Box + Send> { - unimplemented!() + Box::new(futures01::future::ok(())) } fn validate_maker_payment(&self, _input: ValidatePaymentInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_taker_payment(&self, _input: ValidatePaymentInput) -> ValidatePaymentFut<()> { unimplemented!() } + fn validate_taker_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentFut<()> { + let payment_hash_length = input.payment_tx.len(); + // Todo: do we need to do these checks every time (should be done at first only) + if payment_hash_length != 32 { + let error = format!("Invalid payment hash length {}", payment_hash_length); + return Box::new(futures01::future::err(MmError::new( + ValidatePaymentError::TxDeserializationError(error), + ))); + } + let mut payment_hash_array = [b' '; 32]; + payment_hash_array.copy_from_slice(&input.payment_tx); + let payment_hash = PaymentHash(payment_hash_array); + let payment_hex = hex::encode(payment_hash_array); + + let amt_msat = try_f!(sat_from_big_decimal(&input.amount, self.decimals())); + + let coin = self.clone(); + let fut = async move { + match coin.db.get_payment_from_db(payment_hash).await { + Ok(Some(payment)) => { + if payment.amt_msat != Some(amt_msat as i64) { + // Free the htlc to allow for this inbound liquidity to be used for other inbound payments + coin.channel_manager.fail_htlc_backwards(&payment_hash); + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Provided payment {} amount {:?} doesn't match required amount {}", + payment_hex, payment.amt_msat, amt_msat + ))); + } + // Todo: is more validation needed? (locktime, pub key etc.., status = received) + Ok(()) + }, + Ok(None) => MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( + "Payment {} should be found on the database", + payment_hex + ))), + Err(e) => MmError::err(ValidatePaymentError::InternalError(format!( + "Unable to retrieve payment {} from the database error: {}", + payment_hex, e + ))), + } + }; + Box::new(fut.boxed().compat()) + } fn watcher_validate_taker_payment( &self, @@ -560,6 +634,7 @@ impl SwapOps for LightningCoin { unimplemented!(); } + // Todo: This is None for now for the sake of swap P.O.C., this should be implemented probably in next PRs fn check_if_my_payment_sent( &self, _time_lock: u32, @@ -569,7 +644,7 @@ impl SwapOps for LightningCoin { _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> Box, Error = String> + Send> { - unimplemented!() + Box::new(futures01::future::ok(None)) } async fn search_for_swap_tx_spend_my( @@ -586,7 +661,32 @@ impl SwapOps for LightningCoin { unimplemented!() } - fn extract_secret(&self, _secret_hash: &[u8], _spend_tx: &[u8]) -> Result, String> { unimplemented!() } + // Todo: if the secret or preimage is part of the TransactionEnum, there is no need for more calls to db (also if paymentinfo is in the enum) + // Todo: also repeated code + async fn extract_secret(&self, _secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + let payment_hash_length = spend_tx.len(); + // Todo: do we need to do these checks every time (should be done at first only) + if payment_hash_length != 32 { + return ERR!("Invalid payment hash length {}", payment_hash_length); + } + let mut payment_hash_array = [b' '; 32]; + payment_hash_array.copy_from_slice(spend_tx); + let payment_hash = PaymentHash(payment_hash_array); + let payment_hex = hex::encode(payment_hash_array); + + return match self.db.get_payment_from_db(payment_hash).await { + Ok(Some(payment)) => match payment.preimage { + Some(preimage) => Ok(preimage.0.to_vec()), + None => ERR!("Preimage for payment {} should be found on the database", payment_hex), + }, + Ok(None) => ERR!("Payment {} should be found on the database", payment_hex), + Err(e) => ERR!( + "Unable to retrieve payment {} from the database error: {}", + payment_hex, + e + ), + }; + } fn negotiate_swap_contract_addr( &self, @@ -595,7 +695,10 @@ impl SwapOps for LightningCoin { Ok(None) } - fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } + // Todo: should node id and secret be used instead?? (maybe when implementing private swaps for lightning) + fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { + utxo_common::derive_htlc_key_pair(self.platform.coin.as_ref(), swap_unique_data) + } async fn payment_instructions( &self, @@ -623,9 +726,11 @@ impl SwapOps for LightningCoin { ) .await .map_to_mm(|e| PaymentInstructionsErr::LightningInvoiceErr(e.to_string()))?; - Ok(Some(hex::decode(invoice.to_string()).map_to_mm(|e| { - PaymentInstructionsErr::LightningInvoiceErr(e.to_string()) - })?)) + // Todo: revise this + Ok(Some(invoice.to_string().into_bytes())) + // Ok(Some(hex::decode(invoice.to_string()).map_to_mm(|e| { + // PaymentInstructionsErr::LightningInvoiceErr(e.to_string()) + // })?)) } fn validate_instructions( @@ -634,7 +739,8 @@ impl SwapOps for LightningCoin { secret_hash: &[u8], amount: BigDecimal, ) -> Result, MmError> { - let invoice = Invoice::from_str(&hex::encode(instructions))?; + // Todo: should I use from_utf8? do I need to check the instruction size (can any size be sent??) + let invoice = Invoice::from_str(&String::from_utf8_lossy(instructions))?; if (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) || (secret_hash.len() == 32 && invoice.payment_hash().as_inner() != secret_hash) { @@ -731,27 +837,127 @@ impl MarketCoinOps for LightningCoin { )) } - // Todo: Implement this when implementing swaps for lightning as it's is used mainly for swaps + // Todo: Add waiting for confirmation for sending logic, move this inside the code + // Todo: can this be avoided completely in lightning (just return () straight away) fn wait_for_confirmations( &self, - _tx: &[u8], + tx: &[u8], _confirmations: u64, _requires_nota: bool, - _wait_until: u64, - _check_every: u64, + wait_until: u64, + check_every: u64, ) -> Box + Send> { - unimplemented!() + let payment_hash_length = tx.len(); + // Todo: do we need to do these checks every time (should be done at first only) + if payment_hash_length != 32 { + return Box::new(futures01::future::err(ERRL!( + "Invalid payment hash length {}", + payment_hash_length + ))); + } + let mut payment_hash_array = [b' '; 32]; + payment_hash_array.copy_from_slice(tx); + let payment_hash = PaymentHash(payment_hash_array); + let payment_hex = hex::encode(payment_hash_array); + + let coin = self.clone(); + let fut = async move { + loop { + if now_ms() / 1000 > wait_until { + return ERR!( + "Waited too long until {} for payment {} to be received", + wait_until, + payment_hex + ); + } + + // Todo: add note about how this overuses the db + match coin.db.get_payment_from_db(payment_hash).await { + Ok(Some(_)) => { + // Todo: should check for status received after adding payment to db on create_invoice_for_hash (check for claimed after send_maker_spends_taker_payment and for received on validate_taker_payment, and pending or successful??? for taker swap wait for confirmation) + return Ok(()); + }, + // Todo: This should be also an error after adding payment to db on create_invoice_for_hash + Ok(None) => info!("Payment {} not received yet!", payment_hex), + // Todo: Maybe I should return a permanent error here and end the swap + Err(e) => error!( + "Error getting payment {} from db: {}, retrying in {} seconds", + payment_hex, e, check_every + ), + } + + Timer::sleep(check_every as f64).await; + } + }; + Box::new(fut.boxed().compat()) } - // Todo: Implement this when implementing swaps for lightning as it's is used mainly for swaps + // Todo: has similar code to wait_for_confirmation (should create a common function) fn wait_for_tx_spend( &self, - _transaction: &[u8], - _wait_until: u64, + transaction: &[u8], + wait_until: u64, _from_block: u64, _swap_contract_address: &Option, ) -> TransactionFut { - unimplemented!() + let payment_hash_length = transaction.len(); + // Todo: do we need to do these checks every time (should be done at first only) + if payment_hash_length != 32 { + let error = format!("Invalid payment hash length {}", payment_hash_length); + return Box::new(futures01::future::err(TransactionErr::Plain(error))); + } + let mut payment_hash_array = [b' '; 32]; + payment_hash_array.copy_from_slice(transaction); + let payment_hash = PaymentHash(payment_hash_array); + let payment_hex = hex::encode(payment_hash_array); + + let coin = self.clone(); + let fut = async move { + loop { + if now_ms() / 1000 > wait_until { + return Err(TransactionErr::Plain(format!( + "Waited too long until {} for payment {} to be spend", + wait_until, payment_hex + ))); + } + + match coin.db.get_payment_from_db(payment_hash).await { + Ok(Some(payment)) => { + match payment.status { + HTLCStatus::Pending => (), + HTLCStatus::Received => { + return Err(TransactionErr::Plain(format!( + "Payment {} has an invalid status of {} in the db", + payment_hex, payment.status + ))) + }, + HTLCStatus::Succeeded => return Ok(TransactionEnum::LightningPayment(payment_hash)), + // Todo: should I retry first before returning an error (return an error only if all paths failed, what about locktime?) + HTLCStatus::Failed => { + return Err(TransactionErr::Plain(format!( + "Lightning swap payment {} failed", + payment_hex + ))) + }, + } + }, + Ok(None) => { + return Err(TransactionErr::Plain(format!( + "Payment {} not found in DB", + payment_hex + ))) + }, + Err(e) => error!( + "Error getting payment {} from db: {}, retrying in 10 seconds", + payment_hex, e + ), + } + + // Todo: should this be less for lightning?? what about maker payment (takes time to spend) + Timer::sleep(10.).await; + } + }; + Box::new(fut.boxed().compat()) } fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { @@ -777,11 +983,11 @@ impl MarketCoinOps for LightningCoin { .to_string()) } - // Todo: Implement this when implementing swaps for lightning as it's is used only for swaps - fn min_tx_amount(&self) -> BigDecimal { unimplemented!() } + // Todo: Should depend on inbound_htlc_minimum_msat of the channel/s the payment will be sent through, 1 satoshi for now (1000 of the base unit of lightning which is msat) + fn min_tx_amount(&self) -> BigDecimal { big_decimal_from_sat(1000, self.decimals()) } - // Todo: Implement this when implementing swaps for lightning as it's is used only for order matching/swaps - fn min_trading_vol(&self) -> MmNumber { unimplemented!() } + // Todo: Equals to min_tx_amount for now (1 satoshi), should change this later + fn min_trading_vol(&self) -> MmNumber { self.min_tx_amount().into() } } #[async_trait] @@ -834,25 +1040,39 @@ impl MmCoin for LightningCoin { // Todo: Implement this when implementing swaps for lightning as it's is used only for swaps fn get_trade_fee(&self) -> Box + Send> { unimplemented!() } - // Todo: Implement this when implementing swaps for lightning as it's is used only for swaps + // Todo: This uses dummy data for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning async fn get_sender_trade_fee( &self, _value: TradePreimageValue, _stage: FeeApproxStage, ) -> TradePreimageResult { - unimplemented!() + Ok(TradeFee { + coin: self.ticker().to_owned(), + amount: Default::default(), + paid_from_trading_vol: false, + }) } - // Todo: Implement this when implementing swaps for lightning as it's is used only for swaps - fn get_receiver_trade_fee(&self, _stage: FeeApproxStage) -> TradePreimageFut { unimplemented!() } + // Todo: This uses dummy data for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning + fn get_receiver_trade_fee(&self, _stage: FeeApproxStage) -> TradePreimageFut { + Box::new(futures01::future::ok(TradeFee { + coin: self.ticker().to_owned(), + amount: Default::default(), + paid_from_trading_vol: false, + })) + } - // Todo: Implement this when implementing swaps for lightning as it's is used only for swaps + // Todo: This uses dummy data for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning async fn get_fee_to_send_taker_fee( &self, _dex_fee_amount: BigDecimal, _stage: FeeApproxStage, ) -> TradePreimageResult { - unimplemented!() + Ok(TradeFee { + coin: self.ticker().to_owned(), + amount: Default::default(), + paid_from_trading_vol: false, + }) } // Lightning payments are either pending, successful or failed. Once a payment succeeds there is no need to for confirmations @@ -869,11 +1089,11 @@ impl MmCoin for LightningCoin { fn mature_confirmations(&self) -> Option { None } - // Todo: Implement this when implementing order matching for lightning as it's is used only for order matching - fn coin_protocol_info(&self) -> Vec { unimplemented!() } + // Todo: This uses default data for now for the sake of swap P.O.C., this should be implemented probably when implementing order matching if it's needed + fn coin_protocol_info(&self) -> Vec { Vec::new() } - // Todo: Implement this when implementing order matching for lightning as it's is used only for order matching - fn is_coin_protocol_supported(&self, _info: &Option>) -> bool { unimplemented!() } + // Todo: This uses default data for now for the sake of swap P.O.C., this should be implemented probably when implementing order matching if it's needed + fn is_coin_protocol_supported(&self, _info: &Option>) -> bool { true } } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/mm2src/coins/lightning/ln_db.rs b/mm2src/coins/lightning/ln_db.rs index b3b4215491..2f1eb38b78 100644 --- a/mm2src/coins/lightning/ln_db.rs +++ b/mm2src/coins/lightning/ln_db.rs @@ -101,6 +101,7 @@ pub struct GetClosedChannelsResult { #[serde(rename_all = "lowercase")] pub enum HTLCStatus { Pending, + Received, Succeeded, Failed, } @@ -111,6 +112,7 @@ impl FromStr for HTLCStatus { fn from_str(s: &str) -> Result { match s { "Pending" => Ok(HTLCStatus::Pending), + "Received" => Ok(HTLCStatus::Received), "Succeeded" => Ok(HTLCStatus::Succeeded), "Failed" => Ok(HTLCStatus::Failed), _ => Err(FromSqlError::InvalidType), diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index 4f2a7417a7..5d60f91494 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -343,11 +343,32 @@ impl LightningEventHandler { let payment_preimage = match purpose { PaymentPurpose::InvoicePayment { payment_preimage, .. } => match payment_preimage { Some(preimage) => *preimage, + // This is a swap related payment since we don't have the preimage yet None => { - // Free the htlc immediately if we don't have the preimage required to claim the payment - // to allow for this inbound liquidity to be used for other inbound payments. - // Todo: this should be avoided for swaps except on refund - self.channel_manager.fail_htlc_backwards(payment_hash); + // Todo: preimage should be saved to db after it's received (after claim_payment and make payment successful) + let payment_info = PaymentInfo { + payment_hash: *payment_hash, + payment_type: PaymentType::InboundPayment, + // Todo: maybe add this to db on create_invoice_for_hash + description: "".into(), + preimage: None, + // Todo: maybe add this to db on create_invoice_for_hash + secret: None, + amt_msat: Some(received_amount as i64), + fee_paid_msat: None, + status: HTLCStatus::Received, + // Todo: maybe add this to db on create_invoice_for_hash + created_at: (now_ms() / 1000) as i64, + // Todo: maybe add this to db on create_invoice_for_hash + last_updated: (now_ms() / 1000) as i64, + }; + let db = self.db.clone(); + // Todo: Update this after stop-spawned-futures https://github.com/KomodoPlatform/atomicDEX-API/pull/1490 gets merged to dev + spawn(async move { + db.add_or_update_payment_in_db(payment_info) + .await + .error_log_with_msg("Unable to add payment information to DB!"); + }); return; }, }, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 4a2d6b5a43..95f68d5fcb 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -627,7 +627,8 @@ pub trait SwapOps { input: SearchForSwapTxSpendInput<'_>, ) -> Result, String>; - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String>; + // Todo: can be made sync again if the secret is made part of the enum, it will also reduce a call to the DB + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String>; /// Whether the refund transaction can be sent now /// For example: there are no additional conditions for ETH, but for some UTXO coins we should wait for diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 1b648a696d..373485b917 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1009,7 +1009,7 @@ impl SwapOps for Qrc20Coin { .await } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { self.extract_secret_impl(secret_hash, spend_tx) } diff --git a/mm2src/coins/qrc20/qrc20_tests.rs b/mm2src/coins/qrc20/qrc20_tests.rs index 464df0e3d2..556c8bbeaa 100644 --- a/mm2src/coins/qrc20/qrc20_tests.rs +++ b/mm2src/coins/qrc20/qrc20_tests.rs @@ -399,7 +399,7 @@ fn test_extract_secret() { // taker spent maker payment - d3f5dab4d54c14b3d7ed8c7f5c8cc7f47ccf45ce589fdc7cd5140a3c1c3df6e1 let tx_hex = hex::decode("01000000033f56ecafafc8602fde083ba868d1192d6649b8433e42e1a2d79ba007ea4f7abb010000006b48304502210093404e90e40d22730013035d31c404c875646dcf2fad9aa298348558b6d65ba60220297d045eac5617c1a3eddb71d4bca9772841afa3c4c9d6c68d8d2d42ee6de3950121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff9cac7fe90d597922a1d92e05306c2215628e7ea6d5b855bfb4289c2944f4c73a030000006b483045022100b987da58c2c0c40ce5b6ef2a59e8124ed4ef7a8b3e60c7fb631139280019bc93022069649bcde6fe4dd5df9462a1fcae40598488d6af8c324cd083f5c08afd9568be0121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff70b9870f2b0c65d220a839acecebf80f5b44c3ca4c982fa2fdc5552c037f5610010000006a473044022071b34dd3ebb72d29ca24f3fa0fc96571c815668d3b185dd45cc46a7222b6843f02206c39c030e618d411d4124f7b3e7ca1dd5436775bd8083a85712d123d933a51300121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff020000000000000000c35403a0860101284ca402ed292b806a1835a1b514ad643f2acdb5c8db6b6a9714accff3275ea0d79a3f23be8fd00000000000000000000000000000000000000000000000000000000001312d000101010101010101010101010101010101010101010101010101010101010101000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000009e032d4b0090a11dc40fe6c47601499a35d55fbb14ba8b71f3544b93e2f681f996da519a98ace0107ac2c02288d4010000001976a914783cf0be521101942da509846ea476e683aad83288ac0f047f5f").unwrap(); - let secret = coin.extract_secret(secret_hash, &tx_hex).unwrap(); + let secret = block_on(coin.extract_secret(secret_hash, &tx_hex)).unwrap(); assert_eq!(secret, expected_secret); } @@ -420,7 +420,7 @@ fn test_extract_secret_malicious() { let spend_tx = hex::decode("01000000022bc8299981ec0cea664cdf9df4f8306396a02e2067d6ac2d3770b34646d2bc2a010000006b483045022100eb13ef2d99ac1cd9984045c2365654b115dd8a7815b7fbf8e2a257f0b93d1592022060d648e73118c843e97f75fafc94e5ff6da70ec8ba36ae255f8c96e2626af6260121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffffd92a0a10ac6d144b36033916f67ae79889f40f35096629a5cd87be1a08f40ee7010000006b48304502210080cdad5c4770dfbeb760e215494c63cc30da843b8505e75e7bf9e8dad18568000220234c0b11c41bfbcdd50046c69059976aedabe17657fe43d809af71e9635678e20121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff030000000000000000c35403a0860101284ca402ed292b8620ad3b72361a5aeba5dffd333fb64750089d935a1ec974d6a91ef4f24ff6ba0000000000000000000000000000000000000000000000000000000001312d000202020202020202020202020202020202020202020202020202020202020202000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000009e032d4b0090a11dc40fe6c47601499a35d55fbb14ba8b71f3544b93e2f681f996da519a98ace0107ac20000000000000000c35403a0860101284ca402ed292b8620ad3b72361a5aeba5dffd333fb64750089d935a1ec974d6a91ef4f24ff6ba0000000000000000000000000000000000000000000000000000000001312d000101010101010101010101010101010101010101010101010101010101010101000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000009e032d4b0090a11dc40fe6c47601499a35d55fbb14ba8b71f3544b93e2f681f996da519a98ace0107ac2b8ea82d3010000001976a914783cf0be521101942da509846ea476e683aad83288ac735d855f").unwrap(); let expected_secret = &[1; 32]; let secret_hash = &*dhash160(expected_secret); - let actual = coin.extract_secret(secret_hash, &spend_tx); + let actual = block_on(coin.extract_secret(secret_hash, &spend_tx)); assert_eq!(actual, Ok(expected_secret.to_vec())); } diff --git a/mm2src/coins/rpc_command/lightning/generate_invoice.rs b/mm2src/coins/rpc_command/lightning/generate_invoice.rs index 0959cb0b9f..68aa8b2237 100644 --- a/mm2src/coins/rpc_command/lightning/generate_invoice.rs +++ b/mm2src/coins/rpc_command/lightning/generate_invoice.rs @@ -1,14 +1,10 @@ -use crate::lightning::ln_db::{HTLCStatus, LightningDB, PaymentInfo, PaymentType}; use crate::lightning::ln_p2p::connect_to_ln_node; use crate::lightning::DEFAULT_INVOICE_EXPIRY; use crate::{lp_coinfind_or_err, CoinFindError, H256Json, MmCoinEnum}; use bitcoin_hashes::Hash; use common::log::LogOnError; use common::{async_blocking, HttpStatusCode}; -use db_common::sqlite::rusqlite::Error as SqlError; -use gstuff::now_ms; use http::StatusCode; -use lightning::ln::PaymentHash; use lightning_invoice::utils::create_invoice_from_channelmanager; use lightning_invoice::{Invoice, SignOrCreationError}; use mm2_core::mm_ctx::MmArc; @@ -25,8 +21,6 @@ pub enum GenerateInvoiceError { NoSuchCoin(String), #[display(fmt = "Invoice signing or creation error: {}", _0)] SignOrCreationError(String), - #[display(fmt = "DB error {}", _0)] - DbError(String), } impl HttpStatusCode for GenerateInvoiceError { @@ -34,9 +28,7 @@ impl HttpStatusCode for GenerateInvoiceError { match self { GenerateInvoiceError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, GenerateInvoiceError::NoSuchCoin(_) => StatusCode::NOT_FOUND, - GenerateInvoiceError::SignOrCreationError(_) | GenerateInvoiceError::DbError(_) => { - StatusCode::INTERNAL_SERVER_ERROR - }, + GenerateInvoiceError::SignOrCreationError(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } @@ -53,10 +45,6 @@ impl From for GenerateInvoiceError { fn from(e: SignOrCreationError) -> Self { GenerateInvoiceError::SignOrCreationError(e.to_string()) } } -impl From for GenerateInvoiceError { - fn from(err: SqlError) -> GenerateInvoiceError { GenerateInvoiceError::DbError(err.to_string()) } -} - #[derive(Deserialize)] pub struct GenerateInvoiceRequest { pub coin: String, @@ -108,23 +96,9 @@ pub async fn generate_invoice( }) .await?; - // Todo: Should remove adding payment to db step since the preimage can be recreated from the keymanager and the invoice secret (Do I need to check that received amount equals the requested amount?) - let payment_hash = invoice.payment_hash().into_inner(); - let payment_info = PaymentInfo { - payment_hash: PaymentHash(payment_hash), - payment_type: PaymentType::InboundPayment, - description: req.description, - preimage: None, - secret: Some(*invoice.payment_secret()), - amt_msat: req.amount_in_msat.map(|a| a as i64), - fee_paid_msat: None, - status: HTLCStatus::Pending, - created_at: (now_ms() / 1000) as i64, - last_updated: (now_ms() / 1000) as i64, - }; - ln_coin.db.add_or_update_payment_in_db(payment_info).await?; + // Note: adding payment to db step is removed since the preimage can be recreated from the keymanager and the invoice secret Ok(GenerateInvoiceResponse { - payment_hash: payment_hash.into(), + payment_hash: invoice.payment_hash().into_inner().into(), invoice, }) } diff --git a/mm2src/coins/rpc_command/lightning/send_payment.rs b/mm2src/coins/rpc_command/lightning/send_payment.rs index c156589839..e44491008d 100644 --- a/mm2src/coins/rpc_command/lightning/send_payment.rs +++ b/mm2src/coins/rpc_command/lightning/send_payment.rs @@ -1,4 +1,3 @@ -use crate::lightning::ln_db::LightningDB; use crate::lightning::ln_p2p::connect_to_ln_node; use crate::lightning::ln_serialization::PublicKeyForRPC; use crate::lightning::PaymentError; @@ -107,7 +106,7 @@ pub async fn send_payment(ctx: MmArc, req: SendPaymentReq) -> SendPaymentResult< expiry, } => ln_coin.keysend(destination.into(), amount_in_msat, expiry).await?, }; - ln_coin.db.add_or_update_payment_in_db(payment_info.clone()).await?; + Ok(SendPaymentResponse { payment_hash: payment_info.payment_hash.0.into(), }) diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 079799d503..423edca684 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -594,7 +594,7 @@ impl SwapOps for SolanaCoin { unimplemented!() } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { unimplemented!() } + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { unimplemented!() } fn negotiate_swap_contract_addr( &self, diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 083208d2cf..16e49a2cea 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -428,7 +428,7 @@ impl SwapOps for SplToken { unimplemented!() } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { unimplemented!() } + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { unimplemented!() } fn negotiate_swap_contract_addr( &self, diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 3519d355dc..c953407619 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -763,7 +763,7 @@ impl SwapOps for TendermintCoin { todo!() } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { todo!() } + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { todo!() } fn negotiate_swap_contract_addr( &self, diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index dff56f4f30..761d85101e 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -239,7 +239,7 @@ impl SwapOps for TestCoin { unimplemented!() } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { unimplemented!() } + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { unimplemented!() } fn negotiate_swap_contract_addr( &self, diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 3c8ec5b1aa..7ec39dc0b9 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1019,7 +1019,7 @@ impl SwapOps for BchCoin { utxo_common::search_for_swap_tx_spend_other(self, input, utxo_common::DEFAULT_SWAP_VOUT).await } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { utxo_common::extract_secret(secret_hash, spend_tx) } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 97bf34ed1c..4ba6c50040 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -717,7 +717,7 @@ impl SwapOps for QtumCoin { utxo_common::search_for_swap_tx_spend_other(self, input, utxo_common::DEFAULT_SWAP_VOUT).await } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { utxo_common::extract_secret(secret_hash, spend_tx) } diff --git a/mm2src/coins/utxo/rpc_clients.rs b/mm2src/coins/utxo/rpc_clients.rs index 96fd49fea8..ee4c92be42 100644 --- a/mm2src/coins/utxo/rpc_clients.rs +++ b/mm2src/coins/utxo/rpc_clients.rs @@ -187,8 +187,8 @@ impl UtxoRpcClientEnum { ); } error!( - "Tx {} not found on chain, error: {}, retrying in 10 seconds. Retries left: {}", - tx_hash, e, tx_not_found_retries + "Tx {} not found on chain, error: {}, retrying in {} seconds. Retries left: {}", + tx_hash, e, check_every, tx_not_found_retries ); tx_not_found_retries -= 1; Timer::sleep(check_every as f64).await; @@ -199,7 +199,7 @@ impl UtxoRpcClientEnum { let block = match selfi.get_block_count().compat().await { Ok(b) => b, Err(e) => { - error!("Error {} getting block number, retrying in 10 seconds", e); + error!("Error {} getting block number, retrying in {} seconds", e, check_every); Timer::sleep(check_every as f64).await; continue; }, @@ -210,8 +210,8 @@ impl UtxoRpcClientEnum { } } error!( - "Error {:?} getting the transaction {:?}, retrying in 10 seconds", - e, tx_hash + "Error {:?} getting the transaction {:?}, retrying in {} seconds", + e, tx_hash, check_every ) }, } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index ec085a2796..802498bfff 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1439,7 +1439,7 @@ impl SwapOps for SlpToken { utxo_common::search_for_swap_tx_spend_other(&self.platform_coin, input, SLP_SWAP_VOUT).await } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { utxo_common::extract_secret(secret_hash, spend_tx) } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 1ba946ef42..ce5b0a4ab7 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -478,7 +478,7 @@ impl SwapOps for UtxoStandardCoin { utxo_common::search_for_swap_tx_spend_other(self, input, utxo_common::DEFAULT_SWAP_VOUT).await } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { utxo_common::extract_secret(secret_hash, spend_tx) } diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index bcc0910483..1b4c5ffe7e 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -135,7 +135,7 @@ fn test_extract_secret() { let tx_hex = hex::decode("0100000001de7aa8d29524906b2b54ee2e0281f3607f75662cbc9080df81d1047b78e21dbc00000000d7473044022079b6c50820040b1fbbe9251ced32ab334d33830f6f8d0bf0a40c7f1336b67d5b0220142ccf723ddabb34e542ed65c395abc1fbf5b6c3e730396f15d25c49b668a1a401209da937e5609680cb30bff4a7661364ca1d1851c2506fa80c443f00a3d3bf7365004c6b6304f62b0e5cb175210270e75970bb20029b3879ec76c4acd320a8d0589e003636264d01a7d566504bfbac6782012088a9142fb610d856c19fd57f2d0cffe8dff689074b3d8a882103f368228456c940ac113e53dad5c104cf209f2f102a409207269383b6ab9b03deac68ffffffff01d0dc9800000000001976a9146d9d2b554d768232320587df75c4338ecc8bf37d88ac40280e5c").unwrap(); let expected_secret = hex::decode("9da937e5609680cb30bff4a7661364ca1d1851c2506fa80c443f00a3d3bf7365").unwrap(); let secret_hash = &*dhash160(&expected_secret); - let secret = coin.extract_secret(secret_hash, &tx_hex).unwrap(); + let secret = block_on(coin.extract_secret(secret_hash, &tx_hex)).unwrap(); assert_eq!(secret, expected_secret); } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index b7e9bf537f..fa36cfe0eb 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1380,7 +1380,7 @@ impl SwapOps for ZCoin { utxo_common::search_for_swap_tx_spend_other(self, input, utxo_common::DEFAULT_SWAP_VOUT).await } - fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { utxo_common::extract_secret(secret_hash, spend_tx) } diff --git a/mm2src/mm2_main/src/docker_tests.rs b/mm2src/mm2_main/src/docker_tests.rs index 7c4ecef87e..ff66eaefb2 100644 --- a/mm2src/mm2_main/src/docker_tests.rs +++ b/mm2src/mm2_main/src/docker_tests.rs @@ -1214,7 +1214,7 @@ mod docker_tests { .unwrap(); assert!(rc.0.is_success(), "!buy: {}", rc.1); - block_on(mm_alice.wait_for_log(60., |log| log.contains("Taker payment tx hash"))).unwrap(); + block_on(mm_alice.wait_for_log(60., |log| log.contains("Watcher message sent..."))).unwrap(); block_on(mm_alice.stop()).unwrap(); block_on(mm_watcher.wait_for_log(60., |log| log.contains("Maker payment spend tx"))).unwrap(); thread::sleep(Duration::from_secs(5)); diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 87dcccfa07..bbad0e3ac4 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -414,9 +414,11 @@ impl MakerSwap { // Todo: recheck the other get_my_payment_data too // If maker payment is a lightning payment the payment hash will be sent in the message let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex().0; + let hash_algo = SecretHashAlgo::SHA256; if let Some(instructions) = self .taker_coin - .payment_instructions(&self.secret_hash(), &self.taker_amount) + // Todo: revise this with let hash_algo = SecretHashAlgo::SHA256; + .payment_instructions(&hash_algo.hash_secret(self.secret.as_slice()), &self.taker_amount) .await? { Ok(PaymentDataMsg::V2(PaymentDataV2 { @@ -791,12 +793,14 @@ impl MakerSwap { let payment_data_msg = match self.get_my_payment_data().await { Ok(data) => data, Err(e) => { + // Todo: remove this debug after passing this stage in test + debug!("payment_data_msg error: {}", e); return Ok((Some(MakerSwapCommand::RefundMakerPayment), vec![ MakerSwapEvent::MakerPaymentDataSendFailed(e.to_string().into()), MakerSwapEvent::MakerPaymentWaitRefundStarted { wait_until: self.wait_refund_until(), }, - ])) + ])); }, }; let msg = SwapMsg::MakerPayment(payment_data_msg); 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 e52d034658..c80ca81e55 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -380,12 +380,9 @@ async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> Recreate // Then we can continue to process success Maker events. let wait_refund_until = negotiated_event.taker_payment_locktime + 3700; - taker_swap.events.extend(convert_maker_to_taker_events( - event_it, - maker_coin, - secret_hash, - wait_refund_until, - )); + taker_swap + .events + .extend(convert_maker_to_taker_events(event_it, maker_coin, secret_hash, wait_refund_until).await); Ok(taker_swap) } @@ -395,7 +392,8 @@ async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> Recreate /// since they are used outside of this function to generate `TakerSwap` and the initial [`TakerSwapEvent::Started`] and [`TakerSwapEvent::Negotiated`] events. /// /// The `maker_coin` and `secret_hash` function arguments are used to extract a secret from `TakerPaymentSpent`. -fn convert_maker_to_taker_events( +// Todo: async can be removed when extract secret is back as sync +async fn convert_maker_to_taker_events( event_it: impl Iterator, maker_coin: MmCoinEnum, secret_hash: BytesJson, @@ -453,7 +451,7 @@ fn convert_maker_to_taker_events( return events; }, MakerSwapEvent::TakerPaymentSpent(tx_ident) => { - let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()) { + let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()).await { Ok(secret) => H256Json::from(secret.as_slice()), Err(e) => { push_event!(TakerSwapEvent::TakerPaymentWaitForSpendFailed(ERRL!("{}", e).into())); @@ -534,7 +532,7 @@ mod tests { fn test_recreate_taker_swap() { TestCoin::extract_secret.mock_safe(|_coin, _secret_hash, _spend_tx| { let secret = hex::decode("23a6bb64bc0ab2cc14cb84277d8d25134b814e5f999c66e578c9bba3c5e2d3a4").unwrap(); - MockResult::Return(Ok(secret)) + MockResult::Return(Box::pin(async move { Ok(secret) })) }); let maker_saved_swap: MakerSavedSwap = diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 368364c20f..3f026c816a 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -278,6 +278,7 @@ impl Watcher { let secret = match self .taker_coin .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex()) + .await { Ok(bytes) => H256Json::from(bytes.as_slice()), Err(e) => { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 635ae0dda7..cad2b8031e 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1230,6 +1230,7 @@ impl TakerSwap { confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), self.r().data.maker_payment_wait, + // Todo: this can be less for lightning probably WAIT_CONFIRM_INTERVAL, ); if let Err(err) = f.compat().await { @@ -1399,12 +1400,12 @@ impl TakerSwap { async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.clone(); let mut watcher_broadcast_abort_handle = None; - let mut send_abort_handle = None; + // Todo: refactor this if let Some(hex) = tx_hex { if self.ctx.use_watchers() { let preimage_hex = self.r().taker_spends_maker_payment_preimage.clone(); if let Some(preimage_hex) = preimage_hex { - let watcher_data = self.create_watcher_data(hex.0.clone(), preimage_hex); + let watcher_data = self.create_watcher_data(hex.0, preimage_hex); let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); watcher_broadcast_abort_handle = Some(broadcast_swap_message_every( self.ctx.clone(), @@ -1415,19 +1416,22 @@ impl TakerSwap { )); } } - // Todo: The same should be done for MakerPayment (shouldn't be sent on some cases) - let msg = SwapMsg::TakerPayment(hex.0); - send_abort_handle = Some(broadcast_swap_message_every( - self.ctx.clone(), - swap_topic(&self.uuid), - msg, - 600., - self.p2p_privkey, - )); } + // Todo: taker_payment should be a message on lightning network not a swap message + let msg = SwapMsg::TakerPayment(self.r().taker_payment.as_ref().unwrap().tx_hex().0); + let send_abort_handle = Some(broadcast_swap_message_every( + self.ctx.clone(), + swap_topic(&self.uuid), + msg, + 600., + self.p2p_privkey, + )); + let wait_duration = (self.r().data.lock_duration * 4) / 5; let wait_taker_payment = self.r().data.started_at + wait_duration; + // Todo: remove this after successful test + debug!("wait_for_confirmations"); let wait_f = self .taker_coin .wait_for_confirmations( @@ -1449,6 +1453,8 @@ impl TakerSwap { ])); } + // Todo: remove this after successful test + debug!("wait_for_tx_spend"); let f = self.taker_coin.wait_for_tx_spend( &self.r().taker_payment.clone().unwrap().tx_hex(), self.r().data.taker_payment_lock, @@ -1474,10 +1480,11 @@ impl TakerSwap { tx_hex: tx.tx_hex().map(From::from), tx_hash, }; - let secret = match self - .taker_coin - .extract_secret(&self.r().secret_hash.0, &tx_ident.tx_hex()) - { + + // Todo: remove this after successful test + debug!("extract_secret"); + let secret_hash = self.r().secret_hash.clone(); + let secret = match self.taker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()).await { Ok(bytes) => H256Json::from(bytes.as_slice()), Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ @@ -1835,9 +1842,9 @@ impl TakerSwap { FoundSwapTxSpend::Spent(tx) => { check_maker_payment_is_not_spent!(); let tx_hash = tx.tx_hash().0; - let secret = try_s!(self - .taker_coin - .extract_secret(&self.r().secret_hash.0, &tx.tx_hex().unwrap_or(tx_hash))); + let tx_hex = tx.tx_hex().unwrap_or(tx_hash); + let secret_hash = self.r().secret_hash.clone(); + let secret = try_s!(self.taker_coin.extract_secret(&secret_hash.0, &tx_hex).await); let fut = self.maker_coin.send_taker_spends_maker_payment( &maker_payment, @@ -2408,7 +2415,7 @@ mod taker_swap_tests { TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); TestCoin::swap_contract_address.mock_safe(|_| MockResult::Return(None)); - TestCoin::extract_secret.mock_safe(|_, _, _| MockResult::Return(Ok(vec![]))); + TestCoin::extract_secret.mock_safe(|_, _, _| MockResult::Return(Box::pin(async move { Ok(vec![]) }))); static mut MY_PAYMENT_SENT_CALLED: bool = false; TestCoin::check_if_my_payment_sent.mock_safe(|_, _, _, _, _, _, _| { @@ -2523,7 +2530,7 @@ mod taker_swap_tests { TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); TestCoin::swap_contract_address.mock_safe(|_| MockResult::Return(None)); - TestCoin::extract_secret.mock_safe(|_, _, _| MockResult::Return(Ok(vec![]))); + TestCoin::extract_secret.mock_safe(|_, _, _| MockResult::Return(Box::pin(async move { Ok(vec![]) }))); static mut SEARCH_TX_SPEND_CALLED: bool = false; TestCoin::search_for_swap_tx_spend_my.mock_safe(|_, _| { diff --git a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs index 52ea8a1025..6930079aa1 100644 --- a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs +++ b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs @@ -57,7 +57,10 @@ fn start_lightning_nodes(enable_0_confs: bool) -> (MarketMakerIt, MarketMakerIt, } } } - } + }, + // Todo: add those in the test itself and pass it to the function + {"coin":"RICK","asset":"RICK","rpcport":8923,"txversion":4,"overwintered":1,"required_confirmations":0,"protocol":{"type":"UTXO"}}, + {"coin":"MORTY","asset":"MORTY","rpcport":11608,"txversion":4,"overwintered":1,"required_confirmations":0,"protocol":{"type":"UTXO"}} ]); let mm_node_1 = MarketMakerIt::start( @@ -69,13 +72,14 @@ fn start_lightning_nodes(enable_0_confs: bool) -> (MarketMakerIt, MarketMakerIt, "passphrase": node_1_seed.to_string(), "coins": coins, "rpc_password": "pass", + "i_am_seed": true, }), "pass".into(), local_start!("bob"), ) .unwrap(); let (_dump_log, _dump_dashboard) = mm_node_1.mm_dump(); - log!("bob log path: {}", mm_node_1.log_path.display()); + log!("Node 1 log path: {}", mm_node_1.log_path.display()); let electrum = block_on(enable_electrum(&mm_node_1, "tBTC-TEST-segwit", false, T_BTC_ELECTRUMS)); log!("Node 1 tBTC address: {}", electrum.address); @@ -92,13 +96,14 @@ fn start_lightning_nodes(enable_0_confs: bool) -> (MarketMakerIt, MarketMakerIt, "passphrase": node_2_seed.to_string(), "coins": coins, "rpc_password": "pass", + "seednodes": [mm_node_1.my_seed_addr()], }), "pass".into(), local_start!("alice"), ) .unwrap(); let (_dump_log, _dump_dashboard) = mm_node_2.mm_dump(); - log!("alice log path: {}", mm_node_2.log_path.display()); + log!("Node 2 log path: {}", mm_node_2.log_path.display()); let electrum = block_on(enable_electrum(&mm_node_2, "tBTC-TEST-segwit", false, T_BTC_ELECTRUMS)); log!("Node 2 tBTC address: {}", electrum.address); @@ -307,8 +312,10 @@ fn test_open_channel() { #[ignore] #[cfg(not(target_arch = "wasm32"))] // This also tests 0_confs_channels -fn test_send_payment() { - let (mut mm_node_2, mm_node_1, node_2_id, node_1_id) = start_lightning_nodes(true); +// Todo: Return this test to it's old self and do a different test for swaps +// Todo: improve swap time +fn test_send_payment_and_swaps() { + let (mut mm_node_1, mut mm_node_2, node_1_id, node_2_id) = start_lightning_nodes(true); let node_1_address = format!("{}@{}:9735", node_1_id, mm_node_1.ip.to_string()); let add_trusted_node = block_on(mm_node_1.rpc(&json! ({ @@ -422,6 +429,71 @@ fn test_send_payment() { assert_eq!(payment["amount_in_msat"], 1000); assert_eq!(payment["payment_type"]["type"], "Inbound Payment"); + // ---------------------------- Enable coins for swaps -------------------------------- // + + // Enable coins on mm_node_1 side. Print the replies in case we need the "address". + log!( + "enable_coins (mm_node_1): {:?}", + block_on(enable_coins_rick_morty_electrum(&mm_node_1)) + ); + + // Enable coins on mm_node_2 side. Print the replies in case we need the "address". + log!( + "enable_coins (mm_node_2): {:?}", + block_on(enable_coins_rick_morty_electrum(&mm_node_2)) + ); + + // mm_node_1 is maker + let set_price = block_on(mm_node_1.rpc(&json! ({ + "userpass": mm_node_1.userpass, + "method": "setprice", + "base": "RICK", + "rel": "tBTC-TEST-lightning", + "price": 0.000001, + "volume": 0.1 + }))) + .unwrap(); + assert!(set_price.0.is_success(), "!setprice: {}", set_price.1); + + let orderbook = block_on(mm_node_2.rpc(&json! ({ + "userpass": mm_node_2.userpass, + "method": "orderbook", + "base": "RICK", + "rel": "tBTC-TEST-lightning", + }))) + .unwrap(); + assert!(orderbook.0.is_success(), "!orderbook: {}", orderbook.1); + + block_on(Timer::sleep(1.)); + + // mm_node_2 is taker + let buy = block_on(mm_node_2.rpc(&json! ({ + "userpass": mm_node_2.userpass, + "method": "buy", + "base": "RICK", + "rel": "tBTC-TEST-lightning", + "price": 0.000001, + "volume": 0.1 + }))) + .unwrap(); + assert!(buy.0.is_success(), "!buy: {}", buy.1); + let buy_json: Json = serde_json::from_str(&buy.1).unwrap(); + let uuid = buy_json["result"]["uuid"].as_str().unwrap().to_owned(); + + // ensure the swaps are started + block_on(mm_node_2.wait_for_log(5., |log| { + log.contains("Entering the taker_swap_loop RICK/tBTC-TEST-lightning") + })) + .unwrap(); + block_on(mm_node_1.wait_for_log(5., |log| { + log.contains("Entering the maker_swap_loop RICK/tBTC-TEST-lightning") + })) + .unwrap(); + + block_on(mm_node_1.wait_for_log(900., |log| log.contains(&format!("[swap uuid={}] Finished", uuid)))).unwrap(); + + block_on(mm_node_2.wait_for_log(900., |log| log.contains(&format!("[swap uuid={}] Finished", uuid)))).unwrap(); + block_on(mm_node_1.stop()).unwrap(); block_on(mm_node_2.stop()).unwrap(); } From 2f99e1b140426d04fdbf0f59a392ec57b6be4f62 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 7 Oct 2022 02:24:03 +0200 Subject: [PATCH 22/36] refactor some code by adding payment_hash_from_slice fn --- mm2src/coins/lightning.rs | 112 +++++++++++--------------------------- 1 file changed, 32 insertions(+), 80 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 09ad781fab..e911eb77a9 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -496,16 +496,7 @@ impl SwapOps for LightningCoin { _swap_contract_address: &Option, _swap_unique_data: &[u8], ) -> TransactionFut { - let payment_hash_length = taker_payment_tx.len(); - // Todo: do we need to do these checks every time (should be done at first only) - if payment_hash_length != 32 { - let error = format!("Invalid payment hash length {}", payment_hash_length); - return Box::new(futures01::future::err(TransactionErr::Plain(error))); - } - let mut payment_hash_array = [b' '; 32]; - payment_hash_array.copy_from_slice(taker_payment_tx); - let payment_hash = PaymentHash(payment_hash_array); - + let payment_hash = try_tx_fus!(payment_hash_from_slice(taker_payment_tx)); let mut preimage = [b' '; 32]; preimage.copy_from_slice(secret); @@ -584,18 +575,9 @@ impl SwapOps for LightningCoin { fn validate_maker_payment(&self, _input: ValidatePaymentInput) -> ValidatePaymentFut<()> { unimplemented!() } fn validate_taker_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentFut<()> { - let payment_hash_length = input.payment_tx.len(); - // Todo: do we need to do these checks every time (should be done at first only) - if payment_hash_length != 32 { - let error = format!("Invalid payment hash length {}", payment_hash_length); - return Box::new(futures01::future::err(MmError::new( - ValidatePaymentError::TxDeserializationError(error), - ))); - } - let mut payment_hash_array = [b' '; 32]; - payment_hash_array.copy_from_slice(&input.payment_tx); - let payment_hash = PaymentHash(payment_hash_array); - let payment_hex = hex::encode(payment_hash_array); + let payment_hash = try_f!(payment_hash_from_slice(&input.payment_tx) + .map_to_mm(|e| ValidatePaymentError::TxDeserializationError(e.to_string()))); + let payment_hex = hex::encode(payment_hash.0); let amt_msat = try_f!(sat_from_big_decimal(&input.amount, self.decimals())); @@ -664,15 +646,8 @@ impl SwapOps for LightningCoin { // Todo: if the secret or preimage is part of the TransactionEnum, there is no need for more calls to db (also if paymentinfo is in the enum) // Todo: also repeated code async fn extract_secret(&self, _secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { - let payment_hash_length = spend_tx.len(); - // Todo: do we need to do these checks every time (should be done at first only) - if payment_hash_length != 32 { - return ERR!("Invalid payment hash length {}", payment_hash_length); - } - let mut payment_hash_array = [b' '; 32]; - payment_hash_array.copy_from_slice(spend_tx); - let payment_hash = PaymentHash(payment_hash_array); - let payment_hex = hex::encode(payment_hash_array); + let payment_hash = payment_hash_from_slice(spend_tx).map_err(|e| e.to_string())?; + let payment_hex = hex::encode(payment_hash.0); return match self.db.get_payment_from_db(payment_hash).await { Ok(Some(payment)) => match payment.preimage { @@ -707,30 +682,15 @@ impl SwapOps for LightningCoin { ) -> Result>, MmError> { // lightning decimals should be 11 in config since the smallest divisible unit in lightning coin is msat let amt_msat = sat_from_big_decimal(amount, self.decimals())?; - - let secret_hash_length = secret_hash.len(); - if secret_hash_length != 32 { - let error = format!("Invalid secret_hash length {}", secret_hash_length); - return Err(PaymentInstructionsErr::InternalError(error).into()); - } - let mut payment_hash = [b' '; 32]; - payment_hash.copy_from_slice(secret_hash); + let payment_hash = + payment_hash_from_slice(secret_hash).map_to_mm(|e| PaymentInstructionsErr::InternalError(e.to_string()))?; // Todo: Maybe the description can be the swap uuid let invoice = self - .create_invoice_for_hash( - PaymentHash(payment_hash), - Some(amt_msat), - "".into(), - DEFAULT_INVOICE_EXPIRY, - ) + .create_invoice_for_hash(payment_hash, Some(amt_msat), "".into(), DEFAULT_INVOICE_EXPIRY) .await .map_to_mm(|e| PaymentInstructionsErr::LightningInvoiceErr(e.to_string()))?; - // Todo: revise this Ok(Some(invoice.to_string().into_bytes())) - // Ok(Some(hex::decode(invoice.to_string()).map_to_mm(|e| { - // PaymentInstructionsErr::LightningInvoiceErr(e.to_string()) - // })?)) } fn validate_instructions( @@ -759,6 +719,22 @@ impl SwapOps for LightningCoin { } } +#[derive(Debug, Display)] +pub enum PaymentHashFromSliceErr { + #[display(fmt = "Invalid data length of {}", _0)] + InvalidLength(usize), +} + +fn payment_hash_from_slice(data: &[u8]) -> Result { + let len = data.len(); + if len != 32 { + return Err(PaymentHashFromSliceErr::InvalidLength(len)); + } + let mut hash = [b' '; 32]; + hash.copy_from_slice(data); + Ok(PaymentHash(hash)) +} + impl MarketCoinOps for LightningCoin { fn ticker(&self) -> &str { &self.conf.ticker } @@ -847,18 +823,8 @@ impl MarketCoinOps for LightningCoin { wait_until: u64, check_every: u64, ) -> Box + Send> { - let payment_hash_length = tx.len(); - // Todo: do we need to do these checks every time (should be done at first only) - if payment_hash_length != 32 { - return Box::new(futures01::future::err(ERRL!( - "Invalid payment hash length {}", - payment_hash_length - ))); - } - let mut payment_hash_array = [b' '; 32]; - payment_hash_array.copy_from_slice(tx); - let payment_hash = PaymentHash(payment_hash_array); - let payment_hex = hex::encode(payment_hash_array); + let payment_hash = try_f!(payment_hash_from_slice(tx).map_err(|e| e.to_string())); + let payment_hex = hex::encode(payment_hash.0); let coin = self.clone(); let fut = async move { @@ -900,16 +866,8 @@ impl MarketCoinOps for LightningCoin { _from_block: u64, _swap_contract_address: &Option, ) -> TransactionFut { - let payment_hash_length = transaction.len(); - // Todo: do we need to do these checks every time (should be done at first only) - if payment_hash_length != 32 { - let error = format!("Invalid payment hash length {}", payment_hash_length); - return Box::new(futures01::future::err(TransactionErr::Plain(error))); - } - let mut payment_hash_array = [b' '; 32]; - payment_hash_array.copy_from_slice(transaction); - let payment_hash = PaymentHash(payment_hash_array); - let payment_hex = hex::encode(payment_hash_array); + let payment_hash = try_tx_fus!(payment_hash_from_slice(transaction)); + let payment_hex = hex::encode(payment_hash.0); let coin = self.clone(); let fut = async move { @@ -961,15 +919,9 @@ impl MarketCoinOps for LightningCoin { } fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { - // Todo: this block of code is repeated, maybe make it a function - let payment_hash_length = bytes.len(); - if payment_hash_length != 32 { - let error = format!("Invalid payment hash length {}", payment_hash_length); - return Err(TxMarshalingErr::InvalidInput(error).into()); - } - let mut payment_hash = [b' '; 32]; - payment_hash.copy_from_slice(bytes); - Ok(TransactionEnum::LightningPayment(PaymentHash(payment_hash))) + Ok(TransactionEnum::LightningPayment( + payment_hash_from_slice(bytes).map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string()))?, + )) } fn current_block(&self) -> Box + Send> { Box::new(futures01::future::ok(0)) } From 38e90494c71b054f061e5085007a6589d06c32d6 Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 7 Oct 2022 22:02:30 +0200 Subject: [PATCH 23/36] minor fixes and added more todo for next PR/s --- mm2src/coins/lightning.rs | 78 +++++++++++-------- mm2src/coins/lightning/ln_events.rs | 4 +- mm2src/coins/lp_coins.rs | 4 - mm2src/mm2_main/src/lp_swap.rs | 1 - mm2src/mm2_main/src/lp_swap/maker_swap.rs | 1 - .../src/lp_swap/recreate_swap_data.rs | 2 - mm2src/mm2_main/src/lp_swap/taker_swap.rs | 7 -- 7 files changed, 47 insertions(+), 50 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index e911eb77a9..48cad0fe8d 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -30,7 +30,7 @@ use bitcoin_hashes::sha256::Hash as Sha256; use bitcrypto::ChecksumType; use bitcrypto::{dhash256, ripemd160}; use common::executor::{spawn, Timer}; -use common::log::{error, info, LogOnError, LogState}; +use common::log::{info, LogOnError, LogState}; use common::{async_blocking, log, now_ms, PagingOptionsEnum}; use db_common::sqlite::rusqlite::Error as SqlError; use futures::{FutureExt, TryFutureExt}; @@ -396,7 +396,6 @@ impl LightningCoin { // supply. let payment_secret = self .channel_manager - // Todo: review secret creation process .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs) .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; let our_node_pubkey = self.channel_manager.get_our_node_id(); @@ -408,8 +407,8 @@ impl LightningCoin { .payment_hash(Hash::from_inner(payment_hash.0)) .payment_secret(payment_secret) .basic_mpp() + // Todo: This will probably be important in locktime calculations in the next PRs and should be validated by the other side .min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into()) - // Todo: this should be the locktime probably and it should be validated by the other side, what about min_final_cltv_expiry?? .expiry_time(core::time::Duration::from_secs(invoice_expiry_delta_secs.into())); if let Some(amt) = amt_msat { invoice = invoice.amount_milli_satoshis(amt); @@ -559,7 +558,7 @@ impl SwapOps for LightningCoin { unimplemented!() } - // Todo: This validetes the dummy fee for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning + // Todo: This validates the dummy fee for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning fn validate_fee( &self, _fee_tx: &TransactionEnum, @@ -584,16 +583,23 @@ impl SwapOps for LightningCoin { let coin = self.clone(); let fut = async move { match coin.db.get_payment_from_db(payment_hash).await { - Ok(Some(payment)) => { - if payment.amt_msat != Some(amt_msat as i64) { + Ok(Some(mut payment)) => { + let amount_sent = payment.amt_msat; + // Todo: Add more validations if needed, locktime is probably the most important + if amount_sent != Some(amt_msat as i64) { // Free the htlc to allow for this inbound liquidity to be used for other inbound payments coin.channel_manager.fail_htlc_backwards(&payment_hash); + payment.status = HTLCStatus::Failed; + drop_mutability!(payment); + coin.db + .add_or_update_payment_in_db(payment) + .await + .error_log_with_msg("Unable to update payment information in DB!"); return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Provided payment {} amount {:?} doesn't match required amount {}", - payment_hex, payment.amt_msat, amt_msat + payment_hex, amount_sent, amt_msat ))); } - // Todo: is more validation needed? (locktime, pub key etc.., status = received) Ok(()) }, Ok(None) => MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( @@ -643,8 +649,6 @@ impl SwapOps for LightningCoin { unimplemented!() } - // Todo: if the secret or preimage is part of the TransactionEnum, there is no need for more calls to db (also if paymentinfo is in the enum) - // Todo: also repeated code async fn extract_secret(&self, _secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { let payment_hash = payment_hash_from_slice(spend_tx).map_err(|e| e.to_string())?; let payment_hex = hex::encode(payment_hash.0); @@ -670,7 +674,7 @@ impl SwapOps for LightningCoin { Ok(None) } - // Todo: should node id and secret be used instead?? (maybe when implementing private swaps for lightning) + // Todo: This can be changed if private swaps were to be implemented for lightning fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair { utxo_common::derive_htlc_key_pair(self.platform.coin.as_ref(), swap_unique_data) } @@ -685,7 +689,8 @@ impl SwapOps for LightningCoin { let payment_hash = payment_hash_from_slice(secret_hash).map_to_mm(|e| PaymentInstructionsErr::InternalError(e.to_string()))?; - // Todo: Maybe the description can be the swap uuid + // note: No description is provided in the invoice to reduce the payload + // Todo: The invoice expiry should probably be the same as maker_payment_wait/wait_taker_payment let invoice = self .create_invoice_for_hash(payment_hash, Some(amt_msat), "".into(), DEFAULT_INVOICE_EXPIRY) .await @@ -699,7 +704,6 @@ impl SwapOps for LightningCoin { secret_hash: &[u8], amount: BigDecimal, ) -> Result, MmError> { - // Todo: should I use from_utf8? do I need to check the instruction size (can any size be sent??) let invoice = Invoice::from_str(&String::from_utf8_lossy(instructions))?; if (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) || (secret_hash.len() == 32 && invoice.payment_hash().as_inner() != secret_hash) @@ -714,7 +718,7 @@ impl SwapOps for LightningCoin { if big_decimal_from_sat(invoice_amount as i64, self.decimals()) != amount { return Err(ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice amount!".into()).into()); } - // Todo: continue validation here by comparing (locktime, etc..) + // Todo: continue validation here by comparing locktime, etc.. Ok(Some(PaymentInstructions::Lightning(invoice))) } } @@ -813,8 +817,7 @@ impl MarketCoinOps for LightningCoin { )) } - // Todo: Add waiting for confirmation for sending logic, move this inside the code - // Todo: can this be avoided completely in lightning (just return () straight away) + // Todo: Add waiting for confirmations logic for the case of if the channel is closed and the htlc can be claimed on-chain fn wait_for_confirmations( &self, tx: &[u8], @@ -837,28 +840,32 @@ impl MarketCoinOps for LightningCoin { ); } - // Todo: add note about how this overuses the db match coin.db.get_payment_from_db(payment_hash).await { Ok(Some(_)) => { - // Todo: should check for status received after adding payment to db on create_invoice_for_hash (check for claimed after send_maker_spends_taker_payment and for received on validate_taker_payment, and pending or successful??? for taker swap wait for confirmation) + // Todo: This should check for different payment statuses depending on where wait_for_confirmations is called, + // Todo: which might lead to breaking wait_for_confirmations to 3 functions (wait_for_payment_sent_confirmations, wait_for_payment_received_confirmations, wait_for_payment_spent_confirmations) return Ok(()); }, - // Todo: This should be also an error after adding payment to db on create_invoice_for_hash Ok(None) => info!("Payment {} not received yet!", payment_hex), - // Todo: Maybe I should return a permanent error here and end the swap - Err(e) => error!( - "Error getting payment {} from db: {}, retrying in {} seconds", - payment_hex, e, check_every - ), + Err(e) => { + return ERR!( + "Error getting payment {} from db: {}, retrying in {} seconds", + payment_hex, + e, + check_every + ) + }, } + // note: When sleeping for only 1 second the test_send_payment_and_swaps unit test took 20 seconds to complete instead of 37 seconds when WAIT_CONFIRM_INTERVAL (15 seconds) is used + // Todo: In next sprints, should add a mutex for lightning swap payments to avoid overloading the shared db connection with requests when the sleep time is reduced and multiple swaps are ran together + // Todo: The aim is to make lightning swap payments as fast as possible. Running swap payments statuses should be loaded from db on restarts in this case. Timer::sleep(check_every as f64).await; } }; Box::new(fut.boxed().compat()) } - // Todo: has similar code to wait_for_confirmation (should create a common function) fn wait_for_tx_spend( &self, transaction: &[u8], @@ -874,7 +881,7 @@ impl MarketCoinOps for LightningCoin { loop { if now_ms() / 1000 > wait_until { return Err(TransactionErr::Plain(format!( - "Waited too long until {} for payment {} to be spend", + "Waited too long until {} for payment {} to be spent", wait_until, payment_hex ))); } @@ -890,7 +897,7 @@ impl MarketCoinOps for LightningCoin { ))) }, HTLCStatus::Succeeded => return Ok(TransactionEnum::LightningPayment(payment_hash)), - // Todo: should I retry first before returning an error (return an error only if all paths failed, what about locktime?) + // Todo: Retry payment multiple times returning an error only if all paths failed or other permenant error, should also keep locktime in mind when using different paths with different CLTVs HTLCStatus::Failed => { return Err(TransactionErr::Plain(format!( "Lightning swap payment {} failed", @@ -905,13 +912,18 @@ impl MarketCoinOps for LightningCoin { payment_hex ))) }, - Err(e) => error!( - "Error getting payment {} from db: {}, retrying in 10 seconds", - payment_hex, e - ), + Err(e) => { + return Err(TransactionErr::Plain(format!( + "Error getting payment {} from db: {}", + payment_hex, e + ))) + }, } - // Todo: should this be less for lightning?? what about maker payment (takes time to spend) + // note: When sleeping for only 1 second the test_send_payment_and_swaps unit test took 20 seconds to complete instead of 37 seconds when sleeping for 10 seconds + // Todo: In next sprints, should add a mutex for lightning swap payments to avoid overloading the shared db connection with requests when the sleep time is reduced and multiple swaps are ran together. + // Todo: The aim is to make lightning swap payments as fast as possible, more sleep time can be allowed for maker payment since it waits for the secret to be revealed on another chain first. + // Todo: Running swap payments statuses should be loaded from db on restarts in this case. Timer::sleep(10.).await; } }; @@ -935,7 +947,7 @@ impl MarketCoinOps for LightningCoin { .to_string()) } - // Todo: Should depend on inbound_htlc_minimum_msat of the channel/s the payment will be sent through, 1 satoshi for now (1000 of the base unit of lightning which is msat) + // Todo: min_tx_amount should depend on inbound_htlc_minimum_msat of the channel/s the payment will be sent through, 1 satoshi is used for for now (1000 of the base unit of lightning which is msat) fn min_tx_amount(&self) -> BigDecimal { big_decimal_from_sat(1000, self.decimals()) } // Todo: Equals to min_tx_amount for now (1 satoshi), should change this later diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index 5d60f91494..b50c848c47 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -345,11 +345,11 @@ impl LightningEventHandler { Some(preimage) => *preimage, // This is a swap related payment since we don't have the preimage yet None => { - // Todo: preimage should be saved to db after it's received (after claim_payment and make payment successful) + // Todo: preimage should be saved to db after it's received (after claim_funds and make payment successful) let payment_info = PaymentInfo { payment_hash: *payment_hash, payment_type: PaymentType::InboundPayment, - // Todo: maybe add this to db on create_invoice_for_hash + // Todo: maybe add this to db on create_invoice_for_hash, should complete wait for confirmation logic, etc.. description: "".into(), preimage: None, // Todo: maybe add this to db on create_invoice_for_hash diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 95f68d5fcb..36ce0b98e9 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -511,7 +511,6 @@ impl From for ValidateInstructionsErr { pub trait SwapOps { fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut; - // Todo: maybe make a struct for the arguments #[allow(clippy::too_many_arguments)] fn send_maker_payment( &self, @@ -524,7 +523,6 @@ pub trait SwapOps { payment_instructions: &Option, ) -> TransactionFut; - // Todo: maybe make a struct for the arguments #[allow(clippy::too_many_arguments)] fn send_taker_payment( &self, @@ -627,7 +625,6 @@ pub trait SwapOps { input: SearchForSwapTxSpendInput<'_>, ) -> Result, String>; - // Todo: can be made sync again if the secret is made part of the enum, it will also reduce a call to the DB async fn extract_secret(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String>; /// Whether the refund transaction can be sent now @@ -706,7 +703,6 @@ pub trait MarketCoinOps { /// Receives raw transaction bytes as input and returns tx hash in hexadecimal format fn send_raw_tx_bytes(&self, tx: &[u8]) -> Box + Send>; - // Todo: If channel is closed for lightning we might need to wait for confirmations, should be done in the next PRs, move this note to a better place fn wait_for_confirmations( &self, tx: &[u8], diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index d938cf2db0..51486b83fb 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -739,7 +739,6 @@ pub struct TransactionIdentifier { } impl TransactionIdentifier { - // Todo: rename this function fn tx_hex(&self) -> BytesJson { let tx_hash = self.tx_hash.clone(); self.tx_hex.clone().unwrap_or(tx_hash) diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index bbad0e3ac4..ec44f5d0d8 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -1417,7 +1417,6 @@ pub enum MakerSwapCommand { Finish, } -// Todo: mirror what's implemented in taker swap #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "type", content = "data")] #[allow(clippy::large_enum_variant)] 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 c80ca81e55..8b5039293c 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -392,7 +392,6 @@ async fn recreate_taker_swap(ctx: MmArc, maker_swap: MakerSavedSwap) -> Recreate /// since they are used outside of this function to generate `TakerSwap` and the initial [`TakerSwapEvent::Started`] and [`TakerSwapEvent::Negotiated`] events. /// /// The `maker_coin` and `secret_hash` function arguments are used to extract a secret from `TakerPaymentSpent`. -// Todo: async can be removed when extract secret is back as sync async fn convert_maker_to_taker_events( event_it: impl Iterator, maker_coin: MmCoinEnum, @@ -415,7 +414,6 @@ async fn convert_maker_to_taker_events( error: format!("Origin Maker error event: {:?}", event), }; match event { - // Todo: remove unwrap by making tx_ident and enum MakerSwapEvent::TakerFeeValidated(tx_ident) => push_event!(TakerSwapEvent::TakerFeeSent(tx_ident)), MakerSwapEvent::TakerFeeValidateFailed(_) => { push_event!(TakerSwapEvent::MakerPaymentValidateFailed(swap_error)); diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index cad2b8031e..f639ff3491 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1230,7 +1230,6 @@ impl TakerSwap { confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), self.r().data.maker_payment_wait, - // Todo: this can be less for lightning probably WAIT_CONFIRM_INTERVAL, ); if let Err(err) = f.compat().await { @@ -1430,8 +1429,6 @@ impl TakerSwap { let wait_duration = (self.r().data.lock_duration * 4) / 5; let wait_taker_payment = self.r().data.started_at + wait_duration; - // Todo: remove this after successful test - debug!("wait_for_confirmations"); let wait_f = self .taker_coin .wait_for_confirmations( @@ -1453,8 +1450,6 @@ impl TakerSwap { ])); } - // Todo: remove this after successful test - debug!("wait_for_tx_spend"); let f = self.taker_coin.wait_for_tx_spend( &self.r().taker_payment.clone().unwrap().tx_hex(), self.r().data.taker_payment_lock, @@ -1481,8 +1476,6 @@ impl TakerSwap { tx_hash, }; - // Todo: remove this after successful test - debug!("extract_secret"); let secret_hash = self.r().secret_hash.clone(); let secret = match self.taker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()).await { Ok(bytes) => H256Json::from(bytes.as_slice()), From 03aa40dda9875d1aae3d38300918816f686ea3cc Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 10 Oct 2022 12:03:11 +0200 Subject: [PATCH 24/36] refactor lightning taker swap test --- .../mm2_main/src/mm2_tests/lightning_tests.rs | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs index 6930079aa1..84237231a4 100644 --- a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs +++ b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs @@ -58,7 +58,6 @@ fn start_lightning_nodes(enable_0_confs: bool) -> (MarketMakerIt, MarketMakerIt, } } }, - // Todo: add those in the test itself and pass it to the function {"coin":"RICK","asset":"RICK","rpcport":8923,"txversion":4,"overwintered":1,"required_confirmations":0,"protocol":{"type":"UTXO"}}, {"coin":"MORTY","asset":"MORTY","rpcport":11608,"txversion":4,"overwintered":1,"required_confirmations":0,"protocol":{"type":"UTXO"}} ]); @@ -312,10 +311,8 @@ fn test_open_channel() { #[ignore] #[cfg(not(target_arch = "wasm32"))] // This also tests 0_confs_channels -// Todo: Return this test to it's old self and do a different test for swaps -// Todo: improve swap time -fn test_send_payment_and_swaps() { - let (mut mm_node_1, mut mm_node_2, node_1_id, node_2_id) = start_lightning_nodes(true); +fn test_send_payment() { + let (mut mm_node_2, mm_node_1, node_2_id, node_1_id) = start_lightning_nodes(true); let node_1_address = format!("{}@{}:9735", node_1_id, mm_node_1.ip.to_string()); let add_trusted_node = block_on(mm_node_1.rpc(&json! ({ @@ -429,7 +426,55 @@ fn test_send_payment_and_swaps() { assert_eq!(payment["amount_in_msat"], 1000); assert_eq!(payment["payment_type"]["type"], "Inbound Payment"); - // ---------------------------- Enable coins for swaps -------------------------------- // + block_on(mm_node_1.stop()).unwrap(); + block_on(mm_node_2.stop()).unwrap(); +} + +#[test] +// This test is ignored because it requires refilling the tBTC and RICK addresses with test coins periodically. +#[ignore] +#[cfg(not(target_arch = "wasm32"))] +fn test_lightning_taker_swap() { + let (mut mm_node_1, mut mm_node_2, node_1_id, node_2_id) = start_lightning_nodes(true); + let node_1_address = format!("{}@{}:9735", node_1_id, mm_node_1.ip.to_string()); + + let add_trusted_node = block_on(mm_node_1.rpc(&json! ({ + "userpass": mm_node_1.userpass, + "mmrpc": "2.0", + "method": "lightning::add_trusted_node", + "params": { + "coin": "tBTC-TEST-lightning", + "node_id": node_2_id + }, + }))) + .unwrap(); + assert!( + add_trusted_node.0.is_success(), + "!lightning::add_trusted_node: {}", + add_trusted_node.1 + ); + + let open_channel = block_on(mm_node_2.rpc(&json! ({ + "userpass": mm_node_2.userpass, + "mmrpc": "2.0", + "method": "lightning::open_channel", + "params": { + "coin": "tBTC-TEST-lightning", + "node_address": node_1_address, + "amount": { + "type": "Exact", + "value": 0.0002, + }, + }, + }))) + .unwrap(); + assert!( + open_channel.0.is_success(), + "!lightning::open_channel: {}", + open_channel.1 + ); + + block_on(mm_node_2.wait_for_log(60., |log| log.contains("Received message ChannelReady"))).unwrap(); // Enable coins on mm_node_1 side. Print the replies in case we need the "address". log!( From 9ccada116d91915893aed53c4d2380de7c3a106b Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 10 Oct 2022 15:25:57 +0200 Subject: [PATCH 25/36] more refactors, broadcast_p2p_tx_msg shouldn't work with lightning payments, better todo comments --- mm2src/coins/lightning.rs | 2 +- mm2src/coins/lp_coins.rs | 5 ++++ mm2src/coins/my_tx_history_v2.rs | 4 +-- mm2src/coins/utxo/utxo_common.rs | 4 +-- mm2src/mm2_main/src/lp_swap.rs | 9 +++++-- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 9 +++---- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 1 - mm2src/mm2_main/src/lp_swap/taker_swap.rs | 28 ++++++++++----------- 8 files changed, 34 insertions(+), 28 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 48cad0fe8d..56e903c326 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -622,7 +622,7 @@ impl SwapOps for LightningCoin { unimplemented!(); } - // Todo: This is None for now for the sake of swap P.O.C., this should be implemented probably in next PRs + // Todo: This is None for now for the sake of swap P.O.C., this should be implemented probably in next PRs and should be tested across restarts fn check_if_my_payment_sent( &self, _time_lock: u32, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 36ce0b98e9..f29ef3c121 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -369,6 +369,11 @@ ifrom!(TransactionEnum, ZTransaction); #[cfg(not(target_arch = "wasm32"))] ifrom!(TransactionEnum, LightningPayment); +impl TransactionEnum { + #[cfg(not(target_arch = "wasm32"))] + pub fn is_lightning(&self) -> bool { matches!(self, TransactionEnum::LightningPayment(_)) } +} + // NB: When stable and groked by IDEs, `enum_dispatch` can be used instead of `Deref` to speed things up. impl Deref for TransactionEnum { type Target = dyn Transaction; diff --git a/mm2src/coins/my_tx_history_v2.rs b/mm2src/coins/my_tx_history_v2.rs index c98466c5d5..bef6c844e3 100644 --- a/mm2src/coins/my_tx_history_v2.rs +++ b/mm2src/coins/my_tx_history_v2.rs @@ -222,8 +222,8 @@ impl<'a, Addr: Clone + DisplayAddress + Eq + std::hash::Hash, Tx: Transaction> T TransactionDetails { coin: self.coin, - // Todo: remove unwrap, although it should be safe here - tx_hex: self.tx.tx_hex().unwrap().into(), + // It should be safe to unwrap here + tx_hex: self.tx.tx_hex().expect("tx_hex shouldn't be None!").into(), tx_hash: tx_hash.to_tx_hash(), from, to, diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 131bf88b4e..9f13b39b11 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2092,8 +2092,8 @@ pub fn wait_for_output_spend( pub fn tx_enum_from_bytes(coin: &UtxoCoinFields, bytes: &[u8]) -> Result> { let mut transaction: UtxoTx = deserialize(bytes).map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string()))?; - // Todo: remove unwrap, although it's safe to use here - let serialized_length = transaction.tx_hex().unwrap().len(); + // It should be safe to unwrap here + let serialized_length = transaction.tx_hex().expect("tx_hex shouldn't be None!").len(); if bytes.len() != serialized_length { return MmError::err(TxMarshalingErr::CrossCheckFailed(format!( "Expected '{}' lenght of the serialized transaction, found '{}'", diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 51486b83fb..631b797837 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -190,6 +190,11 @@ pub fn broadcast_swap_message(ctx: &MmArc, topic: String, msg: T, /// Broadcast the tx message once pub fn broadcast_p2p_tx_msg(ctx: &MmArc, topic: String, msg: &TransactionEnum, p2p_privkey: &Option) { + #[cfg(not(target_arch = "wasm32"))] + if msg.is_lightning() { + return; + } + let (p2p_private, from) = match p2p_privkey { Some(keypair) => (keypair.private_bytes(), Some(keypair.libp2p_peer_id())), None => (ctx.secp256k1_key_pair().private().secret.take(), None), @@ -739,6 +744,7 @@ pub struct TransactionIdentifier { } impl TransactionIdentifier { + // Todo: find a better name for this fn tx_hex(&self) -> BytesJson { let tx_hash = self.tx_hash.clone(); self.tx_hex.clone().unwrap_or(tx_hash) @@ -1189,8 +1195,7 @@ pub async fn recover_funds_of_swap(ctx: MmArc, req: Json) -> Result (), Err(e) => { return Ok((Some(MakerSwapCommand::Finish), vec![ - // Todo: maybe add a different event for this?? MakerSwapEvent::TakerFeeValidateFailed(e.to_string().into()), ])); }, @@ -730,7 +729,6 @@ impl MakerSwap { let secret_hash = self.secret_hash(); let unique_data = self.unique_swap_data(); - // Todo: For lightning do this check if payment is sent/failed/pending??? what to do in each case?, should I test payments/events across restarts??? let transaction_f = self .maker_coin .check_if_my_payment_sent( @@ -793,8 +791,6 @@ impl MakerSwap { let payment_data_msg = match self.get_my_payment_data().await { Ok(data) => data, Err(e) => { - // Todo: remove this debug after passing this stage in test - debug!("payment_data_msg error: {}", e); return Ok((Some(MakerSwapCommand::RefundMakerPayment), vec![ MakerSwapEvent::MakerPaymentDataSendFailed(e.to_string().into()), MakerSwapEvent::MakerPaymentWaitRefundStarted { @@ -1284,8 +1280,9 @@ impl MakerSwap { .await ); match maybe_maker_payment { - // Todo: remove this unwrap - Some(tx) => tx.tx_hex().unwrap(), + Some(tx) => try_s!(tx + .tx_hex() + .ok_or("recover_funds is not implemented for lightning swaps yet!")), None => return ERR!("Maker payment transaction was not found"), } }, diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 3f026c816a..265434cb84 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -39,7 +39,6 @@ pub struct WatcherMut { secret: H256Json, } -// Todo: how to use payment instructions with watchers?? is it even needed?? what about lightning payments?? (it's instant to pay for the taker) #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct TakerSwapWatcherData { pub uuid: Uuid, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index f639ff3491..ef69bf1738 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1191,7 +1191,6 @@ impl TakerSwap { Ok(None) => (), Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ - // Todo: maybe add a different event for this?? TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), ])); }, @@ -1306,7 +1305,6 @@ impl TakerSwap { } let unique_data = self.unique_swap_data(); - // Todo: For lightning do this check if payment is sent/failed/pending??? what to do in each case?, should I test payments/events across restarts??? let f = self.taker_coin.check_if_my_payment_sent( self.r().data.taker_payment_lock as u32, self.r().other_taker_coin_htlc_pub.as_slice(), @@ -1357,8 +1355,9 @@ impl TakerSwap { }; let mut swap_events = vec![TakerSwapEvent::TakerPaymentSent(tx_ident)]; - // Watchers shouldn't be used with lightning payments for now - // Todo: refactor this and check if watcher can work in some cases with lightning + // Watchers cannot be used for lightning swaps for now + // Todo: Check if watchers can work in some cases with lightning and implement it if it's possible, the watcher will not be able to retrieve the preimage since it's retrieved through the lightning network + // Todo: The watcher can retrieve the preimage only if he is running a lightning node and is part of the nodes that routed the taker payment which is a very low probability event that shouldn't be considered let maybe_maker_hex = self.r().maker_payment.as_ref().unwrap().tx_hex.clone(); if let Some(maker_hex) = maybe_maker_hex { if let Some(taker_hex) = tx_hex { @@ -1371,10 +1370,9 @@ impl TakerSwap { &self.unique_swap_data()[..], ); - match preimage_fut.compat().await { - Ok(preimage) => { - // Todo: revise if it's safe to unwrap here - let watcher_data = self.create_watcher_data(taker_hex.0, preimage.tx_hex().unwrap()); + match preimage_fut.compat().await.map(|p| p.tx_hex()) { + Ok(Some(preimage)) => { + let watcher_data = self.create_watcher_data(taker_hex.0, preimage.clone()); let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); broadcast_swap_message( &self.ctx, @@ -1382,9 +1380,9 @@ impl TakerSwap { swpmsg_watcher, &self.p2p_privkey, ); - // Todo: revise if it's safe to unwrap here - swap_events.push(TakerSwapEvent::WatcherMessageSent(Some(preimage.tx_hex().unwrap()))) + swap_events.push(TakerSwapEvent::WatcherMessageSent(Some(preimage))) }, + Ok(None) => (), Err(e) => error!( "The watcher message could not be sent, error creating the taker spends maker payment preimage: {}", e.get_plain_text_format() @@ -1399,7 +1397,8 @@ impl TakerSwap { async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.clone(); let mut watcher_broadcast_abort_handle = None; - // Todo: refactor this + // Watchers cannot be used for lightning swaps for now + // Todo: Check if watchers can work in some cases with lightning and implement it if it's possible, this part will probably work if only the taker is lightning since the preimage is available if let Some(hex) = tx_hex { if self.ctx.use_watchers() { let preimage_hex = self.r().taker_spends_maker_payment_preimage.clone(); @@ -1593,7 +1592,6 @@ impl TakerSwap { }, }; - // Todo: check broadcast_p2p_tx_msg to not broadcast lightning payments broadcast_p2p_tx_msg( &self.ctx, tx_helper_topic(self.taker_coin.ticker()), @@ -1772,8 +1770,10 @@ impl TakerSwap { .await ); match maybe_sent { - // Todo: remove this unwrap - Some(tx) => tx.tx_hex().unwrap(), + // Todo: Refactor this when implementing recover_funds for lightning swaps + Some(tx) => try_s!(tx + .tx_hex() + .ok_or("recover_funds is not implemented for lightning swaps yet!")), None => return ERR!("Taker payment is not found, swap is not recoverable"), } }, From f108dfcecae8b615627007d669370e7074d035e7 Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 10 Oct 2022 19:29:19 +0200 Subject: [PATCH 26/36] add preimage of swap payment to DB after the secret is revealed (claiming funds) --- mm2src/coins/lightning.rs | 11 ++++++- mm2src/coins/lightning/ln_db.rs | 7 +++++ mm2src/coins/lightning/ln_events.rs | 7 +---- mm2src/coins/lightning/ln_sql.rs | 46 +++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 56e903c326..a1904476aa 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -501,7 +501,16 @@ impl SwapOps for LightningCoin { let coin = self.clone(); let fut = async move { - coin.channel_manager.claim_funds(PaymentPreimage(preimage)); + let payment_preimage = PaymentPreimage(preimage); + coin.channel_manager.claim_funds(payment_preimage); + coin.db + .update_payment_preimage_in_db(payment_hash, payment_preimage) + .await + .error_log_with_msg(&format!( + "Unable to update payment {} information in DB with preimage: {}!", + hex::encode(payment_hash.0), + hex::encode(preimage) + )); Ok(TransactionEnum::LightningPayment(payment_hash)) }; Box::new(fut.boxed().compat()) diff --git a/mm2src/coins/lightning/ln_db.rs b/mm2src/coins/lightning/ln_db.rs index 2f1eb38b78..722e6ab2ac 100644 --- a/mm2src/coins/lightning/ln_db.rs +++ b/mm2src/coins/lightning/ln_db.rs @@ -232,6 +232,13 @@ pub trait LightningDB { /// Inserts or updates a new payment record in the DB. async fn add_or_update_payment_in_db(&self, info: PaymentInfo) -> Result<(), Self::Error>; + /// Updates a payment's preimage in DB by the payment's hash. + async fn update_payment_preimage_in_db( + &self, + hash: PaymentHash, + preimage: PaymentPreimage, + ) -> Result<(), Self::Error>; + /// Gets a payment's record from DB by the payment's hash. async fn get_payment_from_db(&self, hash: PaymentHash) -> Result, Self::Error>; diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index b50c848c47..e509dc4c5e 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -345,21 +345,16 @@ impl LightningEventHandler { Some(preimage) => *preimage, // This is a swap related payment since we don't have the preimage yet None => { - // Todo: preimage should be saved to db after it's received (after claim_funds and make payment successful) let payment_info = PaymentInfo { payment_hash: *payment_hash, payment_type: PaymentType::InboundPayment, - // Todo: maybe add this to db on create_invoice_for_hash, should complete wait for confirmation logic, etc.. - description: "".into(), + description: "Swap Payment".into(), preimage: None, - // Todo: maybe add this to db on create_invoice_for_hash secret: None, amt_msat: Some(received_amount as i64), fee_paid_msat: None, status: HTLCStatus::Received, - // Todo: maybe add this to db on create_invoice_for_hash created_at: (now_ms() / 1000) as i64, - // Todo: maybe add this to db on create_invoice_for_hash last_updated: (now_ms() / 1000) as i64, }; let db = self.db.clone(); diff --git a/mm2src/coins/lightning/ln_sql.rs b/mm2src/coins/lightning/ln_sql.rs index 7809c3e85c..a1da1d9145 100644 --- a/mm2src/coins/lightning/ln_sql.rs +++ b/mm2src/coins/lightning/ln_sql.rs @@ -119,6 +119,21 @@ fn upsert_payment_sql(for_coin: &str) -> Result { Ok(sql) } +fn update_payment_preimage_sql(for_coin: &str) -> Result { + let table_name = payments_history_table(for_coin); + validate_table_name(&table_name)?; + + let sql = format!( + "UPDATE {} SET + preimage = ?1 + WHERE + payment_hash = ?2;", + table_name + ); + + Ok(sql) +} + fn select_channel_by_rpc_id_sql(for_coin: &str) -> Result { let table_name = channels_history_table(for_coin); validate_table_name(&table_name)?; @@ -803,6 +818,27 @@ impl LightningDB for SqliteLightningDB { .await } + async fn update_payment_preimage_in_db( + &self, + hash: PaymentHash, + preimage: PaymentPreimage, + ) -> Result<(), Self::Error> { + let for_coin = self.db_ticker.clone(); + let payment_hash = hex::encode(hash.0); + let preimage = hex::encode(preimage.0); + + let sqlite_connection = self.sqlite_connection.clone(); + async_blocking(move || { + let params = [&preimage as &dyn ToSql, &payment_hash as &dyn ToSql]; + let mut conn = sqlite_connection.lock().unwrap(); + let sql_transaction = conn.transaction()?; + sql_transaction.execute(&update_payment_preimage_sql(&for_coin)?, ¶ms)?; + sql_transaction.commit()?; + Ok(()) + }) + .await + } + async fn get_payment_from_db(&self, hash: PaymentHash) -> Result, Self::Error> { let params = [hex::encode(hash.0)]; let sql = select_payment_by_hash_sql(self.db_ticker.as_str())?; @@ -1173,6 +1209,16 @@ mod tests { let actual_payment_info = block_on(db.get_payment_from_db(PaymentHash([1; 32]))).unwrap().unwrap(); assert_eq!(expected_payment_info, actual_payment_info); + + // Test update_payment_preimage_in_db + let new_preimage = PaymentPreimage([4; 32]); + block_on(db.update_payment_preimage_in_db(PaymentHash([1; 32]), new_preimage)).unwrap(); + let preimage_after_update = block_on(db.get_payment_from_db(PaymentHash([1; 32]))) + .unwrap() + .unwrap() + .preimage + .unwrap(); + assert_eq!(new_preimage, preimage_after_update); } #[test] From 6d622f62056b52b3d36d4d5c6d2f794281deb98f Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 11 Oct 2022 11:12:35 +0200 Subject: [PATCH 27/36] wip --- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index c8e4cd13ef..a4afb8dafe 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -26,7 +26,6 @@ pub enum SavedSwapError { InternalError(String), } -// Todo: check backward compatibility for loading swaps from files, etc.. #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum SavedSwap { From a4e970cd38e15b51fc98a24196cefc68c6225c02 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 11 Oct 2022 16:46:10 +0200 Subject: [PATCH 28/36] Final refactors --- mm2src/coins/lightning/ln_utils.rs | 8 +++--- mm2src/mm2_main/src/lp_swap.rs | 5 +--- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 22 +++++++--------- .../src/lp_swap/recreate_swap_data.rs | 7 +++-- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 2 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 26 +++++++++++-------- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index b3038a5550..37d8364873 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -245,8 +245,7 @@ pub async fn get_open_channels_nodes_addresses( Ok(nodes_addresses) } -// Todo: Make this public by opening a PR in rust-lightning instead of importing it here -// Todo: can a node that has all private channels send wrong data (we need to validate the invoice on the receiver side, if no public channels then trust the sender??) +// Todo: Make this public in rust-lightning by opening a PR there instead of importing it here /// Filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include /// in the invoice. /// @@ -267,8 +266,9 @@ pub(crate) fn filter_channels(channels: Vec, min_inbound_capacit continue; } - // Todo: maybe check for inbound_capacity_msat first?? can this be used for probing?? I don't think so but how can it be avoided?? - // Todo: we need the side who is revealing the private channel to check that the other side has the balance on-chain and not someone trying to probe for private channels + // Todo: if all public channels have inbound_capacity_msat less than min_inbound_capacity we need to give the user the option to reveal his/her private channels to the swap counterparty in this case or not + // Todo: the problem with revealing the private channels in the swap message (invoice) is that it can be used by malicious nodes to probe for private channels so maybe there should be a + // Todo: requirement that the other party has the amount required to be sent in the swap first (do we have a way to check if the other side of the swap has the balance required for the swap on-chain or not) if channel.is_public { // If any public channel exists, return no hints and let the sender // look at the public channels instead. diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 631b797837..8a580afda2 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -744,8 +744,7 @@ pub struct TransactionIdentifier { } impl TransactionIdentifier { - // Todo: find a better name for this - fn tx_hex(&self) -> BytesJson { + fn tx_hex_or_hash(&self) -> BytesJson { let tx_hash = self.tx_hash.clone(); self.tx_hex.clone().unwrap_or(tx_hash) } @@ -1295,7 +1294,6 @@ impl SecretHashAlgo { } // Todo: Maybe add a secret_hash_algo method to the SwapOps trait instead -// Todo: both sides don't need to use SHA256 in the payment script if only one side requires it (check send_taker_payment if SecretHashAlgo::SHA256) #[cfg(not(target_arch = "wasm32"))] fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> SecretHashAlgo { match (maker_coin, taker_coin) { @@ -1306,7 +1304,6 @@ fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> } } -// Todo: Maybe add a secret_hash_algo method to the SwapOps trait instead #[cfg(target_arch = "wasm32")] fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> SecretHashAlgo { match (maker_coin, taker_coin) { diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 15529c5f5e..2f629ba26b 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -410,14 +410,12 @@ impl MakerSwap { } async fn get_my_payment_data(&self) -> Result> { - // Todo: is it needed? the taker has the payment hash that was used in the invoice using the secret sent by the maker anyways, need to recheck this along with if secret_hash new implementation is needed?? - // Todo: recheck the other get_my_payment_data too // If maker payment is a lightning payment the payment hash will be sent in the message - let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex().0; + // It's not really needed here unlike in TakerFee msg since the hash is included in the invoice/payment_instructions but it's kept for symmetry + let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex_or_hash().0; let hash_algo = SecretHashAlgo::SHA256; if let Some(instructions) = self .taker_coin - // Todo: revise this with let hash_algo = SecretHashAlgo::SHA256; .payment_instructions(&hash_algo.hash_secret(self.secret.as_slice()), &self.taker_amount) .await? { @@ -805,7 +803,7 @@ impl MakerSwap { let maker_payment_wait_confirm = self.r().data.started_at + (self.r().data.lock_duration * 2) / 5; let f = self.maker_coin.wait_for_confirmations( - &self.r().maker_payment.clone().unwrap().tx_hex(), + &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), self.r().data.maker_payment_confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), maker_payment_wait_confirm, @@ -879,7 +877,7 @@ impl MakerSwap { let wait_f = self .taker_coin .wait_for_confirmations( - &self.r().taker_payment.clone().unwrap().tx_hex(), + &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), confirmations, self.r().data.taker_payment_requires_nota.unwrap_or(false), wait_taker_payment, @@ -898,7 +896,7 @@ impl MakerSwap { } let validate_input = ValidatePaymentInput { - payment_tx: self.r().taker_payment.clone().unwrap().tx_hex().0, + payment_tx: self.r().taker_payment.clone().unwrap().tx_hex_or_hash().0, time_lock: self.taker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_taker_coin_htlc_pub.to_vec(), unique_swap_data: self.unique_swap_data(), @@ -946,7 +944,7 @@ impl MakerSwap { } let spend_fut = self.taker_coin.send_maker_spends_taker_payment( - &self.r().taker_payment.clone().unwrap().tx_hex(), + &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), self.taker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_taker_coin_htlc_pub, &self.r().data.secret.0, @@ -1006,7 +1004,7 @@ impl MakerSwap { let confirmations = std::cmp::min(1, self.r().data.taker_payment_confirmations); let requires_nota = false; let wait_fut = self.taker_coin.wait_for_confirmations( - &self.r().taker_payment_spend.clone().unwrap().tx_hex(), + &self.r().taker_payment_spend.clone().unwrap().tx_hex_or_hash(), confirmations, requires_nota, self.wait_refund_until(), @@ -1049,7 +1047,7 @@ impl MakerSwap { } let spend_fut = self.maker_coin.send_maker_refunds_payment( - &self.r().maker_payment.clone().unwrap().tx_hex(), + &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), self.r().data.maker_payment_lock as u32, &*self.r().other_maker_coin_htlc_pub, self.secret_hash().as_slice(), @@ -1183,7 +1181,7 @@ impl MakerSwap { .taker_payment .clone() .ok_or(ERRL!("No info about taker payment, swap is not recoverable"))? - .tx_hex(); + .tx_hex_or_hash(); // have to do this because std::sync::RwLockReadGuard returned by r() is not Send, // so it can't be used across await @@ -1264,7 +1262,7 @@ impl MakerSwap { let maybe_maker_payment = self.r().maker_payment.clone(); let maker_payment = match maybe_maker_payment { - Some(tx) => tx.tx_hex().0, + Some(tx) => tx.tx_hex_or_hash().0, None => { let maybe_maker_payment = try_s!( self.maker_coin 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 8b5039293c..c811ef833f 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -212,7 +212,6 @@ fn convert_taker_to_maker_events( // Finish processing Taker events. return events; }, - // Todo: does the taker need to retrieve the invoice from here?? also look in maker_swap.rs for mirror TakerSwapEvent::MakerPaymentReceived(tx_ident) => { if let Some(taker_fee_ident) = taker_fee_ident.take() { push_event!(MakerSwapEvent::TakerFeeValidated(taker_fee_ident)); @@ -261,7 +260,7 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::StartFailed(_) | TakerSwapEvent::Negotiated(_) | TakerSwapEvent::NegotiateFailed(_) - // Todo: add a note here to describe why this is not needed for lightning and that it might be needed if instructions is used later depending on the situation + // Todo: This may be used when implementing convert_taker_to_maker_events for lightning | TakerSwapEvent::TakerPaymentInstructionsReceived(_) | TakerSwapEvent::MakerPaymentWaitConfirmStarted | TakerSwapEvent::MakerPaymentValidatedAndConfirmed @@ -449,7 +448,7 @@ async fn convert_maker_to_taker_events( return events; }, MakerSwapEvent::TakerPaymentSpent(tx_ident) => { - let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()).await { + let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex_or_hash()).await { Ok(secret) => H256Json::from(secret.as_slice()), Err(e) => { push_event!(TakerSwapEvent::TakerPaymentWaitForSpendFailed(ERRL!("{}", e).into())); @@ -467,7 +466,7 @@ async fn convert_maker_to_taker_events( | MakerSwapEvent::StartFailed(_) | MakerSwapEvent::Negotiated(_) | MakerSwapEvent::NegotiateFailed(_) - // Todo: add a note here to describe why this is not needed for lightning and that it might be needed if instructions is used later depending on the situation + // Todo: This may be used when implementing convert_maker_to_taker_events for lightning | MakerSwapEvent::MakerPaymentInstructionsReceived(_) | MakerSwapEvent::TakerPaymentWaitConfirmStarted | MakerSwapEvent::TakerPaymentValidatedAndConfirmed diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 265434cb84..5d7818c9b5 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -276,7 +276,7 @@ impl Watcher { let secret = match self .taker_coin - .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex()) + .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex_or_hash()) .await { Ok(bytes) => H256Json::from(bytes.as_slice()), diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index ef69bf1738..0dee243c9f 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -851,7 +851,7 @@ impl TakerSwap { async fn get_my_payment_data(&self) -> Result> { // If taker fee is a lightning payment the payment hash will be sent in the message - let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex().0; + let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex_or_hash().0; let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); if let Some(instructions) = self @@ -1225,7 +1225,7 @@ impl TakerSwap { info!("Before wait confirm"); let confirmations = self.r().data.maker_payment_confirmations; let f = self.maker_coin.wait_for_confirmations( - &self.r().maker_payment.clone().unwrap().tx_hex(), + &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), self.r().data.maker_payment_wait, @@ -1241,7 +1241,7 @@ impl TakerSwap { info!("After wait confirm"); let validate_input = ValidatePaymentInput { - payment_tx: self.r().maker_payment.clone().unwrap().tx_hex().0, + payment_tx: self.r().maker_payment.clone().unwrap().tx_hex_or_hash().0, time_lock: self.maker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_maker_coin_htlc_pub.to_vec(), secret_hash: self.r().secret_hash.0.to_vec(), @@ -1417,7 +1417,7 @@ impl TakerSwap { } // Todo: taker_payment should be a message on lightning network not a swap message - let msg = SwapMsg::TakerPayment(self.r().taker_payment.as_ref().unwrap().tx_hex().0); + let msg = SwapMsg::TakerPayment(self.r().taker_payment.as_ref().unwrap().tx_hex_or_hash().0); let send_abort_handle = Some(broadcast_swap_message_every( self.ctx.clone(), swap_topic(&self.uuid), @@ -1431,7 +1431,7 @@ impl TakerSwap { let wait_f = self .taker_coin .wait_for_confirmations( - &self.r().taker_payment.clone().unwrap().tx_hex(), + &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), self.r().data.taker_payment_confirmations, self.r().data.taker_payment_requires_nota.unwrap_or(false), wait_taker_payment, @@ -1450,7 +1450,7 @@ impl TakerSwap { } let f = self.taker_coin.wait_for_tx_spend( - &self.r().taker_payment.clone().unwrap().tx_hex(), + &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), self.r().data.taker_payment_lock, self.r().data.taker_coin_start_block, &self.r().data.taker_coin_swap_contract_address, @@ -1476,7 +1476,11 @@ impl TakerSwap { }; let secret_hash = self.r().secret_hash.clone(); - let secret = match self.taker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex()).await { + let secret = match self + .taker_coin + .extract_secret(&secret_hash.0, &tx_ident.tx_hex_or_hash()) + .await + { Ok(bytes) => H256Json::from(bytes.as_slice()), Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ @@ -1501,7 +1505,7 @@ impl TakerSwap { ])); } let spend_fut = self.maker_coin.send_taker_spends_maker_payment( - &self.r().maker_payment.clone().unwrap().tx_hex(), + &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), self.maker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_maker_coin_htlc_pub, &self.r().secret.0, @@ -1566,7 +1570,7 @@ impl TakerSwap { } let refund_fut = self.taker_coin.send_taker_refunds_payment( - &self.r().taker_payment.clone().unwrap().tx_hex(), + &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), self.r().data.taker_payment_lock as u32, &*self.r().other_taker_coin_htlc_pub, &self.r().secret_hash.0, @@ -1701,7 +1705,7 @@ impl TakerSwap { } let maker_payment = match &self.r().maker_payment { - Some(tx) => tx.tx_hex(), + Some(tx) => tx.tx_hex_or_hash(), None => return ERR!("No info about maker payment, swap is not recoverable"), }; @@ -1754,7 +1758,7 @@ impl TakerSwap { let maybe_taker_payment = self.r().taker_payment.clone(); let taker_payment = match maybe_taker_payment { - Some(tx) => tx.tx_hex().0, + Some(tx) => tx.tx_hex_or_hash().0, None => { let maybe_sent = try_s!( self.taker_coin From d2f8158f80ee573d8766c92d107d6aef5d9a3eb9 Mon Sep 17 00:00:00 2001 From: shamardy Date: Wed, 12 Oct 2022 15:30:12 +0200 Subject: [PATCH 29/36] Review fixes: better names for some functions/structs --- mm2src/coins/lp_coins.rs | 5 ++- mm2src/mm2_main/src/lp_swap.rs | 41 +++++++++++------------ mm2src/mm2_main/src/lp_swap/maker_swap.rs | 12 +++---- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 18 +++++----- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 9ec36b59fc..998dc24058 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -371,7 +371,10 @@ ifrom!(TransactionEnum, LightningPayment); impl TransactionEnum { #[cfg(not(target_arch = "wasm32"))] - pub fn is_lightning(&self) -> bool { matches!(self, TransactionEnum::LightningPayment(_)) } + pub fn supports_tx_helper(&self) -> bool { !matches!(self, TransactionEnum::LightningPayment(_)) } + + #[cfg(target_arch = "wasm32")] + pub fn supports_tx_helper(&self) -> bool { true } } // NB: When stable and groked by IDEs, `enum_dispatch` can be used instead of `Deref` to speed things up. diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 8a580afda2..ad36bdccce 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -135,8 +135,8 @@ pub enum SwapMsg { Negotiation(NegotiationDataMsg), NegotiationReply(NegotiationDataMsg), Negotiated(bool), - TakerFee(PaymentDataMsg), - MakerPayment(PaymentDataMsg), + TakerFee(SwapTxDataMsg), + MakerPayment(SwapTxDataMsg), TakerPayment(Vec), } @@ -145,8 +145,8 @@ pub struct SwapMsgStore { negotiation: Option, negotiation_reply: Option, negotiated: Option, - taker_fee: Option, - maker_payment: Option, + taker_fee: Option, + maker_payment: Option, taker_payment: Option>, accept_only_from: bits256, } @@ -190,8 +190,7 @@ pub fn broadcast_swap_message(ctx: &MmArc, topic: String, msg: T, /// Broadcast the tx message once pub fn broadcast_p2p_tx_msg(ctx: &MmArc, topic: String, msg: &TransactionEnum, p2p_privkey: &Option) { - #[cfg(not(target_arch = "wasm32"))] - if msg.is_lightning() { + if !msg.supports_tx_helper() { return; } @@ -704,32 +703,32 @@ impl NegotiationDataMsg { #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] pub struct PaymentDataV2 { data: Vec, - // Instructions for the other side whether taker or maker on how to make it's payment. + // Next step instructions for the other side whether taker or maker. // An example for this is a maker/taker sending the taker/maker a lightning invoice to be payed. - instructions: Vec, + next_step_instructions: Vec, } #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] #[serde(untagged)] -pub enum PaymentDataMsg { +pub enum SwapTxDataMsg { V1(Vec), V2(PaymentDataV2), } -impl PaymentDataMsg { +impl SwapTxDataMsg { #[inline] pub fn data(&self) -> &[u8] { match self { - PaymentDataMsg::V1(v1) => v1, - PaymentDataMsg::V2(v2) => &v2.data, + SwapTxDataMsg::V1(v1) => v1, + SwapTxDataMsg::V2(v2) => &v2.data, } } #[inline] pub fn instructions(&self) -> Option<&[u8]> { match self { - PaymentDataMsg::V1(_) => None, - PaymentDataMsg::V2(v2) => Some(&v2.instructions), + SwapTxDataMsg::V1(_) => None, + SwapTxDataMsg::V2(v2) => Some(&v2.next_step_instructions), } } } @@ -1625,7 +1624,7 @@ mod lp_swap_tests { // old message format should be deserialized to PaymentDataMsg::V1 let old = SwapMsgOld::MakerPayment(vec![1; 300]); - let expected = SwapMsg::MakerPayment(PaymentDataMsg::V1(vec![1; 300])); + let expected = SwapMsg::MakerPayment(SwapTxDataMsg::V1(vec![1; 300])); let serialized = rmp_serde::to_vec(&old).unwrap(); @@ -1634,7 +1633,7 @@ mod lp_swap_tests { assert_eq!(deserialized, expected); // PaymentDataMsg::V1 should be deserialized to old message format - let v1 = SwapMsg::MakerPayment(PaymentDataMsg::V1(vec![1; 300])); + let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::V1(vec![1; 300])); let expected = old; @@ -1645,7 +1644,7 @@ mod lp_swap_tests { assert_eq!(deserialized, expected); // PaymentDataMsg::V1 should be deserialized to PaymentDataMsg::V1 - let v1 = SwapMsg::MakerPayment(PaymentDataMsg::V1(vec![1; 300])); + let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::V1(vec![1; 300])); let serialized = rmp_serde::to_vec(&v1).unwrap(); @@ -1654,9 +1653,9 @@ mod lp_swap_tests { assert_eq!(deserialized, v1); // PaymentDataMsg::V2 should be deserialized to PaymentDataMsg::V2 - let v2 = SwapMsg::MakerPayment(PaymentDataMsg::V2(PaymentDataV2 { + let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::V2(PaymentDataV2 { data: vec![1; 300], - instructions: vec![1; 300], + next_step_instructions: vec![1; 300], })); let serialized = rmp_serde::to_vec(&v2).unwrap(); @@ -1666,9 +1665,9 @@ mod lp_swap_tests { assert_eq!(deserialized, v2); // PaymentDataMsg::V2 shouldn't be deserialized to old message format, new nodes with payment instructions can't swap with old nodes without it. - let v2 = SwapMsg::MakerPayment(PaymentDataMsg::V2(PaymentDataV2 { + let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::V2(PaymentDataV2 { data: vec![1; 300], - instructions: vec![1; 300], + next_step_instructions: vec![1; 300], })); let serialized = rmp_serde::to_vec(&v2).unwrap(); diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index a9d4741189..7b6883bedf 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -6,9 +6,9 @@ use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePr use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_message_every, check_other_coin_balance_for_swap, detect_secret_hash_algo, dex_fee_amount_from_taker_coin, get_locked_amount, recv_swap_msg, swap_topic, tx_helper_topic, AtomicSwap, LockedAmount, MySwapInfo, - NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, PaymentDataMsg, PaymentDataV2, RecoveredSwap, + NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, PaymentDataV2, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, SecretHashAlgo, SwapConfirmationsSettings, - SwapError, SwapMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; + SwapError, SwapMsg, SwapTxDataMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; @@ -409,7 +409,7 @@ impl MakerSwap { } } - async fn get_my_payment_data(&self) -> Result> { + async fn get_my_payment_data(&self) -> Result> { // If maker payment is a lightning payment the payment hash will be sent in the message // It's not really needed here unlike in TakerFee msg since the hash is included in the invoice/payment_instructions but it's kept for symmetry let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex_or_hash().0; @@ -419,12 +419,12 @@ impl MakerSwap { .payment_instructions(&hash_algo.hash_secret(self.secret.as_slice()), &self.taker_amount) .await? { - Ok(PaymentDataMsg::V2(PaymentDataV2 { + Ok(SwapTxDataMsg::V2(PaymentDataV2 { data: payment_data, - instructions, + next_step_instructions: instructions, })) } else { - Ok(PaymentDataMsg::V1(payment_data)) + Ok(SwapTxDataMsg::V1(payment_data)) } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index d8051c1bbf..7fe834fa41 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -7,8 +7,8 @@ use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePr use super::{broadcast_my_swap_status, broadcast_swap_message, broadcast_swap_message_every, check_other_coin_balance_for_swap, dex_fee_amount_from_taker_coin, dex_fee_rate, dex_fee_threshold, get_locked_amount, recv_swap_msg, swap_topic, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, - NegotiationDataV2, NegotiationDataV3, PaymentDataMsg, PaymentDataV2, RecoveredSwap, RecoveredSwapAction, - SavedSwap, SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapsContext, + NegotiationDataV2, NegotiationDataV3, PaymentDataV2, RecoveredSwap, RecoveredSwapAction, SavedSwap, + SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapTxDataMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MatchBy, OrderConfirmationsSettings, TakerAction, TakerOrderBuilder}; @@ -849,9 +849,9 @@ impl TakerSwap { } } - async fn get_my_payment_data(&self) -> Result> { + async fn get_taker_fee_data(&self) -> Result> { // If taker fee is a lightning payment the payment hash will be sent in the message - let payment_data = self.r().taker_fee.as_ref().unwrap().tx_hex_or_hash().0; + let taker_fee_data = self.r().taker_fee.as_ref().unwrap().tx_hex_or_hash().0; let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); if let Some(instructions) = self @@ -859,12 +859,12 @@ impl TakerSwap { .payment_instructions(&secret_hash, &maker_amount) .await? { - Ok(PaymentDataMsg::V2(PaymentDataV2 { - data: payment_data, - instructions, + Ok(SwapTxDataMsg::V2(PaymentDataV2 { + data: taker_fee_data, + next_step_instructions: instructions, })) } else { - Ok(PaymentDataMsg::V1(payment_data)) + Ok(SwapTxDataMsg::V1(taker_fee_data)) } } @@ -1163,7 +1163,7 @@ impl TakerSwap { async fn wait_for_maker_payment(&self) -> Result<(Option, Vec), String> { const MAKER_PAYMENT_WAIT_TIMEOUT: u64 = 600; - let payment_data_msg = match self.get_my_payment_data().await { + let payment_data_msg = match self.get_taker_fee_data().await { Ok(data) => data, Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ From 60acd0af21dda65dbe8f43ca9a1fa6994b22571d Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 13 Oct 2022 18:13:46 +0200 Subject: [PATCH 30/36] Review fixes: some errors fixes, add channels::, nodes::, payments:: namespaces to the RPC methods --- mm2src/coins/lightning/ln_events.rs | 20 ++++-- mm2src/coins/lightning/ln_p2p.rs | 10 +-- mm2src/coins/rpc_command/lightning/mod.rs | 45 ++++++++++---- .../mm2_main/src/mm2_tests/lightning_tests.rs | 48 ++++++++------- .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 61 ++++++------------- 5 files changed, 99 insertions(+), 85 deletions(-) diff --git a/mm2src/coins/lightning/ln_events.rs b/mm2src/coins/lightning/ln_events.rs index e509dc4c5e..c0a7ebfea0 100644 --- a/mm2src/coins/lightning/ln_events.rs +++ b/mm2src/coins/lightning/ln_events.rs @@ -17,7 +17,7 @@ use parking_lot::Mutex as PaMutex; use rand::Rng; use script::{Builder, SignatureVersion}; use secp256k1v22::Secp256k1; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::sync::Arc; use utxo_signer::with_key_pair::sign_tx; @@ -185,6 +185,8 @@ pub enum SignFundingTransactionError { Internal(String), #[display(fmt = "Error converting transaction: {}", _0)] ConvertTxErr(String), + #[display(fmt = "Error signing transaction: {}", _0)] + TxSignFailed(String), } // Generates the raw funding transaction with one output equal to the channel value. @@ -227,7 +229,7 @@ fn sign_funding_transaction( SignatureVersion::WitnessV0, coin.as_ref().conf.fork_id, ) - .map_err(|e| SignFundingTransactionError::Internal(e.to_string()))?; + .map_err(|e| SignFundingTransactionError::TxSignFailed(e.to_string()))?; Transaction::try_from(signed).map_err(|e| SignFundingTransactionError::ConvertTxErr(e.to_string())) } @@ -351,11 +353,19 @@ impl LightningEventHandler { description: "Swap Payment".into(), preimage: None, secret: None, - amt_msat: Some(received_amount as i64), + amt_msat: Some( + received_amount + .try_into() + .expect("received_amount shouldn't exceed i64::MAX"), + ), fee_paid_msat: None, status: HTLCStatus::Received, - created_at: (now_ms() / 1000) as i64, - last_updated: (now_ms() / 1000) as i64, + created_at: (now_ms() / 1000) + .try_into() + .expect("created_at shouldn't exceed i64::MAX"), + last_updated: (now_ms() / 1000) + .try_into() + .expect("last_updated shouldn't exceed i64::MAX"), }; let db = self.db.clone(); // Todo: Update this after stop-spawned-futures https://github.com/KomodoPlatform/atomicDEX-API/pull/1490 gets merged to dev diff --git a/mm2src/coins/lightning/ln_p2p.rs b/mm2src/coins/lightning/ln_p2p.rs index 9257bfc71e..2aa07e546b 100644 --- a/mm2src/coins/lightning/ln_p2p.rs +++ b/mm2src/coins/lightning/ln_p2p.rs @@ -31,8 +31,10 @@ pub enum ConnectToNodeRes { #[derive(Display)] pub enum ConnectionError { - #[display(fmt = "Connection error: {}", _0)] - ConnectionError(String), + #[display(fmt = "Handshake error: {}", _0)] + HandshakeErr(String), + #[display(fmt = "Timeout error: {}", _0)] + TimeOut(String), } pub async fn connect_to_ln_node( @@ -50,7 +52,7 @@ pub async fn connect_to_ln_node( match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, node_addr).await { Some(fut) => Box::pin(fut), None => { - return Err(ConnectionError::ConnectionError(format!( + return Err(ConnectionError::TimeOut(format!( "Failed to connect to node: {}", pubkey ))) @@ -61,7 +63,7 @@ pub async fn connect_to_ln_node( // Make sure the connection is still established. match futures::poll!(&mut connection_closed_future) { std::task::Poll::Ready(_) => { - return Err(ConnectionError::ConnectionError(format!( + return Err(ConnectionError::HandshakeErr(format!( "Node {} disconnected before finishing the handshake", pubkey ))); diff --git a/mm2src/coins/rpc_command/lightning/mod.rs b/mm2src/coins/rpc_command/lightning/mod.rs index 431aeb7977..73aa00bd08 100644 --- a/mm2src/coins/rpc_command/lightning/mod.rs +++ b/mm2src/coins/rpc_command/lightning/mod.rs @@ -1,12 +1,33 @@ -pub mod close_channel; -pub mod connect_to_node; -pub mod generate_invoice; -pub mod get_channel_details; -pub mod get_claimable_balances; -pub mod get_payment_details; -pub mod list_channels; -pub mod list_payments_by_filter; -pub mod open_channel; -pub mod send_payment; -pub mod trusted_nodes; -pub mod update_channel; +mod close_channel; +mod connect_to_node; +mod generate_invoice; +mod get_channel_details; +mod get_claimable_balances; +mod get_payment_details; +mod list_channels; +mod list_payments_by_filter; +mod open_channel; +mod send_payment; +mod trusted_nodes; +mod update_channel; + +pub mod channels { + pub use super::close_channel::*; + pub use super::get_channel_details::*; + pub use super::get_claimable_balances::*; + pub use super::list_channels::*; + pub use super::open_channel::*; + pub use super::update_channel::*; +} + +pub mod nodes { + pub use super::connect_to_node::*; + pub use super::trusted_nodes::*; +} + +pub mod payments { + pub use super::generate_invoice::*; + pub use super::get_payment_details::*; + pub use super::list_payments_by_filter::*; + pub use super::send_payment::*; +} diff --git a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs index 84237231a4..15561795df 100644 --- a/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs +++ b/mm2src/mm2_main/src/mm2_tests/lightning_tests.rs @@ -199,14 +199,18 @@ fn test_connect_to_node() { let connect = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::connect_to_node", + "method": "lightning::nodes::connect_to_node", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, }, }))) .unwrap(); - assert!(connect.0.is_success(), "!lightning::connect_to_node: {}", connect.1); + assert!( + connect.0.is_success(), + "!lightning::nodes::connect_to_node: {}", + connect.1 + ); let connect_res: Json = json::from_str(&connect.1).unwrap(); let expected = format!("Connected successfully to node : {}", node_1_address); assert_eq!(connect_res["result"], expected); @@ -226,7 +230,7 @@ fn test_open_channel() { let open_channel = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::open_channel", + "method": "lightning::channels::open_channel", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, @@ -239,7 +243,7 @@ fn test_open_channel() { .unwrap(); assert!( open_channel.0.is_success(), - "!lightning::open_channel: {}", + "!lightning::channels::open_channel: {}", open_channel.1 ); @@ -248,7 +252,7 @@ fn test_open_channel() { let list_channels_node_1 = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "lightning::list_open_channels_by_filter", + "method": "lightning::channels::list_open_channels_by_filter", "params": { "coin": "tBTC-TEST-lightning", }, @@ -256,7 +260,7 @@ fn test_open_channel() { .unwrap(); assert!( list_channels_node_1.0.is_success(), - "!lightning::list_open_channels_by_filter: {}", + "!lightning::channels::list_open_channels_by_filter: {}", list_channels_node_1.1 ); let list_channels_node_1_res: Json = json::from_str(&list_channels_node_1.1).unwrap(); @@ -277,7 +281,7 @@ fn test_open_channel() { let list_channels_node_2 = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::list_open_channels_by_filter", + "method": "lightning::channels::list_open_channels_by_filter", "params": { "coin": "tBTC-TEST-lightning", }, @@ -285,7 +289,7 @@ fn test_open_channel() { .unwrap(); assert!( list_channels_node_2.0.is_success(), - "!lightning::list_open_channels_by_filter: {}", + "!lightning::channels::list_open_channels_by_filter: {}", list_channels_node_2.1 ); let list_channels_node_2_res: Json = json::from_str(&list_channels_node_2.1).unwrap(); @@ -318,7 +322,7 @@ fn test_send_payment() { let add_trusted_node = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "lightning::add_trusted_node", + "method": "lightning::nodes::add_trusted_node", "params": { "coin": "tBTC-TEST-lightning", "node_id": node_2_id @@ -327,14 +331,14 @@ fn test_send_payment() { .unwrap(); assert!( add_trusted_node.0.is_success(), - "!lightning::add_trusted_node: {}", + "!lightning::nodes::add_trusted_node: {}", add_trusted_node.1 ); let open_channel = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::open_channel", + "method": "lightning::channels::open_channel", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, @@ -347,7 +351,7 @@ fn test_send_payment() { .unwrap(); assert!( open_channel.0.is_success(), - "!lightning::open_channel: {}", + "!lightning::channels::open_channel: {}", open_channel.1 ); @@ -356,7 +360,7 @@ fn test_send_payment() { let send_payment = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::send_payment", + "method": "lightning::payments::send_payment", "params": { "coin": "tBTC-TEST-lightning", "payment": { @@ -370,7 +374,7 @@ fn test_send_payment() { .unwrap(); assert!( send_payment.0.is_success(), - "!lightning::send_payment: {}", + "!lightning::payments::send_payment: {}", send_payment.1 ); @@ -384,7 +388,7 @@ fn test_send_payment() { let get_payment_details = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::get_payment_details", + "method": "lightning::payments::get_payment_details", "params": { "coin": "tBTC-TEST-lightning", "payment_hash": payment_hash @@ -393,7 +397,7 @@ fn test_send_payment() { .unwrap(); assert!( get_payment_details.0.is_success(), - "!lightning::get_payment_details: {}", + "!lightning::payments::get_payment_details: {}", get_payment_details.1 ); @@ -407,7 +411,7 @@ fn test_send_payment() { let get_payment_details = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "lightning::get_payment_details", + "method": "lightning::payments::get_payment_details", "params": { "coin": "tBTC-TEST-lightning", "payment_hash": payment_hash @@ -416,7 +420,7 @@ fn test_send_payment() { .unwrap(); assert!( get_payment_details.0.is_success(), - "!lightning::get_payment_details: {}", + "!lightning::payments::get_payment_details: {}", get_payment_details.1 ); @@ -441,7 +445,7 @@ fn test_lightning_taker_swap() { let add_trusted_node = block_on(mm_node_1.rpc(&json! ({ "userpass": mm_node_1.userpass, "mmrpc": "2.0", - "method": "lightning::add_trusted_node", + "method": "lightning::nodes::add_trusted_node", "params": { "coin": "tBTC-TEST-lightning", "node_id": node_2_id @@ -450,14 +454,14 @@ fn test_lightning_taker_swap() { .unwrap(); assert!( add_trusted_node.0.is_success(), - "!lightning::add_trusted_node: {}", + "!lightning::nodes::add_trusted_node: {}", add_trusted_node.1 ); let open_channel = block_on(mm_node_2.rpc(&json! ({ "userpass": mm_node_2.userpass, "mmrpc": "2.0", - "method": "lightning::open_channel", + "method": "lightning::channels::open_channel", "params": { "coin": "tBTC-TEST-lightning", "node_address": node_1_address, @@ -470,7 +474,7 @@ fn test_lightning_taker_swap() { .unwrap(); assert!( open_channel.0.is_success(), - "!lightning::open_channel: {}", + "!lightning::channels::open_channel: {}", open_channel.1 ); diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index f390f7a3a2..df29161af5 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -275,58 +275,35 @@ async fn gui_storage_dispatcher( /// /// # Note /// -/// `gui_storage_method` is a method name with the `lightning::` prefix removed. +/// `lightning_method` is a method name with the `lightning::` prefix removed. #[cfg(not(target_arch = "wasm32"))] async fn lightning_dispatcher( request: MmRpcRequest, ctx: MmArc, lightning_method: &str, ) -> DispatcherResult>> { - use coins::rpc_command::lightning as lightning_rpc; + use coins::rpc_command::lightning::{channels, nodes, payments}; match lightning_method { - "add_trusted_node" => handle_mmrpc(ctx, request, lightning_rpc::trusted_nodes::add_trusted_node).await, - "close_channel" => handle_mmrpc(ctx, request, lightning_rpc::close_channel::close_channel).await, - "connect_to_node" => handle_mmrpc(ctx, request, lightning_rpc::connect_to_node::connect_to_node).await, - "generate_invoice" => handle_mmrpc(ctx, request, lightning_rpc::generate_invoice::generate_invoice).await, - "get_channel_details" => { - handle_mmrpc(ctx, request, lightning_rpc::get_channel_details::get_channel_details).await + "channels::close_channel" => handle_mmrpc(ctx, request, channels::close_channel).await, + "channels::get_channel_details" => handle_mmrpc(ctx, request, channels::get_channel_details).await, + "channels::get_claimable_balances" => handle_mmrpc(ctx, request, channels::get_claimable_balances).await, + "channels::list_closed_channels_by_filter" => { + handle_mmrpc(ctx, request, channels::list_closed_channels_by_filter).await }, - "get_claimable_balances" => { - handle_mmrpc( - ctx, - request, - lightning_rpc::get_claimable_balances::get_claimable_balances, - ) - .await + "channels::list_open_channels_by_filter" => { + handle_mmrpc(ctx, request, channels::list_open_channels_by_filter).await }, - "get_payment_details" => { - handle_mmrpc(ctx, request, lightning_rpc::get_payment_details::get_payment_details).await - }, - "list_closed_channels_by_filter" => { - handle_mmrpc( - ctx, - request, - lightning_rpc::list_channels::list_closed_channels_by_filter, - ) - .await - }, - "list_open_channels_by_filter" => { - handle_mmrpc(ctx, request, lightning_rpc::list_channels::list_open_channels_by_filter).await - }, - "list_payments_by_filter" => { - handle_mmrpc( - ctx, - request, - lightning_rpc::list_payments_by_filter::list_payments_by_filter, - ) - .await - }, - "list_trusted_nodes" => handle_mmrpc(ctx, request, lightning_rpc::trusted_nodes::list_trusted_nodes).await, - "open_channel" => handle_mmrpc(ctx, request, lightning_rpc::open_channel::open_channel).await, - "remove_trusted_node" => handle_mmrpc(ctx, request, lightning_rpc::trusted_nodes::remove_trusted_node).await, - "send_payment" => handle_mmrpc(ctx, request, lightning_rpc::send_payment::send_payment).await, - "update_channel" => handle_mmrpc(ctx, request, lightning_rpc::update_channel::update_channel).await, + "channels::open_channel" => handle_mmrpc(ctx, request, channels::open_channel).await, + "channels::update_channel" => handle_mmrpc(ctx, request, channels::update_channel).await, + "nodes::add_trusted_node" => handle_mmrpc(ctx, request, nodes::add_trusted_node).await, + "nodes::connect_to_node" => handle_mmrpc(ctx, request, nodes::connect_to_node).await, + "nodes::list_trusted_nodes" => handle_mmrpc(ctx, request, nodes::list_trusted_nodes).await, + "nodes::remove_trusted_node" => handle_mmrpc(ctx, request, nodes::remove_trusted_node).await, + "payments::generate_invoice" => handle_mmrpc(ctx, request, payments::generate_invoice).await, + "payments::get_payment_details" => handle_mmrpc(ctx, request, payments::get_payment_details).await, + "payments::list_payments_by_filter" => handle_mmrpc(ctx, request, payments::list_payments_by_filter).await, + "payments::send_payment" => handle_mmrpc(ctx, request, payments::send_payment).await, _ => MmError::err(DispatcherError::NoSuchMethod), } } From 24dd76a1312c062f1c6cba234be7a44910b029af Mon Sep 17 00:00:00 2001 From: shamardy Date: Fri, 14 Oct 2022 21:51:45 +0200 Subject: [PATCH 31/36] Review fixes: fix some error handling issues --- mm2src/coins/eth.rs | 21 ++++++++-------- mm2src/coins/lightning.rs | 28 +++++++++++----------- mm2src/coins/lp_coins.rs | 1 + mm2src/coins/my_tx_history_v2.rs | 3 +-- mm2src/coins/solana.rs | 2 +- mm2src/coins/solana/spl.rs | 2 +- mm2src/coins/tendermint/tendermint_coin.rs | 2 +- mm2src/coins/test_coin.rs | 2 +- mm2src/coins/utxo/utxo_common.rs | 6 +++-- 9 files changed, 34 insertions(+), 33 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 600c89c294..3f185e04c8 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -61,17 +61,16 @@ use web3_transport::{EthFeeHistoryNamespace, Web3Transport, Web3TransportNode}; use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CoinBalance, CoinProtocol, CoinTransportMetrics, CoinsContext, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MarketCoinOps, MmCoin, MyAddressError, - NegotiateSwapContractAddrErr, NumConversError, NumConversResult, PaymentInstructionsErr, - RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RawTransactionResult, - RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, - SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageError, TradePreimageFut, - TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr, - TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, VerificationError, VerificationResult, WatcherValidatePaymentInput, WithdrawError, - WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; - -use crate::PaymentInstructions; + NegotiateSwapContractAddrErr, NumConversError, NumConversResult, PaymentInstructions, + PaymentInstructionsErr, RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, + RawTransactionResult, RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, + SearchForSwapTxSpendInput, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageError, + TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, + TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, + ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, + WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + pub use rlp; #[cfg(test)] mod eth_tests; diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 2956d4f80d..e350ab06a6 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -232,9 +232,9 @@ impl LightningCoin { destination: PublicKey, amount_msat: u64, final_cltv_expiry_delta: u32, - ) -> Result { + ) -> Result> { if final_cltv_expiry_delta < MIN_FINAL_CLTV_EXPIRY { - return Err(PaymentError::CLTVExpiry(final_cltv_expiry_delta, MIN_FINAL_CLTV_EXPIRY)); + return MmError::err(PaymentError::CLTVExpiry(final_cltv_expiry_delta, MIN_FINAL_CLTV_EXPIRY)); } let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes()); @@ -243,7 +243,7 @@ impl LightningCoin { selfi .invoice_payer .pay_pubkey(destination, payment_preimage, amount_msat, final_cltv_expiry_delta) - .map_err(|e| PaymentError::Keysend(format!("{:?}", e))) + .map_to_mm(|e| PaymentError::Keysend(format!("{:?}", e))) }) .await?; @@ -375,7 +375,7 @@ impl LightningCoin { amt_msat: Option, description: String, invoice_expiry_delta_secs: u32, - ) -> Result> { + ) -> Result>> { let duration = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("for the foreseeable future this shouldn't happen"); @@ -397,7 +397,7 @@ impl LightningCoin { let payment_secret = self .channel_manager .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs) - .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; + .map_to_mm(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; let our_node_pubkey = self.channel_manager.get_our_node_id(); let mut invoice = InvoiceBuilder::new(self.platform.network.clone().into()) @@ -419,7 +419,7 @@ impl LightningCoin { let raw_invoice = match invoice.build_raw() { Ok(inv) => inv, - Err(e) => return Err(SignOrCreationError::CreationError(e)), + Err(e) => return MmError::err(SignOrCreationError::CreationError(e)), }; let hrp_str = raw_invoice.hrp.to_string(); let hrp_bytes = hrp_str.as_bytes(); @@ -430,7 +430,7 @@ impl LightningCoin { }); match signed_raw_invoice { Ok(inv) => Ok(Invoice::from_signed(inv).map_err(|_| SignOrCreationError::SignError(()))?), - Err(e) => Err(SignOrCreationError::SignError(e)), + Err(e) => MmError::err(SignOrCreationError::SignError(e)), } } } @@ -454,9 +454,9 @@ impl SwapOps for LightningCoin { _swap_unique_data: &[u8], payment_instructions: &Option, ) -> TransactionFut { - let PaymentInstructions::Lightning(invoice) = payment_instructions + let PaymentInstructions::Lightning(invoice) = try_tx_fus!(payment_instructions .clone() - .expect("payment_instructions can't be None"); + .ok_or_else(|| TransactionErr::Plain("payment_instructions can't be None".into()))); let coin = self.clone(); let fut = async move { let payment = try_tx_s!(coin.pay_invoice(invoice).await); @@ -475,9 +475,9 @@ impl SwapOps for LightningCoin { _swap_unique_data: &[u8], payment_instructions: &Option, ) -> TransactionFut { - let PaymentInstructions::Lightning(invoice) = payment_instructions + let PaymentInstructions::Lightning(invoice) = try_tx_fus!(payment_instructions .clone() - .expect("payment_instructions can't be None"); + .ok_or_else(|| TransactionErr::Plain("payment_instructions can't be None".into()))); let coin = self.clone(); let fut = async move { let payment = try_tx_s!(coin.pay_invoice(invoice).await); @@ -707,7 +707,7 @@ impl SwapOps for LightningCoin { let invoice = self .create_invoice_for_hash(payment_hash, Some(amt_msat), "".into(), DEFAULT_INVOICE_EXPIRY) .await - .map_to_mm(|e| PaymentInstructionsErr::LightningInvoiceErr(e.to_string()))?; + .map_err(|e| PaymentInstructionsErr::LightningInvoiceErr(e.to_string()))?; Ok(Some(invoice.to_string().into_bytes())) } @@ -718,8 +718,8 @@ impl SwapOps for LightningCoin { amount: BigDecimal, ) -> Result, MmError> { let invoice = Invoice::from_str(&String::from_utf8_lossy(instructions))?; - if (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) - || (secret_hash.len() == 32 && invoice.payment_hash().as_inner() != secret_hash) + if invoice.payment_hash().as_inner() != secret_hash + || (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) { return Err( ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice payment hash!".into()).into(), diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 998dc24058..8816cef6f9 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -399,6 +399,7 @@ pub enum TxMarshalingErr { /// For cases where serialized and deserialized values doesn't verify each other. CrossCheckFailed(String), NotSupported(String), + Internal(String), } #[derive(Debug, Clone)] diff --git a/mm2src/coins/my_tx_history_v2.rs b/mm2src/coins/my_tx_history_v2.rs index bef6c844e3..0344f83817 100644 --- a/mm2src/coins/my_tx_history_v2.rs +++ b/mm2src/coins/my_tx_history_v2.rs @@ -222,8 +222,7 @@ impl<'a, Addr: Clone + DisplayAddress + Eq + std::hash::Hash, Tx: Transaction> T TransactionDetails { coin: self.coin, - // It should be safe to unwrap here - tx_hex: self.tx.tx_hex().expect("tx_hex shouldn't be None!").into(), + tx_hex: self.tx.tx_hex().unwrap_or_default().into(), tx_hash: tx_hash.to_tx_hash(), from, to, diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 96e0b597f4..807ede479c 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -432,7 +432,7 @@ impl MarketCoinOps for SolanaCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Solana yet.".to_string(), )) diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index db546843f4..c2cf20d608 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -270,7 +270,7 @@ impl MarketCoinOps for SplToken { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Spl yet.".to_string(), )) diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index fa566dd8aa..ad403880ad 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -598,7 +598,7 @@ impl MarketCoinOps for TendermintCoin { todo!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Tendermint yet.".to_string(), )) diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 7e7f15dea8..b7cf7f7ebd 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -80,7 +80,7 @@ impl MarketCoinOps for TestCoin { unimplemented!() } - fn tx_enum_from_bytes(&self, bytes: &[u8]) -> Result> { + fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { MmError::err(TxMarshalingErr::NotSupported( "tx_enum_from_bytes is not supported for Test coin yet.".to_string(), )) diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 618e98fe45..194c5f356c 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2093,8 +2093,10 @@ pub fn wait_for_output_spend( pub fn tx_enum_from_bytes(coin: &UtxoCoinFields, bytes: &[u8]) -> Result> { let mut transaction: UtxoTx = deserialize(bytes).map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string()))?; - // It should be safe to unwrap here - let serialized_length = transaction.tx_hex().expect("tx_hex shouldn't be None!").len(); + let serialized_length = transaction + .tx_hex() + .ok_or_else(|| TxMarshalingErr::Internal("tx_hex shouldn't be None!".into()))? + .len(); if bytes.len() != serialized_length { return MmError::err(TxMarshalingErr::CrossCheckFailed(format!( "Expected '{}' lenght of the serialized transaction, found '{}'", From c49f7ea1bae5d12c80696bd0c034fdb3770b1b20 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 18 Oct 2022 18:21:48 +0200 Subject: [PATCH 32/36] more review fixes --- mm2src/coins/lightning.rs | 130 +++++++++++------- mm2src/coins/lightning/ln_utils.rs | 5 +- .../rpc_command/lightning/update_channel.rs | 3 +- mm2src/common/common.rs | 5 + mm2src/mm2_main/src/lp_swap.rs | 15 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 19 +-- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 31 +++-- 7 files changed, 123 insertions(+), 85 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index e350ab06a6..c859b11422 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -31,7 +31,7 @@ use bitcrypto::ChecksumType; use bitcrypto::{dhash256, ripemd160}; use common::executor::{spawn, Timer}; use common::log::{info, LogOnError, LogState}; -use common::{async_blocking, log, now_ms, PagingOptionsEnum}; +use common::{async_blocking, get_local_duration_since_epoch, log, now_ms, PagingOptionsEnum}; use db_common::sqlite::rusqlite::Error as SqlError; use futures::{FutureExt, TryFutureExt}; use futures01::Future; @@ -71,7 +71,6 @@ use std::fmt; use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; -use std::time::SystemTime; pub const DEFAULT_INVOICE_EXPIRY: u32 = 3600; @@ -273,61 +272,100 @@ impl LightningCoin { limit: usize, ) -> GetOpenChannelsResult { fn apply_open_channel_filter(channel_details: &ChannelDetailsForRPC, filter: &OpenChannelsFilter) -> bool { - let is_channel_id = - filter.channel_id.is_none() || Some(&channel_details.channel_id) == filter.channel_id.as_ref(); + // Checking if channel_id is some and not equal + if filter.channel_id.is_some() && Some(&channel_details.channel_id) != filter.channel_id.as_ref() { + return false; + } - let is_counterparty_node_id = filter.counterparty_node_id.is_none() - || Some(&channel_details.counterparty_node_id) == filter.counterparty_node_id.as_ref(); + // Checking if counterparty_node_id is some and not equal + if filter.counterparty_node_id.is_some() + && Some(&channel_details.counterparty_node_id) != filter.counterparty_node_id.as_ref() + { + return false; + } - let is_funding_tx = filter.funding_tx.is_none() || channel_details.funding_tx == filter.funding_tx; + // Checking if funding_tx is some and not equal + if filter.funding_tx.is_some() && channel_details.funding_tx != filter.funding_tx { + return false; + } - let is_from_funding_value_sats = - Some(&channel_details.funding_tx_value_sats) >= filter.from_funding_value_sats.as_ref(); + // Checking if from_funding_value_sats is some and more than funding_tx_value_sats + if filter.from_funding_value_sats.is_some() + && Some(&channel_details.funding_tx_value_sats) < filter.from_funding_value_sats.as_ref() + { + return false; + } - let is_to_funding_value_sats = filter.to_funding_value_sats.is_none() - || Some(&channel_details.funding_tx_value_sats) <= filter.to_funding_value_sats.as_ref(); + // Checking if to_funding_value_sats is some and less than funding_tx_value_sats + if filter.to_funding_value_sats.is_some() + && Some(&channel_details.funding_tx_value_sats) > filter.to_funding_value_sats.as_ref() + { + return false; + } - let is_outbound = - filter.is_outbound.is_none() || Some(&channel_details.is_outbound) == filter.is_outbound.as_ref(); + // Checking if is_outbound is some and not equal + if filter.is_outbound.is_some() && Some(&channel_details.is_outbound) != filter.is_outbound.as_ref() { + return false; + } - let is_from_balance_msat = Some(&channel_details.balance_msat) >= filter.from_balance_msat.as_ref(); + // Checking if from_balance_msat is some and more than balance_msat + if filter.from_balance_msat.is_some() + && Some(&channel_details.balance_msat) < filter.from_balance_msat.as_ref() + { + return false; + } - let is_to_balance_msat = filter.to_balance_msat.is_none() - || Some(&channel_details.balance_msat) <= filter.to_balance_msat.as_ref(); + // Checking if to_balance_msat is some and less than balance_msat + if filter.to_balance_msat.is_some() && Some(&channel_details.balance_msat) > filter.to_balance_msat.as_ref() + { + return false; + } - let is_from_outbound_capacity_msat = - Some(&channel_details.outbound_capacity_msat) >= filter.from_outbound_capacity_msat.as_ref(); + // Checking if from_outbound_capacity_msat is some and more than outbound_capacity_msat + if filter.from_outbound_capacity_msat.is_some() + && Some(&channel_details.outbound_capacity_msat) < filter.from_outbound_capacity_msat.as_ref() + { + return false; + } - let is_to_outbound_capacity_msat = filter.to_outbound_capacity_msat.is_none() - || Some(&channel_details.outbound_capacity_msat) <= filter.to_outbound_capacity_msat.as_ref(); + // Checking if to_outbound_capacity_msat is some and less than outbound_capacity_msat + if filter.to_outbound_capacity_msat.is_some() + && Some(&channel_details.outbound_capacity_msat) > filter.to_outbound_capacity_msat.as_ref() + { + return false; + } - let is_from_inbound_capacity_msat = - Some(&channel_details.inbound_capacity_msat) >= filter.from_inbound_capacity_msat.as_ref(); + // Checking if from_inbound_capacity_msat is some and more than outbound_capacity_msat + if filter.from_inbound_capacity_msat.is_some() + && Some(&channel_details.inbound_capacity_msat) < filter.from_inbound_capacity_msat.as_ref() + { + return false; + } - let is_to_inbound_capacity_msat = filter.to_inbound_capacity_msat.is_none() - || Some(&channel_details.inbound_capacity_msat) <= filter.to_inbound_capacity_msat.as_ref(); + // Checking if to_inbound_capacity_msat is some and less than inbound_capacity_msat + if filter.to_inbound_capacity_msat.is_some() + && Some(&channel_details.inbound_capacity_msat) > filter.to_inbound_capacity_msat.as_ref() + { + return false; + } - let is_confirmed = filter.is_ready.is_none() || Some(&channel_details.is_ready) == filter.is_ready.as_ref(); + // Checking if is_ready is some and not equal + if filter.is_ready.is_some() && Some(&channel_details.is_ready) != filter.is_ready.as_ref() { + return false; + } - let is_usable = filter.is_usable.is_none() || Some(&channel_details.is_usable) == filter.is_usable.as_ref(); + // Checking if is_usable is some and not equal + if filter.is_usable.is_some() && Some(&channel_details.is_usable) != filter.is_usable.as_ref() { + return false; + } - let is_public = filter.is_public.is_none() || Some(&channel_details.is_public) == filter.is_public.as_ref(); + // Checking if is_public is some and not equal + if filter.is_public.is_some() && Some(&channel_details.is_public) != filter.is_public.as_ref() { + return false; + } - is_channel_id - && is_counterparty_node_id - && is_funding_tx - && is_from_funding_value_sats - && is_to_funding_value_sats - && is_outbound - && is_from_balance_msat - && is_to_balance_msat - && is_from_outbound_capacity_msat - && is_to_outbound_capacity_msat - && is_from_inbound_capacity_msat - && is_to_inbound_capacity_msat - && is_confirmed - && is_usable - && is_public + // All checks pass + true } let mut total_open_channels: Vec = @@ -376,10 +414,6 @@ impl LightningCoin { description: String, invoice_expiry_delta_secs: u32, ) -> Result>> { - let duration = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .expect("for the foreseeable future this shouldn't happen"); - let open_channels_nodes = self.open_channels_nodes.lock().clone(); for (node_pubkey, node_addr) in open_channels_nodes { ln_p2p::connect_to_ln_node(node_pubkey, node_addr, self.peer_manager.clone()) @@ -399,6 +433,8 @@ impl LightningCoin { .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs) .map_to_mm(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; let our_node_pubkey = self.channel_manager.get_our_node_id(); + // Todo: Check if it's better to use UTC instead of local time for invoice generations + let duration = get_local_duration_since_epoch().expect("for the foreseeable future this shouldn't happen"); let mut invoice = InvoiceBuilder::new(self.platform.network.clone().into()) .description(description) @@ -719,7 +755,7 @@ impl SwapOps for LightningCoin { ) -> Result, MmError> { let invoice = Invoice::from_str(&String::from_utf8_lossy(instructions))?; if invoice.payment_hash().as_inner() != secret_hash - || (secret_hash.len() == 20 && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash) + && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash { return Err( ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice payment hash!".into()).into(), diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index 37d8364873..9db0e4fc8a 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -20,7 +20,6 @@ use std::collections::hash_map::Entry; use std::fs::File; use std::path::PathBuf; use std::sync::Arc; -use std::time::SystemTime; pub type ChainMonitor = chainmonitor::ChainMonitor< InMemorySigner, @@ -83,9 +82,7 @@ pub async fn init_db(ctx: &MmArc, ticker: String) -> EnableLightningResult EnableLightningResult> { // The current time is used to derive random numbers from the seed where required, to ensure all random generation is unique across restarts. let seed: [u8; 32] = ctx.secp256k1_key_pair().private().secret.into(); - let cur = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map_to_mm(|e| EnableLightningError::SystemTimeError(e.to_string()))?; + let cur = get_local_duration_since_epoch().map_to_mm(|e| EnableLightningError::SystemTimeError(e.to_string()))?; Ok(Arc::new(KeysManager::new(&seed, cur.as_secs(), cur.subsec_nanos()))) } diff --git a/mm2src/coins/rpc_command/lightning/update_channel.rs b/mm2src/coins/rpc_command/lightning/update_channel.rs index 93342c1e3a..a108659ea0 100644 --- a/mm2src/coins/rpc_command/lightning/update_channel.rs +++ b/mm2src/coins/rpc_command/lightning/update_channel.rs @@ -24,8 +24,7 @@ impl HttpStatusCode for UpdateChannelError { fn status_code(&self) -> StatusCode { match self { UpdateChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, - UpdateChannelError::NoSuchChannel(_) => StatusCode::NOT_FOUND, - UpdateChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, + UpdateChannelError::NoSuchChannel(_) | UpdateChannelError::NoSuchCoin(_) => StatusCode::NOT_FOUND, UpdateChannelError::FailureToUpdateChannel(_, _) => StatusCode::INTERNAL_SERVER_ERROR, } } diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 78972752f2..ce2ed27617 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -144,6 +144,7 @@ use std::os::raw::c_void; use std::panic::{set_hook, PanicInfo}; use std::ptr::read_volatile; use std::sync::atomic::Ordering; +use std::time::{Duration, SystemTime, SystemTimeError}; use uuid::Uuid; use crate::executor::spawn; @@ -960,3 +961,7 @@ pub fn spawn_abortable(fut: impl Future03 + Send + 'static) -> Abor #[inline(always)] pub fn get_utc_timestamp() -> i64 { Utc::now().timestamp() } + +pub fn get_local_duration_since_epoch() -> Result { + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) +} diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index ad36bdccce..fd312b3b27 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -731,6 +731,17 @@ impl SwapTxDataMsg { SwapTxDataMsg::V2(v2) => Some(&v2.next_step_instructions), } } + + #[inline] + pub fn new(data: Vec, instructions: Option>) -> Self { + match instructions { + Some(next_step_instructions) => SwapTxDataMsg::V2(PaymentDataV2 { + data, + next_step_instructions, + }), + None => SwapTxDataMsg::V1(data), + } + } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -1279,10 +1290,6 @@ enum SecretHashAlgo { SHA256, } -impl Default for SecretHashAlgo { - fn default() -> Self { SecretHashAlgo::DHASH160 } -} - impl SecretHashAlgo { fn hash_secret(&self, secret: &[u8]) -> Vec { match self { diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 7b6883bedf..3f3a54c8aa 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -6,9 +6,9 @@ use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePr use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_message_every, check_other_coin_balance_for_swap, detect_secret_hash_algo, dex_fee_amount_from_taker_coin, get_locked_amount, recv_swap_msg, swap_topic, tx_helper_topic, AtomicSwap, LockedAmount, MySwapInfo, - NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, PaymentDataV2, RecoveredSwap, - RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, SecretHashAlgo, SwapConfirmationsSettings, - SwapError, SwapMsg, SwapTxDataMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; + NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, + SavedSwapIo, SavedTradeFee, SecretHashAlgo, SwapConfirmationsSettings, SwapError, SwapMsg, SwapTxDataMsg, + SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MakerOrderBuilder, OrderConfirmationsSettings}; @@ -414,18 +414,11 @@ impl MakerSwap { // It's not really needed here unlike in TakerFee msg since the hash is included in the invoice/payment_instructions but it's kept for symmetry let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex_or_hash().0; let hash_algo = SecretHashAlgo::SHA256; - if let Some(instructions) = self + let instructions = self .taker_coin .payment_instructions(&hash_algo.hash_secret(self.secret.as_slice()), &self.taker_amount) - .await? - { - Ok(SwapTxDataMsg::V2(PaymentDataV2 { - data: payment_data, - next_step_instructions: instructions, - })) - } else { - Ok(SwapTxDataMsg::V1(payment_data)) - } + .await?; + Ok(SwapTxDataMsg::new(payment_data, instructions)) } async fn start(&self) -> Result<(Option, Vec), String> { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 7fe834fa41..11f0d62c5d 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -7,8 +7,8 @@ use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePr use super::{broadcast_my_swap_status, broadcast_swap_message, broadcast_swap_message_every, check_other_coin_balance_for_swap, dex_fee_amount_from_taker_coin, dex_fee_rate, dex_fee_threshold, get_locked_amount, recv_swap_msg, swap_topic, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, - NegotiationDataV2, NegotiationDataV3, PaymentDataV2, RecoveredSwap, RecoveredSwapAction, SavedSwap, - SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapTxDataMsg, SwapsContext, + NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, + SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapTxDataMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::{MatchBy, OrderConfirmationsSettings, TakerAction, TakerOrderBuilder}; @@ -851,21 +851,20 @@ impl TakerSwap { async fn get_taker_fee_data(&self) -> Result> { // If taker fee is a lightning payment the payment hash will be sent in the message - let taker_fee_data = self.r().taker_fee.as_ref().unwrap().tx_hex_or_hash().0; + let taker_fee_data = self + .r() + .taker_fee + .as_ref() + .expect("TakerSwapMut::taker_fee must be some value to use get_taker_fee_data") + .tx_hex_or_hash() + .0; let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); - if let Some(instructions) = self + let instructions = self .maker_coin .payment_instructions(&secret_hash, &maker_amount) - .await? - { - Ok(SwapTxDataMsg::V2(PaymentDataV2 { - data: taker_fee_data, - next_step_instructions: instructions, - })) - } else { - Ok(SwapTxDataMsg::V1(taker_fee_data)) - } + .await?; + Ok(SwapTxDataMsg::new(taker_fee_data, instructions)) } async fn start(&self) -> Result<(Option, Vec), String> { @@ -1415,6 +1414,8 @@ impl TakerSwap { } async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { + const BROADCAST_SWAP_MESSAGE_INTERVAL: f64 = 600.; + let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.clone(); let mut watcher_broadcast_abort_handle = None; // Watchers cannot be used for lightning swaps for now @@ -1429,7 +1430,7 @@ impl TakerSwap { self.ctx.clone(), watcher_topic(&self.r().data.taker_coin), swpmsg_watcher, - 600., + BROADCAST_SWAP_MESSAGE_INTERVAL, self.p2p_privkey, )); } @@ -1442,7 +1443,7 @@ impl TakerSwap { self.ctx.clone(), swap_topic(&self.uuid), msg, - 600., + BROADCAST_SWAP_MESSAGE_INTERVAL, self.p2p_privkey, )); From 8b515c860faf640e04438c694561bdc8b9093ee4 Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 24 Oct 2022 12:11:43 +0200 Subject: [PATCH 33/36] review fixes wip: rename SwapTxDataMsg enum variants --- mm2src/coins/lightning.rs | 8 ++++---- mm2src/mm2_main/src/lp_swap.rs | 28 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index f734a9006d..19b7c582ee 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -190,7 +190,7 @@ impl LightningCoin { .find(|chan| chan.user_channel_id == rpc_id) } - pub(crate) async fn pay_invoice(&self, invoice: Invoice) -> Result { + pub(crate) async fn pay_invoice(&self, invoice: Invoice) -> Result> { let payment_hash = PaymentHash((invoice.payment_hash()).into_inner()); let payment_type = PaymentType::OutboundPayment { destination: *invoice.payee_pub_key().unwrap_or(&invoice.recover_payee_pub_key()), @@ -207,7 +207,7 @@ impl LightningCoin { selfi .invoice_payer .pay_invoice(&invoice) - .map_err(|e| PaymentError::Invoice(format!("{:?}", e))) + .map_to_mm(|e| PaymentError::Invoice(format!("{:?}", e))) }) .await?; @@ -425,8 +425,6 @@ impl LightningCoin { )); } - let route_hints = filter_channels(self.channel_manager.list_usable_channels(), amt_msat); - // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin // supply. let payment_secret = self @@ -450,6 +448,8 @@ impl LightningCoin { if let Some(amt) = amt_msat { invoice = invoice.amount_milli_satoshis(amt); } + + let route_hints = filter_channels(self.channel_manager.list_usable_channels(), amt_msat); for hint in route_hints { invoice = invoice.private_route(hint); } diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index a06209e7b3..881f404d33 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -679,7 +679,7 @@ impl NegotiationDataMsg { } #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] -pub struct PaymentDataV2 { +pub struct PaymentWithInstructions { data: Vec, // Next step instructions for the other side whether taker or maker. // An example for this is a maker/taker sending the taker/maker a lightning invoice to be payed. @@ -689,35 +689,35 @@ pub struct PaymentDataV2 { #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] #[serde(untagged)] pub enum SwapTxDataMsg { - V1(Vec), - V2(PaymentDataV2), + Regular(Vec), + WithInstructions(PaymentWithInstructions), } impl SwapTxDataMsg { #[inline] pub fn data(&self) -> &[u8] { match self { - SwapTxDataMsg::V1(v1) => v1, - SwapTxDataMsg::V2(v2) => &v2.data, + SwapTxDataMsg::Regular(data) => data, + SwapTxDataMsg::WithInstructions(p) => &p.data, } } #[inline] pub fn instructions(&self) -> Option<&[u8]> { match self { - SwapTxDataMsg::V1(_) => None, - SwapTxDataMsg::V2(v2) => Some(&v2.next_step_instructions), + SwapTxDataMsg::Regular(_) => None, + SwapTxDataMsg::WithInstructions(p) => Some(&p.next_step_instructions), } } #[inline] pub fn new(data: Vec, instructions: Option>) -> Self { match instructions { - Some(next_step_instructions) => SwapTxDataMsg::V2(PaymentDataV2 { + Some(next_step_instructions) => SwapTxDataMsg::WithInstructions(PaymentWithInstructions { data, next_step_instructions, }), - None => SwapTxDataMsg::V1(data), + None => SwapTxDataMsg::Regular(data), } } } @@ -1594,7 +1594,7 @@ mod lp_swap_tests { // old message format should be deserialized to PaymentDataMsg::V1 let old = SwapMsgOld::MakerPayment(vec![1; 300]); - let expected = SwapMsg::MakerPayment(SwapTxDataMsg::V1(vec![1; 300])); + let expected = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(vec![1; 300])); let serialized = rmp_serde::to_vec(&old).unwrap(); @@ -1603,7 +1603,7 @@ mod lp_swap_tests { assert_eq!(deserialized, expected); // PaymentDataMsg::V1 should be deserialized to old message format - let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::V1(vec![1; 300])); + let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(vec![1; 300])); let expected = old; @@ -1614,7 +1614,7 @@ mod lp_swap_tests { assert_eq!(deserialized, expected); // PaymentDataMsg::V1 should be deserialized to PaymentDataMsg::V1 - let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::V1(vec![1; 300])); + let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(vec![1; 300])); let serialized = rmp_serde::to_vec(&v1).unwrap(); @@ -1623,7 +1623,7 @@ mod lp_swap_tests { assert_eq!(deserialized, v1); // PaymentDataMsg::V2 should be deserialized to PaymentDataMsg::V2 - let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::V2(PaymentDataV2 { + let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::WithInstructions(PaymentWithInstructions { data: vec![1; 300], next_step_instructions: vec![1; 300], })); @@ -1635,7 +1635,7 @@ mod lp_swap_tests { assert_eq!(deserialized, v2); // PaymentDataMsg::V2 shouldn't be deserialized to old message format, new nodes with payment instructions can't swap with old nodes without it. - let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::V2(PaymentDataV2 { + let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::WithInstructions(PaymentWithInstructions { data: vec![1; 300], next_step_instructions: vec![1; 300], })); From 68765b8b8530db7119138ec55a1604dbe8619945 Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 24 Oct 2022 16:00:04 +0200 Subject: [PATCH 34/36] review fixes wip: validate_instructions to return an error instead of None, some refactors --- mm2src/coins/eth.rs | 4 +-- mm2src/coins/lightning.rs | 14 ++++------ mm2src/coins/lightning/ln_utils.rs | 15 +++------- mm2src/coins/lp_coins.rs | 3 +- mm2src/coins/qrc20.rs | 4 +-- .../rpc_command/lightning/connect_to_node.rs | 2 +- mm2src/coins/solana.rs | 2 +- mm2src/coins/solana/spl.rs | 2 +- mm2src/coins/tendermint/tendermint_coin.rs | 4 +-- mm2src/coins/test_coin.rs | 2 +- mm2src/coins/utxo/bch.rs | 4 +-- mm2src/coins/utxo/qtum.rs | 4 +-- mm2src/coins/utxo/slp.rs | 4 +-- mm2src/coins/utxo/utxo_standard.rs | 4 +-- mm2src/coins/z_coin.rs | 4 +-- mm2src/common/common.rs | 1 + mm2src/mm2_main/src/lp_swap.rs | 28 ++++++++++--------- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 21 +++++++------- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 5 ++-- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 20 ++++++------- 20 files changed, 71 insertions(+), 76 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 70a0b99b44..c6514fe9d0 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1167,8 +1167,8 @@ impl SwapOps for EthCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 19b7c582ee..928047e3a4 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -491,9 +491,8 @@ impl SwapOps for LightningCoin { _swap_unique_data: &[u8], payment_instructions: &Option, ) -> TransactionFut { - let PaymentInstructions::Lightning(invoice) = try_tx_fus!(payment_instructions - .clone() - .ok_or_else(|| TransactionErr::Plain("payment_instructions can't be None".into()))); + let PaymentInstructions::Lightning(invoice) = + try_tx_fus!(payment_instructions.clone().ok_or("payment_instructions can't be None")); let coin = self.clone(); let fut = async move { let payment = try_tx_s!(coin.pay_invoice(invoice).await); @@ -512,9 +511,8 @@ impl SwapOps for LightningCoin { _swap_unique_data: &[u8], payment_instructions: &Option, ) -> TransactionFut { - let PaymentInstructions::Lightning(invoice) = try_tx_fus!(payment_instructions - .clone() - .ok_or_else(|| TransactionErr::Plain("payment_instructions can't be None".into()))); + let PaymentInstructions::Lightning(invoice) = + try_tx_fus!(payment_instructions.clone().ok_or("payment_instructions can't be None")); let coin = self.clone(); let fut = async move { let payment = try_tx_s!(coin.pay_invoice(invoice).await); @@ -753,7 +751,7 @@ impl SwapOps for LightningCoin { instructions: &[u8], secret_hash: &[u8], amount: BigDecimal, - ) -> Result, MmError> { + ) -> Result> { let invoice = Invoice::from_str(&String::from_utf8_lossy(instructions))?; if invoice.payment_hash().as_inner() != secret_hash && ripemd160(invoice.payment_hash().as_inner()).as_slice() != secret_hash @@ -769,7 +767,7 @@ impl SwapOps for LightningCoin { return Err(ValidateInstructionsErr::ValidateLightningInvoiceErr("Invalid invoice amount!".into()).into()); } // Todo: continue validation here by comparing locktime, etc.. - Ok(Some(PaymentInstructions::Lightning(invoice))) + Ok(PaymentInstructions::Lightning(invoice)) } } diff --git a/mm2src/coins/lightning/ln_utils.rs b/mm2src/coins/lightning/ln_utils.rs index 3a09b96bd1..d7ddc85595 100644 --- a/mm2src/coins/lightning/ln_utils.rs +++ b/mm2src/coins/lightning/ln_utils.rs @@ -275,17 +275,10 @@ pub(crate) fn filter_channels(channels: Vec, min_inbound_capacit min_capacity_channel_exists = true; }; match filtered_channels.entry(channel.counterparty.node_id) { - Entry::Occupied(mut entry) => { - let current_max_capacity = entry.get().inbound_capacity_msat; - if channel.inbound_capacity_msat < current_max_capacity { - continue; - } - entry.insert(channel); - }, - Entry::Vacant(entry) => { - entry.insert(channel); - }, - } + Entry::Occupied(entry) if channel.inbound_capacity_msat < entry.get().inbound_capacity_msat => continue, + Entry::Occupied(mut entry) => entry.insert(channel), + Entry::Vacant(entry) => entry.insert(channel), + }; } let route_hint_from_channel = |channel: &ChannelDetails| { diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 7621ef8264..e500016072 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -517,6 +517,7 @@ impl From for PaymentInstructionsErr { #[derive(Display)] pub enum ValidateInstructionsErr { ValidateLightningInvoiceErr(String), + UnsupportedCoin(String), } #[cfg(not(target_arch = "wasm32"))] @@ -678,7 +679,7 @@ pub trait SwapOps { instructions: &[u8], secret_hash: &[u8], amount: BigDecimal, - ) -> Result, MmError>; + ) -> Result>; } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 8d1946c624..6ac10d48a8 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1060,8 +1060,8 @@ impl SwapOps for Qrc20Coin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/rpc_command/lightning/connect_to_node.rs b/mm2src/coins/rpc_command/lightning/connect_to_node.rs index 1e0e4258fa..79ba0f1889 100644 --- a/mm2src/coins/rpc_command/lightning/connect_to_node.rs +++ b/mm2src/coins/rpc_command/lightning/connect_to_node.rs @@ -77,7 +77,7 @@ pub async fn connect_to_node(ctx: MmArc, req: ConnectToNodeRequest) -> ConnectTo let node_addr = req.node_address.addr; let res = connect_to_ln_node(node_pubkey, node_addr, ln_coin.peer_manager.clone()).await?; - // If a node that we have an open channel with changed it's address, "connect_to_lightning_node" + // If a node that we have an open channel with changed it's address, "connect_to_node" // can be used to reconnect to the new address while saving this new address for reconnections. if let ConnectToNodeRes::ConnectedSuccessfully { .. } = res { if let Entry::Occupied(mut entry) = ln_coin.open_channels_nodes.lock().entry(node_pubkey) { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index e3752e996a..9c14c2f322 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -631,7 +631,7 @@ impl SwapOps for SolanaCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { + ) -> Result> { unimplemented!() } } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 29fb4513b2..522bd9a4c8 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -461,7 +461,7 @@ impl SwapOps for SplToken { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { + ) -> Result> { unimplemented!() } } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 0cb21c20df..52d9f2ffae 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -801,8 +801,8 @@ impl SwapOps for TendermintCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index bb1eb5a1ac..d332e2596c 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -269,7 +269,7 @@ impl SwapOps for TestCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { + ) -> Result> { unimplemented!() } } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 1dcfe7c9a0..af028846e4 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1060,8 +1060,8 @@ impl SwapOps for BchCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index d8ce4a836b..c30493c3a7 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -753,8 +753,8 @@ impl SwapOps for QtumCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index dd9ca0f753..9068830750 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1479,8 +1479,8 @@ impl SwapOps for SlpToken { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index b5dcc2d9b6..7542d85880 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -519,8 +519,8 @@ impl SwapOps for UtxoStandardCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 8065383bea..090776a128 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1415,8 +1415,8 @@ impl SwapOps for ZCoin { _instructions: &[u8], _secret_hash: &[u8], _amount: BigDecimal, - ) -> Result, MmError> { - Ok(None) + ) -> Result> { + MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } } diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 5e48a5829e..2d020e7e56 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -939,6 +939,7 @@ impl Default for PagingOptionsEnum { #[inline(always)] pub fn get_utc_timestamp() -> i64 { Utc::now().timestamp() } +#[inline(always)] pub fn get_local_duration_since_epoch() -> Result { SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) } diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 881f404d33..436ba7d027 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1581,6 +1581,8 @@ mod lp_swap_tests { #[test] fn check_payment_data_serde() { + const MSG_DATA_INSTRUCTIONS: [u8; 300] = [1; 300]; + #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] enum SwapMsgOld { Negotiation(NegotiationDataMsg), @@ -1591,10 +1593,10 @@ mod lp_swap_tests { TakerPayment(Vec), } - // old message format should be deserialized to PaymentDataMsg::V1 - let old = SwapMsgOld::MakerPayment(vec![1; 300]); + // old message format should be deserialized to PaymentDataMsg::Regular + let old = SwapMsgOld::MakerPayment(MSG_DATA_INSTRUCTIONS.to_vec()); - let expected = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(vec![1; 300])); + let expected = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(MSG_DATA_INSTRUCTIONS.to_vec())); let serialized = rmp_serde::to_vec(&old).unwrap(); @@ -1602,8 +1604,8 @@ mod lp_swap_tests { assert_eq!(deserialized, expected); - // PaymentDataMsg::V1 should be deserialized to old message format - let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(vec![1; 300])); + // PaymentDataMsg::Regular should be deserialized to old message format + let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(MSG_DATA_INSTRUCTIONS.to_vec())); let expected = old; @@ -1613,8 +1615,8 @@ mod lp_swap_tests { assert_eq!(deserialized, expected); - // PaymentDataMsg::V1 should be deserialized to PaymentDataMsg::V1 - let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(vec![1; 300])); + // PaymentDataMsg::Regular should be deserialized to PaymentDataMsg::Regular + let v1 = SwapMsg::MakerPayment(SwapTxDataMsg::Regular(MSG_DATA_INSTRUCTIONS.to_vec())); let serialized = rmp_serde::to_vec(&v1).unwrap(); @@ -1622,10 +1624,10 @@ mod lp_swap_tests { assert_eq!(deserialized, v1); - // PaymentDataMsg::V2 should be deserialized to PaymentDataMsg::V2 + // PaymentDataMsg::WithInstructions should be deserialized to PaymentDataMsg::WithInstructions let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::WithInstructions(PaymentWithInstructions { - data: vec![1; 300], - next_step_instructions: vec![1; 300], + data: MSG_DATA_INSTRUCTIONS.to_vec(), + next_step_instructions: MSG_DATA_INSTRUCTIONS.to_vec(), })); let serialized = rmp_serde::to_vec(&v2).unwrap(); @@ -1634,10 +1636,10 @@ mod lp_swap_tests { assert_eq!(deserialized, v2); - // PaymentDataMsg::V2 shouldn't be deserialized to old message format, new nodes with payment instructions can't swap with old nodes without it. + // PaymentDataMsg::WithInstructions shouldn't be deserialized to old message format, new nodes with payment instructions can't swap with old nodes without it. let v2 = SwapMsg::MakerPayment(SwapTxDataMsg::WithInstructions(PaymentWithInstructions { - data: vec![1; 300], - next_step_instructions: vec![1; 300], + data: MSG_DATA_INSTRUCTIONS.to_vec(), + next_step_instructions: MSG_DATA_INSTRUCTIONS.to_vec(), })); let serialized = rmp_serde::to_vec(&v2).unwrap(); diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index a144bc05e1..6b3cb109fa 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -413,10 +413,12 @@ impl MakerSwap { // If maker payment is a lightning payment the payment hash will be sent in the message // It's not really needed here unlike in TakerFee msg since the hash is included in the invoice/payment_instructions but it's kept for symmetry let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex_or_hash().0; - let hash_algo = SecretHashAlgo::SHA256; let instructions = self .taker_coin - .payment_instructions(&hash_algo.hash_secret(self.secret.as_slice()), &self.taker_amount) + .payment_instructions( + &SecretHashAlgo::SHA256.hash_secret(self.secret.as_slice()), + &self.taker_amount, + ) .await?; Ok(SwapTxDataMsg::new(payment_data, instructions)) } @@ -656,10 +658,7 @@ impl MakerSwap { .maker_coin .validate_instructions(instructions, &self.secret_hash(), self.maker_amount.clone()) { - Ok(Some(instructions)) => { - swap_events.push(MakerSwapEvent::MakerPaymentInstructionsReceived(instructions)) - }, - Ok(None) => (), + Ok(instructions) => swap_events.push(MakerSwapEvent::MakerPaymentInstructionsReceived(instructions)), Err(e) => { return Ok((Some(MakerSwapCommand::Finish), vec![ MakerSwapEvent::TakerFeeValidateFailed(e.to_string().into()), @@ -715,7 +714,7 @@ impl MakerSwap { } let fee_ident = TransactionIdentifier { - tx_hex: taker_fee.tx_hex().map(From::from), + tx_hex: taker_fee.tx_hex().map(BytesJson::from), tx_hash: hash, }; swap_events.push(MakerSwapEvent::TakerFeeValidated(fee_ident)); @@ -783,7 +782,7 @@ impl MakerSwap { info!("Maker payment tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; @@ -866,7 +865,7 @@ impl MakerSwap { let tx_hash = taker_payment.tx_hash(); info!("Taker payment tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: taker_payment.tx_hex().map(From::from), + tx_hex: taker_payment.tx_hex().map(BytesJson::from), tx_hash, }; @@ -996,7 +995,7 @@ impl MakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; @@ -1096,7 +1095,7 @@ impl MakerSwap { let tx_hash = transaction.tx_hash(); info!("Maker payment refund tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 84eae04a1a..4523acb24f 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -9,6 +9,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_libp2p::{decode_signed, pub_sub_topic, TopicPrefix}; use mm2_number::BigDecimal; use parking_lot::Mutex as PaMutex; +use rpc::v1::types::Bytes as BytesJson; use std::cmp::min; use std::sync::Arc; use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -261,7 +262,7 @@ impl Watcher { let tx_hash = tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: tx.tx_hex().map(From::from), + tx_hex: tx.tx_hex().map(BytesJson::from), tx_hash, }; @@ -315,7 +316,7 @@ impl Watcher { let tx_hash = transaction.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index e56aabd820..680a1facd9 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -845,6 +845,9 @@ impl TakerSwap { } } + /// # Panic + /// + /// Panic if taker_fee of [`TakerSwapMut`] is [`Option::None`]. async fn get_taker_fee_data(&self) -> Result> { // If taker fee is a lightning payment the payment hash will be sent in the message let taker_fee_data = self @@ -1146,7 +1149,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker fee tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; @@ -1200,10 +1203,7 @@ impl TakerSwap { &self.r().secret_hash.0, self.taker_amount.clone().into(), ) { - Ok(Some(instructions)) => { - swap_events.push(TakerSwapEvent::TakerPaymentInstructionsReceived(instructions)) - }, - Ok(None) => (), + Ok(instructions) => swap_events.push(TakerSwapEvent::TakerPaymentInstructionsReceived(instructions)), Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ TakerSwapEvent::MakerPaymentValidateFailed(e.to_string().into()), @@ -1226,7 +1226,7 @@ impl TakerSwap { let tx_hash = maker_payment.tx_hash(); info!("Got maker payment {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: maker_payment.tx_hex().map(From::from), + tx_hex: maker_payment.tx_hex().map(BytesJson::from), tx_hash, }; @@ -1362,7 +1362,7 @@ impl TakerSwap { }; let tx_hash = transaction.tx_hash(); - let tx_hex = transaction.tx_hex().map(From::from); + let tx_hex = transaction.tx_hex().map(BytesJson::from); info!("Taker payment tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { tx_hex: tx_hex.clone(), @@ -1488,7 +1488,7 @@ impl TakerSwap { let tx_hash = tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: tx.tx_hex().map(From::from), + tx_hex: tx.tx_hex().map(BytesJson::from), tx_hash, }; @@ -1557,7 +1557,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; @@ -1623,7 +1623,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(From::from), + tx_hex: transaction.tx_hex().map(BytesJson::from), tx_hash, }; From 2113b7a953f4cbfba6f2bb025257a3a6a716dc7f Mon Sep 17 00:00:00 2001 From: shamardy Date: Mon, 24 Oct 2022 17:06:40 +0200 Subject: [PATCH 35/36] Review fixes: make TransactionIdentifier::tx_hex not an Option and add is_supported_by_watchers method to SwapOps --- mm2src/coins/eth.rs | 4 +- mm2src/coins/eth/eth_tests.rs | 4 +- mm2src/coins/lightning.rs | 7 +- mm2src/coins/lp_coins.rs | 4 +- mm2src/coins/my_tx_history_v2.rs | 2 +- mm2src/coins/qrc20.rs | 2 + mm2src/coins/solana.rs | 2 + mm2src/coins/solana/spl.rs | 2 + mm2src/coins/tendermint/tendermint_coin.rs | 2 + mm2src/coins/test_coin.rs | 2 + mm2src/coins/utxo.rs | 6 +- mm2src/coins/utxo/bch.rs | 2 + mm2src/coins/utxo/qtum.rs | 2 + mm2src/coins/utxo/slp.rs | 2 + mm2src/coins/utxo/utxo_common.rs | 5 +- mm2src/coins/utxo/utxo_standard.rs | 2 + mm2src/coins/z_coin.rs | 6 +- mm2src/mm2_main/src/docker_tests.rs | 36 ++--- .../mm2_main/src/docker_tests/qrc20_tests.rs | 46 +++--- mm2src/mm2_main/src/lp_swap.rs | 13 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 32 ++--- .../src/lp_swap/recreate_swap_data.rs | 2 +- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 6 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 134 ++++++++---------- 24 files changed, 166 insertions(+), 159 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index c6514fe9d0..33717d7383 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1170,6 +1170,8 @@ impl SwapOps for EthCoin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { false } } #[cfg_attr(test, mockable)] @@ -3428,7 +3430,7 @@ pub fn wei_from_big_decimal(amount: &BigDecimal, decimals: u8) -> NumConversResu } impl Transaction for SignedEthTx { - fn tx_hex(&self) -> Option> { Some(rlp::encode(self).to_vec()) } + fn tx_hex(&self) -> Vec { rlp::encode(self).to_vec() } fn tx_hash(&self) -> BytesJson { self.hash.to_vec().into() } } diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 21dca7fb95..201f828c1e 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -267,7 +267,7 @@ fn send_and_refund_erc20_payment() { let refund = coin .send_maker_refunds_payment( - &payment.tx_hex().unwrap(), + &payment.tx_hex(), (now_ms() / 1000) as u32 - 200, &DEX_FEE_ADDR_RAW_PUBKEY, &[1; 20], @@ -337,7 +337,7 @@ fn send_and_refund_eth_payment() { let refund = coin .send_maker_refunds_payment( - &payment.tx_hex().unwrap(), + &payment.tx_hex(), (now_ms() / 1000) as u32 - 200, &DEX_FEE_ADDR_RAW_PUBKEY, &[1; 20], diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 928047e3a4..382a1b4a65 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -151,7 +151,7 @@ impl From for PaymentError { } impl Transaction for PaymentHash { - fn tx_hex(&self) -> Option> { None } + fn tx_hex(&self) -> Vec { self.0.to_vec() } fn tx_hash(&self) -> BytesJson { self.0.to_vec().into() } } @@ -769,6 +769,11 @@ impl SwapOps for LightningCoin { // Todo: continue validation here by comparing locktime, etc.. Ok(PaymentInstructions::Lightning(invoice)) } + + // Watchers cannot be used for lightning swaps for now + // Todo: Check if watchers can work in some cases with lightning and implement it if it's possible, the watcher will not be able to retrieve the preimage since it's retrieved through the lightning network + // Todo: The watcher can retrieve the preimage only if he is running a lightning node and is part of the nodes that routed the taker payment which is a very low probability event that shouldn't be considered + fn is_supported_by_watchers(&self) -> bool { false } } #[derive(Debug, Display)] diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index e500016072..6072838094 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -350,7 +350,7 @@ pub enum UnexpectedDerivationMethod { pub trait Transaction: fmt::Debug + 'static { /// Raw transaction bytes of the transaction - fn tx_hex(&self) -> Option>; + fn tx_hex(&self) -> Vec; /// Serializable representation of tx hash for displaying purpose fn tx_hash(&self) -> BytesJson; } @@ -680,6 +680,8 @@ pub trait SwapOps { secret_hash: &[u8], amount: BigDecimal, ) -> Result>; + + fn is_supported_by_watchers(&self) -> bool; } /// Operations that coins have independently from the MarketMaker. diff --git a/mm2src/coins/my_tx_history_v2.rs b/mm2src/coins/my_tx_history_v2.rs index 0344f83817..7e7c97e616 100644 --- a/mm2src/coins/my_tx_history_v2.rs +++ b/mm2src/coins/my_tx_history_v2.rs @@ -222,7 +222,7 @@ impl<'a, Addr: Clone + DisplayAddress + Eq + std::hash::Hash, Tx: Transaction> T TransactionDetails { coin: self.coin, - tx_hex: self.tx.tx_hex().unwrap_or_default().into(), + tx_hex: self.tx.tx_hex().into(), tx_hash: tx_hash.to_tx_hash(), from, to, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 6ac10d48a8..9535a549d9 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1063,6 +1063,8 @@ impl SwapOps for Qrc20Coin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { false } } impl MarketCoinOps for Qrc20Coin { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 9c14c2f322..2a43ee4924 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -634,6 +634,8 @@ impl SwapOps for SolanaCoin { ) -> Result> { unimplemented!() } + + fn is_supported_by_watchers(&self) -> bool { unimplemented!() } } #[allow(clippy::forget_ref, clippy::forget_copy, clippy::cast_ref_to_mut)] diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 522bd9a4c8..6588c31bf7 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -464,6 +464,8 @@ impl SwapOps for SplToken { ) -> Result> { unimplemented!() } + + fn is_supported_by_watchers(&self) -> bool { unimplemented!() } } #[allow(clippy::forget_ref, clippy::forget_copy, clippy::cast_ref_to_mut)] diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 52d9f2ffae..ac98222949 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -804,6 +804,8 @@ impl SwapOps for TendermintCoin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { false } } #[cfg(test)] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index d332e2596c..b3b32728c9 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -272,6 +272,8 @@ impl SwapOps for TestCoin { ) -> Result> { unimplemented!() } + + fn is_supported_by_watchers(&self) -> bool { unimplemented!() } } #[async_trait] diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 74bc889608..d50685a568 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -165,11 +165,11 @@ fn get_special_folder_path() -> PathBuf { fn get_special_folder_path() -> PathBuf { panic!("!windows") } impl Transaction for UtxoTx { - fn tx_hex(&self) -> Option> { + fn tx_hex(&self) -> Vec { if self.has_witness() { - Some(serialize_with_flags(self, SERIALIZE_TRANSACTION_WITNESS).into()) + serialize_with_flags(self, SERIALIZE_TRANSACTION_WITNESS).into() } else { - Some(serialize(self).into()) + serialize(self).into() } } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index af028846e4..bedc5a816c 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1063,6 +1063,8 @@ impl SwapOps for BchCoin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { true } } fn total_unspent_value<'a>(unspents: impl IntoIterator) -> u64 { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index c30493c3a7..58f02155b6 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -756,6 +756,8 @@ impl SwapOps for QtumCoin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { true } } impl MarketCoinOps for QtumCoin { diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 9068830750..4da5ff9a6b 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1482,6 +1482,8 @@ impl SwapOps for SlpToken { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { false } } impl From for TradePreimageError { diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 194c5f356c..4a54c6bcba 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2093,10 +2093,7 @@ pub fn wait_for_output_spend( pub fn tx_enum_from_bytes(coin: &UtxoCoinFields, bytes: &[u8]) -> Result> { let mut transaction: UtxoTx = deserialize(bytes).map_to_mm(|e| TxMarshalingErr::InvalidInput(e.to_string()))?; - let serialized_length = transaction - .tx_hex() - .ok_or_else(|| TxMarshalingErr::Internal("tx_hex shouldn't be None!".into()))? - .len(); + let serialized_length = transaction.tx_hex().len(); if bytes.len() != serialized_length { return MmError::err(TxMarshalingErr::CrossCheckFailed(format!( "Expected '{}' lenght of the serialized transaction, found '{}'", diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 7542d85880..b2da31b21d 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -522,6 +522,8 @@ impl SwapOps for UtxoStandardCoin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { true } } impl MarketCoinOps for UtxoStandardCoin { diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 090776a128..a7efe953f4 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -173,10 +173,10 @@ pub struct ZCoinFields { } impl Transaction for ZTransaction { - fn tx_hex(&self) -> Option> { + fn tx_hex(&self) -> Vec { let mut hex = Vec::with_capacity(1024); self.write(&mut hex).expect("Writing should not fail"); - Some(hex) + hex } fn tx_hash(&self) -> BytesJson { @@ -1418,6 +1418,8 @@ impl SwapOps for ZCoin { ) -> Result> { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } + + fn is_supported_by_watchers(&self) -> bool { false } } #[async_trait] diff --git a/mm2src/mm2_main/src/docker_tests.rs b/mm2src/mm2_main/src/docker_tests.rs index ff66eaefb2..f13b32ae0a 100644 --- a/mm2src/mm2_main/src/docker_tests.rs +++ b/mm2src/mm2_main/src/docker_tests.rs @@ -360,7 +360,7 @@ mod docker_tests { .wait() .unwrap(); self.coin - .wait_for_confirmations(&slp_genesis_tx.tx_hex().unwrap(), 1, false, now_ms() / 1000 + 30, 1) + .wait_for_confirmations(&slp_genesis_tx.tx_hex(), 1, false, now_ms() / 1000 + 30, 1) .wait() .unwrap(); @@ -374,7 +374,7 @@ mod docker_tests { let tx = block_on(adex_slp.send_slp_outputs(slp_outputs)).unwrap(); self.coin - .wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, now_ms() / 1000 + 30, 1) + .wait_for_confirmations(&tx.tx_hex(), 1, false, now_ms() / 1000 + 30, 1) .wait() .unwrap(); *SLP_TOKEN_OWNERS.lock().unwrap() = slp_privkeys; @@ -398,16 +398,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); @@ -415,7 +415,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex().unwrap(), + tx: &tx.tx_hex(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -455,16 +455,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); @@ -472,7 +472,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex().unwrap(), + tx: &tx.tx_hex(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -504,16 +504,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); let spend_tx = coin - .send_maker_spends_taker_payment(&tx.tx_hex().unwrap(), time_lock, my_pubkey, &secret, &None, &[]) + .send_maker_spends_taker_payment(&tx.tx_hex(), time_lock, my_pubkey, &secret, &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&spend_tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&spend_tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); @@ -521,7 +521,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &*dhash160(&secret), - tx: &tx.tx_hex().unwrap(), + tx: &tx.tx_hex(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -553,16 +553,16 @@ mod docker_tests { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); let spend_tx = coin - .send_taker_spends_maker_payment(&tx.tx_hex().unwrap(), time_lock, my_pubkey, &secret, &None, &[]) + .send_taker_spends_maker_payment(&tx.tx_hex(), time_lock, my_pubkey, &secret, &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&spend_tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&spend_tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); @@ -570,7 +570,7 @@ mod docker_tests { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &*dhash160(&secret), - tx: &tx.tx_hex().unwrap(), + tx: &tx.tx_hex(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], diff --git a/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs index 3984b9be1b..3a7aa1a7ed 100644 --- a/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/src/docker_tests/qrc20_tests.rs @@ -189,7 +189,7 @@ fn test_taker_spends_maker_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -226,7 +226,7 @@ fn test_taker_spends_maker_payment() { .wait() .unwrap(); let spend_tx_hash = spend.tx_hash(); - let spend_tx_hex = spend.tx_hex().unwrap(); + let spend_tx_hex = spend.tx_hex(); log!("Taker spends tx: {:?}", spend_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -282,7 +282,7 @@ fn test_maker_spends_taker_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Taker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -319,7 +319,7 @@ fn test_maker_spends_taker_payment() { .wait() .unwrap(); let spend_tx_hash = spend.tx_hash(); - let spend_tx_hex = spend.tx_hex().unwrap(); + let spend_tx_hex = spend.tx_hex(); log!("Maker spends tx: {:?}", spend_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -364,7 +364,7 @@ fn test_maker_refunds_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -390,7 +390,7 @@ fn test_maker_refunds_payment() { .wait() .unwrap(); let refund_tx_hash = refund.tx_hash(); - let refund_tx_hex = refund.tx_hex().unwrap(); + let refund_tx_hex = refund.tx_hex(); log!("Maker refunds payment: {:?}", refund_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -426,7 +426,7 @@ fn test_taker_refunds_payment() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Taker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -452,7 +452,7 @@ fn test_taker_refunds_payment() { .wait() .unwrap(); let refund_tx_hash = refund.tx_hash(); - let refund_tx_hex = refund.tx_hex().unwrap(); + let refund_tx_hex = refund.tx_hex(); log!("Taker refunds payment: {:?}", refund_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -485,7 +485,7 @@ fn test_check_if_my_payment_sent() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 2; @@ -537,7 +537,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -561,7 +561,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { .wait() .unwrap(); let spend_tx_hash = spend.tx_hash(); - let spend_tx_hex = spend.tx_hex().unwrap(); + let spend_tx_hex = spend.tx_hex(); log!("Taker spends tx: {:?}", spend_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -608,7 +608,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -632,7 +632,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { .wait() .unwrap(); let refund_tx_hash = refund.tx_hash(); - let refund_tx_hex = refund.tx_hex().unwrap(); + let refund_tx_hex = refund.tx_hex(); log!("Maker refunds tx: {:?}", refund_tx_hash); let wait_until = (now_ms() / 1000) + 40; // timeout if test takes more than 40 seconds to run @@ -679,7 +679,7 @@ fn test_search_for_swap_tx_spend_not_spent() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -731,7 +731,7 @@ fn test_wait_for_tx_spend() { .wait() .unwrap(); let payment_tx_hash = payment.tx_hash(); - let payment_tx_hex = payment.tx_hex().unwrap(); + let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); let confirmations = 1; @@ -1430,16 +1430,16 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); @@ -1447,7 +1447,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex().unwrap(), + tx: &tx.tx_hex(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], @@ -1471,16 +1471,16 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { .wait() .unwrap(); - coin.wait_for_confirmations(&tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); let refund_tx = coin - .send_maker_refunds_payment(&tx.tx_hex().unwrap(), time_lock, my_public_key, &[0; 20], &None, &[]) + .send_maker_refunds_payment(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) .wait() .unwrap(); - coin.wait_for_confirmations(&refund_tx.tx_hex().unwrap(), 1, false, timeout, 1) + coin.wait_for_confirmations(&refund_tx.tx_hex(), 1, false, timeout, 1) .wait() .unwrap(); @@ -1488,7 +1488,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { time_lock, other_pub: &*coin.my_public_key().unwrap(), secret_hash: &[0; 20], - tx: &tx.tx_hex().unwrap(), + tx: &tx.tx_hex(), search_from_block: 0, swap_contract_address: &None, swap_unique_data: &[], diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 436ba7d027..c0da5d0e03 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -725,19 +725,12 @@ impl SwapTxDataMsg { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct TransactionIdentifier { /// Raw bytes of signed transaction in hexadecimal string, this should be sent as is to send_raw_transaction RPC to broadcast the transaction. - /// Some payments like lightning payments don't have a tx_hex, this is why tx_hex is an option. - tx_hex: Option, + /// Some payments like lightning payments don't have a tx_hex, for such payments tx_hex will be equal to tx_hash. + tx_hex: BytesJson, /// Transaction hash in hexadecimal format tx_hash: BytesJson, } -impl TransactionIdentifier { - fn tx_hex_or_hash(&self) -> BytesJson { - let tx_hash = self.tx_hash.clone(); - self.tx_hex.clone().unwrap_or(tx_hash) - } -} - pub fn my_swaps_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("MY") } pub fn my_swap_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { my_swaps_dir(ctx).join(format!("{}.json", uuid)) } @@ -1167,7 +1160,7 @@ pub async fn recover_funds_of_swap(ctx: MmArc, req: Json) -> Result Result> { // If maker payment is a lightning payment the payment hash will be sent in the message // It's not really needed here unlike in TakerFee msg since the hash is included in the invoice/payment_instructions but it's kept for symmetry - let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex_or_hash().0; + let payment_data = self.r().maker_payment.as_ref().unwrap().tx_hex.0.clone(); let instructions = self .taker_coin .payment_instructions( @@ -714,7 +714,7 @@ impl MakerSwap { } let fee_ident = TransactionIdentifier { - tx_hex: taker_fee.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(taker_fee.tx_hex()), tx_hash: hash, }; swap_events.push(MakerSwapEvent::TakerFeeValidated(fee_ident)); @@ -782,7 +782,7 @@ impl MakerSwap { info!("Maker payment tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; @@ -809,7 +809,7 @@ impl MakerSwap { let maker_payment_wait_confirm = self.r().data.started_at + (self.r().data.lock_duration * 2) / 5; let f = self.maker_coin.wait_for_confirmations( - &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().maker_payment.clone().unwrap().tx_hex, self.r().data.maker_payment_confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), maker_payment_wait_confirm, @@ -865,7 +865,7 @@ impl MakerSwap { let tx_hash = taker_payment.tx_hash(); info!("Taker payment tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: taker_payment.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(taker_payment.tx_hex()), tx_hash, }; @@ -883,7 +883,7 @@ impl MakerSwap { let wait_f = self .taker_coin .wait_for_confirmations( - &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().taker_payment.clone().unwrap().tx_hex, confirmations, self.r().data.taker_payment_requires_nota.unwrap_or(false), wait_taker_payment, @@ -902,7 +902,7 @@ impl MakerSwap { } let validate_input = ValidatePaymentInput { - payment_tx: self.r().taker_payment.clone().unwrap().tx_hex_or_hash().0, + payment_tx: self.r().taker_payment.clone().unwrap().tx_hex.0, time_lock: self.taker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_taker_coin_htlc_pub.to_vec(), unique_swap_data: self.unique_swap_data(), @@ -950,7 +950,7 @@ impl MakerSwap { } let spend_fut = self.taker_coin.send_maker_spends_taker_payment( - &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().taker_payment.clone().unwrap().tx_hex, self.taker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_taker_coin_htlc_pub, &self.r().data.secret.0, @@ -995,7 +995,7 @@ impl MakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; @@ -1010,7 +1010,7 @@ impl MakerSwap { let confirmations = std::cmp::min(1, self.r().data.taker_payment_confirmations); let requires_nota = false; let wait_fut = self.taker_coin.wait_for_confirmations( - &self.r().taker_payment_spend.clone().unwrap().tx_hex_or_hash(), + &self.r().taker_payment_spend.clone().unwrap().tx_hex, confirmations, requires_nota, self.wait_refund_until(), @@ -1053,7 +1053,7 @@ impl MakerSwap { } let spend_fut = self.maker_coin.send_maker_refunds_payment( - &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().maker_payment.clone().unwrap().tx_hex, self.r().data.maker_payment_lock as u32, &*self.r().other_maker_coin_htlc_pub, self.secret_hash().as_slice(), @@ -1095,7 +1095,7 @@ impl MakerSwap { let tx_hash = transaction.tx_hash(); info!("Maker payment refund tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; @@ -1187,7 +1187,7 @@ impl MakerSwap { .taker_payment .clone() .ok_or(ERRL!("No info about taker payment, swap is not recoverable"))? - .tx_hex_or_hash(); + .tx_hex; // have to do this because std::sync::RwLockReadGuard returned by r() is not Send, // so it can't be used across await @@ -1268,7 +1268,7 @@ impl MakerSwap { let maybe_maker_payment = self.r().maker_payment.clone(); let maker_payment = match maybe_maker_payment { - Some(tx) => tx.tx_hex_or_hash().0, + Some(tx) => tx.tx_hex.0, None => { let maybe_maker_payment = try_s!( self.maker_coin @@ -1284,9 +1284,7 @@ impl MakerSwap { .await ); match maybe_maker_payment { - Some(tx) => try_s!(tx - .tx_hex() - .ok_or("recover_funds is not implemented for lightning swaps yet!")), + Some(tx) => tx.tx_hex(), None => return ERR!("Maker payment transaction was not found"), } }, 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 c811ef833f..71bd568aba 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -448,7 +448,7 @@ async fn convert_maker_to_taker_events( return events; }, MakerSwapEvent::TakerPaymentSpent(tx_ident) => { - let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex_or_hash()).await { + let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex).await { Ok(secret) => H256Json::from(secret.as_slice()), Err(e) => { push_event!(TakerSwapEvent::TakerPaymentWaitForSpendFailed(ERRL!("{}", e).into())); diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 4523acb24f..fcf25e04b0 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -262,13 +262,13 @@ impl Watcher { let tx_hash = tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: tx.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(tx.tx_hex()), tx_hash, }; let secret = match self .taker_coin - .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex_or_hash()) + .extract_secret(&self.data.secret_hash[..], &tx_ident.tx_hex) .await { Ok(bytes) => H256Json::from(bytes.as_slice()), @@ -316,7 +316,7 @@ impl Watcher { let tx_hash = transaction.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 680a1facd9..5cb6bd8f02 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -855,8 +855,9 @@ impl TakerSwap { .taker_fee .as_ref() .expect("TakerSwapMut::taker_fee must be some value to use get_taker_fee_data") - .tx_hex_or_hash() - .0; + .tx_hex + .0 + .clone(); let secret_hash = self.r().secret_hash.0.clone(); let maker_amount = self.maker_amount.clone().into(); let instructions = self @@ -1149,7 +1150,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker fee tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; @@ -1226,7 +1227,7 @@ impl TakerSwap { let tx_hash = maker_payment.tx_hash(); info!("Got maker payment {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: maker_payment.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(maker_payment.tx_hex()), tx_hash, }; @@ -1240,7 +1241,7 @@ impl TakerSwap { info!("Before wait confirm"); let confirmations = self.r().data.maker_payment_confirmations; let f = self.maker_coin.wait_for_confirmations( - &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().maker_payment.clone().unwrap().tx_hex, confirmations, self.r().data.maker_payment_requires_nota.unwrap_or(false), self.r().data.maker_payment_wait, @@ -1256,7 +1257,7 @@ impl TakerSwap { info!("After wait confirm"); let validate_input = ValidatePaymentInput { - payment_tx: self.r().maker_payment.clone().unwrap().tx_hex_or_hash().0, + payment_tx: self.r().maker_payment.clone().unwrap().tx_hex.0, time_lock: self.maker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: self.r().other_maker_coin_htlc_pub.to_vec(), secret_hash: self.r().secret_hash.0.to_vec(), @@ -1362,7 +1363,7 @@ impl TakerSwap { }; let tx_hash = transaction.tx_hash(); - let tx_hex = transaction.tx_hex().map(BytesJson::from); + let tx_hex = BytesJson::from(transaction.tx_hex()); info!("Taker payment tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { tx_hex: tx_hex.clone(), @@ -1370,71 +1371,67 @@ impl TakerSwap { }; let mut swap_events = vec![TakerSwapEvent::TakerPaymentSent(tx_ident)]; - // Watchers cannot be used for lightning swaps for now - // Todo: Check if watchers can work in some cases with lightning and implement it if it's possible, the watcher will not be able to retrieve the preimage since it's retrieved through the lightning network - // Todo: The watcher can retrieve the preimage only if he is running a lightning node and is part of the nodes that routed the taker payment which is a very low probability event that shouldn't be considered - let maybe_maker_hex = self.r().maker_payment.as_ref().unwrap().tx_hex.clone(); - if let Some(maker_hex) = maybe_maker_hex { - if let Some(taker_hex) = tx_hex { - if self.ctx.use_watchers() { - let preimage_fut = self.taker_coin.create_taker_spends_maker_payment_preimage( - &maker_hex, - self.maker_payment_lock.load(Ordering::Relaxed) as u32, - self.r().other_maker_coin_htlc_pub.as_slice(), - &self.r().secret_hash.0, - &self.unique_swap_data()[..], - ); + if self.ctx.use_watchers() + && self.taker_coin.is_supported_by_watchers() + && self.maker_coin.is_supported_by_watchers() + { + let preimage_fut = self.taker_coin.create_taker_spends_maker_payment_preimage( + &self.r().maker_payment.as_ref().unwrap().tx_hex, + self.maker_payment_lock.load(Ordering::Relaxed) as u32, + self.r().other_maker_coin_htlc_pub.as_slice(), + &self.r().secret_hash.0, + &self.unique_swap_data()[..], + ); - match preimage_fut.compat().await.map(|p| p.tx_hex()) { - Ok(Some(preimage)) => { - let watcher_data = self.create_watcher_data(taker_hex.0, preimage.clone()); - let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); - broadcast_swap_message( - &self.ctx, - watcher_topic(&self.r().data.taker_coin), - swpmsg_watcher, - &self.p2p_privkey, - ); - swap_events.push(TakerSwapEvent::WatcherMessageSent(Some(preimage))) - }, - Ok(None) => (), - Err(e) => error!( + match preimage_fut.compat().await { + Ok(preimage) => { + let watcher_data = self.create_watcher_data(transaction.tx_hex(), preimage.tx_hex()); + let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); + broadcast_swap_message( + &self.ctx, + watcher_topic(&self.r().data.taker_coin), + swpmsg_watcher, + &self.p2p_privkey, + ); + swap_events.push(TakerSwapEvent::WatcherMessageSent(Some(preimage.tx_hex()))) + }, + Err(e) => error!( "The watcher message could not be sent, error creating the taker spends maker payment preimage: {}", e.get_plain_text_format() ), - } - } } } + Ok((Some(TakerSwapCommand::WaitForTakerPaymentSpend), swap_events)) } async fn wait_for_taker_payment_spend(&self) -> Result<(Option, Vec), String> { const BROADCAST_SWAP_MESSAGE_INTERVAL: f64 = 600.; - let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.clone(); + let tx_hex = self.r().taker_payment.as_ref().unwrap().tx_hex.0.clone(); let mut watcher_broadcast_abort_handle = None; // Watchers cannot be used for lightning swaps for now // Todo: Check if watchers can work in some cases with lightning and implement it if it's possible, this part will probably work if only the taker is lightning since the preimage is available - if let Some(hex) = tx_hex { - if self.ctx.use_watchers() { - let preimage_hex = self.r().taker_spends_maker_payment_preimage.clone(); - if let Some(preimage_hex) = preimage_hex { - let watcher_data = self.create_watcher_data(hex.0, preimage_hex); - let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); - watcher_broadcast_abort_handle = Some(broadcast_swap_message_every( - self.ctx.clone(), - watcher_topic(&self.r().data.taker_coin), - swpmsg_watcher, - BROADCAST_SWAP_MESSAGE_INTERVAL, - self.p2p_privkey, - )); - } + if self.ctx.use_watchers() + && self.taker_coin.is_supported_by_watchers() + && self.maker_coin.is_supported_by_watchers() + { + let preimage_hex = self.r().taker_spends_maker_payment_preimage.clone(); + if let Some(preimage_hex) = preimage_hex { + let watcher_data = self.create_watcher_data(tx_hex.clone(), preimage_hex); + let swpmsg_watcher = SwapWatcherMsg::TakerSwapWatcherMsg(Box::new(watcher_data)); + watcher_broadcast_abort_handle = Some(broadcast_swap_message_every( + self.ctx.clone(), + watcher_topic(&self.r().data.taker_coin), + swpmsg_watcher, + 600., + self.p2p_privkey, + )); } } // Todo: taker_payment should be a message on lightning network not a swap message - let msg = SwapMsg::TakerPayment(self.r().taker_payment.as_ref().unwrap().tx_hex_or_hash().0); + let msg = SwapMsg::TakerPayment(tx_hex); let send_abort_handle = Some(broadcast_swap_message_every( self.ctx.clone(), swap_topic(&self.uuid), @@ -1448,7 +1445,7 @@ impl TakerSwap { let wait_f = self .taker_coin .wait_for_confirmations( - &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().taker_payment.clone().unwrap().tx_hex, self.r().data.taker_payment_confirmations, self.r().data.taker_payment_requires_nota.unwrap_or(false), wait_taker_payment, @@ -1467,7 +1464,7 @@ impl TakerSwap { } let f = self.taker_coin.wait_for_tx_spend( - &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().taker_payment.clone().unwrap().tx_hex, self.r().data.taker_payment_lock, self.r().data.taker_coin_start_block, &self.r().data.taker_coin_swap_contract_address, @@ -1488,16 +1485,12 @@ impl TakerSwap { let tx_hash = tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: tx.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(tx.tx_hex()), tx_hash, }; let secret_hash = self.r().secret_hash.clone(); - let secret = match self - .taker_coin - .extract_secret(&secret_hash.0, &tx_ident.tx_hex_or_hash()) - .await - { + let secret = match self.taker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex).await { Ok(bytes) => H256Json::from(bytes.as_slice()), Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ @@ -1522,7 +1515,7 @@ impl TakerSwap { ])); } let spend_fut = self.maker_coin.send_taker_spends_maker_payment( - &self.r().maker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().maker_payment.clone().unwrap().tx_hex, self.maker_payment_lock.load(Ordering::Relaxed) as u32, &*self.r().other_maker_coin_htlc_pub, &self.r().secret.0, @@ -1557,7 +1550,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; @@ -1587,7 +1580,7 @@ impl TakerSwap { } let refund_fut = self.taker_coin.send_taker_refunds_payment( - &self.r().taker_payment.clone().unwrap().tx_hex_or_hash(), + &self.r().taker_payment.clone().unwrap().tx_hex, self.r().data.taker_payment_lock as u32, &*self.r().other_taker_coin_htlc_pub, &self.r().secret_hash.0, @@ -1623,7 +1616,7 @@ impl TakerSwap { let tx_hash = transaction.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { - tx_hex: transaction.tx_hex().map(BytesJson::from), + tx_hex: BytesJson::from(transaction.tx_hex()), tx_hash, }; @@ -1722,7 +1715,7 @@ impl TakerSwap { } let maker_payment = match &self.r().maker_payment { - Some(tx) => tx.tx_hex_or_hash(), + Some(tx) => tx.tx_hex.0.clone(), None => return ERR!("No info about maker payment, swap is not recoverable"), }; @@ -1775,7 +1768,7 @@ impl TakerSwap { let maybe_taker_payment = self.r().taker_payment.clone(); let taker_payment = match maybe_taker_payment { - Some(tx) => tx.tx_hex_or_hash().0, + Some(tx) => tx.tx_hex.0, None => { let maybe_sent = try_s!( self.taker_coin @@ -1792,9 +1785,7 @@ impl TakerSwap { ); match maybe_sent { // Todo: Refactor this when implementing recover_funds for lightning swaps - Some(tx) => try_s!(tx - .tx_hex() - .ok_or("recover_funds is not implemented for lightning swaps yet!")), + Some(tx) => tx.tx_hex(), None => return ERR!("Taker payment is not found, swap is not recoverable"), } }, @@ -1855,9 +1846,8 @@ impl TakerSwap { Some(spend) => match spend { FoundSwapTxSpend::Spent(tx) => { check_maker_payment_is_not_spent!(); - let tx_hash = tx.tx_hash().0; - let tx_hex = tx.tx_hex().unwrap_or(tx_hash); let secret_hash = self.r().secret_hash.clone(); + let tx_hex = tx.tx_hex(); let secret = try_s!(self.taker_coin.extract_secret(&secret_hash.0, &tx_hex).await); let fut = self.maker_coin.send_taker_spends_maker_payment( From bea917382edf7b4091796fd50df7e88586add92b Mon Sep 17 00:00:00 2001 From: shamardy Date: Wed, 26 Oct 2022 15:15:39 +0200 Subject: [PATCH 36/36] re-add Todo that was removed by merge --- mm2src/coins/lightning.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 3ad2302bc5..3fe081e5e9 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -648,6 +648,7 @@ impl SwapOps for LightningCoin { Box::new(fut.boxed().compat()) } + // Todo: This is None for now for the sake of swap P.O.C., this should be implemented probably in next PRs and should be tested across restarts fn check_if_my_payment_sent( &self, _time_lock: u32,