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

feat(utxo-swap): add utxo burn output for non-kmd taker fee #2112

Open
wants to merge 61 commits into
base: dev
Choose a base branch
from

Conversation

dimxy
Copy link
Collaborator

@dimxy dimxy commented May 6, 2024

Adds a burn output sending 25% of the taker utxo DEX fee to a dedicated pre-burn address. Funds collected on the pre-burn address will be traded for KMD to burn them (thus additionally burning KMD supply).
This PR partially closes #2010

In this PR:

  • split dex fee for non-kmd utxo coins (apart from zcoin) and added an output to the pre-burn account
  • old-style non-split dex fee non-kmd txns are also allowed in validation code (until all taker nodes upgraded) - this feature is removed in favour of version-based burning activation
  • refactored dex fee pubkey in params: instead of passing dex fee and burn pubkeys in function params new methods dex_pubkey() and burn_pubkey() were added to the SwapOps trait
  • add version to maker/taker negotiation message and activate burning for the new version
  • mocktopus was made optional dependency and activated only for development builds (as its doc suggests).

NOTE: As mocktopus now is marked 'optional = true' in coins Cargo.toml and activated from the mm2_main crate by adding features = ["mocktopus"] in [dev-dependencies] section, you also need to mark your mockable code, called from other crates, this way: #[cfg_attr(feature = "mocktopus", mockable)], otherwise mocks won't work (see samples in code)

TODO:

  • fix burn pubkey and burn zaddr
  • Add private burn output for zcoin
  • disable non-split non-kmd dex fee (no burn output) validation when all taker nodes upgrade to new dex fee splitting
  • do not pay dex fee if taker is the dex pubkey (for non-privacy utxo)

