mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-11-15 18:12:53 +00:00
Merge pull request #148 from comit-network/properly-wait-for-tx-finality-for-assertion-on-bitcoin-balance
Wait for Bob's refund finality
This commit is contained in:
commit
9fa900dce8
@ -135,11 +135,9 @@ async fn main() -> Result<()> {
|
|||||||
Arc::new(monero_wallet),
|
Arc::new(monero_wallet),
|
||||||
alice_addr,
|
alice_addr,
|
||||||
alice_peer_id,
|
alice_peer_id,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
let (swap, event_loop) = bob_factory
|
let (swap, event_loop) = bob_factory.with_init_params(swap_amounts).build().await?;
|
||||||
.with_init_params(swap_amounts, config)
|
|
||||||
.build()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tokio::spawn(async move { event_loop.run().await });
|
tokio::spawn(async move { event_loop.run().await });
|
||||||
bob::run(swap).await?;
|
bob::run(swap).await?;
|
||||||
@ -212,6 +210,7 @@ async fn main() -> Result<()> {
|
|||||||
Arc::new(monero_wallet),
|
Arc::new(monero_wallet),
|
||||||
alice_addr,
|
alice_addr,
|
||||||
alice_peer_id,
|
alice_peer_id,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
let (swap, event_loop) = bob_factory.build().await?;
|
let (swap, event_loop) = bob_factory.build().await?;
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ pub struct Swap {
|
|||||||
pub db: Database,
|
pub db: Database,
|
||||||
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
|
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||||
pub monero_wallet: Arc<monero::Wallet>,
|
pub monero_wallet: Arc<monero::Wallet>,
|
||||||
|
pub config: Config,
|
||||||
pub swap_id: Uuid,
|
pub swap_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,17 +61,16 @@ pub struct Builder {
|
|||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
|
|
||||||
init_params: InitParams,
|
init_params: InitParams,
|
||||||
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum InitParams {
|
enum InitParams {
|
||||||
None,
|
None,
|
||||||
New {
|
New { swap_amounts: SwapAmounts },
|
||||||
swap_amounts: SwapAmounts,
|
|
||||||
config: Config,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
seed: Seed,
|
seed: Seed,
|
||||||
db_path: PathBuf,
|
db_path: PathBuf,
|
||||||
@ -79,6 +79,7 @@ impl Builder {
|
|||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
alice_address: Multiaddr,
|
alice_address: Multiaddr,
|
||||||
alice_peer_id: PeerId,
|
alice_peer_id: PeerId,
|
||||||
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let identity = network::Seed::new(seed).derive_libp2p_identity();
|
let identity = network::Seed::new(seed).derive_libp2p_identity();
|
||||||
let peer_id = identity.public().into_peer_id();
|
let peer_id = identity.public().into_peer_id();
|
||||||
@ -93,27 +94,22 @@ impl Builder {
|
|||||||
bitcoin_wallet,
|
bitcoin_wallet,
|
||||||
monero_wallet,
|
monero_wallet,
|
||||||
init_params: InitParams::None,
|
init_params: InitParams::None,
|
||||||
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_init_params(self, swap_amounts: SwapAmounts, config: Config) -> Self {
|
pub fn with_init_params(self, swap_amounts: SwapAmounts) -> Self {
|
||||||
Self {
|
Self {
|
||||||
init_params: InitParams::New {
|
init_params: InitParams::New { swap_amounts },
|
||||||
swap_amounts,
|
|
||||||
config,
|
|
||||||
},
|
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(self) -> Result<(bob::Swap, bob::EventLoop)> {
|
pub async fn build(self) -> Result<(bob::Swap, bob::EventLoop)> {
|
||||||
match self.init_params {
|
match self.init_params {
|
||||||
InitParams::New {
|
InitParams::New { swap_amounts } => {
|
||||||
swap_amounts,
|
|
||||||
config,
|
|
||||||
} => {
|
|
||||||
let initial_state = self
|
let initial_state = self
|
||||||
.make_initial_state(swap_amounts.btc, swap_amounts.xmr, config)
|
.make_initial_state(swap_amounts.btc, swap_amounts.xmr, self.config)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (event_loop, event_loop_handle) = self.init_event_loop()?;
|
let (event_loop, event_loop_handle) = self.init_event_loop()?;
|
||||||
@ -128,10 +124,12 @@ impl Builder {
|
|||||||
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
||||||
monero_wallet: self.monero_wallet.clone(),
|
monero_wallet: self.monero_wallet.clone(),
|
||||||
swap_id: self.swap_id,
|
swap_id: self.swap_id,
|
||||||
|
config: self.config,
|
||||||
},
|
},
|
||||||
event_loop,
|
event_loop,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
InitParams::None => {
|
InitParams::None => {
|
||||||
// reopen the existing database
|
// reopen the existing database
|
||||||
let db = Database::open(self.db_path.as_path())?;
|
let db = Database::open(self.db_path.as_path())?;
|
||||||
@ -155,6 +153,7 @@ impl Builder {
|
|||||||
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
||||||
monero_wallet: self.monero_wallet.clone(),
|
monero_wallet: self.monero_wallet.clone(),
|
||||||
swap_id: self.swap_id,
|
swap_id: self.swap_id,
|
||||||
|
config: self.config,
|
||||||
},
|
},
|
||||||
event_loop,
|
event_loop,
|
||||||
))
|
))
|
||||||
|
@ -15,6 +15,7 @@ use crate::{
|
|||||||
BroadcastSignedTransaction, BuildTxLockPsbt, GetBlockHeight, GetNetwork, GetRawTransaction,
|
BroadcastSignedTransaction, BuildTxLockPsbt, GetBlockHeight, GetNetwork, GetRawTransaction,
|
||||||
Transaction, TransactionBlockHeight, TxCancel, Txid, WatchForRawTransaction,
|
Transaction, TransactionBlockHeight, TxCancel, Txid, WatchForRawTransaction,
|
||||||
},
|
},
|
||||||
|
config::Config,
|
||||||
monero,
|
monero,
|
||||||
monero::monero_private_key,
|
monero::monero_private_key,
|
||||||
protocol::{alice, bob},
|
protocol::{alice, bob},
|
||||||
@ -550,46 +551,34 @@ impl State4 {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refund_btc<W: bitcoin::BroadcastSignedTransaction>(
|
pub async fn refund_btc<W>(&self, bitcoin_wallet: &W, config: Config) -> Result<()>
|
||||||
&self,
|
where
|
||||||
bitcoin_wallet: &W,
|
W: bitcoin::BroadcastSignedTransaction + bitcoin::WaitForTransactionFinality,
|
||||||
) -> Result<()> {
|
{
|
||||||
let tx_cancel =
|
let tx_cancel =
|
||||||
bitcoin::TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
bitcoin::TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public());
|
||||||
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address);
|
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address);
|
||||||
|
|
||||||
{
|
let adaptor = Adaptor::<Sha256, Deterministic<Sha256>>::default();
|
||||||
let sig_b = self.b.sign(tx_cancel.digest());
|
|
||||||
let sig_a = self.tx_cancel_sig_a.clone();
|
|
||||||
|
|
||||||
let signed_tx_cancel = tx_cancel.clone().add_signatures(
|
let sig_b = self.b.sign(tx_refund.digest());
|
||||||
&self.tx_lock,
|
let sig_a =
|
||||||
(self.A, sig_a),
|
adaptor.decrypt_signature(&self.s_b.into_secp256k1(), self.tx_refund_encsig.clone());
|
||||||
(self.b.public(), sig_b),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let _ = bitcoin_wallet
|
let signed_tx_refund = tx_refund.add_signatures(
|
||||||
.broadcast_signed_transaction(signed_tx_cancel)
|
&tx_cancel.clone(),
|
||||||
.await?;
|
(self.A, sig_a),
|
||||||
}
|
(self.b.public(), sig_b),
|
||||||
|
)?;
|
||||||
|
|
||||||
{
|
let txid = bitcoin_wallet
|
||||||
let adaptor = Adaptor::<Sha256, Deterministic<Sha256>>::default();
|
.broadcast_signed_transaction(signed_tx_refund)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let sig_b = self.b.sign(tx_refund.digest());
|
bitcoin_wallet
|
||||||
let sig_a = adaptor
|
.wait_for_transaction_finality(txid, config)
|
||||||
.decrypt_signature(&self.s_b.into_secp256k1(), self.tx_refund_encsig.clone());
|
.await?;
|
||||||
|
|
||||||
let signed_tx_refund = tx_refund.add_signatures(
|
|
||||||
&tx_cancel.clone(),
|
|
||||||
(self.A, sig_a),
|
|
||||||
(self.b.public(), sig_b),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let _ = bitcoin_wallet
|
|
||||||
.broadcast_signed_transaction(signed_tx_refund)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bitcoin,
|
bitcoin,
|
||||||
|
config::Config,
|
||||||
database::{Database, Swap},
|
database::{Database, Swap},
|
||||||
monero,
|
monero,
|
||||||
protocol::bob::{self, event_loop::EventLoopHandle, state::*},
|
protocol::bob::{self, event_loop::EventLoopHandle, state::*},
|
||||||
@ -54,6 +55,7 @@ pub async fn run_until(
|
|||||||
swap.monero_wallet,
|
swap.monero_wallet,
|
||||||
OsRng,
|
OsRng,
|
||||||
swap.swap_id,
|
swap.swap_id,
|
||||||
|
swap.config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -70,6 +72,7 @@ async fn run_until_internal<R>(
|
|||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
mut rng: R,
|
mut rng: R,
|
||||||
swap_id: Uuid,
|
swap_id: Uuid,
|
||||||
|
config: Config,
|
||||||
) -> Result<BobState>
|
) -> Result<BobState>
|
||||||
where
|
where
|
||||||
R: RngCore + CryptoRng + Send,
|
R: RngCore + CryptoRng + Send,
|
||||||
@ -103,6 +106,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -124,6 +128,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -189,6 +194,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -230,6 +236,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -265,6 +272,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -286,6 +294,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -311,6 +320,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -321,7 +331,7 @@ where
|
|||||||
bail!("Internal error: canceled state reached before cancel timelock was expired");
|
bail!("Internal error: canceled state reached before cancel timelock was expired");
|
||||||
}
|
}
|
||||||
ExpiredTimelocks::Cancel => {
|
ExpiredTimelocks::Cancel => {
|
||||||
state.refund_btc(bitcoin_wallet.as_ref()).await?;
|
state.refund_btc(bitcoin_wallet.as_ref(), config).await?;
|
||||||
BobState::BtcRefunded(state)
|
BobState::BtcRefunded(state)
|
||||||
}
|
}
|
||||||
ExpiredTimelocks::Punish => BobState::BtcPunished {
|
ExpiredTimelocks::Punish => BobState::BtcPunished {
|
||||||
@ -340,6 +350,7 @@ where
|
|||||||
monero_wallet,
|
monero_wallet,
|
||||||
rng,
|
rng,
|
||||||
swap_id,
|
swap_id,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ pub mod testutils;
|
|||||||
/// Bob locks btc and Alice locks xmr. Alice fails to act so Bob refunds. Alice
|
/// Bob locks btc and Alice locks xmr. Alice fails to act so Bob refunds. Alice
|
||||||
/// then also refunds.
|
/// then also refunds.
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn given_alice_restarts_after_xmr_is_locked_abort_swap() {
|
async fn given_alice_restarts_after_xmr_is_locked_refund_swap() {
|
||||||
testutils::setup_test(|mut ctx| async move {
|
testutils::setup_test(|mut ctx| async move {
|
||||||
let alice_swap = ctx.new_swap_as_alice().await;
|
let alice_swap = ctx.new_swap_as_alice().await;
|
||||||
let bob_swap = ctx.new_swap_as_bob().await;
|
let bob_swap = ctx.new_swap_as_bob().await;
|
||||||
@ -20,6 +20,7 @@ async fn given_alice_restarts_after_xmr_is_locked_abort_swap() {
|
|||||||
|
|
||||||
// Alice does not act, Bob refunds
|
// Alice does not act, Bob refunds
|
||||||
let bob_state = bob_handle.await.unwrap();
|
let bob_state = bob_handle.await.unwrap();
|
||||||
|
ctx.assert_bob_refunded(bob_state.unwrap()).await;
|
||||||
|
|
||||||
// Once bob has finished Alice is restarted and refunds as well
|
// Once bob has finished Alice is restarted and refunds as well
|
||||||
let alice_swap = ctx.recover_alice_from_db().await;
|
let alice_swap = ctx.recover_alice_from_db().await;
|
||||||
@ -27,11 +28,6 @@ async fn given_alice_restarts_after_xmr_is_locked_abort_swap() {
|
|||||||
|
|
||||||
let alice_state = alice::run(alice_swap).await.unwrap();
|
let alice_state = alice::run(alice_swap).await.unwrap();
|
||||||
|
|
||||||
// TODO: The test passes like this, but the assertion should be done after Bob
|
|
||||||
// refunded, not at the end because this can cause side-effects!
|
|
||||||
// We have to properly wait for the refund tx's finality inside the assertion,
|
|
||||||
// which requires storing the refund_tx_id in the the state!
|
|
||||||
ctx.assert_bob_refunded(bob_state.unwrap()).await;
|
|
||||||
ctx.assert_alice_refunded(alice_state).await;
|
ctx.assert_alice_refunded(alice_state).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -62,6 +62,7 @@ struct BobParams {
|
|||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
alice_address: Multiaddr,
|
alice_address: Multiaddr,
|
||||||
alice_peer_id: PeerId,
|
alice_peer_id: PeerId,
|
||||||
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BobParams {
|
impl BobParams {
|
||||||
@ -74,6 +75,7 @@ impl BobParams {
|
|||||||
self.monero_wallet.clone(),
|
self.monero_wallet.clone(),
|
||||||
self.alice_address.clone(),
|
self.alice_address.clone(),
|
||||||
self.alice_peer_id.clone(),
|
self.alice_peer_id.clone(),
|
||||||
|
self.config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +114,7 @@ impl TestContext {
|
|||||||
let (swap, event_loop) = self
|
let (swap, event_loop) = self
|
||||||
.bob_params
|
.bob_params
|
||||||
.builder()
|
.builder()
|
||||||
.with_init_params(self.swap_amounts, Config::regtest())
|
.with_init_params(self.swap_amounts)
|
||||||
.build()
|
.build()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -357,6 +359,7 @@ where
|
|||||||
monero_wallet: bob_monero_wallet.clone(),
|
monero_wallet: bob_monero_wallet.clone(),
|
||||||
alice_address: alice_params.listen_address.clone(),
|
alice_address: alice_params.listen_address.clone(),
|
||||||
alice_peer_id: alice_params.peer_id().await,
|
alice_peer_id: alice_params.peer_id().await,
|
||||||
|
config,
|
||||||
};
|
};
|
||||||
|
|
||||||
let test = TestContext {
|
let test = TestContext {
|
||||||
|
Loading…
Reference in New Issue
Block a user