xmr: Ensure incoming transfers are unlocked.

2024-05-20_merge
tecnovert 2 years ago
parent 789cd0f6ab
commit a250daca8b
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
  1. 2
      basicswap/__init__.py
  2. 7
      basicswap/basicswap.py
  3. 1
      basicswap/basicswap_util.py
  4. 2
      basicswap/interface/btc.py
  5. 2
      basicswap/interface/part.py
  6. 19
      basicswap/interface/xmr.py
  7. 5
      docker/production/notes.md
  8. 8
      tests/basicswap/common.py
  9. 37
      tests/basicswap/test_xmr.py

@ -1,3 +1,3 @@
name = "basicswap"
__version__ = "0.11.50"
__version__ = "0.11.51"

@ -4674,12 +4674,17 @@ class BasicSwap(BaseApp):
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
return
unlock_time = 0
if bid.debug_ind == DebugTypes.CREATE_INVALID_COIN_B_LOCK:
bid.amount_to -= int(bid.amount_to * 0.1)
self.log.debug('XMR bid %s: Debug %d - Reducing lock b txn amount by 10%% to %s.', bid_id.hex(), bid.debug_ind, ci_to.format_amount(bid.amount_to))
self.logBidEvent(bid.bid_id, EventLogTypes.DEBUG_TWEAK_APPLIED, 'ind {}'.format(bid.debug_ind), session)
if bid.debug_ind == DebugTypes.SEND_LOCKED_XMR:
unlock_time = 10000
self.log.debug('XMR bid %s: Debug %d - Sending locked XMR.', bid_id.hex(), bid.debug_ind)
self.logBidEvent(bid.bid_id, EventLogTypes.DEBUG_TWEAK_APPLIED, 'ind {}'.format(bid.debug_ind), session)
try:
b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate)
b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate, unlock_time=unlock_time)
except Exception as ex:
error_msg = 'publishBLockTx failed for bid {} with error {}'.format(bid_id.hex(), str(ex))
num_retries = self.countBidEvents(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, session)

@ -184,6 +184,7 @@ class DebugTypes(IntEnum):
MAKE_INVALID_PTX = auto()
DONT_SPEND_ITX = auto()
SKIP_LOCK_TX_REFUND = auto()
SEND_LOCKED_XMR = auto()
def strOfferState(state):

@ -993,7 +993,7 @@ class BTCInterface(CoinInterface):
def encodeSharedAddress(self, Kbv, Kbs):
return self.pubkey_to_segwit_address(Kbs)
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for=10, unlock_time=0):
b_lock_tx = self.createBLockTx(Kbs, output_amount)
b_lock_tx = self.fundTx(b_lock_tx, feerate)

@ -639,7 +639,7 @@ class PARTInterfaceAnon(PARTInterface):
def coin_name(self):
return super().coin_name() + ' Anon'
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for=10, unlock_time=0):
sx_addr = self.formatStealthAddress(Kbv, Kbs)
self._log.debug('sx_addr: {}'.format(sx_addr))

