diff --git a/.github/workflows/build-release-binaries.yml b/.github/workflows/build-release-binaries.yml index d21ed1f2..d3832088 100644 --- a/.github/workflows/build-release-binaries.yml +++ b/.github/workflows/build-release-binaries.yml @@ -50,7 +50,7 @@ jobs: ref: ${{ github.event.release.target_commitish }} token: ${{ secrets.BOTTY_GITHUB_TOKEN }} - - uses: Swatinem/rust-cache@v1.4.0 + - uses: Swatinem/rust-cache@v2.0.0 - name: Install compiler for armhf arch if: matrix.target == 'armv7-unknown-linux-gnueabihf' @@ -67,7 +67,7 @@ jobs: run: target/${{ matrix.target }}/release/${{ matrix.bin }} --help # Remove once python 3 is the default - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.x" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bbff145..81330fd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,11 +15,10 @@ jobs: - name: Checkout sources uses: actions/checkout@v3.0.2 - - - uses: Swatinem/rust-cache@v1.4.0 + - uses: Swatinem/rust-cache@v2.0.0 - name: Check formatting - uses: dprint/check@v2.0 + uses: dprint/check@v2.1 - name: Run clippy with default features run: cargo clippy --workspace --all-targets -- -D warnings @@ -44,7 +43,7 @@ jobs: - name: Checkout sources uses: actions/checkout@v3.0.2 - - uses: Swatinem/rust-cache@v1.4.0 + - uses: Swatinem/rust-cache@v2.0.0 - name: Install compiler for armhf arch if: matrix.target == 'armv7-unknown-linux-gnueabihf' @@ -77,7 +76,7 @@ jobs: - name: Checkout sources uses: actions/checkout@v3.0.2 - - uses: Swatinem/rust-cache@v1.4.0 + - uses: Swatinem/rust-cache@v2.0.0 - name: Build tests run: cargo build --tests --workspace --all-features @@ -113,7 +112,7 @@ jobs: - name: Checkout sources uses: actions/checkout@v3.0.2 - - uses: Swatinem/rust-cache@v1.4.0 + - uses: Swatinem/rust-cache@v2.0.0 - name: Run test ${{ matrix.test_name }} run: cargo test --package swap --all-features --test ${{ matrix.test_name }} -- --nocapture diff --git a/.gitignore b/.gitignore index c0b0db30..905e678b 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,6 @@ flycheck_*.el .swap-db/ # End of https://www.toptal.com/developers/gitignore/api/rust,clion+all,emacs + +*.log +.vscode \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e66e84c9..24cd669d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,17 +10,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Change Monero nodes to [Rino tool nodes](https://community.rino.io/nodes.html) -- Revert logs to use rfc3339 local time formatting. - Update from monero v17.2.0 to monero v17.3.0 - Always write logs as JSON to files +- Change to UTC time for log messages, due to a bug causing no logging at all to be printed (linux/macos), and an [unsoundness issue](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/time/struct.LocalTime.html) with local time in [the time crate](https://github.com/time-rs/time/issues/293#issuecomment-748151025) +- Fix potential integer overflow in ASB when calculating maximum Bitcoin amount for Monero balance +- Reduce Monero locking transaction fee amount from 0.000030 to 0.000016 XMR, which is still double the current median fee as reported at [monero.how](https://www.monero.how/monero-transaction-fees) ### Added - Adjust quote based on Bitcoin balance. - If the max_buy_btc in the ASB config is higher than the available balance to trade it will return the max available balance discounting the locking fees for monero, in the case the balance is lower than the min_buy_btc config it will return 0 to the CLI. If the ASB returns a quote of 0 the CLI will not allow you continue with a trade. + If the max_buy_btc in the ASB config is higher than the available balance to trade, it will return the max available balance discounting the Monero locking fees. In the case the balance is lower than the min_buy_btc config it will return 0 to the CLI. If the ASB returns a quote of 0 the CLI will not allow you continue with a trade. - Reduce required confirmations for Bitcoin transactions from 2 to 1 - Update from monero v17.2.0 to monero v17.3.0 - Both the ASB and CLI now support the [Identify](https://github.com/libp2p/specs/blob/master/identify/README.md) protocol. This makes its version and network (testnet/mainnet) avaliable to others +- Display minimum BTC deposit required to cover the minimum quantity plus fee in the Swap CLI ## [0.10.2] - 2021-12-25 diff --git a/Cargo.lock b/Cargo.lock index 495cdfbd..0c7a9038 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" [[package]] name = "arrayref" @@ -113,9 +113,9 @@ checksum = "9d6e24d2cce90c53b948c46271bfb053e4bdc2db9b5d3f65e20f8cf28a1b7fc3" [[package]] name = "async-compression" -version = "0.3.12" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bf394cfbbe876f0ac67b13b6ca819f9c9f2fb9ec67223cceb1555fbab1c31a" +checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" dependencies = [ "bzip2", "futures-core", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.52" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ "proc-macro2", "quote", @@ -541,7 +541,6 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time 0.1.43", "winapi 0.3.9", ] @@ -860,9 +859,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ "darling_core", "darling_macro", @@ -870,9 +869,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", @@ -884,9 +883,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", @@ -925,9 +924,9 @@ dependencies = [ [[package]] name = "dialoguer" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d6b4fabcd9e97e1df1ae15395ac7e49fb144946a0d453959dc2696273b9da" +checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" dependencies = [ "console", "tempfile", @@ -1535,9 +1534,9 @@ checksum = "05842d0d43232b23ccb7060ecb0f0626922c21f30012e97b767b30afd4a5d4b9" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -2151,6 +2150,15 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + [[package]] name = "matches" version = "0.1.8" @@ -2264,7 +2272,7 @@ dependencies = [ "testcontainers 0.12.0", "tokio", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -2298,7 +2306,7 @@ dependencies = [ "rand 0.7.3", "testcontainers 0.12.0", "tokio", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -2513,9 +2521,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.7.2" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "opaque-debug" @@ -2645,11 +2653,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.27" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" dependencies = [ - "pin-project-internal 0.4.27", + "pin-project-internal 0.4.30", ] [[package]] @@ -2663,9 +2671,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "0.4.27" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" dependencies = [ "proc-macro2", "quote", @@ -2787,11 +2795,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -3184,9 +3192,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64 0.13.0", "bytes", @@ -3213,7 +3221,8 @@ dependencies = [ "tokio", "tokio-rustls 0.23.1", "tokio-socks", - "tokio-util 0.6.9", + "tokio-util 0.7.2", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -3249,9 +3258,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.23.1" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22dc69eadbf0ee2110b8d20418c0c6edbaefec2811c4963dc17b6344e11fe0f8" +checksum = "34a3bb58e85333f1ab191bf979104b586ebd77475bc6681882825f4532dfe87c" dependencies = [ "arrayvec 0.7.2", "num-traits", @@ -3260,9 +3269,9 @@ dependencies = [ [[package]] name = "rust_decimal_macros" -version = "1.23.1" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c70be9367d4bc095d10b48d41b819d09ed4dafc528765a144d32ed1d530654" +checksum = "d1467556c7c115165aa0346bcf45bc947203bcc880efad85a09ba24ea17926c4" dependencies = [ "quote", "rust_decimal", @@ -3341,9 +3350,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "0.3.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" dependencies = [ "base64 0.13.0", ] @@ -3373,7 +3382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ "futures", - "pin-project 0.4.27", + "pin-project 0.4.30", "static_assertions", ] @@ -3504,9 +3513,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] @@ -3533,9 +3542,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", @@ -3544,9 +3553,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "indexmap", "itoa 1.0.1", @@ -3568,20 +3577,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.12.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "946fa04a8ac43ff78a1f4b811990afb9ddbdf5890b46d6dda0ba1998230138b7" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ - "rustversion", "serde", "serde_with_macros", ] [[package]] name = "serde_with_macros" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c1fcca18d55d1763e1c16873c4bde0ac3ef75179a28c7b372917e0494625be" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling", "proc-macro2", @@ -3972,9 +3980,9 @@ checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" [[package]] name = "strum" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ "strum_macros 0.24.0", ] @@ -4068,22 +4076,22 @@ dependencies = [ "spectral", "sqlx", "structopt", - "strum 0.24.0", + "strum 0.24.1", "tempfile", "testcontainers 0.12.0", "thiserror", - "time 0.3.9", + "time 0.3.11", "tokio", "tokio-socks", "tokio-tar", "tokio-tungstenite", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "toml", "torut", "tracing", "tracing-appender", "tracing-futures", - "tracing-subscriber", + "tracing-subscriber 0.3.15", "url", "uuid", "vergen", @@ -4093,13 +4101,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.73" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -4180,18 +4188,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -4200,9 +4208,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ "once_cell", ] @@ -4219,9 +4227,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ "itoa 1.0.1", "libc", @@ -4376,9 +4384,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" dependencies = [ "bytes", "futures-core", @@ -4423,9 +4431,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if 1.0.0", "pin-project-lite 0.2.8", @@ -4435,13 +4443,13 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.1.2" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a" +checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ - "chrono", "crossbeam-channel", - "tracing-subscriber", + "time 0.3.11", + "tracing-subscriber 0.3.15", ] [[package]] @@ -4457,11 +4465,11 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.25" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfce9f3241b150f36e8e54bb561a742d5daa1a47b5dd9a5ce369fd4a4db2210" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -4490,9 +4498,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ "serde", "tracing-core", @@ -4507,12 +4515,30 @@ dependencies = [ "ansi_term 0.12.1", "chrono", "lazy_static", - "matchers", + "matchers 0.0.1", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +dependencies = [ + "ansi_term 0.12.1", + "matchers 0.1.0", + "once_cell", "regex", "serde", "serde_json", "sharded-slab", "thread_local", + "time 0.3.11", "tracing", "tracing-core", "tracing-log", @@ -4625,6 +4651,12 @@ dependencies = [ "matches", ] +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + [[package]] name = "unicode-normalization" version = "0.1.17" @@ -4707,9 +4739,9 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "uuid" -version = "1.0.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ "getrandom 0.2.6", "serde", @@ -4746,7 +4778,7 @@ dependencies = [ "git2", "rustversion", "thiserror", - "time 0.3.9", + "time 0.3.11", ] [[package]] diff --git a/docs/asb/README.md b/docs/asb/README.md index e39375a9..e0991d8d 100644 --- a/docs/asb/README.md +++ b/docs/asb/README.md @@ -48,7 +48,7 @@ For example: ```toml [network] -rendezvous_point = "/dnsaddr/rendezvous.coblox.tech/p2p/12D3KooWQUt9DkNZxEn2R5ymJzWj15MpG6mTW84kyd8vDaRZi46o" +rendezvous_point = "/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE" external_addresses = ["/dns4/example.com/tcp/9939"] ``` diff --git a/docs/cli/README.md b/docs/cli/README.md index 82de15d4..d95d90ef 100644 --- a/docs/cli/README.md +++ b/docs/cli/README.md @@ -100,7 +100,7 @@ OPTIONS: --tor-socks5-port Your local Tor socks5 proxy port [default: 9050] ``` -Running `swap --testnet list-sellers --rendezvous-point /dnsaddr/rendezvous.coblox.tech/p2p/12D3KooWQUt9DkNZxEn2R5ymJzWj15MpG6mTW84kyd8vDaRZi46o` will give you something like: +Running `swap --testnet list-sellers --rendezvous-point /dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE` will give you something like: ``` Connected to rendezvous point, discovering nodes in 'xmr-btc-swap-testnet' namespace ... diff --git a/docs/cli/discover_and_take.sh b/docs/cli/discover_and_take.sh index af250951..6aca6475 100755 --- a/docs/cli/discover_and_take.sh +++ b/docs/cli/discover_and_take.sh @@ -8,7 +8,7 @@ # 4th param: Your bech32 Bitcoin testnet address that will be used for any change output (e.g. refund scenario or when swapping an amount smaller than the transferred BTC) # # Example usage: -# discover_and_take.sh "PATH/TO/swap" "/dnsaddr/rendezvous.coblox.tech/p2p/12D3KooWQUt9DkNZxEn2R5ymJzWj15MpG6mTW84kyd8vDaRZi46o" "YOUR_XMR_STAGENET_ADDRESS" "YOUR_BECH32_BITCOIN_TESTNET_ADDRESS" +# discover_and_take.sh "PATH/TO/swap" "/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE" "YOUR_XMR_STAGENET_ADDRESS" "YOUR_BECH32_BITCOIN_TESTNET_ADDRESS" CLI_PATH=$1 RENDEZVOUS_POINT=$2 diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 34c0e775..cfa6e12d 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -60,11 +60,11 @@ tokio-util = { version = "0.7", features = [ "io", "codec" ] } toml = "0.5" torut = { version = "0.2", default-features = false, features = [ "v3", "control" ] } tracing = { version = "0.1", features = [ "attributes" ] } -tracing-appender = "0.1" +tracing-appender = "0.2" tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] } -tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "chrono", "tracing-log", "json" ] } +tracing-subscriber = { version = "0.3", default-features = false, features = [ "fmt", "ansi", "env-filter", "time", "tracing-log", "json" ] } url = { version = "2", features = [ "serde" ] } -uuid = { version = "1.0", features = [ "serde", "v4" ] } +uuid = { version = "1.1", features = [ "serde", "v4" ] } void = "1" [target.'cfg(not(windows))'.dependencies] diff --git a/swap/src/asb/event_loop.rs b/swap/src/asb/event_loop.rs index 1a06d95f..9248a324 100644 --- a/swap/src/asb/event_loop.rs +++ b/swap/src/asb/event_loop.rs @@ -326,11 +326,13 @@ where .ask() .context("Failed to compute asking price")?; - let max_bitcoin_for_monero = self - .monero_wallet - .get_balance() - .await? - .max_bitcoin_for_price(ask_price); + let xmr = self.monero_wallet.get_balance().await?; + + let max_bitcoin_for_monero = xmr.max_bitcoin_for_price(ask_price).ok_or_else(|| { + anyhow::anyhow!("Bitcoin price ({}) x Monero ({}) overflow", ask_price, xmr) + })?; + + tracing::debug!(%ask_price, %xmr, %max_bitcoin_for_monero); if min_buy > max_bitcoin_for_monero { tracing::warn!( diff --git a/swap/src/asb/tracing.rs b/swap/src/asb/tracing.rs index dc3f7cca..c21c1e70 100644 --- a/swap/src/asb/tracing.rs +++ b/swap/src/asb/tracing.rs @@ -1,6 +1,6 @@ use anyhow::Result; use tracing_subscriber::filter::LevelFilter; -use tracing_subscriber::fmt::time::ChronoLocal; +use tracing_subscriber::fmt::time::UtcTime; use tracing_subscriber::FmtSubscriber; pub fn init(level: LevelFilter, json_format: bool, timestamp: bool) -> Result<()> { @@ -14,7 +14,7 @@ pub fn init(level: LevelFilter, json_format: bool, timestamp: bool) -> Result<() .with_env_filter(format!("asb={},swap={}", level, level)) .with_writer(std::io::stderr) .with_ansi(is_terminal) - .with_timer(ChronoLocal::with_format("%F %T".to_owned())) + .with_timer(UtcTime::rfc_3339()) .with_target(false); match (json_format, timestamp) { diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index a1188759..2acfb61a 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -105,6 +105,8 @@ async fn main() -> Result<()> { let event_loop = tokio::spawn(event_loop.run()); let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size()); + let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount); + let (amount, fees) = match determine_btc_to_swap( json, event_loop_handle.request_quote(), @@ -112,6 +114,7 @@ async fn main() -> Result<()> { || bitcoin_wallet.balance(), max_givable, || bitcoin_wallet.sync(), + estimate_fee, ) .await { @@ -558,13 +561,14 @@ fn qr_code(value: &impl ToString) -> Result { Ok(qr_code) } -async fn determine_btc_to_swap( +async fn determine_btc_to_swap( json: bool, bid_quote: impl Future>, get_new_address: impl Future>, balance: FB, max_giveable_fn: FMG, sync: FS, + estimate_fee: FFE, ) -> Result<(bitcoin::Amount, bitcoin::Amount)> where TB: Future>, @@ -573,6 +577,8 @@ where FMG: Fn() -> TMG, TS: Future>, FS: Fn() -> TS, + FFE: Fn(bitcoin::Amount) -> TFE, + TFE: Future>, { tracing::debug!("Requesting quote"); let bid_quote = bid_quote.await?; @@ -600,8 +606,17 @@ where } loop { + let min_outstanding = bid_quote.min_quantity - max_giveable; + let min_fee = estimate_fee(min_outstanding).await?; + let min_deposit = min_outstanding + min_fee; + + tracing::info!( + "Deposit at least {} to cover the min quantity with fee!", + min_deposit + ); tracing::info!( %deposit_address, + %min_deposit, %max_giveable, %minimum_amount, %maximum_amount, @@ -633,9 +648,7 @@ where let balance = balance().await?; let fees = balance - max_giveable; - let max_accepted = bid_quote.max_quantity; - let btc_swap_amount = min(max_giveable, max_accepted); Ok((btc_swap_amount, fees)) @@ -668,6 +681,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .unwrap(); @@ -679,7 +693,8 @@ mod tests { assert_eq!( writer.captured(), r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.00000000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC + INFO swap: Deposit at least 0.00001000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC INFO swap: Received Bitcoin new_balance=0.00100000 BTC max_giveable=0.00090000 BTC " ); @@ -703,6 +718,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .unwrap(); @@ -714,14 +730,15 @@ mod tests { assert_eq!( writer.captured(), r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.00000000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC + INFO swap: Deposit at least 0.00001000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC INFO swap: Received Bitcoin new_balance=0.10010000 BTC max_giveable=0.10000000 BTC " ); } #[tokio::test] - async fn given_initial_balance_below_max_quantity_swaps_max_givable() { + async fn given_initial_balance_below_max_quantity_swaps_max_giveable() { let writer = capture_logs(LevelFilter::INFO); let givable = Arc::new(Mutex::new(MaxGiveable::new(vec![ Amount::from_btc(0.0049).unwrap(), @@ -738,6 +755,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .unwrap(); @@ -748,8 +766,7 @@ mod tests { assert_eq!((amount, fees), (expected_amount, expected_fees)); assert_eq!( writer.captured(), - r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC -" + " INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC\n" ); } @@ -771,6 +788,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .unwrap(); @@ -781,8 +799,7 @@ mod tests { assert_eq!((amount, fees), (expected_amount, expected_fees)); assert_eq!( writer.captured(), - r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC -" + " INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC\n" ); } @@ -804,6 +821,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .unwrap(); @@ -815,7 +833,8 @@ mod tests { assert_eq!( writer.captured(), r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.00000000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC + INFO swap: Deposit at least 0.01001000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.01001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC INFO swap: Received Bitcoin new_balance=0.01010000 BTC max_giveable=0.01000000 BTC " ); @@ -839,6 +858,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .unwrap(); @@ -850,7 +870,8 @@ mod tests { assert_eq!( writer.captured(), r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.00010000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC + INFO swap: Deposit at least 0.00991000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00991000 BTC max_giveable=0.00010000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC INFO swap: Received Bitcoin new_balance=0.01010000 BTC max_giveable=0.01000000 BTC " ); @@ -879,6 +900,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ), ) .await @@ -888,10 +910,12 @@ mod tests { assert_eq!( writer.captured(), r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.00000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC + INFO swap: Deposit at least 0.10001000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC INFO swap: Received Bitcoin new_balance=0.01010000 BTC max_giveable=0.01000000 BTC INFO swap: Deposited amount is less than `min_quantity` - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.01000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC + INFO swap: Deposit at least 0.09001000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.09001000 BTC max_giveable=0.01000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC " ); } @@ -925,6 +949,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ), ) .await @@ -934,14 +959,15 @@ mod tests { assert_eq!( writer.captured(), r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC - INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 max_giveable=0.00000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC + INFO swap: Deposit at least 0.10001000 BTC to cover the min quantity with fee! + INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC INFO swap: Received Bitcoin new_balance=0.21000000 BTC max_giveable=0.20000000 BTC " ); } #[tokio::test] - async fn given_bid_quote_max_amount_0_return_errorq() { + async fn given_bid_quote_max_amount_0_return_error() { let givable = Arc::new(Mutex::new(MaxGiveable::new(vec![ Amount::from_btc(0.0001).unwrap(), Amount::from_btc(0.01).unwrap(), @@ -957,6 +983,7 @@ mod tests { result.give() }, || async { Ok(()) }, + |_| async { Ok(Amount::from_sat(1000)) }, ) .await .err() diff --git a/swap/src/bitcoin/lock.rs b/swap/src/bitcoin/lock.rs index c9efff2b..2f195ff7 100644 --- a/swap/src/bitcoin/lock.rs +++ b/swap/src/bitcoin/lock.rs @@ -11,6 +11,7 @@ use bitcoin::Script; use serde::{Deserialize, Serialize}; const SCRIPT_SIZE: usize = 34; +const TX_LOCK_WEIGHT: usize = 485; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TxLock { @@ -161,6 +162,10 @@ impl TxLock { output: vec![tx_out], } } + + pub fn weight() -> usize { + TX_LOCK_WEIGHT + } } impl From for PartiallySignedTransaction { diff --git a/swap/src/cli/tracing.rs b/swap/src/cli/tracing.rs index 3bafc229..a1cd77fb 100644 --- a/swap/src/cli/tracing.rs +++ b/swap/src/cli/tracing.rs @@ -1,10 +1,11 @@ use anyhow::Result; use std::option::Option::Some; use std::path::Path; +use time::format_description::well_known::Rfc3339; use tracing::subscriber::set_global_default; use tracing::{Event, Level, Subscriber}; use tracing_subscriber::fmt::format::{DefaultFields, Format, JsonFields}; -use tracing_subscriber::fmt::time::ChronoLocal; +use tracing_subscriber::fmt::time::UtcTime; use tracing_subscriber::layer::{Context, SubscriberExt}; use tracing_subscriber::{fmt, EnvFilter, FmtSubscriber, Layer, Registry}; use uuid::Uuid; @@ -15,7 +16,8 @@ pub fn init(debug: bool, json: bool, dir: impl AsRef, swap_id: Option, swap_id: Option, swap_id: Option, swap_id: Option { @@ -79,25 +80,25 @@ type StdErrJsonLayer = tracing_subscriber::fmt::Layer< fn() -> std::io::Stderr, >; -fn debug_terminal_printer() -> StdErrPrinter> { +fn debug_terminal_printer() -> StdErrPrinter>> { let is_terminal = atty::is(atty::Stream::Stderr); StdErrPrinter { inner: fmt::layer() .with_ansi(is_terminal) .with_target(false) - .with_timer(ChronoLocal::with_format("%F %T".to_owned())) + .with_timer(UtcTime::rfc_3339()) .with_writer(std::io::stderr), level: Level::DEBUG, } } -fn debug_json_terminal_printer() -> StdErrPrinter> { +fn debug_json_terminal_printer() -> StdErrPrinter>> { let is_terminal = atty::is(atty::Stream::Stderr); StdErrPrinter { inner: fmt::layer() .with_ansi(is_terminal) .with_target(false) - .with_timer(ChronoLocal::with_format("%F %T".to_owned())) + .with_timer(UtcTime::rfc_3339()) .json() .with_writer(std::io::stderr), level: Level::DEBUG, diff --git a/swap/src/monero.rs b/swap/src/monero.rs index bf3045ce..94555c8a 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -81,8 +81,8 @@ pub struct PublicViewKey(PublicKey); #[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd)] pub struct Amount(u64); -// Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, XMR 0.000_015 * 2 (to be on the safe side) -pub const MONERO_FEE: Amount = Amount::from_piconero(30000000); +// Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, XMR 0.000_008 * 2 (to be on the safe side) +pub const MONERO_FEE: Amount = Amount::from_piconero(16_000_000); impl Amount { pub const ZERO: Self = Self(0); @@ -95,22 +95,30 @@ impl Amount { Amount(amount) } + /// Return Monero Amount as Piconero. pub fn as_piconero(&self) -> u64 { self.0 } - pub fn max_bitcoin_for_price(&self, ask_price: bitcoin::Amount) -> bitcoin::Amount { - let piconero_minus_fee = self.as_piconero().saturating_sub(MONERO_FEE.as_piconero()); + /// Calculate the maximum amount of Bitcoin that can be bought at a given + /// asking price for this amount of Monero including the median fee. + pub fn max_bitcoin_for_price(&self, ask_price: bitcoin::Amount) -> Option { + let pico_minus_fee = self.as_piconero().saturating_sub(MONERO_FEE.as_piconero()); - if piconero_minus_fee == 0 { - return bitcoin::Amount::ZERO; + if pico_minus_fee == 0 { + return Some(bitcoin::Amount::ZERO); } - // There needs to be an offset for difference in zeroes beetween Piconeros and - // Satoshis - let piconero_calc = (piconero_minus_fee * ask_price.as_sat()) / PICONERO_OFFSET; + // safely convert the BTC/XMR rate to sat/pico + let ask_sats = Decimal::from(ask_price.as_sat()); + let pico_per_xmr = Decimal::from(PICONERO_OFFSET); + let ask_sats_per_pico = ask_sats / pico_per_xmr; - bitcoin::Amount::from_sat(piconero_calc) + let pico = Decimal::from(pico_minus_fee); + let max_sats = pico.checked_mul(ask_sats_per_pico)?; + let satoshi = max_sats.to_u64()?; + + Some(bitcoin::Amount::from_sat(satoshi)) } pub fn from_monero(amount: f64) -> Result { @@ -375,27 +383,88 @@ mod tests { } #[test] - fn geting_max_bitcoin_to_trade() { - let amount = Amount::parse_monero("10").unwrap(); - let bitcoin_price_sats = bitcoin::Amount::from_sat(382_900); + fn max_bitcoin_to_trade() { + // sanity check: if the asking price is 1 BTC / 1 XMR + // and we have μ XMR + fee + // then max BTC we can buy is μ + let ask = bitcoin::Amount::from_btc(1.0).unwrap(); - let monero_max_from_bitcoin = amount.max_bitcoin_for_price(bitcoin_price_sats); + let xmr = Amount::parse_monero("1.0").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); - assert_eq!( - bitcoin::Amount::from_sat(3_828_988), - monero_max_from_bitcoin - ); + assert_eq!(btc, bitcoin::Amount::from_btc(1.0).unwrap()); + + let xmr = Amount::parse_monero("0.5").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_btc(0.5).unwrap()); + + let xmr = Amount::parse_monero("2.5").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_btc(2.5).unwrap()); + + let xmr = Amount::parse_monero("420").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_btc(420.0).unwrap()); + + let xmr = Amount::parse_monero("0.00001").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_btc(0.00001).unwrap()); + + // other ask prices + + let ask = bitcoin::Amount::from_btc(0.5).unwrap(); + let xmr = Amount::parse_monero("2").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_btc(1.0).unwrap()); + + let ask = bitcoin::Amount::from_btc(2.0).unwrap(); + let xmr = Amount::parse_monero("1").unwrap() + MONERO_FEE; + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_btc(2.0).unwrap()); + + let ask = bitcoin::Amount::from_sat(382_900); + let xmr = Amount::parse_monero("10").unwrap(); + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_sat(3_828_993)); + + // example from https://github.com/comit-network/xmr-btc-swap/issues/1084 + // with rate from kraken at that time + let ask = bitcoin::Amount::from_sat(685_800); + let xmr = Amount::parse_monero("0.826286435921").unwrap(); + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(btc, bitcoin::Amount::from_sat(566_656)); } #[test] - fn geting_max_bitcoin_to_trade_with_balance_smaller_than_locking_fee() { - let monero = "0.00001"; - let amount = Amount::parse_monero(monero).unwrap(); - let bitcoin_price_sats = bitcoin::Amount::from_sat(382_900); + fn max_bitcoin_to_trade_overflow() { + let xmr = Amount::from_monero(30.0).unwrap(); + let ask = bitcoin::Amount::from_sat(728_688); + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); + + assert_eq!(bitcoin::Amount::from_sat(21_860_628), btc); - let monero_max_from_bitcoin = amount.max_bitcoin_for_price(bitcoin_price_sats); + let xmr = Amount::from_piconero(u64::MAX); + let ask = bitcoin::Amount::from_sat(u64::MAX); + let btc = xmr.max_bitcoin_for_price(ask); + + assert!(btc.is_none()); + } + + #[test] + fn geting_max_bitcoin_to_trade_with_balance_smaller_than_locking_fee() { + let ask = bitcoin::Amount::from_sat(382_900); + let xmr = Amount::parse_monero("0.00001").unwrap(); + let btc = xmr.max_bitcoin_for_price(ask).unwrap(); - assert_eq!(bitcoin::Amount::ZERO, monero_max_from_bitcoin); + assert_eq!(bitcoin::Amount::ZERO, btc); } use rand::rngs::OsRng; diff --git a/swap/src/monero/wallet.rs b/swap/src/monero/wallet.rs index 859b451b..0d3d3389 100644 --- a/swap/src/monero/wallet.rs +++ b/swap/src/monero/wallet.rs @@ -314,8 +314,13 @@ async fn wait_for_confirmations proof, - Err(jsonrpc::Error::JsonRpc(jsonrpc::JsonRpcError { code: -1, .. })) => { - tracing::warn!(%txid, "`monero-wallet-rpc` failed to fetch transaction, may need to be restarted"); + Err(jsonrpc::Error::JsonRpc(jsonrpc::JsonRpcError { + code: -1, + message, + data, + })) => { + tracing::debug!(message, ?data); + tracing::warn!(%txid, message, "`monero-wallet-rpc` failed to fetch transaction, may need to be restarted"); continue; } // TODO: Implement this using a generic proxy for each function call once https://github.com/thomaseizinger/rust-jsonrpc-client/issues/47 is fixed. diff --git a/swap/src/tracing_ext.rs b/swap/src/tracing_ext.rs index 912187fd..6fd7eaba 100644 --- a/swap/src/tracing_ext.rs +++ b/swap/src/tracing_ext.rs @@ -41,7 +41,7 @@ impl MakeCapturingWriter { } } -impl MakeWriter for MakeCapturingWriter { +impl<'a> MakeWriter<'a> for MakeCapturingWriter { type Writer = CapturingWriter; fn make_writer(&self) -> Self::Writer {