Browse Source

Handle lost noscript lock transaction.

pull/22/head
tecnovert 2 years ago
parent
commit
2a9e423eaa
No known key found for this signature in database GPG Key ID: 8ED6D8750C4E3F93
  1. 2
      basicswap/__init__.py
  2. 77
      basicswap/basicswap.py
  3. 5
      basicswap/basicswap_util.py
  4. 2
      basicswap/interface/btc.py
  5. 17
      basicswap/interface/dash.py
  6. 46
      basicswap/interface/xmr.py
  7. 12
      basicswap/js_server.py
  8. 18
      basicswap/protocols/xmr_swap_1.py
  9. 22
      basicswap/templates/bid.html
  10. 20
      basicswap/templates/bid_xmr.html
  11. 11
      basicswap/ui/page_offers.py
  12. 8
      basicswap/ui/util.py
  13. 18
      doc/release-notes.md
  14. 20
      tests/basicswap/test_xmr.py

2
basicswap/__init__.py

@ -1,3 +1,3 @@
name = "basicswap"
__version__ = "0.11.53"
__version__ = "0.11.54"

77
basicswap/basicswap.py

@ -3065,6 +3065,33 @@ class BasicSwap(BaseApp):
return sum_unspent
return None
def findTxB(self, ci_to, xmr_swap, bid, session) -> bool:
bid_changed = False
# Have to use findTxB instead of relying on the first seen height to detect chain reorgs
found_tx = ci_to.findTxB(xmr_swap.vkbv, xmr_swap.pkbs, bid.amount_to, ci_to.blocks_confirmed, bid.chain_b_height_start, bid.was_sent)
if isinstance(found_tx, int) and found_tx == -1:
if self.countBidEvents(bid, EventLogTypes.LOCK_TX_B_INVALID, session) < 1:
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_INVALID, 'Detected invalid lock tx B', session)
bid_changed = True
elif found_tx is not None:
if bid.xmr_b_lock_tx is None or not bid.xmr_b_lock_tx.chain_height:
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_SEEN, '', session)
if bid.xmr_b_lock_tx is None:
self.log.debug('Found {} lock tx in chain'.format(ci_to.coin_name()))
xmr_swap.b_lock_tx_id = bytes.fromhex(found_tx['txid'])
bid.xmr_b_lock_tx = SwapTx(
bid_id=bid.bid_id,
tx_type=TxTypes.XMR_SWAP_B_LOCK,
txid=xmr_swap.b_lock_tx_id,
chain_height=found_tx['height'],
)
bid_changed = True
else:
bid.xmr_b_lock_tx.chain_height = found_tx['height']
bid_changed = True
return bid_changed
def checkXmrBidState(self, bid_id, bid, offer):
rv = False
@ -3176,6 +3203,13 @@ class BasicSwap(BaseApp):
rv = True # Remove from swaps_in_progress
elif state == BidStates.XMR_SWAP_FAILED_SWIPED:
rv = True # Remove from swaps_in_progress
elif state == BidStates.XMR_SWAP_FAILED:
if bid.was_sent and bid.xmr_b_lock_tx:
if self.countQueuedActions(session, bid_id, ActionTypes.RECOVER_XMR_SWAP_LOCK_TX_B) < 1:
delay = random.randrange(self.min_delay_event, self.max_delay_event)
self.log.info('Recovering xmr swap chain B lock tx for bid %s in %d seconds', bid_id.hex(), delay)
self.createActionInSession(delay, ActionTypes.RECOVER_XMR_SWAP_LOCK_TX_B, bid_id, session)
session.commit()
elif state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
if bid.xmr_a_lock_tx is None:
return rv
@ -3217,33 +3251,7 @@ class BasicSwap(BaseApp):
session.commit()
elif state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED:
if bid.was_sent and bid.xmr_b_lock_tx is None:
return rv
bid_changed = False
# Have to use findTxB instead of relying on the first seen height to detect chain reorgs
found_tx = ci_to.findTxB(xmr_swap.vkbv, xmr_swap.pkbs, bid.amount_to, ci_to.blocks_confirmed, bid.chain_b_height_start, bid.was_sent)
if isinstance(found_tx, int) and found_tx == -1:
if self.countBidEvents(bid, EventLogTypes.LOCK_TX_B_INVALID, session) < 1:
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_INVALID, 'Detected invalid lock tx B', session)
bid_changed = True
elif found_tx is not None:
if bid.xmr_b_lock_tx is None or not bid.xmr_b_lock_tx.chain_height:
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_SEEN, '', session)
if bid.xmr_b_lock_tx is None:
self.log.debug('Found {} lock tx in chain'.format(ci_to.coin_name()))
xmr_swap.b_lock_tx_id = bytes.fromhex(found_tx['txid'])
bid.xmr_b_lock_tx = SwapTx(
bid_id=bid_id,
tx_type=TxTypes.XMR_SWAP_B_LOCK,
txid=xmr_swap.b_lock_tx_id,
chain_height=found_tx['height'],
)
bid_changed = True
else:
bid.xmr_b_lock_tx.chain_height = found_tx['height']
bid_changed = True
bid_changed = self.findTxB(ci_to, xmr_swap, bid, session)
if bid.xmr_b_lock_tx and bid.xmr_b_lock_tx.chain_height is not None and bid.xmr_b_lock_tx.chain_height > 0:
chain_height = ci_to.getChainHeight()
@ -4715,6 +4723,14 @@ class BasicSwap(BaseApp):
ci_from = self.ci(Coins(offer.coin_from))
ci_to = self.ci(Coins(offer.coin_to))
if self.findTxB(ci_to, xmr_swap, bid, session) is True:
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
return
if bid.xmr_b_lock_tx:
self.log.warning('Coin B lock tx {} exists for xmr bid {}'.format(bid.xmr_b_lock_tx.b_lock_tx_id, bid_id.hex()))
return
if bid.debug_ind == DebugTypes.BID_STOP_AFTER_COIN_A_LOCK:
self.log.debug('XMR bid %s: Stalling bid for testing: %d.', bid_id.hex(), bid.debug_ind)
bid.setState(BidStates.BID_STALLED_FOR_TEST)
@ -4723,7 +4739,7 @@ class BasicSwap(BaseApp):
return
unlock_time = 0
if bid.debug_ind == DebugTypes.CREATE_INVALID_COIN_B_LOCK:
if bid.debug_ind in (DebugTypes.CREATE_INVALID_COIN_B_LOCK, DebugTypes.B_LOCK_TX_MISSED_SEND):
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)
@ -4731,8 +4747,13 @@ class BasicSwap(BaseApp):
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, unlock_time=unlock_time)
if bid.debug_ind == DebugTypes.B_LOCK_TX_MISSED_SEND:
self.log.debug('XMR bid %s: Debug %d - Losing xmr lock tx %s.', bid_id.hex(), bid.debug_ind, b_lock_tx_id.hex())
self.logBidEvent(bid.bid_id, EventLogTypes.DEBUG_TWEAK_APPLIED, 'ind {}'.format(bid.debug_ind), session)
raise TemporaryError('Fail for debug event')
except Exception as ex:
if self.debug:
self.log.error(traceback.format_exc())

