From e1983d5639ab49656acc67f8836328cdbc87be7b Mon Sep 17 00:00:00 2001 From: binarybaron <86064887+binarybaron@users.noreply.github.com> Date: Fri, 2 Dec 2022 21:46:27 +0100 Subject: [PATCH] Cleanup, formatting, add `get_seller`, `get_swap_start_date` RPC endpoints --- swap/Cargo.toml | 2 +- swap/sqlx-data.json | 78 +++++++-- swap/src/api.rs | 308 ++++++++++++++++++---------------- swap/src/bin/swap.rs | 10 +- swap/src/cli/command.rs | 323 +++++++++++++++++++++--------------- swap/src/database/sqlite.rs | 48 +++++- swap/src/lib.rs | 2 +- swap/src/protocol.rs | 3 + swap/src/rpc.rs | 25 +-- swap/src/rpc/methods.rs | 195 +++++++++++++++++----- 10 files changed, 639 insertions(+), 355 deletions(-) diff --git a/swap/Cargo.toml b/swap/Cargo.toml index fe3cc9aa..e67e8170 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -31,7 +31,7 @@ ed25519-dalek = "1" futures = { version = "0.3", default-features = false } hex = "0.4" itertools = "0.10" -jsonrpsee = { version = "0.15.1", features = [ "server"] } +jsonrpsee = { version = "0.15.1", features = [ "server" ] } jsonrpsee-core = "0.15.1" libp2p = { version = "0.42.2", default-features = false, features = [ "tcp-tokio", "yamux", "mplex", "dns-tokio", "noise", "request-response", "websocket", "ping", "rendezvous", "identify" ] } monero = { version = "0.12", features = [ "serde_support" ] } diff --git a/swap/sqlx-data.json b/swap/sqlx-data.json index fd50cd87..3afb7065 100644 --- a/swap/sqlx-data.json +++ b/swap/sqlx-data.json @@ -28,6 +28,24 @@ }, "query": "\n insert into peer_addresses (\n peer_id,\n address\n ) values (?, ?);\n " }, + "0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c": { + "describe": { + "columns": [ + { + "name": "start_date", + "ordinal": 0, + "type_info": "Text" + } + ], + "nullable": [ + true + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT min(entered_at) as start_date\n FROM swap_states\n WHERE swap_id = ?\n " + }, "1ec38c85e7679b2eb42b3df75d9098772ce44fdb8db3012d3c2410d828b74157": { "describe": { "columns": [ @@ -62,6 +80,30 @@ }, "query": "\n insert into peers (\n swap_id,\n peer_id\n ) values (?, ?);\n " }, + "3f2bfdd2d134586ccad22171cd85a465800fc5c4fdaf191d206974e530240c87": { + "describe": { + "columns": [ + { + "name": "swap_id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "state", + "ordinal": 1, + "type_info": "Text" + } + ], + "nullable": [ + false, + false + ], + "parameters": { + "Right": 0 + } + }, + "query": "\n SELECT swap_id, state\n FROM swap_states\n " + }, "50a5764546f69c118fa0b64120da50f51073d36257d49768de99ff863e3511e0": { "describe": { "columns": [], @@ -90,24 +132,6 @@ }, "query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n " }, - "a0eb85d04ee3842c52291dad4d225941d1141af735922fcbc665868997fce304": { - "describe": { - "columns": [ - { - "name": "address", - "ordinal": 0, - "type_info": "Text" - } - ], - "nullable": [ - false - ], - "parameters": { - "Right": 1 - } - }, - "query": "\n SELECT address\n FROM peer_addresses\n WHERE peer_id = ?\n " - }, "b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": { "describe": { "columns": [], @@ -135,5 +159,23 @@ } }, "query": "\n SELECT address\n FROM monero_addresses\n WHERE swap_id = ?\n " + }, + "d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2": { + "describe": { + "columns": [ + { + "name": "address", + "ordinal": 0, + "type_info": "Text" + } + ], + "nullable": [ + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT DISTINCT address\n FROM peer_addresses\n WHERE peer_id = ?\n " } } \ No newline at end of file diff --git a/swap/src/api.rs b/swap/src/api.rs index b4594ed4..cbb2c18b 100644 --- a/swap/src/api.rs +++ b/swap/src/api.rs @@ -1,39 +1,34 @@ +use crate::bitcoin::{Amount, TxLock}; +use crate::cli::command::{Bitcoin, Command, Monero, Tor}; +use crate::cli::{list_sellers, EventLoop, SellerStatus}; +use crate::database::open_db; +use crate::env::{Config, GetConfig, Mainnet, Testnet}; +use crate::fs::system_data_dir; +use crate::libp2p_ext::MultiAddrExt; +use crate::network::quote::{BidQuote, ZeroQuoteReceived}; +use crate::network::rendezvous::XmrBtcNamespace; +use crate::network::swarm; +use crate::protocol::bob::{BobState, Swap}; +use crate::protocol::{bob, Database}; +use crate::seed::Seed; +use crate::{bitcoin, cli, monero, rpc}; use anyhow::{bail, Context as AnyContext, Result}; use comfy_table::Table; +use libp2p::core::Multiaddr; use qrcode::render::unicode; use qrcode::QrCode; -use crate::env::GetConfig; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde_json::json; use std::cmp::min; -use crate::network::rendezvous::XmrBtcNamespace; -use std::net::SocketAddr; -use libp2p::core::Multiaddr; use std::convert::TryInto; -use crate::bitcoin::Amount; +use std::fmt; use std::future::Future; +use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; -use crate::bitcoin::TxLock; -use crate::cli::command::{Command, Bitcoin, Monero, Tor}; -use crate::cli::{list_sellers, EventLoop, SellerStatus}; -use crate::database::open_db; -use crate::libp2p_ext::MultiAddrExt; -use crate::network::quote::{BidQuote, ZeroQuoteReceived}; -use crate::network::swarm; -use crate::protocol::bob; -use crate::protocol::bob::{BobState, Swap}; -use crate::seed::Seed; -use crate::rpc; -use crate::{bitcoin, cli, monero}; use url::Url; use uuid::Uuid; -use crate::protocol::Database; -use crate::env::{Config, Mainnet, Testnet}; -use crate::fs::system_data_dir; -use serde_json::json; -use serde::ser::{Serialize, Serializer, SerializeStruct}; -use std::fmt; - #[derive(PartialEq, Debug)] pub struct Request { @@ -53,7 +48,7 @@ pub struct Params { } pub struct Context { - db: Arc, + pub db: Arc, bitcoin_wallet: Option>, monero_wallet: Option>, monero_rpc_process: Option, @@ -81,10 +76,17 @@ impl Request { let bitcoin_change_address = self.params.bitcoin_change_address.clone().unwrap(); let bitcoin_wallet = btc; - let seller_peer_id = self.params.seller.as_ref().unwrap() + let seller_peer_id = self + .params + .seller + .as_ref() + .unwrap() .extract_peer_id() .context("Seller address must contain peer ID")?; - context.db.insert_address(seller_peer_id, seller.clone()).await?; + context + .db + .insert_address(seller_peer_id, seller.clone()) + .await?; let behaviour = cli::Behaviour::new( seller_peer_id, @@ -92,8 +94,12 @@ impl Request { bitcoin_wallet.clone(), (seed.derive_libp2p_identity(), context.namespace), ); - let mut swarm = - swarm::cli(seed.derive_libp2p_identity(), context.tor_socks5_port.unwrap(), behaviour).await?; + let mut swarm = swarm::cli( + seed.derive_libp2p_identity(), + context.tor_socks5_port.unwrap(), + behaviour, + ) + .await?; swarm.behaviour_mut().add_address(seller_peer_id, seller); tracing::debug!(peer_id = %swarm.local_peer_id(), "Network layer initialized"); @@ -128,7 +134,9 @@ impl Request { tracing::info!(%amount, %fees, "Determined swap amount"); context.db.insert_peer_id(swap_id, seller_peer_id).await?; - context.db.insert_monero_address(swap_id, monero_receive_address) + context + .db + .insert_monero_address(swap_id, monero_receive_address) .await?; let monero_wallet = context.monero_wallet.as_ref().unwrap(); @@ -165,18 +173,20 @@ impl Request { let state: BobState = state.try_into()?; vec.push((swap_id, state.to_string())); } - json!({ - "swaps": vec - }) - + json!({ "swaps": vec }) } Command::Config => { -// tracing::info!(path=%data_dir.display(), "Data directory"); -// tracing::info!(path=%format!("{}/logs", data_dir.display()), "Log files directory"); -// tracing::info!(path=%format!("{}/sqlite", data_dir.display()), "Sqlite file location"); -// tracing::info!(path=%format!("{}/seed.pem", data_dir.display()), "Seed file location"); -// tracing::info!(path=%format!("{}/monero", data_dir.display()), "Monero-wallet-rpc directory"); -// tracing::info!(path=%format!("{}/wallet", data_dir.display()), "Internal bitcoin wallet directory"); + // tracing::info!(path=%data_dir.display(), "Data directory"); + // tracing::info!(path=%format!("{}/logs", data_dir.display()), + // "Log files directory"); + // tracing::info!(path=%format!("{}/sqlite", data_dir.display()), "Sqlite file + // location"); + // tracing::info!(path=%format!("{}/seed.pem", data_dir.display()), "Seed file + // location"); + // tracing::info!(path=%format!("{}/monero", data_dir.display()), + // "Monero-wallet-rpc directory"); + // tracing::info!(path=%format!("{}/wallet", data_dir.display()), "Internal + // bitcoin wallet directory"); json!({ "result": [] @@ -200,7 +210,9 @@ impl Request { .await?; let signed_tx = bitcoin_wallet.sign_and_finalize(psbt).await?; - bitcoin_wallet.broadcast(signed_tx.clone(), "withdraw").await?; + bitcoin_wallet + .broadcast(signed_tx.clone(), "withdraw") + .await?; json!({ "signed_tx": signed_tx, @@ -220,9 +232,7 @@ impl Request { Some(handle) } }; - loop { - - } + loop {} json!({ "result": [] }) @@ -255,8 +265,12 @@ impl Request { Arc::clone(context.bitcoin_wallet.as_ref().unwrap()), (seed.clone(), context.namespace), ); - let mut swarm = - swarm::cli(seed.clone(), context.tor_socks5_port.clone().unwrap(), behaviour).await?; + let mut swarm = swarm::cli( + seed.clone(), + context.tor_socks5_port.clone().unwrap(), + behaviour, + ) + .await?; let our_peer_id = swarm.local_peer_id(); tracing::debug!(peer_id = %our_peer_id, "Network layer initialized"); @@ -267,7 +281,8 @@ impl Request { .add_address(seller_peer_id, seller_address); } - let (event_loop, event_loop_handle) = EventLoop::new(swap_id, swarm, seller_peer_id)?; + let (event_loop, event_loop_handle) = + EventLoop::new(swap_id, swarm, seller_peer_id)?; let handle = tokio::spawn(event_loop.run()); let monero_receive_address = context.db.get_monero_address(swap_id).await?; @@ -297,8 +312,13 @@ impl Request { Command::Cancel => { let bitcoin_wallet = context.bitcoin_wallet.as_ref().unwrap(); - let (txid, _) = cli::cancel(self.params.swap_id.unwrap(), Arc::clone(bitcoin_wallet), Arc::clone(&context.db)).await?; - + let (txid, _) = cli::cancel( + self.params.swap_id.unwrap(), + Arc::clone(bitcoin_wallet), + Arc::clone(&context.db), + ) + .await?; + tracing::debug!("Cancel transaction successfully published with id {}", txid); json!({ @@ -308,11 +328,14 @@ impl Request { Command::Refund => { let bitcoin_wallet = context.bitcoin_wallet.as_ref().unwrap(); - let state = cli::refund(self.params.swap_id.unwrap(), Arc::clone(bitcoin_wallet), Arc::clone(&context.db)).await?; + let state = cli::refund( + self.params.swap_id.unwrap(), + Arc::clone(bitcoin_wallet), + Arc::clone(&context.db), + ) + .await?; - json!({ - "result": state - }) + json!({ "result": state }) } Command::ListSellers => { let rendezvous_point = self.params.rendezvous_point.clone().unwrap(); @@ -328,7 +351,8 @@ impl Request { context.namespace, context.tor_socks5_port.unwrap(), identity, - ).await?; + ) + .await?; for seller in &sellers { match seller.status { @@ -352,9 +376,7 @@ impl Request { } } - json!({ - "sellers": sellers - }) + json!({ "sellers": sellers }) } Command::ExportBitcoinWallet => { let bitcoin_wallet = context.bitcoin_wallet.as_ref().unwrap(); @@ -366,7 +388,11 @@ impl Request { }) } Command::MoneroRecovery => { - let swap_state: BobState = context.db.get_state(self.params.swap_id.clone().unwrap()).await?.try_into()?; + let swap_state: BobState = context + .db + .get_state(self.params.swap_id.clone().unwrap()) + .await? + .try_into()?; match swap_state { BobState::Started { .. } @@ -411,81 +437,77 @@ impl Request { impl Context { pub async fn build( bitcoin: Option, - monero: Option, - tor: Option, - data: Option, + monero: Option, + tor: Option, + data: Option, is_testnet: bool, debug: bool, json: bool, server_address: Option, - ) -> Result { - let data_dir = data::data_dir_from(data, is_testnet)?; - let env_config = env_config_from(is_testnet); + ) -> Result { + let data_dir = data::data_dir_from(data, is_testnet)?; + let env_config = env_config_from(is_testnet); - let seed = Seed::from_file_or_generate(data_dir.as_path()) - .context("Failed to read seed in file")?; + let seed = Seed::from_file_or_generate(data_dir.as_path()) + .context("Failed to read seed in file")?; - let bitcoin_wallet = { - if let Some(bitcoin) = bitcoin { - let (bitcoin_electrum_rpc_url, bitcoin_target_block) = - bitcoin.apply_defaults(is_testnet)?; - Some(Arc::new(init_bitcoin_wallet( + let bitcoin_wallet = { + if let Some(bitcoin) = bitcoin { + let (bitcoin_electrum_rpc_url, bitcoin_target_block) = + bitcoin.apply_defaults(is_testnet)?; + Some(Arc::new( + init_bitcoin_wallet( bitcoin_electrum_rpc_url, &seed, data_dir.clone(), env_config, bitcoin_target_block, - ) - .await?)) - } else { - None - } - }; + ) + .await?, + )) + } else { + None + } + }; - let (monero_wallet, monero_rpc_process) = { - if let Some(monero) = monero { - let monero_daemon_address = monero.apply_defaults(is_testnet); - let (wlt, prc) = init_monero_wallet( - data_dir.clone(), - monero_daemon_address, - env_config, - ).await?; - (Some(Arc::new(wlt)), Some(prc)) - } else { - (None, None) - } - }; + let (monero_wallet, monero_rpc_process) = { + if let Some(monero) = monero { + let monero_daemon_address = monero.apply_defaults(is_testnet); + let (wlt, prc) = + init_monero_wallet(data_dir.clone(), monero_daemon_address, env_config).await?; + (Some(Arc::new(wlt)), Some(prc)) + } else { + (None, None) + } + }; + let tor_socks5_port = { + if let Some(tor) = tor { + Some(tor.tor_socks5_port) + } else { + None + } + }; - let tor_socks5_port = { - if let Some(tor) = tor { - Some(tor.tor_socks5_port) - } else { - None - } - }; + cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - cli::tracing::init(debug, json, data_dir.join("logs"), None)?; + let init = Context { + bitcoin_wallet, + monero_wallet, + monero_rpc_process, + tor_socks5_port, + namespace: XmrBtcNamespace::from_is_testnet(is_testnet), + db: open_db(data_dir.join("sqlite")).await?, + env_config, + seed: Some(seed), + debug, + json, + is_testnet, + server_address, + }; - let init = Context { - bitcoin_wallet, - monero_wallet, - monero_rpc_process, - tor_socks5_port: tor_socks5_port, - namespace: XmrBtcNamespace::from_is_testnet(is_testnet), - db: open_db(data_dir.join("sqlite")).await?, - env_config, - seed: Some(seed), - debug, - json, - is_testnet, - server_address, - }; - - - Ok(init) + Ok(init) } - } impl Serialize for Context { @@ -503,17 +525,17 @@ impl Serialize for Context { impl PartialEq for Context { fn eq(&self, other: &Self) -> bool { - self.tor_socks5_port == other.tor_socks5_port && - self.namespace == other.namespace && - self.debug == other.debug && - self.json == other.json && - self.server_address == other.server_address + self.tor_socks5_port == other.tor_socks5_port + && self.namespace == other.namespace + && self.debug == other.debug + && self.json == other.json + && self.server_address == other.server_address } } impl fmt::Debug for Context { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Testing {}", true) + write!(f, "Testing {}", true) } } @@ -709,26 +731,34 @@ pub mod api_test { pub const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b"; impl Context { - - pub async fn default(is_testnet: bool, data_dir: PathBuf, json: bool, debug: bool) -> Result { - + pub async fn default( + is_testnet: bool, + data_dir: PathBuf, + json: bool, + debug: bool, + ) -> Result { Ok(Context::build( - Some(Bitcoin { bitcoin_electrum_rpc_url: None, bitcoin_target_block: None}), - Some(Monero { monero_daemon_address: None }), - Some(Tor { tor_socks5_port: DEFAULT_SOCKS5_PORT }), + Some(Bitcoin { + bitcoin_electrum_rpc_url: None, + bitcoin_target_block: None, + }), + Some(Monero { + monero_daemon_address: None, + }), + Some(Tor { + tor_socks5_port: DEFAULT_SOCKS5_PORT, + }), Some(data_dir), is_testnet, debug, json, - None - ).await?) + None, + ) + .await?) } - } impl Request { - pub fn buy_xmr(is_testnet: bool) -> Request { - let seller = Multiaddr::from_str(MULTI_ADDRESS).unwrap(); let bitcoin_change_address = { if is_testnet { @@ -752,9 +782,8 @@ pub mod api_test { bitcoin_change_address: Some(bitcoin_change_address), monero_receive_address: Some(monero_receive_address), ..Default::default() - }, - cmd: Command::BuyXmr + cmd: Command::BuyXmr, } } @@ -763,9 +792,8 @@ pub mod api_test { params: Params { swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()), ..Default::default() - }, - cmd: Command::Resume + cmd: Command::Resume, } } @@ -774,9 +802,8 @@ pub mod api_test { params: Params { swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()), ..Default::default() - }, - cmd: Command::Cancel + cmd: Command::Cancel, } } @@ -785,9 +812,8 @@ pub mod api_test { params: Params { swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()), ..Default::default() - }, - cmd: Command::Refund + cmd: Command::Refund, } } } diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index 4e8f6027..8be7cbae 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -14,9 +14,9 @@ use anyhow::Result; use std::env; +use std::sync::Arc; use swap::cli::command::{parse_args_and_apply_defaults, ParseResult}; use swap::common::check_latest_version; -use std::sync::Arc; #[tokio::main] async fn main() -> Result<()> { @@ -36,18 +36,16 @@ async fn main() -> Result<()> { Ok(()) } - - #[cfg(test)] mod tests { use super::*; - use swap::api::determine_btc_to_swap; use ::bitcoin::Amount; use std::sync::Mutex; + use std::time::Duration; + use swap::api::determine_btc_to_swap; + use swap::network::quote::BidQuote; use swap::tracing_ext::capture_logs; use tracing::level_filters::LevelFilter; - use swap::network::quote::BidQuote; - use std::time::Duration; #[tokio::test] async fn given_no_balance_and_transfers_less_than_max_swaps_max_giveable() { diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index 9ccd800f..7c9d3c91 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -1,19 +1,19 @@ +use crate::api::{Context, Params, Request}; use crate::bitcoin::Amount; +use crate::fs::system_data_dir; use crate::{env, monero}; -use crate::api::{Request, Params, Context}; use anyhow::{bail, Context as AnyContext, Result}; use bitcoin::{Address, AddressType}; use libp2p::core::Multiaddr; use serde::Serialize; use std::ffi::OsString; +use std::net::SocketAddr; use std::path::PathBuf; use std::str::FromStr; +use std::sync::Arc; use structopt::{clap, StructOpt}; use url::Url; use uuid::Uuid; -use std::net::SocketAddr; -use std::sync::Arc; -use crate::fs::system_data_dir; // See: https://moneroworld.com/ pub const DEFAULT_MONERO_DAEMON_ADDRESS: &str = "node.community.rino.io:18081"; @@ -79,8 +79,9 @@ where is_testnet, debug, json, - None - ).await?; + None, + ) + .await?; let request = Request { params: Params { bitcoin_change_address: Some(bitcoin_change_address), @@ -93,44 +94,26 @@ where (context, request) } RawCommand::History => { - let context = Context::build( - None, - None, - None, - data, - is_testnet, - debug, - json, - None - ).await?; + let context = + Context::build(None, None, None, data, is_testnet, debug, json, None).await?; let request = Request { params: Params::default(), cmd: Command::History, }; (context, request) - }, + } RawCommand::Config => { - let context = Context::build( - None, - None, - None, - data, - is_testnet, - debug, - json, - None - ).await?; + let context = + Context::build(None, None, None, data, is_testnet, debug, json, None).await?; let request = Request { params: Params::default(), cmd: Command::Config, }; (context, request) - }, - RawCommand::Balance { - bitcoin, - } => { + } + RawCommand::Balance { bitcoin } => { let context = Context::build( Some(bitcoin), None, @@ -139,8 +122,9 @@ where is_testnet, debug, json, - None - ).await?; + None, + ) + .await?; let request = Request { params: Params::default(), cmd: Command::Balance, @@ -162,7 +146,8 @@ where debug, json, server_address, - ).await?; + ) + .await?; let request = Request { params: Params::default(), cmd: Command::StartDaemon, @@ -182,11 +167,12 @@ where is_testnet, debug, json, - None - ).await?; + None, + ) + .await?; let request = Request { params: Params { - amount: amount, + amount, address: Some(address), ..Default::default() }, @@ -209,7 +195,8 @@ where debug, json, None, - ).await?; + ) + .await?; let request = Request { params: Params { swap_id: Some(swap_id), @@ -231,8 +218,9 @@ where is_testnet, debug, json, - None - ).await?; + None, + ) + .await?; let request = Request { params: Params { swap_id: Some(swap_id), @@ -254,8 +242,9 @@ where is_testnet, debug, json, - None - ).await?; + None, + ) + .await?; let request = Request { params: Params { swap_id: Some(swap_id), @@ -269,16 +258,8 @@ where rendezvous_point, tor, } => { - let context = Context::build( - None, - None, - Some(tor), - data, - is_testnet, - debug, - json, - None - ).await?; + let context = + Context::build(None, None, Some(tor), data, is_testnet, debug, json, None).await?; let request = Request { params: Params { @@ -298,25 +279,18 @@ where is_testnet, debug, json, - None - ).await?; + None, + ) + .await?; let request = Request { params: Params::default(), cmd: Command::ExportBitcoinWallet, }; (context, request) - }, + } RawCommand::MoneroRecovery { swap_id } => { - let context = Context::build( - None, - None, - None, - data, - is_testnet, - debug, - json, - None - ).await?; + let context = + Context::build(None, None, None, data, is_testnet, debug, json, None).await?; let request = Request { params: Params { @@ -326,7 +300,7 @@ where cmd: Command::MoneroRecovery, }; (context, request) - }, + } }; Ok(ParseResult::Context(Arc::new(context), Box::new(request))) @@ -347,7 +321,6 @@ pub enum Command { StartDaemon, } - #[derive(structopt::StructOpt, Debug)] #[structopt( name = "swap", @@ -435,15 +408,18 @@ enum RawCommand { #[structopt(flatten)] bitcoin: Bitcoin, }, - #[structopt(about="Starts a JSON-RPC server")] + #[structopt(about = "Starts a JSON-RPC server")] StartDaemon { #[structopt(flatten)] bitcoin: Bitcoin, #[structopt(flatten)] monero: Monero, - - #[structopt(long="server-address", help = "The socket address the server should use")] + + #[structopt( + long = "server-address", + help = "The socket address the server should use" + )] server_address: Option, #[structopt(flatten)] tor: Tor, @@ -588,7 +564,6 @@ struct Seller { seller: Multiaddr, } - fn bitcoin_address(address: Address, is_testnet: bool) -> Result
{ let network = if is_testnet { bitcoin::Network::Testnet @@ -699,12 +674,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -729,12 +708,16 @@ mod tests { let (is_testnet, debug, json) = (true, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -798,12 +781,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -818,12 +805,16 @@ mod tests { let (is_testnet, debug, json) = (true, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -839,12 +830,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::cancel()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::cancel(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -859,12 +854,16 @@ mod tests { let (is_testnet, debug, json) = (true, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::cancel()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::cancel(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -879,12 +878,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::refund()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::refund(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -899,12 +902,16 @@ mod tests { let (is_testnet, debug, json) = (true, false, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::refund()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::refund(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -932,12 +939,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, false); let data_dir = PathBuf::from_str(args_data_dir).unwrap(); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir.clone(), debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir.clone(), debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -960,12 +971,16 @@ mod tests { let args = parse_args_and_apply_defaults(raw_ars).await.unwrap(); let (is_testnet, debug, json) = (true, false, false); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir.clone(), debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir.clone(), debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -983,12 +998,16 @@ mod tests { let args = parse_args_and_apply_defaults(raw_ars).await.unwrap(); let (is_testnet, debug, json) = (false, false, false); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir.clone(), debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir.clone(), debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1003,18 +1022,20 @@ mod tests { "--swap-id", SWAP_ID, ]; - - let args = parse_args_and_apply_defaults(raw_ars).await.unwrap(); let (is_testnet, debug, json) = (true, false, false); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir.clone(), debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir.clone(), debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1039,12 +1060,16 @@ mod tests { let (is_testnet, debug, json) = (false, true, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1067,12 +1092,16 @@ mod tests { let (is_testnet, debug, json) = (true, true, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1084,12 +1113,16 @@ mod tests { let (is_testnet, debug, json) = (false, true, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1108,12 +1141,16 @@ mod tests { let (is_testnet, debug, json) = (true, true, false); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1138,12 +1175,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, true); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1166,12 +1207,16 @@ mod tests { let (is_testnet, debug, json) = (true, false, true); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::buy_xmr(is_testnet)); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::buy_xmr(is_testnet), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1182,12 +1227,16 @@ mod tests { let (is_testnet, debug, json) = (false, false, true); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1206,12 +1255,16 @@ mod tests { let (is_testnet, debug, json) = (true, false, true); let data_dir = data_dir_path_cli(is_testnet); - let (expected_context, expected_request) = - (Context::default(is_testnet, data_dir, debug, json).await.unwrap(), Request::resume()); + let (expected_context, expected_request) = ( + Context::default(is_testnet, data_dir, debug, json) + .await + .unwrap(), + Request::resume(), + ); let (actual_context, actual_request) = match args { ParseResult::Context(context, request) => (context, request), - _ => panic!("Couldn't parse result") + _ => panic!("Couldn't parse result"), }; assert_eq!(actual_context, Arc::new(expected_context)); @@ -1266,7 +1319,7 @@ mod tests { MULTI_ADDRESS, ]; let result = parse_args_and_apply_defaults(raw_ars).await.unwrap(); - //assert!(matches!(result, ParseResult::Arguments(_))); + // assert!(matches!(result, ParseResult::Arguments(_))); assert!(true); } @@ -1318,7 +1371,7 @@ mod tests { MULTI_ADDRESS, ]; let result = parse_args_and_apply_defaults(raw_ars).await.unwrap(); - //assert!(matches!(result, ParseResult::Arguments(_))); + // assert!(matches!(result, ParseResult::Arguments(_))); assert!(true); } diff --git a/swap/src/database/sqlite.rs b/swap/src/database/sqlite.rs index d39e3f87..45e56fc2 100644 --- a/swap/src/database/sqlite.rs +++ b/swap/src/database/sqlite.rs @@ -6,6 +6,7 @@ use async_trait::async_trait; use libp2p::{Multiaddr, PeerId}; use sqlx::sqlite::Sqlite; use sqlx::{Pool, SqlitePool}; +use std::collections::HashMap; use std::path::Path; use std::str::FromStr; use time::OffsetDateTime; @@ -149,7 +150,7 @@ impl Database for SqliteDatabase { let rows = sqlx::query!( r#" - SELECT address + SELECT DISTINCT address FROM peer_addresses WHERE peer_id = ? "#, @@ -169,6 +170,24 @@ impl Database for SqliteDatabase { addresses } + async fn get_swap_start_date(&self, swap_id: Uuid) -> Result { + let mut conn = self.pool.acquire().await?; + let swap_id = swap_id.to_string(); + + let row = sqlx::query!( + r#" + SELECT min(entered_at) as start_date + FROM swap_states + WHERE swap_id = ? + "#, + swap_id + ) + .fetch_one(&mut conn) + .await?; + + return Ok(row.start_date.unwrap()); + } + async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()> { let mut conn = self.pool.acquire().await?; let entered_at = OffsetDateTime::now_utc(); @@ -249,6 +268,33 @@ impl Database for SqliteDatabase { result } + + async fn raw_all(&self) -> Result>> { + let mut conn = self.pool.acquire().await?; + let rows = sqlx::query!( + r#" + SELECT swap_id, state + FROM swap_states + "# + ) + .fetch_all(&mut conn) + .await?; + + let mut swaps: HashMap> = HashMap::new(); + + for row in &rows { + let swap_id = Uuid::from_str(&row.swap_id).unwrap(); + let state = serde_json::from_str(&row.state).unwrap(); + + if swaps.contains_key(&swap_id) { + swaps.get_mut(&swap_id).unwrap().push(state); + } else { + swaps.insert(swap_id, vec![state]); + } + } + + Ok(swaps) + } } #[cfg(test)] diff --git a/swap/src/lib.rs b/swap/src/lib.rs index c04c7059..84a39dcc 100644 --- a/swap/src/lib.rs +++ b/swap/src/lib.rs @@ -16,6 +16,7 @@ missing_copy_implementations )] +pub mod api; pub mod asb; pub mod bitcoin; pub mod cli; @@ -24,7 +25,6 @@ pub mod database; pub mod env; pub mod fs; pub mod kraken; -pub mod api; pub mod libp2p_ext; pub mod monero; pub mod network; diff --git a/swap/src/protocol.rs b/swap/src/protocol.rs index 5bf1f5b4..70ffd648 100644 --- a/swap/src/protocol.rs +++ b/swap/src/protocol.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; use sha2::Sha256; use sigma_fun::ext::dl_secp256k1_ed25519_eq::{CrossCurveDLEQ, CrossCurveDLEQProof}; use sigma_fun::HashTranscript; +use std::collections::HashMap; use std::convert::TryInto; use uuid::Uuid; @@ -139,7 +140,9 @@ pub trait Database { async fn get_monero_address(&self, swap_id: Uuid) -> Result; async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()>; async fn get_addresses(&self, peer_id: PeerId) -> Result>; + async fn get_swap_start_date(&self, swap_id: Uuid) -> Result; async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()>; async fn get_state(&self, swap_id: Uuid) -> Result; async fn all(&self) -> Result>; + async fn raw_all(&self) -> Result>>; } diff --git a/swap/src/rpc.rs b/swap/src/rpc.rs index 4983afd4..d512a458 100644 --- a/swap/src/rpc.rs +++ b/swap/src/rpc.rs @@ -1,8 +1,8 @@ +use crate::api::Context; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}; use std::net::SocketAddr; -use jsonrpsee::http_server::{RpcModule, HttpServerBuilder, HttpServerHandle}; -use thiserror::Error; -use crate::api::{Context}; use std::sync::Arc; +use thiserror::Error; pub mod methods; @@ -12,18 +12,21 @@ pub enum Error { ExampleError, } -pub async fn run_server(server_address: SocketAddr, context: Arc) -> anyhow::Result<(SocketAddr, HttpServerHandle)> { - let server = HttpServerBuilder::default().build(server_address).await?; +pub async fn run_server( + server_address: SocketAddr, + context: Arc, +) -> anyhow::Result<(SocketAddr, HttpServerHandle)> { + let server = HttpServerBuilder::default().build(server_address).await?; let mut modules = RpcModule::new(()); { - modules.merge(methods::register_modules(Arc::clone(&context))) + modules + .merge(methods::register_modules(Arc::clone(&context))) .unwrap() } - let addr = server.local_addr()?; - let server_handle = server.start(modules)?; + let addr = server.local_addr()?; + let server_handle = server.start(modules)?; tracing::info!(%addr, "Started RPC server"); - - Ok((addr, server_handle)) -} + Ok((addr, server_handle)) +} diff --git a/swap/src/rpc/methods.rs b/swap/src/rpc/methods.rs index 2737275f..10d57693 100644 --- a/swap/src/rpc/methods.rs +++ b/swap/src/rpc/methods.rs @@ -1,66 +1,164 @@ -use jsonrpsee::http_server::{RpcModule}; -use crate::api::{Request, Context, Params}; -use crate::cli::command::{Command}; -use std::str::FromStr; +use crate::api::{Context, Params, Request}; +use crate::cli::command::Command; use crate::rpc::Error; use crate::{bitcoin, monero}; +use jsonrpsee::http_server::RpcModule; +use libp2p::core::Multiaddr; +use serde_json::json; +use std::collections::HashMap; +use std::str::FromStr; use std::sync::Arc; use uuid::Uuid; -use std::collections::HashMap; -use libp2p::core::Multiaddr; pub fn register_modules(context: Arc) -> RpcModule> { let mut module = RpcModule::new(context); module .register_async_method("get_bitcoin_balance", |_, context| async move { - get_bitcoin_balance(&context).await.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) - }, - ) + get_bitcoin_balance(&context) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) .unwrap(); module .register_async_method("get_history", |_, context| async move { - get_history(&context).await.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) - }, - ) + get_history(&context) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + module + .register_async_method("raw_get_history", |_, context| async move { + context + .db + .raw_all() + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + module + .register_async_method("get_seller", |params, context| async move { + let params: HashMap = params.parse()?; + let swap_id = Uuid::from_str(params.get("swap_id").ok_or_else(|| { + jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()) + })?) + .unwrap(); + let peerId = context + .db + .get_peer_id(swap_id) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + .unwrap(); + let addresses = context + .db + .get_addresses(peerId) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + .unwrap(); + Ok(json!({ + "peerId": peerId.to_base58(), + "addresses": addresses + })) + }) + .unwrap(); + module + .register_async_method("get_swap_start_date", |params, context| async move { + let params: HashMap = params.parse()?; + let swap_id = Uuid::from_str(params.get("swap_id").ok_or_else(|| { + jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()) + })?) + .unwrap(); + let start_date = context + .db + .get_swap_start_date(swap_id) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + .unwrap(); + + Ok(json!({ + "start_date": start_date, + })) + }) .unwrap(); module .register_async_method("resume_swap", |params, context| async move { - let swap_id: HashMap = params.parse()?; - let swap_id = Uuid::from_str(swap_id.get("swap_id").ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()))?).unwrap(); - resume_swap(swap_id, &context).await.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) - }, - ) + let params: HashMap = params.parse()?; + let swap_id = Uuid::from_str(params.get("swap_id").ok_or_else(|| { + jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()) + })?) + .unwrap(); + resume_swap(swap_id, &context) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) .unwrap(); module .register_async_method("withdraw_btc", |params, context| async move { - let map_params: HashMap = params.parse()?; - let amount = if let Some(amount_str) = map_params.get("amount") { - Some(::bitcoin::Amount::from_str_in(amount_str, ::bitcoin::Denomination::Bitcoin).map_err(|_| jsonrpsee_core::Error::Custom("Unable to parse amount".to_string()))?) + let params: HashMap = params.parse()?; + let amount = if let Some(amount_str) = params.get("amount") { + Some( + ::bitcoin::Amount::from_str_in(amount_str, ::bitcoin::Denomination::Bitcoin) + .map_err(|_| { + jsonrpsee_core::Error::Custom("Unable to parse amount".to_string()) + })?, + ) } else { None }; - let withdraw_address = bitcoin::Address::from_str(map_params.get("address").ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain address".to_string()))?).unwrap(); - withdraw_btc(withdraw_address, amount, &context).await.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) - }, - ) + let withdraw_address = + bitcoin::Address::from_str(params.get("address").ok_or_else(|| { + jsonrpsee_core::Error::Custom("Does not contain address".to_string()) + })?) + .unwrap(); + withdraw_btc(withdraw_address, amount, &context) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) .unwrap(); module .register_async_method("buy_xmr", |params, context| async move { - let map_params: HashMap = params.parse()?; - let bitcoin_change_address = bitcoin::Address::from_str(map_params.get("bitcoin_change_address").ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain bitcoin_change_address".to_string()))?).unwrap(); - let monero_receive_address = monero::Address::from_str(map_params.get("monero_receive_address").ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain monero_receiveaddress".to_string()))?).unwrap(); - let seller = Multiaddr::from_str(map_params.get("seller").ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain seller".to_string()))?).unwrap(); - buy_xmr(bitcoin_change_address, monero_receive_address, seller, &context).await.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) - }, - ) + let params: HashMap = params.parse()?; + let bitcoin_change_address = bitcoin::Address::from_str( + params.get("bitcoin_change_address").ok_or_else(|| { + jsonrpsee_core::Error::Custom( + "Does not contain bitcoin_change_address".to_string(), + ) + })?, + ) + .unwrap(); + let monero_receive_address = monero::Address::from_str( + params.get("monero_receive_address").ok_or_else(|| { + jsonrpsee_core::Error::Custom( + "Does not contain monero_receiveaddress".to_string(), + ) + })?, + ) + .unwrap(); + let seller = Multiaddr::from_str(params.get("seller").ok_or_else(|| { + jsonrpsee_core::Error::Custom("Does not contain seller".to_string()) + })?) + .unwrap(); + buy_xmr( + bitcoin_change_address, + monero_receive_address, + seller, + &context, + ) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) .unwrap(); module .register_async_method("list_sellers", |params, context| async move { - let map_params: HashMap = params.parse()?; - let rendezvous_point = Multiaddr::from_str(map_params.get("rendezvous_point").ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain rendezvous_point".to_string()))?).unwrap(); - list_sellers(rendezvous_point, &context).await.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) - }, - ) + let params: HashMap = params.parse()?; + let rendezvous_point = + Multiaddr::from_str(params.get("rendezvous_point").ok_or_else(|| { + jsonrpsee_core::Error::Custom("Does not contain rendezvous_point".to_string()) + })?) + .unwrap(); + list_sellers(rendezvous_point, &context) + .await + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) .unwrap(); module } @@ -83,7 +181,10 @@ async fn get_history(context: &Arc) -> anyhow::Result) -> anyhow::Result { +async fn resume_swap( + swap_id: Uuid, + context: &Arc, +) -> anyhow::Result { let request = Request { params: Params { swap_id: Some(swap_id), @@ -95,10 +196,14 @@ async fn resume_swap(swap_id: Uuid, context: &Arc) -> anyhow::Result, context: &Arc) -> anyhow::Result { +async fn withdraw_btc( + withdraw_address: bitcoin::Address, + amount: Option, + context: &Arc, +) -> anyhow::Result { let request = Request { params: Params { - amount: amount, + amount, address: Some(withdraw_address), ..Default::default() }, @@ -108,7 +213,12 @@ async fn withdraw_btc(withdraw_address: bitcoin::Address, amount: Option) -> anyhow::Result { +async fn buy_xmr( + bitcoin_change_address: bitcoin::Address, + monero_receive_address: monero::Address, + seller: Multiaddr, + context: &Arc, +) -> anyhow::Result { let request = Request { params: Params { bitcoin_change_address: Some(bitcoin_change_address), @@ -122,7 +232,10 @@ async fn buy_xmr(bitcoin_change_address: bitcoin::Address, monero_receive_addres Ok(swap) } -async fn list_sellers(rendezvous_point: Multiaddr, context: &Arc) -> anyhow::Result { +async fn list_sellers( + rendezvous_point: Multiaddr, + context: &Arc, +) -> anyhow::Result { let request = Request { params: Params { rendezvous_point: Some(rendezvous_point),