mirror of
https://github.com/basicswap/basicswap.git
synced 2025-05-06 04:42:18 +00:00
Add estimated tx fee to amount check when posting bid.
Add more log messages around balance checks.
This commit is contained in:
parent
c945e267e7
commit
c79ed493aa
9 changed files with 235 additions and 176 deletions
.github/workflows
basicswap
tests/basicswap
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -56,9 +56,9 @@ jobs:
|
||||||
- name: Running test_xmr
|
- name: Running test_xmr
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH=$(pwd)
|
export PYTHONPATH=$(pwd)
|
||||||
export PARTICL_BINDIR="$BIN_DIR/particl";
|
export PARTICL_BINDIR="$BIN_DIR/particl"
|
||||||
export BITCOIN_BINDIR="$BIN_DIR/bitcoin";
|
export BITCOIN_BINDIR="$BIN_DIR/bitcoin"
|
||||||
export XMR_BINDIR="$BIN_DIR/monero";
|
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"
|
||||||
- name: Running test_encrypted_xmr_reload
|
- name: Running test_encrypted_xmr_reload
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -1974,6 +1974,32 @@ class BasicSwap(BaseApp):
|
||||||
if not offer.rate_negotiable:
|
if not offer.rate_negotiable:
|
||||||
ensure(offer.rate == bid_rate, "Bid rate must match offer rate.")
|
ensure(offer.rate == bid_rate, "Bid rate must match offer rate.")
|
||||||
|
|
||||||
|
def ensureWalletCanSend(
|
||||||
|
self, ci, swap_type, ensure_balance: int, estimated_fee: int, for_offer=True
|
||||||
|
) -> None:
|
||||||
|
balance_msg: str = (
|
||||||
|
f"{ci.format_amount(ensure_balance)} {ci.coin_name()} with estimated fee {ci.format_amount(estimated_fee)}"
|
||||||
|
)
|
||||||
|
self.log.debug(f"Ensuring wallet can send {balance_msg}.")
|
||||||
|
try:
|
||||||
|
if ci.interface_type() in self.scriptless_coins:
|
||||||
|
ci.ensureFunds(ensure_balance + estimated_fee)
|
||||||
|
else:
|
||||||
|
pi = self.pi(swap_type)
|
||||||
|
_ = pi.getFundedInitiateTxTemplate(ci, ensure_balance, False)
|
||||||
|
# TODO: Save the prefunded tx so the fee can't change, complicates multiple offers at the same time.
|
||||||
|
except Exception as e:
|
||||||
|
type_str = "offer" if for_offer else "bid"
|
||||||
|
err_msg = f"Insufficient funds for {type_str} of {balance_msg}."
|
||||||
|
if self.debug:
|
||||||
|
self.log.error(f"ensureWalletCanSend failed {e}")
|
||||||
|
current_balance: int = ci.getSpendableBalance()
|
||||||
|
err_msg += (
|
||||||
|
f" Debug: Spendable balance: {ci.format_amount(current_balance)}."
|
||||||
|
)
|
||||||
|
self.log.error(err_msg)
|
||||||
|
raise ValueError(err_msg)
|
||||||
|
|
||||||
def getOfferAddressTo(self, extra_options) -> str:
|
def getOfferAddressTo(self, extra_options) -> str:
|
||||||
if "addr_send_to" in extra_options:
|
if "addr_send_to" in extra_options:
|
||||||
return extra_options["addr_send_to"]
|
return extra_options["addr_send_to"]
|
||||||
|
@ -2007,25 +2033,25 @@ class BasicSwap(BaseApp):
|
||||||
except Exception:
|
except Exception:
|
||||||
raise ValueError("Unknown coin to type")
|
raise ValueError("Unknown coin to type")
|
||||||
|
|
||||||
valid_for_seconds: int = extra_options.get("valid_for_seconds", 60 * 60)
|
|
||||||
amount_to: int = extra_options.get(
|
|
||||||
"amount_to", int((amount * rate) // ci_from.COIN())
|
|
||||||
)
|
|
||||||
|
|
||||||
# Recalculate the rate so it will match the bid rate
|
|
||||||
rate = ci_from.make_int(amount_to / amount, r=1)
|
|
||||||
|
|
||||||
self.validateSwapType(coin_from_t, coin_to_t, swap_type)
|
self.validateSwapType(coin_from_t, coin_to_t, swap_type)
|
||||||
self.validateOfferAmounts(
|
|
||||||
coin_from_t, coin_to_t, amount, amount_to, min_bid_amount
|
|
||||||
)
|
|
||||||
self.validateOfferLockValue(
|
self.validateOfferLockValue(
|
||||||
swap_type, coin_from_t, coin_to_t, lock_type, lock_value
|
swap_type, coin_from_t, coin_to_t, lock_type, lock_value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
valid_for_seconds: int = extra_options.get("valid_for_seconds", 60 * 60)
|
||||||
self.validateOfferValidTime(
|
self.validateOfferValidTime(
|
||||||
swap_type, coin_from_t, coin_to_t, valid_for_seconds
|
swap_type, coin_from_t, coin_to_t, valid_for_seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
|
amount_to: int = extra_options.get(
|
||||||
|
"amount_to", int((amount * rate) // ci_from.COIN())
|
||||||
|
)
|
||||||
|
self.validateOfferAmounts(
|
||||||
|
coin_from_t, coin_to_t, amount, amount_to, min_bid_amount
|
||||||
|
)
|
||||||
|
# Recalculate the rate so it will match the bid rate
|
||||||
|
rate: int = ci_from.make_int(amount_to / amount, r=1)
|
||||||
|
|
||||||
offer_addr_to = self.getOfferAddressTo(extra_options)
|
offer_addr_to = self.getOfferAddressTo(extra_options)
|
||||||
|
|
||||||
reverse_bid: bool = self.is_reverse_ads_bid(coin_from, coin_to)
|
reverse_bid: bool = self.is_reverse_ads_bid(coin_from, coin_to)
|
||||||
|
@ -2066,8 +2092,8 @@ class BasicSwap(BaseApp):
|
||||||
)
|
)
|
||||||
|
|
||||||
if "from_fee_override" in extra_options:
|
if "from_fee_override" in extra_options:
|
||||||
msg_buf.fee_rate_from = make_int(
|
msg_buf.fee_rate_from = ci_from.make_int(
|
||||||
extra_options["from_fee_override"], self.ci(coin_from).exp()
|
extra_options["from_fee_override"]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# TODO: conf_target = ci_from.settings.get('conf_target', 2)
|
# TODO: conf_target = ci_from.settings.get('conf_target', 2)
|
||||||
|
@ -2077,12 +2103,10 @@ class BasicSwap(BaseApp):
|
||||||
fee_rate, fee_src = self.getFeeRateForCoin(coin_from, conf_target)
|
fee_rate, fee_src = self.getFeeRateForCoin(coin_from, conf_target)
|
||||||
if "from_fee_multiplier_percent" in extra_options:
|
if "from_fee_multiplier_percent" in extra_options:
|
||||||
fee_rate *= extra_options["fee_multiplier"] / 100.0
|
fee_rate *= extra_options["fee_multiplier"] / 100.0
|
||||||
msg_buf.fee_rate_from = make_int(fee_rate, self.ci(coin_from).exp())
|
msg_buf.fee_rate_from = ci_from.make_int(fee_rate)
|
||||||
|
|
||||||
if "to_fee_override" in extra_options:
|
if "to_fee_override" in extra_options:
|
||||||
msg_buf.fee_rate_to = make_int(
|
msg_buf.fee_rate_to = ci_to.make_int(extra_options["to_fee_override"])
|
||||||
extra_options["to_fee_override"], self.ci(coin_to).exp()
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# TODO: conf_target = ci_to.settings.get('conf_target', 2)
|
# TODO: conf_target = ci_to.settings.get('conf_target', 2)
|
||||||
conf_target = 2
|
conf_target = 2
|
||||||
|
@ -2091,7 +2115,7 @@ class BasicSwap(BaseApp):
|
||||||
fee_rate, fee_src = self.getFeeRateForCoin(coin_to, conf_target)
|
fee_rate, fee_src = self.getFeeRateForCoin(coin_to, conf_target)
|
||||||
if "to_fee_multiplier_percent" in extra_options:
|
if "to_fee_multiplier_percent" in extra_options:
|
||||||
fee_rate *= extra_options["fee_multiplier"] / 100.0
|
fee_rate *= extra_options["fee_multiplier"] / 100.0
|
||||||
msg_buf.fee_rate_to = make_int(fee_rate, self.ci(coin_to).exp())
|
msg_buf.fee_rate_to = ci_to.make_int(fee_rate)
|
||||||
|
|
||||||
if swap_type == SwapTypes.XMR_SWAP:
|
if swap_type == SwapTypes.XMR_SWAP:
|
||||||
xmr_offer = XmrOffer()
|
xmr_offer = XmrOffer()
|
||||||
|
@ -2116,27 +2140,19 @@ class BasicSwap(BaseApp):
|
||||||
msg_buf.fee_rate_to
|
msg_buf.fee_rate_to
|
||||||
) # Unused: TODO - Set priority?
|
) # Unused: TODO - Set priority?
|
||||||
|
|
||||||
ensure_balance: int = int(amount)
|
# If a prefunded txn is not used, check that the wallet balance can cover the tx fee.
|
||||||
if coin_from in self.scriptless_coins:
|
if "prefunded_itx" not in extra_options:
|
||||||
# TODO: Better tx size estimate, xmr_swap_b_lock_tx_vsize could be larger than xmr_swap_b_lock_spend_tx_vsize
|
# TODO: Better tx size estimate, xmr_swap_b_lock_tx_vsize could be larger than xmr_swap_b_lock_spend_tx_vsize
|
||||||
estimated_fee: int = (
|
estimated_fee: int = (
|
||||||
msg_buf.fee_rate_from
|
msg_buf.fee_rate_from * ci_from.est_lock_tx_vsize() // 1000
|
||||||
* ci_from.xmr_swap_b_lock_spend_tx_vsize()
|
|
||||||
/ 1000
|
|
||||||
)
|
)
|
||||||
ci_from.ensureFunds(msg_buf.amount_from + estimated_fee)
|
self.ensureWalletCanSend(ci_from, swap_type, int(amount), estimated_fee)
|
||||||
else:
|
|
||||||
# If a prefunded txn is not used, check that the wallet balance can cover the tx fee.
|
|
||||||
if "prefunded_itx" not in extra_options:
|
|
||||||
pi = self.pi(SwapTypes.XMR_SWAP)
|
|
||||||
_ = pi.getFundedInitiateTxTemplate(ci_from, ensure_balance, False)
|
|
||||||
# TODO: Save the prefunded tx so the fee can't change, complicates multiple offers at the same time.
|
|
||||||
|
|
||||||
# TODO: Send proof of funds with offer
|
# TODO: Send proof of funds with offer
|
||||||
# proof_of_funds_hash = getOfferProofOfFundsHash(msg_buf, offer_addr)
|
# proof_of_funds_hash = getOfferProofOfFundsHash(msg_buf, offer_addr)
|
||||||
# proof_addr, proof_sig, proof_utxos = self.getProofOfFunds(
|
# proof_addr, proof_sig, proof_utxos = self.getProofOfFunds(
|
||||||
# coin_from_t, ensure_balance, proof_of_funds_hash
|
# coin_from_t, ensure_balance, proof_of_funds_hash
|
||||||
# )
|
# )
|
||||||
|
|
||||||
offer_bytes = msg_buf.to_bytes()
|
offer_bytes = msg_buf.to_bytes()
|
||||||
payload_hex = str.format("{:02x}", MessageTypes.OFFER) + offer_bytes.hex()
|
payload_hex = str.format("{:02x}", MessageTypes.OFFER) + offer_bytes.hex()
|
||||||
|
@ -2174,6 +2190,8 @@ class BasicSwap(BaseApp):
|
||||||
was_sent=True,
|
was_sent=True,
|
||||||
bid_reversed=bid_reversed,
|
bid_reversed=bid_reversed,
|
||||||
security_token=security_token,
|
security_token=security_token,
|
||||||
|
from_feerate=msg_buf.fee_rate_from,
|
||||||
|
to_feerate=msg_buf.fee_rate_to,
|
||||||
)
|
)
|
||||||
offer.setState(OfferStates.OFFER_SENT)
|
offer.setState(OfferStates.OFFER_SENT)
|
||||||
|
|
||||||
|
@ -3525,14 +3543,12 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
self.checkCoinsReady(coin_from, coin_to)
|
self.checkCoinsReady(coin_from, coin_to)
|
||||||
|
|
||||||
balance_to: int = ci_to.getSpendableBalance()
|
# TODO: Better tx size estimate
|
||||||
ensure(
|
fee_rate, fee_src = self.getFeeRateForCoin(coin_to, conf_target=2)
|
||||||
balance_to > amount_to,
|
fee_rate_to = ci_to.make_int(fee_rate)
|
||||||
"{} spendable balance is too low: {} < {}".format(
|
estimated_fee: int = fee_rate_to * ci_to.est_lock_tx_vsize() // 1000
|
||||||
ci_to.coin_name(),
|
self.ensureWalletCanSend(
|
||||||
ci_to.format_amount(balance_to),
|
ci_to, offer.swap_type, int(amount_to), estimated_fee, for_offer=False
|
||||||
ci_to.format_amount(amount_to),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
reverse_bid: bool = self.is_reverse_ads_bid(coin_from, coin_to)
|
reverse_bid: bool = self.is_reverse_ads_bid(coin_from, coin_to)
|
||||||
|
@ -4069,6 +4085,18 @@ class BasicSwap(BaseApp):
|
||||||
ci_from = self.ci(coin_from)
|
ci_from = self.ci(coin_from)
|
||||||
ci_to = self.ci(coin_to)
|
ci_to = self.ci(coin_to)
|
||||||
|
|
||||||
|
# TODO: Better tx size estimate
|
||||||
|
fee_rate, fee_src = self.getFeeRateForCoin(coin_to, conf_target=2)
|
||||||
|
fee_rate_from = ci_to.make_int(fee_rate)
|
||||||
|
estimated_fee: int = fee_rate_from * ci_to.est_lock_tx_vsize() // 1000
|
||||||
|
self.ensureWalletCanSend(
|
||||||
|
ci_to,
|
||||||
|
offer.swap_type,
|
||||||
|
offer.amount_from,
|
||||||
|
estimated_fee,
|
||||||
|
for_offer=False,
|
||||||
|
)
|
||||||
|
|
||||||
if xmr_swap.contract_count is None:
|
if xmr_swap.contract_count is None:
|
||||||
xmr_swap.contract_count = self.getNewContractId(use_cursor)
|
xmr_swap.contract_count = self.getNewContractId(use_cursor)
|
||||||
|
|
||||||
|
|
|
@ -440,7 +440,9 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
||||||
swap_client.log.info(f"Starting {display_name} daemon")
|
swap_client.log.info(f"Starting {display_name} daemon")
|
||||||
|
|
||||||
filename: str = getCoreBinName(coin_id, v, c + "d")
|
filename: str = getCoreBinName(coin_id, v, c + "d")
|
||||||
extra_opts = getCoreBinArgs(coin_id, v, use_tor_proxy=swap_client.use_tor_proxy)
|
extra_opts = getCoreBinArgs(
|
||||||
|
coin_id, v, use_tor_proxy=swap_client.use_tor_proxy
|
||||||
|
)
|
||||||
daemons.append(
|
daemons.append(
|
||||||
startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts)
|
startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts)
|
||||||
)
|
)
|
||||||
|
|
|
@ -217,6 +217,10 @@ class BTCInterface(Secp256k1Interface):
|
||||||
rv += output.nValue
|
rv += output.nValue
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 110
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 147
|
return 147
|
||||||
|
|
|
@ -211,6 +211,10 @@ class DCRInterface(Secp256k1Interface):
|
||||||
def txoType():
|
def txoType():
|
||||||
return CTxOut
|
return CTxOut
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 224
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 327
|
return 327
|
||||||
|
|
|
@ -24,6 +24,10 @@ class DOGEInterface(BTCInterface):
|
||||||
def coin_type():
|
def coin_type():
|
||||||
return Coins.DOGE
|
return Coins.DOGE
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 192
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||||
return 192
|
return 192
|
||||||
|
|
|
@ -66,6 +66,10 @@ class PARTInterface(BTCInterface):
|
||||||
def txVersion() -> int:
|
def txVersion() -> int:
|
||||||
return 0xA0
|
return 0xA0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 138
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 200
|
return 200
|
||||||
|
@ -195,6 +199,10 @@ class PARTInterfaceBlind(PARTInterface):
|
||||||
def balance_type():
|
def balance_type():
|
||||||
return BalanceTypes.BLIND
|
return BalanceTypes.BLIND
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 980
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 1032
|
return 1032
|
||||||
|
@ -1220,6 +1228,10 @@ class PARTInterfaceAnon(PARTInterface):
|
||||||
def balance_type():
|
def balance_type():
|
||||||
return BalanceTypes.ANON
|
return BalanceTypes.ANON
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 1153
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
raise ValueError("Not possible")
|
raise ValueError("Not possible")
|
||||||
|
|
|
@ -71,6 +71,11 @@ class XMRInterface(CoinInterface):
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
raise ValueError("Not possible")
|
raise ValueError("Not possible")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
# TODO: Estimate with ringsize
|
||||||
|
return 1604
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||||
# TODO: Estimate with ringsize
|
# TODO: Estimate with ringsize
|
||||||
|
|
|
@ -10,9 +10,6 @@ import random
|
||||||
import logging
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from basicswap.db import (
|
|
||||||
Concepts,
|
|
||||||
)
|
|
||||||
from basicswap.basicswap import (
|
from basicswap.basicswap import (
|
||||||
BidStates,
|
BidStates,
|
||||||
Coins,
|
Coins,
|
||||||
|
@ -23,6 +20,9 @@ from basicswap.basicswap_util import (
|
||||||
TxLockTypes,
|
TxLockTypes,
|
||||||
EventLogTypes,
|
EventLogTypes,
|
||||||
)
|
)
|
||||||
|
from basicswap.db import (
|
||||||
|
Concepts,
|
||||||
|
)
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
make_int,
|
make_int,
|
||||||
)
|
)
|
||||||
|
@ -32,10 +32,10 @@ from tests.basicswap.util import (
|
||||||
)
|
)
|
||||||
from tests.basicswap.common import (
|
from tests.basicswap.common import (
|
||||||
abandon_all_swaps,
|
abandon_all_swaps,
|
||||||
|
wait_for_balance,
|
||||||
wait_for_bid,
|
wait_for_bid,
|
||||||
wait_for_event,
|
wait_for_event,
|
||||||
wait_for_offer,
|
wait_for_offer,
|
||||||
wait_for_balance,
|
|
||||||
wait_for_unspent,
|
wait_for_unspent,
|
||||||
wait_for_none_active,
|
wait_for_none_active,
|
||||||
BTC_BASE_RPC_PORT,
|
BTC_BASE_RPC_PORT,
|
||||||
|
@ -640,6 +640,129 @@ class TestFunctions(BaseTest):
|
||||||
wait_for=(self.extra_wait_time + 180),
|
wait_for=(self.extra_wait_time + 180),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def do_test_08_insufficient_funds(self, coin_from, coin_to):
|
||||||
|
logging.info(
|
||||||
|
"---------- Test {} to {} Insufficient Funds".format(
|
||||||
|
coin_from.name, coin_to.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
swap_clients = self.swap_clients
|
||||||
|
reverse_bid: bool = swap_clients[0].is_reverse_ads_bid(coin_from, coin_to)
|
||||||
|
|
||||||
|
id_offerer: int = self.node_c_id
|
||||||
|
id_bidder: int = self.node_b_id
|
||||||
|
|
||||||
|
self.prepare_balance(
|
||||||
|
coin_from,
|
||||||
|
10.0,
|
||||||
|
1800 + id_offerer,
|
||||||
|
1801 if coin_from in (Coins.XMR,) else 1800,
|
||||||
|
)
|
||||||
|
jsw = read_json_api(1800 + id_offerer, "wallets")
|
||||||
|
balance_from_before: float = self.getBalance(jsw, coin_from)
|
||||||
|
self.prepare_balance(
|
||||||
|
coin_to,
|
||||||
|
balance_from_before + 1,
|
||||||
|
1800 + id_bidder,
|
||||||
|
1801 if coin_to in (Coins.XMR,) else 1800,
|
||||||
|
)
|
||||||
|
|
||||||
|
swap_clients = self.swap_clients
|
||||||
|
ci_from = swap_clients[id_offerer].ci(coin_from)
|
||||||
|
ci_to = swap_clients[id_bidder].ci(coin_to)
|
||||||
|
|
||||||
|
amt_swap: int = ci_from.make_int(balance_from_before, r=1)
|
||||||
|
rate_swap: int = ci_to.make_int(2.0, r=1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
offer_id = swap_clients[id_offerer].postOffer(
|
||||||
|
coin_from,
|
||||||
|
coin_to,
|
||||||
|
amt_swap,
|
||||||
|
rate_swap,
|
||||||
|
amt_swap,
|
||||||
|
SwapTypes.XMR_SWAP,
|
||||||
|
auto_accept_bids=True,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
assert "Insufficient funds" in str(e)
|
||||||
|
else:
|
||||||
|
assert False, "Should fail"
|
||||||
|
|
||||||
|
# Test that postbid errors when offer is for the full balance
|
||||||
|
id_offerer_test_bid = id_bidder
|
||||||
|
id_bidder_test_bid = id_offerer
|
||||||
|
amt_swap_test_bid_to: int = ci_from.make_int(balance_from_before, r=1)
|
||||||
|
amt_swap_test_bid_from: int = ci_to.make_int(1.0)
|
||||||
|
offer_id = swap_clients[id_offerer_test_bid].postOffer(
|
||||||
|
coin_to,
|
||||||
|
coin_from,
|
||||||
|
amt_swap_test_bid_from,
|
||||||
|
0,
|
||||||
|
amt_swap_test_bid_from,
|
||||||
|
SwapTypes.XMR_SWAP,
|
||||||
|
extra_options={"amount_to": amt_swap_test_bid_to},
|
||||||
|
)
|
||||||
|
wait_for_offer(test_delay_event, swap_clients[id_bidder_test_bid], offer_id)
|
||||||
|
try:
|
||||||
|
bid_id = swap_clients[id_bidder_test_bid].postBid(
|
||||||
|
offer_id, amt_swap_test_bid_from
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
assert "Insufficient funds" in str(e)
|
||||||
|
else:
|
||||||
|
assert False, "Should fail"
|
||||||
|
|
||||||
|
amt_swap -= ci_from.make_int(1)
|
||||||
|
offer_id = swap_clients[id_offerer].postOffer(
|
||||||
|
coin_from,
|
||||||
|
coin_to,
|
||||||
|
amt_swap,
|
||||||
|
rate_swap,
|
||||||
|
amt_swap,
|
||||||
|
SwapTypes.XMR_SWAP,
|
||||||
|
auto_accept_bids=True,
|
||||||
|
)
|
||||||
|
wait_for_offer(test_delay_event, swap_clients[id_bidder], offer_id)
|
||||||
|
|
||||||
|
# First bid should work
|
||||||
|
bid_id = swap_clients[id_bidder].postXmrBid(offer_id, amt_swap)
|
||||||
|
wait_for_bid(
|
||||||
|
test_delay_event,
|
||||||
|
swap_clients[id_offerer],
|
||||||
|
bid_id,
|
||||||
|
(
|
||||||
|
(BidStates.SWAP_COMPLETED, BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED)
|
||||||
|
if reverse_bid
|
||||||
|
else (BidStates.BID_ACCEPTED, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED)
|
||||||
|
),
|
||||||
|
wait_for=120,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should be out of funds for second bid (over remaining offer value causes a hard auto accept fail)
|
||||||
|
bid_id = swap_clients[id_bidder].postXmrBid(offer_id, amt_swap)
|
||||||
|
wait_for_bid(
|
||||||
|
test_delay_event,
|
||||||
|
swap_clients[id_offerer],
|
||||||
|
bid_id,
|
||||||
|
BidStates.BID_AACCEPT_FAIL,
|
||||||
|
wait_for=40,
|
||||||
|
)
|
||||||
|
event = wait_for_event(
|
||||||
|
test_delay_event,
|
||||||
|
swap_clients[id_offerer],
|
||||||
|
Concepts.BID,
|
||||||
|
bid_id,
|
||||||
|
event_type=EventLogTypes.AUTOMATION_CONSTRAINT,
|
||||||
|
)
|
||||||
|
assert "Over remaining offer value" in event.event_msg
|
||||||
|
try:
|
||||||
|
swap_clients[id_offerer].acceptBid(bid_id)
|
||||||
|
except Exception as e:
|
||||||
|
assert "Insufficient funds" in str(e) or "Balance too low" in str(e)
|
||||||
|
else:
|
||||||
|
assert False, "Should fail"
|
||||||
|
|
||||||
|
|
||||||
class BasicSwapTest(TestFunctions):
|
class BasicSwapTest(TestFunctions):
|
||||||
|
|
||||||
|
@ -1714,133 +1837,10 @@ class BasicSwapTest(TestFunctions):
|
||||||
swap_clients[0].setMockTimeOffset(0)
|
swap_clients[0].setMockTimeOffset(0)
|
||||||
|
|
||||||
def test_08_insufficient_funds(self):
|
def test_08_insufficient_funds(self):
|
||||||
tla_from = self.test_coin_from.name
|
self.do_test_08_insufficient_funds(self.test_coin_from, Coins.XMR)
|
||||||
logging.info("---------- Test {} Insufficient Funds".format(tla_from))
|
|
||||||
swap_clients = self.swap_clients
|
|
||||||
coin_from = self.test_coin_from
|
|
||||||
coin_to = Coins.XMR
|
|
||||||
|
|
||||||
self.prepare_balance(coin_from, 10.0, 1802, 1800)
|
|
||||||
|
|
||||||
id_offerer: int = self.node_c_id
|
|
||||||
id_bidder: int = self.node_b_id
|
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
|
||||||
ci_from = swap_clients[id_offerer].ci(coin_from)
|
|
||||||
ci_to = swap_clients[id_bidder].ci(coin_to)
|
|
||||||
|
|
||||||
jsw = read_json_api(1800 + id_offerer, "wallets")
|
|
||||||
balance_from_before: float = self.getBalance(jsw, coin_from)
|
|
||||||
|
|
||||||
amt_swap: int = ci_from.make_int(balance_from_before, r=1)
|
|
||||||
rate_swap: int = ci_to.make_int(2.0, r=1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
offer_id = swap_clients[id_offerer].postOffer(
|
|
||||||
coin_from,
|
|
||||||
coin_to,
|
|
||||||
amt_swap,
|
|
||||||
rate_swap,
|
|
||||||
amt_swap,
|
|
||||||
SwapTypes.XMR_SWAP,
|
|
||||||
auto_accept_bids=True,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
assert "Insufficient funds" in str(e)
|
|
||||||
else:
|
|
||||||
assert False, "Should fail"
|
|
||||||
amt_swap -= ci_from.make_int(1)
|
|
||||||
offer_id = swap_clients[id_offerer].postOffer(
|
|
||||||
coin_from,
|
|
||||||
coin_to,
|
|
||||||
amt_swap,
|
|
||||||
rate_swap,
|
|
||||||
amt_swap,
|
|
||||||
SwapTypes.XMR_SWAP,
|
|
||||||
auto_accept_bids=True,
|
|
||||||
)
|
|
||||||
wait_for_offer(test_delay_event, swap_clients[id_bidder], offer_id)
|
|
||||||
|
|
||||||
# First bid should work
|
|
||||||
bid_id = swap_clients[id_bidder].postXmrBid(offer_id, amt_swap)
|
|
||||||
wait_for_bid(
|
|
||||||
test_delay_event,
|
|
||||||
swap_clients[id_offerer],
|
|
||||||
bid_id,
|
|
||||||
(BidStates.BID_ACCEPTED, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED),
|
|
||||||
wait_for=40,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Should be out of funds for second bid (over remaining offer value causes a hard auto accept fail)
|
|
||||||
bid_id = swap_clients[id_bidder].postXmrBid(offer_id, amt_swap)
|
|
||||||
wait_for_bid(
|
|
||||||
test_delay_event,
|
|
||||||
swap_clients[id_offerer],
|
|
||||||
bid_id,
|
|
||||||
BidStates.BID_AACCEPT_FAIL,
|
|
||||||
wait_for=40,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
swap_clients[id_offerer].acceptBid(bid_id)
|
|
||||||
except Exception as e:
|
|
||||||
assert "Insufficient funds" in str(e)
|
|
||||||
else:
|
|
||||||
assert False, "Should fail"
|
|
||||||
|
|
||||||
def test_08_insufficient_funds_rev(self):
|
def test_08_insufficient_funds_rev(self):
|
||||||
tla_from = self.test_coin_from.name
|
self.do_test_08_insufficient_funds(Coins.XMR, self.test_coin_from)
|
||||||
logging.info("---------- Test {} Insufficient Funds (reverse)".format(tla_from))
|
|
||||||
swap_clients = self.swap_clients
|
|
||||||
coin_from = Coins.XMR
|
|
||||||
coin_to = self.test_coin_from
|
|
||||||
|
|
||||||
self.prepare_balance(coin_to, 10.0, 1802, 1800)
|
|
||||||
|
|
||||||
id_offerer: int = self.node_b_id
|
|
||||||
id_bidder: int = self.node_c_id
|
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
|
||||||
ci_from = swap_clients[id_offerer].ci(coin_from)
|
|
||||||
ci_to = swap_clients[id_bidder].ci(coin_to)
|
|
||||||
|
|
||||||
jsw = read_json_api(1800 + id_bidder, "wallets")
|
|
||||||
balance_to_before: float = self.getBalance(jsw, coin_to)
|
|
||||||
|
|
||||||
amt_swap: int = ci_from.make_int(balance_to_before, r=1)
|
|
||||||
rate_swap: int = ci_to.make_int(1.0, r=1)
|
|
||||||
|
|
||||||
amt_swap -= 1
|
|
||||||
offer_id = swap_clients[id_offerer].postOffer(
|
|
||||||
coin_from,
|
|
||||||
coin_to,
|
|
||||||
amt_swap,
|
|
||||||
rate_swap,
|
|
||||||
amt_swap,
|
|
||||||
SwapTypes.XMR_SWAP,
|
|
||||||
auto_accept_bids=True,
|
|
||||||
)
|
|
||||||
wait_for_offer(test_delay_event, swap_clients[id_bidder], offer_id)
|
|
||||||
|
|
||||||
bid_id = swap_clients[id_bidder].postXmrBid(offer_id, amt_swap)
|
|
||||||
|
|
||||||
event = wait_for_event(
|
|
||||||
test_delay_event,
|
|
||||||
swap_clients[id_bidder],
|
|
||||||
Concepts.BID,
|
|
||||||
bid_id,
|
|
||||||
event_type=EventLogTypes.ERROR,
|
|
||||||
wait_for=60,
|
|
||||||
)
|
|
||||||
assert "Insufficient funds" in event.event_msg
|
|
||||||
|
|
||||||
wait_for_bid(
|
|
||||||
test_delay_event,
|
|
||||||
swap_clients[id_bidder],
|
|
||||||
bid_id,
|
|
||||||
BidStates.BID_ERROR,
|
|
||||||
sent=True,
|
|
||||||
wait_for=20,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestBTC(BasicSwapTest):
|
class TestBTC(BasicSwapTest):
|
||||||
|
|
Loading…
Reference in a new issue