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:
Daniel Karzel 2021-01-21 12:06:12 +11:00 committed by GitHub
commit 9fa900dce8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 54 additions and 57 deletions

View File

@ -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?;

View File

@ -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,
)) ))

View File

@ -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(())
} }

View File

@ -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
} }

View File

@ -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;

View File

@ -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 {