Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
34 changes: 24 additions & 10 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7229,34 +7229,48 @@ 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 doesn't have taker_payment_spend_preimage. Report the Bug!".to_string(),
))
}

/// Wrapper for [EthCoin::sign_and_broadcast_taker_payment_spend_impl]
/// Eth uses [EthCoin::sign_and_broadcast_taker_payment_spend_without_preimage] instead, as it skips taker_payment_spend_preimage
async fn sign_and_broadcast_taker_payment_spend(
&self,
_preimage: &TxPreimageWithSig<Self>,
_gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
_secret: &[u8],
_swap_unique_data: &[u8],
) -> Result<Self::Tx, TransactionErr> {
Err(TransactionErr::Plain(ERRL!(
"EVM-based coin doesn't have taker_payment_spend_preimage. Report the Bug!"
)))
}

/// Wrapper for [EthCoin::sign_and_broadcast_taker_payment_spend_impl]
async fn sign_and_broadcast_taker_payment_spend_without_preimage(
&self,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
_swap_unique_data: &[u8],
) -> Result<Self::Tx, TransactionErr> {
self.sign_and_broadcast_taker_payment_spend_impl(gen_args, secret).await
}
Expand Down
8 changes: 8 additions & 0 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,8 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn
async fn refund_combined_taker_payment(&self, args: RefundTakerPaymentArgs<'_>)
-> Result<Self::Tx, TransactionErr>;

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 @@ -2054,6 +2056,12 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn
swap_unique_data: &[u8],
) -> Result<Self::Tx, TransactionErr>;

async fn sign_and_broadcast_taker_payment_spend_without_preimage(
&self,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
) -> Result<Self::Tx, TransactionErr>;
Comment thread
onur-ozkan marked this conversation as resolved.
Outdated

/// Wait until taker payment spend transaction is found on-chain
async fn find_taker_payment_spend_tx(
&self,
Expand Down
8 changes: 8 additions & 0 deletions mm2src/coins/test_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,14 @@ impl TakerCoinSwapOpsV2 for TestCoin {
unimplemented!()
}

async fn sign_and_broadcast_taker_payment_spend_without_preimage(
&self,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
) -> Result<Self::Tx, TransactionErr> {
unreachable!()
}

async fn find_taker_payment_spend_tx(
&self,
taker_payment: &Self::Tx,
Expand Down
10 changes: 10 additions & 0 deletions mm2src/coins/utxo/utxo_standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,16 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin {
utxo_common::sign_and_broadcast_taker_payment_spend(self, preimage, gen_args, secret, &htlc_keypair).await
}

async fn sign_and_broadcast_taker_payment_spend_without_preimage(
&self,
_gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
_secret: &[u8],
) -> Result<Self::Tx, TransactionErr> {
Err(TransactionErr::Plain(ERRL!(
"UTXO coin must not skip taker_payment_spend_preimage. Report the Bug!"
)))
}

async fn find_taker_payment_spend_tx(
&self,
taker_payment: &Self::Tx,
Expand Down
186 changes: 178 additions & 8 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 taker payment spend preimage validation skipped.
TakerPaymentReceivedAndPreimageValidationSkipped {
Comment thread
onur-ozkan marked this conversation as resolved.
Outdated
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 @@ -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_without_preimage(&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