diff --git a/.github/workflows/build-release-binaries.yml b/.github/workflows/build-release-binaries.yml index 8e842a00..3db89051 100644 --- a/.github/workflows/build-release-binaries.yml +++ b/.github/workflows/build-release-binaries.yml @@ -45,16 +45,16 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout tagged commit - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 with: ref: ${{ github.event.release.target_commitish }} token: ${{ secrets.BOTTY_GITHUB_TOKEN }} - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.63 + toolchain: 1.67 targets: armv7-unknown-linux-gnueabihf - name: Build ${{ matrix.target }} ${{ matrix.bin }} release binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 055e61b5..af335076 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,14 +13,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 - uses: dtolnay/rust-toolchain@master with: toolchain: 1.67 components: clippy,rustfmt - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - name: Check formatting uses: dprint/check@v2.2 @@ -37,9 +37,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - name: Build swap run: cargo build --bin swap @@ -51,12 +51,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - name: Install sqlx-cli - run: cargo install sqlx-cli + run: cargo install sqlx-cli --locked - name: Run sqlite_dev_setup.sh script run: | @@ -78,9 +78,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout sources - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - uses: dtolnay/rust-toolchain@master with: @@ -118,9 +118,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout sources - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - name: Build tests run: cargo build --tests --workspace --all-features @@ -155,9 +155,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4.0.0 - - uses: Swatinem/rust-cache@v2.6.0 + - uses: Swatinem/rust-cache@v2.6.2 - name: Run test ${{ matrix.test_name }} run: cargo test --package swap --all-features --test ${{ matrix.test_name }} -- --nocapture diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index f3bee225..c95f8d22 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -11,7 +11,7 @@ jobs: if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@v4.0.0 - name: Extract version from branch name id: extract-version diff --git a/.github/workflows/draft-new-release.yml b/.github/workflows/draft-new-release.yml index 338ff7c5..2e89fd6c 100644 --- a/.github/workflows/draft-new-release.yml +++ b/.github/workflows/draft-new-release.yml @@ -12,7 +12,7 @@ jobs: name: "Draft a new release" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@v4.0.0 with: token: ${{ secrets.BOTTY_GITHUB_TOKEN }} @@ -41,8 +41,12 @@ jobs: - name: Commit changelog and manifest files id: make-commit + env: + DPRINT_VERSION: 0.39.1 + RUST_TOOLCHAIN: 1.67 run: | - curl -fsSL https://dprint.dev/install.sh | sh + rustup component add rustfmt --toolchain "$RUST_TOOLCHAIN-x86_64-unknown-linux-gnu" + curl -fsSL https://dprint.dev/install.sh | sh -s $DPRINT_VERSION /home/runner/.dprint/bin/dprint fmt git add CHANGELOG.md Cargo.lock swap/Cargo.toml diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml index 37e04333..6e557033 100644 --- a/.github/workflows/preview-release.yml +++ b/.github/workflows/preview-release.yml @@ -10,7 +10,7 @@ jobs: name: Create preview release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@v4.0.0 - name: Delete 'preview' release uses: larryjoelane/delete-release-action@v1.0.24 diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f4730b..160c34a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.12.3] - 2023-09-20 + +- Swap: If no Monero daemon is manually specified, we will automatically choose one from a list of public daemons by connecting to each and checking their availability. + +## [0.12.2] - 2023-08-08 + ### Changed - Minimum Supported Rust Version (MSRV) bumped to 1.67 @@ -343,7 +349,9 @@ It is possible to migrate critical data from the old db to the sqlite but there - Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them. Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version. -[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.1...HEAD +[Unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.3...HEAD +[0.12.3]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.2...0.12.3 +[0.12.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.1...0.12.2 [0.12.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.0...0.12.1 [0.12.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...0.12.0 [0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0 diff --git a/Cargo.lock b/Cargo.lock index 1e5d878a..e3619fce 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" @@ -2324,15 +2404,44 @@ dependencies = [ ] [[package]] -name = "mio" -version = "0.8.4" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +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", @@ -4116,7 +4269,7 @@ checksum = "8049cf85f0e715d6af38dde439cb0ccb91f67fb9f5f63c80f8b43e48356e1a3f" [[package]] name = "swap" -version = "0.12.1" +version = "0.12.3" dependencies = [ "anyhow", "async-compression", @@ -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 7d0e1141..b2872469 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "swap" -version = "0.12.1" +version = "0.12.3" authors = [ "The COMIT guys " ] edition = "2021" description = "XMR/BTC trustless atomic swaps." @@ -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/bitcoin/wallet.rs b/swap/src/bitcoin/wallet.rs index 9a8500b6..2e5732f8 100644 --- a/swap/src/bitcoin/wallet.rs +++ b/swap/src/bitcoin/wallet.rs @@ -761,9 +761,10 @@ impl Client { self.blockchain.get_tx(txid) } - fn update_state(&mut self) -> Result<()> { + fn update_state(&mut self, force_sync: bool) -> Result<()> { let now = Instant::now(); - if now < self.last_sync + self.sync_interval { + + if !force_sync && now < self.last_sync + self.sync_interval { return Ok(()); } @@ -783,9 +784,14 @@ impl Client { if !self.script_history.contains_key(&script) { self.script_history.insert(script.clone(), vec![]); - } - self.update_state()?; + // When we first subscribe to a script we want to immediately fetch its status + // Otherwise we would have to wait for the next sync interval, which can take a minute + // This would result in potentially inaccurate status updates until that next sync interval is hit + self.update_state(true)?; + } else { + self.update_state(false)?; + } let history = self.script_history.entry(script).or_default(); 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.rs b/swap/src/monero/wallet.rs index c8110b02..eb747d5d 100644 --- a/swap/src/monero/wallet.rs +++ b/swap/src/monero/wallet.rs @@ -179,11 +179,6 @@ impl Wallet { pub async fn transfer(&self, request: TransferRequest) -> Result { let inner = self.inner.lock().await; - inner - .open_wallet(self.name.clone()) - .await - .with_context(|| format!("Failed to open wallet {}", self.name))?; - let TransferRequest { public_spend_key, public_view_key, 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()); + } +}