5
basicswap/basicswap_util.py

@ -189,6 +189,7 @@ class DebugTypes(IntEnum):
DONT_SPEND_ITX = auto()
SKIP_LOCK_TX_REFUND = auto()
SEND_LOCKED_XMR = auto()
B_LOCK_TX_MISSED_SEND = auto()
def strOfferState(state):
@ -322,8 +323,6 @@ def getLockName(lock_type):
def describeEventEntry(event_type, event_msg):
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
return 'Failed to publish lock tx B'
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
return 'Failed to publish lock tx B'
if event_type == EventLogTypes.LOCK_TX_A_PUBLISHED:
@ -456,4 +455,6 @@ def isActiveBidState(state):
return True
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
return True
if state == BidStates.XMR_SWAP_FAILED:
return True
return False

2
basicswap/interface/btc.py

@ -1186,7 +1186,7 @@ class BTCInterface(CoinInterface):
return True if address_hash == pubkey_hash else False
def showLockTransfers(self, Kbv, Kbs):
def showLockTransfers(self, kbv, Kbs, restore_height):
raise ValueError('Unimplemented')
def getLockTxSwapOutputValue(self, bid, xmr_swap):

17
basicswap/interface/dash.py

@ -20,12 +20,18 @@ 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)
self._wallet_passphrase = ''
def seedToMnemonic(self, key):
return Mnemonic('english').to_mnemonic(key)
def initialiseWallet(self, key):
words = self.seedToMnemonic(key)
self.rpc_callback('upgradetohd', [words, ])
mnemonic_passphrase = ''
self.rpc_callback('upgradetohd', [words, mnemonic_passphrase, self._wallet_passphrase])
def decodeAddress(self, address):
return decodeAddress(address)[1:]
@ -70,3 +76,12 @@ class DASHInterface(BTCInterface):
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None
def unlockWallet(self, password: str):
super().unlockWallet(password)
# Store password for initialiseWallet
self._wallet_passphrase = password
def lockWallet(self):
super().lockWallet()
self._wallet_passphrase = ''

