-
Notifications
You must be signed in to change notification settings - Fork 94
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
feat(trading-proto-upgrade): fees fixes among other things #2109
base: dev
Are you sure you want to change the base?
Conversation
@mariocynicys please add label for if this is under review or in progress. |
@mariocynicys please merge dev to feature branch as it contains build fixes
|
fdd2451
to
a9dbd14
Compare
@@ -822,6 +824,8 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp | |||
}, | |||
}; | |||
|
|||
// FIXME: I am lost here, is this the fee for funding spend or taker payment spend? | |||
// if it's the former, where is the fee for the the taker payment spend? | |||
let taker_payment_spend_trade_fee = match state_machine.taker_coin.get_receiver_trade_fee(stage).compat().await | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the fee paid by the receiver of a payment, it was an htlc spend fee in the legacy swap protocol. For v2, it should be 0 for maker like we agreed before (but maybe not in this PR, and we make it the taker payment spend fee for now), and for taker it should be the fee needed to spend the maker payment which is the same as the legacy protocol. Is this clear enough?
komodo-defi-framework/mm2src/coins/lp_coins.rs
Lines 3102 to 3103 in 3c888aa
/// Get fee to be paid by receiver per whole swap and check if the wallet has sufficient balance to pay the fee. | |
fn get_receiver_trade_fee(&self, stage: FeeApproxStage) -> TradePreimageFut<TradeFee>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and for taker it should be the fee needed to spend the maker payment which is the same as the legacy protocol.
As discussed in DM with @mariocynicys, this should be different than legacy protocol too since the redeem script is a bit different due to the immediate refund path. I will make this in progress again @mariocynicys
Actually should be called funding spend fee or taker payment fee, but we are just following the current code naming at the moment. The negotiation goes as follows: 1- Taker and maker each decide how much this fee should be at the beginning of the swap. 2- The taker will send the maker their proposed fee during negotiation, if the maker deems the fee as low enough (less than 90% of the maker's own calculated fee), they will refuse to trade before the trade starts. Otherwise, they will continue and use this fee for validation later. 3- The maker will validate that the taker has accounted for this fee in the funding transaction. 4- The taker will validate that the taker payment preimage (generated by the maker) uses the negotiated fee or greater (greater number will deduct from the maker's trading volume anyway). If not, the taker will stop the trade since the maker is clearly stealing funds that isn't his which might affect the completion of the swap if the fee is low.
…o u64 The big decimal might be a fraction less than zero and gets converted to zero when sent over the wire (because we send the fee as u64). Scale it properly before sending it.
noticed this in the integration tests, the fee might be a fraction so clamping it to one yields a fee higher than the actually one, thus fails the ratio check
use a signature-size dummy date instead
taker payment spend is actually taker payment here, next commit should fix the naming inconsistencies
a9dbd14
to
481ba3e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the PR! here are some small notes from my side
… avoid weird precision
ps @mariocynicys this pr started to have conflicts |
ps @mariocynicys lint check fails |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the enhancements! Next review iteration!
mm2src/coins/lp_coins.rs
Outdated
/// Get the fee to be paid for the processing of the maker payment transaction. | ||
async fn get_maker_payment_fee(&self, stage: &FeeApproxStage) -> TradePreimageResult<TradeFee>; | ||
|
||
/// Get the fee to be paid for the processing of the maker payment spend transaction aka the claimation transaction. | ||
async fn get_maker_payment_spend_fee(&self, stage: &FeeApproxStage) -> TradePreimageResult<TradeFee>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we need the get_maker_payment_spend_fee
function in the future? I understand that get_maker_payment_fee
will be used so that the taker can cover this fee on the funding transaction, but I don't see a need for get_maker_payment_spend_fee
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is just to calculate fee from the taker side in a right way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be used here:
let maker_payment_spend_fee = match state_machine.maker_coin.get_receiver_trade_fee(stage).compat().await { |
In place of get_receiver_trade_fee
, but I am trying to figure out why didn't i use it O_O
mm2src/coins/utxo/utxo_common.rs
Outdated
// expected fee knowing that the maker will always accept incurring the difference. | ||
calculated_fee.max(taker_instructed_fee) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not always use the calculated_fee
? if it's less than or equal to the taker_instructed_fee
makers can get some additional amount which is better than giving it to the miners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We actually discussed this before xD
IIRC, the point was to not compromise the swap success if the maker wants to greedily cut on the fees. to stop that, any additional funds that were designated to be used as fees should only be used as fees otherwise the swap will fail.
mm2src/coins/utxo/utxo_common.rs
Outdated
let instructed_fee = | ||
sat_from_big_decimal(&gen_args.taker_payment_fee, coin.as_ref().decimals).mm_err(|e| e.into())?; | ||
|
||
let fee_div = expected_fee as f64 / actual_fee as f64; | ||
|
||
if !(0.9..=1.1).contains(&fee_div) { | ||
if actual_fee < instructed_fee { | ||
return MmError::err(ValidateTakerFundingSpendPreimageError::UnexpectedPreimageFee(format!( | ||
"Too large difference between expected {} and actual {} fees", | ||
expected_fee, actual_fee | ||
"A fee of {} was used, less than the agreed upon fee of {}", | ||
actual_fee, instructed_fee |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh, I see why you used the max of calculated_fee
and taker_instructed_fee
above. I don't think this validation is actually needed at all, this is done from the taker side, and once the swap starts any additional fee or reduced fee will either reduce the maker amount or benefit him so the maker can choose any fee on his side when generating the preimage and the taker doesn't have to validate it. Also for the below, maker negotiates a fee at the start / negotiation phase and taker either approves it or not, so there is no room for abuse.
// FIXME: Possible abuse vector is that the taker can always send the fee to be just above 90% of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
once the swap starts any additional fee or reduced fee will either reduce the maker amount or benefit him so the maker can choose any fee on his side when generating the preimage and the taker doesn't have to validate it
I guess we have to lookout for the refund case though, since if maker chooses a very high fee and doesn't complete the swap, taker will lose funds on refund.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also for the below, maker negotiates a fee at the start / negotiation phase and taker either approves it or not, so there is no room for abuse.
What about removing the fee pumping logic (so we won't calculate calculated_fee
and won't use the max
) and use the taker_instructed_fee
right away. Re-thinking about it, the max
-ing might not make that much of a difference (most of the time), and this would also stop the attack vector where the maker sets a high fee just to hit the taker on his refund.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the taker_instructed_fee
is the right idea IMHO but the problem is fees spikes as always. There are 2 solutions:
-
Either just use
taker_instructed_fee
, taker will want to optimize for this since they wouldn't want to overpay and they will want a fast swap at the same time so there would be no attack vector and if the taker payment transaction takes too much time to confirm due to a fee spike there is always the option of immediate refund. We should allow maker paying less for fees if possible since it will benefit either the maker on success or taker on refund, so taker will validate the fee to be eithertaker_instructed_fee
or less. -
The other solution is more complicated and it's a borrow from lightning where the maker and taker renegotiate the fee if there is a fee spike but this complicates things a lot.
Let's use option one for now and wait until this is tested in a real environment. For fee spikes we can allow the taker to use a bit higher fee from his side (10% higher or we can set it more accurately once we see real data) if needed in the future.
also changed to todo
revising the commit history, looks like this was deliberate
c68b87a
to
28fc442
Compare
always rename discuss markers to fixmes since i forget about them xD
there is no calculatable tx_size for other tx (non-htlc) (like funding and maker payment txs). their fees depend really on the utxos we have and whether they are segwit or not. used get_sender_trade_fee for now, though I think this method needs reviewing since its doc comment doesn't make a lot of sense
b16f050
to
bc32198
Compare
ps @mariocynicys this pr started to have conflicts |
The main feat here is
taker payment spend fee
negotiation (I guess we will refactor namings in the swaps v2 code and rename this totaker payment fee
/funding spend fee
):The negotiation goes as follows:
if the maker deems the fee as low enough (less than 90% of the maker's own calculated fee),
they will refuse to trade before the trade starts. Otherwise, they
will continue and use this fee for validation later.
the funding transaction.
uses the negotiated fee or greater (greater number will deduct from the maker's trading volume anyway).
If not, the taker will stop the trade since the maker is clearly stealing funds that isn't his
which might affect the completion of the swap if the fee is low.
Proto v2 issue pool:
accept_only_from
our order peer.u8
fork idrecover_funds_for_swap
rpc for swaps v2test_taker_completes_swap_after_restart
is flakyFixmes & discuss comments are intended to be covered in the PR.