Compare commits

...

332 Commits

Author SHA1 Message Date
dependabot[bot] 1930540c1f
build(deps): bump reqwest from 0.11.27 to 0.12.4 (#1588)
* build(deps): bump reqwest from 0.11.27 to 0.12.0

Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.27 to 0.12.0.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.27...v0.12.0)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* wip

* wip

* ci: lock sqlx-cli install

* bump reqwest to 0.12.2

* deps: reqwest to 0.12.4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Byron Hambly <byron@hambly.dev>
3 days ago
COMIT Botty McBotface 2932abc9ec
Prepare release 0.13.0 (#1659) 4 days ago
binarybaron 41687ffab9
CI: Fix faulty x86-darwin release name (#1658) 4 days ago
Byron Hambly 6c487d4a5f
Merge pull request #1654 from comit-network/dependabot/cargo/serde-1.0.203
build(deps): bump serde from 1.0.202 to 1.0.203
5 days ago
Byron Hambly 21e6bbe681
Merge pull request #1656 from delta1/ci-binaries
ci: wip fixing binaries
5 days ago
Byron Hambly e09401b9f7
ci: wip fixing build binaries actions 5 days ago
Byron Hambly 81d1f7e1bb
Merge pull request #1655 from delta1/ci-binaries
ci: wip fixing build binaries actions
5 days ago
Byron Hambly 66bc59892a
ci: wip fixing build binaries actions 5 days ago
dependabot[bot] 6399343de9
build(deps): bump serde from 1.0.202 to 1.0.203
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.202 to 1.0.203.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.202...v1.0.203)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 days ago
Byron Hambly 7b9ca3b420
Merge pull request #1653 from delta1/deps-miniscript
deps: bump miniscript 9.0.0 to 9.0.2 to fix overflow issue
6 days ago
Byron Hambly 268a24083f
deps: bump miniscript 9.0.0 to 9.0.2 to fix overflow issue 6 days ago
Byron Hambly 1d4a536bb1
Merge pull request #1652 from delta1/ci-release-bins
ci: fix build-release-binaries
6 days ago
Byron Hambly 52d56ae254
ci: fix build-release-binaries 6 days ago
Byron Hambly 2ef2863e89
Merge pull request #1651 from delta1/ci-release-bins
ci: fix build-release-binaries
6 days ago
Byron Hambly bbf8f84312
ci: fix build-release-binaries 6 days ago
Byron Hambly a471a17a88
Merge pull request #1648 from delta1/ci-fix-stable
ci: fix duplicate definition of check_stable
6 days ago
Byron Hambly e6d37c01a6
ci: fix duplicate definition of check_stable 6 days ago
Byron Hambly 796863359f
upgrade secp256kfun (#1466)
* ci: add cargo check on rust stable

* refactor: upgrade secp256kfun and fix resulting issues

* build(deps): update sigma_fun and ecdsa_fun to a52142cf7f

 #1520
 #1521

* chore: fix clippy issue

* update to 91112f80b24

* bump to 294de1721add

* chore(deps): remove spectral

spectral fails to compile on rust stable 1.76 due to dep on deprecated
rustc-serialize

* secp256kfun: update to 7da9d277 and set rev in manifest

* update to 6fdc5d8

* switch to crates.io versions of ecdsa_fun and sigma_fun

* ci: update toolchain to 1.74 and fix draft action

* clippy fixes

---------

Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com>
6 days ago
Byron Hambly 1e2dddb61b
Merge pull request #1629 from comit-network/dependabot/cargo/toml-0.8.13
build(deps): bump toml from 0.8.12 to 0.8.13
6 days ago
Byron Hambly dc8e1e53f2
Merge pull request #1647 from comit-network/monero-wallet-rpc-update-01831
CLI: Upgrade monero-wallet-rpc to 0.18.3.1, clarify a few log. messages
6 days ago
binarybaron 32ca0b1a4a CLI: Upgrade monero-wallet-rpc to 0.18.3.1, clarify a few log. messages 1 week ago
binarybaron c433bd2389
CLI: Initiate tracing earlier to avoid lost logs (#1646) 1 week ago
binarybaron 0ca98cd0b7
Make tracing file appender blocking (#1643) 1 week ago
binarybaron 2f28ef9401
Add missing changelog entries (#1641) 1 week ago
binarybaron 0c4b7d50c2
Lower DEFAULT_BITCOIN_CONFIRMATION_TARGET to 1 to ensure timely confirmation of bitcoin transactions (#1640) 1 week ago
Byron Hambly 838a7ab5fb
Merge pull request #1637 from comit-network/dependabot/github_actions/actions/checkout-4.1.6
build(deps): bump actions/checkout from 4.1.1 to 4.1.6
1 week ago
Byron Hambly db78a1e99e
Merge pull request #1636 from comit-network/dependabot/github_actions/Swatinem/rust-cache-2.7.3
build(deps): bump Swatinem/rust-cache from 2.7.1 to 2.7.3
1 week ago
dependabot[bot] b443a96469
build(deps): bump actions/checkout from 4.1.1 to 4.1.6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.1...v4.1.6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 week ago
dependabot[bot] 97210739a1
build(deps): bump Swatinem/rust-cache from 2.7.1 to 2.7.3
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.7.1 to 2.7.3.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.7.1...v2.7.3)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 week ago
yamabiiko 5ff46be279
RPC server for API Interface (#1276)
* saving: implementing internal api shared by cli and rpc server

* writing async rpc methods and using arc for shared struct references

* cleaning up, renamed Init to Context

* saving: cleaning up and initial work for tests

* Respond with bitcoin withdraw txid

* Print RPC server address

* Cleanup, formatting, add `get_seller`, `get_swap_start_date` RPC endpoints

* fixing tests in cli module

* uncommenting and fixing more tests

* split api module and propagate errors with rpc server

* moving methods to api and validating addresses for rpc

* add broadcast channel to handle shutdowns gracefully and prepare for RPC server test

* added files

* Update rpc.rs

* adding new unfinished RPC tests

* updating rpc-server tests

* fixing warnings

* fixing formatting and cargo clippy warnings

* fix missing import in test

* fix: add data_dir to config to make config command work

* set server listen address manually and return file locations in JSON on Config

* Add called api method and swap_id to tracing for context, reduced boilerplate

* Pass server_address properly to RpcServer

* Update Cargo.lock

* dprint fmt

* Add cancel_refund RPC endpoint

* Combine Cmd and Params

* Disallow concurrent swaps

* Use RwLock instead of Mutex to allow for parallel reads and add get_current_swap endpoint

* Return wallet descriptor to RPC API caller

* Append all cli logs to single log file

After careful consideration, I've concluded that it's not practical/possible to ensure that the previous behaviour (one log file per swap) is preserved due to limitations of the tracing-subscriber crate and a big in the built in JSON formatter

* Add get_swap_expired_timelock timelock, other small refactoring

- Add get_swap_expired_timelock endpoint to return expired timelock if one exists. Fails if bitcoin lock tx has not yet published or if swap is already finished.
- Rename current_epoch to expired_timelock to enforce consistent method names
- Add blocks left until current expired timelock expires (next timelock expires) to ExpiredTimelock struct
- Change .expect() to .unwrap() in rpc server method register because those will only fail if we register the same method twice which will never happen

* initiating swaps in a separate task and handling shutdown signals with broadcast queues

* Replace get_swap_start_date, get_seller, get_expired_timelock with one get_swap_info rpc method

* WIP: Struct for concurrent swaps manager

* Ensure correct tracing spans

* Add note regarding Request, Method structs

* Update request.rs

* Add tracing span attribute log_reference_id to logs caused by rpc call

* Sync bitcoin wallet before initial max_giveable call

* use Span::current() to pass down to tracing span to spawned tasks

* Remove unused shutdown channel

* Add `get_monero_recovery_info` RPC endpoint

- Add `get_monero_recovery_info` RPC endpoint
- format PrivateViewKey using Display

* Rename `Method::RawHistory` to `Method::GetRawStates`

* Wait for swap to be suspended after sending signal

* Remove notes

* Add tracing span attribute log_reference_id to logs caused by rpc call

* Sync bitcoin wallet before initial max_giveable call

* use Span::current() to pass down to tracing span to spawned tasks

* Remove unused shutdown channel

* Add `get_monero_recovery_info` RPC endpoint

- Add `get_monero_recovery_info` RPC endpoint
- format PrivateViewKey using Display

* Rename `Method::RawHistory` to `Method::GetRawStates`

* Wait for swap to be suspended after sending signal

* Return additonal info on GetSwapInfo

* Update wallet.rs

* fix compile issues for tests and use serial_test crate

* fix rpc tests, only check for RPC errors and not returned values

* Rename `get_raw_history` tp `get_raw_states`

* Fix typo in rpc server stopped tracing log

* Remove unnecessary success property on suspend_current_swap response

* fixing test_cli_arguments and other tests

* WIP: RPC server integration tests

* WIP: Integration tests for RPC server

* Update rpc tests

* fix compile and warnings in tests/rpc.rs

* test: fix assert

* clippy --fix

* remove otp file

* cargo clippy fixes

* move resume swap initialization code out of spawned task

* Use `in_current_span` to pass down tracing span to spawned tasks

* moving buy_xmr initialization code out of spawned tasks

* cargo fmt

* Moving swap initialization code inside tokio select block to handle swap lock release logic

* Remove unnecessary swap suspension listener from determine_btc_to_swap call in BuyXmr

* Spawn event loop before requesting quote

* Release swap lock after receiving shutdown signal

* Remove inner tokio::select in BuyXmr and Resume

* Improve debug text for swap resume

* Return error to API caller if bid quote request fails

* Print error if one occurs during process invoked by API call

* Return bid quote to API caller

* Use type safe query! macro for database retrieval of states

* Return tx_lock_fee to API caller on GetSwapInfo call

Update request.rs

* Allow API caller to retrieve last synced bitcoin balane and avoid costly sync

* Return restore height on MoneroRecovery command to API Caller

* Include entire error cause-chain in API response

* Add span to bitcoin wallet logs

* Log event loop connection properties as tracing fields

* Wait for background tasks to complete before exiting CLI

* clippy

* specify sqlx patch version explicitly

* remove mem::forget and replace with _guard

* ci: add rpc test job

* test: wrap rpc test in #[cfg(test)]

* add missing tokio::test attribute

* fix and merge rpc tests, parse uuuid and multiaddr from serde_json value

* default Tor socks port to 9050, Cargo fmt

* Update swap/sqlite_dev_setup.sh: add version

Co-authored-by: Byron Hambly <byron@hambly.dev>

* ci: free up space on ubuntu test job

* Update swap/src/bitcoin/wallet.rs

Co-authored-by: Byron Hambly <byron@hambly.dev>

* Update swap/src/bitcoin/wallet.rs

Co-authored-by: Byron Hambly <byron@hambly.dev>

* fmt

---------

Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com>
Co-authored-by: Byron Hambly <byron@hambly.dev>
2 weeks ago
Byron Hambly 7d4a6c90f3
Merge pull request #1633 from comit-network/dependabot/cargo/anyhow-1.0.86
build(deps): bump anyhow from 1.0.83 to 1.0.86
2 weeks ago
Byron Hambly 2fba218429
Merge pull request #1632 from comit-network/dependabot/cargo/thiserror-1.0.61
build(deps): bump thiserror from 1.0.60 to 1.0.61
2 weeks ago
dependabot[bot] 928fe33532
build(deps): bump anyhow from 1.0.83 to 1.0.86
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.83 to 1.0.86.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.83...1.0.86)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 weeks ago
dependabot[bot] 3bb6f060a0
build(deps): bump thiserror from 1.0.60 to 1.0.61
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.60 to 1.0.61.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.60...1.0.61)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 weeks ago
icy-ux 8d8c938a94
document use of asb export-bitcoin-wallet (#1280)
* document use of asb export-bitcoin-wallet

Add to the `asb` document @delta1's text on exporting the Bitcoin wallet
descriptor and importing the descriptor into Sparrow Wallet.

TODO: maybe it would make sense to add the images to Git, instead of
linking to files on Github?

* bring asb/README.md into compliance with dprint

* update bullet point formatting in asb/README.md

* run

* add images to git

---------

Co-authored-by: icyfestive <>
Co-authored-by: Byron Hambly <byron@hambly.dev>
2 weeks ago
Byron Hambly 0b5a26a5e8
Merge pull request #1631 from comit-network/dependabot/cargo/itertools-0.13.0
build(deps): bump itertools from 0.12.1 to 0.13.0
2 weeks ago
dependabot[bot] 0e5241787f
build(deps): bump itertools from 0.12.1 to 0.13.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.1 to 0.13.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.1...v0.13.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2 weeks ago
Byron Hambly 4b8f7987f7
Merge pull request #1630 from comit-network/dependabot/github_actions/actions/checkout-4.1.6
build(deps): bump actions/checkout from 4.1.5 to 4.1.6
2 weeks ago
dependabot[bot] c8e5768955
build(deps): bump actions/checkout from 4.1.5 to 4.1.6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.5 to 4.1.6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.5...v4.1.6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 weeks ago
dependabot[bot] 6edcc28b8d
build(deps): bump toml from 0.8.12 to 0.8.13
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.12 to 0.8.13.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.12...toml-v0.8.13)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 weeks ago
Byron Hambly 8cd0d3cd17
Merge pull request #1628 from comit-network/dependabot/cargo/serde-1.0.202
build(deps): bump serde from 1.0.201 to 1.0.202
3 weeks ago
dependabot[bot] d9de1b6cf3
build(deps): bump serde from 1.0.201 to 1.0.202
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.201 to 1.0.202.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.201...v1.0.202)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 weeks ago
Byron Hambly 0a2a32a31b
Merge pull request #1627 from comit-network/dependabot/cargo/serde-1.0.201
build(deps): bump serde from 1.0.200 to 1.0.201
4 weeks ago
Byron Hambly 71ea249d73
Merge pull request #1626 from comit-network/dependabot/cargo/serde_json-1.0.117
build(deps): bump serde_json from 1.0.116 to 1.0.117
4 weeks ago
dependabot[bot] 081bb965ee
build(deps): bump serde from 1.0.200 to 1.0.201
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.200 to 1.0.201.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.200...v1.0.201)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
dependabot[bot] f1d7859334
build(deps): bump serde_json from 1.0.116 to 1.0.117
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.116 to 1.0.117.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.116...v1.0.117)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
Byron Hambly 6acabda6ab
Merge pull request #1623 from comit-network/dependabot/github_actions/actions/checkout-4.1.5
build(deps): bump actions/checkout from 4.1.4 to 4.1.5
4 weeks ago
Byron Hambly 38c8fd76a4
Merge pull request #1624 from comit-network/dependabot/cargo/anyhow-1.0.83
build(deps): bump anyhow from 1.0.82 to 1.0.83
4 weeks ago
Byron Hambly 84b5a6f7e9
Merge pull request #1625 from comit-network/dependabot/cargo/thiserror-1.0.60
build(deps): bump thiserror from 1.0.59 to 1.0.60
4 weeks ago
dependabot[bot] 252c394ef6
build(deps): bump thiserror from 1.0.59 to 1.0.60
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.59 to 1.0.60.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.59...1.0.60)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
dependabot[bot] d40059192a
build(deps): bump anyhow from 1.0.82 to 1.0.83
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.82 to 1.0.83.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.82...1.0.83)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
dependabot[bot] ed8deeff57
build(deps): bump actions/checkout from 4.1.4 to 4.1.5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.4 to 4.1.5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.4...v4.1.5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
Byron Hambly 98b5335231
Merge pull request #1622 from comit-network/dependabot/cargo/tokio-util-0.7.11
build(deps): bump tokio-util from 0.7.10 to 0.7.11
4 weeks ago
Byron Hambly 417bfe34a5
Merge pull request #1621 from comit-network/dependabot/github_actions/thomaseizinger/create-pull-request-1.4.0
build(deps): bump thomaseizinger/create-pull-request from 1.3.1 to 1.4.0
4 weeks ago
dependabot[bot] 7661b19300
build(deps): bump tokio-util from 0.7.10 to 0.7.11
Bumps [tokio-util](https://github.com/tokio-rs/tokio) from 0.7.10 to 0.7.11.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-util-0.7.10...tokio-util-0.7.11)

---
updated-dependencies:
- dependency-name: tokio-util
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
dependabot[bot] ca5bfed392
build(deps): bump thomaseizinger/create-pull-request from 1.3.1 to 1.4.0
Bumps [thomaseizinger/create-pull-request](https://github.com/thomaseizinger/create-pull-request) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/thomaseizinger/create-pull-request/releases)
- [Changelog](https://github.com/thomaseizinger/create-pull-request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thomaseizinger/create-pull-request/compare/1.3.1...1.4.0)

---
updated-dependencies:
- dependency-name: thomaseizinger/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
4 weeks ago
Byron Hambly 58b775bdf2
Merge pull request #1620 from comit-network/dependabot/cargo/serde-1.0.200
build(deps): bump serde from 1.0.199 to 1.0.200
1 month ago
dependabot[bot] cc3abf647c
build(deps): bump serde from 1.0.199 to 1.0.200
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.199 to 1.0.200.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.199...v1.0.200)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
Byron Hambly 73bb1b88c9
Merge pull request #1619 from comit-network/dependabot/cargo/base64-0.22.1
build(deps): bump base64 from 0.22.0 to 0.22.1
1 month ago
dependabot[bot] ad6b00beec
build(deps): bump base64 from 0.22.0 to 0.22.1
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.22.0 to 0.22.1.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.22.0...v0.22.1)

---
updated-dependencies:
- dependency-name: base64
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
Byron Hambly 61a19c35a7
Merge pull request #1616 from comit-network/dependabot/cargo/serde-1.0.199
build(deps): bump serde from 1.0.198 to 1.0.199
1 month ago
Byron Hambly 026fcd13f2
Merge pull request #1617 from comit-network/dependabot/cargo/data-encoding-2.6.0
build(deps): bump data-encoding from 2.5.0 to 2.6.0
1 month ago
dependabot[bot] 1aca4462e7
build(deps): bump serde from 1.0.198 to 1.0.199
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.198 to 1.0.199.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.198...v1.0.199)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
dependabot[bot] 79b4d8efbc
build(deps): bump data-encoding from 2.5.0 to 2.6.0
Bumps [data-encoding](https://github.com/ia0/data-encoding) from 2.5.0 to 2.6.0.
- [Commits](https://github.com/ia0/data-encoding/commits)

---
updated-dependencies:
- dependency-name: data-encoding
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
Byron Hambly 0beb91f537
Merge pull request #1618 from delta1/ci-macos
ci: fix macos-latest and add macos-12
1 month ago
Byron Hambly 24466c0234
ci: fix macos-latest and add macos-12
macos-latest is now aarch64, add macos-12 for x64_64
1 month ago
Byron Hambly 2b850924b1
Merge pull request #1615 from comit-network/dependabot/github_actions/actions/checkout-4.1.4
build(deps): bump actions/checkout from 4.1.3 to 4.1.4
1 month ago
dependabot[bot] 8a88bde4f5
build(deps): bump actions/checkout from 4.1.3 to 4.1.4
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
Byron Hambly 1fecc687a7
Merge pull request #1614 from comit-network/dependabot/github_actions/actions/checkout-4.1.3
build(deps): bump actions/checkout from 4.1.2 to 4.1.3
1 month ago
Byron Hambly afa683b7e0
Merge pull request #1613 from comit-network/dependabot/cargo/thiserror-1.0.59
build(deps): bump thiserror from 1.0.58 to 1.0.59
1 month ago
dependabot[bot] 5997453525
build(deps): bump actions/checkout from 4.1.2 to 4.1.3
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.2...v4.1.3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
dependabot[bot] e389027439
build(deps): bump thiserror from 1.0.58 to 1.0.59
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.59.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.59)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
1 month ago
Byron Hambly aeefd8f271
Merge pull request #1612 from comit-network/dependabot/cargo/serde-1.0.198
build(deps): bump serde from 1.0.197 to 1.0.198
2 months ago
Byron Hambly effeabd007
Merge pull request #1611 from comit-network/dependabot/cargo/hyper-1.3.1
build(deps): bump hyper from 1.3.0 to 1.3.1
2 months ago
dependabot[bot] 1ff67ec943
build(deps): bump serde from 1.0.197 to 1.0.198
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.197 to 1.0.198.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.197...v1.0.198)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
dependabot[bot] 6ba11b1738
build(deps): bump hyper from 1.3.0 to 1.3.1
Bumps [hyper](https://github.com/hyperium/hyper) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly 18ec158d2e
Merge pull request #1609 from comit-network/dependabot/cargo/serde_json-1.0.116
build(deps): bump serde_json from 1.0.115 to 1.0.116
2 months ago
Byron Hambly b0dffc5370
Merge pull request #1610 from comit-network/dependabot/cargo/hyper-1.3.0
build(deps): bump hyper from 1.2.0 to 1.3.0
2 months ago
dependabot[bot] 76c72cceb2
build(deps): bump serde_json from 1.0.115 to 1.0.116
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.115 to 1.0.116.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.115...v1.0.116)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
dependabot[bot] f0f197de20
build(deps): bump hyper from 1.2.0 to 1.3.0
Bumps [hyper](https://github.com/hyperium/hyper) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly f380d0928e
Merge pull request #1603 from delta1/ci-check-stable
ci: add cargo check job on stable rust
2 months ago
Byron Hambly 877001549a
Merge pull request #1607 from comit-network/dependabot/cargo/async-trait-0.1.80
build(deps): bump async-trait from 0.1.79 to 0.1.80
2 months ago
dependabot[bot] 23216dea07
build(deps): bump async-trait from 0.1.79 to 0.1.80
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.79 to 0.1.80.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.79...0.1.80)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly a250940ff1
Merge pull request #1606 from comit-network/dependabot/cargo/time-0.3.36
build(deps): bump time from 0.3.34 to 0.3.36
2 months ago
dependabot[bot] a449d9b600
build(deps): bump time from 0.3.34 to 0.3.36
Bumps [time](https://github.com/time-rs/time) from 0.3.34 to 0.3.36.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.34...v0.3.36)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly 0072fbe0c6
Merge pull request #1604 from comit-network/dependabot/cargo/anyhow-1.0.82
build(deps): bump anyhow from 1.0.81 to 1.0.82
2 months ago
dependabot[bot] 4767c68467
build(deps): bump anyhow from 1.0.81 to 1.0.82
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.81...1.0.82)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly 0d63087ab9
ci: add cargo check job on stable rust 2 months ago
Byron Hambly d230ed2705
Merge pull request #1600 from comit-network/dependabot/github_actions/thomaseizinger/keep-a-changelog-new-release-3.0.0
build(deps): bump thomaseizinger/keep-a-changelog-new-release from 2.0.0 to 3.0.0
2 months ago
Byron Hambly 19705becc5
Merge pull request #1601 from comit-network/dependabot/cargo/pem-3.0.4
build(deps): bump pem from 3.0.3 to 3.0.4
2 months ago
dependabot[bot] cf0b9aa601
build(deps): bump pem from 3.0.3 to 3.0.4
Bumps [pem](https://github.com/jcreekmore/pem-rs) from 3.0.3 to 3.0.4.
- [Changelog](https://github.com/jcreekmore/pem-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jcreekmore/pem-rs/compare/v3.0.3...v3.0.4)

---
updated-dependencies:
- dependency-name: pem
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
dependabot[bot] a87ffaa631
build(deps): bump thomaseizinger/keep-a-changelog-new-release
Bumps [thomaseizinger/keep-a-changelog-new-release](https://github.com/thomaseizinger/keep-a-changelog-new-release) from 2.0.0 to 3.0.0.
- [Release notes](https://github.com/thomaseizinger/keep-a-changelog-new-release/releases)
- [Changelog](https://github.com/thomaseizinger/keep-a-changelog-new-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thomaseizinger/keep-a-changelog-new-release/compare/2.0.0...3.0.0)

---
updated-dependencies:
- dependency-name: thomaseizinger/keep-a-changelog-new-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly bad0117e58
Merge pull request #1590 from comit-network/dependabot/cargo/async-trait-0.1.79
build(deps): bump async-trait from 0.1.78 to 0.1.79
2 months ago
Byron Hambly b67ee29bf7
Merge branch 'master' into dependabot/cargo/async-trait-0.1.79 2 months ago
Byron Hambly f76dee4c30
Merge pull request #1595 from comit-network/dependabot/cargo/serde_json-1.0.115
build(deps): bump serde_json from 1.0.114 to 1.0.115
2 months ago
Byron Hambly af70589eba
Merge pull request #1599 from comit-network/dependabot/cargo/comfy-table-7.1.1
build(deps): bump comfy-table from 7.1.0 to 7.1.1
2 months ago
dependabot[bot] 73370ce7d9
build(deps): bump comfy-table from 7.1.0 to 7.1.1
Bumps [comfy-table](https://github.com/nukesor/comfy-table) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/nukesor/comfy-table/releases)
- [Changelog](https://github.com/Nukesor/comfy-table/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nukesor/comfy-table/compare/v7.1.0...v7.1.1)

---
updated-dependencies:
- dependency-name: comfy-table
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly 2c27bb8f68
Merge pull request #1598 from comit-network/dependabot/cargo/port_check-0.2.1
build(deps): bump port_check from 0.1.5 to 0.2.1
2 months ago
dependabot[bot] c231a34399
build(deps): bump port_check from 0.1.5 to 0.2.1
Bumps [port_check](https://github.com/ufoscout/port-check-rs) from 0.1.5 to 0.2.1.
- [Commits](https://github.com/ufoscout/port-check-rs/commits/v0.2.1)

---
updated-dependencies:
- dependency-name: port_check
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly 4801098601
Merge pull request #1597 from comit-network/dependabot/cargo/tokio-1.37.0
build(deps): bump tokio from 1.36.0 to 1.37.0
2 months ago
dependabot[bot] be8b3c1dde
build(deps): bump tokio from 1.36.0 to 1.37.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.36.0...tokio-1.37.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
dependabot[bot] f575a93bbb
build(deps): bump serde_json from 1.0.114 to 1.0.115
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.114 to 1.0.115.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.114...v1.0.115)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
dependabot[bot] 870911bd58
build(deps): bump async-trait from 0.1.78 to 0.1.79
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.78 to 0.1.79.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.78...0.1.79)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly bd07bde535
Merge pull request #1594 from comit-network/monero-wallet-refresh-fix
Monero wallet refresh fix redux
2 months ago
Byron Hambly 91460a29cd
Merge branch 'master' into monero-wallet-refresh-fix 2 months ago
Byron Hambly d6cac9fc1b
Merge pull request #1593 from comit-network/revert-1487-monero-wallet-refresh-fix
Revert "Monero wallet refresh fix (#1487)"
2 months ago
Byron Hambly f337708877
Merge branch 'master' into monero-wallet-refresh-fix 2 months ago
Byron Hambly 36edac8ae3
Merge branch 'master' into revert-1487-monero-wallet-refresh-fix 2 months ago
Byron Hambly 50ff0b1e91
Merge pull request #1592 from comit-network/ci-sqlx-locked
ci: lock install version for sqlx-cli
2 months ago
Byron Hambly 9d426066a9
ci: lock install version for sqlx-cli 2 months ago
Byron Hambly a19501a002
Revert "Monero wallet refresh fix (#1487)"
This reverts commit d8dacbdee9.
2 months ago
binarybaron d8dacbdee9
Monero wallet refresh fix (#1487)
* Upgrade monero-wallet-rpc to `v0.18.3.1`

* Give feedback to user about state of monero refresh and retry if fails

This commit changes the following behaviour in the refresh functionality of the monero wallet
- Allows for multiple retries because in some cases users have experienced an issue where the wallet rpc returns `no connection to daemon` even though the daemon is available. I'm not 100% sure why this happens but retrying often fixes the issue
- Attempt to print the current sync height while the wallet is syncing. This only works to some degree because the `monero-wallet-rpc` stops responding (or takes a long time to respond) while it's refreshing
- The `monero-wallet-rpc` is started with the `--no-initial-sync` flag which ensures that as soon as it's started, it's ready to respond to requests
---------

Co-authored-by: Byron Hambly <bhambly@blockstream.com>
Co-authored-by: Byron Hambly <byron@hambly.dev>
2 months ago
binarybaron bfc1e829dc Update CHANGELOG.md 2 months ago
binarybaron 85cee51eee Run dprint fmt 2 months ago
binarybaron 8eaf437d74
Merge branch 'master' into monero-wallet-refresh-fix 2 months ago
Ian McKenzie eb15f477fa
Verify hashes of monero cli on download (#1572)
* Bump Monero CLI for macos aarch64 to match other platforms

* Check hash on download of monero cli

* change panic to bail

---------

Co-authored-by: Byron Hambly <bhambly@blockstream.com>
2 months ago
Byron Hambly c0d3a02beb
Merge pull request #1587 from comit-network/dependabot/cargo/reqwest-0.11.27
build(deps): bump reqwest from 0.11.26 to 0.11.27
2 months ago
dependabot[bot] 1901594d28
build(deps): bump reqwest from 0.11.26 to 0.11.27
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.26 to 0.11.27.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.26...v0.11.27)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly 06bbb89631
Merge pull request #1585 from comit-network/dependabot/cargo/uuid-1.8.0
build(deps): bump uuid from 1.7.0 to 1.8.0
2 months ago
Byron Hambly 993e68edca
Merge pull request #1586 from comit-network/dependabot/cargo/toml-0.8.12
build(deps): bump toml from 0.8.11 to 0.8.12
2 months ago
dependabot[bot] 5d1b9cf4c3
build(deps): bump toml from 0.8.11 to 0.8.12
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.11 to 0.8.12.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.11...toml-v0.8.12)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
dependabot[bot] d77f208aae
build(deps): bump uuid from 1.7.0 to 1.8.0
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.7.0...1.8.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2 months ago
Byron Hambly d059ed8644
Merge pull request #1584 from comit-network/dependabot/cargo/async-trait-0.1.78
build(deps): bump async-trait from 0.1.77 to 0.1.78
3 months ago
dependabot[bot] 3b83822a71
build(deps): bump async-trait from 0.1.77 to 0.1.78
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.77 to 0.1.78.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.77...0.1.78)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly b11733dea9
Merge pull request #1582 from comit-network/dependabot/cargo/qrcode-0.14.0
build(deps): bump qrcode from 0.13.0 to 0.14.0
3 months ago
dependabot[bot] fea34cc35b
build(deps): bump qrcode from 0.13.0 to 0.14.0
Bumps [qrcode](https://github.com/kennytm/qrcode-rust) from 0.13.0 to 0.14.0.
- [Release notes](https://github.com/kennytm/qrcode-rust/releases)
- [Commits](https://github.com/kennytm/qrcode-rust/commits)

---
updated-dependencies:
- dependency-name: qrcode
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly c69acfbeb1
Merge pull request #1581 from comit-network/dependabot/cargo/reqwest-0.11.26
build(deps): bump reqwest from 0.11.25 to 0.11.26
3 months ago
dependabot[bot] a61f7809d9
build(deps): bump reqwest from 0.11.25 to 0.11.26
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.25 to 0.11.26.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.25...v0.11.26)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly 98920d97e4
Merge pull request #1579 from comit-network/dependabot/cargo/thiserror-1.0.58
build(deps): bump thiserror from 1.0.57 to 1.0.58
3 months ago
Byron Hambly 2cde21a5a1
Merge pull request #1578 from comit-network/dependabot/cargo/anyhow-1.0.81
build(deps): bump anyhow from 1.0.80 to 1.0.81
3 months ago
Byron Hambly bb1df6b987
Merge pull request #1577 from comit-network/dependabot/cargo/toml-0.8.11
build(deps): bump toml from 0.8.10 to 0.8.11
3 months ago
Byron Hambly d1b10efdee
Merge pull request #1576 from comit-network/dependabot/github_actions/actions/checkout-4.1.2
build(deps): bump actions/checkout from 4.1.1 to 4.1.2
3 months ago
dependabot[bot] 46eee5ccfc
build(deps): bump anyhow from 1.0.80 to 1.0.81
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.80 to 1.0.81.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.80...1.0.81)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
dependabot[bot] ccff348cdd
build(deps): bump toml from 0.8.10 to 0.8.11
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.10 to 0.8.11.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.10...toml-v0.8.11)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
dependabot[bot] bf262acf00
build(deps): bump thiserror from 1.0.57 to 1.0.58
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.57 to 1.0.58.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.57...1.0.58)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
dependabot[bot] 28937b8693
build(deps): bump actions/checkout from 4.1.1 to 4.1.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.1...v4.1.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly c05597970d
Merge pull request #1580 from comit-network/delta1-patch-1
ci: specify version of sqlx-cli
3 months ago
Byron Hambly 82eac08bfe
ci: specify version of sqlx-cli 3 months ago
Byron Hambly efc06e7401
Merge pull request #1574 from comit-network/dependabot/cargo/strum-0.26.2
build(deps): bump strum from 0.26.1 to 0.26.2
3 months ago
Byron Hambly d15a4476f9
Merge pull request #1575 from comit-network/dependabot/cargo/reqwest-0.11.25
build(deps): bump reqwest from 0.11.24 to 0.11.25
3 months ago
dependabot[bot] 6af198743a
build(deps): bump reqwest from 0.11.24 to 0.11.25
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.24 to 0.11.25.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.24...v0.11.25)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
dependabot[bot] 397aa972bb
build(deps): bump strum from 0.26.1 to 0.26.2
Bumps [strum](https://github.com/Peternator7/strum) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly 81fabdd451
Merge pull request #1570 from comit-network/dependabot/cargo/base64-0.22.0
build(deps): bump base64 from 0.21.7 to 0.22.0
3 months ago
dependabot[bot] bb596fcc09
build(deps): bump base64 from 0.21.7 to 0.22.0
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.21.7 to 0.22.0.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.7...v0.22.0)

---
updated-dependencies:
- dependency-name: base64
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly 76a59e8d85
Merge pull request #1566 from comit-network/dependabot/cargo/tempfile-3.10.1
build(deps): bump tempfile from 3.10.0 to 3.10.1
3 months ago
dependabot[bot] 3037bbcbe4
build(deps): bump tempfile from 3.10.0 to 3.10.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.10.0 to 3.10.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.10.0...v3.10.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
dependabot[bot] 00abded1c5
build(deps): bump mockito from 1.2.0 to 1.3.0 (#1561)
* build(deps): bump mockito from 1.2.0 to 1.3.0

Bumps [mockito](https://github.com/lipanski/mockito) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/lipanski/mockito/releases)
- [Commits](https://github.com/lipanski/mockito/compare/1.2.0...1.3.0)

---
updated-dependencies:
- dependency-name: mockito
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): update http to 0.2.11

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Byron Hambly <bhambly@blockstream.com>
3 months ago
Ian McKenzie b2ca1b5f8c
Upgrade testcontainers from 0.12 to 0.14 (#1552)
* Upgrade testcontainers from 0.12 to 0.14

* minor cleanup

---------

Co-authored-by: Byron Hambly <bhambly@blockstream.com>
3 months ago
Byron Hambly 9c5914ff7a
Merge pull request #1562 from comit-network/dependabot/cargo/hyper-1.2.0
build(deps): bump hyper from 1.1.0 to 1.2.0
3 months ago
dependabot[bot] 005361c833
build(deps): bump hyper from 1.1.0 to 1.2.0
Bumps [hyper](https://github.com/hyperium/hyper) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.1.0...v1.2.0)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly c3b474d7db
Merge pull request #1559 from comit-network/dependabot/cargo/serde_json-1.0.114
build(deps): bump serde_json from 1.0.113 to 1.0.114
3 months ago
Byron Hambly abc0897948
Merge pull request #1560 from comit-network/dependabot/cargo/serde-1.0.197
build(deps): bump serde from 1.0.196 to 1.0.197
3 months ago
dependabot[bot] 344440bb54
build(deps): bump serde from 1.0.196 to 1.0.197
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.196 to 1.0.197.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.196...v1.0.197)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
dependabot[bot] d8d1c4178e
build(deps): bump serde_json from 1.0.113 to 1.0.114
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.113 to 1.0.114.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.113...v1.0.114)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly d3797bf50a
Merge pull request #1558 from comit-network/dependabot/cargo/anyhow-1.0.80
build(deps): bump anyhow from 1.0.79 to 1.0.80
3 months ago
dependabot[bot] 2c11f38659
build(deps): bump anyhow from 1.0.79 to 1.0.80
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.79 to 1.0.80.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.79...1.0.80)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
3 months ago
Byron Hambly 82e22f0eea
Merge pull request #1554 from comit-network/dependabot/github_actions/thomaseizinger/keep-a-changelog-new-release-2.0.0
build(deps): bump thomaseizinger/keep-a-changelog-new-release from 1.3.0 to 2.0.0
4 months ago
Byron Hambly fbe1244392
Merge pull request #1553 from comit-network/dependabot/cargo/thiserror-1.0.57
build(deps): bump thiserror from 1.0.56 to 1.0.57
4 months ago
dependabot[bot] 640290023a
build(deps): bump thiserror from 1.0.56 to 1.0.57
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.56...1.0.57)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] 3d2b51998e
build(deps): bump thomaseizinger/keep-a-changelog-new-release
Bumps [thomaseizinger/keep-a-changelog-new-release](https://github.com/thomaseizinger/keep-a-changelog-new-release) from 1.3.0 to 2.0.0.
- [Release notes](https://github.com/thomaseizinger/keep-a-changelog-new-release/releases)
- [Changelog](https://github.com/thomaseizinger/keep-a-changelog-new-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thomaseizinger/keep-a-changelog-new-release/compare/1.3.0...2.0.0)

---
updated-dependencies:
- dependency-name: thomaseizinger/keep-a-changelog-new-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Byron Hambly f12cf36364
ci: free up space on ubuntu test job (#1555)
* ci: free up space on ubuntu test job

(cherry picked from commit d234d2335f)

* fixup! ci: free up space on ubuntu test job

* ci use commit hash instead of malleable tag for free space action
4 months ago
binarybaron 2bc8d0062e Merge branch 'monero-wallet-refresh-fix' of https://github.com/comit-network/xmr-btc-swap into monero-wallet-refresh-fix 4 months ago
Byron Hambly 1507a0787a
Merge pull request #1548 from comit-network/dependabot/cargo/tempfile-3.10.0
build(deps): bump tempfile from 3.9.0 to 3.10.0
4 months ago
Byron Hambly 0c7e84449a
Merge pull request #1551 from comit-network/dependabot/cargo/toml-0.8.10
build(deps): bump toml from 0.8.9 to 0.8.10
4 months ago
dependabot[bot] 039192aeb6
build(deps): bump toml from 0.8.9 to 0.8.10
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.9 to 0.8.10.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.9...toml-v0.8.10)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] b1a72940df
build(deps): bump tempfile from 3.9.0 to 3.10.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.9.0 to 3.10.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.9.0...v3.10.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Byron Hambly 0dcf6d887c
Merge branch 'master' into monero-wallet-refresh-fix 4 months ago
Byron Hambly 4cfef2f1fd
Merge pull request #1544 from comit-network/dependabot/cargo/time-0.3.34
build(deps): bump time from 0.3.32 to 0.3.34
4 months ago
Byron Hambly dd30566c26
Merge branch 'master' into monero-wallet-refresh-fix 4 months ago
Byron Hambly ecad2150c7
Merge pull request #1545 from comit-network/dependabot/cargo/tokio-1.36.0
build(deps): bump tokio from 1.35.1 to 1.36.0
4 months ago
dependabot[bot] 1b0a6d22fe
build(deps): bump tokio from 1.35.1 to 1.36.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.35.1 to 1.36.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.1...tokio-1.36.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] ac2a13b1d0
build(deps): bump time from 0.3.32 to 0.3.34
Bumps [time](https://github.com/time-rs/time) from 0.3.32 to 0.3.34.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.32...v0.3.34)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Ian McKenzie 140dc6dc04
Upgrade to vergen 8.3, move to git describe --dirty --tags for semver (#1543)
* Upgrade to vergen 8.3, move to git describe --dirty --tags for semver

* Add newline to build.rs
4 months ago
Byron Hambly 4a758f7c8a
Merge pull request #1540 from comit-network/dependabot/cargo/config-0.14.0
build(deps): bump config from 0.13.4 to 0.14.0
4 months ago
Byron Hambly 11517a64c9
Merge pull request #1541 from comit-network/dependabot/cargo/time-0.3.32
build(deps): bump time from 0.3.31 to 0.3.32
4 months ago
Byron Hambly fb4468a458
Merge pull request #1542 from comit-network/dependabot/cargo/reqwest-0.11.24
build(deps): bump reqwest from 0.11.23 to 0.11.24
4 months ago
Byron Hambly ac15963e13
Merge pull request #1539 from comit-network/dependabot/cargo/toml-0.8.9
build(deps): bump toml from 0.8.8 to 0.8.9
4 months ago
dependabot[bot] e08770f405
build(deps): bump reqwest from 0.11.23 to 0.11.24
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.23 to 0.11.24.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.23...v0.11.24)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] 9a024db955
build(deps): bump time from 0.3.31 to 0.3.32
Bumps [time](https://github.com/time-rs/time) from 0.3.31 to 0.3.32.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.31...v0.3.32)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] 83080abe30
build(deps): bump config from 0.13.4 to 0.14.0
Bumps [config](https://github.com/mehcode/config-rs) from 0.13.4 to 0.14.0.
- [Changelog](https://github.com/mehcode/config-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mehcode/config-rs/compare/v0.13.4...0.14.0)

---
updated-dependencies:
- dependency-name: config
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] 45d6326dc1
build(deps): bump toml from 0.8.8 to 0.8.9
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.8 to 0.8.9.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.8...toml-v0.8.9)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Byron Hambly 4fd52b74e8
Merge pull request #1538 from comit-network/dependabot/cargo/itertools-0.12.1
build(deps): bump itertools from 0.12.0 to 0.12.1
4 months ago
dependabot[bot] db7fe0dc99
build(deps): bump itertools from 0.12.0 to 0.12.1
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.0 to 0.12.1.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.0...v0.12.1)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Byron Hambly ab3720d51a
Merge pull request #1525 from comit-network/dependabot/cargo/serial_test-3.0.0
build(deps): bump serial_test from 2.0.0 to 3.0.0
4 months ago
Byron Hambly 14d83b1792
Merge pull request #1536 from comit-network/dependabot/cargo/strum-0.26.1
build(deps): bump strum from 0.25.0 to 0.26.1
4 months ago
Byron Hambly 8ff8c95228
Merge pull request #1537 from comit-network/dependabot/cargo/serde-1.0.196
build(deps): bump serde from 1.0.195 to 1.0.196
4 months ago
Byron Hambly 779fe93279
Merge pull request #1535 from comit-network/dependabot/cargo/serde_json-1.0.113
build(deps): bump serde_json from 1.0.111 to 1.0.113
4 months ago
dependabot[bot] 9d9e408ee0
build(deps): bump serde from 1.0.195 to 1.0.196
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.195 to 1.0.196.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.195...v1.0.196)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] 1ad6af6d5c
build(deps): bump strum from 0.25.0 to 0.26.1
Bumps [strum](https://github.com/Peternator7/strum) from 0.25.0 to 0.26.1.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
dependabot[bot] 44875e2274
build(deps): bump serde_json from 1.0.111 to 1.0.113
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.111 to 1.0.113.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.111...v1.0.113)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Byron Hambly b6453315c2
Merge pull request #1533 from comit-network/dependabot/cargo/uuid-1.7.0
build(deps): bump uuid from 1.6.1 to 1.7.0
4 months ago
dependabot[bot] fada511c9a
build(deps): bump uuid from 1.6.1 to 1.7.0
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.6.1...1.7.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
4 months ago
Byron Hambly 6f60117aa4
Merge pull request #1532 from comit-network/dependabot/github_actions/Swatinem/rust-cache-2.7.3
build(deps): bump Swatinem/rust-cache from 2.7.2 to 2.7.3
5 months ago
dependabot[bot] 85712aa95f
build(deps): bump Swatinem/rust-cache from 2.7.2 to 2.7.3
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.7.2 to 2.7.3.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.7.2...v2.7.3)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 06bfada867
Merge pull request #1530 from comit-network/dependabot/cargo/base64-0.21.7
build(deps): bump base64 from 0.21.6 to 0.21.7
5 months ago
dependabot[bot] b945fa3ee6
build(deps): bump base64 from 0.21.6 to 0.21.7
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.21.6 to 0.21.7.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.6...v0.21.7)

