Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(legacy_swap): check for existing maker/taker payment before timeout #2283

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 68 additions & 54 deletions mm2src/mm2_main/src/lp_swap/maker_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,32 +794,52 @@ impl MakerSwap {
}

async fn maker_payment(&self) -> Result<(Option<MakerSwapCommand>, Vec<MakerSwapEvent>), String> {
// Extract values from lock before async operations
let lock_duration = self.r().data.lock_duration;
let timeout = self.r().data.started_at + lock_duration / 3;
let now = now_sec();
if now > timeout {
return Ok((Some(MakerSwapCommand::Finish), vec![
MakerSwapEvent::MakerPaymentTransactionFailed(ERRL!("Timeout {} > {}", now, timeout).into()),
]));
}

let maker_payment_lock = self.r().data.maker_payment_lock;
let other_maker_coin_htlc_pub = self.r().other_maker_coin_htlc_pub;
let secret_hash = self.secret_hash();
let maker_coin_swap_contract_address = self.r().data.maker_coin_swap_contract_address.clone();
let unique_data = self.unique_swap_data();
let payment_instructions = self.r().payment_instructions.clone();
let transaction_f = self.maker_coin.check_if_my_payment_sent(CheckIfMyPaymentSentArgs {
time_lock: maker_payment_lock,
other_pub: &*other_maker_coin_htlc_pub,
secret_hash: secret_hash.as_slice(),
search_from_block: self.r().data.maker_coin_start_block,
swap_contract_address: &maker_coin_swap_contract_address,
swap_unique_data: &unique_data,
amount: &self.maker_amount,
payment_instructions: &payment_instructions,
});
let maker_coin_start_block = self.r().data.maker_coin_start_block;

// Look for previously sent maker payment in case of restart
let maybe_existing_payment = match self
.maker_coin
.check_if_my_payment_sent(CheckIfMyPaymentSentArgs {
time_lock: maker_payment_lock,
other_pub: &*other_maker_coin_htlc_pub,
secret_hash: secret_hash.as_slice(),
search_from_block: maker_coin_start_block,
swap_contract_address: &maker_coin_swap_contract_address,
swap_unique_data: &unique_data,
amount: &self.maker_amount,
payment_instructions: &payment_instructions,
})
.await
{
Ok(Some(tx)) => Some(tx),
Ok(None) => None,
Err(e) => {
return Ok((Some(MakerSwapCommand::Finish), vec![
MakerSwapEvent::MakerPaymentTransactionFailed(ERRL!("{}", e).into()),
]))
},
};

// Skip timeout check if payment was already sent
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comment wasn't easy to interpret given that we check the opposite.
what about:
// If the payment is not yet sent make sure we didn't miss the deadline for sending it.

if maybe_existing_payment.is_none() {
let timeout = self.r().data.started_at + lock_duration / 3;
let now = now_sec();
if now > timeout {
return Ok((Some(MakerSwapCommand::Finish), vec![
MakerSwapEvent::MakerPaymentTransactionFailed(ERRL!("Timeout {} > {}", now, timeout).into()),
]));
}
}

// Set up watcher reward if enabled
let wait_maker_payment_until = wait_for_maker_payment_conf_until(self.r().data.started_at, lock_duration);
let watcher_reward = if self.r().watcher_reward {
Comment on lines +841 to 844
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move this comment one line down.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also maybe we can move the wait_maker_payment_until above with the other .r() pre-calculations

match self
Expand All @@ -838,45 +858,39 @@ impl MakerSwap {
None
};

let transaction = match transaction_f.await {
Ok(res) => match res {
Some(tx) => tx,
None => {
let payment = self
.maker_coin
.send_maker_payment(SendPaymentArgs {
time_lock_duration: lock_duration,
time_lock: maker_payment_lock,
other_pubkey: &*other_maker_coin_htlc_pub,
secret_hash: secret_hash.as_slice(),
amount: self.maker_amount.clone(),
swap_contract_address: &maker_coin_swap_contract_address,
swap_unique_data: &unique_data,
payment_instructions: &payment_instructions,
watcher_reward,
wait_for_confirmation_until: wait_maker_payment_until,
})
.await;

match payment {
Ok(t) => t,
Err(err) => {
return Ok((Some(MakerSwapCommand::Finish), vec![
MakerSwapEvent::MakerPaymentTransactionFailed(
ERRL!("{}", err.get_plain_text_format()).into(),
),
]));
},
}
},
},
Err(e) => {
return Ok((Some(MakerSwapCommand::Finish), vec![
MakerSwapEvent::MakerPaymentTransactionFailed(ERRL!("{}", e).into()),
]))
// Use existing payment or create new one
let transaction = match maybe_existing_payment {
Some(tx) => tx,
None => {
match self
.maker_coin
.send_maker_payment(SendPaymentArgs {
time_lock_duration: lock_duration,
time_lock: maker_payment_lock,
other_pubkey: &*other_maker_coin_htlc_pub,
secret_hash: secret_hash.as_slice(),
amount: self.maker_amount.clone(),
swap_contract_address: &maker_coin_swap_contract_address,
swap_unique_data: &unique_data,
payment_instructions: &payment_instructions,
watcher_reward,
wait_for_confirmation_until: wait_maker_payment_until,
})
.await
{
Ok(t) => t,
Err(err) => {
return Ok((Some(MakerSwapCommand::Finish), vec![
MakerSwapEvent::MakerPaymentTransactionFailed(
ERRL!("{}", err.get_plain_text_format()).into(),
),
]));
},
}
},
};

// Build transaction identifier and prepare events
let tx_hash = transaction.tx_hash_as_bytes();
info!("{}: Maker payment tx {:02x}", MAKER_PAYMENT_SENT_LOG, tx_hash);

Expand Down
148 changes: 83 additions & 65 deletions mm2src/mm2_main/src/lp_swap/taker_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1514,34 +1514,53 @@ impl TakerSwap {
]));
}

let timeout = self.r().data.maker_payment_wait;
let now = now_sec();
if now > timeout {
return Ok((Some(TakerSwapCommand::Finish), vec![
TakerSwapEvent::TakerPaymentTransactionFailed(ERRL!("Timeout {} > {}", now, timeout).into()),
]));
}

// Extract values from the lock before async operations
let taker_payment_lock = self.r().data.taker_payment_lock;
let other_taker_coin_htlc_pub = self.r().other_taker_coin_htlc_pub;
let secret_hash = self.r().secret_hash.clone();
let taker_coin_start_block = self.r().data.taker_coin_start_block;
let taker_coin_swap_contract_address = self.r().data.taker_coin_swap_contract_address.clone();
let unique_data = self.unique_swap_data();
let taker_amount_decimal = self.taker_amount.to_decimal();
let payment_instructions = self.r().payment_instructions.clone();
let f = self.taker_coin.check_if_my_payment_sent(CheckIfMyPaymentSentArgs {
time_lock: taker_payment_lock,
other_pub: other_taker_coin_htlc_pub.as_slice(),
secret_hash: &secret_hash.0,
search_from_block: self.r().data.taker_coin_start_block,
swap_contract_address: &taker_coin_swap_contract_address,
swap_unique_data: &unique_data,
amount: &taker_amount_decimal,
payment_instructions: &payment_instructions,
});

// Look for previously sent taker payment in case of restart
let maybe_existing_payment = match self
.taker_coin
.check_if_my_payment_sent(CheckIfMyPaymentSentArgs {
time_lock: taker_payment_lock,
other_pub: other_taker_coin_htlc_pub.as_slice(),
secret_hash: &secret_hash.0,
search_from_block: taker_coin_start_block,
swap_contract_address: &taker_coin_swap_contract_address,
swap_unique_data: &unique_data,
amount: &taker_amount_decimal,
payment_instructions: &payment_instructions,
})
.await
{
Ok(Some(tx)) => Some(tx),
Ok(None) => None,
Err(e) => {
return Ok((Some(TakerSwapCommand::Finish), vec![
TakerSwapEvent::TakerPaymentTransactionFailed(ERRL!("{}", e).into()),
]))
},
};

// Skip timeout check if payment was already sent
if maybe_existing_payment.is_none() {
let timeout = self.r().data.maker_payment_wait;
let now = now_sec();
if now > timeout {
return Ok((Some(TakerSwapCommand::Finish), vec![
TakerSwapEvent::TakerPaymentTransactionFailed(ERRL!("Timeout {} > {}", now, timeout).into()),
]));
}
}

// Set up watcher reward if enabled
let reward_amount = self.r().reward_amount.clone();
let wait_until = taker_payment_lock;
let watcher_reward = if self.r().watcher_reward {
match self
.taker_coin
Expand All @@ -1550,7 +1569,7 @@ impl TakerSwap {
Some(self.taker_amount.clone().into()),
Some(self.maker_amount.clone().into()),
reward_amount,
wait_until,
taker_payment_lock,
)
.await
{
Expand All @@ -1567,59 +1586,55 @@ impl TakerSwap {
None
};

let transaction = match f.await {
Ok(res) => match res {
Some(tx) => tx,
None => {
let time_lock = match std::env::var("USE_TEST_LOCKTIME") {
Ok(_) => self.r().data.started_at,
Err(_) => taker_payment_lock,
};
let lock_duration = self.r().data.lock_duration;
let payment = self
.taker_coin
.send_taker_payment(SendPaymentArgs {
time_lock_duration: lock_duration,
time_lock,
other_pubkey: &*other_taker_coin_htlc_pub,
secret_hash: &secret_hash.0,
amount: taker_amount_decimal,
swap_contract_address: &taker_coin_swap_contract_address,
swap_unique_data: &unique_data,
payment_instructions: &payment_instructions,
watcher_reward,
wait_for_confirmation_until: taker_payment_lock,
})
.await;
// Use existing payment or create new one
let transaction = match maybe_existing_payment {
Some(tx) => tx,
None => {
let time_lock = match std::env::var("USE_TEST_LOCKTIME") {
Ok(_) => self.r().data.started_at,
Err(_) => taker_payment_lock,
};

match payment {
Ok(t) => t,
Err(err) => {
return Ok((Some(TakerSwapCommand::Finish), vec![
TakerSwapEvent::TakerPaymentTransactionFailed(
ERRL!("{}", err.get_plain_text_format()).into(),
),
]));
},
}
},
},
Err(e) => {
return Ok((Some(TakerSwapCommand::Finish), vec![
TakerSwapEvent::TakerPaymentTransactionFailed(ERRL!("{}", e).into()),
]))
let lock_duration = self.r().data.lock_duration;
match self
.taker_coin
.send_taker_payment(SendPaymentArgs {
time_lock_duration: lock_duration,
time_lock,
other_pubkey: &*other_taker_coin_htlc_pub,
secret_hash: &secret_hash.0,
amount: taker_amount_decimal,
swap_contract_address: &taker_coin_swap_contract_address,
swap_unique_data: &unique_data,
payment_instructions: &payment_instructions,
watcher_reward,
wait_for_confirmation_until: taker_payment_lock,
})
.await
{
Ok(t) => t,
Err(err) => {
return Ok((Some(TakerSwapCommand::Finish), vec![
TakerSwapEvent::TakerPaymentTransactionFailed(
ERRL!("{}", err.get_plain_text_format()).into(),
),
]))
},
}
},
};

// Create transaction identifier and prepare `TakerPaymentSent` success event
let tx_hash = transaction.tx_hash_as_bytes();
let tx_hex = BytesJson::from(transaction.tx_hex());
info!("Taker payment tx hash {:02x}", tx_hash);
let tx_ident = TransactionIdentifier {
tx_hex: tx_hex.clone(),
tx_hash,
};

let mut swap_events = vec![TakerSwapEvent::TakerPaymentSent(tx_ident)];

// Process watcher logic if enabled and supported by both coins
laruh marked this conversation as resolved.
Show resolved Hide resolved
if self.ctx.use_watchers()
&& self.taker_coin.is_supported_by_watchers()
&& self.maker_coin.is_supported_by_watchers()
Expand All @@ -1636,6 +1651,7 @@ impl TakerSwap {
Ok(_) => self.r().data.started_at,
Err(_) => self.r().data.taker_payment_lock,
};

let taker_payment_refund_preimage_fut = self.taker_coin.create_taker_payment_refund_preimage(
&transaction.tx_hex(),
time_lock,
Expand All @@ -1644,13 +1660,15 @@ impl TakerSwap {
&self.r().data.taker_coin_swap_contract_address,
&self.unique_swap_data(),
);
let payment_fut_pair = try_join(

match try_join(
maker_payment_spend_preimage_fut.compat(),
taker_payment_refund_preimage_fut.compat(),
);

match payment_fut_pair.await {
)
.await
{
Ok((maker_payment_spend, taker_payment_refund)) => {
// Prepare and broadcast watcher message
let watcher_data = self.create_watcher_data(
transaction.tx_hash_as_bytes().into_vec(),
maker_payment_spend.tx_hex(),
Expand Down
Loading