46
basicswap/interface/xmr.py

@ -5,7 +5,6 @@
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import json
import logging
@ -100,8 +99,7 @@ class XMRInterface(CoinInterface):
try:
# Can't reopen the same wallet in windows, !is_keys_file_locked()
if os.name == 'nt':
self.rpc_wallet_cb('close_wallet')
self.rpc_wallet_cb('close_wallet')
except Exception:
pass
self.rpc_wallet_cb('open_wallet', params)
@ -263,6 +261,7 @@ class XMRInterface(CoinInterface):
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for: int = 10, unlock_time: int = 0) -> bytes:
with self._mx_wallet:
self.openWallet(self._wallet_filename)
self.rpc_wallet_cb('refresh')
shared_addr = xmr_util.encode_address(Kbv, Kbs)
@ -276,8 +275,8 @@ class XMRInterface(CoinInterface):
if self._sc.debug:
i = 0
while not self._sc.delay_event.is_set():
params = {'out': True, 'pending': True, 'failed': True, 'pool': True, }
rv = self.rpc_wallet_cb('get_transfers', params)
gt_params = {'out': True, 'pending': True, 'failed': True, 'pool': True, }
rv = self.rpc_wallet_cb('get_transfers', gt_params)
self._log.debug('get_transfers {}'.format(dumpj(rv)))
if 'pending' not in rv:
break
@ -292,11 +291,6 @@ class XMRInterface(CoinInterface):
Kbv = self.getPubkey(kbv)
address_b58 = xmr_util.encode_address(Kbv, Kbs)
try:
self.rpc_wallet_cb('close_wallet')
except Exception as e:
self._log.warning('close_wallet failed %s', str(e))
kbv_le = kbv[::-1]
params = {
'restore_height': restore_height,
@ -328,10 +322,14 @@ class XMRInterface(CoinInterface):
rv = None
if 'transfers' in transfers:
for transfer in transfers['transfers']:
# unlocked <- wallet->is_transfer_unlocked() checks unlock_time and CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
if not transfer['unlocked']:
self._log.warning('Coin b lock txn is locked: {}'.format(transfer['tx_hash']))
rv = -1
continue
full_tx = self.rpc_wallet_cb('get_transfer_by_txid', {'txid': transfer['tx_hash']})
unlock_time = full_tx['transfer']['unlock_time']
if unlock_time != 0:
self._log.warning('Coin b lock txn is locked: {}, unlock_time {}'.format(transfer['tx_hash'], unlock_time))
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:
@ -367,11 +365,6 @@ class XMRInterface(CoinInterface):
Kbs = self.getPubkey(kbs)
address_b58 = xmr_util.encode_address(Kbv, Kbs)
try:
self.rpc_wallet_cb('close_wallet')
except Exception as e:
self._log.warning('close_wallet failed %s', str(e))
wallet_filename = address_b58 + '_spend'
params = {
@ -446,16 +439,29 @@ class XMRInterface(CoinInterface):
rv = self.rpc_wallet_cb('transfer', params)
return rv['tx_hash']
def showLockTransfers(self, Kbv, Kbs):
def showLockTransfers(self, kbv, Kbs, restore_height):
with self._mx_wallet:
try:
Kbv = self.getPubkey(kbv)
address_b58 = xmr_util.encode_address(Kbv, Kbs)
wallet_file = address_b58 + '_spend'
try:
self.openWallet(wallet_file)
except Exception:
wallet_file = address_b58
self.openWallet(wallet_file)
try:
self.openWallet(wallet_file)
except Exception:
self._log.info(f'showLockTransfers trying to create wallet for address {address_b58}.')
kbv_le = kbv[::-1]
params = {
'restore_height': restore_height,
'filename': address_b58,
'address': address_b58,
'viewkey': b2h(kbv_le),
}
self.createWallet(params)
self.openWallet(address_b58)
self.rpc_wallet_cb('refresh')

12
basicswap/js_server.py

@ -168,11 +168,11 @@ def js_offers(self, url_split, post_string, is_json, sent=False):
filters['limit'] = int(get_data_entry(post_data, 'limit'))
assert (filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
offers = self.server.swap_client.listOffers(sent, filters)
offers = swap_client.listOffers(sent, filters)
rv = []
for o in offers:
ci_from = self.server.swap_client.ci(o.coin_from)
ci_to = self.server.swap_client.ci(o.coin_to)
ci_from = swap_client.ci(o.coin_from)
ci_to = swap_client.ci(o.coin_to)
rv.append({
'addr_from': o.addr_from,
'addr_to': o.addr_to,
@ -494,6 +494,10 @@ def js_lock(self, url_split, post_string, is_json):
return bytes(json.dumps({'success': True}), 'UTF-8')
def js_404(self, url_split, post_string, is_json):
return bytes(json.dumps({'Error': 'path unknown'}), 'UTF-8')
def js_help(self, url_split, post_string, is_json):
# TODO: Add details and examples
commands = []
@ -528,5 +532,5 @@ pages = {
def js_url_to_function(url_split):
if len(url_split) > 2:
return pages.get(url_split[2], js_index)
return pages.get(url_split[2], js_404)
return js_index

18
basicswap/protocols/xmr_swap_1.py

@ -91,6 +91,24 @@ def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
return ci_to.encodeKey(swap_client.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, key_type, True if offer.coin_to == Coins.XMR else False))
def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
ci_from = swap_client.ci(offer.coin_from)
ci_to = swap_client.ci(offer.coin_to)
if bid.was_sent:
if xmr_swap.a_lock_refund_spend_tx:
af_lock_refund_spend_tx_sig = ci_from.extractFollowerSig(xmr_swap.a_lock_refund_spend_tx)
kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl)
return ci_to.encodeKey(kbsl)
else:
if xmr_swap.a_lock_spend_tx:
al_lock_spend_tx_sig = ci_from.extractLeaderSig(xmr_swap.a_lock_spend_tx)
kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, al_lock_spend_tx_sig, xmr_swap.pkasf)
return ci_to.encodeKey(kbsf)
return None
class XmrSwapInterface(ProtocolInterface):
swap_type = SwapTypes.XMR_SWAP

22
basicswap/templates/bid.html

@ -83,7 +83,7 @@
<td class="py-4 px-6 bold">Swap</td>
<td>
<div class="content flex py-2">
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
<svg aria-hidden="true " class="w-5 h-5 ml-3 mr-3" fill="currentColor " viewBox="0 0 20 20 " xmlns="http://www.w3.org/2000/svg ">
<path fill-rule="evenodd " d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z " clip-rule="evenodd "></path></svg>
<span class="bold">{{ data.amt_from }} {{ data.ticker_from }}</span>
@ -95,7 +95,7 @@
<td class="py-4 px-6 bold">Swap</td>
<td>
<div class="content flex py-2">
<span class="bold">{{ data.amt_from }} {{ data.ticker_from }}</span>
<span class="bold">{{ data.amt_from }} {{ data.ticker_from }}</span>
<svg aria-hidden="true " class="w-5 h-5 ml-3 mr-3" fill="currentColor " viewBox="0 0 20 20 " xmlns="http://www.w3.org/2000/svg ">
<path fill-rule="evenodd " d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z " clip-rule="evenodd "></path></svg>
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
@ -264,7 +264,7 @@
<div>{{ s[0] | formatts }}</div>
</div>
</td>
</tr>
</tr>
{% endfor %}
</table>
</div>
@ -306,7 +306,7 @@
<form method="post">
<div class="p-6 pt-10 bg-white bg-opacity-60 rounded-b-md">
<div class="w-full md:w-0/12">
<div class="flex flex-wrap justify-end -m-1.5">
<div class="flex flex-wrap justify-end -m-1.5">
{% if edit_bid %}
<table class="mt-1">
<tr>
@ -319,11 +319,11 @@
<select class="appearance-none bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" name="new_state">
{% for s in data.bid_states %}
<option value="{{ s[0] }}" {% if data.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
{% endfor %}
{% endfor %}
</select>
</div>
</td>
</tr>
</tr>
{% if data.debug_ui == true %}
<tr>
<td class="py-6 pr-2 bold">Debug Option:</td>
@ -331,7 +331,7 @@
<div class="relative">
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
</svg>
</svg>
<select class="appearance-none bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" name="debugind">
<option{% if data.debug_ind=="-1" %} selected{% endif %} value="-1">None</option>
{% for a in data.debug_options %}
@ -349,7 +349,7 @@
</div>
<div class="w-full md:w-auto p-1.5">
<button name="edit_bid_submit" value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Submit Edit Bid </button>
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Submit Edit </button>
</div>
{% else %}
{% if data.show_bidder_seq_diagram %}
@ -393,14 +393,14 @@
{% endif %}
<div class="w-full md:w-auto p-1.5">
<button name="edit_bid" type="submit" value="Edit Bid" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Edit bit </button>
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Edit bid </button>
</div>
{% endif %}
{% if data.was_received == 'True' %}
<div class="w-full md:w-auto p-1.5">
<button name="accept_bid" value="Accept Bid" type="submit" onclick='return confirmPopup("Accept");' class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round" ><polyline points=" 6,12 10,16 18,8 " stroke="#ffffff"></polyline> <circle cx="12" cy="12" r="11"></circle></g></svg> Accept Bid </button>
</div>
</div>
{% endif %}
</div>
</div>
@ -451,4 +451,4 @@
</div>
{% include 'footer.html' %}
</body>
</html>
</html>

20
basicswap/templates/bid_xmr.html

@ -82,7 +82,7 @@
<td class="py-4 px-6 bold">Swap</td>
<td>
<div class="content flex py-2">
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
<svg aria-hidden="true " class="w-5 h-5 ml-3 mr-3" fill="currentColor " viewBox="0 0 20 20 " xmlns="http://www.w3.org/2000/svg ">
<path fill-rule="evenodd " d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z " clip-rule="evenodd "></path></svg>
<span class="bold">{{ data.amt_from }} {{ data.ticker_from }}</span>
@ -305,10 +305,16 @@
{% endif %}
{% if data.xmr_b_half_privatekey %}
<tr class="bg-white border-t hover:bg-gray-50">
<td class="py-4 px-6 bold">Key Half:</td>
<td class="py-4 px-6 bold">Key Half (WARNING key data!):</td>
<td class="py-4 monospace">{{ data.xmr_b_half_privatekey }}</td>
</tr>
{% endif %}
{% endif %}
{% if data.xmr_b_half_privatekey_remote %}
<tr class="bg-white border-t hover:bg-gray-50">
<td class="py-4 px-6 bold">Remote Key Half:</td>
<td class="py-4 monospace">{{ data.xmr_b_half_privatekey_remote }}</td>
</tr>
{% endif %}
</table>
</div>
</div>
@ -475,7 +481,7 @@
{% if data.debug_ui == true %}
<tr>
<td class="py-6 pr-2 bold"">Debug Option</td>
<td>
<td>
<div class="relative">
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
@ -503,7 +509,7 @@
</div>
<div class="w-full md:w-auto p-1.5">
<button name="edit_bid_submit" value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Submit Edit Bid </button>
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Submit Edit </button>
</div>
{% else %}
{% if data.show_bidder_seq_diagram %}
@ -547,7 +553,7 @@
{% endif %}
<div class="w-full md:w-auto p-1.5">
<button name="edit_bid" type="submit" value="Edit Bid" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Edit bit </button>
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" ><line x1="2" y1="23" x2="22" y2="23" stroke="#556987"></line> <line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line> <polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon></g></svg> Edit bid </button>
</div>
{% endif %}
{% if data.was_received == 'True' %}
@ -572,4 +578,4 @@
</div>
{% include 'footer.html' %}
</body>
</html>
</html>

11
basicswap/ui/page_offers.py

@ -213,7 +213,16 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
def postNewOfferFromParsed(swap_client, parsed_data):
swap_type = SwapTypes.SELLER_FIRST
if parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
if swap_type in parsed_data:
str_swap_type = parsed_data['swap_type'].lower()
if str_swap_type == 'seller_first':
swap_type = SwapTypes.SELLER_FIRST
elif str_swap_type == 'xmr_swap':
swap_type = SwapTypes.XMR_SWAP
else:
raise ValueError('Unknown swap type')
elif parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
swap_type = SwapTypes.XMR_SWAP
if swap_client.coin_clients[parsed_data['coin_from']]['use_csv'] and swap_client.coin_clients[parsed_data['coin_to']]['use_csv']:

8
basicswap/ui/util.py

@ -28,7 +28,7 @@ from basicswap.basicswap_util import (
getLastBidState,
)
from basicswap.protocols.xmr_swap_1 import getChainBSplitKey
from basicswap.protocols.xmr_swap_1 import getChainBSplitKey, getChainBRemoteSplitKey
PAGE_LIMIT = 50
invalid_coins_from = (Coins.XMR, Coins.PART_ANON)
@ -302,12 +302,16 @@ def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_b
if swap_client.debug_ui:
try:
data['xmr_b_half_privatekey'] = getChainBSplitKey(swap_client, bid, xmr_swap, offer)
remote_split_key = getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer)
if remote_split_key:
data['xmr_b_half_privatekey_remote'] = remote_split_key
except Exception as e:
swap_client.log.error(traceback.format_exc())
if show_lock_transfers:
if xmr_swap.pkbs:
data['lock_transfers'] = json.dumps(ci_to.showLockTransfers(xmr_swap.pkbv, xmr_swap.pkbs), indent=4)
data['lock_transfers'] = json.dumps(ci_to.showLockTransfers(xmr_swap.vkbv, xmr_swap.pkbs, bid.chain_b_height_start), indent=4)
else:
data['lock_transfers'] = 'Shared address not yet known.'
else:

18
doc/release-notes.md

@ -3,6 +3,24 @@
==============
0.0.54
==============
- If the XMR daemon is busy the wallet can fail a transfer, later sending the tx unknown to bsx.
- Check for existing transfers before trying to send the chain b lock tx.
- Check for transfers in XMR_SWAP_SCRIPT_COIN_LOCKED state when bid is sent.
- Continually try refund noscript lock tx in XMR_SWAP_FAILED state.
- showLockTransfers will attempt to create a wallet if none exists.
- tests:
- Add B_LOCK_TX_MISSED_SEND debug event and test.
- Store the Dash wallet password in memory for use in upgradetohd
- Remove false positive warning. Check for unlock_time transfer is not unlocked.
- ui:
- Add 'Remote Key Half' to Show More Info section (with debug_ui on)
- api:
- An unknown path will return an error instead of the default/index data.
0.0.32
==============

20
tests/basicswap/test_xmr.py

@ -1318,6 +1318,26 @@ class Test(BaseTest):
assert (txin['txid'] == itx_after['vin'][i]['txid'])
assert (txin['vout'] == itx_after['vin'][i]['vout'])
def test_15_missed_xmr_send(self):
logging.info('---------- Test PART to XMR B lock tx is lost')
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,
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=28)
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.B_LOCK_TX_MISSED_SEND)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=1800)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=1800, sent=True)
def test_98_withdraw_all(self):
logging.info('---------- Test XMR withdrawal all')
try:

Loading…
Cancel
Save