---
updated-dependencies:
- dependency-name: base64
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly da86cf4f58
Merge pull request #1528 from comit-network/dependabot/github_actions/Swatinem/rust-cache-2.7.2
build(deps): bump Swatinem/rust-cache from 2.7.1 to 2.7.2
5 months ago
dependabot[bot] aedabcbcbb
build(deps): bump Swatinem/rust-cache from 2.7.1 to 2.7.2
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.7.1 to 2.7.2.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.7.1...v2.7.2)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly d7a19f1450
Merge pull request #1527 from comit-network/dependabot/cargo/base64-0.21.6
build(deps): bump base64 from 0.21.5 to 0.21.6
5 months ago
dependabot[bot] f50ee16bf1
build(deps): bump base64 from 0.21.5 to 0.21.6
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.21.5 to 0.21.6.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.5...v0.21.6)

---
updated-dependencies:
- dependency-name: base64
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 979e97c40a
Merge pull request #1526 from comit-network/dependabot/cargo/serde-1.0.195
build(deps): bump serde from 1.0.194 to 1.0.195
5 months ago
dependabot[bot] afe0060c4a
build(deps): bump serde from 1.0.194 to 1.0.195
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.194 to 1.0.195.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.194...v1.0.195)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] dd9d5246c3
build(deps): bump serial_test from 2.0.0 to 3.0.0
Bumps [serial_test](https://github.com/palfrey/serial_test) from 2.0.0 to 3.0.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v2.0.0...v3.0.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly e5c776e52e
Merge pull request #1519 from comit-network/dependabot/cargo/anyhow-1.0.79
build(deps): bump anyhow from 1.0.78 to 1.0.79
5 months ago
Byron Hambly 47d9313e5c
Merge pull request #1522 from comit-network/dependabot/cargo/serde_json-1.0.111
build(deps): bump serde_json from 1.0.110 to 1.0.111
5 months ago
dependabot[bot] 57d19ad852
build(deps): bump serde_json from 1.0.110 to 1.0.111
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.110 to 1.0.111.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.110...v1.0.111)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly f88ba46323
Merge pull request #1518 from comit-network/dependabot/cargo/serde_json-1.0.110
build(deps): bump serde_json from 1.0.109 to 1.0.110
5 months ago
Byron Hambly e868d6b2b3
Merge pull request #1517 from comit-network/dependabot/cargo/thiserror-1.0.56
build(deps): bump thiserror from 1.0.53 to 1.0.56
5 months ago
Byron Hambly 72cab2fed1
Merge pull request #1516 from comit-network/dependabot/cargo/async-trait-0.1.77
build(deps): bump async-trait from 0.1.76 to 0.1.77
5 months ago
Byron Hambly 5381117a92
Merge pull request #1515 from comit-network/dependabot/cargo/serde-1.0.194
build(deps): bump serde from 1.0.193 to 1.0.194
5 months ago
dependabot[bot] b31779dfe1
build(deps): bump anyhow from 1.0.78 to 1.0.79
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.78 to 1.0.79.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.78...1.0.79)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] 10ba9203fa
build(deps): bump serde_json from 1.0.109 to 1.0.110
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.109 to 1.0.110.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.109...v1.0.110)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] 36c4e4fa6e
build(deps): bump thiserror from 1.0.53 to 1.0.56
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.53 to 1.0.56.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.53...1.0.56)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] ed2d0c9ddc
build(deps): bump async-trait from 0.1.76 to 0.1.77
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.76 to 0.1.77.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/commits)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] d675626477
build(deps): bump serde from 1.0.193 to 1.0.194
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.193 to 1.0.194.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.193...v1.0.194)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 81bb856808
Merge pull request #1514 from comit-network/dependabot/cargo/serde_json-1.0.109
build(deps): bump serde_json from 1.0.108 to 1.0.109
5 months ago
Byron Hambly dfe02beb00
Merge pull request #1513 from comit-network/dependabot/cargo/thiserror-1.0.53
build(deps): bump thiserror from 1.0.52 to 1.0.53
5 months ago
Byron Hambly 0264c0edb1
Merge pull request #1512 from comit-network/dependabot/cargo/anyhow-1.0.78
build(deps): bump anyhow from 1.0.77 to 1.0.78
5 months ago
Byron Hambly 717b960d53
Merge pull request #1511 from comit-network/dependabot/cargo/async-trait-0.1.76
build(deps): bump async-trait from 0.1.75 to 0.1.76
5 months ago
dependabot[bot] 81119af523
build(deps): bump serde_json from 1.0.108 to 1.0.109
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.108 to 1.0.109.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.108...v1.0.109)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] 5de4d1dfa7
build(deps): bump thiserror from 1.0.52 to 1.0.53
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.52 to 1.0.53.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.52...1.0.53)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] 1c8c9034c0
build(deps): bump anyhow from 1.0.77 to 1.0.78
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.77 to 1.0.78.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.77...1.0.78)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] 4f6f6b8054
build(deps): bump async-trait from 0.1.75 to 0.1.76
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.75 to 0.1.76.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.75...0.1.76)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 8387fb8c3e
Merge pull request #1510 from comit-network/dependabot/cargo/tempfile-3.9.0
build(deps): bump tempfile from 3.8.1 to 3.9.0
5 months ago
Byron Hambly a8991f571e
Merge branch 'master' into monero-wallet-refresh-fix 5 months ago
binarybaron bd3e6136ce Update wallet.rs 5 months ago
binarybaron 48abcd5b43 Add extra log message before opening redeem XMR wallet on Bob 5 months ago
binarybaron 07101deab1 Unify monero-wallet-rpc downloader logging 5 months ago
binarybaron 9e33e8b1d1 Give feedback to user about state of monero refresh and retry if fails
This commit changes the following behaviour in the refresh functionality of the monero wallet
- Allows for multiple retries because in some cases users have experienced an issue where the wallet rpc returns `no connection to daemon` even though the daemon is available. I'm not 100% sure why this happens but retrying often fixes the issue
- Attempt to print the current sync height while the wallet is syncing. This only works to some degree because the `monero-wallet-rpc` stops responding (or takes a long time to respond) while it's refreshing
- The `monero-wallet-rpc` is started with the `--no-initial-sync` flag which ensures that as soon as it's started, it's ready to respond to requests
5 months ago
dependabot[bot] 05b460a2c9
build(deps): bump tempfile from 3.8.1 to 3.9.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.8.1 to 3.9.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.8.1...v3.9.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 8704d263c5
Merge pull request #1509 from comit-network/dependabot/cargo/anyhow-1.0.77
build(deps): bump anyhow from 1.0.76 to 1.0.77
5 months ago
dependabot[bot] 844bf23ed5
build(deps): bump anyhow from 1.0.76 to 1.0.77
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.76 to 1.0.77.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.76...1.0.77)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 1d860b2794
Merge pull request #1508 from comit-network/dependabot/cargo/thiserror-1.0.52
build(deps): bump thiserror from 1.0.51 to 1.0.52
5 months ago
dependabot[bot] 0f68415129
build(deps): bump thiserror from 1.0.51 to 1.0.52
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.51 to 1.0.52.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.51...1.0.52)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 3ec7f78c79
Merge pull request #1507 from comit-network/dependabot/cargo/futures-0.3.30
build(deps): bump futures from 0.3.29 to 0.3.30
5 months ago
dependabot[bot] 2026bb9fdf
build(deps): bump futures from 0.3.29 to 0.3.30
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.29 to 0.3.30.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.29...0.3.30)

