mirror of
https://github.com/basicswap/basicswap.git
synced 2026-06-08 12:11:41 +02:00
@@ -60,7 +60,7 @@ jobs:
|
||||
flake8 --ignore=E203,E501,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,.eggs,.tox,bin/install_certifi.py
|
||||
- name: Run codespell
|
||||
run: |
|
||||
codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py,*basicswap/static
|
||||
codespell
|
||||
- name: Run black
|
||||
run: |
|
||||
black --check --diff --exclude="contrib" .
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
export PARTICL_BINDIR="$BIN_DIR/particl"
|
||||
export BITCOIN_BINDIR="$BIN_DIR/bitcoin"
|
||||
export XMR_BINDIR="$BIN_DIR/monero"
|
||||
pytest tests/basicswap/test_btc_xmr.py::TestBTC -k "test_003_api or test_02_a_leader_recover_a_lock_tx"
|
||||
pytest tests/basicswap/test_btc_xmr.py::TestBTC -k "test_003_api or test_02_a_leader_recover_a_lock_tx or test_11_fee_validation"
|
||||
- name: Run test_encrypted_xmr_reload
|
||||
id: test_encrypted_xmr_reload
|
||||
run: |
|
||||
|
||||
@@ -4126,6 +4126,9 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
||||
msg_buf.fee_rate_to = ci_to.make_int(fee_rate)
|
||||
|
||||
if swap_type == SwapTypes.XMR_SWAP:
|
||||
ci_from.validateFeeRate(msg_buf.fee_rate_from)
|
||||
ci_to.validateFeeRate(msg_buf.fee_rate_to)
|
||||
|
||||
xmr_offer = XmrOffer()
|
||||
|
||||
chain_a_ci = ci_to if reverse_bid else ci_from
|
||||
@@ -6029,6 +6032,9 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
||||
if offer.swap_type != SwapTypes.XMR_SWAP:
|
||||
raise ValueError(f"TODO: Unknown swap type {offer.swap_type.name}")
|
||||
|
||||
ci_from.validateFeeRate(xmr_offer.a_fee_rate)
|
||||
ci_to.validateFeeRate(xmr_offer.b_fee_rate)
|
||||
|
||||
if not (self.debug and extra_options.get("debug_skip_validation", False)):
|
||||
self.validateBidValidTime(
|
||||
offer.swap_type, coin_from, coin_to, valid_for_seconds
|
||||
@@ -10069,6 +10075,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
||||
ensure(len(offer_data.proof_signature) == 0, "Unexpected data")
|
||||
ensure(len(offer_data.pkhash_seller) == 0, "Unexpected data")
|
||||
ensure(len(offer_data.secret_hash) == 0, "Unexpected data")
|
||||
|
||||
ci_from.validateFeeRate(offer_data.fee_rate_from)
|
||||
ci_to.validateFeeRate(offer_data.fee_rate_to)
|
||||
|
||||
else:
|
||||
raise ValueError("Unknown swap type {}.".format(offer_data.swap_type))
|
||||
|
||||
@@ -10094,6 +10104,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
||||
# Check for sent
|
||||
existing_offer = self.getOffer(offer_id, cursor=cursor)
|
||||
if existing_offer is None:
|
||||
|
||||
bid_reversed: bool = (
|
||||
offer_data.swap_type == SwapTypes.XMR_SWAP
|
||||
and self.is_reverse_ads_bid(
|
||||
|
||||
@@ -50,7 +50,7 @@ class CoinInterface:
|
||||
def compareFeeRates(a, b) -> bool:
|
||||
return abs(a - b) < 20
|
||||
|
||||
def __init__(self, network):
|
||||
def __init__(self, network, **kwargs):
|
||||
self.setDefaults()
|
||||
self._network = network
|
||||
self._mx_wallet = threading.Lock()
|
||||
@@ -195,6 +195,9 @@ class AdaptorSigInterface:
|
||||
|
||||
|
||||
class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@staticmethod
|
||||
def curve_type():
|
||||
return Curves.secp256k1
|
||||
|
||||
@@ -71,8 +71,13 @@ class BCHInterface(BTCInterface):
|
||||
# TODO: BCH Watchonly: Remove when BCH watchonly works.
|
||||
return True
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(BCHInterface, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self.swap_client = swap_client
|
||||
|
||||
def has_segwit(self) -> bool:
|
||||
|
||||
@@ -27,6 +27,7 @@ from basicswap.basicswap_util import (
|
||||
getVoutByScriptPubKey,
|
||||
)
|
||||
from basicswap.interface.base import Secp256k1Interface
|
||||
from basicswap.interface.utils import FeeValidator
|
||||
from basicswap.util import (
|
||||
b2i,
|
||||
ensure,
|
||||
@@ -184,7 +185,7 @@ def extractScriptLockRefundScriptValues(script_bytes: bytes):
|
||||
return pk1, pk2, csv_val, pk3
|
||||
|
||||
|
||||
class BTCInterface(Secp256k1Interface):
|
||||
class BTCInterface(FeeValidator, Secp256k1Interface):
|
||||
_scantxoutset_lock = threading.Lock()
|
||||
_MAX_SCANTXOUTSET_RETRIES = 3
|
||||
|
||||
@@ -278,8 +279,15 @@ class BTCInterface(Secp256k1Interface):
|
||||
def depth_spendable() -> int:
|
||||
return 0
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(network)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self._rpc_host = coin_settings.get("rpchost", "127.0.0.1")
|
||||
self._rpcport = coin_settings["rpcport"]
|
||||
self._rpcauth = coin_settings["rpcauth"]
|
||||
@@ -304,8 +312,6 @@ class BTCInterface(Secp256k1Interface):
|
||||
self.setConfTarget(coin_settings["conf_target"])
|
||||
self._use_segwit = coin_settings["use_segwit"]
|
||||
self._connection_type = coin_settings["connection_type"]
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
self._expect_seedid_hex = None
|
||||
self._altruistic = coin_settings.get("altruistic", True)
|
||||
self._use_descriptors = coin_settings.get("use_descriptors", False)
|
||||
|
||||
@@ -24,8 +24,13 @@ class DASHInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.DASH
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self._wallet_passphrase = ""
|
||||
self._have_checked_seed = False
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2024 tecnovert
|
||||
# Copyright (c) 2024-2025 The Basicswap developers
|
||||
# Copyright (c) 2024-2026 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -20,9 +20,8 @@ from basicswap.chainparams import Coins
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScriptNum,
|
||||
)
|
||||
from basicswap.interface.base import (
|
||||
Secp256k1Interface,
|
||||
)
|
||||
from basicswap.interface.base import Secp256k1Interface
|
||||
from basicswap.interface.utils import FeeValidator
|
||||
from basicswap.interface.btc import (
|
||||
extractScriptLockScriptValues,
|
||||
extractScriptLockRefundScriptValues,
|
||||
@@ -181,7 +180,7 @@ def extract_sig_and_pk(sig_script: bytes) -> (bytes, bytes):
|
||||
return sig, pk
|
||||
|
||||
|
||||
class DCRInterface(Secp256k1Interface):
|
||||
class DCRInterface(FeeValidator, Secp256k1Interface):
|
||||
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
@@ -258,13 +257,13 @@ class DCRInterface(Secp256k1Interface):
|
||||
def depth_spendable() -> int:
|
||||
return 0
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(network)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
super().__init__(coin_settings=coin_settings, network=network, **kwargs)
|
||||
self._rpc_host = coin_settings.get("rpchost", "127.0.0.1")
|
||||
self._rpcport = coin_settings["rpcport"]
|
||||
self._rpcauth = coin_settings["rpcauth"]
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
if "walletrpcport" in coin_settings:
|
||||
self._walletrpcport = coin_settings["walletrpcport"]
|
||||
|
||||
@@ -32,8 +32,13 @@ class DOGEInterface(BTCInterface):
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
return 192
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(DOGEInterface, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def getScriptDest(self, script: bytearray) -> bytearray:
|
||||
# P2SH
|
||||
|
||||
@@ -38,8 +38,13 @@ class FIROInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.FIRO
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(FIROInterface, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||
|
||||
@@ -16,8 +16,13 @@ class LTCInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.LTC
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(LTCInterface, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self._rpc_wallet_mweb = coin_settings.get("mweb_wallet_name", "mweb")
|
||||
self.rpc_wallet_mweb = make_rpc_func(
|
||||
self._rpcport,
|
||||
@@ -265,8 +270,13 @@ class LTCInterfaceMWEB(LTCInterface):
|
||||
def interface_type(self) -> int:
|
||||
return Coins.LTC_MWEB
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(LTCInterfaceMWEB, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self._rpc_wallet = coin_settings.get("mweb_wallet_name", "mweb")
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
||||
|
||||
@@ -73,8 +73,13 @@ class NAVInterface(BTCInterface):
|
||||
def txoType():
|
||||
return CTxOut
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(NAVInterface, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||
|
||||
@@ -81,8 +81,17 @@ class PARTInterface(BTCInterface):
|
||||
def txoType():
|
||||
return CTxOutPart
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(coin_settings, network, swap_client)
|
||||
@staticmethod
|
||||
def defaultMaxFeeRate() -> int:
|
||||
return PARTInterface.COIN() // 2
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self.setAnonTxRingSize(int(coin_settings.get("anon_tx_ring_size", 12)))
|
||||
|
||||
def use_tx_vsize(self) -> bool:
|
||||
|
||||
@@ -10,8 +10,13 @@ from basicswap.contrib.test_framework.messages import CTxOut
|
||||
|
||||
|
||||
class PassthroughBTCInterface(BTCInterface):
|
||||
def __init__(self, coin_settings, network):
|
||||
super().__init__(coin_settings, network)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
self.txoType = CTxOut
|
||||
self._network = network
|
||||
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
||||
|
||||
@@ -27,8 +27,13 @@ class PIVXInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.PIVX
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(PIVXInterface, self).__init__(coin_settings, network, swap_client)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2026 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from basicswap.contrib.test_framework.messages import COIN
|
||||
|
||||
|
||||
class FeeValidator:
|
||||
@staticmethod
|
||||
def defaultMaxFeeRate() -> int:
|
||||
return COIN // 10
|
||||
|
||||
def makeIntFromSetting(
|
||||
self, settings: dict, setting_name: str, default: int
|
||||
) -> int:
|
||||
# Return make_int(setting), or already integer default
|
||||
if setting_name in settings:
|
||||
return self.make_int(settings[setting_name])
|
||||
return default
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
default_low_fee_conf_target: int = 24
|
||||
default_low_fee_rate: int = 0
|
||||
default_high_estimated_feerate_multiplier: float = 2.0
|
||||
default_high_fee_rate: int = self.defaultMaxFeeRate()
|
||||
if self._sc:
|
||||
chain_client_settings = self._sc.getChainClientSettings(
|
||||
self.coin_type()
|
||||
) # basicswap.json
|
||||
settings = self._sc.settings
|
||||
default_low_fee_conf_target = int(
|
||||
settings.get("low_fee_conf_target", default_low_fee_conf_target)
|
||||
)
|
||||
default_low_fee_rate = self.makeIntFromSetting(
|
||||
settings, "low_feerate", default_low_fee_rate
|
||||
)
|
||||
default_high_estimated_feerate_multiplier = float(
|
||||
settings.get(
|
||||
"high_estimated_feerate_multiplier",
|
||||
default_high_estimated_feerate_multiplier,
|
||||
)
|
||||
)
|
||||
default_high_fee_rate = self.makeIntFromSetting(
|
||||
settings, "high_feerate", default_high_fee_rate
|
||||
)
|
||||
else:
|
||||
if kwargs.get("network") != "regtest":
|
||||
raise ValueError("swapclient unset")
|
||||
chain_client_settings = {}
|
||||
|
||||
self._low_fee_conf_target = int(
|
||||
chain_client_settings.get(
|
||||
"low_fee_conf_target", default_low_fee_conf_target
|
||||
)
|
||||
)
|
||||
self._low_feerate = self.makeIntFromSetting(
|
||||
chain_client_settings, "low_feerate", default_low_fee_rate
|
||||
)
|
||||
|
||||
# Set below 1.0 to disable estimating the max feerate and use max_feerate
|
||||
self._high_estimated_feerate_multiplier = float(
|
||||
chain_client_settings.get(
|
||||
"high_estimated_feerate_multiplier",
|
||||
default_high_estimated_feerate_multiplier,
|
||||
)
|
||||
)
|
||||
self._high_feerate = self.makeIntFromSetting(
|
||||
chain_client_settings, "high_feerate", default_high_fee_rate
|
||||
)
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def validateFeeRate(self, feerate: int) -> None:
|
||||
if self._low_feerate > 0:
|
||||
min_feerate_src = "set_value"
|
||||
min_feerate = self._low_feerate
|
||||
else:
|
||||
min_feerate, min_feerate_src = self.get_fee_rate(self._low_fee_conf_target)
|
||||
min_feerate = self.make_int(min_feerate)
|
||||
|
||||
if self._high_estimated_feerate_multiplier >= 1.0:
|
||||
max_feerate, max_feerate_src = self.get_fee_rate()
|
||||
max_feerate = (
|
||||
self.make_int(max_feerate) * self._high_estimated_feerate_multiplier
|
||||
)
|
||||
else:
|
||||
max_feerate_src = "set_value"
|
||||
max_feerate = self._high_feerate
|
||||
|
||||
if max_feerate_src in ("estimatesmartfee", "electrum"):
|
||||
if max_feerate > self._high_feerate:
|
||||
max_feerate_src = "clamped_to_set_value"
|
||||
max_feerate = self._high_feerate
|
||||
|
||||
self._log.debug(
|
||||
f"Verify {self.ticker()} fee rate {feerate}, min {min_feerate} {min_feerate_src}, max {max_feerate} {max_feerate_src}"
|
||||
)
|
||||
if feerate < min_feerate:
|
||||
err_msg: str = (
|
||||
f"Fee rate too low, {feerate} < {min_feerate}, {min_feerate_src}"
|
||||
)
|
||||
self._log.error(err_msg)
|
||||
raise ValueError(err_msg)
|
||||
if feerate > max_feerate:
|
||||
err_msg: str = (
|
||||
f"Fee rate too high, {feerate} > {max_feerate}, {max_feerate_src}"
|
||||
)
|
||||
self._log.error(err_msg)
|
||||
raise ValueError(err_msg)
|
||||
@@ -101,8 +101,13 @@ class XMRInterface(CoinInterface):
|
||||
return True
|
||||
return super().is_transient_error(ex)
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(network)
|
||||
def __init__(self, coin_settings, network, swap_client=None, **kwargs):
|
||||
super().__init__(
|
||||
coin_settings=coin_settings,
|
||||
network=network,
|
||||
swap_client=swap_client,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self._addr_prefix = self.chainparams_network()["address_prefix"]
|
||||
|
||||
@@ -857,3 +862,6 @@ class XMRInterface(CoinInterface):
|
||||
except Exception as e:
|
||||
self._log.error(f"listWalletTransactions failed: {e}")
|
||||
return []
|
||||
|
||||
def validateFeeRate(self, fee_rate: int) -> bool:
|
||||
pass # Fee rate isn't used
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
0.16.3
|
||||
==============
|
||||
|
||||
- Automatic fee validation.
|
||||
- Prevent sending bids to offers
|
||||
- Reject received offers, and
|
||||
- Prevent sending offers where the chain feerates are out of range.
|
||||
- Valid feerate range is the nodes estimated feerate for confirmation in 24 blocks to 2x the estimated feerate.
|
||||
- The minimum feerate confirmation can be adjusted with the "low_fee_conf_target" setting.
|
||||
- If "low_feerate" is set above 0 it is used instead of the dynamic feerate with "low_fee_conf_target"
|
||||
- The maximum feerate multiplier can be adjusted with the "high_estimated_feerate_multiplier" setting.
|
||||
- If "high_estimated_feerate_multiplier" is set below 1.0 the max feerate can be set with the "high_feerate" setting.
|
||||
- New setting "startup_delay"
|
||||
- Adjusts the time waited for coin daemons to start between "startup_tries".
|
||||
- Valid as a base setting and can be overridden per coin with chainclients settings.
|
||||
|
||||
|
||||
|
||||
0.14.5
|
||||
==============
|
||||
|
||||
|
||||
@@ -48,3 +48,11 @@ allow-direct-references = true
|
||||
|
||||
[tool.ruff]
|
||||
exclude = ["basicswap/contrib","basicswap/interface/contrib"]
|
||||
|
||||
[tool.codespell]
|
||||
check-filenames = true
|
||||
disable-colors = true
|
||||
quiet-level = 7
|
||||
dictionary = "tests/lint/spelling.extra_dictionary.txt,-"
|
||||
ignore-words = "tests/lint/spelling.ignore-words.txt"
|
||||
skip = ".git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py,*basicswap/static"
|
||||
|
||||
@@ -741,7 +741,7 @@ class Test(BaseTest):
|
||||
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
||||
if not cls.restore_instance:
|
||||
dcr_mining_addr = ci0.rpc_wallet("getnewaddress")
|
||||
assert dcr_mining_addr in cls.dcr_mining_addrs
|
||||
assert dcr_mining_addr == cls.dcr_mining_addr
|
||||
cls.dcr_ticket_account = ci0.rpc_wallet(
|
||||
"getaccount",
|
||||
[
|
||||
|
||||
@@ -2329,6 +2329,162 @@ class BasicSwapTest(TestFunctions):
|
||||
"TODO"
|
||||
) # Build without xmr first for quicker test iterations
|
||||
|
||||
def test_11_fee_validation(self):
|
||||
coin_from, coin_to = (self.test_coin_from, Coins.XMR)
|
||||
logging.info(
|
||||
f"---------- Test {coin_from.name} to {coin_to.name} expires bid stuck on accepted"
|
||||
)
|
||||
|
||||
swap_clients = self.swap_clients
|
||||
ci_from = swap_clients[0].ci(coin_from)
|
||||
ci_to = swap_clients[0].ci(coin_to)
|
||||
|
||||
ci1_from = swap_clients[1].ci(coin_from)
|
||||
ci1_to = swap_clients[1].ci(coin_to)
|
||||
|
||||
amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
|
||||
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
|
||||
|
||||
offer_id = swap_clients[0].postOffer(
|
||||
coin_from,
|
||||
coin_to,
|
||||
amt_swap,
|
||||
rate_swap,
|
||||
amt_swap,
|
||||
SwapTypes.XMR_SWAP,
|
||||
)
|
||||
|
||||
amt_swap_reverse = ci1_to.make_int(random.uniform(0.1, 2.0), r=1)
|
||||
offer_reverse_id = swap_clients[1].postOffer(
|
||||
coin_to,
|
||||
coin_from,
|
||||
amt_swap_reverse,
|
||||
rate_swap,
|
||||
amt_swap_reverse,
|
||||
SwapTypes.XMR_SWAP,
|
||||
)
|
||||
|
||||
ci_from_settings = swap_clients[0].getChainClientSettings(coin_from)
|
||||
old_override_feerate = ci_from_settings.get("override_feerate", None)
|
||||
ci1_from_settings = swap_clients[1].getChainClientSettings(coin_from)
|
||||
old_override_feerate1 = ci1_from_settings.get("override_feerate", None)
|
||||
|
||||
networkinfo = ci_from.rpc("getnetworkinfo")
|
||||
assert ci_from.make_int(networkinfo["relayfee"]) == ci_from.make_int(
|
||||
ci_from.get_fee_rate()[0]
|
||||
)
|
||||
try:
|
||||
# Set override_feerate to increase feerate from get_fee_rate()
|
||||
ci_from_settings["override_feerate"] = ci_from.format_amount(120)
|
||||
ci1_from_settings["override_feerate"] = ci1_from.format_amount(120)
|
||||
try:
|
||||
swap_clients[0].postXmrBid(offer_id, amt_swap)
|
||||
except Exception as e:
|
||||
assert "Fee rate too low, 100 < 120, override_feerate" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
# Test reverse bid, low fee
|
||||
try:
|
||||
swap_clients[1].postXmrBid(offer_reverse_id, amt_swap_reverse)
|
||||
except Exception as e:
|
||||
assert "Fee rate too low, 100 < 120, override_feerate" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
# Clear override_feerate (get_fee_rate()), set low_feerate (validateFeeRate())
|
||||
ci_from_settings["override_feerate"] = None
|
||||
ci1_from_settings["override_feerate"] = None
|
||||
ci_from._low_feerate = 120
|
||||
ci1_from._low_feerate = 120
|
||||
try:
|
||||
swap_clients[0].postOffer(
|
||||
coin_from,
|
||||
coin_to,
|
||||
amt_swap,
|
||||
rate_swap,
|
||||
amt_swap,
|
||||
SwapTypes.XMR_SWAP,
|
||||
)
|
||||
except Exception as e:
|
||||
assert "Fee rate too low, 100 < 120, set_value" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
# Test reverse offer, low fee
|
||||
try:
|
||||
swap_clients[1].postOffer(
|
||||
coin_to,
|
||||
coin_from,
|
||||
amt_swap_reverse,
|
||||
rate_swap,
|
||||
amt_swap_reverse,
|
||||
SwapTypes.XMR_SWAP,
|
||||
)
|
||||
except Exception as e:
|
||||
assert "Fee rate too low, 100 < 120, set_value" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
ci_from._low_feerate = 0
|
||||
ci1_from._low_feerate = 0
|
||||
ci_from._high_estimated_feerate_multiplier = (
|
||||
0 # Disable high fee from estimate
|
||||
)
|
||||
ci_from._high_feerate = (
|
||||
80 # ci_from_settings["high_feerate"] = ci_from.format_amount(80)
|
||||
)
|
||||
logging.info(f"[rm] ci_from.get_fee_rate() {ci_from.get_fee_rate()}")
|
||||
try:
|
||||
swap_clients[0].postXmrBid(offer_id, amt_swap)
|
||||
except Exception as e:
|
||||
assert "Fee rate too high, 100 > 80, set_value" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
# Test reverse bid, high fee
|
||||
ci1_from._high_estimated_feerate_multiplier = 0
|
||||
ci1_from._high_feerate = 80
|
||||
try:
|
||||
swap_clients[1].postXmrBid(offer_reverse_id, amt_swap_reverse)
|
||||
except Exception as e:
|
||||
assert "Fee rate too high, 100 > 80, set_value" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
try:
|
||||
swap_clients[0].postOffer(
|
||||
coin_from,
|
||||
coin_to,
|
||||
amt_swap,
|
||||
rate_swap,
|
||||
amt_swap,
|
||||
SwapTypes.XMR_SWAP,
|
||||
)
|
||||
except Exception as e:
|
||||
assert "Fee rate too high, 100 > 80, set_value" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
# Test reverse offer, high fee
|
||||
try:
|
||||
swap_clients[1].postOffer(
|
||||
coin_to,
|
||||
coin_from,
|
||||
amt_swap_reverse,
|
||||
rate_swap,
|
||||
amt_swap_reverse,
|
||||
SwapTypes.XMR_SWAP,
|
||||
)
|
||||
except Exception as e:
|
||||
assert "Fee rate too high, 100 > 80, set_value" in str(e)
|
||||
else:
|
||||
assert False, "Should fail"
|
||||
|
||||
finally:
|
||||
ci_from_settings["override_feerate"] = old_override_feerate
|
||||
ci1_from_settings["override_feerate"] = old_override_feerate1
|
||||
|
||||
|
||||
class TestBTC(BasicSwapTest):
|
||||
__test__ = True
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
confimration->confirmation
|
||||
Reference in New Issue
Block a user