Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
23 changes: 13 additions & 10 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7229,31 +7229,34 @@ impl TakerCoinSwapOpsV2 for EthCoin {
self.refund_taker_payment_with_timelock_impl(args).await
}

/// Eth doesnt have preimages
fn skip_taker_payment_spend_preimage(&self) -> bool { true }

/// Eth skips taker_payment_spend_preimage, as it doesnt need it
async fn gen_taker_payment_spend_preimage(
&self,
args: &GenTakerPaymentSpendArgs<'_, Self>,
_args: &GenTakerPaymentSpendArgs<'_, Self>,
_swap_unique_data: &[u8],
) -> GenPreimageResult<Self> {
Ok(TxPreimageWithSig {
preimage: args.taker_tx.clone(),
signature: args.taker_tx.signature(),
})
MmError::err(TxGenError::Other(
"EVM-based coin doesn't have taker_payment_spend_preimage. Report the Bug!".to_string(),
))
}

/// Eth doesnt have preimages
/// Eth skips taker_payment_spend_preimage, as it doesnt need it
async fn validate_taker_payment_spend_preimage(
&self,
_gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
_preimage: &TxPreimageWithSig<Self>,
) -> ValidateTakerPaymentSpendPreimageResult {
Ok(())
MmError::err(ValidateTakerPaymentSpendPreimageError::InvalidPreimage(
"EVM-based coin skips taker_payment_spend_preimage validation. Report the Bug!".to_string(),
))
}

