diff --git a/Cargo.lock b/Cargo.lock index f0b51599..6f37fab9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler32" version = "1.2.0" @@ -105,6 +120,16 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6e24d2cce90c53b948c46271bfb053e4bdc2db9b5d3f65e20f8cf28a1b7fc3" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-compression" version = "0.3.15" @@ -114,7 +139,7 @@ dependencies = [ "bzip2", "futures-core", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "tokio", ] @@ -139,7 +164,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", ] [[package]] @@ -186,11 +211,26 @@ dependencies = [ "futures-core", "getrandom 0.2.6", "instant", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "rand 0.8.3", "tokio", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.7.1", + "object", + "rustc-demangle", +] + [[package]] name = "base32" version = "0.4.0" @@ -359,6 +399,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "blake2" version = "0.9.2" @@ -423,7 +469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "822462c1e7b17b31961798a6874b36daea6818e99e0cb7d3b7b0fa3c477751c3" dependencies = [ "borsh-derive", - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] @@ -616,7 +662,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term 0.11.0", "atty", - "bitflags", + "bitflags 1.3.2", "strsim 0.8.0", "textwrap", "unicode-width", @@ -629,6 +675,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colored" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +dependencies = [ + "is-terminal", + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "comfy-table" version = "6.1.4" @@ -804,7 +861,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -1225,7 +1282,7 @@ dependencies = [ "crc32fast", "libc", "libz-sys", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -1384,7 +1441,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "pin-utils", "slab", ] @@ -1459,13 +1516,19 @@ dependencies = [ "polyval", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "git2" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -1664,7 +1727,7 @@ dependencies = [ "httparse", "httpdate", "itoa 1.0.1", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "socket2 0.4.7", "tokio", "tower-service", @@ -1784,6 +1847,17 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.1", + "rustix 0.38.11", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1871,9 +1945,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libgit2-sys" @@ -2211,6 +2285,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + [[package]] name = "lock_api" version = "0.4.6" @@ -2323,16 +2403,45 @@ dependencies = [ "adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mockito" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c762b6267c4593555bb38f1df19e9318985bc4de60b5e8462890856a9a5b4c" +dependencies = [ + "assert-json-diff", + "colored", + "futures", + "hyper", + "lazy_static", + "log", + "rand 0.8.3", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", ] [[package]] @@ -2593,6 +2702,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.13.0" @@ -2785,9 +2903,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2904,7 +3022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -3159,7 +3277,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3168,7 +3286,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3183,9 +3301,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -3204,9 +3322,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rend" @@ -3239,7 +3357,7 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "rustls 0.21.1", "rustls-pemfile", "serde", @@ -3337,6 +3455,12 @@ dependencies = [ "rust_decimal", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -3364,11 +3488,24 @@ version = "0.37.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.5", "windows-sys 0.48.0", ] @@ -3556,7 +3693,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -3814,6 +3951,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0242b8e50dd9accdd56170e94ca1ebd223b098eb9c83539a6e367d0f36ae68" +[[package]] +name = "similar" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" + [[package]] name = "slab" version = "0.4.2" @@ -3881,6 +4024,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.0" @@ -3950,7 +4103,7 @@ checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ "ahash", "atoi", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "crc", @@ -4144,6 +4297,7 @@ dependencies = [ "hyper", "itertools", "libp2p", + "mockito", "monero", "monero-harness", "monero-rpc", @@ -4233,7 +4387,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.11", "windows-sys 0.48.0", ] @@ -4381,33 +4535,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", "parking_lot 0.12.0", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "signal-hook-registry", - "socket2 0.4.7", + "socket2 0.5.3", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4461,7 +4614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "tokio", ] @@ -4506,7 +4659,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "tokio", "tracing", ] @@ -4550,7 +4703,7 @@ version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.13", "tracing-attributes", "tracing-core", ] @@ -5124,19 +5277,6 @@ dependencies = [ "windows_x86_64_msvc 0.32.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5173,12 +5313,6 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" @@ -5191,12 +5325,6 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.48.0" @@ -5209,12 +5337,6 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.48.0" @@ -5227,12 +5349,6 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" @@ -5251,12 +5367,6 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 3ef9e09c..074ad0df 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -77,6 +77,7 @@ zip = "0.5" bitcoin-harness = "0.2.2" get-port = "3" hyper = "0.14" +mockito = "1.1.0" monero-harness = { path = "../monero-harness" } port_check = "0.1" proptest = "1" diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index 33c718c3..292a4586 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -521,7 +521,7 @@ async fn init_bitcoin_wallet( async fn init_monero_wallet( data_dir: PathBuf, - monero_daemon_address: String, + monero_daemon_address: Option, env_config: Config, ) -> Result<(monero::Wallet, monero::WalletRpcProcess)> { let network = env_config.monero_network; @@ -531,7 +531,7 @@ async fn init_monero_wallet( let monero_wallet_rpc = monero::WalletRpc::new(data_dir.join("monero")).await?; let monero_wallet_rpc_process = monero_wallet_rpc - .run(network, monero_daemon_address.as_str()) + .run(network, monero_daemon_address) .await?; let monero_wallet = monero::Wallet::open_or_create( diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index e4da8e7d..ee48428a 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -14,10 +14,6 @@ use structopt::{clap, StructOpt}; use url::Url; use uuid::Uuid; -// See: https://moneroworld.com/ -pub const DEFAULT_MONERO_DAEMON_ADDRESS: &str = "node.community.rino.io:18081"; -pub const DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET: &str = "stagenet.community.rino.io:38081"; - // See: https://1209k.com/bitcoin-eye/ele.php?chain=btc const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://blockstream.info:700"; // See: https://1209k.com/bitcoin-eye/ele.php?chain=tbtc @@ -80,11 +76,11 @@ where } => { let (bitcoin_electrum_rpc_url, bitcoin_target_block) = bitcoin.apply_defaults(is_testnet)?; - let monero_daemon_address = monero.apply_defaults(is_testnet); let monero_receive_address = validate_monero_address(monero_receive_address, is_testnet)?; let bitcoin_change_address = validate_bitcoin_address(bitcoin_change_address, is_testnet)?; + let monero_daemon_address = monero.monero_daemon_address; Arguments { env_config: env_config_from(is_testnet), @@ -167,7 +163,7 @@ where } => { let (bitcoin_electrum_rpc_url, bitcoin_target_block) = bitcoin.apply_defaults(is_testnet)?; - let monero_daemon_address = monero.apply_defaults(is_testnet); + let monero_daemon_address = monero.monero_daemon_address; Arguments { env_config: env_config_from(is_testnet), @@ -254,7 +250,7 @@ pub enum Command { bitcoin_target_block: usize, bitcoin_change_address: bitcoin::Address, monero_receive_address: monero::Address, - monero_daemon_address: String, + monero_daemon_address: Option, tor_socks5_port: u16, namespace: XmrBtcNamespace, }, @@ -274,7 +270,7 @@ pub enum Command { swap_id: Uuid, bitcoin_electrum_rpc_url: Url, bitcoin_target_block: usize, - monero_daemon_address: String, + monero_daemon_address: Option, tor_socks5_port: u16, namespace: XmrBtcNamespace, }, @@ -436,23 +432,11 @@ enum RawCommand { struct Monero { #[structopt( long = "monero-daemon-address", - help = "Specify to connect to a monero daemon of your choice: :" + help = "Specify to connect to a monero daemon of your choice: :. If none is specified, we will connect to a public node." )] monero_daemon_address: Option, } -impl Monero { - fn apply_defaults(self, testnet: bool) -> String { - if let Some(address) = self.monero_daemon_address { - address - } else if testnet { - DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET.to_string() - } else { - DEFAULT_MONERO_DAEMON_ADDRESS.to_string() - } - } -} - #[derive(structopt::StructOpt, Debug)] struct Bitcoin { #[structopt(long = "electrum-rpc", help = "Provide the Bitcoin Electrum RPC URL")] @@ -1174,7 +1158,7 @@ mod tests { bitcoin_change_address: BITCOIN_TESTNET_ADDRESS.parse().unwrap(), monero_receive_address: monero::Address::from_str(MONERO_STAGENET_ADDRESS) .unwrap(), - monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET.to_string(), + monero_daemon_address: None, tor_socks5_port: DEFAULT_SOCKS5_PORT, namespace: XmrBtcNamespace::Testnet, }, @@ -1194,7 +1178,7 @@ mod tests { bitcoin_change_address: BITCOIN_MAINNET_ADDRESS.parse().unwrap(), monero_receive_address: monero::Address::from_str(MONERO_MAINNET_ADDRESS) .unwrap(), - monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS.to_string(), + monero_daemon_address: None, tor_socks5_port: DEFAULT_SOCKS5_PORT, namespace: XmrBtcNamespace::Mainnet, }, @@ -1212,7 +1196,7 @@ mod tests { bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET) .unwrap(), bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET, - monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET.to_string(), + monero_daemon_address: None, tor_socks5_port: DEFAULT_SOCKS5_PORT, namespace: XmrBtcNamespace::Testnet, }, @@ -1229,7 +1213,7 @@ mod tests { swap_id: Uuid::from_str(SWAP_ID).unwrap(), bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(), bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET, - monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS.to_string(), + monero_daemon_address: None, tor_socks5_port: DEFAULT_SOCKS5_PORT, namespace: XmrBtcNamespace::Mainnet, }, diff --git a/swap/src/monero/wallet_rpc.rs b/swap/src/monero/wallet_rpc.rs index 819eeccb..e44d800e 100644 --- a/swap/src/monero/wallet_rpc.rs +++ b/swap/src/monero/wallet_rpc.rs @@ -1,19 +1,45 @@ use ::monero::Network; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Error, Result}; use big_bytes::BigByte; use futures::{StreamExt, TryStreamExt}; use monero_rpc::wallet::{Client, MoneroWalletRpc as _}; use reqwest::header::CONTENT_LENGTH; use reqwest::Url; +use serde::Deserialize; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::process::Stdio; +use std::time::Duration; use tokio::fs::{remove_file, OpenOptions}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::process::{Child, Command}; use tokio_util::codec::{BytesCodec, FramedRead}; use tokio_util::io::StreamReader; +// See: https://www.moneroworld.com/#nodes, https://monero.fail +// We don't need any testnet nodes because we don't support testnet at all +const MONERO_DAEMONS: [MoneroDaemon; 17] = [ + MoneroDaemon::new("xmr-node.cakewallet.com", 18081, Network::Mainnet), + MoneroDaemon::new("nodex.monerujo.io", 18081, Network::Mainnet), + MoneroDaemon::new("node.moneroworld.com", 18089, Network::Mainnet), + MoneroDaemon::new("nodes.hashvault.pro", 18081, Network::Mainnet), + MoneroDaemon::new("p2pmd.xmrvsbeast.com", 18081, Network::Mainnet), + MoneroDaemon::new("node.monerodevs.org", 18089, Network::Mainnet), + MoneroDaemon::new("xmr-node-usa-east.cakewallet.com", 18081, Network::Mainnet), + MoneroDaemon::new("xmr-node-uk.cakewallet.com", 18081, Network::Mainnet), + MoneroDaemon::new("node.community.rino.io", 18081, Network::Mainnet), + MoneroDaemon::new("testingjohnross.com", 20031, Network::Mainnet), + MoneroDaemon::new("xmr.litepay.ch", 18081, Network::Mainnet), + MoneroDaemon::new("node.trocador.app", 18089, Network::Mainnet), + MoneroDaemon::new("stagenet.xmr-tw.org", 38081, Network::Stagenet), + MoneroDaemon::new("node.monerodevs.org", 38089, Network::Stagenet), + MoneroDaemon::new("singapore.node.xmr.pm", 38081, Network::Stagenet), + MoneroDaemon::new("xmr-lux.boldsuck.org", 38081, Network::Stagenet), + MoneroDaemon::new("stagenet.community.rino.io", 38081, Network::Stagenet), +]; + #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] compile_error!("unsupported operating system"); @@ -50,6 +76,91 @@ pub struct WalletRpcProcess { port: u16, } +struct MoneroDaemon { + address: &'static str, + port: u16, + network: Network, +} + +impl MoneroDaemon { + const fn new(address: &'static str, port: u16, network: Network) -> Self { + Self { + address, + port, + network, + } + } + + /// Checks if the Monero daemon is available by sending a request to its `get_info` endpoint. + async fn is_available(&self, client: &reqwest::Client) -> Result { + let url = format!("http://{}:{}/get_info", self.address, self.port); + let res = client + .get(url) + .send() + .await + .context("Failed to send request to get_info endpoint")?; + + let json: MoneroDaemonGetInfoResponse = res + .json() + .await + .context("Failed to deserialize daemon get_info response")?; + + let is_status_ok = json.status == "OK"; + let is_synchronized = json.synchronized; + let is_correct_network = match self.network { + Network::Mainnet => json.mainnet, + Network::Stagenet => json.stagenet, + Network::Testnet => json.testnet, + }; + + Ok(is_status_ok && is_synchronized && is_correct_network) + } +} + +impl Display for MoneroDaemon { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.address, self.port) + } +} + +#[derive(Deserialize)] +struct MoneroDaemonGetInfoResponse { + status: String, + synchronized: bool, + mainnet: bool, + stagenet: bool, + testnet: bool, +} + +/// Chooses an available Monero daemon based on the specified network. +async fn choose_monero_daemon(network: Network) -> Result<&'static MoneroDaemon, Error> { + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(30)) + .https_only(false) + .build()?; + + // We only want to check for daemons that match the specified network + let network_matching_daemons = MONERO_DAEMONS + .iter() + .filter(|daemon| daemon.network == network); + + for daemon in network_matching_daemons { + match daemon.is_available(&client).await { + Ok(true) => { + tracing::debug!(%daemon, "Found available Monero daemon"); + return Ok(daemon); + } + Err(err) => { + tracing::debug!(%err, %daemon, "Failed to connect to Monero daemon"); + continue; + } + Ok(false) => continue, + } + } + + bail!("No Monero daemon could be found. Please specify one manually or try again later.") +} + impl WalletRpcProcess { pub fn endpoint(&self) -> Url { Url::parse(&format!("http://127.0.0.1:{}/json_rpc", self.port)) @@ -153,13 +264,23 @@ impl WalletRpc { Ok(monero_wallet_rpc) } - pub async fn run(&self, network: Network, daemon_address: &str) -> Result { + pub async fn run( + &self, + network: Network, + daemon_address: Option, + ) -> Result { let port = tokio::net::TcpListener::bind("127.0.0.1:0") .await? .local_addr()? .port(); + let daemon_address = match daemon_address { + Some(daemon_address) => daemon_address, + None => choose_monero_daemon(network).await?.to_string(), + }; + tracing::debug!( + %daemon_address, %port, "Starting monero-wallet-rpc" ); @@ -232,7 +353,6 @@ impl WalletRpc { #[cfg(not(target_os = "windows"))] async fn extract_archive(monero_wallet_rpc: &Self) -> Result<()> { - use anyhow::bail; use tokio_tar::Archive; let mut options = OpenOptions::new(); @@ -297,3 +417,123 @@ impl WalletRpc { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn extract_host_and_port(address: String) -> (&'static str, u16) { + let parts: Vec<&str> = address.split(':').collect(); + + if parts.len() == 2 { + let host = parts[0].to_string(); + let port = parts[1].parse::().unwrap(); + let static_str_host: &'static str = Box::leak(host.into_boxed_str()); + return (static_str_host, port); + } + panic!("Could not extract host and port from address: {}", address) + } + + #[tokio::test] + async fn test_is_daemon_available_success() { + let mut server = mockito::Server::new(); + + let _ = server + .mock("GET", "/get_info") + .with_status(200) + .with_body( + r#" + { + "status": "OK", + "synchronized": true, + "mainnet": true, + "stagenet": false, + "testnet": false + } + "#, + ) + .create(); + + let (host, port) = extract_host_and_port(server.host_with_port()); + + let client = reqwest::Client::new(); + let result = MoneroDaemon::new(host, port, Network::Mainnet) + .is_available(&client) + .await; + + assert!(result.is_ok()); + assert!(result.unwrap()); + } + + #[tokio::test] + async fn test_is_daemon_available_wrong_network_failure() { + let mut server = mockito::Server::new(); + + let _ = server + .mock("GET", "/get_info") + .with_status(200) + .with_body( + r#" + { + "status": "OK", + "synchronized": true, + "mainnet": true, + "stagenet": false, + "testnet": false + } + "#, + ) + .create(); + + let (host, port) = extract_host_and_port(server.host_with_port()); + + let client = reqwest::Client::new(); + let result = MoneroDaemon::new(host, port, Network::Stagenet) + .is_available(&client) + .await; + + assert!(result.is_ok()); + assert!(!result.unwrap()); + } + + #[tokio::test] + async fn test_is_daemon_available_not_synced_failure() { + let mut server = mockito::Server::new(); + + let _ = server + .mock("GET", "/get_info") + .with_status(200) + .with_body( + r#" + { + "status": "OK", + "synchronized": false, + "mainnet": true, + "stagenet": false, + "testnet": false + } + "#, + ) + .create(); + + let (host, port) = extract_host_and_port(server.host_with_port()); + + let client = reqwest::Client::new(); + let result = MoneroDaemon::new(host, port, Network::Mainnet) + .is_available(&client) + .await; + + assert!(result.is_ok()); + assert!(!result.unwrap()); + } + + #[tokio::test] + async fn test_is_daemon_available_network_error_failure() { + let client = reqwest::Client::new(); + let result = MoneroDaemon::new("does.not.exist.com", 18081, Network::Mainnet) + .is_available(&client) + .await; + + assert!(result.is_err()); + } +}