diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b48c8628..0806f58b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -262,29 +262,6 @@ jobs: - name: Run test ${{ matrix.test_name }} run: cargo test --package swap --test ${{ matrix.test_name }} -- --nocapture - rpc_tests: - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4.1.7 - - - uses: Swatinem/rust-cache@v2.7.3 - - - name: Install dependencies required by Tauri v2 (ubuntu only) - run: | - sudo apt update - sudo apt install libwebkit2gtk-4.1-dev \ - build-essential \ - curl \ - wget \ - file \ - libxdo-dev \ - libssl-dev \ - libayatana-appindicator3-dev \ - librsvg2-dev - - - name: Run RPC server tests - run: cargo test --package swap --test rpc -- --nocapture check_stable: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 879628aa..79c661b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs: Document `external_bitcoin_address` option for using a specific Bitcoin address when redeeming or punishing swaps. +- Removed the JSON-RPC daemon and the `start-daemon` CLI command. ## [1.1.1] - 2025-05-20 diff --git a/Cargo.lock b/Cargo.lock index 15937490..2477ac52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,7 +428,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.0", + "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -494,7 +494,7 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ - "async-lock 3.4.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", @@ -507,22 +507,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.4.0", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -535,12 +526,12 @@ checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" dependencies = [ "async-channel", "async-io", - "async-lock 3.4.0", + "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", + "event-listener", "futures-lite", "rustix 0.38.44", "tracing", @@ -564,7 +555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ "async-io", - "async-lock 3.4.0", + "async-lock", "atomic-waker", "cfg-if", "futures-core", @@ -892,15 +883,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - [[package]] name = "big-bytes" version = "1.0.0" @@ -2844,12 +2826,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.0" @@ -2867,7 +2843,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener", "pin-project-lite", ] @@ -3585,19 +3561,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" -[[package]] -name = "globset" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - [[package]] name = "gobject-sys" version = "0.18.0" @@ -4632,115 +4595,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "jsonrpsee" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "367a292944c07385839818bb71c8d76611138e2dedb0677d035b8da21d29c78b" -dependencies = [ - "jsonrpsee-core", - "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-ws-client", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8b3815d9f5d5de348e5f162b316dc9cdf4548305ebb15b4eb9328e66cf27d7a" -dependencies = [ - "futures-util", - "http 0.2.12", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "rustls-native-certs 0.6.3", - "soketto", - "thiserror 1.0.69", - "tokio", - "tokio-rustls 0.24.1", - "tokio-util", - "tracing", - "webpki-roots 0.25.4", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5dde66c53d6dcdc8caea1874a45632ec0fcf5b437789f1e45766a1512ce803" -dependencies = [ - "anyhow", - "arrayvec", - "async-lock 2.8.0", - "async-trait", - "beef", - "futures-channel", - "futures-timer", - "futures-util", - "globset", - "hyper 0.14.32", - "jsonrpsee-types", - "parking_lot 0.12.3", - "rand 0.8.5", - "rustc-hash 1.1.0", - "serde", - "serde_json", - "soketto", - "thiserror 1.0.69", - "tokio", - "tracing", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4d945a6008c9b03db3354fb3c83ee02d2faa9f2e755ec1dfb69c3551b8f4ba" -dependencies = [ - "futures-channel", - "futures-util", - "http 0.2.12", - "hyper 0.14.32", - "jsonrpsee-core", - "jsonrpsee-types", - "serde", - "serde_json", - "soketto", - "tokio", - "tokio-stream", - "tokio-util", - "tower 0.4.13", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245ba8e5aa633dd1c1e4fae72bce06e71f42d34c14a2767c6b4d173b57bee5e5" -dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror 1.0.69", - "tracing", -] - -[[package]] -name = "jsonrpsee-ws-client" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1b3975ed5d73f456478681a417128597acd6a2487855fdb7b4a3d4d195bf5e" -dependencies = [ - "http 0.2.12", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", -] - [[package]] name = "k12" version = "0.3.0" @@ -5681,11 +5535,11 @@ version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ - "async-lock 3.4.0", + "async-lock", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "event-listener 5.4.0", + "event-listener", "futures-util", "loom", "parking_lot 0.12.3", @@ -7279,7 +7133,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash", "rustls 0.23.27", "socket2", "thiserror 2.0.12", @@ -7299,7 +7153,7 @@ dependencies = [ "lru-slab", "rand 0.9.1", "ring 0.17.14", - "rustc-hash 2.1.1", + "rustc-hash", "rustls 0.23.27", "rustls-pki-types", "slab", @@ -7630,7 +7484,7 @@ dependencies = [ "quinn", "rustls 0.23.27", "rustls-native-certs 0.8.1", - "rustls-pemfile 2.2.0", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -7817,12 +7671,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -7943,18 +7791,6 @@ dependencies = [ "security-framework 2.11.1", ] -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile 1.0.4", - "schannel", - "security-framework 2.11.1", -] - [[package]] name = "rustls-native-certs" version = "0.8.1" @@ -7967,15 +7803,6 @@ dependencies = [ "security-framework 3.2.0", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -8873,22 +8700,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "soketto" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" -dependencies = [ - "base64 0.13.1", - "bytes", - "futures", - "http 0.2.12", - "httparse", - "log", - "rand 0.8.5", - "sha-1", -] - [[package]] name = "soup3" version = "0.5.0" @@ -8964,7 +8775,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.0", + "event-listener", "futures-core", "futures-intrusive", "futures-io", @@ -9351,8 +9162,6 @@ dependencies = [ "futures", "get-port", "hex", - "jsonrpsee", - "jsonrpsee-core", "libp2p", "libp2p-community-tor", "mockito", @@ -10209,16 +10018,6 @@ dependencies = [ "webpki 0.21.4", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.2" @@ -10653,7 +10452,7 @@ dependencies = [ "derive_more 1.0.0", "digest 0.10.7", "educe", - "event-listener 5.4.0", + "event-listener", "fs-mistrust", "fslock", "futures", @@ -13303,14 +13102,14 @@ dependencies = [ "async-broadcast", "async-executor", "async-io", - "async-lock 3.4.0", + "async-lock", "async-process", "async-recursion", "async-task", "async-trait", "blocking", "enumflags2", - "event-listener 5.4.0", + "event-listener", "futures-core", "futures-lite", "hex", diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 960a3339..e545abf4 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -43,8 +43,6 @@ ecdsa_fun = { version = "0.10", default-features = false, features = [ ed25519-dalek = "1" futures = { version = "0.3", default-features = false } hex = "0.4" -jsonrpsee = { version = "0.16.2", features = [ "server" ] } -jsonrpsee-core = "0.16.2" libp2p = { version = "0.53.2", features = [ "tcp", "yamux", "dns", "noise", "request-response", "ping", "rendezvous", "identify", "macros", "cbor", "json", "tokio", "serde", "rsa" ] } libp2p-community-tor = { git = "https://github.com/umgefahren/libp2p-tor", branch = "main", features = [ "listen-onion-service" ] } moka = { version = "0.12", features = [ "sync", "future" ] } @@ -129,7 +127,6 @@ zip = "0.5" [dev-dependencies] bitcoin-harness = { git = "https://github.com/UnstoppableSwap/bitcoin-harness-rs", branch = "master" } get-port = "3" -jsonrpsee = { version = "0.16.2", features = [ "ws-client" ] } mockito = "1.4" monero-harness = { path = "../monero-harness" } proptest = "1" diff --git a/swap/src/cli/api/request.rs b/swap/src/cli/api/request.rs index beafb2c8..eb15cb84 100644 --- a/swap/src/cli/api/request.rs +++ b/swap/src/cli/api/request.rs @@ -10,7 +10,7 @@ use crate::network::quote::{BidQuote, ZeroQuoteReceived}; use crate::network::swarm; use crate::protocol::bob::{BobState, Swap}; use crate::protocol::{bob, State}; -use crate::{bitcoin, cli, monero, rpc}; +use crate::{bitcoin, cli, monero}; use ::bitcoin::address::NetworkUnchecked; use ::bitcoin::Txid; use ::monero::Network; @@ -25,7 +25,6 @@ use serde_json::json; use std::cmp::min; use std::convert::TryInto; use std::future::Future; -use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; @@ -191,22 +190,6 @@ impl Request for ListSellersArgs { } } -// StartDaemon -#[typeshare] -#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct StartDaemonArgs { - #[typeshare(serialized_as = "string")] - pub server_address: Option, -} - -impl Request for StartDaemonArgs { - type Response = serde_json::Value; - - async fn request(self, ctx: Arc) -> Result { - start_daemon(self, (*ctx).clone()).await - } -} - // GetSwapInfo #[typeshare] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -1044,26 +1027,6 @@ pub async fn withdraw_btc( }) } -#[tracing::instrument(fields(method = "start_daemon"), skip(context))] -pub async fn start_daemon( - start_daemon: StartDaemonArgs, - context: Context, -) -> Result { - let StartDaemonArgs { server_address } = start_daemon; - // Default to 127.0.0.1:1234 - let server_address = server_address.unwrap_or("127.0.0.1:1234".parse()?); - - let (addr, server_handle) = rpc::run_server(server_address, context).await?; - - tracing::info!(%addr, "Started RPC server"); - - server_handle.stopped().await; - - tracing::info!("Stopped RPC server"); - - Ok(json!({})) -} - #[tracing::instrument(fields(method = "get_balance"), skip(context))] pub async fn get_balance(balance: BalanceArgs, context: Arc) -> Result { let BalanceArgs { force_refresh } = balance; diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index 342118bc..928bce52 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -1,8 +1,7 @@ use crate::bitcoin::{bitcoin_address, Amount}; use crate::cli::api::request::{ BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ExportBitcoinWalletArgs, GetConfigArgs, - GetHistoryArgs, ListSellersArgs, MoneroRecoveryArgs, Request, ResumeSwapArgs, StartDaemonArgs, - WithdrawBtcArgs, + GetHistoryArgs, ListSellersArgs, MoneroRecoveryArgs, Request, ResumeSwapArgs, WithdrawBtcArgs, }; use crate::cli::api::Context; use crate::monero; @@ -11,7 +10,6 @@ use anyhow::Result; use bitcoin::address::NetworkUnchecked; use libp2p::core::Multiaddr; use std::ffi::OsString; -use std::net::SocketAddr; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; @@ -172,30 +170,6 @@ where Ok(context) } - CliCommand::StartDaemon { - server_address, - bitcoin, - monero, - tor, - } => { - let context = Arc::new( - ContextBuilder::new(is_testnet) - .with_tor(tor.enable_tor) - .with_bitcoin(bitcoin) - .with_monero(monero) - .with_data_dir(data) - .with_debug(debug) - .with_json(json) - .build() - .await?, - ); - - StartDaemonArgs { server_address } - .request(context.clone()) - .await?; - - Ok(context) - } CliCommand::WithdrawBtc { bitcoin, amount, @@ -430,23 +404,6 @@ enum CliCommand { #[structopt(flatten)] bitcoin: Bitcoin, }, - #[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" - )] - server_address: Option, - - #[structopt(flatten)] - tor: Tor, - }, /// Resume a swap Resume { #[structopt(flatten)] diff --git a/swap/src/lib.rs b/swap/src/lib.rs index 6714fb80..7ab938d3 100644 --- a/swap/src/lib.rs +++ b/swap/src/lib.rs @@ -29,7 +29,6 @@ pub mod monero; mod monero_ext; pub mod network; pub mod protocol; -pub mod rpc; pub mod seed; pub mod tracing_ext; diff --git a/swap/src/rpc.rs b/swap/src/rpc.rs deleted file mode 100644 index a6503c12..00000000 --- a/swap/src/rpc.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::cli::api::Context; -use std::net::SocketAddr; -use thiserror::Error; -use tower_http::cors::CorsLayer; - -use jsonrpsee::{ - core::server::host_filtering::AllowHosts, - server::{ServerBuilder, ServerHandle}, -}; - -pub mod methods; - -#[derive(Debug, Error)] -pub enum Error { - #[error("Could not parse key value from params")] - ParseError, -} - -pub async fn run_server( - server_address: SocketAddr, - context: Context, -) -> anyhow::Result<(SocketAddr, ServerHandle)> { - let cors = CorsLayer::permissive(); - let middleware = tower::ServiceBuilder::new().layer(cors); - - let server = ServerBuilder::default() - .set_host_filtering(AllowHosts::Any) - .set_middleware(middleware) - .build(server_address) - .await?; - - let modules = methods::register_modules(context)?; - - let addr = server.local_addr()?; - let server_handle = server.start(modules)?; - - Ok((addr, server_handle)) -} diff --git a/swap/src/rpc/methods.rs b/swap/src/rpc/methods.rs deleted file mode 100644 index 9b85e02c..00000000 --- a/swap/src/rpc/methods.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::bitcoin::bitcoin_address; -use crate::cli::api::request::{ - BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, GetCurrentSwapArgs, GetHistoryArgs, GetLogsArgs, - GetSwapInfoArgs, ListSellersArgs, MoneroRecoveryArgs, Request, ResumeSwapArgs, - SuspendCurrentSwapArgs, WithdrawBtcArgs, -}; -use crate::cli::api::Context; -use crate::monero::monero_address; -use anyhow::Result; -use jsonrpsee::server::RpcModule; - -trait ConvertToJsonRpseeError { - fn to_jsonrpsee_result(self) -> Result; -} - -impl ConvertToJsonRpseeError for Result { - fn to_jsonrpsee_result(self) -> Result { - self.map_err(|e| jsonrpsee_core::Error::Custom(e.to_string())) - } -} - -pub fn register_modules(outer_context: Context) -> Result> { - let mut module = RpcModule::new(outer_context); - - module.register_async_method("suspend_current_swap", |_, context| async move { - SuspendCurrentSwapArgs {} - .request(context) - .await - .to_jsonrpsee_result() - })?; - - module.register_async_method("get_swap_info", |params_raw, context| async move { - let params: GetSwapInfoArgs = params_raw.parse()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method("get_bitcoin_balance", |params_raw, context| async move { - let params: BalanceArgs = params_raw.parse()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method("get_history", |_, context| async move { - GetHistoryArgs {} - .request(context) - .await - .to_jsonrpsee_result() - })?; - - module.register_async_method("get_logs", |params_raw, context| async move { - let params: GetLogsArgs = params_raw.parse()?; - - let logs = params.request(context).await?; - - Ok(logs) - })?; - - module.register_async_method("resume_swap", |params_raw, context| async move { - let params: ResumeSwapArgs = params_raw.parse()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method("cancel_refund_swap", |params_raw, context| async move { - let params: CancelAndRefundArgs = params_raw.parse()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method( - "get_monero_recovery_info", - |params_raw, context| async move { - let params: MoneroRecoveryArgs = params_raw.parse()?; - - params.request(context).await.to_jsonrpsee_result() - }, - )?; - - module.register_async_method("withdraw_btc", |params_raw, context| async move { - let mut params: WithdrawBtcArgs = params_raw.parse()?; - - params.address = bitcoin_address::revalidate_network( - params.address, - context.config.env_config.bitcoin_network, - ) - .to_jsonrpsee_result()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method("buy_xmr", |params_raw, context| async move { - let mut params: BuyXmrArgs = params_raw.parse()?; - - params.bitcoin_change_address = params - .bitcoin_change_address - .map(|address| { - bitcoin_address::validate_network( - address, - context.config.env_config.bitcoin_network, - ) - .map(|a| a.into_unchecked()) - }) - .transpose() - .to_jsonrpsee_result()?; - - params.monero_receive_address = monero_address::validate( - params.monero_receive_address, - context.config.env_config.monero_network, - ) - .to_jsonrpsee_result()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method("list_sellers", |params_raw, context| async move { - let params: ListSellersArgs = params_raw.parse()?; - - params.request(context).await.to_jsonrpsee_result() - })?; - - module.register_async_method("get_current_swap", |_, context| async move { - GetCurrentSwapArgs {} - .request(context) - .await - .to_jsonrpsee_result() - })?; - - Ok(module) -} diff --git a/swap/tests/rpc.rs b/swap/tests/rpc.rs deleted file mode 100644 index a27b7de4..00000000 --- a/swap/tests/rpc.rs +++ /dev/null @@ -1,430 +0,0 @@ -pub mod harness; -#[cfg(test)] -mod test { - - use anyhow::Result; - - use jsonrpsee::ws_client::WsClientBuilder; - use jsonrpsee_core::client::{Client, ClientT}; - use jsonrpsee_core::params::ObjectParams; - - use serial_test::serial; - - use serde_json::Value; - use std::collections::HashMap; - use std::net::SocketAddr; - use std::sync::Arc; - use std::time::Duration; - use swap::cli::api::request::{Request, StartDaemonArgs}; - use swap::cli::api::Context; - - use crate::harness::alice_run_until::is_xmr_lock_transaction_sent; - use crate::harness::bob_run_until::is_btc_locked; - use crate::harness::{setup_test, SlowCancelConfig, TestContext}; - use swap::asb::FixedRate; - use swap::protocol::{alice, bob}; - use swap::tracing_ext::{capture_logs, MakeCapturingWriter}; - use tracing_subscriber::filter::LevelFilter; - use uuid::Uuid; - - const SERVER_ADDRESS: &str = "127.0.0.1:1234"; - const SERVER_START_TIMEOUT_SECS: u64 = 50; - const BITCOIN_ADDR: &str = "bcrt1qahvhjfc7vx5857zf8knxs8yp5lkm26jgyt0k76"; - const MONERO_ADDR: &str = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAoNybEUvYBKHcnrSgxnVWgZsTvRBaHBNXPa8tHiCU51a"; - const SELLER: &str = - "/ip4/127.0.0.1/tcp/9939/p2p/12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi"; - const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b"; - - pub async fn setup_daemon( - harness_ctx: TestContext, - ) -> (Client, MakeCapturingWriter, Arc) { - let writer = capture_logs(LevelFilter::DEBUG); - let server_address: Option = - SERVER_ADDRESS.parse::().unwrap().into(); - - let context = Arc::new(harness_ctx.get_bob_context().await); - - let context_clone = context.clone(); - - tokio::spawn(async move { - let args = StartDaemonArgs { server_address }; - if let Err(err) = args.request(context_clone).await { - println!("Failed to initialize daemon for testing: {}", err); - } - }); - - for _ in 0..SERVER_START_TIMEOUT_SECS { - if writer.captured().contains("Started RPC server") { - let url = format!("ws://{}", SERVER_ADDRESS); - let client = WsClientBuilder::default().build(&url).await.unwrap(); - - return (client, writer, context); - } - - tokio::time::sleep(Duration::from_secs(1)).await; - } - - panic!( - "Failed to start RPC server after {} seconds", - SERVER_START_TIMEOUT_SECS - ); - } - - fn assert_has_keys_serde(map: &serde_json::Map, keys: &[&str]) { - for &key in keys { - assert!(map.contains_key(key), "Key {} is missing", key); - } - } - - // Helper function for HashMap - fn assert_has_keys_hashmap(map: &HashMap, keys: &[&str]) { - for &key in keys { - assert!(map.contains_key(key), "Key {} is missing", key); - } - } - - #[tokio::test] - #[serial] - pub async fn get_swap_info() { - setup_test(SlowCancelConfig, |mut harness_ctx| async move { - // Start a swap and wait for xmr lock transaction to be published (XmrLockTransactionSent) - let (bob_swap, _) = harness_ctx.bob_swap().await; - let bob_swap_id = bob_swap.id; - tokio::spawn(bob::run_until(bob_swap, is_btc_locked)); - let alice_swap = harness_ctx.alice_next_swap().await; - alice::run_until( - alice_swap, - is_xmr_lock_transaction_sent, - FixedRate::default(), - ) - .await?; - - let (client, _, _) = setup_daemon(harness_ctx).await; - - let response: HashMap>> = client - .request("get_history", ObjectParams::new()) - .await - .unwrap(); - - let swaps: Vec> = vec![HashMap::from([ - ("swap_id".to_string(), bob_swap_id.to_string()), - ("state".to_string(), "btc is locked".to_string()), - ])]; - - assert_eq!(response.get("swaps").unwrap(), &swaps); - - let mut params = ObjectParams::new(); - params.insert("swap_id", bob_swap_id).unwrap(); - let response: HashMap = - client.request("get_swap_info", params).await.unwrap(); - - // Check primary keys in response - assert_has_keys_hashmap( - &response, - &[ - "tx_refund_fee", - "swap_id", - "cancel_timelock", - "timelock", - "punish_timelock", - "state_name", - "btc_amount", - "start_date", - "btc_refund_address", - "tx_cancel_fee", - "xmr_amount", - "completed", - "tx_lock_id", - "seller", - ], - ); - - // Assert specific fields - assert_eq!(response.get("swap_id").unwrap(), &bob_swap_id.to_string()); - assert_eq!( - response.get("state_name").unwrap(), - &"btc is locked".to_string() - ); - assert_eq!(response.get("completed").unwrap(), &Value::Bool(false)); - - // Check seller object and its keys - let seller = response - .get("seller") - .expect("Field 'seller' is missing from response") - .as_object() - .expect("'seller' is not an object"); - assert_has_keys_serde(seller, &["peer_id"]); - - // Check timelock object, nested 'None' object, and blocks_left - let timelock = response - .get("timelock") - .expect("Field 'timelock' is missing from response") - .as_object() - .expect("'timelock' is not an object"); - let timelock_type = timelock - .get("type") - .expect("Field 'type' is missing from 'timelock'") - .as_str() - .expect("'type' is not a string"); - - assert_eq!(timelock_type, "None"); - - let timelock_content = timelock - .get("content") - .expect("Field 'content' is missing from 'None'") - .as_object() - .expect("'content' is not an object"); - let blocks_left = timelock_content - .get("blocks_left") - .expect("Field 'blocks_left' is missing from 'None'") - .as_i64() - .expect("'blocks_left' is not an integer"); - - // Validate blocks_left - assert!( - blocks_left > 0 && blocks_left <= 180, - "Field 'blocks_left' should be > 0 and <= 180 but got {}", - blocks_left - ); - - Ok(()) - }) - .await; - } - - #[tokio::test] - #[serial] - pub async fn test_rpc_calls() { - setup_test(SlowCancelConfig, |harness_ctx| async move { - let alice_addr = harness_ctx.bob_params.get_concentenated_alice_address(); - let (change_address, receive_address) = - harness_ctx.bob_params.get_change_receive_addresses().await; - - let (client, _, _) = setup_daemon(harness_ctx).await; - assert!(client.is_connected()); - - let mut params = ObjectParams::new(); - - params.insert("force_refresh", false).unwrap(); - let response: HashMap = - client.request("get_bitcoin_balance", params).await.unwrap(); - - assert_eq!(response, HashMap::from([("balance".to_string(), 10000000)])); - - // TODO: Renable this test once the "log reference id" feature has been added again. The feature was removed as part of this PR: - // https://github.com/UnstoppableSwap/xmr-btc-swap/pull/10 - // - // let mut params = ObjectParams::new(); - // params.insert("log_reference_id", "test_ref_id").unwrap(); - // params.insert("force_refresh", false).unwrap(); - - // let _: HashMap = client.request("get_bitcoin_balance", params).await.unwrap(); - - // assert!(writer.captured().contains( - // r#"method{method_name="Balance" log_reference_id="\"test_ref_id\""}: swap::api::request: Current Bitcoin balance as of last sync balance=0.1 BTC"# - // )); - - for method in ["get_swap_info", "resume_swap", "cancel_refund_swap"].iter() { - let mut params = ObjectParams::new(); - params.insert("swap_id", "invalid_swap").unwrap(); - - let response: Result, _> = - client.request(method, params).await; - response.expect_err(&format!( - "Expected an error when swap_id is invalid for method {}", - method - )); - - let params = ObjectParams::new(); - - let response: Result, _> = - client.request(method, params).await; - response.expect_err(&format!( - "Expected an error when swap_id is missing for method {}", - method - )); - } - - let params = ObjectParams::new(); - let result: Result, _> = - client.request("list_sellers", params).await; - - result.expect_err("Expected an error when rendezvous_point is missing"); - - let params = ObjectParams::new(); - let result: Result, _> = - client.request("list_sellers", params).await; - - result.expect_err("Expected an error when rendezvous_point is missing"); - - let params = ObjectParams::new(); - let response: Result, _> = - client.request("withdraw_btc", params).await; - response.expect_err("Expected an error when withdraw_address is missing"); - - let mut params = ObjectParams::new(); - params.insert("address", "invalid_address").unwrap(); - let response: Result, _> = - client.request("withdraw_btc", params).await; - response.expect_err("Expected an error when withdraw_address is malformed"); - - let mut params = ObjectParams::new(); - params.insert("address", BITCOIN_ADDR).unwrap(); - params.insert("amount", "0").unwrap(); - let response: Result, _> = - client.request("withdraw_btc", params).await; - response.expect_err("Expected an error when amount is 0"); - - let mut params = ObjectParams::new(); - params.insert("address", BITCOIN_ADDR).unwrap(); - params.insert("amount", 1000000).unwrap(); - let response: HashMap = client - .request("withdraw_btc", params) - .await - .expect("Expected a valid response"); - - assert_has_keys_hashmap(&response, &["amount", "txid"]); - assert_eq!(response.get("amount").unwrap().as_u64().unwrap(), 1_000_000); - - let params = ObjectParams::new(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when no params are given"); - - let mut params = ObjectParams::new(); - params - .insert("bitcoin_change_address", BITCOIN_ADDR) - .unwrap(); - params - .insert("monero_receive_address", MONERO_ADDR) - .unwrap(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when seller is missing"); - - let mut params = ObjectParams::new(); - params - .insert("bitcoin_change_address", BITCOIN_ADDR) - .unwrap(); - params.insert("seller", SELLER).unwrap(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when monero_receive_address is missing"); - - let mut params = ObjectParams::new(); - params - .insert("monero_receive_address", MONERO_ADDR) - .unwrap(); - params.insert("seller", SELLER).unwrap(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when bitcoin_change_address is missing"); - - let mut params = ObjectParams::new(); - params - .insert("bitcoin_change_address", "invalid_address") - .unwrap(); - params - .insert("monero_receive_address", MONERO_ADDR) - .unwrap(); - params.insert("seller", SELLER).unwrap(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when bitcoin_change_address is malformed"); - - let mut params = ObjectParams::new(); - params - .insert("bitcoin_change_address", BITCOIN_ADDR) - .unwrap(); - params - .insert("monero_receive_address", "invalid_address") - .unwrap(); - params.insert("seller", SELLER).unwrap(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when monero_receive_address is malformed"); - - let mut params = ObjectParams::new(); - params - .insert("bitcoin_change_address", BITCOIN_ADDR) - .unwrap(); - params - .insert("monero_receive_address", MONERO_ADDR) - .unwrap(); - params.insert("seller", "invalid_seller").unwrap(); - let response: Result, _> = - client.request("buy_xmr", params).await; - response.expect_err("Expected an error when seller is malformed"); - - let response: Result, _> = client - .request("suspend_current_swap", ObjectParams::new()) - .await; - response.expect_err("Expected an error when no swap is running"); - - let mut params = ObjectParams::new(); - params - .insert("bitcoin_change_address", change_address) - .unwrap(); - params - .insert("monero_receive_address", receive_address) - .unwrap(); - - params.insert("seller", alice_addr).unwrap(); - let response: HashMap = client - .request("buy_xmr", params) - .await - .expect("Expected a HashMap, got an error"); - - assert_has_keys_hashmap(&response, &["swap_id"]); - - Ok(()) - }) - .await; - } - - #[tokio::test] - #[serial] - pub async fn suspend_current_swap_swap_running() { - setup_test(SlowCancelConfig, |harness_ctx| async move { - let (client, _, ctx) = setup_daemon(harness_ctx).await; - - ctx.swap_lock - .acquire_swap_lock(Uuid::parse_str(SWAP_ID).unwrap()) - .await - .unwrap(); - let cloned_ctx = ctx.clone(); - - tokio::spawn(async move { - // Immediately release lock when suspend signal is received. Mocks a running swap that is then cancelled. - ctx.swap_lock - .listen_for_swap_force_suspension() - .await - .unwrap(); - ctx.swap_lock.release_swap_lock().await.unwrap(); - }); - - let response: HashMap = client - .request("suspend_current_swap", ObjectParams::new()) - .await - .unwrap(); - assert_eq!( - response, - HashMap::from([("swap_id".to_string(), SWAP_ID.to_string())]) - ); - - cloned_ctx - .swap_lock - .acquire_swap_lock(Uuid::parse_str(SWAP_ID).unwrap()) - .await - .unwrap(); - - let response: Result, _> = client - .request("suspend_current_swap", ObjectParams::new()) - .await; - response.expect_err("Expected an error when suspend signal times out"); - - Ok(()) - }) - .await; - } -}