From e63014026df5dddecdc98081a041f967b745d8b5 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Thu, 9 May 2024 01:02:21 +0200 Subject: [PATCH] Prefer to set bid amounts from offer amount-to instead of rate. --- basicswap/basicswap.py | 60 +++++++++++++++++++++++-------------- tests/basicswap/test_run.py | 25 ++++++++++++++++ tests/basicswap/test_xmr.py | 17 +++++++++-- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index f9c0a2e..07c1409 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -2250,6 +2250,40 @@ class BasicSwap(BaseApp): if session is None: self.closeSession(use_session, commit=False) + def setBidAmounts(self, amount: int, offer, extra_options, ci_from) -> (int, int, int): + if 'amount_to' in extra_options: + amount_to: int = extra_options['amount_to'] + elif 'bid_rate' in extra_options: + bid_rate = extra_options['bid_rate'] + amount_to: int = int((amount * bid_rate) // ci_from.COIN()) + if not offer.rate_negotiable: + self.log.warning('Fixed-rate offer bids should set amount to instead of bid rate.') + else: + amount_to: int = offer.amount_to + bid_rate: int = ci_from.make_int(amount_to / amount, r=1) + + if offer.amount_negotiable and not offer.rate_negotiable: + if bid_rate != offer.rate and extra_options.get('adjust_amount_for_rate', True): + self.log.debug('Attempting to reduce amount to match offer rate.') + for i in range(100): + test_amount = amount - i + test_amount_to: int = int((test_amount * offer.rate) // ci_from.COIN()) + test_bid_rate: int = ci_from.make_int(test_amount_to / test_amount, r=1) + if test_bid_rate != offer.rate: + test_amount_to -= 1 + test_bid_rate: int = ci_from.make_int(test_amount_to / test_amount, r=1) + if test_bid_rate == offer.rate: + if amount != test_amount: + self.log.info('Reducing bid amount-from from {} to {} to match offer rate.'.format(amount, test_amount)) + elif amount_to != test_amount_to: + # Only show on first loop iteration (amount from unchanged) + self.log.info('Reducing bid amount-to from {} to {} to match offer rate.'.format(amount_to, test_amount_to)) + amount = test_amount + amount_to = test_amount_to + bid_rate = test_bid_rate + break + return amount, amount_to, bid_rate + def postBid(self, offer_id: bytes, amount: int, addr_send_from: str = None, extra_options={}) -> bytes: # Bid to send bid.amount * bid.rate of coin_to in exchange for bid.amount of coin_from self.log.debug('postBid for offer: %s', offer_id.hex()) @@ -2274,13 +2308,7 @@ class BasicSwap(BaseApp): ci_from = self.ci(coin_from) ci_to = self.ci(coin_to) - if 'amount_to' in extra_options: - amount_to: int = extra_options['amount_to'] - bid_rate: int = ci_from.make_int(amount_to / amount, r=1) - else: - bid_rate = extra_options.get('bid_rate', offer.rate) - amount_to: int = int((amount * bid_rate) // ci_from.COIN()) - + amount, amount_to, bid_rate = self.setBidAmounts(amount, offer, extra_options, ci_from) self.validateBidAmount(offer, amount, bid_rate) self.mxDB.acquire() @@ -2294,8 +2322,6 @@ class BasicSwap(BaseApp): msg_buf.amount = amount # amount of coin_from msg_buf.amount_to = amount_to - amount_to = int((msg_buf.amount * bid_rate) // ci_from.COIN()) - now: int = self.getTime() if offer.swap_type == SwapTypes.SELLER_FIRST: proof_addr, proof_sig, proof_utxos = self.getProofOfFunds(coin_to, amount_to, offer_id) @@ -2642,15 +2668,7 @@ class BasicSwap(BaseApp): valid_for_seconds: int = extra_options.get('valid_for_seconds', 60 * 10) - if 'amount_to' in extra_options: - amount_to: int = extra_options['amount_to'] - bid_rate: int = ci_from.make_int(amount_to / amount, r=1) - elif 'bid_rate' in extra_options: - bid_rate: int = extra_options.get('bid_rate', offer.rate) - amount_to: int = int((int(amount) * bid_rate) // ci_from.COIN()) - else: - amount_to: int = offer.amount_to - bid_rate: int = ci_from.make_int(amount_to / amount, r=1) + amount, amount_to, bid_rate = self.setBidAmounts(amount, offer, extra_options, ci_from) bid_created_at: int = self.getTime() if offer.swap_type != SwapTypes.XMR_SWAP: @@ -3180,9 +3198,7 @@ class BasicSwap(BaseApp): return None ci = self.ci(coin_to) - amount_to = bid.amount_to - # Check required? - assert (amount_to == (bid.amount * bid.rate) // self.ci(offer.coin_from).COIN()) + amount_to: int = bid.amount_to if bid.debug_ind == DebugTypes.MAKE_INVALID_PTX: amount_to -= 1 @@ -3680,7 +3696,7 @@ class BasicSwap(BaseApp): self.saveBidInSession(bid_id, bid, session, xmr_swap) session.commit() - if TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns: + if TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns and BidStates(bid.state) != BidStates.BID_STALLED_FOR_TEST: try: txid = ci_from.publishTx(xmr_swap.a_lock_refund_swipe_tx) self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_A_REFUND_SWIPE_TX_PUBLISHED, '', session) diff --git a/tests/basicswap/test_run.py b/tests/basicswap/test_run.py index 0c88c48..fc10488 100644 --- a/tests/basicswap/test_run.py +++ b/tests/basicswap/test_run.py @@ -731,6 +731,31 @@ class Test(BaseTest): assert (txin['txid'] == itx_after['vin'][i]['txid']) assert (txin['vout'] == itx_after['vin'][i]['vout']) + def test_15_variable_amount(self): + logging.info('---------- Test fixed rate variable amount offer') + swap_clients = self.swap_clients + + swap_value = 86474957 + offer_rate = 996774992 + extra_options = {'amount_negotiable': True, 'rate_negotiable': False} + offer_id = swap_clients[1].postOffer(Coins.PART, Coins.BTC, swap_value, offer_rate, swap_value // 2, SwapTypes.SELLER_FIRST, extra_options=extra_options) + + wait_for_offer(test_delay_event, swap_clients[0], offer_id) + offer = swap_clients[0].getOffer(offer_id) + + bid_amount = 86474842 + bid_id = swap_clients[0].postBid(offer_id, bid_amount, extra_options={'bid_rate': offer_rate}) + wait_for_bid(test_delay_event, swap_clients[1], bid_id) + bid = swap_clients[1].getBid(bid_id) + assert (bid.amount == 86474828) + + swap_clients[1].acceptBid(bid_id) + + wait_for_in_progress(test_delay_event, swap_clients[0], bid_id, sent=True) + + wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60) + wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=60) + def pass_99_delay(self): logging.info('Delay') for i in range(60 * 10): diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index 1518657..f94c01d 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -34,8 +34,9 @@ from basicswap.basicswap_util import ( ) from basicswap.util import ( COIN, - make_int, format_amount, + make_int, + TemporaryError ) from basicswap.util.address import ( toWIF, @@ -800,13 +801,23 @@ class Test(BaseTest): lock_tx_b_txid = ci.publishBLockTx(v, S, amount, fee_rate) addr_out = ci.getNewAddress(True) - lock_tx_b_spend_txid = ci.spendBLockTx(lock_tx_b_txid, addr_out, v, s, amount, fee_rate, 0) + for i in range(20): + try: + lock_tx_b_spend_txid = ci.spendBLockTx(lock_tx_b_txid, addr_out, v, s, amount, fee_rate, 0) + break + except Exception as e: + if isinstance(e, TemporaryError): + continue + else: + raise (e) + test_delay_event.wait(1) + lock_tx_b_spend = ci.getTransaction(lock_tx_b_spend_txid) actual_size: int = len(lock_tx_b_spend['txs_as_hex'][0]) // 2 expect_size: int = ci.xmr_swap_b_lock_spend_tx_vsize() assert (expect_size >= actual_size) - assert (expect_size - actual_size < 10) + assert (expect_size - actual_size < 100) # TODO def test_01_part_xmr(self): logging.info('---------- Test PART to XMR')