mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-03-02 03:29:14 -05:00
merging master changes
This commit is contained in:
commit
925c7bc179
3
.github/workflows/build-release-binaries.yml
vendored
3
.github/workflows/build-release-binaries.yml
vendored
@ -52,8 +52,9 @@ jobs:
|
|||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2.2.0
|
- uses: Swatinem/rust-cache@v2.2.0
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
|
toolchain: 1.63
|
||||||
targets: armv7-unknown-linux-gnueabihf
|
targets: armv7-unknown-linux-gnueabihf
|
||||||
|
|
||||||
- name: Build ${{ matrix.target }} ${{ matrix.bin }} release binary
|
- name: Build ${{ matrix.target }} ${{ matrix.bin }} release binary
|
||||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -75,8 +75,9 @@ jobs:
|
|||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2.2.0
|
- uses: Swatinem/rust-cache@v2.2.0
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
|
toolchain: 1.63
|
||||||
targets: armv7-unknown-linux-gnueabihf
|
targets: armv7-unknown-linux-gnueabihf
|
||||||
|
|
||||||
- name: Build binary
|
- name: Build binary
|
||||||
@ -134,6 +135,7 @@ jobs:
|
|||||||
happy_path_restart_bob_before_xmr_locked,
|
happy_path_restart_bob_before_xmr_locked,
|
||||||
happy_path_restart_alice_after_xmr_locked,
|
happy_path_restart_alice_after_xmr_locked,
|
||||||
alice_and_bob_refund_using_cancel_and_refund_command,
|
alice_and_bob_refund_using_cancel_and_refund_command,
|
||||||
|
alice_and_bob_refund_using_cancel_then_refund_command,
|
||||||
alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired,
|
alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired,
|
||||||
punish,
|
punish,
|
||||||
alice_punishes_after_restart_bob_dead,
|
alice_punishes_after_restart_bob_dead,
|
||||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Minimum Supported Rust Version (MSRV) bumped to 1.63
|
||||||
|
|
||||||
|
## [0.12.1] - 2023-01-09
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Swap: merge separate cancel/refund commands into one `cancel-and-refund` command for stuck swaps
|
||||||
|
|
||||||
## [0.12.0] - 2022-12-31
|
## [0.12.0] - 2022-12-31
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@ -332,7 +342,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.
|
- 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.
|
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.0...HEAD
|
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.1...HEAD
|
||||||
|
[0.12.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.0...0.12.1
|
||||||
[0.12.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...0.12.0
|
[0.12.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...0.12.0
|
||||||
[0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0
|
[0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0
|
||||||
[0.10.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.1...0.10.2
|
[0.10.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.1...0.10.2
|
||||||
|
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -130,9 +130,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.60"
|
version = "0.1.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
|
checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -4214,7 +4214,7 @@ checksum = "8049cf85f0e715d6af38dde439cb0ccb91f67fb9f5f63c80f8b43e48356e1a3f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swap"
|
name = "swap"
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-compression",
|
"async-compression",
|
||||||
|
@ -50,7 +50,7 @@ Please have a look at the [contribution guidelines](./CONTRIBUTING.md).
|
|||||||
## Rust Version Support
|
## Rust Version Support
|
||||||
|
|
||||||
Please note that only the latest stable Rust toolchain is supported.
|
Please note that only the latest stable Rust toolchain is supported.
|
||||||
All stable toolchains since 1.62 _should_ work.
|
All stable toolchains since 1.63 _should_ work.
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ status = [
|
|||||||
"docker_tests (happy_path_restart_alice_after_xmr_locked)",
|
"docker_tests (happy_path_restart_alice_after_xmr_locked)",
|
||||||
"docker_tests (happy_path_restart_bob_before_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_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 (alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired)",
|
||||||
"docker_tests (punish)",
|
"docker_tests (punish)",
|
||||||
"docker_tests (alice_punishes_after_restart_bob_dead)",
|
"docker_tests (alice_punishes_after_restart_bob_dead)",
|
||||||
|
@ -157,7 +157,7 @@ pub struct OutKey {
|
|||||||
pub unlocked: bool,
|
pub unlocked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
pub struct BaseResponse {
|
pub struct BaseResponse {
|
||||||
pub credits: u64,
|
pub credits: u64,
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
@ -165,7 +165,7 @@ pub struct BaseResponse {
|
|||||||
pub untrusted: bool,
|
pub untrusted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
pub struct GetOIndexesResponse {
|
pub struct GetOIndexesResponse {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: BaseResponse,
|
pub base: BaseResponse,
|
||||||
@ -173,7 +173,7 @@ pub struct GetOIndexesResponse {
|
|||||||
pub o_indexes: Vec<u64>,
|
pub o_indexes: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
#[serde(rename = "OK")]
|
#[serde(rename = "OK")]
|
||||||
Ok,
|
Ok,
|
||||||
|
@ -157,7 +157,7 @@ pub struct Transfer {
|
|||||||
pub unsigned_txset: String,
|
pub unsigned_txset: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct BlockHeight {
|
pub struct BlockHeight {
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.62"
|
channel = "1.63" # also update this in the readme, changelog, and github actions
|
||||||
components = ["clippy"]
|
components = ["clippy"]
|
||||||
targets = ["armv7-unknown-linux-gnueabihf"]
|
targets = ["armv7-unknown-linux-gnueabihf"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "swap"
|
name = "swap"
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
authors = [ "The COMIT guys <hello@comit.network>" ]
|
authors = [ "The COMIT guys <hello@comit.network>" ]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "XMR/BTC trustless atomic swaps."
|
description = "XMR/BTC trustless atomic swaps."
|
||||||
|
@ -301,7 +301,7 @@ pub mod api_test {
|
|||||||
swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()),
|
swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cmd: Method::Cancel,
|
cmd: Method::CancelAndRefund,
|
||||||
shutdown: Shutdown::new(tx.subscribe()),
|
shutdown: Shutdown::new(tx.subscribe()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ pub mod api_test {
|
|||||||
swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()),
|
swap_id: Some(Uuid::from_str(SWAP_ID).unwrap()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cmd: Method::Refund,
|
cmd: Method::CancelAndRefund,
|
||||||
shutdown: Shutdown::new(tx.subscribe()),
|
shutdown: Shutdown::new(tx.subscribe()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,17 +377,10 @@ impl Request {
|
|||||||
"result": []
|
"result": []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Method::Cancel => {
|
Method::CancelAndRefund => {
|
||||||
let bitcoin_wallet = context.bitcoin_wallet.as_ref().unwrap();
|
let bitcoin_wallet = context.bitcoin_wallet.as_ref().unwrap();
|
||||||
|
|
||||||
let (txid, _) = cli::cancel(
|
let (txid, _) = cli::cancel_and_refund(swap_id, Arc::clone(bitcoin_wallet), Arc::clone(&context.db).await?;
|
||||||
self.params.swap_id.unwrap(),
|
|
||||||
Arc::clone(bitcoin_wallet),
|
|
||||||
Arc::clone(&context.db),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tracing::debug!("Cancel transaction successfully published with id {}", txid);
|
|
||||||
|
|
||||||
json!({
|
json!({
|
||||||
"txid": txid,
|
"txid": txid,
|
||||||
|
@ -171,7 +171,7 @@ fn env_config(is_testnet: bool) -> env::Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Serialize)]
|
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||||
#[error("Invalid Bitcoin address provided, expected address on network {expected:?} but address provided is on {actual:?}")]
|
#[error("Invalid Bitcoin address provided, expected address on network {expected:?} but address provided is on {actual:?}")]
|
||||||
pub struct BitcoinAddressNetworkMismatch {
|
pub struct BitcoinAddressNetworkMismatch {
|
||||||
#[serde(with = "crate::bitcoin::network")]
|
#[serde(with = "crate::bitcoin::network")]
|
||||||
@ -180,7 +180,7 @@ pub struct BitcoinAddressNetworkMismatch {
|
|||||||
actual: bitcoin::Network,
|
actual: bitcoin::Network,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
pub testnet: bool,
|
pub testnet: bool,
|
||||||
pub json: bool,
|
pub json: bool,
|
||||||
@ -190,7 +190,7 @@ pub struct Arguments {
|
|||||||
pub cmd: Command,
|
pub cmd: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Start {
|
Start {
|
||||||
resume_only: bool,
|
resume_only: bool,
|
||||||
|
@ -84,7 +84,7 @@ const DEFAULT_MIN_BUY_AMOUNT: f64 = 0.002f64;
|
|||||||
const DEFAULT_MAX_BUY_AMOUNT: f64 = 0.02f64;
|
const DEFAULT_MAX_BUY_AMOUNT: f64 = 0.02f64;
|
||||||
const DEFAULT_SPREAD: f64 = 0.02f64;
|
const DEFAULT_SPREAD: f64 = 0.02f64;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub data: Data,
|
pub data: Data,
|
||||||
@ -123,13 +123,13 @@ impl TryFrom<config::Config> for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub dir: PathBuf,
|
pub dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Network {
|
pub struct Network {
|
||||||
#[serde(deserialize_with = "addr_list::deserialize")]
|
#[serde(deserialize_with = "addr_list::deserialize")]
|
||||||
@ -181,7 +181,7 @@ mod addr_list {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Bitcoin {
|
pub struct Bitcoin {
|
||||||
pub electrum_rpc_url: Url,
|
pub electrum_rpc_url: Url,
|
||||||
@ -191,7 +191,7 @@ pub struct Bitcoin {
|
|||||||
pub network: bitcoin::Network,
|
pub network: bitcoin::Network,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Monero {
|
pub struct Monero {
|
||||||
pub wallet_rpc_url: Url,
|
pub wallet_rpc_url: Url,
|
||||||
@ -200,14 +200,14 @@ pub struct Monero {
|
|||||||
pub network: monero::Network,
|
pub network: monero::Network,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct TorConf {
|
pub struct TorConf {
|
||||||
pub control_port: u16,
|
pub control_port: u16,
|
||||||
pub socks5_port: u16,
|
pub socks5_port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Maker {
|
pub struct Maker {
|
||||||
#[serde(with = "::bitcoin::util::amount::serde::as_btc")]
|
#[serde(with = "::bitcoin::util::amount::serde::as_btc")]
|
||||||
|
@ -5,7 +5,7 @@ use rust_decimal::Decimal;
|
|||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
|
||||||
/// Represents the rate at which we are willing to trade 1 XMR.
|
/// Represents the rate at which we are willing to trade 1 XMR.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Rate {
|
pub struct Rate {
|
||||||
/// Represents the asking price from the market.
|
/// Represents the asking price from the market.
|
||||||
ask: bitcoin::Amount,
|
ask: bitcoin::Amount,
|
||||||
|
@ -108,7 +108,7 @@ impl SecretKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct PublicKey(Point);
|
pub struct PublicKey(Point);
|
||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
|
@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
const SCRIPT_SIZE: usize = 34;
|
const SCRIPT_SIZE: usize = 34;
|
||||||
const TX_LOCK_WEIGHT: usize = 485;
|
const TX_LOCK_WEIGHT: usize = 485;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct TxLock {
|
pub struct TxLock {
|
||||||
inner: PartiallySignedTransaction,
|
inner: PartiallySignedTransaction,
|
||||||
pub(in crate::bitcoin) output_descriptor: Descriptor<::bitcoin::PublicKey>,
|
pub(in crate::bitcoin) output_descriptor: Descriptor<::bitcoin::PublicKey>,
|
||||||
|
@ -37,7 +37,7 @@ impl Add<u32> for BlockHeight {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ExpiredTimelocks {
|
pub enum ExpiredTimelocks {
|
||||||
None,
|
None,
|
||||||
Cancel,
|
Cancel,
|
||||||
|
@ -876,7 +876,7 @@ impl EstimateFeeRate for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum ScriptStatus {
|
pub enum ScriptStatus {
|
||||||
Unseen,
|
Unseen,
|
||||||
InMempool,
|
InMempool,
|
||||||
@ -893,7 +893,7 @@ impl ScriptStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Confirmed {
|
pub struct Confirmed {
|
||||||
/// The depth of this transaction within the blockchain.
|
/// The depth of this transaction within the blockchain.
|
||||||
///
|
///
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
mod behaviour;
|
mod behaviour;
|
||||||
pub mod cancel;
|
pub mod cancel_and_refund;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod list_sellers;
|
mod list_sellers;
|
||||||
pub mod refund;
|
|
||||||
pub mod tracing;
|
pub mod tracing;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
pub use behaviour::{Behaviour, OutEvent};
|
pub use behaviour::{Behaviour, OutEvent};
|
||||||
pub use cancel::cancel;
|
pub use cancel_and_refund::{cancel, cancel_and_refund, refund};
|
||||||
pub use event_loop::{EventLoop, EventLoopHandle};
|
pub use event_loop::{EventLoop, EventLoopHandle};
|
||||||
pub use list_sellers::{list_sellers, Seller, Status as SellerStatus};
|
pub use list_sellers::{list_sellers, Seller, Status as SellerStatus};
|
||||||
pub use refund::refund;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Txid, Wallet};
|
|
||||||
use crate::protocol::bob::BobState;
|
|
||||||
use crate::protocol::Database;
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub async fn cancel(
|
|
||||||
swap_id: Uuid,
|
|
||||||
bitcoin_wallet: Arc<Wallet>,
|
|
||||||
db: Arc<dyn Database + Send + Sync>,
|
|
||||||
) -> Result<(Txid, BobState)> {
|
|
||||||
let state = db.get_state(swap_id).await?.try_into()?;
|
|
||||||
|
|
||||||
let state6 = match state {
|
|
||||||
BobState::BtcLocked { state3, .. } => state3.cancel(),
|
|
||||||
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
|
|
||||||
BobState::XmrLocked(state4) => state4.cancel(),
|
|
||||||
BobState::EncSigSent(state4) => state4.cancel(),
|
|
||||||
BobState::CancelTimelockExpired(state6) => state6,
|
|
||||||
BobState::BtcRefunded(state6) => state6,
|
|
||||||
BobState::BtcCancelled(state6) => state6,
|
|
||||||
|
|
||||||
BobState::Started { .. }
|
|
||||||
| BobState::SwapSetupCompleted(_)
|
|
||||||
| BobState::BtcRedeemed(_)
|
|
||||||
| BobState::XmrRedeemed { .. }
|
|
||||||
| BobState::BtcPunished { .. }
|
|
||||||
| BobState::SafelyAborted => bail!(
|
|
||||||
"Cannot cancel swap {} because it is in state {} which is not refundable.",
|
|
||||||
swap_id,
|
|
||||||
state
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing::info!(%swap_id, "Manually cancelling swap");
|
|
||||||
|
|
||||||
let txid = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
|
||||||
Ok(txid) => txid,
|
|
||||||
Err(err) => {
|
|
||||||
if let Ok(code) = parse_rpc_error_code(&err) {
|
|
||||||
if code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) {
|
|
||||||
tracing::info!("Cancel transaction has already been confirmed on chain")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bail!(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let state = BobState::BtcCancelled(state6);
|
|
||||||
db.insert_latest_state(swap_id, state.clone().into())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok((txid, state))
|
|
||||||
}
|
|
115
swap/src/cli/cancel_and_refund.rs
Normal file
115
swap/src/cli/cancel_and_refund.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
use crate::bitcoin::wallet::Subscription;
|
||||||
|
use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet};
|
||||||
|
use crate::protocol::bob::BobState;
|
||||||
|
use crate::protocol::Database;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use bitcoin::Txid;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub async fn cancel_and_refund(
|
||||||
|
swap_id: Uuid,
|
||||||
|
bitcoin_wallet: Arc<Wallet>,
|
||||||
|
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");
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = match refund(swap_id, bitcoin_wallet, db).await {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => bail!(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!("Refund transaction submitted");
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn cancel(
|
||||||
|
swap_id: Uuid,
|
||||||
|
bitcoin_wallet: Arc<Wallet>,
|
||||||
|
db: Arc<dyn Database>,
|
||||||
|
) -> Result<(Txid, Subscription, BobState)> {
|
||||||
|
let state = db.get_state(swap_id).await?.try_into()?;
|
||||||
|
|
||||||
|
let state6 = match state {
|
||||||
|
BobState::BtcLocked { state3, .. } => state3.cancel(),
|
||||||
|
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
|
||||||
|
BobState::XmrLocked(state4) => state4.cancel(),
|
||||||
|
BobState::EncSigSent(state4) => state4.cancel(),
|
||||||
|
BobState::CancelTimelockExpired(state6) => state6,
|
||||||
|
BobState::BtcRefunded(state6) => state6,
|
||||||
|
BobState::BtcCancelled(state6) => state6,
|
||||||
|
|
||||||
|
BobState::Started { .. }
|
||||||
|
| BobState::SwapSetupCompleted(_)
|
||||||
|
| BobState::BtcRedeemed(_)
|
||||||
|
| BobState::XmrRedeemed { .. }
|
||||||
|
| BobState::BtcPunished { .. }
|
||||||
|
| BobState::SafelyAborted => bail!(
|
||||||
|
"Cannot cancel swap {} because it is in state {} which is not refundable.",
|
||||||
|
swap_id,
|
||||||
|
state
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!(%swap_id, "Manually cancelling swap");
|
||||||
|
|
||||||
|
let (txid, subscription) = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
||||||
|
Ok(txid) => txid,
|
||||||
|
Err(err) => {
|
||||||
|
if let Ok(error_code) = parse_rpc_error_code(&err) {
|
||||||
|
tracing::debug!(%error_code, "parse rpc error");
|
||||||
|
if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) {
|
||||||
|
tracing::info!("Cancel transaction has already been confirmed on chain");
|
||||||
|
} else if error_code == i64::from(RpcErrorCode::RpcVerifyError) {
|
||||||
|
tracing::info!("General error trying to submit cancel transaction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bail!(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = BobState::BtcCancelled(state6);
|
||||||
|
db.insert_latest_state(swap_id, state.clone().into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok((txid, subscription, state))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn refund(
|
||||||
|
swap_id: Uuid,
|
||||||
|
bitcoin_wallet: Arc<Wallet>,
|
||||||
|
db: Arc<dyn Database>,
|
||||||
|
) -> Result<BobState> {
|
||||||
|
let state = db.get_state(swap_id).await?.try_into()?;
|
||||||
|
|
||||||
|
let state6 = match state {
|
||||||
|
BobState::BtcLocked { state3, .. } => state3.cancel(),
|
||||||
|
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
|
||||||
|
BobState::XmrLocked(state4) => state4.cancel(),
|
||||||
|
BobState::EncSigSent(state4) => state4.cancel(),
|
||||||
|
BobState::CancelTimelockExpired(state6) => state6,
|
||||||
|
BobState::BtcCancelled(state6) => state6,
|
||||||
|
BobState::Started { .. }
|
||||||
|
| BobState::SwapSetupCompleted(_)
|
||||||
|
| BobState::BtcRedeemed(_)
|
||||||
|
| BobState::BtcRefunded(_)
|
||||||
|
| BobState::XmrRedeemed { .. }
|
||||||
|
| BobState::BtcPunished { .. }
|
||||||
|
| BobState::SafelyAborted => bail!(
|
||||||
|
"Cannot refund swap {} because it is in state {} which is not refundable.",
|
||||||
|
swap_id,
|
||||||
|
state
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!(%swap_id, "Manually refunding swap");
|
||||||
|
state6.publish_refund_btc(bitcoin_wallet.as_ref()).await?;
|
||||||
|
|
||||||
|
let state = BobState::BtcRefunded(state6);
|
||||||
|
db.insert_latest_state(swap_id, state.clone().into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(state)
|
||||||
|
}
|
@ -227,7 +227,7 @@ where
|
|||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
}
|
}
|
||||||
CliCommand::Cancel {
|
CliCommand::CancelAndRefund {
|
||||||
swap_id: SwapId { swap_id },
|
swap_id: SwapId { swap_id },
|
||||||
bitcoin,
|
bitcoin,
|
||||||
tor,
|
tor,
|
||||||
@ -237,35 +237,7 @@ where
|
|||||||
swap_id: Some(swap_id),
|
swap_id: Some(swap_id),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cmd: Method::Cancel,
|
cmd: Method::CancelAndRefund,
|
||||||
shutdown: Shutdown::new(rx.subscribe()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let context = Context::build(
|
|
||||||
Some(bitcoin),
|
|
||||||
None,
|
|
||||||
Some(tor),
|
|
||||||
data,
|
|
||||||
is_testnet,
|
|
||||||
debug,
|
|
||||||
json,
|
|
||||||
None,
|
|
||||||
rx,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
(context, request)
|
|
||||||
}
|
|
||||||
CliCommand::Refund {
|
|
||||||
swap_id: SwapId { swap_id },
|
|
||||||
bitcoin,
|
|
||||||
tor,
|
|
||||||
} => {
|
|
||||||
let request = Request {
|
|
||||||
params: Params {
|
|
||||||
swap_id: Some(swap_id),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
cmd: Method::Refund,
|
|
||||||
shutdown: Shutdown::new(rx.subscribe()),
|
shutdown: Shutdown::new(rx.subscribe()),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -465,21 +437,9 @@ enum CliCommand {
|
|||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
tor: Tor,
|
tor: Tor,
|
||||||
},
|
},
|
||||||
/// Force submission of the cancel transaction overriding the protocol state
|
/// Force the submission of the cancel and refund transactions of a swap
|
||||||
/// machine and blockheight checks (expert users only)
|
#[structopt(aliases = &["cancel", "refund"])]
|
||||||
Cancel {
|
CancelAndRefund {
|
||||||
#[structopt(flatten)]
|
|
||||||
swap_id: SwapId,
|
|
||||||
|
|
||||||
#[structopt(flatten)]
|
|
||||||
bitcoin: Bitcoin,
|
|
||||||
|
|
||||||
#[structopt(flatten)]
|
|
||||||
tor: Tor,
|
|
||||||
},
|
|
||||||
/// Force submission of the refund transaction overriding the protocol state
|
|
||||||
/// machine and blockheight checks (expert users only)
|
|
||||||
Refund {
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
swap_id: SwapId,
|
swap_id: SwapId,
|
||||||
|
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
use crate::bitcoin::Wallet;
|
|
||||||
use crate::protocol::bob::BobState;
|
|
||||||
use crate::protocol::Database;
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub async fn refund(
|
|
||||||
swap_id: Uuid,
|
|
||||||
bitcoin_wallet: Arc<Wallet>,
|
|
||||||
db: Arc<dyn Database + Send + Sync>,
|
|
||||||
) -> Result<BobState> {
|
|
||||||
let state = db.get_state(swap_id).await?.try_into()?;
|
|
||||||
|
|
||||||
let state6 = match state {
|
|
||||||
BobState::BtcLocked { state3, .. } => state3.cancel(),
|
|
||||||
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
|
|
||||||
BobState::XmrLocked(state4) => state4.cancel(),
|
|
||||||
BobState::EncSigSent(state4) => state4.cancel(),
|
|
||||||
BobState::CancelTimelockExpired(state6) => state6,
|
|
||||||
BobState::BtcCancelled(state6) => state6,
|
|
||||||
BobState::Started { .. }
|
|
||||||
| BobState::SwapSetupCompleted(_)
|
|
||||||
| BobState::BtcRedeemed(_)
|
|
||||||
| BobState::BtcRefunded(_)
|
|
||||||
| BobState::XmrRedeemed { .. }
|
|
||||||
| BobState::BtcPunished { .. }
|
|
||||||
| BobState::SafelyAborted => bail!(
|
|
||||||
"Cannot refund swap {} because it is in state {} which is not refundable.",
|
|
||||||
swap_id,
|
|
||||||
state
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
state6.publish_refund_btc(bitcoin_wallet.as_ref()).await?;
|
|
||||||
|
|
||||||
let state = BobState::BtcRefunded(state6);
|
|
||||||
db.insert_latest_state(swap_id, state.clone().into())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(state)
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ use anyhow::anyhow;
|
|||||||
|
|
||||||
const LATEST_RELEASE_URL: &str = "https://github.com/comit-network/xmr-btc-swap/releases/latest";
|
const LATEST_RELEASE_URL: &str = "https://github.com/comit-network/xmr-btc-swap/releases/latest";
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Version {
|
pub enum Version {
|
||||||
Current,
|
Current,
|
||||||
Available,
|
Available,
|
||||||
|
@ -70,7 +70,7 @@ pub enum Alice {
|
|||||||
Done(AliceEndState),
|
Done(AliceEndState),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Copy, Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub enum AliceEndState {
|
pub enum AliceEndState {
|
||||||
SafelyAborted,
|
SafelyAborted,
|
||||||
BtcRedeemed,
|
BtcRedeemed,
|
||||||
|
@ -5,7 +5,7 @@ use std::cmp::max;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use time::ext::NumericalStdDuration;
|
use time::ext::NumericalStdDuration;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub bitcoin_lock_mempool_timeout: Duration,
|
pub bitcoin_lock_mempool_timeout: Duration,
|
||||||
pub bitcoin_lock_confirmed_timeout: Duration,
|
pub bitcoin_lock_confirmed_timeout: Duration,
|
||||||
|
@ -230,7 +230,7 @@ mod wire {
|
|||||||
use bitcoin::util::amount::ParseAmountError;
|
use bitcoin::util::amount::ParseAmountError;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(tag = "event")]
|
#[serde(tag = "event")]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
#[serde(rename = "systemStatus")]
|
#[serde(rename = "systemStatus")]
|
||||||
|
@ -39,7 +39,7 @@ pub fn private_key_from_secp256k1_scalar(scalar: bitcoin::Scalar) -> PrivateKey
|
|||||||
PrivateKey::from_scalar(Scalar::from_bytes_mod_order(bytes))
|
PrivateKey::from_scalar(Scalar::from_bytes_mod_order(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct PrivateViewKey(#[serde(with = "monero_private_key")] PrivateKey);
|
pub struct PrivateViewKey(#[serde(with = "monero_private_key")] PrivateKey);
|
||||||
|
|
||||||
impl PrivateViewKey {
|
impl PrivateViewKey {
|
||||||
@ -78,7 +78,7 @@ impl From<PublicViewKey> for PublicKey {
|
|||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct PublicViewKey(PublicKey);
|
pub struct PublicViewKey(PublicKey);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd)]
|
||||||
pub struct Amount(u64);
|
pub struct Amount(u64);
|
||||||
|
|
||||||
// Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, XMR 0.000_008 * 2 (to be on the safe side)
|
// Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, XMR 0.000_008 * 2 (to be on the safe side)
|
||||||
@ -185,7 +185,7 @@ impl fmt::Display for Amount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct TransferProof {
|
pub struct TransferProof {
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
#[serde(with = "monero_private_key")]
|
#[serde(with = "monero_private_key")]
|
||||||
@ -205,7 +205,7 @@ impl TransferProof {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add constructor/ change String to fixed length byte array
|
// TODO: add constructor/ change String to fixed length byte array
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct TxHash(pub String);
|
pub struct TxHash(pub String);
|
||||||
|
|
||||||
impl From<TxHash> for String {
|
impl From<TxHash> for String {
|
||||||
@ -227,7 +227,7 @@ pub struct InsufficientFunds {
|
|||||||
pub actual: Amount,
|
pub actual: Amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
|
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
|
||||||
#[error("Overflow, cannot convert {0} to u64")]
|
#[error("Overflow, cannot convert {0} to u64")]
|
||||||
pub struct OverflowError(pub String);
|
pub struct OverflowError(pub String);
|
||||||
|
|
||||||
@ -507,10 +507,10 @@ mod tests {
|
|||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct MoneroPrivateKey(#[serde(with = "monero_private_key")] crate::monero::PrivateKey);
|
pub struct MoneroPrivateKey(#[serde(with = "monero_private_key")] crate::monero::PrivateKey);
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct MoneroAmount(#[serde(with = "monero_amount")] crate::monero::Amount);
|
pub struct MoneroAmount(#[serde(with = "monero_amount")] crate::monero::Amount);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -26,15 +26,16 @@ impl Wallet {
|
|||||||
pub async fn open_or_create(url: Url, name: String, env_config: Config) -> Result<Self> {
|
pub async fn open_or_create(url: Url, name: String, env_config: Config) -> Result<Self> {
|
||||||
let client = wallet::Client::new(url)?;
|
let client = wallet::Client::new(url)?;
|
||||||
|
|
||||||
let open_wallet_response = client.open_wallet(name.clone()).await;
|
match client.open_wallet(name.clone()).await {
|
||||||
if open_wallet_response.is_err() {
|
Err(error) => {
|
||||||
client.create_wallet(name.clone(), "English".to_owned()).await.context(
|
tracing::debug!(%error, "Open wallet response error");
|
||||||
"Unable to create Monero wallet, please ensure that the monero-wallet-rpc is available",
|
client.create_wallet(name.clone(), "English".to_owned()).await.context(
|
||||||
)?;
|
"Unable to create Monero wallet, please ensure that the monero-wallet-rpc is available",
|
||||||
|
)?;
|
||||||
|
|
||||||
tracing::debug!(monero_wallet_name = %name, "Created Monero wallet");
|
tracing::debug!(monero_wallet_name = %name, "Created Monero wallet");
|
||||||
} else {
|
}
|
||||||
tracing::debug!(monero_wallet_name = %name, "Opened Monero wallet");
|
Ok(_) => tracing::debug!(monero_wallet_name = %name, "Opened Monero wallet"),
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::connect(client, name, env_config).await
|
Self::connect(client, name, env_config).await
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use libp2p::rendezvous::Namespace;
|
use libp2p::rendezvous::Namespace;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum XmrBtcNamespace {
|
pub enum XmrBtcNamespace {
|
||||||
Mainnet,
|
Mainnet,
|
||||||
Testnet,
|
Testnet,
|
||||||
|
@ -37,7 +37,7 @@ pub mod protocol {
|
|||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct BlockchainNetwork {
|
pub struct BlockchainNetwork {
|
||||||
#[serde(with = "crate::bitcoin::network")]
|
#[serde(with = "crate::bitcoin::network")]
|
||||||
pub bitcoin: bitcoin::Network,
|
pub bitcoin: bitcoin::Network,
|
||||||
|
@ -258,7 +258,7 @@ impl From<SpotPriceResponse> for Result<monero::Amount, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, thiserror::Error, PartialEq)]
|
#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Seller currently does not accept incoming swap requests, please try again later")]
|
#[error("Seller currently does not accept incoming swap requests, please try again later")]
|
||||||
NoSwapsAccepted,
|
NoSwapsAccepted,
|
||||||
|
@ -102,11 +102,11 @@ impl From<BobState> for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq)]
|
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[error("Not in the role of Alice")]
|
#[error("Not in the role of Alice")]
|
||||||
pub struct NotAlice;
|
pub struct NotAlice;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq)]
|
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[error("Not in the role of Bob")]
|
#[error("Not in the role of Bob")]
|
||||||
pub struct NotBob;
|
pub struct NotBob;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::bitcoin::wallet::EstimateFeeRate;
|
use crate::bitcoin::wallet::{EstimateFeeRate, Subscription};
|
||||||
use crate::bitcoin::{
|
use crate::bitcoin::{
|
||||||
self, current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel,
|
self, current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel,
|
||||||
TxLock, Txid,
|
TxLock, Txid,
|
||||||
@ -561,7 +561,7 @@ impl State4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct State5 {
|
pub struct State5 {
|
||||||
#[serde(with = "monero_private_key")]
|
#[serde(with = "monero_private_key")]
|
||||||
s_a: monero::PrivateKey,
|
s_a: monero::PrivateKey,
|
||||||
@ -642,7 +642,10 @@ impl State6 {
|
|||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn submit_tx_cancel(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<Txid> {
|
pub async fn submit_tx_cancel(
|
||||||
|
&self,
|
||||||
|
bitcoin_wallet: &bitcoin::Wallet,
|
||||||
|
) -> Result<(Txid, Subscription)> {
|
||||||
let transaction = bitcoin::TxCancel::new(
|
let transaction = bitcoin::TxCancel::new(
|
||||||
&self.tx_lock,
|
&self.tx_lock,
|
||||||
self.cancel_timelock,
|
self.cancel_timelock,
|
||||||
@ -653,9 +656,9 @@ impl State6 {
|
|||||||
.complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone())
|
.complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone())
|
||||||
.context("Failed to complete Bitcoin cancel transaction")?;
|
.context("Failed to complete Bitcoin cancel transaction")?;
|
||||||
|
|
||||||
let (tx_id, _) = bitcoin_wallet.broadcast(transaction, "cancel").await?;
|
let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?;
|
||||||
|
|
||||||
Ok(tx_id)
|
Ok((tx_id, subscription))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn publish_refund_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<()> {
|
pub async fn publish_refund_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<()> {
|
||||||
|
@ -50,7 +50,7 @@ async fn given_alice_and_bob_manually_refund_after_funds_locked_both_refund() {
|
|||||||
|
|
||||||
// Bob manually cancels
|
// Bob manually cancels
|
||||||
bob_join_handle.abort();
|
bob_join_handle.abort();
|
||||||
let (_, state) = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db).await?;
|
let (_, _, state) = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db).await?;
|
||||||
assert!(matches!(state, BobState::BtcCancelled { .. }));
|
assert!(matches!(state, BobState::BtcCancelled { .. }));
|
||||||
|
|
||||||
let (bob_swap, bob_join_handle) = ctx
|
let (bob_swap, bob_join_handle) = ctx
|
||||||
@ -64,7 +64,7 @@ async fn given_alice_and_bob_manually_refund_after_funds_locked_both_refund() {
|
|||||||
|
|
||||||
ctx.assert_bob_refunded(bob_state).await;
|
ctx.assert_bob_refunded(bob_state).await;
|
||||||
|
|
||||||
// manually refund ALice's swap
|
// manually refund Alice's swap
|
||||||
ctx.restart_alice().await;
|
ctx.restart_alice().await;
|
||||||
let alice_swap = ctx.alice_next_swap().await;
|
let alice_swap = ctx.alice_next_swap().await;
|
||||||
let alice_state = asb::refund(
|
let alice_state = asb::refund(
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
pub mod harness;
|
||||||
|
|
||||||
|
use harness::alice_run_until::is_xmr_lock_transaction_sent;
|
||||||
|
use harness::bob_run_until::is_btc_locked;
|
||||||
|
use harness::FastCancelConfig;
|
||||||
|
use swap::asb::FixedRate;
|
||||||
|
use swap::protocol::alice::AliceState;
|
||||||
|
use swap::protocol::bob::BobState;
|
||||||
|
use swap::protocol::{alice, bob};
|
||||||
|
use swap::{asb, cli};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn given_alice_and_bob_manually_cancel_and_refund_after_funds_locked_both_refund() {
|
||||||
|
harness::setup_test(FastCancelConfig, |mut ctx| async move {
|
||||||
|
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||||
|
let bob_swap_id = bob_swap.id;
|
||||||
|
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
|
||||||
|
|
||||||
|
let alice_swap = ctx.alice_next_swap().await;
|
||||||
|
let alice_swap = tokio::spawn(alice::run_until(
|
||||||
|
alice_swap,
|
||||||
|
is_xmr_lock_transaction_sent,
|
||||||
|
FixedRate::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let bob_state = bob_swap.await??;
|
||||||
|
assert!(matches!(bob_state, BobState::BtcLocked { .. }));
|
||||||
|
|
||||||
|
let alice_state = alice_swap.await??;
|
||||||
|
assert!(matches!(
|
||||||
|
alice_state,
|
||||||
|
AliceState::XmrLockTransactionSent { .. }
|
||||||
|
));
|
||||||
|
|
||||||
|
let (bob_swap, bob_join_handle) = ctx
|
||||||
|
.stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Ensure cancel timelock is expired
|
||||||
|
if let BobState::BtcLocked { state3, .. } = bob_swap.state.clone() {
|
||||||
|
bob_swap
|
||||||
|
.bitcoin_wallet
|
||||||
|
.subscribe_to(state3.tx_lock)
|
||||||
|
.await
|
||||||
|
.wait_until_confirmed_with(state3.cancel_timelock)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
panic!("Bob in unexpected state {}", bob_swap.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob manually cancels and refunds
|
||||||
|
bob_join_handle.abort();
|
||||||
|
let bob_state =
|
||||||
|
cli::cancel_and_refund(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db).await?;
|
||||||
|
|
||||||
|
ctx.assert_bob_refunded(bob_state).await;
|
||||||
|
|
||||||
|
// manually refund Alice's swap
|
||||||
|
ctx.restart_alice().await;
|
||||||
|
let alice_swap = ctx.alice_next_swap().await;
|
||||||
|
let alice_state = asb::refund(
|
||||||
|
alice_swap.swap_id,
|
||||||
|
alice_swap.bitcoin_wallet,
|
||||||
|
alice_swap.monero_wallet,
|
||||||
|
alice_swap.db,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
ctx.assert_alice_refunded(alice_state).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user