/// Wrapper for [EthCoin::sign_and_broadcast_taker_payment_spend_impl]
/// Eth doesnt have preimages
async fn sign_and_broadcast_taker_payment_spend(
&self,
_preimage: &TxPreimageWithSig<Self>,
_preimage: Option<&TxPreimageWithSig<Self>>,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
_swap_unique_data: &[u8],
Expand Down
7 changes: 6 additions & 1 deletion mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,11 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn
async fn refund_combined_taker_payment(&self, args: RefundTakerPaymentArgs<'_>)
-> Result<Self::Tx, TransactionErr>;

/// A bool flag that allows skipping the generation and P2P message broadcasting of `TakerPaymentSpendPreimage` on the Taker side,
/// as well as its reception and validation on the Maker side.
/// This is typically used for coins that rely on smart contracts.
fn skip_taker_payment_spend_preimage(&self) -> bool { false }
Comment thread
onur-ozkan marked this conversation as resolved.

/// Generates and signs taker payment spend preimage. The preimage and signature should be
/// shared with maker to proceed with protocol execution.
async fn gen_taker_payment_spend_preimage(
Expand All @@ -2048,7 +2053,7 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn
/// Sign and broadcast taker payment spend on maker's side.
async fn sign_and_broadcast_taker_payment_spend(
&self,
preimage: &TxPreimageWithSig<Self>,
preimage: Option<&TxPreimageWithSig<Self>>,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
swap_unique_data: &[u8],
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/test_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ impl TakerCoinSwapOpsV2 for TestCoin {

async fn sign_and_broadcast_taker_payment_spend(
&self,
preimage: &TxPreimageWithSig<Self>,
preimage: Option<&TxPreimageWithSig<Self>>,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
swap_unique_data: &[u8],
Expand Down
4 changes: 3 additions & 1 deletion mm2src/coins/utxo/utxo_standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,11 +798,13 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin {

async fn sign_and_broadcast_taker_payment_spend(
&self,
preimage: &TxPreimageWithSig<Self>,
preimage: Option<&TxPreimageWithSig<Self>>,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
swap_unique_data: &[u8],
) -> Result<Self::Tx, TransactionErr> {
let preimage = preimage
.ok_or_else(|| TransactionErr::Plain(ERRL!("taker_payment_spend_preimage must be Some for UTXO coin")))?;
let htlc_keypair = self.derive_htlc_key_pair(swap_unique_data);
utxo_common::sign_and_broadcast_taker_payment_spend(self, preimage, gen_args, secret, &htlc_keypair).await
}
Expand Down
188 changes: 179 additions & 9 deletions mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ pub enum MakerSwapEvent {
maker_payment: TransactionIdentifier,
taker_payment: TransactionIdentifier,
},
/// `Taker payment` has been received and preimage of `taker payment spend` has been skipped.
Comment thread
onur-ozkan marked this conversation as resolved.
Outdated
TakerPaymentReceivedAndPreimageValidationSkipped {
maker_coin_start_block: u64,
taker_coin_start_block: u64,
negotiation_data: StoredNegotiationData,
maker_payment: TransactionIdentifier,
taker_payment: TransactionIdentifier,
},
/// Maker successfully spent taker's payment.
TakerPaymentSpent {
maker_coin_start_block: u64,
Expand Down Expand Up @@ -602,6 +610,29 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
&recreate_ctx.taker_coin,
)?,
}),
MakerSwapEvent::TakerPaymentReceivedAndPreimageValidationSkipped {
maker_coin_start_block,
taker_coin_start_block,
negotiation_data,
maker_payment,
taker_payment,
} => Box::new(TakerPaymentReceivedAndPreimageValidationSkipped {
maker_coin_start_block,
taker_coin_start_block,
maker_payment: recreate_ctx
.maker_coin
.parse_tx(&maker_payment.tx_hex.0)
.map_err(|e| SwapRecreateError::FailedToParseData(e.to_string()))?,
taker_payment: recreate_ctx
.taker_coin
.parse_tx(&taker_payment.tx_hex.0)
.map_err(|e| SwapRecreateError::FailedToParseData(e.to_string()))?,
negotiation_data: NegotiationData::from_stored_data(
negotiation_data,
&recreate_ctx.maker_coin,
&recreate_ctx.taker_coin,
)?,
}),
MakerSwapEvent::TakerPaymentSpent {
maker_coin_start_block,
taker_coin_start_block,
Expand Down Expand Up @@ -737,6 +768,7 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
| MakerSwapEvent::MakerPaymentRefundRequired { .. }
| MakerSwapEvent::MakerPaymentRefunded { .. }
| MakerSwapEvent::TakerPaymentReceived { .. }
| MakerSwapEvent::TakerPaymentReceivedAndPreimageValidationSkipped { .. }
| MakerSwapEvent::TakerPaymentSpent { .. }
| MakerSwapEvent::Aborted { .. }
| MakerSwapEvent::Completed => (),
Expand Down Expand Up @@ -787,6 +819,7 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
| MakerSwapEvent::MakerPaymentRefundRequired { .. }
| MakerSwapEvent::MakerPaymentRefunded { .. }
| MakerSwapEvent::TakerPaymentReceived { .. }
| MakerSwapEvent::TakerPaymentReceivedAndPreimageValidationSkipped { .. }
| MakerSwapEvent::TakerPaymentSpent { .. }
| MakerSwapEvent::Aborted { .. }
| MakerSwapEvent::Completed => (),
Expand Down Expand Up @@ -1344,14 +1377,25 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
.await;
match search_result {
Ok(Some(FundingTxSpend::TransferredToTakerPayment(taker_payment))) => {
let next_state = TakerPaymentReceived {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
maker_payment: self.maker_payment,
taker_payment,
negotiation_data: self.negotiation_data,
};
break Self::change_state(next_state, state_machine).await;
if state_machine.taker_coin.skip_taker_payment_spend_preimage() {
let next_state = TakerPaymentReceivedAndPreimageValidationSkipped {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
maker_payment: self.maker_payment,
taker_payment,
negotiation_data: self.negotiation_data,
};
break Self::change_state(next_state, state_machine).await;
} else {
let next_state = TakerPaymentReceived {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
maker_payment: self.maker_payment,
taker_payment,
negotiation_data: self.negotiation_data,
};
break Self::change_state(next_state, state_machine).await;
}
},
// it's not really possible as taker's funding time lock is 3 * lock_duration, though we have to
// handle this case anyway
Expand Down Expand Up @@ -1467,6 +1511,11 @@ impl<MakerCoin: ParseCoinAssocTypes, TakerCoin: TakerCoinSwapOpsV2>
TransitionFrom<TakerPaymentReceived<MakerCoin, TakerCoin>> for MakerPaymentRefundRequired<MakerCoin, TakerCoin>
{
}
impl<MakerCoin: ParseCoinAssocTypes, TakerCoin: TakerCoinSwapOpsV2>
TransitionFrom<TakerPaymentReceivedAndPreimageValidationSkipped<MakerCoin, TakerCoin>>
for MakerPaymentRefundRequired<MakerCoin, TakerCoin>
{
}
impl<MakerCoin: ParseCoinAssocTypes, TakerCoin: ParseCoinAssocTypes>
TransitionFrom<TakerPaymentSpent<MakerCoin, TakerCoin>> for MakerPaymentRefundRequired<MakerCoin, TakerCoin>
{
Expand Down Expand Up @@ -1710,7 +1759,7 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
let taker_payment_spend = match state_machine
.taker_coin
.sign_and_broadcast_taker_payment_spend(
&tx_preimage,
Some(&tx_preimage),
&gen_args,
state_machine.secret.as_slice(),
&unique_data,
Expand Down Expand Up @@ -1769,6 +1818,121 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
}
}

struct TakerPaymentReceivedAndPreimageValidationSkipped<MakerCoin: ParseCoinAssocTypes, TakerCoin: ParseCoinAssocTypes>
{
maker_coin_start_block: u64,
taker_coin_start_block: u64,
maker_payment: MakerCoin::Tx,
taker_payment: TakerCoin::Tx,
negotiation_data: NegotiationData<MakerCoin, TakerCoin>,
}

impl<MakerCoin: ParseCoinAssocTypes, TakerCoin: ParseCoinAssocTypes>
TransitionFrom<MakerPaymentSentFundingSpendGenerated<MakerCoin, TakerCoin>>
for TakerPaymentReceivedAndPreimageValidationSkipped<MakerCoin, TakerCoin>
{
}

#[async_trait]
impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOpsV2> State
for TakerPaymentReceivedAndPreimageValidationSkipped<MakerCoin, TakerCoin>
{
type StateMachine = MakerSwapStateMachine<MakerCoin, TakerCoin>;

async fn on_changed(self: Box<Self>, state_machine: &mut Self::StateMachine) -> StateResult<Self::StateMachine> {
info!("Skipped validation of the taker payment spend preimage because the taker's coin does not require it, and the taker did not generate and send it.");

let input = ConfirmPaymentInput {
payment_tx: self.taker_payment.tx_hex(),
confirmations: state_machine.conf_settings.taker_coin_confs,
requires_nota: state_machine.conf_settings.taker_coin_nota,
wait_until: state_machine.taker_payment_conf_timeout(),
check_every: 10,
};

if let Err(e) = state_machine.taker_coin.wait_for_confirmations(input).compat().await {
let next_state = MakerPaymentRefundRequired {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
negotiation_data: self.negotiation_data,
maker_payment: self.maker_payment,
reason: MakerPaymentRefundReason::TakerPaymentNotConfirmedInTime(e),
};
return Self::change_state(next_state, state_machine).await;
}

let unique_data = state_machine.unique_data();

let gen_args = GenTakerPaymentSpendArgs {
taker_tx: &self.taker_payment,
time_lock: self.negotiation_data.taker_payment_locktime,
maker_secret_hash: &state_machine.secret_hash(),
maker_pub: &state_machine.taker_coin.derive_htlc_pubkey_v2(&unique_data),
maker_address: &state_machine.taker_coin.my_addr().await,
taker_pub: &self.negotiation_data.taker_coin_htlc_pub_from_taker,
dex_fee: &state_machine.dex_fee,
premium_amount: Default::default(),
trading_amount: state_machine.taker_volume.to_decimal(),
dex_fee_pub: &DEX_FEE_ADDR_RAW_PUBKEY,
};

let taker_payment_spend = match state_machine
.taker_coin
.sign_and_broadcast_taker_payment_spend(None, &gen_args, state_machine.secret.as_slice(), &[])
.await
{
Ok(tx) => tx,
Err(e) => {
let next_state = MakerPaymentRefundRequired {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
negotiation_data: self.negotiation_data,
maker_payment: self.maker_payment,
reason: MakerPaymentRefundReason::TakerPaymentSpendBroadcastFailed(format!("{:?}", e)),
};
return Self::change_state(next_state, state_machine).await;
},
};
info!(
"Spent taker payment {} tx {:02x} during swap {}",
state_machine.taker_coin.ticker(),
taker_payment_spend.tx_hash_as_bytes(),
state_machine.uuid
);
let next_state = TakerPaymentSpent {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
maker_payment: self.maker_payment,
taker_payment: self.taker_payment,
taker_payment_spend,
negotiation_data: self.negotiation_data,
};
Self::change_state(next_state, state_machine).await
}
}

impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOpsV2> StorableState
for TakerPaymentReceivedAndPreimageValidationSkipped<MakerCoin, TakerCoin>
{
type StateMachine = MakerSwapStateMachine<MakerCoin, TakerCoin>;

fn get_event(&self) -> MakerSwapEvent {
MakerSwapEvent::TakerPaymentReceivedAndPreimageValidationSkipped {
maker_coin_start_block: self.maker_coin_start_block,
taker_coin_start_block: self.taker_coin_start_block,
negotiation_data: self.negotiation_data.to_stored_data(),
maker_payment: TransactionIdentifier {
tx_hex: self.maker_payment.tx_hex().into(),
tx_hash: self.maker_payment.tx_hash_as_bytes(),
},
taker_payment: TransactionIdentifier {
tx_hex: self.taker_payment.tx_hex().into(),
tx_hash: self.taker_payment.tx_hash_as_bytes(),
},
}
}
}

struct TakerPaymentSpent<MakerCoin: ParseCoinAssocTypes, TakerCoin: ParseCoinAssocTypes> {
maker_coin_start_block: u64,
taker_coin_start_block: u64,
Expand All @@ -1783,6 +1947,12 @@ impl<MakerCoin: ParseCoinAssocTypes, TakerCoin: ParseCoinAssocTypes>
{
}

impl<MakerCoin: ParseCoinAssocTypes, TakerCoin: ParseCoinAssocTypes>
TransitionFrom<TakerPaymentReceivedAndPreimageValidationSkipped<MakerCoin, TakerCoin>>
for TakerPaymentSpent<MakerCoin, TakerCoin>
{
}

#[async_trait]
impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOpsV2> State
for TakerPaymentSpent<MakerCoin, TakerCoin>
Expand Down
Loading