* dev:
  docs(README): remove outdated information from the README (#2097)
  fix(sia): fix sia compilation after hd wallet PR merge (#2103)
  feat(hd_wallet): utxo and evm hd wallet and trezor (#1962)
  feat(sia): initial Sia integration (#2086)
  fix(BCH): deserialize BCH header that uses KAWPOW version correctly (#2099)
  fix(eth_tests): remove ETH_DEV_NODE from tests (#2101)
@dimxy dimxy changed the title Add utxo burn output for non-kmd taker fee feat(utxo-swap): add utxo burn output for non-kmd taker fee May 6, 2024
@dimxy dimxy added in progress Changes will be made from the author under review and removed in progress Changes will be made from the author labels May 6, 2024
* dev:
  feat(tendermint): pubkey-only activation and unsigned tx (#2088)
  fix(tests): set txfee for some tbtc tests (#2116)
  fix(eth): remove my_address from sign_and_send_transaction_with_keypair (#2115)
  fix(utxo-swap): apply events occurred while taker down (#2114)
  refactor(memory): memory usage improvements (#2098)
  feat(app-dir): implement root application dir `.kdf` (#2102)
  fix tendermint fee calculation (#2106)
  update dockerfile (#2104)
* dev:
  feat(ETH): eip1559 gas fee estimator and rpcs (#2051)
  fix(p2pk-tests): fix p2pk tests post merge (#2119)
  fix(p2pk): show and spend P2PK balance (#2053)
  fix(swap): use tmp file for swap and order files (#2118)
@dimxy dimxy added in progress Changes will be made from the author and removed under review labels Jun 9, 2024
fix zhtlc send and spend tests
add should_burn_dex_fee method to indicate which coin has burn output
* dev:
  fix(indexeddb): window usage in worker env (#2131)
  feat(tx-history): handle encoded transaction values (#2133)
  fix(core): tendermint withdraws on hd accounts (#2130)
  fix(core): improve validation rules for table names (#2123)
  fix(test): improve log wait condition to fix taker restart test (#2125)
…er test

add timeout in wait for fee in qrc20 docker test
… docker test failed due to different dex fee values if trait default impl was used)
Copy link
Member

@laruh laruh left a comment

Choose a reason for hiding this comment

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

Great progress! Here is the first review iteration

mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
use same ovk for dex fee and burn outputs
add sanity check for dex_fee in calc_burn_amount_for_op_return
@dimxy
Copy link
Collaborator Author

dimxy commented Aug 14, 2024

In the recent 16 commits I added a 'version' field to negotiation protocol. This is done in the current PR to activate burning of non-kmd part of dex fee (a new tx output to the pre-burn account) by node's supported protocol version.
However I guess version is a useful feature for other future protocol changes (so this PR closes this issue #640 too).

How negotiation with version is working, also to provide compatibility with old nodes (see MakerSwap::negotiate and TakerSwap::negotiate fn):

Now new Maker sends both versioned and old non-versioned negotiation message to the Taker.
If Taker is new:

  • New Taker receives the versioned message first and responses with the versioned negotiation reply (and ignores the old negotiation message). I estimate this ordering (versioned message is sent first, non-versioned is second) should work.
  • New Maker receives the versioned reply and both new Taker and new Maker now know version of each other (now it is 1) and use the new DexFee (with the burning output).

If Taker is old:

  • it ignores the versioned message and responses with old reply.
  • New Maker receives the old reply and now knows the Taker is old (its version is 0) - both use old DexFee
    Also, if Maker is old and Taker is new - new Taker would respond with old non-versioned reply and so on.

When all nodes upgrade we can remove old non-versioned negotiation protocol support.

I added two cargo features to allow to emulate old node behaviour (sending always non-versioned negotiation request or reply) for use with docker tests, like test_trade_base_rel_mycoin_mycoin1_coins

I think we should add the similar version field to the TPU protocol too.

Copy link
Collaborator

@shamardy shamardy left a comment

Choose a reason for hiding this comment

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

Amazing work! Next review iteration!

The below tests are failing and you need to fix them

test utxo::utxo_tests::test_validate_old_fee_tx ... FAILED
test lp_swap::recreate_swap_data::tests::test_recreate_maker_swap ... FAILED
test lp_swap::recreate_swap_data::tests::test_recreate_maker_swap_maker_payment_wait_confirm_failed ... FAILED
test lp_swap::recreate_swap_data::tests::test_recreate_taker_swap ... FAILED
test lp_swap::recreate_swap_data::tests::test_recreate_taker_swap_taker_payment_wait_confirm_failed ... FAILED

Please also add To Test to the opening PR comment #2112 (comment) for QA to know what to test after the PR is merged.

P.S. we still need to add protocol version to v2 swaps, and burning for EVM TPU. We can add them in another PR but these are required for TPU release c.c. @laruh

@@ -230,6 +233,7 @@ pub mod coin_balance;
use coin_balance::{AddressBalanceStatus, HDAddressBalance, HDWalletBalanceOps};

pub mod lp_price;
pub mod swap_features;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this be part of mm2src/mm2_main/src/lp_swap/ instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did like that initially but had to put it into the coins mod (mm2_main depends on it) as the coins code need to use it too

Comment on lines +328 to +337
/// Default swap protocol version before version field added to NegotiationDataMsg
pub const LEGACY_PROTOCOL_VERSION: u16 = 0;

/// Current swap protocol version
pub const SWAP_PROTOCOL_VERSION: u16 = 1;

/// Minimal supported swap protocol version implemented by remote peer
pub const MIN_SWAP_PROTOCOL_VERSION: u16 = LEGACY_PROTOCOL_VERSION;

// TODO: add version field to the SWAP V2 negotiation protocol
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same thing for these constants.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same as above

/// Current swap protocol version
pub const SWAP_PROTOCOL_VERSION: u16 = 1;

/// Minimal supported swap protocol version implemented by remote peer
Copy link
Collaborator

Choose a reason for hiding this comment

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

The version is not implemented by the remote peer, how about
/// Minimal swap protocol version required by remote peer to swap with local peer running SWAP_PROTOCOL_VERSION

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I meant with with const MIN_SWAP_PROTOCOL_VERSION we will reject too old nodes (with their version less than MIN_SWAP_PROTOCOL_VERSION)

burn_amount,
burn_destination: DexFeeBurnDestination::KmdOpReturn,
};
} else if taker_coin.should_burn_dex_fee() && burn_active {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is interesting since we might add more coins with should_burn_dex_fee being true in the future so the check for burn_active and should_burn_dex_fee will not be enough for backward compatibility and we will add another bool, I guess we will make should_burn_dex_fee result depend on swap protocol version so do we need to pass burn_active or swap protocol version instead? I guess this can be handled in the future anyways even the swap feature name

can be changed in the future as the naming is local and not part of the p2p messages.

Comment on lines 3 to 18
#[derive(PartialEq)]
pub enum SwapFeature {
SendToPreBurnAccount,
}

impl SwapFeature {
// add new features to activate
const SWAP_FEATURE_ACTIVATION: &[(u16, SwapFeature)] = &[(1, SwapFeature::SendToPreBurnAccount)];

pub fn is_active(feature: SwapFeature, version: u16) -> bool {
if let Some(found) = Self::SWAP_FEATURE_ACTIVATION.iter().find(|fv| fv.1 == feature) {
return version >= found.0;
}
false
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

#[derive(PartialEq)]
pub enum SwapFeature {
    SendToPreBurnAccount = 1,
}

impl SwapFeature {
    pub fn is_active(feature: SwapFeature, version: u16) -> bool { version >= feature as u16 }
}

Much simpler this way

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But we may have multiple features activated with the same version
(actually I did part similar to one in zebra codebase)

Comment on lines 1895 to 1899
/// Do not add refund fee to calculate fee needed only to make a successful swap
pub(crate) const NO_REFUND_FEE: bool = false;

/// Sending part of dex fee to the pre-burn account is active
pub const PRE_BURN_ACCOUNT_ACTIVE: bool = true;
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 all constants to the top.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in 74d8ea3

if remote_version < MIN_SWAP_PROTOCOL_VERSION {
self.broadcast_negotiated_false();
return Ok((Some(MakerSwapCommand::Finish), vec![MakerSwapEvent::NegotiateFailed(
ERRL!("Taker protocol version {} too old", remote_version).into(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be good to also show the current supported version in this error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in 1def60d

#[allow(clippy::absurd_extreme_comparisons)]
if remote_version < MIN_SWAP_PROTOCOL_VERSION {
return Ok((Some(TakerSwapCommand::Finish), vec![TakerSwapEvent::NegotiateFailed(
ERRL!("Maker protocol version {} too old", remote_version).into(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here for adding taker version in the error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in 1def60d

.r()
.data
.maker_version
.ok_or("No swap protocol version".to_owned())?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we should return string errors from swap logic / commands

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But all functions in this src return errors as String (what, I agree, is not good but maybe it's a task for a dedicated PR to refactor code)

@@ -3890,6 +3890,7 @@ fn test_eth_swap_negotiation_fails_maker_no_fallback() {
#[test]
fn test_trade_base_rel_eth_erc20_coins() { trade_base_rel(("ETH", "ERC20DEV")); }

// run this test also with features "test-use-old-maker" or "test-use-old-taker" to emulate non-versioned nodes
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess this is a todo, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed (made dedicated tests instead)

@dimxy dimxy added under review and removed in progress Changes will be made from the author labels Aug 26, 2024
* dev:
  chore(RPCs): rename `get_peers_info` RPC to `get_directly_connected_peers` (#2195)
  chore(WASM-builds): remove `wasm-opt` overriding (#2200)
  fix(coins): add p2p feature to mm2_net dependency (#2210)
  chore(test): turn on debug assertion (#2204)
  feat(sia): extract sia lib to external repo (#2167)
  feat(eth-swap): eth tpu v2 methods, eth docker test enhancements (#2169)
  fix(cors): allow OPTIONS request to KDF server (#2191)
  docs(README): update commit badges to use dev branch (#2193)
  use default value for `komodo_proxy` (#2192)
  feat(cosmos): komodo-defi-proxy support (#2173)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants