mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-22 19:49:20 +00:00
Fix recoverNoScriptTxnWithKey for reverse bids.
Some checks are pending
lint / build (3.12) (push) Waiting to run
Some checks are pending
lint / build (3.12) (push) Waiting to run
This commit is contained in:
parent
3345d56f5b
commit
647396611b
11 changed files with 313 additions and 37 deletions
|
@ -7659,6 +7659,9 @@ class BasicSwap(BaseApp):
|
||||||
return
|
return
|
||||||
|
|
||||||
bid = self.getBid(bid_id)
|
bid = self.getBid(bid_id)
|
||||||
|
if bid is None:
|
||||||
|
raise ValueError('Bid not found.')
|
||||||
|
|
||||||
bid.debug_ind = debug_ind
|
bid.debug_ind = debug_ind
|
||||||
|
|
||||||
# Update in memory copy. TODO: Improve
|
# Update in memory copy. TODO: Improve
|
||||||
|
|
|
@ -351,7 +351,7 @@ class XmrSwap(Base):
|
||||||
|
|
||||||
vkbv = sa.Column(sa.LargeBinary) # chain b view private key
|
vkbv = sa.Column(sa.LargeBinary) # chain b view private key
|
||||||
pkbv = sa.Column(sa.LargeBinary) # chain b view public key
|
pkbv = sa.Column(sa.LargeBinary) # chain b view public key
|
||||||
pkbs = sa.Column(sa.LargeBinary) # chain b view spend key
|
pkbs = sa.Column(sa.LargeBinary) # chain b spend public key
|
||||||
|
|
||||||
a_lock_tx = sa.Column(sa.LargeBinary)
|
a_lock_tx = sa.Column(sa.LargeBinary)
|
||||||
a_lock_tx_script = sa.Column(sa.LargeBinary)
|
a_lock_tx_script = sa.Column(sa.LargeBinary)
|
||||||
|
|
|
@ -452,6 +452,8 @@ class XMRInterface(CoinInterface):
|
||||||
if len(txns) > 0:
|
if len(txns) > 0:
|
||||||
txid = txns[0]['txid']
|
txid = txns[0]['txid']
|
||||||
self._log.warning(f'spendBLockTx detected spending tx: {txid}.')
|
self._log.warning(f'spendBLockTx detected spending tx: {txid}.')
|
||||||
|
|
||||||
|
# Should check for address_to, but only the from address is found in the output
|
||||||
if txns[0]['address'] == address_b58:
|
if txns[0]['address'] == address_b58:
|
||||||
return bytes.fromhex(txid)
|
return bytes.fromhex(txid)
|
||||||
|
|
||||||
|
@ -472,7 +474,6 @@ class XMRInterface(CoinInterface):
|
||||||
params['priority'] = self._fee_priority
|
params['priority'] = self._fee_priority
|
||||||
|
|
||||||
rv = self.rpc_wallet('sweep_all', params)
|
rv = self.rpc_wallet('sweep_all', params)
|
||||||
self._log.debug('sweep_all {}'.format(json.dumps(rv)))
|
|
||||||
|
|
||||||
return bytes.fromhex(rv['tx_hash_list'][0])
|
return bytes.fromhex(rv['tx_hash_list'][0])
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
ensure,
|
ensure,
|
||||||
)
|
)
|
||||||
|
@ -41,7 +43,7 @@ def addLockRefundSigs(self, xmr_swap, ci):
|
||||||
|
|
||||||
|
|
||||||
def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, session=None):
|
def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, session=None):
|
||||||
self.log.info('Manually recovering %s', bid_id.hex())
|
self.log.info(f'Manually recovering {bid_id.hex()}')
|
||||||
# Manually recover txn if other key is known
|
# Manually recover txn if other key is known
|
||||||
try:
|
try:
|
||||||
use_session = self.openSession(session)
|
use_session = self.openSession(session)
|
||||||
|
@ -51,37 +53,57 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, session=None):
|
||||||
offer, xmr_offer = self.getXmrOfferFromSession(use_session, bid.offer_id, sent=False)
|
offer, xmr_offer = self.getXmrOfferFromSession(use_session, bid.offer_id, sent=False)
|
||||||
ensure(offer, 'Offer not found: {}.'.format(bid.offer_id.hex()))
|
ensure(offer, 'Offer not found: {}.'.format(bid.offer_id.hex()))
|
||||||
ensure(xmr_offer, 'Adaptor-sig offer not found: {}.'.format(bid.offer_id.hex()))
|
ensure(xmr_offer, 'Adaptor-sig offer not found: {}.'.format(bid.offer_id.hex()))
|
||||||
ci_to = self.ci(offer.coin_to)
|
|
||||||
|
|
||||||
for_ed25519 = True if Coins(offer.coin_to) == Coins.XMR else False
|
# The no-script coin is always the follower
|
||||||
|
reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from)
|
||||||
|
ci_from = self.ci(Coins(offer.coin_from))
|
||||||
|
ci_to = self.ci(Coins(offer.coin_to))
|
||||||
|
ci_leader = ci_to if reverse_bid else ci_from
|
||||||
|
ci_follower = ci_from if reverse_bid else ci_to
|
||||||
|
|
||||||
try:
|
try:
|
||||||
decoded_key_half = ci_to.decodeKey(encoded_key)
|
decoded_key_half = ci_follower.decodeKey(encoded_key)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError('Failed to decode provided key-half: ', str(e))
|
raise ValueError('Failed to decode provided key-half: ', str(e))
|
||||||
|
|
||||||
if bid.was_sent:
|
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
||||||
kbsl = decoded_key_half
|
|
||||||
kbsf = self.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSF, for_ed25519)
|
|
||||||
else:
|
|
||||||
kbsl = self.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSL, for_ed25519)
|
|
||||||
kbsf = decoded_key_half
|
|
||||||
ensure(ci_to.verifyKey(kbsl), 'Invalid kbsl')
|
|
||||||
ensure(ci_to.verifyKey(kbsf), 'Invalid kbsf')
|
|
||||||
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
|
||||||
|
|
||||||
if offer.coin_to == Coins.XMR:
|
localkeyhalf = ci_follower.decodeKey(getChainBSplitKey(self, bid, xmr_swap, offer))
|
||||||
address_to = self.getCachedMainWalletAddress(ci_to, use_session)
|
if was_sent:
|
||||||
|
kbsl = decoded_key_half
|
||||||
|
kbsf = localkeyhalf
|
||||||
else:
|
else:
|
||||||
address_to = self.getCachedStealthAddressForCoin(offer.coin_to, use_session)
|
kbsl = localkeyhalf
|
||||||
|
kbsf = decoded_key_half
|
||||||
|
|
||||||
|
ensure(ci_follower.verifyKey(kbsl), 'Invalid kbsl')
|
||||||
|
ensure(ci_follower.verifyKey(kbsf), 'Invalid kbsf')
|
||||||
|
vkbs = ci_follower.sumKeys(kbsl, kbsf)
|
||||||
|
|
||||||
|
# Ensure summed key matches the expected pubkey
|
||||||
|
summed_pkbs = ci_follower.getPubkey(vkbs)
|
||||||
|
if (summed_pkbs != xmr_swap.pkbs):
|
||||||
|
err_msg: str = 'Summed key does not match expected wallet'
|
||||||
|
have_pk = summed_pkbs.hex()
|
||||||
|
expect_pk = xmr_swap.pkbs.hex()
|
||||||
|
self.log.error(f'{err_msg}. Got: {have_pk}, Expect: {expect_pk}')
|
||||||
|
raise ValueError(err_msg)
|
||||||
|
|
||||||
|
if ci_follower.coin_type() in (Coins.XMR, Coins.WOW):
|
||||||
|
address_to = self.getCachedMainWalletAddress(ci_follower, use_session)
|
||||||
|
else:
|
||||||
|
address_to = self.getCachedStealthAddressForCoin(ci_follower.coin_type(), use_session)
|
||||||
amount = bid.amount_to
|
amount = bid.amount_to
|
||||||
lock_tx_vout = bid.getLockTXBVout()
|
lock_tx_vout = bid.getLockTXBVout()
|
||||||
txid = ci_to.spendBLockTx(xmr_swap.b_lock_tx_id, address_to, xmr_swap.vkbv, vkbs, amount, xmr_offer.b_fee_rate, bid.chain_b_height_start, spend_actual_balance=True, lock_tx_vout=lock_tx_vout)
|
txid = ci_follower.spendBLockTx(xmr_swap.b_lock_tx_id, address_to, xmr_swap.vkbv, vkbs, amount, xmr_offer.b_fee_rate, bid.chain_b_height_start, spend_actual_balance=True, lock_tx_vout=lock_tx_vout)
|
||||||
self.log.debug('Submitted lock B spend txn %s to %s chain for bid %s', txid.hex(), ci_to.coin_name(), bid_id.hex())
|
self.log.debug('Submitted lock B spend txn %s to %s chain for bid %s', txid.hex(), ci_follower.coin_name(), bid_id.hex())
|
||||||
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED, txid.hex(), use_session)
|
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED, txid.hex(), use_session)
|
||||||
use_session.commit()
|
use_session.commit()
|
||||||
|
|
||||||
return txid
|
return txid
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error(traceback.format_exc())
|
||||||
|
raise (e)
|
||||||
finally:
|
finally:
|
||||||
if session is None:
|
if session is None:
|
||||||
self.closeSession(use_session, commit=False)
|
self.closeSession(use_session, commit=False)
|
||||||
|
@ -89,10 +111,14 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, session=None):
|
||||||
|
|
||||||
def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
|
def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
|
||||||
reverse_bid: bool = offer.bid_reversed
|
reverse_bid: bool = offer.bid_reversed
|
||||||
|
ci_leader = swap_client.ci(offer.coin_to if reverse_bid else offer.coin_from)
|
||||||
ci_follower = swap_client.ci(offer.coin_from if reverse_bid else offer.coin_to)
|
ci_follower = swap_client.ci(offer.coin_from if reverse_bid else offer.coin_to)
|
||||||
|
|
||||||
key_type = KeyTypes.KBSF if bid.was_sent else KeyTypes.KBSL
|
for_ed25519: bool = True if ci_follower.curve_type() == Curves.ed25519 else False
|
||||||
return ci_follower.encodeKey(swap_client.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, key_type, True if ci_follower.coin_type() == Coins.XMR else False))
|
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
||||||
|
|
||||||
|
key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL
|
||||||
|
return ci_follower.encodeKey(swap_client.getPathKey(ci_leader.coin_type(), ci_follower.coin_type(), bid.created_at, xmr_swap.contract_count, key_type, for_ed25519))
|
||||||
|
|
||||||
|
|
||||||
def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
|
def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
|
||||||
|
|
|
@ -378,13 +378,13 @@
|
||||||
{% if data.xmr_b_half_privatekey %}
|
{% if data.xmr_b_half_privatekey %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Key Half (WARNING key data!):</td>
|
<td class="py-3 px-6 bold">Key Half (WARNING key data!):</td>
|
||||||
<td class="py-3 px-6 monospace">{{ data.xmr_b_half_privatekey }}</td>
|
<td class="py-3 px-6 monospace" id="localkeyhalf">{{ data.xmr_b_half_privatekey }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if data.xmr_b_half_privatekey_remote %}
|
{% if data.xmr_b_half_privatekey_remote %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Remote Key Half:</td>
|
<td class="py-3 px-6 bold">Remote Key Half:</td>
|
||||||
<td class="py-3 px-6 monospace">{{ data.xmr_b_half_privatekey_remote }}</td>
|
<td class="py-3 px-6 monospace" id="remotekeyhalf">{{ data.xmr_b_half_privatekey_remote }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -34,6 +34,7 @@ dev = [
|
||||||
"flake8",
|
"flake8",
|
||||||
"pip-tools",
|
"pip-tools",
|
||||||
"pytest",
|
"pytest",
|
||||||
|
"selenium",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.hatch.version]
|
[tool.hatch.version]
|
||||||
|
|
|
@ -15,6 +15,10 @@ export XMR_RPC_USER=xmr_user
|
||||||
export XMR_RPC_PWD=xmr_pwd
|
export XMR_RPC_PWD=xmr_pwd
|
||||||
python tests/basicswap/extended/test_xmr_persistent.py
|
python tests/basicswap/extended/test_xmr_persistent.py
|
||||||
|
|
||||||
|
|
||||||
|
# Copy coin releases to permanent storage for faster subsequent startups
|
||||||
|
cp -r ${TEST_PATH}/bin/ ~/tmp/basicswap_bin
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
|
@ -1,2 +1,13 @@
|
||||||
|
|
||||||
|
cd basicswap
|
||||||
|
. $SWAP_DATADIR/venv/bin/activate
|
||||||
|
export PYTHONPATH=$(pwd)
|
||||||
python tests/basicswap/extended/test_xmr_persistent.py
|
python tests/basicswap/extended/test_xmr_persistent.py
|
||||||
python tests/basicswap/selenium/test_wallets.py
|
|
||||||
|
In another terminal:
|
||||||
|
cd basicswap
|
||||||
|
. $SWAP_DATADIR/venv/bin/activate
|
||||||
|
export PYTHONPATH=$(pwd)
|
||||||
|
pip install -e .[dev]
|
||||||
|
|
||||||
|
python tests/basicswap/selenium/test_settings.py
|
||||||
|
|
218
tests/basicswap/selenium/test_recover_chain_b_lock_tx.py
Normal file
218
tests/basicswap/selenium/test_recover_chain_b_lock_tx.py
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2024 The Basicswap developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
|
from selenium.webdriver.support.select import Select
|
||||||
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
|
from util import (
|
||||||
|
BSX_0_PORT, BSX_1_PORT,
|
||||||
|
click_option,
|
||||||
|
get_driver,
|
||||||
|
)
|
||||||
|
from tests.basicswap.util import read_json_api
|
||||||
|
|
||||||
|
|
||||||
|
base_url = 'http://localhost'
|
||||||
|
|
||||||
|
|
||||||
|
def recover_chain_b_lock_tx(driver, offer_data, offerer_port, bidder_port):
|
||||||
|
print('Test case: {} -> {}'.format(offer_data['coin_from'], offer_data['coin_to']))
|
||||||
|
offerer_url = f'{base_url}:{offerer_port}'
|
||||||
|
bidder_url = f'{base_url}:{bidder_port}'
|
||||||
|
|
||||||
|
rv = read_json_api(offerer_port, 'offers/new', offer_data)
|
||||||
|
offer0_id = rv['offer_id']
|
||||||
|
|
||||||
|
for i in range(10):
|
||||||
|
rv = read_json_api(bidder_port, f'offers/{offer0_id}')
|
||||||
|
if len(rv) > 0:
|
||||||
|
break
|
||||||
|
print('Bidder: Waiting for offer')
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
bid_data = {
|
||||||
|
'offer_id': offer0_id,
|
||||||
|
'amount_from': 1.0}
|
||||||
|
rv = read_json_api(bidder_port, 'bids/new', bid_data)
|
||||||
|
bid0_id = rv['bid_id']
|
||||||
|
|
||||||
|
bid_state = None
|
||||||
|
for i in range(10):
|
||||||
|
rv = read_json_api(offerer_port, f'bids/{bid0_id}')
|
||||||
|
if 'error' not in rv:
|
||||||
|
bid_state = rv['bid_state']
|
||||||
|
if bid_state == 'Received':
|
||||||
|
break
|
||||||
|
print('Offerer: Waiting for bid')
|
||||||
|
time.sleep(2)
|
||||||
|
assert (bid_state == 'Received')
|
||||||
|
|
||||||
|
# Set BID_STOP_AFTER_COIN_B_LOCK (13) debugind
|
||||||
|
rv = read_json_api(offerer_port, f'bids/{bid0_id}', {'debugind': 13})
|
||||||
|
assert ('error' not in rv)
|
||||||
|
|
||||||
|
# Accept bid
|
||||||
|
rv = read_json_api(offerer_port, f'bids/{bid0_id}', {'accept': 1})
|
||||||
|
assert ('error' not in rv)
|
||||||
|
|
||||||
|
for i in range(100):
|
||||||
|
rv = read_json_api(bidder_port, f'bids/{bid0_id}')
|
||||||
|
bid_state = rv['bid_state']
|
||||||
|
if bid_state == 'Scriptless coin locked':
|
||||||
|
break
|
||||||
|
print('Bidder: Waiting for state')
|
||||||
|
time.sleep(5)
|
||||||
|
assert (bid_state == 'Scriptless coin locked')
|
||||||
|
|
||||||
|
for i in range(100):
|
||||||
|
rv = read_json_api(offerer_port, f'bids/{bid0_id}')
|
||||||
|
bid_state = rv['bid_state']
|
||||||
|
if bid_state == 'Stalled (debug)':
|
||||||
|
break
|
||||||
|
print('Offerer: Waiting for state')
|
||||||
|
time.sleep(5)
|
||||||
|
assert (bid_state == 'Stalled (debug)')
|
||||||
|
|
||||||
|
# Show bid state history
|
||||||
|
rv = read_json_api(offerer_port, f'bids/{bid0_id}/states')
|
||||||
|
assert (len(rv) > 1)
|
||||||
|
|
||||||
|
url = f'{bidder_url}/bid/{bid0_id}'
|
||||||
|
driver.get(url)
|
||||||
|
btn_more_info = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'show_txns')))
|
||||||
|
btn_more_info.click()
|
||||||
|
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.NAME, 'hide_txns')))
|
||||||
|
bidder_localkeyhalf = driver.find_element(By.ID, 'localkeyhalf').text
|
||||||
|
print('Bidder keyhalf', bidder_localkeyhalf)
|
||||||
|
try:
|
||||||
|
driver.find_element(By.ID, 'remotekeyhalf')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ValueError('Nodes should not have remotekeyhalves yet.')
|
||||||
|
|
||||||
|
url = f'{offerer_url}/bid/{bid0_id}'
|
||||||
|
driver.get(url)
|
||||||
|
btn_edit = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'edit_bid')))
|
||||||
|
btn_edit.click()
|
||||||
|
btn_submit = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.NAME, 'edit_bid_submit')))
|
||||||
|
|
||||||
|
kbs_other = driver.find_element(By.ID, 'kbs_other')
|
||||||
|
print('Trying to recover with incorrect key')
|
||||||
|
last_byte = bidder_localkeyhalf[-2:]
|
||||||
|
invalid_byte = '01' if last_byte == '00' else '00'
|
||||||
|
kbs_other.send_keys(bidder_localkeyhalf[:-2] + invalid_byte)
|
||||||
|
btn_submit.click()
|
||||||
|
|
||||||
|
btn_edit = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'edit_bid')))
|
||||||
|
elements = driver.find_elements(By.CLASS_NAME, 'error_msg')
|
||||||
|
expect_err_msg: str = 'Summed key does not match expected wallet'
|
||||||
|
assert (any(expect_err_msg in el.text for el in elements))
|
||||||
|
btn_edit.click()
|
||||||
|
btn_submit = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.NAME, 'edit_bid_submit')))
|
||||||
|
|
||||||
|
lock_tx_b_depth = -1
|
||||||
|
for i in range(100):
|
||||||
|
# Check the non-stalled node
|
||||||
|
rv = read_json_api(bidder_port, f'bids/{bid0_id}', {'show_extra': True})
|
||||||
|
for tx in rv['txns']:
|
||||||
|
if tx['type'] == 'Chain B Lock' and tx['confirms'] is not None:
|
||||||
|
lock_tx_b_depth = tx['confirms']
|
||||||
|
break
|
||||||
|
if lock_tx_b_depth >= 10:
|
||||||
|
break
|
||||||
|
print(f'Waiting for lock tx B depth, have {lock_tx_b_depth}')
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
kbs_other = driver.find_element(By.ID, 'kbs_other')
|
||||||
|
print('Trying to recover with correct key')
|
||||||
|
kbs_other.send_keys(bidder_localkeyhalf)
|
||||||
|
btn_submit.click()
|
||||||
|
btn_edit = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'edit_bid')))
|
||||||
|
elements = driver.find_elements(By.CLASS_NAME, 'infomsg')
|
||||||
|
expect_msg: str = 'Bid edited'
|
||||||
|
assert (any(expect_msg in el.text for el in elements))
|
||||||
|
|
||||||
|
print('Trying with nodes reversed (should fail as already spent)')
|
||||||
|
url = f'{offerer_url}/bid/{bid0_id}'
|
||||||
|
driver.get(url)
|
||||||
|
btn_more_info = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'show_txns')))
|
||||||
|
btn_more_info.click()
|
||||||
|
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.NAME, 'hide_txns')))
|
||||||
|
offerer_localkeyhalf = driver.find_element(By.ID, 'localkeyhalf').text
|
||||||
|
print('Offerer keyhalf', offerer_localkeyhalf)
|
||||||
|
|
||||||
|
url = f'{bidder_url}/bid/{bid0_id}'
|
||||||
|
driver.get(url)
|
||||||
|
btn_edit = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'edit_bid')))
|
||||||
|
btn_edit.click()
|
||||||
|
btn_submit = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.NAME, 'edit_bid_submit')))
|
||||||
|
|
||||||
|
kbs_other = driver.find_element(By.ID, 'kbs_other')
|
||||||
|
kbs_other.send_keys(offerer_localkeyhalf)
|
||||||
|
btn_submit.click()
|
||||||
|
btn_edit = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'edit_bid')))
|
||||||
|
# in log: "Balance is too low, checking for existing spend"
|
||||||
|
# Should error here, but the code can't tell where the tx was sent, and treats any existing send as correct.
|
||||||
|
elements = driver.find_elements(By.CLASS_NAME, 'infomsg')
|
||||||
|
expect_msg: str = 'Bid edited'
|
||||||
|
assert (any(expect_msg in el.text for el in elements))
|
||||||
|
|
||||||
|
|
||||||
|
def enable_debug_ui(driver):
|
||||||
|
for port in (BSX_0_PORT, BSX_1_PORT):
|
||||||
|
url = f'{base_url}:{port}/settings'
|
||||||
|
driver.get(url)
|
||||||
|
driver.find_element(By.ID, 'general-tab').click()
|
||||||
|
|
||||||
|
btn_apply_general = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.NAME, 'apply_general')))
|
||||||
|
|
||||||
|
el = driver.find_element(By.NAME, 'debugmode')
|
||||||
|
selected_option = Select(el).first_selected_option
|
||||||
|
if selected_option.text != 'True':
|
||||||
|
click_option(el, 'True')
|
||||||
|
|
||||||
|
el = driver.find_element(By.NAME, 'debugui')
|
||||||
|
selected_option = Select(el).first_selected_option
|
||||||
|
if selected_option.text != 'True':
|
||||||
|
click_option(el, 'True')
|
||||||
|
|
||||||
|
btn_apply_general.click()
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests():
|
||||||
|
driver = get_driver()
|
||||||
|
try:
|
||||||
|
enable_debug_ui(driver)
|
||||||
|
|
||||||
|
offer_data = {
|
||||||
|
'coin_from': 'BTC',
|
||||||
|
'coin_to': 'XMR',
|
||||||
|
'amt_from': 1.0,
|
||||||
|
'amt_to': 2.0,
|
||||||
|
'lockhrs': 24}
|
||||||
|
recover_chain_b_lock_tx(driver, offer_data, BSX_0_PORT, BSX_1_PORT)
|
||||||
|
|
||||||
|
offer_data = {
|
||||||
|
'coin_from': 'XMR',
|
||||||
|
'coin_to': 'BTC',
|
||||||
|
'amt_from': 1.0,
|
||||||
|
'amt_to': 2.0,
|
||||||
|
'lockhrs': 24}
|
||||||
|
recover_chain_b_lock_tx(driver, offer_data, BSX_1_PORT, BSX_0_PORT)
|
||||||
|
|
||||||
|
print('Test Passed!')
|
||||||
|
finally:
|
||||||
|
driver.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run_tests()
|
|
@ -13,20 +13,17 @@ from selenium.webdriver.support.wait import WebDriverWait
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from util import get_driver
|
from util import (
|
||||||
|
BSX_0_PORT, BSX_1_PORT,
|
||||||
|
click_option,
|
||||||
|
get_driver,
|
||||||
|
)
|
||||||
from basicswap.ui.page_offers import default_chart_api_key
|
from basicswap.ui.page_offers import default_chart_api_key
|
||||||
|
|
||||||
|
|
||||||
def click_option(el, option_text):
|
|
||||||
for option in el.find_elements(By.TAG_NAME, 'option'):
|
|
||||||
if option.text == option_text:
|
|
||||||
option.click()
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
def test_settings(driver):
|
def test_settings(driver):
|
||||||
base_url = 'http://localhost:12701'
|
base_url = f'http://localhost:{BSX_0_PORT}'
|
||||||
node2_url = 'http://localhost:12702'
|
node2_url = f'http://localhost:{BSX_1_PORT}'
|
||||||
|
|
||||||
url = base_url + '/settings'
|
url = base_url + '/settings'
|
||||||
driver.get(url)
|
driver.get(url)
|
||||||
|
|
|
@ -2,13 +2,28 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2023 tecnovert
|
# Copyright (c) 2023 tecnovert
|
||||||
|
# Copyright (c) 2024 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
import os
|
||||||
from selenium.webdriver import Firefox
|
from selenium.webdriver import Firefox
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
|
||||||
|
|
||||||
|
BSX_0_PORT = int(os.getenv('BSX_0_PORT', 12701))
|
||||||
|
BSX_1_PORT = int(os.getenv('BSX_1_PORT', BSX_0_PORT + 1))
|
||||||
|
BSX_2_PORT = int(os.getenv('BSX_1_PORT', BSX_0_PORT + 2))
|
||||||
|
|
||||||
|
|
||||||
def get_driver():
|
def get_driver():
|
||||||
# driver = Chrome() # 2023-11: Hangs here
|
# driver = Chrome() # 2023-11: Hangs here
|
||||||
driver = Firefox()
|
driver = Firefox()
|
||||||
return driver
|
return driver
|
||||||
|
|
||||||
|
|
||||||
|
def click_option(el, option_text):
|
||||||
|
for option in el.find_elements(By.TAG_NAME, 'option'):
|
||||||
|
if option.text == option_text:
|
||||||
|
option.click()
|
||||||
|
break
|
||||||
|
|
Loading…
Reference in a new issue