@ -252,13 +252,13 @@ class XMRInterface(CoinInterface):
def encodeSharedAddress(self, Kbv, Kbs):
return xmr_util.encode_address(Kbv, Kbs)
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for=10):
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for=10, unlock_time=0):
with self._mx_wallet:
self.openWallet(self._wallet_filename)
shared_addr = xmr_util.encode_address(Kbv, Kbs)
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}]}
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}], 'unlock_time': unlock_time}
if self._fee_priority > 0:
params['priority'] = self._fee_priority
rv = self.rpc_wallet_cb('transfer', params)
@ -316,15 +316,20 @@ class XMRInterface(CoinInterface):
# and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
'''
params = {'transfer_type': 'available'}
rv = self.rpc_wallet_cb('incoming_transfers', params)
if 'transfers' in rv:
for transfer in rv['transfers']:
transfers = self.rpc_wallet_cb('incoming_transfers', params)
rv = None
if 'transfers' in transfers:
for transfer in transfers['transfers']:
if not transfer['unlocked']:
self._log.warning('Coin b lock txn is locked: {}'.format(transfer['tx_hash']))
rv = -1
continue
if transfer['amount'] == cb_swap_value:
return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': 0 if 'block_height' not in transfer else transfer['block_height']}
else:
self._log.warning('Incorrect amount detected for coin b lock txn: {}'.format(transfer['tx_hash']))
return -1
return None
rv = -1
return rv
def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
with self._mx_wallet:

@ -3,6 +3,11 @@
This will setup Basicswap so that each coin runs in it's own container.
Install dependencies:
sudo apt install basez docker-compose
Copy and edit .env config:
cp example.env .env

@ -159,7 +159,7 @@ def wait_for_bid_tx_state(delay_event, swap_client, bid_id, initiate_state, part
raise ValueError('wait_for_bid_tx_state timed out.')
def wait_for_event(delay_event, swap_client, linked_type, linked_id, wait_for=20):
def wait_for_event(delay_event, swap_client, linked_type, linked_id, event_type=None, wait_for=20):
logging.info('wait_for_event')
for i in range(wait_for):
@ -167,8 +167,10 @@ def wait_for_event(delay_event, swap_client, linked_type, linked_id, wait_for=20
raise ValueError('Test stopped.')
delay_event.wait(1)
rv = swap_client.getEvents(linked_type, linked_id)
if len(rv) > 0:
return rv
for event in rv:
if event_type is None or event.event_type == event_type:
return event
raise ValueError('wait_for_event timed out.')

@ -30,6 +30,7 @@ from basicswap.basicswap import (
)
from basicswap.basicswap_util import (
TxLockTypes,
EventLogTypes,
)
from basicswap.util import (
COIN,
@ -1037,8 +1038,8 @@ class Test(BaseTest):
}
bid_id = swap_clients[1].postBid(offer_id, below_min_bid, extra_options=extra_bid_options)
events = wait_for_event(test_delay_event, swap_clients[0], Concepts.NETWORK_MESSAGE, bid_id)
assert ('Bid amount below minimum' in events[0].event_msg)
event = wait_for_event(test_delay_event, swap_clients[0], Concepts.NETWORK_MESSAGE, bid_id)
assert ('Bid amount below minimum' in event.event_msg)
bid_ids = []
for i in range(5):
@ -1047,8 +1048,8 @@ class Test(BaseTest):
# Should fail > max concurrent
test_delay_event.wait(1.0)
bid_id = swap_clients[1].postBid(offer_id, min_bid)
events = wait_for_event(test_delay_event, swap_clients[0], Concepts.AUTOMATION, bid_id)
assert ('Already have 5 bids to complete' in events[0].event_msg)
event = wait_for_event(test_delay_event, swap_clients[0], Concepts.AUTOMATION, bid_id)
assert ('Already have 5 bids to complete' in event.event_msg)
for bid_id in bid_ids:
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
@ -1059,8 +1060,8 @@ class Test(BaseTest):
# Should fail > total value
amt_bid += 1
bid_id = swap_clients[1].postBid(offer_id, amt_bid)
events = wait_for_event(test_delay_event, swap_clients[0], Concepts.AUTOMATION, bid_id)
assert ('Over remaining offer value' in events[0].event_msg)
event = wait_for_event(test_delay_event, swap_clients[0], Concepts.AUTOMATION, bid_id)
assert ('Over remaining offer value' in event.event_msg)
# Should pass
amt_bid -= 1
@ -1219,6 +1220,30 @@ class Test(BaseTest):
node0_blind_after = js_0['blind_balance'] + js_0['blind_unconfirmed']
assert (node0_blind_after < node0_blind_before - amount_from)
def test_13_locked_xmr(self):
logging.info('---------- Test PART to XMR leader recovers coin a lock tx')
swap_clients = self.swap_clients
amt_swap = make_int(random.uniform(0.1, 10.0), scale=8, r=1)
rate_swap = make_int(random.uniform(2.0, 20.0), scale=12, r=1)
offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.SEND_LOCKED_XMR)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_event(test_delay_event, swap_clients[0], Concepts.BID, bid_id, event_type=EventLogTypes.LOCK_TX_B_INVALID, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, sent=True)
swap_clients[0].abandonBid(bid_id)
swap_clients[1].abandonBid(bid_id)
def test_98_withdraw_all(self):
logging.info('---------- Test XMR withdrawal all')
try:

Loading…
Cancel
Save