---
updated-dependencies:
- dependency-name: futures
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 9279af837f
Merge pull request #1505 from comit-network/dependabot/cargo/anyhow-1.0.76
build(deps): bump anyhow from 1.0.75 to 1.0.76
5 months ago
Byron Hambly 012260e361
Merge pull request #1506 from comit-network/dependabot/cargo/async-trait-0.1.75
build(deps): bump async-trait from 0.1.74 to 0.1.75
5 months ago
dependabot[bot] 712327ed03
build(deps): bump async-trait from 0.1.74 to 0.1.75
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.74 to 0.1.75.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.74...0.1.75)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
dependabot[bot] f29ff00feb
build(deps): bump anyhow from 1.0.75 to 1.0.76
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.76.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.76)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 771809a226
Merge pull request #1504 from comit-network/dependabot/cargo/tokio-1.35.1
build(deps): bump tokio from 1.35.0 to 1.35.1
5 months ago
Byron Hambly 7f2b265ceb
Merge pull request #1503 from comit-network/dependabot/cargo/reqwest-0.11.23
build(deps): bump reqwest from 0.11.22 to 0.11.23
5 months ago
dependabot[bot] 3b9fbc866d
build(deps): bump tokio from 1.35.0 to 1.35.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.35.0 to 1.35.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.0...tokio-1.35.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
5 months ago
Byron Hambly 7f2a0334d7
Merge pull request #1502 from comit-network/dependabot/cargo/time-0.3.31
build(deps): bump time from 0.3.30 to 0.3.31
6 months ago
Byron Hambly 0f7a415a6a
Merge pull request #1501 from comit-network/dependabot/cargo/async-trait-0.1.74
build(deps): bump async-trait from 0.1.68 to 0.1.74
6 months ago
Byron Hambly 89312fe09d
Merge pull request #1500 from comit-network/dependabot/cargo/toml-0.8.8
build(deps): bump toml from 0.7.6 to 0.8.8
6 months ago
Byron Hambly bdd8231a92
Merge pull request #1499 from comit-network/dependabot/cargo/hyper-1.1.0
build(deps): bump hyper from 1.0.1 to 1.1.0
6 months ago
dependabot[bot] 3298313d51
build(deps): bump reqwest from 0.11.22 to 0.11.23
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.22 to 0.11.23.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.22...v0.11.23)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 3d88102cd9
build(deps): bump time from 0.3.30 to 0.3.31
Bumps [time](https://github.com/time-rs/time) from 0.3.30 to 0.3.31.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.30...v0.3.31)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] c9757caf1e
build(deps): bump async-trait from 0.1.68 to 0.1.74
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.68 to 0.1.74.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.68...0.1.74)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] cf89da7e84
build(deps): bump toml from 0.7.6 to 0.8.8
Bumps [toml](https://github.com/toml-rs/toml) from 0.7.6 to 0.8.8.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.7.6...toml-v0.8.8)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 38ba2fc387
build(deps): bump hyper from 1.0.1 to 1.1.0
Bumps [hyper](https://github.com/hyperium/hyper) from 1.0.1 to 1.1.0.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.0.1...v1.1.0)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly ec9b162585
Merge pull request #1477 from comit-network/dependabot/cargo/dialoguer-0.11.0
build(deps): bump dialoguer from 0.10.4 to 0.11.0
6 months ago
Byron Hambly d68fccba56
Merge pull request #1495 from comit-network/dependabot/cargo/futures-0.3.29
build(deps): bump futures from 0.3.28 to 0.3.29
6 months ago
Byron Hambly 336fb658f3
Merge pull request #1498 from comit-network/dependabot/cargo/rust_decimal_macros-1.30.0
build(deps): bump rust_decimal_macros from 1.29.1 to 1.30.0
6 months ago
Byron Hambly 298d44fa09
Merge pull request #1497 from comit-network/dependabot/cargo/itertools-0.12.0
build(deps): bump itertools from 0.10.5 to 0.12.0
6 months ago
Byron Hambly e6a3157913
Merge pull request #1496 from comit-network/dependabot/cargo/tracing-appender-0.2.3
build(deps): bump tracing-appender from 0.2.2 to 0.2.3
6 months ago
Byron Hambly b813679b91
Merge pull request #1494 from comit-network/dependabot/cargo/thiserror-1.0.51
build(deps): bump thiserror from 1.0.50 to 1.0.51
6 months ago
dependabot[bot] 85b5c40384
build(deps): bump rust_decimal_macros from 1.29.1 to 1.30.0
Bumps [rust_decimal_macros](https://github.com/paupino/rust-decimal) from 1.29.1 to 1.30.0.
- [Release notes](https://github.com/paupino/rust-decimal/releases)
- [Changelog](https://github.com/paupino/rust-decimal/blob/master/CHANGELOG.md)
- [Commits](https://github.com/paupino/rust-decimal/compare/1.29.1...1.30.0)

---
updated-dependencies:
- dependency-name: rust_decimal_macros
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 46d93b67cb
build(deps): bump itertools from 0.10.5 to 0.12.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.5 to 0.12.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.5...v0.12.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 839d74be05
build(deps): bump tracing-appender from 0.2.2 to 0.2.3
Bumps [tracing-appender](https://github.com/tokio-rs/tracing) from 0.2.2 to 0.2.3.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-appender-0.2.2...tracing-appender-0.2.3)

---
updated-dependencies:
- dependency-name: tracing-appender
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 07db057b34
build(deps): bump futures from 0.3.28 to 0.3.29
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.28 to 0.3.29.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.28...0.3.29)

---
updated-dependencies:
- dependency-name: futures
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] ab43a51fbf
build(deps): bump thiserror from 1.0.50 to 1.0.51
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.50 to 1.0.51.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.50...1.0.51)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly f5c2d63527
Merge pull request #1493 from comit-network/dependabot/cargo/mockito-1.2.0
build(deps): bump mockito from 1.1.0 to 1.2.0
6 months ago
Byron Hambly cab4dc0a6a
Merge pull request #1488 from comit-network/dependabot/github_actions/actions/upload-artifact-4
build(deps): bump actions/upload-artifact from 3 to 4
6 months ago
Byron Hambly 9cd080f026
Merge pull request #1492 from comit-network/dependabot/cargo/tempfile-3.8.1
build(deps): bump tempfile from 3.8.0 to 3.8.1
6 months ago
Byron Hambly df7aff938f
Merge pull request #1491 from comit-network/dependabot/cargo/qrcode-0.13.0
build(deps): bump qrcode from 0.12.0 to 0.13.0
6 months ago
Byron Hambly c2d71f1616
Merge pull request #1490 from comit-network/dependabot/cargo/config-0.13.4
build(deps): bump config from 0.13.3 to 0.13.4
6 months ago
Byron Hambly c8412761e0
Merge pull request #1489 from comit-network/dependabot/cargo/time-0.3.30
build(deps): bump time from 0.3.24 to 0.3.30
6 months ago
dependabot[bot] 55f95c970e
build(deps): bump mockito from 1.1.0 to 1.2.0
Bumps [mockito](https://github.com/lipanski/mockito) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/lipanski/mockito/releases)
- [Commits](https://github.com/lipanski/mockito/compare/1.1.0...1.2.0)

---
updated-dependencies:
- dependency-name: mockito
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 4d200f0c78
build(deps): bump tempfile from 3.8.0 to 3.8.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.8.0 to 3.8.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/commits)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] e44134c96d
build(deps): bump qrcode from 0.12.0 to 0.13.0
Bumps [qrcode](https://github.com/kennytm/qrcode-rust) from 0.12.0 to 0.13.0.
- [Release notes](https://github.com/kennytm/qrcode-rust/releases)
- [Commits](https://github.com/kennytm/qrcode-rust/commits)

---
updated-dependencies:
- dependency-name: qrcode
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] b4eb49e6ce
build(deps): bump config from 0.13.3 to 0.13.4
Bumps [config](https://github.com/mehcode/config-rs) from 0.13.3 to 0.13.4.
- [Changelog](https://github.com/mehcode/config-rs/blob/v0.13.4/CHANGELOG.md)
- [Commits](https://github.com/mehcode/config-rs/compare/0.13.3...v0.13.4)

---
updated-dependencies:
- dependency-name: config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 999adf8215
build(deps): bump time from 0.3.24 to 0.3.30
Bumps [time](https://github.com/time-rs/time) from 0.3.24 to 0.3.30.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.24...v0.3.30)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 8d95643fa7
build(deps): bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly 5408a02072
Merge pull request #1483 from comit-network/dependabot/cargo/serde_json-1.0.108
build(deps): bump serde_json from 1.0.96 to 1.0.108
6 months ago
binarybaron dc1ad6df7d Upgrade monero-wallet-rpc to `v0.18.3.1` 6 months ago
Byron Hambly 64674f230a
Merge pull request #1486 from comit-network/dependabot/cargo/tokio-tar-0.3.1
build(deps): bump tokio-tar from 0.3.0 to 0.3.1
6 months ago
Byron Hambly 87be3a121b
Merge pull request #1485 from comit-network/dependabot/cargo/reqwest-0.11.22
build(deps): bump reqwest from 0.11.18 to 0.11.22
6 months ago
Byron Hambly 9ba607b98b
Merge pull request #1484 from comit-network/dependabot/cargo/tokio-1.35.0
build(deps): bump tokio from 1.32.0 to 1.35.0
6 months ago
Byron Hambly 7c5b1b3abd
Merge pull request #1482 from comit-network/dependabot/cargo/pem-3.0.3
build(deps): bump pem from 3.0.2 to 3.0.3
6 months ago
dependabot[bot] b7b54e8c5d
build(deps): bump tokio-tar from 0.3.0 to 0.3.1
Bumps [tokio-tar](https://github.com/vorot93/tokio-tar) from 0.3.0 to 0.3.1.
- [Commits](https://github.com/vorot93/tokio-tar/compare/v0.3.0...v0.3.1)

---
updated-dependencies:
- dependency-name: tokio-tar
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 9e4303c196
build(deps): bump reqwest from 0.11.18 to 0.11.22
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.18 to 0.11.22.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.18...v0.11.22)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] e21043be4e
build(deps): bump tokio from 1.32.0 to 1.35.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.32.0 to 1.35.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.32.0...tokio-1.35.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] d39c9eb738
build(deps): bump serde_json from 1.0.96 to 1.0.108
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.96 to 1.0.108.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.96...v1.0.108)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] ab2db98c79
build(deps): bump pem from 3.0.2 to 3.0.3
Bumps [pem](https://github.com/jcreekmore/pem-rs) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/jcreekmore/pem-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jcreekmore/pem-rs/compare/v3.0.2...v3.0.3)

---
updated-dependencies:
- dependency-name: pem
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly 6d87781a68
Merge pull request #1476 from comit-network/dependabot/cargo/base64-0.21.5
build(deps): bump base64 from 0.21.2 to 0.21.5
6 months ago
Byron Hambly 1db391dc6a
Merge pull request #1478 from comit-network/dependabot/cargo/toml-0.7.6
build(deps): bump toml from 0.5.11 to 0.7.6
6 months ago
Byron Hambly b54938ff7b
Merge pull request #1480 from comit-network/dependabot/cargo/anyhow-1.0.75
build(deps): bump anyhow from 1.0.71 to 1.0.75
6 months ago
Byron Hambly 9f401cd471
Merge pull request #1481 from comit-network/dependabot/cargo/serde-1.0.193
build(deps): bump serde from 1.0.164 to 1.0.193
6 months ago
Byron Hambly f9c4a4e344
Merge pull request #1475 from comit-network/dependabot/cargo/tempfile-3.8.0
build(deps): bump tempfile from 3.6.0 to 3.8.0
6 months ago
dependabot[bot] 9481f54fe0
build(deps): bump serde from 1.0.164 to 1.0.193
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.164 to 1.0.193.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.164...v1.0.193)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] b015397400
build(deps): bump anyhow from 1.0.71 to 1.0.75
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.71 to 1.0.75.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.71...1.0.75)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 641c107012
build(deps): bump toml from 0.5.11 to 0.7.6
Bumps [toml](https://github.com/toml-rs/toml) from 0.5.11 to 0.7.6.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.5.11...toml-v0.7.6)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 69751d204d
build(deps): bump dialoguer from 0.10.4 to 0.11.0
Bumps [dialoguer](https://github.com/console-rs/dialoguer) from 0.10.4 to 0.11.0.
- [Changelog](https://github.com/console-rs/dialoguer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/console-rs/dialoguer/compare/v0.10.4...v0.11.0)

---
updated-dependencies:
- dependency-name: dialoguer
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] ae67e6386b
build(deps): bump base64 from 0.21.2 to 0.21.5
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.21.2 to 0.21.5.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.2...v0.21.5)

---
updated-dependencies:
- dependency-name: base64
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 8ff683f815
build(deps): bump tempfile from 3.6.0 to 3.8.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.6.0 to 3.8.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.6.0...v3.8.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly 66283194e5
Merge pull request #1474 from comit-network/dependabot/cargo/comfy-table-7.1.0
build(deps): bump comfy-table from 6.1.4 to 7.1.0
6 months ago
Byron Hambly 8a3eed18b0
Merge pull request #1468 from comit-network/dependabot/cargo/uuid-1.6.1
build(deps): bump uuid from 1.4.1 to 1.6.1
6 months ago
Byron Hambly 3fb4926b6b
Merge pull request #1473 from comit-network/dependabot/cargo/sha2-0.10.8
build(deps): bump sha2 from 0.10.6 to 0.10.8
6 months ago
Byron Hambly 38e248ce3a
Merge pull request #1472 from comit-network/dependabot/cargo/hyper-1.0.1
build(deps): bump hyper from 0.14.26 to 1.0.1
6 months ago
Byron Hambly 2877a6bc60
Merge pull request #1471 from comit-network/dependabot/cargo/thiserror-1.0.50
build(deps): bump thiserror from 1.0.40 to 1.0.50
6 months ago
Byron Hambly 7b3374f29d
Merge pull request #1470 from comit-network/dependabot/cargo/proptest-1.4.0
build(deps): bump proptest from 1.2.0 to 1.4.0
6 months ago
Byron Hambly a3555fe733
Merge pull request #1469 from comit-network/dependabot/cargo/data-encoding-2.5.0
build(deps): bump data-encoding from 2.4.0 to 2.5.0
6 months ago
dependabot[bot] 0ea8ce71b6
build(deps): bump comfy-table from 6.1.4 to 7.1.0
Bumps [comfy-table](https://github.com/nukesor/comfy-table) from 6.1.4 to 7.1.0.
- [Release notes](https://github.com/nukesor/comfy-table/releases)
- [Changelog](https://github.com/Nukesor/comfy-table/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nukesor/comfy-table/compare/v6.1.4...v7.1.0)

---
updated-dependencies:
- dependency-name: comfy-table
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 8b8c69a9cc
build(deps): bump sha2 from 0.10.6 to 0.10.8
Bumps [sha2](https://github.com/RustCrypto/hashes) from 0.10.6 to 0.10.8.
- [Commits](https://github.com/RustCrypto/hashes/compare/sha2-v0.10.6...sha2-v0.10.8)

---
updated-dependencies:
- dependency-name: sha2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 81ea313d4e
build(deps): bump hyper from 0.14.26 to 1.0.1
Bumps [hyper](https://github.com/hyperium/hyper) from 0.14.26 to 1.0.1.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v0.14.26...v1.0.1)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 42e0cdcae9
build(deps): bump thiserror from 1.0.40 to 1.0.50
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.40 to 1.0.50.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.40...1.0.50)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 9c7d33acbe
build(deps): bump proptest from 1.2.0 to 1.4.0
Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.2.0 to 1.4.0.
- [Release notes](https://github.com/proptest-rs/proptest/releases)
- [Changelog](https://github.com/proptest-rs/proptest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/proptest-rs/proptest/compare/v1.2.0...v1.4.0)

---
updated-dependencies:
- dependency-name: proptest
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] c7bf84d936
build(deps): bump data-encoding from 2.4.0 to 2.5.0
Bumps [data-encoding](https://github.com/ia0/data-encoding) from 2.4.0 to 2.5.0.
- [Commits](https://github.com/ia0/data-encoding/commits)

---
updated-dependencies:
- dependency-name: data-encoding
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] f602bec096
build(deps): bump uuid from 1.4.1 to 1.6.1
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.4.1 to 1.6.1.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.4.1...1.6.1)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly b7347da27a
Merge pull request #1461 from comit-network/dependabot/cargo/url-2.5.0
build(deps): bump url from 2.4.0 to 2.5.0
6 months ago
Byron Hambly c8034c58a8
Merge pull request #1462 from comit-network/dependabot/cargo/tokio-util-0.7.10
build(deps): bump tokio-util from 0.7.3 to 0.7.10
6 months ago
Byron Hambly 141b9a99fc
Merge pull request #1463 from comit-network/dependabot/cargo/strum-0.25.0
build(deps): bump strum from 0.24.1 to 0.25.0
6 months ago
Byron Hambly 8570e2b658
Merge pull request #1464 from comit-network/dependabot/cargo/tracing-0.1.40
build(deps): bump tracing from 0.1.38 to 0.1.40
6 months ago
dependabot[bot] 46a73e2a63
build(deps): bump tracing from 0.1.38 to 0.1.40
Bumps [tracing](https://github.com/tokio-rs/tracing) from 0.1.38 to 0.1.40.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-0.1.38...tracing-0.1.40)

---
updated-dependencies:
- dependency-name: tracing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 73162adcd7
build(deps): bump strum from 0.24.1 to 0.25.0
Bumps [strum](https://github.com/Peternator7/strum) from 0.24.1 to 0.25.0.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 564302b184
build(deps): bump tokio-util from 0.7.3 to 0.7.10
Bumps [tokio-util](https://github.com/tokio-rs/tokio) from 0.7.3 to 0.7.10.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/commits)

---
updated-dependencies:
- dependency-name: tokio-util
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
dependabot[bot] 7ccb9d61be
build(deps): bump url from 2.4.0 to 2.5.0
Bumps [url](https://github.com/servo/rust-url) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.4.0...v2.5.0)

---
updated-dependencies:
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly 3ae7e581f0
Merge pull request #1458 from comit-network/dependabot/github_actions/actions/setup-python-5
build(deps): bump actions/setup-python from 4 to 5
6 months ago
dependabot[bot] c8802bad3d
build(deps): bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
6 months ago
Byron Hambly db400ad4c4
Merge pull request #1456 from delta1/rust-1.70
ci: update minimum rust version to 1.70
6 months ago
Byron Hambly dbb66fe6b7
ci: remove bors
bors has been deprecated for some time and seems to be gone now.
6 months ago
Byron Hambly 53972cdeb3
ci: update minimum rust version to 1.70
this is to fix the issue with installing sqlx-cli in ci
6 months ago
bors[bot] e1c3a5d991
Merge #1451
1451: build(deps): bump Swatinem/rust-cache from 2.6.2 to 2.7.1 r=delta1 a=dependabot[bot]

Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.6.2 to 2.7.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/swatinem/rust-cache/releases">Swatinem/rust-cache's releases</a>.</em></p>
<blockquote>
<h2>v2.7.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix save-if documentation in readme by <a href="https://github.com/rukai"><code>`@​rukai</code></a>` in <a href="https://redirect.github.com/Swatinem/rust-cache/pull/166">Swatinem/rust-cache#166</a></li>
<li>Support for <code>trybuild</code> and similar macro testing tools by <a href="https://github.com/neysofu"><code>`@​neysofu</code></a>` in <a href="https://redirect.github.com/Swatinem/rust-cache/pull/168">Swatinem/rust-cache#168</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/rukai"><code>`@​rukai</code></a>` made their first contribution in <a href="https://redirect.github.com/Swatinem/rust-cache/pull/166">Swatinem/rust-cache#166</a></li>
<li><a href="https://github.com/neysofu"><code>`@​neysofu</code></a>` made their first contribution in <a href="https://redirect.github.com/Swatinem/rust-cache/pull/168">Swatinem/rust-cache#168</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/Swatinem/rust-cache/compare/v2.6.2...v2.7.0">https://github.com/Swatinem/rust-cache/compare/v2.6.2...v2.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md">Swatinem/rust-cache's changelog</a>.</em></p>
<blockquote>
<h2>2.7.1</h2>
<ul>
<li>Update toml parser to fix parsing errors.</li>
</ul>
<h2>2.7.0</h2>
<ul>
<li>Properly cache <code>trybuild</code> tests.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="3cf7f8cc28"><code>3cf7f8c</code></a> 2.7.1</li>
<li><a href="e03705e031"><code>e03705e</code></a> changelog</li>
<li><a href="b86d1c6caa"><code>b86d1c6</code></a> bump all the other dependencies too</li>
<li><a href="f27990c89a"><code>f27990c</code></a> Update Dependencies (<a href="https://redirect.github.com/swatinem/rust-cache/issues/172">#172</a>)</li>
<li><a href="a95ba19544"><code>a95ba19</code></a> 2.7.0</li>
<li><a href="82c8487d00"><code>82c8487</code></a> changelog</li>
<li><a href="67c46e7159"><code>67c46e7</code></a> Support for <code>trybuild</code> and similar macro testing tools (<a href="https://redirect.github.com/swatinem/rust-cache/issues/168">#168</a>)</li>
<li><a href="44b6087283"><code>44b6087</code></a> Fix save-if documentation in readme (<a href="https://redirect.github.com/swatinem/rust-cache/issues/166">#166</a>)</li>
<li>See full diff in <a href="https://github.com/swatinem/rust-cache/compare/v2.6.2...v2.7.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Swatinem/rust-cache&package-manager=github_actions&previous-version=2.6.2&new-version=2.7.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7 months ago
dependabot[bot] 9819f74c12
build(deps): bump Swatinem/rust-cache from 2.6.2 to 2.7.1
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.6.2 to 2.7.1.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.6.2...v2.7.1)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
7 months ago
bors[bot] d6c1353ed5
Merge #1450
1450: build(deps): bump pem to 3.0 r=delta1 a=delta1

replaces #1415

also fixes the `seed_from_pem_fails_for_long_seed` unit test, which was "passing" but not actually testing what it meant to.

Co-authored-by: Byron Hambly <bhambly@blockstream.com>
7 months ago
Byron Hambly dcf9f24b77
build(deps): bump pem to 3.0
replaces #1415

also fixes the `seed_from_pem_fails_for_long_seed` unit test, which was
"passing" but not actually testing what it meant to.
7 months ago
bors[bot] 04e618feaa
Merge #1449
1449: build(deps): bump actions/checkout from 4.0.0 to 4.1.1 r=delta1 a=dependabot[bot]

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update CODEOWNERS to Launch team by <a href="https://github.com/joshmgross"><code>`@​joshmgross</code></a>` in <a href="https://redirect.github.com/actions/checkout/pull/1510">actions/checkout#1510</a></li>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>`@​peterbe</code></a>` in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkout#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>`@​cory-miller</code></a>` in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkout#1514</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/joshmgross"><code>`@​joshmgross</code></a>` made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1510">actions/checkout#1510</a></li>
<li><a href="https://github.com/peterbe"><code>`@​peterbe</code></a>` made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkout#1511</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4...v4.1.1">https://github.com/actions/checkout/compare/v4...v4.1.1</a></p>
<h2>v4.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md for V4 by <a href="https://github.com/sivapalan"><code>`@​sivapalan</code></a>` in <a href="https://redirect.github.com/actions/checkout/pull/1452">actions/checkout#1452</a></li>
<li>Add support for partial checkout filters by <a href="https://github.com/finleygn"><code>`@​finleygn</code></a>` in <a href="https://redirect.github.com/actions/checkout/pull/1396">actions/checkout#1396</a></li>
<li>Prepare 4.1.0 release by <a href="https://github.com/cory-miller"><code>`@​cory-miller</code></a>` in <a href="https://redirect.github.com/actions/checkout/pull/1496">actions/checkout#1496</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/sivapalan"><code>`@​sivapalan</code></a>` made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1452">actions/checkout#1452</a></li>
<li><a href="https://github.com/finleygn"><code>`@​finleygn</code></a>` made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1396">actions/checkout#1396</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.0.0...v4.1.0">https://github.com/actions/checkout/compare/v4.0.0...v4.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="b4ffde65f4"><code>b4ffde6</code></a> Link to release page from what's new section (<a href="https://redirect.github.com/actions/checkout/issues/1514">#1514</a>)</li>
<li><a href="8530928916"><code>8530928</code></a> Correct link to GitHub Docs (<a href="https://redirect.github.com/actions/checkout/issues/1511">#1511</a>)</li>
<li><a href="7cdaf2fbc0"><code>7cdaf2f</code></a> Update CODEOWNERS to Launch team (<a href="https://redirect.github.com/actions/checkout/issues/1510">#1510</a>)</li>
<li><a href="8ade135a41"><code>8ade135</code></a> Prepare 4.1.0 release (<a href="https://redirect.github.com/actions/checkout/issues/1496">#1496</a>)</li>
<li><a href="c533a0a4cf"><code>c533a0a</code></a> Add support for partial checkout filters (<a href="https://redirect.github.com/actions/checkout/issues/1396">#1396</a>)</li>
<li><a href="72f2cec99f"><code>72f2cec</code></a> Update README.md for V4 (<a href="https://redirect.github.com/actions/checkout/issues/1452">#1452</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/v4.0.0...v4.1.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.0.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8 months ago
dependabot[bot] eac6a6be5c
build(deps): bump actions/checkout from 4.0.0 to 4.1.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.0.0...v4.1.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
8 months ago
bors[bot] c93eaa8057
Merge #1447
1447: Update lock timeout to 10 min r=binarybaron a=pokkst

This updates the lock timeout from 3 min to 10 min.

Over Tor, it is sometimes very slow to setup a swap, completely missing the 3 minute window. The BTC will still lock and broadcast, but the ASB will have already stopped watching for the lock tx, thus will not lock the XMR once the BTC lock tx confirms. This mitigates that issue.

Co-authored-by: pokkst <pokkst@protonmail.com>
8 months ago
pokkst 5182b4f53a
Update lock timeout to 10 min 8 months ago

@ -6,8 +6,9 @@ on:
jobs:
build_binaries:
name: Build swap and asb binaries
name: Build
strategy:
fail-fast: false
matrix:
include:
- bin: swap
@ -20,6 +21,10 @@ jobs:
archive_ext: tar
- bin: swap
target: x86_64-apple-darwin
os: macos-12
archive_ext: tar
- bin: swap
target: aarch64-apple-darwin
os: macos-latest
archive_ext: tar
- bin: swap
@ -36,6 +41,10 @@ jobs:
archive_ext: tar
- bin: asb
target: x86_64-apple-darwin
os: macos-12
archive_ext: tar
- bin: asb
target: aarch64-apple-darwin
os: macos-latest
archive_ext: tar
- bin: asb
@ -45,34 +54,33 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout tagged commit
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
with:
ref: ${{ github.event.release.target_commitish }}
token: ${{ secrets.BOTTY_GITHUB_TOKEN }}
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.67
targets: armv7-unknown-linux-gnueabihf
toolchain: "1.74"
- name: Cross Build ${{ matrix.target }} ${{ matrix.bin }} binary
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
run: |
curl -L "https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz" | tar xzv
sudo mv cross /usr/bin
sudo mv cross-util /usr/bin
cross build --target=${{ matrix.target }} --release --package swap --bin ${{ matrix.bin }}
- name: Build ${{ matrix.target }} ${{ matrix.bin }} release binary
uses: actions-rs/cargo@v1
with:
command: build
args: --target=${{ matrix.target }} --release --package swap --bin ${{ matrix.bin }}
use-cross: true
if: matrix.target != 'armv7-unknown-linux-gnueabihf'
run: cargo build --target=${{ matrix.target }} --release --package swap --bin ${{ matrix.bin }}
- name: Smoke test the binary
if: matrix.target != 'armv7-unknown-linux-gnueabihf' # armv7-unknown-linux-gnueabihf is only cross-compiled, no smoke test
if: matrix.target != 'armv7-unknown-linux-gnueabihf'
run: target/${{ matrix.target }}/release/${{ matrix.bin }} --help
# Remove once python 3 is the default
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- id: create-archive-name
shell: python # Use python to have a prettier name for the archive on Windows.
run: |
@ -89,7 +97,7 @@ jobs:
print(f'::set-output name=archive::{archive_name}')
- name: Pack macos archive
if: matrix.os == 'macos-latest'
if: startsWith(matrix.os, 'macos')
shell: bash
run: gtar -C ./target/${{ matrix.target }}/release --create --file=${{ steps.create-archive-name.outputs.archive }} ${{ matrix.bin }}

@ -4,8 +4,6 @@ on:
pull_request: # Need to run on pull-requests, otherwise PRs from forks don't run
push:
branches:
- "staging" # Bors uses this branch
- "trying" # Bors uses this branch
- "master" # Always build head of master for the badge in the README
jobs:
@ -13,14 +11,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.67
toolchain: "1.74"
components: clippy,rustfmt
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- name: Check formatting
uses: dprint/check@v2.2
@ -37,9 +35,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- name: Build swap
run: cargo build --bin swap
@ -51,12 +49,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- name: Install sqlx-cli
run: cargo install sqlx-cli --locked
run: cargo install --locked --version 0.6.3 sqlx-cli
- name: Run sqlite_dev_setup.sh script
run: |
@ -72,19 +70,21 @@ jobs:
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-12
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.67
toolchain: "1.74"
targets: armv7-unknown-linux-gnueabihf
- name: Build binary
@ -100,13 +100,13 @@ jobs:
run: cross build -p swap --target ${{ matrix.target }}
- name: Upload swap binary
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: swap-${{ matrix.target }}
path: target/${{ matrix.target }}/debug/swap
- name: Upload asb binary
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: asb-${{ matrix.target }}
path: target/${{ matrix.target }}/debug/asb
@ -117,10 +117,23 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: (Free disk space on Ubuntu)
if: matrix.os == 'ubuntu-latest'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# removing all of these takes ~10 mins, so just do as needed
android: true
dotnet: true
haskell: true
docker-images: false
large-packages: false
swap-storage: false
tool-cache: false
- name: Checkout sources
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- name: Build tests
run: cargo build --tests --workspace --all-features
@ -155,9 +168,33 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.0.0
uses: actions/checkout@v4.1.6
- uses: Swatinem/rust-cache@v2.6.2
- uses: Swatinem/rust-cache@v2.7.3
- name: Run test ${{ matrix.test_name }}
run: cargo test --package swap --all-features --test ${{ matrix.test_name }} -- --nocapture
rpc_tests:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.1.6
- uses: Swatinem/rust-cache@v2.7.3
- name: Run RPC server tests
run: cargo test --package swap --all-features --test rpc -- --nocapture
check_stable:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.1.6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2.7.3
- name: Run cargo check on stable rust
run: cargo check --all-targets

@ -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@v4.0.0
- uses: actions/checkout@v4.1.6
- name: Extract version from branch name
id: extract-version

@ -12,7 +12,7 @@ jobs:
name: "Draft a new release"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.0.0
- uses: actions/checkout@v4.1.6
with:
token: ${{ secrets.BOTTY_GITHUB_TOKEN }}
@ -20,7 +20,7 @@ jobs:
run: git checkout -b release/${{ github.event.inputs.version }}
- name: Update changelog
uses: thomaseizinger/keep-a-changelog-new-release@1.3.0
uses: thomaseizinger/keep-a-changelog-new-release@3.0.0
with:
version: ${{ github.event.inputs.version }}
changelogPath: CHANGELOG.md
@ -42,8 +42,8 @@ jobs:
- name: Commit changelog and manifest files
id: make-commit
env:
DPRINT_VERSION: 0.39.1
RUST_TOOLCHAIN: 1.67
DPRINT_VERSION: "0.39.1"
RUST_TOOLCHAIN: "1.74"
run: |
rustup component add rustfmt --toolchain "$RUST_TOOLCHAIN-x86_64-unknown-linux-gnu"
curl -fsSL https://dprint.dev/install.sh | sh -s $DPRINT_VERSION
@ -58,7 +58,7 @@ jobs:
run: git push origin release/${{ github.event.inputs.version }} --force
- name: Create pull request
uses: thomaseizinger/create-pull-request@1.3.1
uses: thomaseizinger/create-pull-request@1.4.0
with:
GITHUB_TOKEN: ${{ secrets.BOTTY_GITHUB_TOKEN }}
head: release/${{ github.event.inputs.version }}

@ -10,7 +10,7 @@ jobs:
name: Create preview release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.0.0
- uses: actions/checkout@v4.1.6
- name: Delete 'preview' release
uses: larryjoelane/delete-release-action@v1.0.24

@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.13.0] - 2024-05-29
- Minimum Supported Rust Version (MSRV) bumped to 1.74
- Lowered default Bitcoin confirmation target for Bob to 1 to make sure Bitcoin transactions get confirmed in time
- Added support for starting the CLI (using the `start-daemon` subcommand) as a Daemon that accepts JSON-RPC requests
- Update monero-wallet-rpc version to v0.18.3.1
## [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.
@ -349,7 +356,8 @@ 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.3...HEAD
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.0...HEAD
[0.13.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.3...0.13.0
[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

1749
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,5 +1,8 @@
[workspace]
resolver = "2"
members = [ "monero-harness", "monero-rpc", "swap", "monero-wallet" ]
[patch.crates-io]
# patch until new release https://github.com/thomaseizinger/rust-jsonrpc-client/pull/51
jsonrpc_client = { git = "https://github.com/delta1/rust-jsonrpc-client.git", rev = "3b6081697cd616c952acb9c2f02d546357d35506" }
monero = { git = "https://github.com/comit-network/monero-rs", rev = "818f38b" }

@ -50,7 +50,7 @@ Please have a look at the [contribution guidelines](./CONTRIBUTING.md).
## Rust Version Support
Please note that only the latest stable Rust toolchain is supported.
All stable toolchains since 1.67 _should_ work.
All stable toolchains since 1.74 _should_ work.
## Contact

@ -1,25 +0,0 @@
status = [
"static_analysis",
"bdk_test",
"sqlx_test",
"build (x86_64-unknown-linux-gnu, ubuntu-latest)",
"build (armv7-unknown-linux-gnueabihf, ubuntu-latest)",
"build (x86_64-apple-darwin, macos-latest)",
"build (x86_64-pc-windows-msvc, windows-latest)",
"test (ubuntu-latest)",
"test (macos-latest)",
"docker_tests (happy_path)",
"docker_tests (happy_path_restart_bob_after_xmr_locked)",
"docker_tests (happy_path_restart_alice_after_xmr_locked)",
"docker_tests (happy_path_restart_bob_before_xmr_locked)",
"docker_tests (alice_and_bob_refund_using_cancel_and_refund_command)",
"docker_tests (alice_and_bob_refund_using_cancel_then_refund_command)",
"docker_tests (alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired)",
"docker_tests (punish)",
"docker_tests (alice_punishes_after_restart_bob_dead)",
"docker_tests (alice_manually_punishes_after_bob_dead)",
"docker_tests (alice_refunds_after_restart_bob_refunded)",
"docker_tests (ensure_same_swap_id)",
"docker_tests (concurrent_bobs_before_xmr_lock_proof_sent)",
"docker_tests (alice_manually_redeems_after_enc_sig_learned)"
]

@ -164,3 +164,38 @@ May 01 01:32:05.018 INFO Tor found. Setting up hidden service.
May 01 01:32:07.475 INFO /onion3/z4findrdwtfbpoq64ayjtmxvr52vvxnsynerlenlfkmm52dqxsl4deyd:9939
May 01 01:32:07.476 INFO /onion3/z4findrdwtfbpoq64ayjtmxvr52vvxnsynerlenlfkmm52dqxsl4deyd:9940
```
### Exporting the Bitcoin wallet descriptor
First use `swap` or `asb` with the `export-bitcoin-wallet` subcommand.
Output example:
```json
{"descriptor":"wpkh(tprv8Zgredacted.../84'/1'/0'/0/*)","blockheight":2415616,"label":"asb-testnet"}
```
The wallet can theoretically be directly imported into
[bdk-cli](https://bitcoindevkit.org/bdk-cli/installation/) but it is easier to
use Sparrow Wallet.
Sparrow wallet import works as follows:
- File -> New wallet -> Give it a name
- Select "New or Imported Software Wallet"
- Click "Enter Private Key" for "Master Private Key (BIP32)"
- Enter the `xprv...` or `tprv...` part of the descriptor (example above is `tprv8Zgredacted...`:
![image](enter-master-private-key.png)
- Click "Import"
- Leave the derivation path as `m/84'/0'/0'` and click "Import Keystore" button
- Click "Apply" and then supply password
![image](import-keystore.png)
- Click Transactions tab
- ???
- Profit!
![image](transactions-tab.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

@ -10,8 +10,7 @@ anyhow = "1"
futures = "0.3"
monero-rpc = { path = "../monero-rpc" }
rand = "0.7"
spectral = "0.6"
testcontainers = "0.12"
testcontainers = "0.15"
tokio = { version = "1", default-features = false, features = [ "rt-multi-thread", "time", "macros" ] }
tracing = "0.1"
tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "tracing-log" ] }

@ -1,6 +1,4 @@
use std::collections::HashMap;
use testcontainers::core::{Container, Docker, WaitForMessage};
use testcontainers::Image;
use testcontainers::{core::WaitFor, Image, ImageArgs};
pub const MONEROD_DAEMON_CONTAINER_NAME: &str = "monerod";
pub const MONEROD_DEFAULT_NETWORK: &str = "monero_network";
@ -13,43 +11,22 @@ pub const MONEROD_DEFAULT_NETWORK: &str = "monero_network";
/// this doesn't matter.
pub const RPC_PORT: u16 = 18081;
#[derive(Debug, Default)]
pub struct Monerod {
args: MonerodArgs,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Monerod;
impl Image for Monerod {
type Args = MonerodArgs;
type EnvVars = HashMap<String, String>;
type Volumes = HashMap<String, String>;
type EntryPoint = str;
fn descriptor(&self) -> String {
"rinocommunity/monero:v0.18.1.2".to_owned()
}
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
container
.logs()
.stdout
.wait_for_message("RPC server started ok")
.unwrap();
}
fn args(&self) -> <Self as Image>::Args {
self.args.clone()
}
fn volumes(&self) -> Self::Volumes {
HashMap::new()
fn name(&self) -> String {
"rinocommunity/monero".into()
}
fn env_vars(&self) -> Self::EnvVars {
HashMap::new()
fn tag(&self) -> String {
"v0.18.1.2".into()
}
fn with_args(self, args: <Self as Image>::Args) -> Self {
Self { args }
fn ready_conditions(&self) -> Vec<WaitFor> {
vec![WaitFor::message_on_stdout("RPC server started ok")]
}
fn entrypoint(&self) -> Option<String> {
@ -58,43 +35,22 @@ impl Image for Monerod {
}
}
#[derive(Debug, Default)]
pub struct MoneroWalletRpc {
args: MoneroWalletRpcArgs,
}
#[derive(Clone, Copy, Debug)]
pub struct MoneroWalletRpc;
impl Image for MoneroWalletRpc {
type Args = MoneroWalletRpcArgs;
type EnvVars = HashMap<String, String>;
type Volumes = HashMap<String, String>;
type EntryPoint = str;
fn descriptor(&self) -> String {
"rinocommunity/monero:v0.18.1.2".to_owned()
}
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
container
.logs()
.stdout
.wait_for_message("Run server thread name: RPC")
.unwrap();
}
fn args(&self) -> <Self as Image>::Args {
self.args.clone()
}
fn volumes(&self) -> Self::Volumes {
HashMap::new()
fn name(&self) -> String {
"rinocommunity/monero".into()
}
fn env_vars(&self) -> Self::EnvVars {
HashMap::new()
fn tag(&self) -> String {
"v0.18.1.2".into()
}
fn with_args(self, args: <Self as Image>::Args) -> Self {
Self { args }
fn ready_conditions(&self) -> Vec<WaitFor> {
vec![WaitFor::message_on_stdout("Run server thread name: RPC")]
}
fn entrypoint(&self) -> Option<String> {
@ -104,10 +60,9 @@ impl Image for MoneroWalletRpc {
}
impl MoneroWalletRpc {
pub fn new(name: &str, daemon_address: String) -> Self {
Self {
args: MoneroWalletRpcArgs::new(name, daemon_address),
}
pub fn new(name: &str, daemon_address: String) -> (Self, MoneroWalletRpcArgs) {
let args = MoneroWalletRpcArgs::new(name, daemon_address);
(Self, args)
}
}
@ -191,6 +146,12 @@ impl IntoIterator for MonerodArgs {
}
}
impl ImageArgs for MonerodArgs {
fn into_iterator(self) -> Box<dyn Iterator<Item = String>> {
Box::new(self.into_iter())
}
}
#[derive(Debug, Clone)]
pub struct MoneroWalletRpcArgs {
pub disable_rpc_login: bool,
@ -200,12 +161,6 @@ pub struct MoneroWalletRpcArgs {
pub daemon_address: String,
}
impl Default for MoneroWalletRpcArgs {
fn default() -> Self {
unimplemented!("A default instance for `MoneroWalletRpc` doesn't make sense because we always need to connect to a node.")
}
}
impl MoneroWalletRpcArgs {
pub fn new(wallet_name: &str, daemon_address: String) -> Self {
Self {
@ -247,3 +202,9 @@ impl IntoIterator for MoneroWalletRpcArgs {
args.into_iter()
}
}
impl ImageArgs for MoneroWalletRpcArgs {
fn into_iterator(self) -> Box<dyn Iterator<Item = String>> {
Box::new(self.into_iter())
}
}

@ -20,17 +20,20 @@
//! every BLOCK_TIME_SECS seconds.
//!
//! Also provides standalone JSON RPC clients for monerod and monero-wallet-rpc.
pub mod image;
use std::time::Duration;
use crate::image::{MONEROD_DAEMON_CONTAINER_NAME, MONEROD_DEFAULT_NETWORK, RPC_PORT};
use anyhow::{anyhow, bail, Context, Result};
use testcontainers::clients::Cli;
use testcontainers::{Container, RunnableImage};
use tokio::time;
use monero_rpc::monerod;
use monero_rpc::monerod::MonerodRpc as _;
use monero_rpc::wallet::{self, GetAddress, MoneroWalletRpc as _, Refreshed, Transfer};
use std::time::Duration;
use testcontainers::clients::Cli;
use testcontainers::{Container, Docker, RunArgs};
use tokio::time;
use crate::image::{MONEROD_DAEMON_CONTAINER_NAME, MONEROD_DEFAULT_NETWORK, RPC_PORT};
pub mod image;
/// How often we mine a block.
const BLOCK_TIME_SECS: u64 = 1;
@ -56,8 +59,8 @@ impl<'c> Monero {
additional_wallets: Vec<&'static str>,
) -> Result<(
Self,
Container<'c, Cli, image::Monerod>,
Vec<Container<'c, Cli, image::MoneroWalletRpc>>,
Container<'c, image::Monerod>,
Vec<Container<'c, image::MoneroWalletRpc>>,
)> {
let prefix = format!("{}_", random_prefix());
let monerod_name = format!("{}{}", prefix, MONEROD_DAEMON_CONTAINER_NAME);
@ -221,15 +224,14 @@ impl<'c> Monerod {
cli: &'c Cli,
name: String,
network: String,
) -> Result<(Self, Container<'c, Cli, image::Monerod>)> {
let image = image::Monerod::default();
let run_args = RunArgs::default()
.with_name(name.clone())
) -> Result<(Self, Container<'c, image::Monerod>)> {
let image = image::Monerod;
let image: RunnableImage<image::Monerod> = RunnableImage::from(image)
.with_container_name(name.clone())
.with_network(network.clone());
let container = cli.run_with_args(image, run_args);
let monerod_rpc_port = container
.get_host_port(RPC_PORT)
.context("port not exposed")?;
let container = cli.run(image);
let monerod_rpc_port = container.get_host_port_ipv4(RPC_PORT);
Ok((
Self {
@ -262,19 +264,15 @@ impl<'c> MoneroWalletRpc {
name: &str,
monerod: &Monerod,
prefix: String,
) -> Result<(Self, Container<'c, Cli, image::MoneroWalletRpc>)> {
) -> Result<(Self, Container<'c, image::MoneroWalletRpc>)> {
let daemon_address = format!("{}:{}", monerod.name, RPC_PORT);
let image = image::MoneroWalletRpc::new(name, daemon_address);
let (image, args) = image::MoneroWalletRpc::new(name, daemon_address);
let image = RunnableImage::from((image, args))
.with_container_name(format!("{}{}", prefix, name))
.with_network(monerod.network.clone());
let network = monerod.network.clone();
let run_args = RunArgs::default()
// prefix the container name so we can run multiple tests
.with_name(format!("{}{}", prefix, name))
.with_network(network.clone());
let container = cli.run_with_args(image, run_args);
let wallet_rpc_port = container
.get_host_port(RPC_PORT)
.context("port not exposed")?;
let container = cli.run(image);
let wallet_rpc_port = container.get_host_port_ipv4(RPC_PORT);
let client = wallet::Client::localhost(wallet_rpc_port)?;

@ -1,6 +1,5 @@
use monero_harness::Monero;
use monero_rpc::monerod::MonerodRpc as _;
use spectral::prelude::*;
use std::time::Duration;
use testcontainers::clients::Cli;
use tokio::time;
@ -21,12 +20,12 @@ async fn init_miner_and_mine_to_miner_address() {
let miner_wallet = monero.wallet("miner").unwrap();
let got_miner_balance = miner_wallet.balance().await.unwrap();
assert_that!(got_miner_balance).is_greater_than(0);
assert!(got_miner_balance > 0);
time::sleep(Duration::from_millis(1010)).await;
// after a bit more than 1 sec another block should have been mined
let block_height = monerod.client().get_block_count().await.unwrap().count;
assert_that(&block_height).is_greater_than(70);
assert!(block_height > 70);
}

@ -1,6 +1,5 @@
use monero_harness::{Monero, MoneroWalletRpc};
use monero_rpc::wallet::MoneroWalletRpc as _;
use spectral::prelude::*;
use std::time::Duration;
use testcontainers::clients::Cli;
use tokio::time::sleep;
@ -29,7 +28,7 @@ async fn fund_transfer_and_check_tx_key() {
// check alice balance
let got_alice_balance = alice_wallet.balance().await.unwrap();
assert_that(&got_alice_balance).is_equal_to(fund_alice);
assert_eq!(got_alice_balance, fund_alice);
// transfer from alice to bob
let bob_address = bob_wallet.address().await.unwrap().address;
@ -41,7 +40,7 @@ async fn fund_transfer_and_check_tx_key() {
wait_for_wallet_to_catch_up(bob_wallet, send_to_bob).await;
let got_bob_balance = bob_wallet.balance().await.unwrap();
assert_that(&got_bob_balance).is_equal_to(send_to_bob);
assert_eq!(got_bob_balance, send_to_bob);
// check if tx was actually seen
let tx_id = transfer.tx_hash;
@ -52,7 +51,7 @@ async fn fund_transfer_and_check_tx_key() {
.await
.expect("failed to check tx by key");
assert_that!(res.received).is_equal_to(send_to_bob);
assert_eq!(res.received, send_to_bob);
}
async fn wait_for_wallet_to_catch_up(wallet: &MoneroWalletRpc, expected_balance: u64) {

@ -12,7 +12,7 @@ jsonrpc_client = { version = "0.7", features = [ "reqwest" ] }
monero = "0.12"
monero-epee-bin-serde = "1"
rand = "0.7"
reqwest = { version = "0.11", default-features = false, features = [ "json" ] }
reqwest = { version = "0.12", default-features = false, features = [ "json" ] }
rust_decimal = { version = "1", features = [ "serde-float" ] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"

@ -14,6 +14,6 @@ rand = "0.7"
curve25519-dalek = "3"
monero-harness = { path = "../monero-harness" }
rand = "0.7"
testcontainers = "0.12"
testcontainers = "0.15"
tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs" ] }
tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "chrono", "tracing-log" ] }

@ -61,13 +61,12 @@ mod tests {
use monero_harness::image::Monerod;
use monero_rpc::monerod::{Client, GetOutputsOut};
use testcontainers::clients::Cli;
use testcontainers::Docker;
#[tokio::test]
async fn get_outs_for_key_offsets() {
let cli = Cli::default();
let container = cli.run(Monerod::default());
let rpc_client = Client::localhost(container.get_host_port(18081).unwrap()).unwrap();
let container = cli.run(Monerod);
let rpc_client = Client::localhost(container.get_host_port_ipv4(18081)).unwrap();
rpc_client.generateblocks(150, "498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj".to_owned()).await.unwrap();
let wallet = Wallet {
client: rpc_client.clone(),

@ -1,4 +1,4 @@
[toolchain]
channel = "1.67" # also update this in the readme, changelog, and github actions
channel = "1.74" # also update this in the readme, changelog, and github actions
components = ["clippy"]
targets = ["armv7-unknown-linux-gnueabihf"]

@ -1,6 +1,6 @@
[package]
name = "swap"
version = "0.12.3"
version = "0.13.0"
authors = [ "The COMIT guys <hello@comit.network>" ]
edition = "2021"
description = "XMR/BTC trustless atomic swaps."
@ -14,32 +14,35 @@ async-compression = { version = "0.3", features = [ "bzip2", "tokio" ] }
async-trait = "0.1"
atty = "0.2"
backoff = { version = "0.4", features = [ "tokio" ] }
base64 = "0.21"
base64 = "0.22"
bdk = "0.28"
big-bytes = "1"
bitcoin = { version = "0.29", features = [ "rand", "serde" ] }
bmrng = "0.5"
comfy-table = "6.1"
config = { version = "0.13", default-features = false, features = [ "toml" ] }
comfy-table = "7.1"
config = { version = "0.14", default-features = false, features = [ "toml" ] }
conquer-once = "0.4"
curve25519-dalek = { package = "curve25519-dalek-ng", version = "4" }
data-encoding = "2.4"
dialoguer = "0.10"
data-encoding = "2.6"
dialoguer = "0.11"
digest = "0.10.7"
directories-next = "2"
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] }
ecdsa_fun = { version = "0.10", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] }
ed25519-dalek = "1"
futures = { version = "0.3", default-features = false }
hex = "0.4"
itertools = "0.10"
itertools = "0.13"
jsonrpsee = { version = "0.16.2", features = [ "server" ] }
jsonrpsee-core = "0.16.2"
libp2p = { version = "0.42.2", default-features = false, features = [ "tcp-tokio", "yamux", "mplex", "dns-tokio", "noise", "request-response", "websocket", "ping", "rendezvous", "identify" ] }
monero = { version = "0.12", features = [ "serde_support" ] }
monero-rpc = { path = "../monero-rpc" }
pem = "1.1"
pem = "3.0"
proptest = "1"
qrcode = "0.12"
qrcode = "0.14"
rand = "0.8"
rand_chacha = "0.3"
reqwest = { version = "0.11", features = [ "rustls-tls", "stream", "socks" ], default-features = false }
reqwest = { version = "0.12", features = [ "http2", "rustls-tls", "stream", "socks" ], default-features = false }
rust_decimal = { version = "1", features = [ "serde-float" ] }
rust_decimal_macros = "1"
serde = { version = "1", features = [ "derive" ] }
@ -47,24 +50,24 @@ serde_cbor = "0.11"
serde_json = "1"
serde_with = { version = "1", features = [ "macros" ] }
sha2 = "0.10"
sigma_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "ed25519", "serde", "secp256k1", "alloc" ] }
sqlx = { version = "0.6", features = [ "sqlite", "runtime-tokio-rustls", "offline" ] }
sigma_fun = { version = "0.7", default-features = false, features = [ "ed25519", "serde", "secp256k1", "alloc" ] }
sqlx = { version = "0.6.3", features = [ "sqlite", "runtime-tokio-rustls", "offline" ] }
structopt = "0.3"
strum = { version = "0.24", features = [ "derive" ] }
strum = { version = "0.26", features = [ "derive" ] }
thiserror = "1"
time = "0.3"
tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] }
tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net", "parking_lot" ] }
tokio-socks = "0.5"
tokio-tungstenite = { version = "0.15", features = [ "rustls-tls" ] }
tokio-util = { version = "0.7", features = [ "io", "codec" ] }
toml = "0.5"
toml = "0.8"
torut = { version = "0.2", default-features = false, features = [ "v3", "control" ] }
tracing = { version = "0.1", features = [ "attributes" ] }
tracing-appender = "0.2"
tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] }
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.4", features = [ "serde", "v4" ] }
uuid = { version = "1.8", features = [ "serde", "v4" ] }
void = "1"
[target.'cfg(not(windows))'.dependencies]
@ -74,19 +77,20 @@ tokio-tar = "0.3"
zip = "0.5"
[dev-dependencies]
bitcoin-harness = "0.2.2"
bitcoin-harness = { git = "https://github.com/delta1/bitcoin-harness-rs.git", rev = "80cc8d05db2610d8531011be505b7bee2b5cdf9f" }
get-port = "3"
hyper = "0.14"
mockito = "1.1.0"
hyper = "1.3"
jsonrpsee = { version = "0.16.2", features = [ "ws-client" ] }
mockito = "1.3.0"
monero-harness = { path = "../monero-harness" }
port_check = "0.1"
port_check = "0.2"
proptest = "1"
sequential-test = "0.2.4"
serde_cbor = "0.11"
serial_test = "2.0"
spectral = "0.6"
serial_test = "3.0"
tempfile = "3"
testcontainers = "0.12"
testcontainers = "0.15"
[build-dependencies]
anyhow = "1"
vergen = { version = "7.5", default-features = false, features = [ "git", "build" ] }
vergen = { version = "8.3", default-features = false, features = [ "build", "git", "git2" ] }

@ -1,9 +1,9 @@
use anyhow::Result;
use vergen::{vergen, Config, SemverKind};
use vergen::EmitBuilder;
fn main() -> Result<()> {
let mut config = Config::default();
*config.git_mut().semver_kind_mut() = SemverKind::Lightweight;
vergen(config)
EmitBuilder::builder()
.git_describe(true, true, None)
.emit()?;
Ok(())
}

@ -1,7 +1,8 @@
#!/bin/bash
# run this script from the swap dir
# make sure you have sqlx-cli installed: cargo install sqlx-cli
# make sure you have sqlx-cli installed: cargo install --version 0.6.3 sqlx-cli
# it's advised for the sqlx-cli to be the same version as specified in cargo.toml
# this script creates a temporary sqlite database
# then runs the migration scripts to create the tables (migrations folder)

@ -28,6 +28,24 @@
},
"query": "\n insert into peer_addresses (\n peer_id,\n address\n ) values (?, ?);\n "
},
"0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c": {
"describe": {
"columns": [
{
"name": "start_date",
"ordinal": 0,
"type_info": "Text"
}
],
"nullable": [
true
],
"parameters": {
"Right": 1
}
},
"query": "\n SELECT min(entered_at) as start_date\n FROM swap_states\n WHERE swap_id = ?\n "
},
"1ec38c85e7679b2eb42b3df75d9098772ce44fdb8db3012d3c2410d828b74157": {
"describe": {
"columns": [
@ -62,6 +80,30 @@
},
"query": "\n insert into peers (\n swap_id,\n peer_id\n ) values (?, ?);\n "
},
"3f2bfdd2d134586ccad22171cd85a465800fc5c4fdaf191d206974e530240c87": {
"describe": {
"columns": [
{
"name": "swap_id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "state",
"ordinal": 1,
"type_info": "Text"
}
],
"nullable": [
false,
false
],
"parameters": {
"Right": 0
}
},
"query": "\n SELECT swap_id, state\n FROM swap_states\n "
},
"50a5764546f69c118fa0b64120da50f51073d36257d49768de99ff863e3511e0": {
"describe": {
"columns": [],
@ -90,7 +132,17 @@
},
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n "
},
"a0eb85d04ee3842c52291dad4d225941d1141af735922fcbc665868997fce304": {
"b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 3
}
},
"query": "\n insert into swap_states (\n swap_id,\n entered_at,\n state\n ) values (?, ?, ?);\n "
},
"ce270dd4a4b9615695a79864240c5401e2122077365e5e5a19408c068c7f9454": {
"describe": {
"columns": [
{
@ -106,23 +158,31 @@
"Right": 1
}
},
"query": "\n SELECT address\n FROM peer_addresses\n WHERE peer_id = ?\n "
"query": "\n SELECT address\n FROM monero_addresses\n WHERE swap_id = ?\n "
},
"b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": {
"d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2": {
"describe": {
"columns": [],
"nullable": [],
"columns": [
{
"name": "address",
"ordinal": 0,
"type_info": "Text"
}
],
"nullable": [
false
],
"parameters": {
"Right": 3
"Right": 1
}
},
"query": "\n insert into swap_states (\n swap_id,\n entered_at,\n state\n ) values (?, ?, ?);\n "
"query": "\n SELECT DISTINCT address\n FROM peer_addresses\n WHERE peer_id = ?\n "
},
"ce270dd4a4b9615695a79864240c5401e2122077365e5e5a19408c068c7f9454": {
"e05620f420f8c1022971eeb66a803323a8cf258cbebb2834e3f7cf8f812fa646": {
"describe": {
"columns": [
{
"name": "address",
"name": "state",
"ordinal": 0,
"type_info": "Text"
}
@ -134,6 +194,6 @@
"Right": 1
}
},
"query": "\n SELECT address\n FROM monero_addresses\n WHERE swap_id = ?\n "
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n "
}
}

@ -0,0 +1,460 @@
pub mod request;
use crate::cli::command::{Bitcoin, Monero, Tor};
use crate::database::open_db;
use crate::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet};
use crate::fs::system_data_dir;
use crate::network::rendezvous::XmrBtcNamespace;
use crate::protocol::Database;
use crate::seed::Seed;
use crate::{bitcoin, cli, monero};
use anyhow::{bail, Context as AnyContext, Error, Result};
use futures::future::try_join_all;
use std::fmt;
use std::future::Future;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::sync::{Arc, Once};
use tokio::sync::{broadcast, broadcast::Sender, Mutex, RwLock};
use tokio::task::JoinHandle;
use url::Url;
static START: Once = Once::new();
#[derive(Clone, PartialEq, Debug)]
pub struct Config {
tor_socks5_port: u16,
namespace: XmrBtcNamespace,
server_address: Option<SocketAddr>,
pub env_config: EnvConfig,
seed: Option<Seed>,
debug: bool,
json: bool,
data_dir: PathBuf,
is_testnet: bool,
}
use uuid::Uuid;
#[derive(Default)]
pub struct PendingTaskList(Mutex<Vec<JoinHandle<()>>>);
impl PendingTaskList {
pub async fn spawn<F, T>(&self, future: F)
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
let handle = tokio::spawn(async move {
let _ = future.await;
});
self.0.lock().await.push(handle);
}
pub async fn wait_for_tasks(&self) -> Result<()> {
let tasks = {
// Scope for the lock, to avoid holding it for the entire duration of the async block
let mut guard = self.0.lock().await;
guard.drain(..).collect::<Vec<_>>()
};
try_join_all(tasks).await?;
Ok(())
}
}
pub struct SwapLock {
current_swap: RwLock<Option<Uuid>>,
suspension_trigger: Sender<()>,
}
impl SwapLock {
pub fn new() -> Self {
let (suspension_trigger, _) = broadcast::channel(10);
SwapLock {
current_swap: RwLock::new(None),
suspension_trigger,
}
}
pub async fn listen_for_swap_force_suspension(&self) -> Result<(), Error> {
let mut listener = self.suspension_trigger.subscribe();
let event = listener.recv().await;
match event {
Ok(_) => Ok(()),
Err(e) => {
tracing::error!("Error receiving swap suspension signal: {}", e);
bail!(e)
}
}
}
pub async fn acquire_swap_lock(&self, swap_id: Uuid) -> Result<(), Error> {
let mut current_swap = self.current_swap.write().await;
if current_swap.is_some() {
bail!("There already exists an active swap lock");
}
tracing::debug!(swap_id = %swap_id, "Acquiring swap lock");
*current_swap = Some(swap_id);
Ok(())
}
pub async fn get_current_swap_id(&self) -> Option<Uuid> {
*self.current_swap.read().await
}
/// Sends a signal to suspend all ongoing swap processes.
///
/// This function performs the following steps:
/// 1. Triggers the suspension by sending a unit `()` signal to all listeners via `self.suspension_trigger`.
/// 2. Polls the `current_swap` state every 50 milliseconds to check if it has been set to `None`, indicating that the swap processes have been suspended and the lock released.
/// 3. If the lock is not released within 10 seconds, the function returns an error.
///
/// If we send a suspend signal while no swap is in progress, the function will not fail, but will return immediately.
///
/// # Returns
/// - `Ok(())` if the swap lock is successfully released.
/// - `Err(Error)` if the function times out waiting for the swap lock to be released.
///
/// # Notes
/// The 50ms polling interval is considered negligible overhead compared to the typical time required to suspend ongoing swap processes.
pub async fn send_suspend_signal(&self) -> Result<(), Error> {
const TIMEOUT: u64 = 10_000;
const INTERVAL: u64 = 50;
let _ = self.suspension_trigger.send(())?;
for _ in 0..(TIMEOUT / INTERVAL) {
if self.get_current_swap_id().await.is_none() {
return Ok(());
}
tokio::time::sleep(tokio::time::Duration::from_millis(INTERVAL)).await;
}
bail!("Timed out waiting for swap lock to be released");
}
pub async fn release_swap_lock(&self) -> Result<Uuid, Error> {
let mut current_swap = self.current_swap.write().await;
if let Some(swap_id) = current_swap.as_ref() {
tracing::debug!(swap_id = %swap_id, "Releasing swap lock");
let prev_swap_id = *swap_id;
*current_swap = None;
drop(current_swap);
Ok(prev_swap_id)
} else {
bail!("There is no current swap lock to release");
}
}
}
impl Default for SwapLock {
fn default() -> Self {
Self::new()
}
}
// workaround for warning over monero_rpc_process which we must own but not read
#[allow(dead_code)]
pub struct Context {
pub db: Arc<dyn Database + Send + Sync>,
bitcoin_wallet: Option<Arc<bitcoin::Wallet>>,
monero_wallet: Option<Arc<monero::Wallet>>,
monero_rpc_process: Option<monero::WalletRpcProcess>,
pub swap_lock: Arc<SwapLock>,
pub config: Config,
pub tasks: Arc<PendingTaskList>,
}
#[allow(clippy::too_many_arguments)]
impl Context {
pub async fn build(
bitcoin: Option<Bitcoin>,
monero: Option<Monero>,
tor: Option<Tor>,
data: Option<PathBuf>,
is_testnet: bool,
debug: bool,
json: bool,
server_address: Option<SocketAddr>,
) -> Result<Context> {
let data_dir = data::data_dir_from(data, is_testnet)?;
let env_config = env_config_from(is_testnet);
START.call_once(|| {
let _ = cli::tracing::init(debug, json, data_dir.join("logs"));
});
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read seed in file")?;
let bitcoin_wallet = {
if let Some(bitcoin) = bitcoin {
let (bitcoin_electrum_rpc_url, bitcoin_target_block) =
bitcoin.apply_defaults(is_testnet)?;
Some(Arc::new(
init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir.clone(),
env_config,
bitcoin_target_block,
)
.await?,
))
} else {
None
}
};
let (monero_wallet, monero_rpc_process) = {
if let Some(monero) = monero {
let monero_daemon_address = monero.apply_defaults(is_testnet);
let (wlt, prc) =
init_monero_wallet(data_dir.clone(), monero_daemon_address, env_config).await?;
(Some(Arc::new(wlt)), Some(prc))
} else {
(None, None)
}
};
let tor_socks5_port = tor.map_or(9050, |tor| tor.tor_socks5_port);
let context = Context {
db: open_db(data_dir.join("sqlite")).await?,
bitcoin_wallet,
monero_wallet,
monero_rpc_process,
config: Config {
tor_socks5_port,
namespace: XmrBtcNamespace::from_is_testnet(is_testnet),
env_config,
seed: Some(seed),
server_address,
debug,
json,
is_testnet,
data_dir,
},
swap_lock: Arc::new(SwapLock::new()),
tasks: Arc::new(PendingTaskList::default()),
};
Ok(context)
}
pub async fn for_harness(
seed: Seed,
env_config: EnvConfig,
db_path: PathBuf,
bob_bitcoin_wallet: Arc<bitcoin::Wallet>,
bob_monero_wallet: Arc<monero::Wallet>,
) -> Self {
let config = Config::for_harness(seed, env_config);
Self {
bitcoin_wallet: Some(bob_bitcoin_wallet),
monero_wallet: Some(bob_monero_wallet),
config,
db: open_db(db_path)
.await
.expect("Could not open sqlite database"),
monero_rpc_process: None,
swap_lock: Arc::new(SwapLock::new()),
tasks: Arc::new(PendingTaskList::default()),
}
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "")
}
}
async fn init_bitcoin_wallet(
electrum_rpc_url: Url,
seed: &Seed,
data_dir: PathBuf,
env_config: EnvConfig,
bitcoin_target_block: usize,
) -> Result<bitcoin::Wallet> {
let wallet_dir = data_dir.join("wallet");
let wallet = bitcoin::Wallet::new(
electrum_rpc_url.clone(),
&wallet_dir,
seed.derive_extended_private_key(env_config.bitcoin_network)?,
env_config,
bitcoin_target_block,
)
.await
.context("Failed to initialize Bitcoin wallet")?;
wallet.sync().await?;
Ok(wallet)
}
async fn init_monero_wallet(
data_dir: PathBuf,
monero_daemon_address: String,
env_config: EnvConfig,
) -> Result<(monero::Wallet, monero::WalletRpcProcess)> {
let network = env_config.monero_network;
const MONERO_BLOCKCHAIN_MONITORING_WALLET_NAME: &str = "swap-tool-blockchain-monitoring-wallet";
let monero_wallet_rpc = monero::WalletRpc::new(data_dir.join("monero")).await?;
let monero_wallet_rpc_process = monero_wallet_rpc
.run(network, Some(monero_daemon_address))
.await?;
let monero_wallet = monero::Wallet::open_or_create(
monero_wallet_rpc_process.endpoint(),
MONERO_BLOCKCHAIN_MONITORING_WALLET_NAME.to_string(),
env_config,
)
.await?;
Ok((monero_wallet, monero_wallet_rpc_process))
}
mod data {
use super::*;
pub fn data_dir_from(arg_dir: Option<PathBuf>, testnet: bool) -> Result<PathBuf> {
let base_dir = match arg_dir {
Some(custom_base_dir) => custom_base_dir,
None => os_default()?,
};
let sub_directory = if testnet { "testnet" } else { "mainnet" };
Ok(base_dir.join(sub_directory))
}
fn os_default() -> Result<PathBuf> {
Ok(system_data_dir()?.join("cli"))
}
}
fn env_config_from(testnet: bool) -> EnvConfig {
if testnet {
Testnet::get_config()
} else {
Mainnet::get_config()
}
}
impl Config {
pub fn for_harness(seed: Seed, env_config: EnvConfig) -> Self {
let data_dir = data::data_dir_from(None, false).expect("Could not find data directory");
Self {
tor_socks5_port: 9050,
namespace: XmrBtcNamespace::from_is_testnet(false),
server_address: None,
env_config,
seed: Some(seed),
debug: false,
json: false,
is_testnet: false,
data_dir,
}
}
}
#[cfg(test)]
pub mod api_test {
use super::*;
use crate::api::request::{Method, Request};
use libp2p::Multiaddr;
use std::str::FromStr;
use uuid::Uuid;
pub const MULTI_ADDRESS: &str =
"/ip4/127.0.0.1/tcp/9939/p2p/12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi";
pub const MONERO_STAGENET_ADDRESS: &str = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAoNybEUvYBKHcnrSgxnVWgZsTvRBaHBNXPa8tHiCU51a";
pub const BITCOIN_TESTNET_ADDRESS: &str = "tb1qr3em6k3gfnyl8r7q0v7t4tlnyxzgxma3lressv";
pub const MONERO_MAINNET_ADDRESS: &str = "44Ato7HveWidJYUAVw5QffEcEtSH1DwzSP3FPPkHxNAS4LX9CqgucphTisH978FLHE34YNEx7FcbBfQLQUU8m3NUC4VqsRa";
pub const BITCOIN_MAINNET_ADDRESS: &str = "bc1qe4epnfklcaa0mun26yz5g8k24em5u9f92hy325";
pub const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b";
impl Config {
pub fn default(
is_testnet: bool,
data_dir: Option<PathBuf>,
debug: bool,
json: bool,
) -> Self {
let data_dir = data::data_dir_from(data_dir, is_testnet).unwrap();
let seed = Seed::from_file_or_generate(data_dir.as_path()).unwrap();
let env_config = env_config_from(is_testnet);
Self {
tor_socks5_port: 9050,
namespace: XmrBtcNamespace::from_is_testnet(is_testnet),
server_address: None,
env_config,
seed: Some(seed),
debug,
json,
is_testnet,
data_dir,
}
}
}
impl Request {
pub fn buy_xmr(is_testnet: bool) -> Request {
let seller = Multiaddr::from_str(MULTI_ADDRESS).unwrap();
let bitcoin_change_address = {
if is_testnet {
bitcoin::Address::from_str(BITCOIN_TESTNET_ADDRESS).unwrap()
} else {
bitcoin::Address::from_str(BITCOIN_MAINNET_ADDRESS).unwrap()
}
};
let monero_receive_address = {
if is_testnet {
monero::Address::from_str(MONERO_STAGENET_ADDRESS).unwrap()
} else {
monero::Address::from_str(MONERO_MAINNET_ADDRESS).unwrap()
}
};
Request::new(Method::BuyXmr {
seller,
bitcoin_change_address,
monero_receive_address,
swap_id: Uuid::new_v4(),
})
}
pub fn resume() -> Request {
Request::new(Method::Resume {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
})
}
pub fn cancel() -> Request {
Request::new(Method::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
})
}
pub fn refund() -> Request {
Request::new(Method::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
})
}
}
}

@ -0,0 +1,930 @@
use crate::api::Context;
use crate::bitcoin::{Amount, ExpiredTimelocks, TxLock};
use crate::cli::{list_sellers, EventLoop, SellerStatus};
use crate::libp2p_ext::MultiAddrExt;
use crate::network::quote::{BidQuote, ZeroQuoteReceived};
use crate::network::swarm;
use crate::protocol::bob::{BobState, Swap};
use crate::protocol::{bob, State};
use crate::{bitcoin, cli, monero, rpc};
use anyhow::{bail, Context as AnyContext, Result};
use libp2p::core::Multiaddr;
use qrcode::render::unicode;
use qrcode::QrCode;
use serde_json::json;
use std::cmp::min;
use std::convert::TryInto;
use std::future::Future;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tracing::{debug_span, field, Instrument, Span};
use uuid::Uuid;
#[derive(PartialEq, Debug)]
pub struct Request {
pub cmd: Method,
pub log_reference: Option<String>,
}
#[derive(Debug, PartialEq)]
pub enum Method {
BuyXmr {
seller: Multiaddr,
bitcoin_change_address: bitcoin::Address,
monero_receive_address: monero::Address,
swap_id: Uuid,
},
Resume {
swap_id: Uuid,
},
CancelAndRefund {
swap_id: Uuid,
},
MoneroRecovery {
swap_id: Uuid,
},
History,
Config,
WithdrawBtc {
amount: Option<Amount>,
address: bitcoin::Address,
},
Balance {
force_refresh: bool,
},
ListSellers {
rendezvous_point: Multiaddr,
},
ExportBitcoinWallet,
SuspendCurrentSwap,
StartDaemon {
server_address: Option<SocketAddr>,
},
GetCurrentSwap,
GetSwapInfo {
swap_id: Uuid,
},
GetRawStates,
}
impl Method {
fn get_tracing_span(&self, log_reference_id: Option<String>) -> Span {
let span = match self {
Method::Balance { .. } => {
debug_span!(
"method",
method_name = "Balance",
log_reference_id = field::Empty
)
}
Method::BuyXmr { swap_id, .. } => {
debug_span!("method", method_name="BuyXmr", swap_id=%swap_id, log_reference_id=field::Empty)
}
Method::CancelAndRefund { swap_id } => {
debug_span!("method", method_name="CancelAndRefund", swap_id=%swap_id, log_reference_id=field::Empty)
}
Method::Resume { swap_id } => {
debug_span!("method", method_name="Resume", swap_id=%swap_id, log_reference_id=field::Empty)
}
Method::Config => {
debug_span!(
"method",
method_name = "Config",
log_reference_id = field::Empty
)
}
Method::ExportBitcoinWallet => {
debug_span!(
"method",
method_name = "ExportBitcoinWallet",
log_reference_id = field::Empty
)
}
Method::GetCurrentSwap => {
debug_span!(
"method",
method_name = "GetCurrentSwap",
log_reference_id = field::Empty
)
}
Method::GetSwapInfo { .. } => {
debug_span!(
"method",
method_name = "GetSwapInfo",
log_reference_id = field::Empty
)
}
Method::History => {
debug_span!(
"method",
method_name = "History",
log_reference_id = field::Empty
)
}
Method::ListSellers { .. } => {
debug_span!(
"method",
method_name = "ListSellers",
log_reference_id = field::Empty
)
}
Method::MoneroRecovery { .. } => {
debug_span!(
"method",
method_name = "MoneroRecovery",
log_reference_id = field::Empty
)
}
Method::GetRawStates => debug_span!(
"method",
method_name = "RawHistory",
log_reference_id = field::Empty
),
Method::StartDaemon { .. } => {
debug_span!(
"method",
method_name = "StartDaemon",
log_reference_id = field::Empty
)
}
Method::SuspendCurrentSwap => {
debug_span!(
"method",
method_name = "SuspendCurrentSwap",
log_reference_id = field::Empty
)
}
Method::WithdrawBtc { .. } => {
debug_span!(
"method",
method_name = "WithdrawBtc",
log_reference_id = field::Empty
)
}
};
if let Some(log_reference_id) = log_reference_id {
span.record("log_reference_id", log_reference_id.as_str());
}
span
}
}
impl Request {
pub fn new(cmd: Method) -> Request {
Request {
cmd,
log_reference: None,
}
}
pub fn with_id(cmd: Method, id: Option<String>) -> Request {
Request {
cmd,
log_reference: id,
}
}
async fn handle_cmd(self, context: Arc<Context>) -> Result<serde_json::Value> {
match self.cmd {
Method::SuspendCurrentSwap => {
let swap_id = context.swap_lock.get_current_swap_id().await;
if let Some(id_value) = swap_id {
context.swap_lock.send_suspend_signal().await?;
Ok(json!({ "swapId": id_value }))
} else {
bail!("No swap is currently running")
}
}
Method::GetSwapInfo { swap_id } => {
let bitcoin_wallet = context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?;
let state = context.db.get_state(swap_id).await?;
let is_completed = state.swap_finished();
let peerId = context
.db
.get_peer_id(swap_id)
.await
.with_context(|| "Could not get PeerID")?;
let addresses = context
.db
.get_addresses(peerId)
.await
.with_context(|| "Could not get addressess")?;
let start_date = context.db.get_swap_start_date(swap_id).await?;
let swap_state: BobState = state.try_into()?;
let state_name = format!("{}", swap_state);
let (
xmr_amount,
btc_amount,
tx_lock_id,
tx_cancel_fee,
tx_refund_fee,
tx_lock_fee,
btc_refund_address,
cancel_timelock,
punish_timelock,
) = context
.db
.get_states(swap_id)
.await?
.iter()
.find_map(|state| {
if let State::Bob(BobState::SwapSetupCompleted(state2)) = state {
let xmr_amount = state2.xmr;
let btc_amount = state2.tx_lock.lock_amount().to_sat();
let tx_cancel_fee = state2.tx_cancel_fee.to_sat();
let tx_refund_fee = state2.tx_refund_fee.to_sat();
let tx_lock_id = state2.tx_lock.txid();
let btc_refund_address = state2.refund_address.to_string();
if let Ok(tx_lock_fee) = state2.tx_lock.fee() {
let tx_lock_fee = tx_lock_fee.to_sat();
Some((
xmr_amount,
btc_amount,
tx_lock_id,
tx_cancel_fee,
tx_refund_fee,
tx_lock_fee,
btc_refund_address,
state2.cancel_timelock,
state2.punish_timelock,
))
} else {
None
}
} else {
None
}
})
.with_context(|| "Did not find SwapSetupCompleted state for swap")?;
let timelock = match swap_state {
BobState::Started { .. }
| BobState::SafelyAborted
| BobState::SwapSetupCompleted(_) => None,
BobState::BtcLocked { state3: state, .. }
| BobState::XmrLockProofReceived { state, .. } => {
Some(state.expired_timelock(bitcoin_wallet).await)
}
BobState::XmrLocked(state) | BobState::EncSigSent(state) => {
Some(state.expired_timelock(bitcoin_wallet).await)
}
BobState::CancelTimelockExpired(state) | BobState::BtcCancelled(state) => {
Some(state.expired_timelock(bitcoin_wallet).await)
}
BobState::BtcPunished { .. } => Some(Ok(ExpiredTimelocks::Punish)),
BobState::BtcRefunded(_)
| BobState::BtcRedeemed(_)
| BobState::XmrRedeemed { .. } => None,
};
Ok(json!({
"swapId": swap_id,
"seller": {
"peerId": peerId.to_string(),
"addresses": addresses
},
"completed": is_completed,
"startDate": start_date,
"stateName": state_name,
"xmrAmount": xmr_amount,
"btcAmount": btc_amount,
"txLockId": tx_lock_id,
"txCancelFee": tx_cancel_fee,
"txRefundFee": tx_refund_fee,
"txLockFee": tx_lock_fee,
"btcRefundAddress": btc_refund_address.to_string(),
"cancelTimelock": cancel_timelock,
"punishTimelock": punish_timelock,
// If the timelock is None, it means that the swap is in a state where the timelock is not accessible to us.
// If that is the case, we return null. Otherwise, we return the timelock.
"timelock": timelock.map(|tl| tl.map(|tl| json!(tl)).unwrap_or(json!(null))).unwrap_or(json!(null)),
}))
}
Method::BuyXmr {
seller,
bitcoin_change_address,
monero_receive_address,
swap_id,
} => {
let bitcoin_wallet = Arc::clone(
context
.bitcoin_wallet
.as_ref()
.expect("Could not find Bitcoin wallet"),
);
let monero_wallet = Arc::clone(
context
.monero_wallet
.as_ref()
.context("Could not get Monero wallet")?,
);
let env_config = context.config.env_config;
let seed = context.config.seed.clone().context("Could not get seed")?;
let seller_peer_id = seller
.extract_peer_id()
.context("Seller address must contain peer ID")?;
context
.db
.insert_address(seller_peer_id, seller.clone())
.await?;
let behaviour = cli::Behaviour::new(
seller_peer_id,
env_config,
bitcoin_wallet.clone(),
(seed.derive_libp2p_identity(), context.config.namespace),
);
let mut swarm = swarm::cli(
seed.derive_libp2p_identity(),
context.config.tor_socks5_port,
behaviour,
)
.await?;
swarm.behaviour_mut().add_address(seller_peer_id, seller);
context
.db
.insert_monero_address(swap_id, monero_receive_address)
.await?;
tracing::debug!(peer_id = %swarm.local_peer_id(), "Network layer initialized");
context.swap_lock.acquire_swap_lock(swap_id).await?;
let initialize_swap = tokio::select! {
biased;
_ = context.swap_lock.listen_for_swap_force_suspension() => {
tracing::debug!("Shutdown signal received, exiting");
context.swap_lock.release_swap_lock().await.expect("Shutdown signal received but failed to release swap lock. The swap process has been terminated but the swap lock is still active.");
bail!("Shutdown signal received");
},
result = async {
let (event_loop, mut event_loop_handle) =
EventLoop::new(swap_id, swarm, seller_peer_id)?;
let event_loop = tokio::spawn(event_loop.run().in_current_span());
let bid_quote = event_loop_handle.request_quote().await?;
Ok::<_, anyhow::Error>((event_loop, event_loop_handle, bid_quote))
} => {
result
},
};
let (event_loop, event_loop_handle, bid_quote) = match initialize_swap {
Ok(result) => result,
Err(error) => {
tracing::error!(%swap_id, "Swap initialization failed: {:#}", error);
context
.swap_lock
.release_swap_lock()
.await
.expect("Could not release swap lock");
bail!(error);
}
};
context.tasks.clone().spawn(async move {
tokio::select! {
biased;
_ = context.swap_lock.listen_for_swap_force_suspension() => {
tracing::debug!("Shutdown signal received, exiting");
context.swap_lock.release_swap_lock().await.expect("Shutdown signal received but failed to release swap lock. The swap process has been terminated but the swap lock is still active.");
bail!("Shutdown signal received");
},
event_loop_result = event_loop => {
match event_loop_result {
Ok(_) => {
tracing::debug!(%swap_id, "EventLoop completed")
}
Err(error) => {
tracing::error!(%swap_id, "EventLoop failed: {:#}", error)
}
}
},
swap_result = async {
let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size());
let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount);
let determine_amount = determine_btc_to_swap(
context.config.json,
bid_quote,
bitcoin_wallet.new_address(),
|| bitcoin_wallet.balance(),
max_givable,
|| bitcoin_wallet.sync(),
estimate_fee,
);
let (amount, fees) = match determine_amount.await {
Ok(val) => val,
Err(error) => match error.downcast::<ZeroQuoteReceived>() {
Ok(_) => {
bail!("Seller's XMR balance is currently too low to initiate a swap, please try again later")
}
Err(other) => bail!(other),
},
};
tracing::info!(%amount, %fees, "Determined swap amount");
context.db.insert_peer_id(swap_id, seller_peer_id).await?;
let swap = Swap::new(
Arc::clone(&context.db),
swap_id,
Arc::clone(&bitcoin_wallet),
monero_wallet,
env_config,
event_loop_handle,
monero_receive_address,
bitcoin_change_address,
amount,
);
bob::run(swap).await
} => {
match swap_result {
Ok(state) => {
tracing::debug!(%swap_id, state=%state, "Swap completed")
}
Err(error) => {
tracing::error!(%swap_id, "Failed to complete swap: {:#}", error)
}
}
},
};
tracing::debug!(%swap_id, "Swap completed");
context
.swap_lock
.release_swap_lock()
.await
.expect("Could not release swap lock");
Ok::<_, anyhow::Error>(())
}.in_current_span()).await;
Ok(json!({
"swapId": swap_id.to_string(),
"quote": bid_quote,
}))
}
Method::Resume { swap_id } => {
context.swap_lock.acquire_swap_lock(swap_id).await?;
let seller_peer_id = context.db.get_peer_id(swap_id).await?;
let seller_addresses = context.db.get_addresses(seller_peer_id).await?;
let seed = context
.config
.seed
.as_ref()
.context("Could not get seed")?
.derive_libp2p_identity();
let behaviour = cli::Behaviour::new(
seller_peer_id,
context.config.env_config,
Arc::clone(
context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?,
),
(seed.clone(), context.config.namespace),
);
let mut swarm =
swarm::cli(seed.clone(), context.config.tor_socks5_port, behaviour).await?;
let our_peer_id = swarm.local_peer_id();
tracing::debug!(peer_id = %our_peer_id, "Network layer initialized");
for seller_address in seller_addresses {
swarm
.behaviour_mut()
.add_address(seller_peer_id, seller_address);
}
let (event_loop, event_loop_handle) =
EventLoop::new(swap_id, swarm, seller_peer_id)?;
let monero_receive_address = context.db.get_monero_address(swap_id).await?;
let swap = Swap::from_db(
Arc::clone(&context.db),
swap_id,
Arc::clone(
context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?,
),
Arc::clone(
context
.monero_wallet
.as_ref()
.context("Could not get Monero wallet")?,
),
context.config.env_config,
event_loop_handle,
monero_receive_address,
)
.await?;
context.tasks.clone().spawn(
async move {
let handle = tokio::spawn(event_loop.run().in_current_span());
tokio::select! {
biased;
_ = context.swap_lock.listen_for_swap_force_suspension() => {
tracing::debug!("Shutdown signal received, exiting");
context.swap_lock.release_swap_lock().await.expect("Shutdown signal received but failed to release swap lock. The swap process has been terminated but the swap lock is still active.");
bail!("Shutdown signal received");
},
event_loop_result = handle => {
match event_loop_result {
Ok(_) => {
tracing::debug!(%swap_id, "EventLoop completed during swap resume")
}
Err(error) => {
tracing::error!(%swap_id, "EventLoop failed during swap resume: {:#}", error)
}
}
},
swap_result = bob::run(swap) => {
match swap_result {
Ok(state) => {
tracing::debug!(%swap_id, state=%state, "Swap completed after resuming")
}
Err(error) => {
tracing::error!(%swap_id, "Failed to resume swap: {:#}", error)
}
}
}
}
context
.swap_lock
.release_swap_lock()
.await
.expect("Could not release swap lock");
Ok::<(), anyhow::Error>(())
}
.in_current_span(),
).await;
Ok(json!({
"result": "ok",
}))
}
Method::CancelAndRefund { swap_id } => {
let bitcoin_wallet = context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?;
context.swap_lock.acquire_swap_lock(swap_id).await?;
let state = cli::cancel_and_refund(
swap_id,
Arc::clone(bitcoin_wallet),
Arc::clone(&context.db),
)
.await;
context
.swap_lock
.release_swap_lock()
.await
.expect("Could not release swap lock");
state.map(|state| {
json!({
"result": state,
})
})
}
Method::History => {
let swaps = context.db.all().await?;
let mut vec: Vec<(Uuid, String)> = Vec::new();
for (swap_id, state) in swaps {
let state: BobState = state.try_into()?;
vec.push((swap_id, state.to_string()));
}
Ok(json!({ "swaps": vec }))
}
Method::GetRawStates => {
let raw_history = context.db.raw_all().await?;
Ok(json!({ "raw_states": raw_history }))
}
Method::Config => {
let data_dir_display = context.config.data_dir.display();
tracing::info!(path=%data_dir_display, "Data directory");
tracing::info!(path=%format!("{}/logs", data_dir_display), "Log files directory");
tracing::info!(path=%format!("{}/sqlite", data_dir_display), "Sqlite file location");
tracing::info!(path=%format!("{}/seed.pem", data_dir_display), "Seed file location");
tracing::info!(path=%format!("{}/monero", data_dir_display), "Monero-wallet-rpc directory");
tracing::info!(path=%format!("{}/wallet", data_dir_display), "Internal bitcoin wallet directory");
Ok(json!({
"log_files": format!("{}/logs", data_dir_display),
"sqlite": format!("{}/sqlite", data_dir_display),
"seed": format!("{}/seed.pem", data_dir_display),
"monero-wallet-rpc": format!("{}/monero", data_dir_display),
"bitcoin_wallet": format!("{}/wallet", data_dir_display),
}))
}
Method::WithdrawBtc { address, amount } => {
let bitcoin_wallet = context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?;
let amount = match amount {
Some(amount) => amount,
None => {
bitcoin_wallet
.max_giveable(address.script_pubkey().len())
.await?
}
};
let psbt = bitcoin_wallet
.send_to_address(address, amount, None)
.await?;
let signed_tx = bitcoin_wallet.sign_and_finalize(psbt).await?;
bitcoin_wallet
.broadcast(signed_tx.clone(), "withdraw")
.await?;
Ok(json!({
"signed_tx": signed_tx,
"amount": amount.to_sat(),
"txid": signed_tx.txid(),
}))
}
Method::StartDaemon { server_address } => {
// Default to 127.0.0.1:1234
let server_address = server_address.unwrap_or("127.0.0.1:1234".parse()?);
let (addr, server_handle) =
rpc::run_server(server_address, Arc::clone(&context)).await?;
tracing::info!(%addr, "Started RPC server");
server_handle.stopped().await;
tracing::info!("Stopped RPC server");
Ok(json!({}))
}
Method::Balance { force_refresh } => {
let bitcoin_wallet = context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?;
if force_refresh {
bitcoin_wallet.sync().await?;
}
let bitcoin_balance = bitcoin_wallet.balance().await?;
if force_refresh {
tracing::info!(
balance = %bitcoin_balance,
"Checked Bitcoin balance",
);
} else {
tracing::debug!(
balance = %bitcoin_balance,
"Current Bitcoin balance as of last sync",
);
}
Ok(json!({
"balance": bitcoin_balance.to_sat()
}))
}
Method::ListSellers { rendezvous_point } => {
let rendezvous_node_peer_id = rendezvous_point
.extract_peer_id()
.context("Rendezvous node address must contain peer ID")?;
let identity = context
.config
.seed
.as_ref()
.context("Cannot extract seed")?
.derive_libp2p_identity();
let sellers = list_sellers(
rendezvous_node_peer_id,
rendezvous_point,
context.config.namespace,
context.config.tor_socks5_port,
identity,
)
.await?;
for seller in &sellers {
match seller.status {
SellerStatus::Online(quote) => {
tracing::info!(
price = %quote.price.to_string(),
min_quantity = %quote.min_quantity.to_string(),
max_quantity = %quote.max_quantity.to_string(),
status = "Online",
address = %seller.multiaddr.to_string(),
"Fetched peer status"
);
}
SellerStatus::Unreachable => {
tracing::info!(
status = "Unreachable",
address = %seller.multiaddr.to_string(),
"Fetched peer status"
);
}
}
}
Ok(json!({ "sellers": sellers }))
}
Method::ExportBitcoinWallet => {
let bitcoin_wallet = context
.bitcoin_wallet
.as_ref()
.context("Could not get Bitcoin wallet")?;
let wallet_export = bitcoin_wallet.wallet_export("cli").await?;
tracing::info!(descriptor=%wallet_export.to_string(), "Exported bitcoin wallet");
Ok(json!({
"descriptor": wallet_export.to_string(),
}))
}
Method::MoneroRecovery { swap_id } => {
let swap_state: BobState = context.db.get_state(swap_id).await?.try_into()?;
if let BobState::BtcRedeemed(state5) = swap_state {
let (spend_key, view_key) = state5.xmr_keys();
let restore_height = state5.monero_wallet_restore_blockheight.height;
let address = monero::Address::standard(
context.config.env_config.monero_network,
monero::PublicKey::from_private_key(&spend_key),
monero::PublicKey::from(view_key.public()),
);
tracing::info!(restore_height=%restore_height, address=%address, spend_key=%spend_key, view_key=%view_key, "Monero recovery information");
Ok(json!({
"address": address,
"spend_key": spend_key.to_string(),
"view_key": view_key.to_string(),
"restore_height": state5.monero_wallet_restore_blockheight.height,
}))
} else {
bail!(
"Cannot print monero recovery information in state {}, only possible for BtcRedeemed",
swap_state
)
}
}
Method::GetCurrentSwap => Ok(json!({
"swap_id": context.swap_lock.get_current_swap_id().await
})),
}
}
pub async fn call(self, context: Arc<Context>) -> Result<serde_json::Value> {
let method_span = self.cmd.get_tracing_span(self.log_reference.clone());
self.handle_cmd(context)
.instrument(method_span.clone())
.await
.map_err(|err| {
method_span.in_scope(|| {
tracing::debug!(err = format!("{:?}", err), "API call resulted in an error");
});
err
})
}
}
fn qr_code(value: &impl ToString) -> Result<String> {
let code = QrCode::new(value.to_string())?;
let qr_code = code
.render::<unicode::Dense1x2>()
.dark_color(unicode::Dense1x2::Light)
.light_color(unicode::Dense1x2::Dark)
.build();
Ok(qr_code)
}
pub async fn determine_btc_to_swap<FB, TB, FMG, TMG, FS, TS, FFE, TFE>(
json: bool,
bid_quote: BidQuote,
get_new_address: impl Future<Output = Result<bitcoin::Address>>,
balance: FB,
max_giveable_fn: FMG,
sync: FS,
estimate_fee: FFE,
) -> Result<(Amount, Amount)>
where
TB: Future<Output = Result<Amount>>,
FB: Fn() -> TB,
TMG: Future<Output = Result<Amount>>,
FMG: Fn() -> TMG,
TS: Future<Output = Result<()>>,
FS: Fn() -> TS,
FFE: Fn(Amount) -> TFE,
TFE: Future<Output = Result<Amount>>,
{
if bid_quote.max_quantity == Amount::ZERO {
bail!(ZeroQuoteReceived)
}
tracing::info!(
price = %bid_quote.price,
minimum_amount = %bid_quote.min_quantity,
maximum_amount = %bid_quote.max_quantity,
"Received quote",
);
sync().await?;
let mut max_giveable = max_giveable_fn().await?;
if max_giveable == Amount::ZERO || max_giveable < bid_quote.min_quantity {
let deposit_address = get_new_address.await?;
let minimum_amount = bid_quote.min_quantity;
let maximum_amount = bid_quote.max_quantity;
if !json {
eprintln!("{}", qr_code(&deposit_address)?);
}
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,
"Waiting for Bitcoin deposit",
);
max_giveable = loop {
sync().await?;
let new_max_givable = max_giveable_fn().await?;
if new_max_givable > max_giveable {
break new_max_givable;
}
tokio::time::sleep(Duration::from_secs(1)).await;
};
let new_balance = balance().await?;
tracing::info!(%new_balance, %max_giveable, "Received Bitcoin");
if max_giveable < bid_quote.min_quantity {
tracing::info!("Deposited amount is less than `min_quantity`");
continue;
}
break;
}
};
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))
}

@ -226,7 +226,7 @@ pub enum Command {
name = "asb",
about = "Automated Swap Backend for swapping XMR for BTC",
author,
version = env!("VERGEN_GIT_SEMVER_LIGHTWEIGHT")
version = env!("VERGEN_GIT_DESCRIBE")
)]
pub struct RawArguments {
#[structopt(long, help = "Swap on testnet")]

@ -279,10 +279,10 @@ where
SwarmEvent::IncomingConnectionError { send_back_addr: address, error, .. } => {
tracing::warn!(%address, "Failed to set up connection with peer: {:#}", error);
}
SwarmEvent::ConnectionClosed { peer_id: peer, num_established, endpoint, cause: Some(error) } if num_established == 0 => {
SwarmEvent::ConnectionClosed { peer_id: peer, num_established: 0, endpoint, cause: Some(error) } => {
tracing::debug!(%peer, address = %endpoint.get_remote_address(), "Lost connection to peer: {:#}", error);
}
SwarmEvent::ConnectionClosed { peer_id: peer, num_established, endpoint, cause: None } if num_established == 0 => {
SwarmEvent::ConnectionClosed { peer_id: peer, num_established: 0, endpoint, cause: None } => {
tracing::info!(%peer, address = %endpoint.get_remote_address(), "Successfully closed connection");
}
SwarmEvent::NewListenAddr{address, ..} => {
@ -296,7 +296,7 @@ where
Some(Ok((peer, transfer_proof, responder))) => {
if !self.swarm.behaviour_mut().transfer_proof.is_connected(&peer) {
tracing::warn!(%peer, "No active connection to peer, buffering transfer proof");
self.buffered_transfer_proofs.entry(peer).or_insert_with(Vec::new).push((transfer_proof, responder));
self.buffered_transfer_proofs.entry(peer).or_default().push((transfer_proof, responder));
continue;
}

@ -302,7 +302,7 @@ pub mod rendezvous {
fn inject_disconnected(&mut self, peer_id: &PeerId) {
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
let node = &mut self.rendezvous_nodes[i];
if peer_id == &node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;
}
@ -325,7 +325,7 @@ pub mod rendezvous {
_error: &DialError,
) {
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
let node = &mut self.rendezvous_nodes[i];
if let Some(id) = peer_id {
if id == node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;

@ -12,43 +12,15 @@
#![forbid(unsafe_code)]
#![allow(non_snake_case)]
use anyhow::{bail, Context, Result};
use comfy_table::Table;
use qrcode::render::unicode;
use qrcode::QrCode;
use std::cmp::min;
use std::convert::TryInto;
use anyhow::Result;
use std::env;
use std::future::Future;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use swap::bitcoin::TxLock;
use swap::cli::command::{parse_args_and_apply_defaults, Arguments, Command, ParseResult};
use swap::cli::{list_sellers, EventLoop, SellerStatus};
use swap::cli::command::{parse_args_and_apply_defaults, ParseResult};
use swap::common::check_latest_version;
use swap::database::open_db;
use swap::env::Config;
use swap::libp2p_ext::MultiAddrExt;
use swap::network::quote::{BidQuote, ZeroQuoteReceived};
use swap::network::swarm;
use swap::protocol::bob;
use swap::protocol::bob::{BobState, Swap};
use swap::seed::Seed;
use swap::{bitcoin, cli, monero};
use url::Url;
use uuid::Uuid;
#[tokio::main]
async fn main() -> Result<()> {
let Arguments {
env_config,
data_dir,
debug,
json,
cmd,
} = match parse_args_and_apply_defaults(env::args_os())? {
ParseResult::Arguments(args) => *args,
let (context, request) = match parse_args_and_apply_defaults(env::args_os()).await? {
ParseResult::Context(context, request) => (context, request),
ParseResult::PrintAndExitZero { message } => {
println!("{}", message);
std::process::exit(0);
@ -58,601 +30,19 @@ async fn main() -> Result<()> {
if let Err(e) = check_latest_version(env!("CARGO_PKG_VERSION")).await {
eprintln!("{}", e);
}
match cmd {
Command::BuyXmr {
seller,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
bitcoin_change_address,
monero_receive_address,
monero_daemon_address,
tor_socks5_port,
namespace,
} => {
let swap_id = Uuid::new_v4();
cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?;
let db = open_db(data_dir.join("sqlite")).await?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir.clone(),
env_config,
bitcoin_target_block,
)
.await?;
let (monero_wallet, _process) =
init_monero_wallet(data_dir, monero_daemon_address, env_config).await?;
let bitcoin_wallet = Arc::new(bitcoin_wallet);
let seller_peer_id = seller
.extract_peer_id()
.context("Seller address must contain peer ID")?;
db.insert_address(seller_peer_id, seller.clone()).await?;
let behaviour = cli::Behaviour::new(
seller_peer_id,
env_config,
bitcoin_wallet.clone(),
(seed.derive_libp2p_identity(), namespace),
);
let mut swarm =
swarm::cli(seed.derive_libp2p_identity(), tor_socks5_port, behaviour).await?;
swarm.behaviour_mut().add_address(seller_peer_id, seller);
tracing::debug!(peer_id = %swarm.local_peer_id(), "Network layer initialized");
let (event_loop, mut event_loop_handle) =
EventLoop::new(swap_id, swarm, seller_peer_id)?;
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(),
bitcoin_wallet.new_address(),
|| bitcoin_wallet.balance(),
max_givable,
|| bitcoin_wallet.sync(),
estimate_fee,
)
.await
{
Ok(val) => val,
Err(error) => match error.downcast::<ZeroQuoteReceived>() {
Ok(_) => {
bail!("Seller's XMR balance is currently too low to initiate a swap, please try again later")
}
Err(other) => bail!(other),
},
};
tracing::info!(%amount, %fees, "Determined swap amount");
db.insert_peer_id(swap_id, seller_peer_id).await?;
db.insert_monero_address(swap_id, monero_receive_address)
.await?;
let swap = Swap::new(
db,
swap_id,
bitcoin_wallet,
Arc::new(monero_wallet),
env_config,
event_loop_handle,
monero_receive_address,
bitcoin_change_address,
amount,
);
tokio::select! {
result = event_loop => {
result
.context("EventLoop panicked")?;
},
result = bob::run(swap) => {
result.context("Failed to complete swap")?;
}
}
}
Command::History => {
cli::tracing::init(debug, json, data_dir.join("logs"), None)?;
let db = open_db(data_dir.join("sqlite")).await?;
let swaps = db.all().await?;
if json {
for (swap_id, state) in swaps {
let state: BobState = state.try_into()?;
tracing::info!(swap_id=%swap_id.to_string(), state=%state.to_string(), "Read swap state from database");
}
} else {
let mut table = Table::new();
table.set_header(vec!["SWAP ID", "STATE"]);
for (swap_id, state) in swaps {
let state: BobState = state.try_into()?;
table.add_row(vec![swap_id.to_string(), state.to_string()]);
}
println!("{}", table);
}
}
Command::Config => {
cli::tracing::init(debug, json, data_dir.join("logs"), None)?;
tracing::info!(path=%data_dir.display(), "Data directory");
tracing::info!(path=%format!("{}/logs", data_dir.display()), "Log files directory");
tracing::info!(path=%format!("{}/sqlite", data_dir.display()), "Sqlite file location");
tracing::info!(path=%format!("{}/seed.pem", data_dir.display()), "Seed file location");
tracing::info!(path=%format!("{}/monero", data_dir.display()), "Monero-wallet-rpc directory");
tracing::info!(path=%format!("{}/wallet", data_dir.display()), "Internal bitcoin wallet directory");
}
Command::WithdrawBtc {
bitcoin_electrum_rpc_url,
bitcoin_target_block,
amount,
address,
} => {
cli::tracing::init(debug, json, data_dir.join("logs"), None)?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir.clone(),
env_config,
bitcoin_target_block,
)
.await?;
let amount = match amount {
Some(amount) => amount,
None => {
bitcoin_wallet
.max_giveable(address.script_pubkey().len())
.await?
}
};
let psbt = bitcoin_wallet
.send_to_address(address, amount, None)
.await?;
let signed_tx = bitcoin_wallet.sign_and_finalize(psbt).await?;
bitcoin_wallet.broadcast(signed_tx, "withdraw").await?;
}
Command::Balance {
bitcoin_electrum_rpc_url,
bitcoin_target_block,
} => {
cli::tracing::init(debug, json, data_dir.join("logs"), None)?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir.clone(),
env_config,
bitcoin_target_block,
)
.await?;
let bitcoin_balance = bitcoin_wallet.balance().await?;
tracing::info!(
balance = %bitcoin_balance,
"Checked Bitcoin balance",
);
}
Command::Resume {
swap_id,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
monero_daemon_address,
tor_socks5_port,
namespace,
} => {
cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?;
let db = open_db(data_dir.join("sqlite")).await?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir.clone(),
env_config,
bitcoin_target_block,
)
.await?;
let (monero_wallet, _process) =
init_monero_wallet(data_dir, monero_daemon_address, env_config).await?;
let bitcoin_wallet = Arc::new(bitcoin_wallet);
let seller_peer_id = db.get_peer_id(swap_id).await?;
let seller_addresses = db.get_addresses(seller_peer_id).await?;
let behaviour = cli::Behaviour::new(
seller_peer_id,
env_config,
bitcoin_wallet.clone(),
(seed.derive_libp2p_identity(), namespace),
);
let mut swarm =
swarm::cli(seed.derive_libp2p_identity(), tor_socks5_port, behaviour).await?;
let our_peer_id = swarm.local_peer_id();
tracing::debug!(peer_id = %our_peer_id, "Network layer initialized");
for seller_address in seller_addresses {
swarm
.behaviour_mut()
.add_address(seller_peer_id, seller_address);
}
let (event_loop, event_loop_handle) = EventLoop::new(swap_id, swarm, seller_peer_id)?;
let handle = tokio::spawn(event_loop.run());
let monero_receive_address = db.get_monero_address(swap_id).await?;
let swap = Swap::from_db(
db,
swap_id,
bitcoin_wallet,
Arc::new(monero_wallet),
env_config,
event_loop_handle,
monero_receive_address,
)
.await?;
tokio::select! {
event_loop_result = handle => {
event_loop_result?;
},
swap_result = bob::run(swap) => {
swap_result?;
}
}
}
Command::CancelAndRefund {
swap_id,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
} => {
cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?;
let db = open_db(data_dir.join("sqlite")).await?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir,
env_config,
bitcoin_target_block,
)
.await?;
cli::cancel_and_refund(swap_id, Arc::new(bitcoin_wallet), db).await?;
}
Command::ListSellers {
rendezvous_point,
namespace,
tor_socks5_port,
} => {
let rendezvous_node_peer_id = rendezvous_point
.extract_peer_id()
.context("Rendezvous node address must contain peer ID")?;
cli::tracing::init(debug, json, data_dir.join("logs"), None)?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let identity = seed.derive_libp2p_identity();
let sellers = list_sellers(
rendezvous_node_peer_id,
rendezvous_point,
namespace,
tor_socks5_port,
identity,
)
.await?;
if json {
for seller in sellers {
match seller.status {
SellerStatus::Online(quote) => {
tracing::info!(
price = %quote.price.to_string(),
min_quantity = %quote.min_quantity.to_string(),
max_quantity = %quote.max_quantity.to_string(),
status = "Online",
address = %seller.multiaddr.to_string(),
"Fetched peer status"
);
}
SellerStatus::Unreachable => {
tracing::info!(
status = "Unreachable",
address = %seller.multiaddr.to_string(),
"Fetched peer status"
);
}
}
}
} else {
let mut table = Table::new();
table.set_header(vec![
"PRICE",
"MIN_QUANTITY",
"MAX_QUANTITY",
"STATUS",
"ADDRESS",
]);
for seller in sellers {
let row = match seller.status {
SellerStatus::Online(quote) => {
vec![
quote.price.to_string(),
quote.min_quantity.to_string(),
quote.max_quantity.to_string(),
"Online".to_owned(),
seller.multiaddr.to_string(),
]
}
SellerStatus::Unreachable => {
vec![
"???".to_owned(),
"???".to_owned(),
"???".to_owned(),
"Unreachable".to_owned(),
seller.multiaddr.to_string(),
]
}
};
table.add_row(row);
}
println!("{}", table);
}
}
Command::ExportBitcoinWallet {
bitcoin_electrum_rpc_url,
bitcoin_target_block,
} => {
cli::tracing::init(debug, json, data_dir.join("logs"), None)?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;
let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir.clone(),
env_config,
bitcoin_target_block,
)
.await?;
let wallet_export = bitcoin_wallet.wallet_export("cli").await?;
tracing::info!(descriptor=%wallet_export.to_string(), "Exported bitcoin wallet");
}
Command::MoneroRecovery { swap_id } => {
cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?;
let db = open_db(data_dir.join("sqlite")).await?;
let swap_state: BobState = db.get_state(swap_id).await?.try_into()?;
match swap_state {
BobState::Started { .. }
| BobState::SwapSetupCompleted(_)
| BobState::BtcLocked { .. }
| BobState::XmrLockProofReceived { .. }
| BobState::XmrLocked(_)
| BobState::EncSigSent(_)
| BobState::CancelTimelockExpired(_)
| BobState::BtcCancelled(_)
| BobState::BtcRefunded(_)
| BobState::BtcPunished { .. }
| BobState::SafelyAborted
| BobState::XmrRedeemed { .. } => {
bail!("Cannot print monero recovery information in state {}, only possible for BtcRedeemed", swap_state)
}
BobState::BtcRedeemed(state5) => {
let (spend_key, view_key) = state5.xmr_keys();
let address = monero::Address::standard(
env_config.monero_network,
monero::PublicKey::from_private_key(&spend_key),
monero::PublicKey::from(view_key.public()),
);
tracing::info!("Wallet address: {}", address.to_string());
let view_key = serde_json::to_string(&view_key)?;
println!("View key: {}", view_key);
println!("Spend key: {}", spend_key);
}
}
}
};
request.call(context.clone()).await?;
context.tasks.wait_for_tasks().await?;
Ok(())
}
async fn init_bitcoin_wallet(
electrum_rpc_url: Url,
seed: &Seed,
data_dir: PathBuf,
env_config: Config,
bitcoin_target_block: usize,
) -> Result<bitcoin::Wallet> {
tracing::debug!("Initializing bitcoin wallet");
let xprivkey = seed.derive_extended_private_key(env_config.bitcoin_network)?;
let wallet = bitcoin::Wallet::new(
electrum_rpc_url.clone(),
data_dir,
xprivkey,
env_config,
bitcoin_target_block,
)
.await
.context("Failed to initialize Bitcoin wallet")?;
tracing::debug!("Syncing bitcoin wallet");
wallet.sync().await?;
Ok(wallet)
}
async fn init_monero_wallet(
data_dir: PathBuf,
monero_daemon_address: Option<String>,
env_config: Config,
) -> Result<(monero::Wallet, monero::WalletRpcProcess)> {
let network = env_config.monero_network;
const MONERO_BLOCKCHAIN_MONITORING_WALLET_NAME: &str = "swap-tool-blockchain-monitoring-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)
.await?;
let monero_wallet = monero::Wallet::open_or_create(
monero_wallet_rpc_process.endpoint(),
MONERO_BLOCKCHAIN_MONITORING_WALLET_NAME.to_string(),
env_config,
)
.await?;
Ok((monero_wallet, monero_wallet_rpc_process))
}
fn qr_code(value: &impl ToString) -> Result<String> {
let code = QrCode::new(value.to_string())?;
let qr_code = code
.render::<unicode::Dense1x2>()
.dark_color(unicode::Dense1x2::Light)
.light_color(unicode::Dense1x2::Dark)
.build();
Ok(qr_code)
}
async fn determine_btc_to_swap<FB, TB, FMG, TMG, FS, TS, FFE, TFE>(
json: bool,
bid_quote: impl Future<Output = Result<BidQuote>>,
get_new_address: impl Future<Output = Result<bitcoin::Address>>,
balance: FB,
max_giveable_fn: FMG,
sync: FS,
estimate_fee: FFE,
) -> Result<(bitcoin::Amount, bitcoin::Amount)>
where
TB: Future<Output = Result<bitcoin::Amount>>,
FB: Fn() -> TB,
TMG: Future<Output = Result<bitcoin::Amount>>,
FMG: Fn() -> TMG,
TS: Future<Output = Result<()>>,
FS: Fn() -> TS,
FFE: Fn(bitcoin::Amount) -> TFE,
TFE: Future<Output = Result<bitcoin::Amount>>,
{
tracing::debug!("Requesting quote");
let bid_quote = bid_quote.await?;
if bid_quote.max_quantity == bitcoin::Amount::ZERO {
bail!(ZeroQuoteReceived)
}
tracing::info!(
price = %bid_quote.price,
minimum_amount = %bid_quote.min_quantity,
maximum_amount = %bid_quote.max_quantity,
"Received quote",
);
let mut max_giveable = max_giveable_fn().await?;
if max_giveable == bitcoin::Amount::ZERO || max_giveable < bid_quote.min_quantity {
let deposit_address = get_new_address.await?;
let minimum_amount = bid_quote.min_quantity;
let maximum_amount = bid_quote.max_quantity;
if !json {
eprintln!("{}", qr_code(&deposit_address)?);
}
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,
"Waiting for Bitcoin deposit",
);
max_giveable = loop {
sync().await?;
let new_max_givable = max_giveable_fn().await?;
if new_max_givable > max_giveable {
break new_max_givable;
}
tokio::time::sleep(Duration::from_secs(1)).await;
};
let new_balance = balance().await?;
tracing::info!(%new_balance, %max_giveable, "Received Bitcoin");
if max_giveable < bid_quote.min_quantity {
tracing::info!("Deposited amount is less than `min_quantity`");
continue;
}
break;
}
};
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))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::determine_btc_to_swap;
use ::bitcoin::Amount;
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use swap::api::request::determine_btc_to_swap;
use swap::network::quote::BidQuote;
use swap::tracing_ext::capture_logs;
use tracing::level_filters::LevelFilter;
@ -666,7 +56,7 @@ mod tests {
let (amount, fees) = determine_btc_to_swap(
true,
async { Ok(quote_with_max(0.01)) },
quote_with_max(0.01),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.001)?) },
|| async {
@ -685,10 +75,10 @@ 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: 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
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Deposit at least 0.00001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001 BTC max_giveable=0 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.001 BTC max_giveable=0.0009 BTC
"
);
}
@ -703,7 +93,7 @@ mod tests {
let (amount, fees) = determine_btc_to_swap(
true,
async { Ok(quote_with_max(0.01)) },
quote_with_max(0.01),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.1001)?) },
|| async {
@ -722,10 +112,10 @@ 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: 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
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Deposit at least 0.00001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001 BTC max_giveable=0 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.1001 BTC max_giveable=0.1 BTC
"
);
}
@ -740,7 +130,7 @@ mod tests {
let (amount, fees) = determine_btc_to_swap(
true,
async { Ok(quote_with_max(0.01)) },
quote_with_max(0.01),
async { panic!("should not request new address when initial balance is > 0") },
|| async { Ok(Amount::from_btc(0.005)?) },
|| async {
@ -759,7 +149,7 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC\n"
" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC\n"
);
}
@ -773,7 +163,7 @@ mod tests {
let (amount, fees) = determine_btc_to_swap(
true,
async { Ok(quote_with_max(0.01)) },
quote_with_max(0.01),
async { panic!("should not request new address when initial balance is > 0") },
|| async { Ok(Amount::from_btc(0.1001)?) },
|| async {
@ -792,7 +182,7 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC\n"
" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC\n"
);
}
@ -806,7 +196,7 @@ mod tests {
let (amount, fees) = determine_btc_to_swap(
true,
async { Ok(quote_with_min(0.01)) },
quote_with_min(0.01),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.0101)?) },
|| async {
@ -825,10 +215,10 @@ 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.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
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.01001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.01001 BTC max_giveable=0 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.0101 BTC max_giveable=0.01 BTC
"
);
}
@ -843,7 +233,7 @@ mod tests {
let (amount, fees) = determine_btc_to_swap(
true,
async { Ok(quote_with_min(0.01)) },
quote_with_min(0.01),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.0101)?) },
|| async {
@ -862,10 +252,10 @@ 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.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
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.00991 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00991 BTC max_giveable=0.0001 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.0101 BTC max_giveable=0.01 BTC
"
);
}
@ -885,7 +275,7 @@ mod tests {
Duration::from_secs(1),
determine_btc_to_swap(
true,
async { Ok(quote_with_min(0.1)) },
quote_with_min(0.1),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.0101)?) },
|| async {
@ -902,13 +292,13 @@ mod tests {
assert!(matches!(error, tokio::time::error::Elapsed { .. }));
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: 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: 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
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.10001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001 BTC max_giveable=0 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.0101 BTC max_giveable=0.01 BTC
INFO swap::api::request: Deposited amount is less than `min_quantity`
INFO swap::api::request: Deposit at least 0.09001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.09001 BTC max_giveable=0.01 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
"
);
}
@ -933,7 +323,7 @@ mod tests {
Duration::from_secs(10),
determine_btc_to_swap(
true,
async { Ok(quote_with_min(0.1)) },
quote_with_min(0.1),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.21)?) },
|| async {
@ -951,10 +341,10 @@ 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: 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
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.10001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001 BTC max_giveable=0 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.21 BTC max_giveable=0.2 BTC
"
);
}
@ -968,7 +358,7 @@ mod tests {
let determination_error = determine_btc_to_swap(
true,
async { Ok(quote_with_max(0.00)) },
quote_with_max(0.00),
get_dummy_address(),
|| async { Ok(Amount::from_btc(0.0101)?) },
|| async {

@ -15,7 +15,8 @@ pub use crate::bitcoin::refund::TxRefund;
pub use crate::bitcoin::timelocks::{BlockHeight, ExpiredTimelocks};
pub use ::bitcoin::util::amount::Amount;
pub use ::bitcoin::util::psbt::PartiallySignedTransaction;
pub use ::bitcoin::{Address, Network, Transaction, Txid};
pub use ::bitcoin::{Address, AddressType, Network, Transaction, Txid};
use bitcoin::secp256k1::ecdsa;
pub use ecdsa_fun::adaptor::EncryptedSignature;
pub use ecdsa_fun::fun::Scalar;
pub use ecdsa_fun::Signature;
@ -25,9 +26,8 @@ pub use wallet::Wallet;
pub use wallet::WalletBuilder;
use crate::bitcoin::wallet::ScriptStatus;
use ::bitcoin::hashes::hex::ToHex;
use ::bitcoin::hashes::Hash;
use ::bitcoin::{secp256k1, Sighash};
use ::bitcoin::Sighash;
use anyhow::{bail, Context, Result};
use bdk::miniscript::descriptor::Wsh;
use bdk::miniscript::{Descriptor, Segwitv0};
@ -206,20 +206,21 @@ pub fn verify_encsig(
#[error("encrypted signature is invalid")]
pub struct InvalidEncryptedSignature;
pub fn build_shared_output_descriptor(A: Point, B: Point) -> Descriptor<bitcoin::PublicKey> {
pub fn build_shared_output_descriptor(
A: Point,
B: Point,
) -> Result<Descriptor<bitcoin::PublicKey>> {
const MINISCRIPT_TEMPLATE: &str = "c:and_v(v:pk(A),pk_k(B))";
// NOTE: This shouldn't be a source of error, but maybe it is
let A = ToHex::to_hex(&secp256k1::PublicKey::from(A));
let B = ToHex::to_hex(&secp256k1::PublicKey::from(B));
let miniscript = MINISCRIPT_TEMPLATE.replace('A', &A).replace('B', &B);
let miniscript = MINISCRIPT_TEMPLATE
.replace('A', &A.to_string())
.replace('B', &B.to_string());
let miniscript =
bdk::miniscript::Miniscript::<bitcoin::PublicKey, Segwitv0>::from_str(&miniscript)
.expect("a valid miniscript");
Descriptor::Wsh(Wsh::new(miniscript).expect("a valid descriptor"))
Ok(Descriptor::Wsh(Wsh::new(miniscript)?))
}
pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Result<SecretKey> {
@ -244,10 +245,72 @@ pub fn current_epoch(
}
if tx_lock_status.is_confirmed_with(cancel_timelock) {
return ExpiredTimelocks::Cancel;
return ExpiredTimelocks::Cancel {
blocks_left: tx_cancel_status.blocks_left_until(punish_timelock),
};
}
ExpiredTimelocks::None
ExpiredTimelocks::None {
blocks_left: tx_lock_status.blocks_left_until(cancel_timelock),
}
}
pub mod bitcoin_address {
use anyhow::{bail, Result};
use serde::Serialize;
use std::str::FromStr;
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Serialize)]
#[error("Invalid Bitcoin address provided, expected address on network {expected:?} but address provided is on {actual:?}")]
pub struct BitcoinAddressNetworkMismatch {
#[serde(with = "crate::bitcoin::network")]
expected: bitcoin::Network,
#[serde(with = "crate::bitcoin::network")]
actual: bitcoin::Network,
}
pub fn parse(addr_str: &str) -> Result<bitcoin::Address> {
let address = bitcoin::Address::from_str(addr_str)?;
if address.address_type() != Some(bitcoin::AddressType::P2wpkh) {
anyhow::bail!("Invalid Bitcoin address provided, only bech32 format is supported!")
}
Ok(address)
}
pub fn validate(
address: bitcoin::Address,
expected_network: bitcoin::Network,
) -> Result<bitcoin::Address> {
if address.network != expected_network {
bail!(BitcoinAddressNetworkMismatch {
expected: expected_network,
actual: address.network
});
}
Ok(address)
}
pub fn validate_is_testnet(
address: bitcoin::Address,
is_testnet: bool,
) -> Result<bitcoin::Address> {
let expected_network = if is_testnet {
bitcoin::Network::Testnet
} else {
bitcoin::Network::Bitcoin
};
validate(address, expected_network)
}
}
// Transform the ecdsa der signature bytes into a secp256kfun ecdsa signature type.
pub fn extract_ecdsa_sig(sig: &[u8]) -> Result<Signature> {
let data = &sig[..sig.len() - 1];
let sig = ecdsa::Signature::from_der(data)?.serialize_compact();
Signature::from_bytes(sig).ok_or(anyhow::anyhow!("invalid signature"))
}
/// Bitcoin error codes: https://github.com/bitcoin/bitcoin/blob/97d3500601c1d28642347d014a6de1e38f53ae4e/src/rpc/protocol.h#L23
@ -323,7 +386,10 @@ mod tests {
use super::*;
use crate::env::{GetConfig, Regtest};
use crate::protocol::{alice, bob};
use bitcoin::secp256k1;
use ecdsa_fun::fun::marker::{NonZero, Public};
use rand::rngs::OsRng;
use std::matches;
use uuid::Uuid;
#[test]
@ -338,7 +404,7 @@ mod tests {
tx_cancel_status,
);
assert_eq!(expired_timelock, ExpiredTimelocks::None)
assert!(matches!(expired_timelock, ExpiredTimelocks::None { .. }));
}
#[test]
@ -353,7 +419,7 @@ mod tests {
tx_cancel_status,
);
assert_eq!(expired_timelock, ExpiredTimelocks::Cancel)
assert!(matches!(expired_timelock, ExpiredTimelocks::Cancel { .. }));
}
#[test]
@ -468,4 +534,16 @@ mod tests {
transaction
)
}
#[test]
fn compare_point_hex() {
// secp256kfun Point and secp256k1 PublicKey should have the same bytes and hex representation
let secp = secp256k1::Secp256k1::default();
let keypair = secp256k1::KeyPair::new(&secp, &mut OsRng);
let pubkey = keypair.public_key();
let point: Point<_, Public, NonZero> = Point::from_bytes(pubkey.serialize()).unwrap();
assert_eq!(pubkey.to_string(), point.to_string());
}
}

@ -5,7 +5,8 @@ use crate::bitcoin::{
};
use ::bitcoin::util::sighash::SighashCache;
use ::bitcoin::{
EcdsaSighashType, OutPoint, PackedLockTime, Script, Sequence, Sighash, TxIn, TxOut, Txid,
secp256k1, EcdsaSighashType, OutPoint, PackedLockTime, Script, Sequence, Sighash, TxIn, TxOut,
Txid,
};
use anyhow::Result;
use bdk::miniscript::Descriptor;
@ -24,6 +25,12 @@ use std::ops::Add;
#[serde(transparent)]
pub struct CancelTimelock(u32);
impl From<CancelTimelock> for u32 {
fn from(cancel_timelock: CancelTimelock) -> Self {
cancel_timelock.0
}
}
impl CancelTimelock {
pub const fn new(number_of_blocks: u32) -> Self {
Self(number_of_blocks)
@ -64,6 +71,12 @@ impl fmt::Display for CancelTimelock {
#[serde(transparent)]
pub struct PunishTimelock(u32);
impl From<PunishTimelock> for u32 {
fn from(punish_timelock: PunishTimelock) -> Self {
punish_timelock.0
}
}
impl PunishTimelock {
pub const fn new(number_of_blocks: u32) -> Self {
Self(number_of_blocks)
@ -105,8 +118,8 @@ impl TxCancel {
A: PublicKey,
B: PublicKey,
spending_fee: Amount,
) -> Self {
let cancel_output_descriptor = build_shared_output_descriptor(A.0, B.0);
) -> Result<Self> {
let cancel_output_descriptor = build_shared_output_descriptor(A.0, B.0)?;
let tx_in = TxIn {
previous_output: tx_lock.as_outpoint(),
@ -136,12 +149,12 @@ impl TxCancel {
)
.expect("sighash");
Self {
Ok(Self {
inner: transaction,
digest,
output_descriptor: cancel_output_descriptor,
lock_output_descriptor: tx_lock.output_descriptor.clone(),
}
})
}
pub fn txid(&self) -> Txid {
@ -202,25 +215,27 @@ impl TxCancel {
let A = ::bitcoin::PublicKey {
compressed: true,
inner: A.0.into(),
inner: secp256k1::PublicKey::from_slice(&A.0.to_bytes())?,
};
let B = ::bitcoin::PublicKey {
compressed: true,
inner: B.0.into(),
inner: secp256k1::PublicKey::from_slice(&B.0.to_bytes())?,
};
// The order in which these are inserted doesn't matter
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);

@ -4,9 +4,10 @@ use crate::bitcoin::{
};
use ::bitcoin::util::psbt::PartiallySignedTransaction;
use ::bitcoin::{OutPoint, TxIn, TxOut, Txid};
use anyhow::{bail, Result};
use anyhow::{bail, Context, Result};
use bdk::database::BatchDatabase;
use bdk::miniscript::Descriptor;
use bdk::psbt::PsbtUtils;
use bitcoin::{PackedLockTime, Script, Sequence};
use serde::{Deserialize, Serialize};
@ -31,7 +32,7 @@ impl TxLock {
C: EstimateFeeRate,
D: BatchDatabase,
{
let lock_output_descriptor = build_shared_output_descriptor(A.0, B.0);
let lock_output_descriptor = build_shared_output_descriptor(A.0, B.0)?;
let address = lock_output_descriptor
.address(wallet.get_network())
.expect("can derive address from descriptor");
@ -83,7 +84,7 @@ impl TxLock {
}
};
let descriptor = build_shared_output_descriptor(A.0, B.0);
let descriptor = build_shared_output_descriptor(A.0, B.0)?;
let legit_shared_output_script = descriptor.script_pubkey();
if shared_output_candidate.script_pubkey != legit_shared_output_script {
@ -100,6 +101,15 @@ impl TxLock {
Amount::from_sat(self.inner.clone().extract_tx().output[self.lock_output_vout()].value)
}
pub fn fee(&self) -> Result<Amount> {
Ok(Amount::from_sat(
self.inner
.clone()
.fee_amount()
.context("The PSBT is missing a TxOut for an input")?,
))
}
pub fn txid(&self) -> Txid {
self.inner.clone().extract_tx().txid()
}
@ -253,7 +263,7 @@ mod tests {
fn estimated_tx_lock_script_size_never_changes(a in crate::proptest::ecdsa_fun::point(), b in crate::proptest::ecdsa_fun::point()) {
proptest::prop_assume!(a != b);
let computed_size = build_shared_output_descriptor(a, b).script_pubkey().len();
let computed_size = build_shared_output_descriptor(a, b).unwrap().script_pubkey().len();
assert_eq!(computed_size, SCRIPT_SIZE);
}

@ -1,7 +1,7 @@
use crate::bitcoin::wallet::Watchable;
use crate::bitcoin::{self, Address, Amount, PunishTimelock, Transaction, TxCancel, Txid};
use ::bitcoin::util::sighash::SighashCache;
use ::bitcoin::{EcdsaSighashType, Sighash};
use ::bitcoin::{secp256k1, EcdsaSighashType, Sighash};
use anyhow::{Context, Result};
use bdk::bitcoin::Script;
use bdk::miniscript::Descriptor;
@ -64,18 +64,20 @@ impl TxPunish {
let A = a.public().try_into()?;
let B = B.try_into()?;
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
// The order in which these are inserted doesn't matter
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);

@ -6,7 +6,7 @@ use crate::bitcoin::{
use ::bitcoin::{Sighash, Txid};
use anyhow::{bail, Context, Result};
use bdk::miniscript::Descriptor;
use bitcoin::secp256k1::ecdsa;
use bitcoin::secp256k1;
use bitcoin::util::sighash::SighashCache;
use bitcoin::{EcdsaSighashType, Script};
use ecdsa_fun::adaptor::{Adaptor, HashTranscript};
@ -16,6 +16,8 @@ use ecdsa_fun::Signature;
use sha2::Sha256;
use std::collections::HashMap;
use super::extract_ecdsa_sig;
#[derive(Clone, Debug)]
pub struct TxRedeem {
inner: Transaction,
@ -64,7 +66,7 @@ impl TxRedeem {
) -> Result<Transaction> {
verify_encsig(
B,
PublicKey::from(s_a.clone()),
PublicKey::from(s_a),
&self.digest(),
&encrypted_signature,
)
@ -79,25 +81,27 @@ impl TxRedeem {
let A = ::bitcoin::PublicKey {
compressed: true,
inner: a.public.into(),
inner: secp256k1::PublicKey::from_slice(&a.public.to_bytes())?,
};
let B = ::bitcoin::PublicKey {
compressed: true,
inner: B.0.into(),
inner: secp256k1::PublicKey::from_slice(&B.0.to_bytes())?,
};
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
// The order in which these are inserted doesn't matter
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);
@ -120,16 +124,16 @@ impl TxRedeem {
let input = match candidate_transaction.input.as_slice() {
[input] => input,
[] => bail!(NoInputs),
[inputs @ ..] => bail!(TooManyInputs(inputs.len())),
inputs => bail!(TooManyInputs(inputs.len())),
};
let sigs = match input.witness.iter().collect::<Vec<_>>().as_slice() {
let sigs = match input.witness.to_vec().as_slice() {
[sig_1, sig_2, _script] => [sig_1, sig_2]
.iter()
.map(|sig| ecdsa::Signature::from_der(&sig[..sig.len() - 1]).map(Signature::from))
.collect::<std::result::Result<Vec<_>, _>>(),
.into_iter()
.map(|sig| extract_ecdsa_sig(sig))
.collect::<Result<Vec<_>, _>>(),
[] => bail!(EmptyWitnessStack),
[witnesses @ ..] => bail!(NotThreeWitnesses(witnesses.len())),
witnesses => bail!(NotThreeWitnesses(witnesses.len())),
}?;
let sig = sigs

@ -4,7 +4,7 @@ use crate::bitcoin::{
TooManyInputs, Transaction, TxCancel,
};
use crate::{bitcoin, monero};
use ::bitcoin::secp256k1::ecdsa;
use ::bitcoin::secp256k1;
use ::bitcoin::util::sighash::SighashCache;
use ::bitcoin::{EcdsaSighashType, Script, Sighash, Txid};
use anyhow::{bail, Context, Result};
@ -12,6 +12,8 @@ use bdk::miniscript::Descriptor;
use ecdsa_fun::Signature;
use std::collections::HashMap;
use super::extract_ecdsa_sig;
#[derive(Debug)]
pub struct TxRefund {
inner: Transaction,
@ -62,25 +64,27 @@ impl TxRefund {
let A = ::bitcoin::PublicKey {
compressed: true,
inner: A.0.into(),
inner: secp256k1::PublicKey::from_slice(&A.0.to_bytes())?,
};
let B = ::bitcoin::PublicKey {
compressed: true,
inner: B.0.into(),
inner: secp256k1::PublicKey::from_slice(&B.0.to_bytes())?,
};
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
// The order in which these are inserted doesn't matter
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);
@ -127,16 +131,16 @@ impl TxRefund {
let input = match candidate_transaction.input.as_slice() {
[input] => input,
[] => bail!(NoInputs),
[inputs @ ..] => bail!(TooManyInputs(inputs.len())),
inputs => bail!(TooManyInputs(inputs.len())),
};
let sigs = match input.witness.iter().collect::<Vec<_>>().as_slice() {
let sigs = match input.witness.to_vec().as_slice() {
[sig_1, sig_2, _script] => [sig_1, sig_2]
.iter()
.map(|sig| ecdsa::Signature::from_der(&sig[..sig.len() - 1]).map(Signature::from))
.collect::<std::result::Result<Vec<_>, _>>(),
.into_iter()
.map(|sig| extract_ecdsa_sig(sig))
.collect::<Result<Vec<_>, _>>(),
[] => bail!(EmptyWitnessStack),
[witnesses @ ..] => bail!(NotThreeWitnesses(witnesses.len())),
witnesses => bail!(NotThreeWitnesses(witnesses.len())),
}?;
let sig = sigs

@ -37,9 +37,9 @@ impl Add<u32> for BlockHeight {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExpiredTimelocks {
None,
Cancel,
None { blocks_left: u32 },
Cancel { blocks_left: u32 },
Punish,
}

@ -24,6 +24,7 @@ use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::{watch, Mutex};
use tracing::{debug_span, Instrument};
const SLED_TREE_NAME: &str = "default_tree";
@ -64,9 +65,7 @@ impl Wallet {
database,
) {
Ok(w) => w,
Err(e) if matches!(e, bdk::Error::ChecksumMismatch) => {
Self::migrate(data_dir, xprivkey, network)?
}
Err(bdk::Error::ChecksumMismatch) => Self::migrate(data_dir, xprivkey, network)?,
err => err?,
};
@ -192,7 +191,7 @@ impl Wallet {
tokio::time::sleep(Duration::from_secs(5)).await;
}
});
}.instrument(debug_span!("BitcoinWalletSubscription")));
Subscription {
receiver,
@ -274,7 +273,7 @@ impl Subscription {
pub async fn wait_until_confirmed_with<T>(&self, target: T) -> Result<()>
where
u32: PartialOrd<T>,
T: Into<u32>,
T: Copy,
{
self.wait_until(|status| status.is_confirmed_with(target))
@ -933,9 +932,20 @@ impl Confirmed {
pub fn meets_target<T>(&self, target: T) -> bool
where
u32: PartialOrd<T>,
T: Into<u32>,
{
self.confirmations() >= target.into()
}
pub fn blocks_left_until<T>(&self, target: T) -> u32
where
T: Into<u32> + Copy,
{
self.confirmations() >= target
if self.meets_target(target) {
0
} else {
target.into() - self.confirmations()
}
}
}
@ -948,7 +958,7 @@ impl ScriptStatus {
/// Check if the script has met the given confirmation target.
pub fn is_confirmed_with<T>(&self, target: T) -> bool
where
u32: PartialOrd<T>,
T: Into<u32>,
{
match self {
ScriptStatus::Confirmed(inner) => inner.meets_target(target),
@ -956,6 +966,17 @@ impl ScriptStatus {
}
}
// Calculate the number of blocks left until the target is met.
pub fn blocks_left_until<T>(&self, target: T) -> u32
where
T: Into<u32> + Copy,
{
match self {
ScriptStatus::Confirmed(inner) => inner.blocks_left_until(target),
_ => target.into(),
}
}
pub fn has_been_seen(&self) -> bool {
matches!(self, ScriptStatus::InMempool | ScriptStatus::Confirmed(_))
}
@ -987,7 +1008,7 @@ mod tests {
fn given_depth_0_should_meet_confirmation_target_one() {
let script = ScriptStatus::Confirmed(Confirmed { depth: 0 });
let confirmed = script.is_confirmed_with(1);
let confirmed = script.is_confirmed_with(1_u32);
assert!(confirmed)
}
@ -996,7 +1017,7 @@ mod tests {
fn given_confirmations_1_should_meet_confirmation_target_one() {
let script = ScriptStatus::from_confirmations(1);
let confirmed = script.is_confirmed_with(1);
let confirmed = script.is_confirmed_with(1_u32);
assert!(confirmed)
}
@ -1011,6 +1032,33 @@ mod tests {
assert_eq!(confirmed.depth, 0)
}
#[test]
fn given_depth_0_should_return_0_blocks_left_until_1() {
let script = ScriptStatus::Confirmed(Confirmed { depth: 0 });
let blocks_left = script.blocks_left_until(1_u32);
assert_eq!(blocks_left, 0)
}
#[test]
fn given_depth_1_should_return_0_blocks_left_until_1() {
let script = ScriptStatus::Confirmed(Confirmed { depth: 1 });
let blocks_left = script.blocks_left_until(1_u32);
assert_eq!(blocks_left, 0)
}
#[test]
fn given_depth_0_should_return_1_blocks_left_until_2() {
let script = ScriptStatus::Confirmed(Confirmed { depth: 0 });
let blocks_left = script.blocks_left_until(2_u32);
assert_eq!(blocks_left, 1)
}
#[test]
fn given_one_BTC_and_100k_sats_per_vb_fees_should_not_hit_max() {
// 400 weight = 100 vbyte

@ -10,7 +10,7 @@ use uuid::Uuid;
pub async fn cancel_and_refund(
swap_id: Uuid,
bitcoin_wallet: Arc<Wallet>,
db: Arc<dyn Database>,
db: Arc<dyn Database + Send + Sync>,
) -> Result<BobState> {
if let Err(err) = cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await {
tracing::info!(%err, "Could not submit cancel transaction");
@ -28,7 +28,7 @@ pub async fn cancel_and_refund(
pub async fn cancel(
swap_id: Uuid,
bitcoin_wallet: Arc<Wallet>,
db: Arc<dyn Database>,
db: Arc<dyn Database + Send + Sync>,
) -> Result<(Txid, Subscription, BobState)> {
let state = db.get_state(swap_id).await?.try_into()?;
@ -80,7 +80,7 @@ pub async fn cancel(
pub async fn refund(
swap_id: Uuid,
bitcoin_wallet: Arc<Wallet>,
db: Arc<dyn Database>,
db: Arc<dyn Database + Send + Sync>,
) -> Result<BobState> {
let state = db.get_state(swap_id).await?.try_into()?;

File diff suppressed because it is too large Load Diff

@ -151,28 +151,28 @@ impl EventLoop {
return;
}
SwarmEvent::Behaviour(OutEvent::Failure { peer, error }) => {
tracing::warn!(%peer, "Communication error: {:#}", error);
tracing::warn!(%peer, err = %error, "Communication error");
return;
}
SwarmEvent::ConnectionEstablished { peer_id, endpoint, .. } if peer_id == self.alice_peer_id => {
tracing::info!("Connected to Alice at {}", endpoint.get_remote_address());
tracing::info!(peer_id = %endpoint.get_remote_address(), "Connected to Alice");
}
SwarmEvent::Dialing(peer_id) if peer_id == self.alice_peer_id => {
tracing::debug!("Dialling Alice at {}", peer_id);
tracing::debug!(%peer_id, "Dialling Alice");
}
SwarmEvent::ConnectionClosed { peer_id, endpoint, num_established, cause: Some(error) } if peer_id == self.alice_peer_id && num_established == 0 => {
tracing::warn!("Lost connection to Alice at {}, cause: {}", endpoint.get_remote_address(), error);
tracing::warn!(peer_id = %endpoint.get_remote_address(), cause = %error, "Lost connection to Alice");
}
SwarmEvent::ConnectionClosed { peer_id, num_established, cause: None, .. } if peer_id == self.alice_peer_id && num_established == 0 => {
// no error means the disconnection was requested
tracing::info!("Successfully closed connection to Alice");
return;
}
SwarmEvent::OutgoingConnectionError { peer_id, error } if matches!(peer_id, Some(alice_peer_id) if alice_peer_id == self.alice_peer_id) => {
tracing::warn!( "Failed to dial Alice: {}", error);
SwarmEvent::OutgoingConnectionError { peer_id: Some(alice_peer_id), error } if alice_peer_id == self.alice_peer_id => {
tracing::warn!(%error, "Failed to dial Alice");
if let Some(duration) = self.swarm.behaviour_mut().redial.until_next_redial() {
tracing::info!("Next redial attempt in {}s", duration.as_secs());
tracing::info!(seconds_until_next_redial = %duration.as_secs(), "Waiting for next redial attempt");
}
}
@ -241,6 +241,7 @@ impl EventLoopHandle {
}
pub async fn request_quote(&mut self) -> Result<BidQuote> {
tracing::debug!("Requesting quote");
Ok(self.quote.send_receive(()).await?)
}

@ -1,5 +1,4 @@
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;
@ -7,55 +6,31 @@ use tracing::{Event, Level, Subscriber};
use tracing_subscriber::fmt::format::{DefaultFields, Format, JsonFields};
use tracing_subscriber::fmt::time::UtcTime;
use tracing_subscriber::layer::{Context, SubscriberExt};
use tracing_subscriber::{fmt, EnvFilter, FmtSubscriber, Layer, Registry};
use uuid::Uuid;
use tracing_subscriber::{fmt, EnvFilter, Layer, Registry};
pub fn init(debug: bool, json: bool, dir: impl AsRef<Path>, swap_id: Option<Uuid>) -> Result<()> {
if let Some(swap_id) = swap_id {
let level_filter = EnvFilter::try_new("swap=debug")?;
pub fn init(debug: bool, json: bool, dir: impl AsRef<Path>) -> Result<()> {
let level_filter = EnvFilter::try_new("swap=debug")?;
let registry = Registry::default().with(level_filter);
let registry = Registry::default().with(level_filter);
let appender = tracing_appender::rolling::never(dir.as_ref(), "swap-all.log");
let appender =
tracing_appender::rolling::never(dir.as_ref(), format!("swap-{}.log", swap_id));
let (appender, guard) = tracing_appender::non_blocking(appender);
std::mem::forget(guard);
let file_logger = registry.with(
fmt::layer()
.with_ansi(false)
.with_target(false)
.json()
.with_writer(appender),
);
if json && debug {
set_global_default(file_logger.with(debug_json_terminal_printer()))?;
} else if json && !debug {
set_global_default(file_logger.with(info_json_terminal_printer()))?;
} else if !json && debug {
set_global_default(file_logger.with(debug_terminal_printer()))?;
} else {
set_global_default(file_logger.with(info_terminal_printer()))?;
}
let file_logger = registry.with(
fmt::layer()
.with_ansi(false)
.with_target(false)
.json()
.with_writer(appender),
);
if json && debug {
set_global_default(file_logger.with(debug_json_terminal_printer()))?;
} else if json && !debug {
set_global_default(file_logger.with(info_json_terminal_printer()))?;
} else if !json && debug {
set_global_default(file_logger.with(debug_terminal_printer()))?;
} else {
let level = if debug { Level::DEBUG } else { Level::INFO };
let is_terminal = atty::is(atty::Stream::Stderr);
let builder = FmtSubscriber::builder()
.with_env_filter(format!("swap={}", level))
.with_writer(std::io::stderr)
.with_ansi(is_terminal)
.with_timer(UtcTime::rfc_3339())
.with_target(false);
if json {
builder.json().init();
} else {
builder.init();
}
};
set_global_default(file_logger.with(info_terminal_printer()))?;
}
tracing::info!("Logging initialized to {}", dir.as_ref().display());
Ok(())
@ -66,19 +41,11 @@ pub struct StdErrPrinter<L> {
level: Level,
}
type StdErrLayer<S, T> = tracing_subscriber::fmt::Layer<
S,
DefaultFields,
Format<tracing_subscriber::fmt::format::Full, T>,
fn() -> std::io::Stderr,
>;
type StdErrLayer<S, T> =
fmt::Layer<S, DefaultFields, Format<fmt::format::Full, T>, fn() -> std::io::Stderr>;
type StdErrJsonLayer<S, T> = tracing_subscriber::fmt::Layer<
S,
JsonFields,
Format<tracing_subscriber::fmt::format::Json, T>,
fn() -> std::io::Stderr,
>;
type StdErrJsonLayer<S, T> =
fmt::Layer<S, JsonFields, Format<fmt::format::Json, T>, fn() -> std::io::Stderr>;
fn debug_terminal_printer<S>() -> StdErrPrinter<StdErrLayer<S, UtcTime<Rfc3339>>> {
let is_terminal = atty::is(atty::Stream::Stderr);

@ -1,11 +1,12 @@
use crate::database::Swap;
use crate::monero::Address;
use crate::protocol::{Database, State};
use anyhow::{Context, Result};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use libp2p::{Multiaddr, PeerId};
use sqlx::sqlite::Sqlite;
use sqlx::{Pool, SqlitePool};
use std::collections::HashMap;
use std::path::Path;
use std::str::FromStr;
use time::OffsetDateTime;
@ -149,7 +150,7 @@ impl Database for SqliteDatabase {
let rows = sqlx::query!(
r#"
SELECT address
SELECT DISTINCT address
FROM peer_addresses
WHERE peer_id = ?
"#,
@ -169,6 +170,25 @@ impl Database for SqliteDatabase {
addresses
}
async fn get_swap_start_date(&self, swap_id: Uuid) -> Result<String> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
let row = sqlx::query!(
r#"
SELECT min(entered_at) as start_date
FROM swap_states
WHERE swap_id = ?
"#,
swap_id
)
.fetch_one(&mut conn)
.await?;
row.start_date
.ok_or_else(|| anyhow!("Could not get swap start date"))
}
async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()> {
let mut conn = self.pool.acquire().await?;
let entered_at = OffsetDateTime::now_utc();
@ -249,6 +269,69 @@ impl Database for SqliteDatabase {
result
}
async fn get_states(&self, swap_id: Uuid) -> Result<Vec<State>> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
// TODO: We should use query! instead of query here to allow for at-compile-time validation
// I didn't manage to generate the mappings for the query! macro because of problems with sqlx-cli
let rows = sqlx::query!(
r#"
SELECT state
FROM swap_states
WHERE swap_id = ?
"#,
swap_id
)
.fetch_all(&mut conn)
.await?;
let result = rows
.iter()
.map(|row| {
let state_str: &str = &row.state;
let state = match serde_json::from_str::<Swap>(state_str) {
Ok(a) => Ok(State::from(a)),
Err(e) => Err(e),
}?;
Ok(state)
})
.collect::<Result<Vec<State>>>();
result
}
async fn raw_all(&self) -> Result<HashMap<Uuid, Vec<serde_json::Value>>> {
let mut conn = self.pool.acquire().await?;
let rows = sqlx::query!(
r#"
SELECT swap_id, state
FROM swap_states
"#
)
.fetch_all(&mut conn)
.await?;
let mut swaps: HashMap<Uuid, Vec<serde_json::Value>> = HashMap::new();
for row in &rows {
let swap_id = Uuid::from_str(&row.swap_id)?;
let state = serde_json::from_str(&row.state)?;
if let std::collections::hash_map::Entry::Vacant(e) = swaps.entry(swap_id) {
e.insert(vec![state]);
} else {
swaps
.get_mut(&swap_id)
.ok_or_else(|| anyhow!("Error while retrieving the swap"))?
.push(state);
}
}
Ok(swaps)
}
}
#[cfg(test)]

@ -46,7 +46,7 @@ pub struct Regtest;
impl GetConfig for Mainnet {
fn get_config() -> Config {
Config {
bitcoin_lock_mempool_timeout: 3.std_minutes(),
bitcoin_lock_mempool_timeout: 10.std_minutes(),
bitcoin_lock_confirmed_timeout: 2.std_hours(),
bitcoin_finality_confirmations: 1,
bitcoin_avg_block_time: 10.std_minutes(),
@ -63,7 +63,7 @@ impl GetConfig for Mainnet {
impl GetConfig for Testnet {
fn get_config() -> Config {
Config {
bitcoin_lock_mempool_timeout: 3.std_minutes(),
bitcoin_lock_mempool_timeout: 10.std_minutes(),
bitcoin_lock_confirmed_timeout: 1.std_hours(),
bitcoin_finality_confirmations: 1,
bitcoin_avg_block_time: 10.std_minutes(),

@ -16,6 +16,7 @@
missing_copy_implementations
)]
pub mod api;
pub mod asb;
pub mod bitcoin;
pub mod cli;
@ -28,6 +29,7 @@ pub mod libp2p_ext;
pub mod monero;
pub mod network;
pub mod protocol;
pub mod rpc;
pub mod seed;
pub mod tor;
pub mod tracing_ext;

@ -42,6 +42,13 @@ pub fn private_key_from_secp256k1_scalar(scalar: bitcoin::Scalar) -> PrivateKey
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct PrivateViewKey(#[serde(with = "monero_private_key")] PrivateKey);
impl fmt::Display for PrivateViewKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Delegate to the Display implementation of PrivateKey
write!(f, "{}", self.0)
}
}
impl PrivateViewKey {
pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let scalar = Scalar::random(rng);
@ -320,6 +327,52 @@ pub mod monero_amount {
}
}
pub mod monero_address {
use anyhow::{bail, Context, Result};
use std::str::FromStr;
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq)]
#[error("Invalid monero address provided, expected address on network {expected:?} but address provided is on {actual:?}")]
pub struct MoneroAddressNetworkMismatch {
pub expected: monero::Network,
pub actual: monero::Network,
}
pub fn parse(s: &str) -> Result<monero::Address> {
monero::Address::from_str(s).with_context(|| {
format!(
"Failed to parse {} as a monero address, please make sure it is a valid address",
s
)
})
}
pub fn validate(
address: monero::Address,
expected_network: monero::Network,
) -> Result<monero::Address> {
if address.network != expected_network {
bail!(MoneroAddressNetworkMismatch {
expected: expected_network,
actual: address.network,
});
}
Ok(address)
}
pub fn validate_is_testnet(
address: monero::Address,
is_testnet: bool,
) -> Result<monero::Address> {
let expected_network = if is_testnet {
monero::Network::Stagenet
} else {
monero::Network::Mainnet
};
validate(address, expected_network)
}
}
#[cfg(test)]
mod tests {
use super::*;

@ -1,11 +1,13 @@
use ::monero::Network;
use anyhow::{bail, Context, Error, Result};
use big_bytes::BigByte;
use data_encoding::HEXLOWER;
use futures::{StreamExt, TryStreamExt};
use monero_rpc::wallet::{Client, MoneroWalletRpc as _};
use reqwest::header::CONTENT_LENGTH;
use reqwest::Url;
use serde::Deserialize;
use sha2::{Digest, Sha256};
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::io::ErrorKind;
@ -44,20 +46,30 @@ const MONERO_DAEMONS: [MoneroDaemon; 17] = [
compile_error!("unsupported operating system");
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.1.2.tar.bz2";
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.3.1.tar.bz2";
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
const DOWNLOAD_HASH: &str = "7f8bd9364ef16482b418aa802a65be0e4cc660c794bb5d77b2d17bc84427883a";
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.0.0.tar.bz2";
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.3.1.tar.bz2";
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
const DOWNLOAD_HASH: &str = "915288b023cb5811e626e10052adc6ac5323dd283c5a25b91059b0fb86a21fb6";
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.1.2.tar.bz2";
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.1.tar.bz2";
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
const DOWNLOAD_HASH: &str = "23af572fdfe3459b9ab97e2e9aa7e3c11021c955d6064b801a27d7e8c21ae09d";
#[cfg(all(target_os = "linux", target_arch = "arm"))]
const DOWNLOAD_URL: &str =
"https://downloads.getmonero.org/cli/monero-linux-armv7-v0.18.1.2.tar.bz2";
"https://downloads.getmonero.org/cli/monero-linux-armv7-v0.18.3.1.tar.bz2";
#[cfg(all(target_os = "linux", target_arch = "arm"))]
const DOWNLOAD_HASH: &str = "2ea2c8898cbab88f49423f4f6c15f2a94046cb4bbe827493dd061edc0fd5f1ca";
#[cfg(target_os = "windows")]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-win-x64-v0.18.1.2.zip";
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-win-x64-v0.18.3.1.zip";
#[cfg(target_os = "windows")]
const DOWNLOAD_HASH: &str = "35dcc4bee4caad3442659d37837e0119e4649a77f2e3b5e80dd6d9b8fc4fb6ad";
#[cfg(any(target_os = "macos", target_os = "linux"))]
const PACKED_FILE: &str = "monero-wallet-rpc";
@ -65,7 +77,7 @@ const PACKED_FILE: &str = "monero-wallet-rpc";
#[cfg(target_os = "windows")]
const PACKED_FILE: &str = "monero-wallet-rpc.exe";
const WALLET_RPC_VERSION: &str = "v0.18.1.2";
const WALLET_RPC_VERSION: &str = "v0.18.3.1";
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("monero wallet rpc executable not found in downloaded archive")]
@ -221,13 +233,20 @@ impl WalletRpc {
.parse::<u64>()?;
tracing::info!(
"Downloading monero-wallet-rpc ({}) from {}",
content_length.big_byte(2),
DOWNLOAD_URL
progress="0%",
size=%content_length.big_byte(2),
download_url=DOWNLOAD_URL,
"Downloading monero-wallet-rpc",
);
let mut hasher = Sha256::new();
let byte_stream = response
.bytes_stream()
.map_ok(|bytes| {
hasher.update(&bytes);
bytes
})
.map_err(|err| std::io::Error::new(ErrorKind::Other, err));
#[cfg(not(target_os = "windows"))]
@ -250,12 +269,36 @@ impl WalletRpc {
let total = 3 * content_length;
let percent = 100 * received as u64 / total;
if percent != notified && percent % 10 == 0 {
tracing::debug!("{}%", percent);
tracing::info!(
progress=format!("{}%", percent),
size=%content_length.big_byte(2),
download_url=DOWNLOAD_URL,
"Downloading monero-wallet-rpc",
);
notified = percent;
}
file.write_all(&bytes).await?;
}
tracing::info!(
progress="100%",
size=%content_length.big_byte(2),
download_url=DOWNLOAD_URL,
"Downloading monero-wallet-rpc",
);
let result = hasher.finalize();
let result_hash = HEXLOWER.encode(result.as_ref());
if result_hash != DOWNLOAD_HASH {
bail!(
"SHA256 of download ({}) does not match expected ({})!",
result_hash,
DOWNLOAD_HASH
);
} else {
tracing::debug!("Hashes match");
}
file.flush().await?;
tracing::debug!("Extracting archive");

@ -19,7 +19,7 @@ pub struct CborCodec<P, Req, Res> {
impl<P, Req, Res> Default for CborCodec<P, Req, Res> {
fn default() -> Self {
Self {
phantom: PhantomData::default(),
phantom: PhantomData,
}
}
}

@ -25,7 +25,7 @@ pub struct JsonPullCodec<P, Res> {
impl<P, Res> Default for JsonPullCodec<P, Res> {
fn default() -> Self {
Self {
phantom: PhantomData::default(),
phantom: PhantomData,
}
}
}

@ -40,7 +40,7 @@ where
.expect("failed to create dh_keys");
let noise = NoiseConfig::xx(dh_keys).into_authenticated();
let transport = MemoryTransport::default()
let transport = MemoryTransport
.or_transport(TokioTcpConfig::new())
.upgrade(Version::V1)
.authenticate(noise)

@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
use sha2::Sha256;
use sigma_fun::ext::dl_secp256k1_ed25519_eq::{CrossCurveDLEQ, CrossCurveDLEQProof};
use sigma_fun::HashTranscript;
use std::collections::HashMap;
use std::convert::TryInto;
use uuid::Uuid;
@ -139,7 +140,10 @@ pub trait Database {
async fn get_monero_address(&self, swap_id: Uuid) -> Result<monero::Address>;
async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()>;
async fn get_addresses(&self, peer_id: PeerId) -> Result<Vec<Multiaddr>>;
async fn get_swap_start_date(&self, swap_id: Uuid) -> Result<String>;
async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()>;
async fn get_state(&self, swap_id: Uuid) -> Result<State>;
async fn get_states(&self, swap_id: Uuid) -> Result<Vec<State>>;
async fn all(&self) -> Result<Vec<(Uuid, State)>>;
async fn raw_all(&self) -> Result<HashMap<Uuid, Vec<serde_json::Value>>>;
}

@ -310,7 +310,8 @@ impl State2 {
self.a.public(),
self.B,
self.tx_cancel_fee,
);
)
.expect("valid cancel tx");
let tx_refund =
bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee);
@ -335,7 +336,7 @@ impl State2 {
self.a.public(),
self.B,
self.tx_cancel_fee,
);
)?;
bitcoin::verify_sig(&self.B, &tx_cancel.digest(), &msg.tx_cancel_sig)
.context("Failed to verify cancel transaction")?;
let tx_punish = bitcoin::TxPunish::new(
@ -458,6 +459,7 @@ impl State3 {
self.B,
self.tx_cancel_fee,
)
.expect("valid cancel tx")
}
pub fn tx_refund(&self) -> TxRefund {

@ -112,7 +112,7 @@ where
}
AliceState::BtcLocked { state3 } => {
match state3.expired_timelocks(bitcoin_wallet).await? {
ExpiredTimelocks::None => {
ExpiredTimelocks::None { .. } => {
// Record the current monero wallet block height so we don't have to scan from
// block 0 for scenarios where we create a refund wallet.
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
@ -135,7 +135,7 @@ where
transfer_proof,
state3,
} => match state3.expired_timelocks(bitcoin_wallet).await? {
ExpiredTimelocks::None => {
ExpiredTimelocks::None { .. } => {
monero_wallet
.watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof.clone(), 1))
.await
@ -221,7 +221,7 @@ where
encrypted_signature,
state3,
} => match state3.expired_timelocks(bitcoin_wallet).await? {
ExpiredTimelocks::None => {
ExpiredTimelocks::None { .. } => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
match state3.signed_redeem_transaction(*encrypted_signature) {
Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await {

@ -21,9 +21,10 @@ use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof;
use std::fmt;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum BobState {
Started {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
btc_amount: bitcoin::Amount,
change_address: bitcoin::Address,
},
@ -242,7 +243,7 @@ impl State1 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_refund =
bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee);
@ -287,13 +288,13 @@ pub struct State2 {
S_a_monero: monero::PublicKey,
S_a_bitcoin: bitcoin::PublicKey,
v: monero::PrivateViewKey,
xmr: monero::Amount,
cancel_timelock: CancelTimelock,
punish_timelock: PunishTimelock,
refund_address: bitcoin::Address,
pub xmr: monero::Amount,
pub cancel_timelock: CancelTimelock,
pub punish_timelock: PunishTimelock,
pub refund_address: bitcoin::Address,
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
tx_lock: bitcoin::TxLock,
pub tx_lock: bitcoin::TxLock,
tx_cancel_sig_a: Signature,
tx_refund_encsig: bitcoin::EncryptedSignature,
min_monero_confirmations: u64,
@ -302,9 +303,9 @@ pub struct State2 {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_punish_fee: bitcoin::Amount,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_refund_fee: bitcoin::Amount,
pub tx_refund_fee: bitcoin::Amount,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_cancel_fee: bitcoin::Amount,
pub tx_cancel_fee: bitcoin::Amount,
}
impl State2 {
@ -315,7 +316,8 @@ impl State2 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)
.expect("valid cancel tx");
let tx_cancel_sig = self.b.sign(tx_cancel.digest());
let tx_punish = bitcoin::TxPunish::new(
&tx_cancel,
@ -439,7 +441,7 @@ impl State3 {
self.tx_lock.txid()
}
pub async fn current_epoch(
pub async fn expired_timelock(
&self,
bitcoin_wallet: &bitcoin::Wallet,
) -> Result<ExpiredTimelocks> {
@ -449,7 +451,7 @@ impl State3 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?;
let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?;
@ -530,7 +532,7 @@ impl State4 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?;
let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?;
@ -611,7 +613,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?;
let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?;
@ -634,7 +636,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx = bitcoin_wallet.get_raw_transaction(tx_cancel.txid()).await?;
@ -651,7 +653,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
)
)?
.complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone())
.context("Failed to complete Bitcoin cancel transaction")?;
@ -674,7 +676,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_refund =
bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee);

@ -117,7 +117,7 @@ async fn next_state(
} => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
if let ExpiredTimelocks::None = state3.current_epoch(bitcoin_wallet).await? {
if let ExpiredTimelocks::None { .. } = state3.expired_timelock(bitcoin_wallet).await? {
let transfer_proof_watcher = event_loop_handle.recv_transfer_proof();
let cancel_timelock_expires =
tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock);
@ -156,7 +156,7 @@ async fn next_state(
} => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
if let ExpiredTimelocks::None = state.current_epoch(bitcoin_wallet).await? {
if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {
let watch_request = state.lock_xmr_watch_request(lock_transfer_proof);
select! {
@ -185,7 +185,7 @@ async fn next_state(
BobState::XmrLocked(state) => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? {
if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {
// Alice has locked Xmr
// Bob sends Alice his key
@ -209,7 +209,7 @@ async fn next_state(
BobState::EncSigSent(state) => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? {
if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {
select! {
state5 = state.watch_for_redeem_btc(bitcoin_wallet) => {
BobState::BtcRedeemed(state5?)
@ -227,6 +227,9 @@ async fn next_state(
let (spend_key, view_key) = state.xmr_keys();
let wallet_file_name = swap_id.to_string();
tracing::info!(%wallet_file_name, "Generating and opening Monero wallet from the extracted keys to redeem the Monero");
if let Err(e) = monero_wallet
.create_from_and_load(
wallet_file_name.clone(),
@ -269,12 +272,12 @@ async fn next_state(
BobState::BtcCancelled(state) => {
// Bob has cancelled the swap
match state.expired_timelock(bitcoin_wallet).await? {
ExpiredTimelocks::None => {
ExpiredTimelocks::None { .. } => {
bail!(
"Internal error: canceled state reached before cancel timelock was expired"
);
}
ExpiredTimelocks::Cancel => {
ExpiredTimelocks::Cancel { .. } => {
state.publish_refund_btc(bitcoin_wallet).await?;
BobState::BtcRefunded(state)
}

@ -0,0 +1,31 @@
use crate::api::Context;
use jsonrpsee::server::{RpcModule, ServerBuilder, ServerHandle};
use std::net::SocketAddr;
use std::sync::Arc;
use thiserror::Error;
pub mod methods;
#[derive(Debug, Error)]
pub enum Error {
#[error("Could not parse key value from params")]
ParseError,
}
pub async fn run_server(
server_address: SocketAddr,
context: Arc<Context>,
) -> anyhow::Result<(SocketAddr, ServerHandle)> {
let server = ServerBuilder::default().build(server_address).await?;
let mut modules = RpcModule::new(());
{
modules
.merge(methods::register_modules(Arc::clone(&context))?)
.expect("Could not register RPC modules")
}
let addr = server.local_addr()?;
let server_handle = server.start(modules)?;
Ok((addr, server_handle))
}

@ -0,0 +1,236 @@
use crate::api::request::{Method, Request};
use crate::api::Context;
use crate::bitcoin::bitcoin_address;
use crate::monero::monero_address;
use crate::{bitcoin, monero};
use anyhow::Result;
use jsonrpsee::server::RpcModule;
use jsonrpsee::types::Params;
use libp2p::core::Multiaddr;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use uuid::Uuid;
pub fn register_modules(context: Arc<Context>) -> Result<RpcModule<Arc<Context>>> {
let mut module = RpcModule::new(context);
module.register_async_method("suspend_current_swap", |params, context| async move {
execute_request(params, Method::SuspendCurrentSwap, &context).await
})?;
module.register_async_method("get_swap_info", |params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let swap_id = params
.get("swap_id")
.ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()))?;
let swap_id = as_uuid(swap_id)
.ok_or_else(|| jsonrpsee_core::Error::Custom("Could not parse swap_id".to_string()))?;
execute_request(params_raw, Method::GetSwapInfo { swap_id }, &context).await
})?;
module.register_async_method("get_bitcoin_balance", |params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let force_refresh = params
.get("force_refresh")
.ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain force_refresh".to_string())
})?
.as_bool()
.ok_or_else(|| {
jsonrpsee_core::Error::Custom("force_refesh is not a boolean".to_string())
})?;
execute_request(params_raw, Method::Balance { force_refresh }, &context).await
})?;
module.register_async_method("get_history", |params, context| async move {
execute_request(params, Method::History, &context).await
})?;
module.register_async_method("get_raw_states", |params, context| async move {
execute_request(params, Method::GetRawStates, &context).await
})?;
module.register_async_method("resume_swap", |params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let swap_id = params
.get("swap_id")
.ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()))?;
let swap_id = as_uuid(swap_id)
.ok_or_else(|| jsonrpsee_core::Error::Custom("Could not parse swap_id".to_string()))?;
execute_request(params_raw, Method::Resume { swap_id }, &context).await
})?;
module.register_async_method("cancel_refund_swap", |params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let swap_id = params
.get("swap_id")
.ok_or_else(|| jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string()))?;
let swap_id = as_uuid(swap_id)
.ok_or_else(|| jsonrpsee_core::Error::Custom("Could not parse swap_id".to_string()))?;
execute_request(params_raw, Method::CancelAndRefund { swap_id }, &context).await
})?;
module.register_async_method(
"get_monero_recovery_info",
|params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let swap_id = params.get("swap_id").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain swap_id".to_string())
})?;
let swap_id = as_uuid(swap_id).ok_or_else(|| {
jsonrpsee_core::Error::Custom("Could not parse swap_id".to_string())
})?;
execute_request(params_raw, Method::MoneroRecovery { swap_id }, &context).await
},
)?;
module.register_async_method("withdraw_btc", |params_raw, context| async move {
let params: HashMap<String, String> = params_raw.parse()?;
let amount = if let Some(amount_str) = params.get("amount") {
Some(
::bitcoin::Amount::from_str_in(amount_str, ::bitcoin::Denomination::Bitcoin)
.map_err(|_| {
jsonrpsee_core::Error::Custom("Unable to parse amount".to_string())
})?,
)
} else {
None
};
let withdraw_address =
bitcoin::Address::from_str(params.get("address").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain address".to_string())
})?)
.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string()))?;
let withdraw_address =
bitcoin_address::validate(withdraw_address, context.config.env_config.bitcoin_network)?;
execute_request(
params_raw,
Method::WithdrawBtc {
amount,
address: withdraw_address,
},
&context,
)
.await
})?;
module.register_async_method("buy_xmr", |params_raw, context| async move {
let params: HashMap<String, String> = params_raw.parse()?;
let bitcoin_change_address =
bitcoin::Address::from_str(params.get("bitcoin_change_address").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain bitcoin_change_address".to_string())
})?)
.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string()))?;
let bitcoin_change_address = bitcoin_address::validate(
bitcoin_change_address,
context.config.env_config.bitcoin_network,
)?;
let monero_receive_address =
monero::Address::from_str(params.get("monero_receive_address").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain monero_receiveaddress".to_string())
})?)
.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string()))?;
let monero_receive_address = monero_address::validate(
monero_receive_address,
context.config.env_config.monero_network,
)?;
let seller =
Multiaddr::from_str(params.get("seller").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain seller".to_string())
})?)
.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string()))?;
execute_request(
params_raw,
Method::BuyXmr {
bitcoin_change_address,
monero_receive_address,
seller,
swap_id: Uuid::new_v4(),
},
&context,
)
.await
})?;
module.register_async_method("list_sellers", |params_raw, context| async move {
let params: HashMap<String, serde_json::Value> = params_raw.parse()?;
let rendezvous_point = params.get("rendezvous_point").ok_or_else(|| {
jsonrpsee_core::Error::Custom("Does not contain rendezvous_point".to_string())
})?;
let rendezvous_point = rendezvous_point
.as_str()
.and_then(|addr_str| Multiaddr::from_str(addr_str).ok())
.ok_or_else(|| {
jsonrpsee_core::Error::Custom("Could not parse valid multiaddr".to_string())
})?;
execute_request(
params_raw,
Method::ListSellers {
rendezvous_point: rendezvous_point.clone(),
},
&context,
)
.await
})?;
module.register_async_method("get_current_swap", |params, context| async move {
execute_request(params, Method::GetCurrentSwap, &context).await
})?;
Ok(module)
}
fn as_uuid(json_value: &serde_json::Value) -> Option<Uuid> {
if let Some(uuid_str) = json_value.as_str() {
Uuid::parse_str(uuid_str).ok()
} else {
None
}
}
async fn execute_request(
params: Params<'static>,
cmd: Method,
context: &Arc<Context>,
) -> Result<serde_json::Value, jsonrpsee_core::Error> {
// If we fail to parse the params as a String HashMap, it's most likely because its an empty object
// In that case, we want to make sure not to fail the request, so we set the log_reference_id to None
// and swallow the error
let reference_id = params
.parse::<HashMap<String, serde_json::Value>>()
.ok()
.and_then(|params_parsed| params_parsed.get("log_reference_id").cloned());
let request = Request::with_id(cmd, reference_id.map(|log_ref| log_ref.to_string()));
request
.call(Arc::clone(context))
.await
.map_err(|err| jsonrpsee_core::Error::Custom(format!("{:#}", err)))
}

@ -16,7 +16,7 @@ use torut::onion::TorSecretKeyV3;
pub const SEED_LENGTH: usize = 32;
#[derive(Eq, PartialEq)]
#[derive(Clone, Eq, PartialEq)]
pub struct Seed([u8; SEED_LENGTH]);
impl Seed {
@ -106,11 +106,12 @@ impl Seed {
}
fn from_pem(pem: pem::Pem) -> Result<Self, Error> {
if pem.contents.len() != SEED_LENGTH {
Err(Error::IncorrectLength(pem.contents.len()))
let contents = pem.contents();
if contents.len() != SEED_LENGTH {
Err(Error::IncorrectLength(contents.len()))
} else {
let mut array = [0; SEED_LENGTH];
for (i, b) in pem.contents.iter().enumerate() {
for (i, b) in contents.iter().enumerate() {
array[i] = *b;
}
@ -122,10 +123,7 @@ impl Seed {
ensure_directory_exists(&seed_file)?;
let data = self.bytes();
let pem = Pem {
tag: String::from("SEED"),
contents: data.to_vec(),
};
let pem = Pem::new("SEED", data);
let pem_string = encode(&pem);
@ -224,19 +222,20 @@ VnZUNFZ4dlY=
}
#[test]
#[should_panic]
fn seed_from_pem_fails_for_long_seed() {
let long = "-----BEGIN SEED-----
mbKANv2qKGmNVg1qtquj6Hx1pFPelpqOfE2JaJJAMEg1FlFhNRNlFlE=
mbKANv2qKGmNVg1qtquj6Hx1pFPelpqOfE2JaJJAMEg1FlFhNRNlFlE=
MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc
dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO
-----END SEED-----
";
let pem = pem::parse(long).unwrap();
assert_eq!(pem.contents().len(), 96);
match Seed::from_pem(pem) {
Ok(_) => panic!("should fail for long payload"),
Err(e) => {
match e {
Error::IncorrectLength(_) => {} // pass
Error::IncorrectLength(len) => assert_eq!(len, 96), // pass
_ => panic!("should fail with IncorrectLength error"),
}
}

@ -1,6 +1,5 @@
use std::collections::HashMap;
use testcontainers::core::{Container, Docker, WaitForMessage};
use testcontainers::Image;
use std::collections::BTreeMap;
use testcontainers::{core::WaitFor, Image, ImageArgs};
pub const RPC_USER: &str = "admin";
pub const RPC_PASSWORD: &str = "123";
@ -10,57 +9,27 @@ pub const DATADIR: &str = "/home/bdk";
#[derive(Debug)]
pub struct Bitcoind {
args: BitcoindArgs,
entrypoint: Option<String>,
volume: Option<String>,
volumes: BTreeMap<String, String>,
}
impl Image for Bitcoind {
type Args = BitcoindArgs;
type EnvVars = HashMap<String, String>;
type Volumes = HashMap<String, String>;
type EntryPoint = str;
fn descriptor(&self) -> String {
"coblox/bitcoin-core:0.21.0".to_string()
fn name(&self) -> String {
"coblox/bitcoin-core".into()
}
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
container
.logs()
.stdout
.wait_for_message("init message: Done loading")
.unwrap();
fn tag(&self) -> String {
"0.21.0".into()
}
fn args(&self) -> <Self as Image>::Args {
self.args.clone()
fn ready_conditions(&self) -> Vec<WaitFor> {
vec![WaitFor::message_on_stdout("init message: Done loading")]
}
fn volumes(&self) -> Self::Volumes {
let mut volumes = HashMap::new();
match self.volume.clone() {
None => {}
Some(volume) => {
volumes.insert(volume, DATADIR.to_string());
}
}
volumes
}
fn env_vars(&self) -> Self::EnvVars {
HashMap::new()
}
fn with_args(self, args: <Self as Image>::Args) -> Self {
Bitcoind { args, ..self }
}
fn with_entrypoint(self, entrypoint: &Self::EntryPoint) -> Self {
Self {
entrypoint: Some(entrypoint.to_string()),
..self
}
fn volumes(&self) -> Box<dyn Iterator<Item = (&String, &String)> + '_> {
Box::new(self.volumes.iter())
}
fn entrypoint(&self) -> Option<String> {
@ -71,16 +40,15 @@ impl Image for Bitcoind {
impl Default for Bitcoind {
fn default() -> Self {
Bitcoind {
args: BitcoindArgs::default(),
entrypoint: Some("/usr/bin/bitcoind".into()),
volume: None,
volumes: BTreeMap::default(),
}
}
}
impl Bitcoind {
pub fn with_volume(mut self, volume: String) -> Self {
self.volume = Some(volume);
self.volumes.insert(volume, DATADIR.to_string());
self
}
}
@ -109,7 +77,6 @@ impl IntoIterator for BitcoindArgs {
format!("-rpcuser={}", RPC_USER),
format!("-rpcpassword={}", RPC_PASSWORD),
"-printtoconsole".to_string(),
"-rest".to_string(),
"-fallbackfee=0.0002".to_string(),
format!("-datadir={}", DATADIR),
format!("-rpcport={}", RPC_PORT),
@ -120,3 +87,9 @@ impl IntoIterator for BitcoindArgs {
args.into_iter()
}
}
impl ImageArgs for BitcoindArgs {
fn into_iterator(self) -> Box<dyn Iterator<Item = String>> {
Box::new(self.into_iter())
}
}

@ -1,8 +1,8 @@
use std::collections::BTreeMap;
use crate::harness::bitcoind;
use bitcoin::Network;
use std::collections::HashMap;
use testcontainers::core::{Container, Docker, WaitForMessage};
use testcontainers::Image;
use testcontainers::{core::WaitFor, Image, ImageArgs};
pub const HTTP_PORT: u16 = 60401;
pub const RPC_PORT: u16 = 3002;
@ -13,50 +13,25 @@ pub struct Electrs {
args: ElectrsArgs,
entrypoint: Option<String>,
wait_for_message: String,
volume: String,
volumes: BTreeMap<String, String>,
}
impl Image for Electrs {
type Args = ElectrsArgs;
type EnvVars = HashMap<String, String>;
type Volumes = HashMap<String, String>;
type EntryPoint = str;
fn descriptor(&self) -> String {
format!("vulpemventures/electrs:{}", self.tag)
}
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
container
.logs()
.stderr
.wait_for_message(&self.wait_for_message)
.unwrap();
fn name(&self) -> String {
"vulpemventures/electrs".into()
}
fn args(&self) -> <Self as Image>::Args {
self.args.clone()
fn tag(&self) -> String {
self.tag.clone()
}
fn volumes(&self) -> Self::Volumes {
let mut volumes = HashMap::new();
volumes.insert(self.volume.clone(), bitcoind::DATADIR.to_string());
volumes
fn ready_conditions(&self) -> Vec<WaitFor> {
vec![WaitFor::message_on_stderr(self.wait_for_message.clone())]
}
fn env_vars(&self) -> Self::EnvVars {
HashMap::new()
}
fn with_args(self, args: <Self as Image>::Args) -> Self {
Electrs { args, ..self }
}
fn with_entrypoint(self, entrypoint: &Self::EntryPoint) -> Self {
Self {
entrypoint: Some(entrypoint.to_string()),
..self
}
fn volumes(&self) -> Box<dyn Iterator<Item = (&String, &String)> + '_> {
Box::new(self.volumes.iter())
}
fn entrypoint(&self) -> Option<String> {
@ -71,7 +46,7 @@ impl Default for Electrs {
args: ElectrsArgs::default(),
entrypoint: Some("/build/electrs".into()),
wait_for_message: "Running accept thread".to_string(),
volume: uuid::Uuid::new_v4().to_string(),
volumes: BTreeMap::default(),
}
}
}
@ -85,7 +60,7 @@ impl Electrs {
}
pub fn with_volume(mut self, volume: String) -> Self {
self.volume = volume;
self.volumes.insert(volume, bitcoind::DATADIR.to_string());
self
}
@ -93,6 +68,11 @@ impl Electrs {
self.args.daemon_rpc_addr = name;
self
}
pub fn self_and_args(self) -> (Self, ElectrsArgs) {
let args = self.args.clone();
(self, args)
}
}
#[derive(Debug, Clone)]
@ -137,7 +117,7 @@ impl IntoIterator for ElectrsArgs {
}
args.push("-vvvvv".to_string());
args.push(format!("--daemon-dir=={}", self.daemon_dir.as_str()));
args.push(format!("--daemon-dir={}", self.daemon_dir.as_str()));
args.push(format!("--daemon-rpc-addr={}", self.daemon_rpc_addr));
args.push(format!("--cookie={}", self.cookie));
args.push(format!("--http-addr={}", self.http_addr));
@ -147,3 +127,9 @@ impl IntoIterator for ElectrsArgs {
args.into_iter()
}
}
impl ImageArgs for ElectrsArgs {
fn into_iterator(self) -> Box<dyn Iterator<Item = String>> {
Box::new(self.into_iter())
}
}

@ -23,12 +23,12 @@ use swap::network::rendezvous::XmrBtcNamespace;
use swap::network::swarm;
use swap::protocol::alice::{AliceState, Swap};
use swap::protocol::bob::BobState;
use swap::protocol::{alice, bob};
use swap::protocol::{alice, bob, Database};
use swap::seed::Seed;
use swap::{asb, bitcoin, cli, env, monero};
use swap::{api, asb, bitcoin, cli, env, monero};
use tempfile::{tempdir, NamedTempFile};
use testcontainers::clients::Cli;
use testcontainers::{Container, Docker, RunArgs};
use testcontainers::{Container, RunnableImage};
use tokio::sync::mpsc;
use tokio::sync::mpsc::Receiver;
use tokio::task::JoinHandle;
@ -61,10 +61,7 @@ where
let alice_starting_balances =
StartingBalances::new(bitcoin::Amount::ZERO, xmr_amount, Some(10));
let electrs_rpc_port = containers
.electrs
.get_host_port(electrs::RPC_PORT)
.expect("Could not map electrs rpc port");
let electrs_rpc_port = containers.electrs.get_host_port_ipv4(electrs::RPC_PORT);
let alice_seed = Seed::random().unwrap();
let (alice_bitcoin_wallet, alice_monero_wallet) = init_test_wallets(
@ -146,14 +143,14 @@ where
async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
let prefix = random_prefix();
let bitcoind_name = format!("{}_{}", prefix, "bitcoind");
let (bitcoind, bitcoind_url) =
let (_bitcoind, bitcoind_url, mapped_port) =
init_bitcoind_container(cli, prefix.clone(), bitcoind_name.clone(), prefix.clone())
.await
.expect("could not init bitcoind");
let electrs = init_electrs_container(cli, prefix.clone(), bitcoind_name, prefix)
let electrs = init_electrs_container(cli, prefix.clone(), bitcoind_name, prefix, mapped_port)
.await
.expect("could not init electrs");
let (monero, monerod_container, monero_wallet_rpc_containers) =
let (monero, _monerod_container, _monero_wallet_rpc_containers) =
Monero::new(cli, vec![MONERO_WALLET_NAME_ALICE, MONERO_WALLET_NAME_BOB])
.await
.unwrap();
@ -162,9 +159,9 @@ async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
monero,
Containers {
bitcoind_url,
bitcoind,
monerod_container,
monero_wallet_rpc_containers,
_bitcoind,
_monerod_container,
_monero_wallet_rpc_containers,
electrs,
},
)
@ -175,29 +172,28 @@ async fn init_bitcoind_container(
volume: String,
name: String,
network: String,
) -> Result<(Container<'_, Cli, bitcoind::Bitcoind>, Url)> {
) -> Result<(Container<'_, bitcoind::Bitcoind>, Url, u16)> {
let image = bitcoind::Bitcoind::default().with_volume(volume);
let image = RunnableImage::from(image)
.with_container_name(name)
.with_network(network);
let run_args = RunArgs::default().with_name(name).with_network(network);
let docker = cli.run_with_args(image, run_args);
let a = docker
.get_host_port(bitcoind::RPC_PORT)
.context("Could not map bitcoind rpc port")?;
let docker = cli.run(image);
let port = docker.get_host_port_ipv4(bitcoind::RPC_PORT);
let bitcoind_url = {
let input = format!(
"http://{}:{}@localhost:{}",
bitcoind::RPC_USER,
bitcoind::RPC_PASSWORD,
a
port
);
Url::parse(&input).unwrap()
};
init_bitcoind(bitcoind_url.clone(), 5).await?;
Ok((docker, bitcoind_url.clone()))
Ok((docker, bitcoind_url.clone(), bitcoind::RPC_PORT))
}
pub async fn init_electrs_container(
@ -205,16 +201,18 @@ pub async fn init_electrs_container(
volume: String,
bitcoind_container_name: String,
network: String,
) -> Result<Container<'_, Cli, electrs::Electrs>> {
let bitcoind_rpc_addr = format!("{}:{}", bitcoind_container_name, bitcoind::RPC_PORT);
port: u16,
) -> Result<Container<'_, electrs::Electrs>> {
let bitcoind_rpc_addr = format!("{}:{}", bitcoind_container_name, port);
let image = electrs::Electrs::default()
.with_volume(volume)
.with_daemon_rpc_addr(bitcoind_rpc_addr)
.with_tag("latest");
let image = RunnableImage::from(image.self_and_args())
.with_network(network.clone())
.with_container_name(format!("{}_electrs", network));
let run_args = RunArgs::default().with_network(network);
let docker = cli.run_with_args(image, run_args);
let docker = cli.run(image);
Ok(docker)
}
@ -402,7 +400,7 @@ impl StartingBalances {
}
}
struct BobParams {
pub struct BobParams {
seed: Seed,
db_path: PathBuf,
bitcoin_wallet: Arc<bitcoin::Wallet>,
@ -413,6 +411,21 @@ struct BobParams {
}
impl BobParams {
pub fn get_concentenated_alice_address(&self) -> String {
format!(
"{}/p2p/{}",
self.alice_address.clone(),
self.alice_peer_id.clone().to_base58()
)
}
pub async fn get_change_receive_addresses(&self) -> (bitcoin::Address, monero::Address) {
(
self.bitcoin_wallet.new_address().await.unwrap(),
self.monero_wallet.get_main_address(),
)
}
pub async fn new_swap_from_db(&self, swap_id: Uuid) -> Result<(bob::Swap, cli::EventLoop)> {
let (event_loop, handle) = self.new_eventloop(swap_id).await?;
@ -454,6 +467,8 @@ impl BobParams {
}
let db = Arc::new(SqliteDatabase::open(&self.db_path).await?);
db.insert_peer_id(swap_id, self.alice_peer_id).await?;
let swap = bob::Swap::new(
db,
swap_id,
@ -527,13 +542,24 @@ pub struct TestContext {
alice_swap_handle: mpsc::Receiver<Swap>,
alice_handle: AliceApplicationHandle,
bob_params: BobParams,
pub bob_params: BobParams,
bob_starting_balances: StartingBalances,
bob_bitcoin_wallet: Arc<bitcoin::Wallet>,
bob_monero_wallet: Arc<monero::Wallet>,
}
impl TestContext {
pub async fn get_bob_context(self) -> api::Context {
api::Context::for_harness(
self.bob_params.seed,
self.env_config,
self.bob_params.db_path,
self.bob_bitcoin_wallet,
self.bob_monero_wallet,
)
.await
}
pub async fn restart_alice(&mut self) {
self.alice_handle.abort();
@ -952,13 +978,12 @@ pub async fn mint(node_url: Url, address: bitcoin::Address, amount: bitcoin::Amo
}
// This is just to keep the containers alive
#[allow(dead_code)]
struct Containers<'a> {
bitcoind_url: Url,
bitcoind: Container<'a, Cli, bitcoind::Bitcoind>,
monerod_container: Container<'a, Cli, image::Monerod>,
monero_wallet_rpc_containers: Vec<Container<'a, Cli, image::MoneroWalletRpc>>,
electrs: Container<'a, Cli, electrs::Electrs>,
_bitcoind: Container<'a, bitcoind::Bitcoind>,
_monerod_container: Container<'a, image::Monerod>,
_monero_wallet_rpc_containers: Vec<Container<'a, image::MoneroWalletRpc>>,
electrs: Container<'a, electrs::Electrs>,
}
pub mod alice_run_until {

@ -0,0 +1,436 @@
pub mod harness;
#[cfg(test)]
mod test {
use anyhow::Result;
use jsonrpsee::ws_client::WsClientBuilder;
use jsonrpsee_core::client::{Client, ClientT};
use jsonrpsee_core::params::ObjectParams;
use serial_test::serial;
use serde_json::Value;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use swap::api::request::{Method, Request};
use swap::api::Context;
use crate::harness::alice_run_until::is_xmr_lock_transaction_sent;
use crate::harness::bob_run_until::is_btc_locked;
use crate::harness::{setup_test, SlowCancelConfig, TestContext};
use swap::asb::FixedRate;
use swap::protocol::{alice, bob};
use swap::tracing_ext::{capture_logs, MakeCapturingWriter};
use tracing_subscriber::filter::LevelFilter;
use uuid::Uuid;
const SERVER_ADDRESS: &str = "127.0.0.1:1234";
const SERVER_START_TIMEOUT_SECS: u64 = 50;
const BITCOIN_ADDR: &str = "bcrt1qahvhjfc7vx5857zf8knxs8yp5lkm26jgyt0k76";
const MONERO_ADDR: &str = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAoNybEUvYBKHcnrSgxnVWgZsTvRBaHBNXPa8tHiCU51a";
const SELLER: &str =
"/ip4/127.0.0.1/tcp/9939/p2p/12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi";
const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b";
pub async fn setup_daemon(
harness_ctx: TestContext,
) -> (Client, MakeCapturingWriter, Arc<Context>) {
let writer = capture_logs(LevelFilter::DEBUG);
let server_address: SocketAddr = SERVER_ADDRESS.parse().unwrap();
let request = Request::new(Method::StartDaemon {
server_address: Some(server_address),
});
let context = Arc::new(harness_ctx.get_bob_context().await);
let context_clone = context.clone();
tokio::spawn(async move {
if let Err(err) = request.call(context_clone).await {
println!("Failed to initialize daemon for testing: {}", err);
}
});
for _ in 0..SERVER_START_TIMEOUT_SECS {
if writer.captured().contains("Started RPC server") {
let url = format!("ws://{}", SERVER_ADDRESS);
let client = WsClientBuilder::default().build(&url).await.unwrap();
return (client, writer, context);
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
panic!(
"Failed to start RPC server after {} seconds",
SERVER_START_TIMEOUT_SECS
);
}
fn assert_has_keys_serde(map: &serde_json::Map<String, Value>, keys: &[&str]) {
for &key in keys {
assert!(map.contains_key(key), "Key {} is missing", key);
}
}
// Helper function for HashMap
fn assert_has_keys_hashmap<T>(map: &HashMap<String, T>, keys: &[&str]) {
for &key in keys {
assert!(map.contains_key(key), "Key {} is missing", key);
}
}
#[tokio::test]
#[serial]
pub async fn get_swap_info() {
setup_test(SlowCancelConfig, |mut harness_ctx| async move {
// Start a swap and wait for xmr lock transaction to be published (XmrLockTransactionSent)
let (bob_swap, _) = harness_ctx.bob_swap().await;
let bob_swap_id = bob_swap.id;
tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
let alice_swap = harness_ctx.alice_next_swap().await;
alice::run_until(
alice_swap,
is_xmr_lock_transaction_sent,
FixedRate::default(),
)
.await?;
let (client, _, _) = setup_daemon(harness_ctx).await;
let response: HashMap<String, Vec<(Uuid, String)>> = client
.request("get_history", ObjectParams::new())
.await
.unwrap();
let swaps: Vec<(Uuid, String)> = vec![(bob_swap_id, "btc is locked".to_string())];
assert_eq!(response, HashMap::from([("swaps".to_string(), swaps)]));
let response: HashMap<String, HashMap<Uuid, Vec<Value>>> = client
.request("get_raw_states", ObjectParams::new())
.await
.unwrap();
let response_raw_states = response.get("raw_states").unwrap();
assert!(response_raw_states.contains_key(&bob_swap_id));
assert_eq!(response_raw_states.get(&bob_swap_id).unwrap().len(), 2);
let mut params = ObjectParams::new();
params.insert("swap_id", bob_swap_id).unwrap();
let response: HashMap<String, Value> =
client.request("get_swap_info", params).await.unwrap();
// Check primary keys in response
assert_has_keys_hashmap(
&response,
&[
"txRefundFee",
"swapId",
"cancelTimelock",
"timelock",
"punishTimelock",
"stateName",
"btcAmount",
"startDate",
"btcRefundAddress",
"txCancelFee",
"xmrAmount",
"completed",
"txLockId",
"seller",
],
);
// Assert specific fields
assert_eq!(response.get("swapId").unwrap(), &bob_swap_id.to_string());
assert_eq!(
response.get("stateName").unwrap(),
&"btc is locked".to_string()
);
assert_eq!(response.get("completed").unwrap(), &Value::Bool(false));
// Check seller object and its keys
let seller = response
.get("seller")
.expect("Field 'seller' is missing from response")
.as_object()
.expect("'seller' is not an object");
assert_has_keys_serde(seller, &["peerId"]);
// Check timelock object, nested 'None' object, and blocks_left
let timelock = response
.get("timelock")
.expect("Field 'timelock' is missing from response")
.as_object()
.expect("'timelock' is not an object");
let none_obj = timelock
.get("None")
.expect("Field 'None' is missing from 'timelock'")
.as_object()
.expect("'None' is not an object in 'timelock'");
let blocks_left = none_obj
.get("blocks_left")
.expect("Field 'blocks_left' is missing from 'None'")
.as_i64()
.expect("'blocks_left' is not an integer");
// Validate blocks_left
assert!(
blocks_left > 0 && blocks_left <= 180,
"Field 'blocks_left' should be > 0 and <= 180 but got {}",
blocks_left
);
Ok(())
})
.await;
}
#[tokio::test]
#[serial]
pub async fn test_rpc_calls() {
setup_test(SlowCancelConfig, |harness_ctx| async move {
let alice_addr = harness_ctx.bob_params.get_concentenated_alice_address();
let (change_address, receive_address) =
harness_ctx.bob_params.get_change_receive_addresses().await;
let (client, writer, _) = setup_daemon(harness_ctx).await;
assert!(client.is_connected());
let mut params = ObjectParams::new();
params.insert("force_refresh", false).unwrap();
let response: HashMap<String, i32> = client
.request("get_bitcoin_balance", params)
.await
.unwrap();
assert_eq!(response, HashMap::from([("balance".to_string(), 10000000)]));
let mut params = ObjectParams::new();
params.insert("log_reference_id", "test_ref_id").unwrap();
params.insert("force_refresh", false).unwrap();
let _: HashMap<String, i32> = client.request("get_bitcoin_balance", params).await.unwrap();
assert!(writer.captured().contains(
r#"method{method_name="Balance" log_reference_id="\"test_ref_id\""}: swap::api::request: Current Bitcoin balance as of last sync balance=0.1 BTC"#
));
for method in ["get_swap_info", "resume_swap", "cancel_refund_swap"].iter() {
let mut params = ObjectParams::new();
params.insert("swap_id", "invalid_swap").unwrap();
let response: Result<HashMap<String, String>, _> =
client.request(method, params).await;
response.expect_err(&format!(
"Expected an error when swap_id is invalid for method {}",
method
));
let params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> =
client.request(method, params).await;
response.expect_err(&format!(
"Expected an error when swap_id is missing for method {}",
method
));
}
let params = ObjectParams::new();
let result: Result<HashMap<String, String>, _> =
client.request("list_sellers", params).await;
result.expect_err("Expected an error when rendezvous_point is missing");
let params = ObjectParams::new();
let result: Result<HashMap<String, String>, _> =
client.request("list_sellers", params).await;
result.expect_err("Expected an error when rendezvous_point is missing");
let params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> =
client.request("withdraw_btc", params).await;
response.expect_err("Expected an error when withdraw_address is missing");
let mut params = ObjectParams::new();
params.insert("address", "invalid_address").unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("withdraw_btc", params).await;
response.expect_err("Expected an error when withdraw_address is malformed");
let mut params = ObjectParams::new();
params.insert("address", BITCOIN_ADDR).unwrap();
params.insert("amount", "0").unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("withdraw_btc", params).await;
response.expect_err("Expected an error when amount is 0");
let mut params = ObjectParams::new();
params
.insert("address", BITCOIN_ADDR)
.unwrap();
params.insert("amount", "0.01").unwrap();
let response: HashMap<String, Value> = client
.request("withdraw_btc", params)
.await
.expect("Expected a valid response");
assert_has_keys_hashmap(&response, &["signed_tx", "amount", "txid"]);
assert_eq!(
response.get("amount").unwrap().as_u64().unwrap(),
1_000_000
);
let params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when no params are given");
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when seller is missing");
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when monero_receive_address is missing");
let mut params = ObjectParams::new();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when bitcoin_change_address is missing");
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", "invalid_address")
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when bitcoin_change_address is malformed");
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", "invalid_address")
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when monero_receive_address is malformed");
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", "invalid_seller").unwrap();
let response: Result<HashMap<String, String>, _> =
client.request("buy_xmr", params).await;
response.expect_err("Expected an error when seller is malformed");
let response: Result<HashMap<String, String>, _> = client
.request("suspend_current_swap", ObjectParams::new())
.await;
response.expect_err("Expected an error when no swap is running");
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", change_address)
.unwrap();
params
.insert("monero_receive_address", receive_address)
.unwrap();
params.insert("seller", alice_addr).unwrap();
let response: HashMap<String, Value> = client
.request("buy_xmr", params)
.await
.expect("Expected a HashMap, got an error");
assert_has_keys_hashmap(&response, &["swapId"]);
Ok(())
})
.await;
}
#[tokio::test]
#[serial]
pub async fn suspend_current_swap_swap_running() {
setup_test(SlowCancelConfig, |harness_ctx| async move {
let (client, _, ctx) = setup_daemon(harness_ctx).await;
ctx.swap_lock
.acquire_swap_lock(Uuid::parse_str(SWAP_ID).unwrap())
.await
.unwrap();
let cloned_ctx = ctx.clone();
tokio::spawn(async move {
// Immediately release lock when suspend signal is received. Mocks a running swap that is then cancelled.
ctx.swap_lock
.listen_for_swap_force_suspension()
.await
.unwrap();
ctx.swap_lock.release_swap_lock().await.unwrap();
});
let response: HashMap<String, String> = client
.request("suspend_current_swap", ObjectParams::new())
.await
.unwrap();
assert_eq!(
response,
HashMap::from([("swapId".to_string(), SWAP_ID.to_string())])
);
cloned_ctx
.swap_lock
.acquire_swap_lock(Uuid::parse_str(SWAP_ID).unwrap())
.await
.unwrap();
let response: Result<HashMap<String, String>, _> = client
.request("suspend_current_swap", ObjectParams::new())
.await;
response.expect_err("Expected an error when suspend signal times out");
Ok(())
})
.await;
}
}
Loading…
Cancel
Save