tests: Add prefunded itx and xmr protocol tests

2024-05-20_merge
tecnovert 2 years ago
parent 80df3b1a34
commit 6860279faa
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
  1. 12
      basicswap/basicswap.py
  2. 2
      basicswap/chainparams.py
  3. 18
      basicswap/interface/btc.py
  4. 30
      basicswap/interface/dash.py
  5. 25
      basicswap/interface/firo.py
  6. 35
      basicswap/interface/pivx.py
  7. 7
      basicswap/protocols/__init__.py
  8. 6
      basicswap/protocols/atomic_swap_1.py
  9. 6
      basicswap/protocols/xmr_swap_1.py
  10. 15
      doc/protocols/sequence_diagrams/notes.txt
  11. 146
      tests/basicswap/extended/test_dash.py
  12. 132
      tests/basicswap/extended/test_firo.py
  13. 4
      tests/basicswap/extended/test_network.py
  14. 4
      tests/basicswap/extended/test_nmc.py
  15. 157
      tests/basicswap/extended/test_pivx.py

@ -1194,15 +1194,21 @@ class BasicSwap(BaseApp):
ensure(amount_to > ci_to.min_amount(), 'To amount below min value for chain') ensure(amount_to > ci_to.min_amount(), 'To amount below min value for chain')
ensure(amount_to < ci_to.max_amount(), 'To amount above max value for chain') ensure(amount_to < ci_to.max_amount(), 'To amount above max value for chain')
def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value): def validateOfferLockValue(self, swap_type, coin_from, coin_to, lock_type, lock_value):
coin_from_has_csv = self.coin_clients[coin_from]['use_csv'] coin_from_has_csv = self.coin_clients[coin_from]['use_csv']
coin_to_has_csv = self.coin_clients[coin_to]['use_csv'] coin_to_has_csv = self.coin_clients[coin_to]['use_csv']
if lock_type == OfferMessage.SEQUENCE_LOCK_TIME: if lock_type == OfferMessage.SEQUENCE_LOCK_TIME:
ensure(lock_value >= self.min_sequence_lock_seconds and lock_value <= self.max_sequence_lock_seconds, 'Invalid lock_value time') ensure(lock_value >= self.min_sequence_lock_seconds and lock_value <= self.max_sequence_lock_seconds, 'Invalid lock_value time')
if swap_type == SwapTypes.XMR_SWAP:
ensure(coin_from_has_csv, 'Coin from needs CSV activated.')
else:
ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.') ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
elif lock_type == OfferMessage.SEQUENCE_LOCK_BLOCKS: elif lock_type == OfferMessage.SEQUENCE_LOCK_BLOCKS:
ensure(lock_value >= 5 and lock_value <= 1000, 'Invalid lock_value blocks') ensure(lock_value >= 5 and lock_value <= 1000, 'Invalid lock_value blocks')
if swap_type == SwapTypes.XMR_SWAP:
ensure(coin_from_has_csv, 'Coin from needs CSV activated.')
else:
ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.') ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
elif lock_type == TxLockTypes.ABS_LOCK_TIME: elif lock_type == TxLockTypes.ABS_LOCK_TIME:
# TODO: range? # TODO: range?
@ -1262,7 +1268,7 @@ class BasicSwap(BaseApp):
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, rate, min_bid_amount) self.validateOfferAmounts(coin_from_t, coin_to_t, amount, rate, min_bid_amount)
self.validateOfferLockValue(coin_from_t, coin_to_t, lock_type, lock_value) self.validateOfferLockValue(swap_type, coin_from_t, coin_to_t, lock_type, lock_value)
self.validateOfferValidTime(swap_type, coin_from_t, coin_to_t, valid_for_seconds) self.validateOfferValidTime(swap_type, coin_from_t, coin_to_t, valid_for_seconds)
offer_addr_to = self.getOfferAddressTo(extra_options) offer_addr_to = self.getOfferAddressTo(extra_options)
@ -3916,7 +3922,7 @@ class BasicSwap(BaseApp):
self.validateSwapType(coin_from, coin_to, offer_data.swap_type) self.validateSwapType(coin_from, coin_to, offer_data.swap_type)
self.validateOfferAmounts(coin_from, coin_to, offer_data.amount_from, offer_data.rate, offer_data.min_bid_amount) self.validateOfferAmounts(coin_from, coin_to, offer_data.amount_from, offer_data.rate, offer_data.min_bid_amount)
self.validateOfferLockValue(coin_from, coin_to, offer_data.lock_type, offer_data.lock_value) self.validateOfferLockValue(offer_data.swap_type, coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
self.validateOfferValidTime(offer_data.swap_type, coin_from, coin_to, offer_data.time_valid) self.validateOfferValidTime(offer_data.swap_type, coin_from, coin_to, offer_data.time_valid)
ensure(msg['sent'] + offer_data.time_valid >= now, 'Offer expired') ensure(msg['sent'] + offer_data.time_valid >= now, 'Offer expired')

@ -321,7 +321,7 @@ chainparams = {
'rpcport': 28888, 'rpcport': 28888,
'pubkey_address': 65, 'pubkey_address': 65,
'script_address': 178, 'script_address': 178,
'key_prefix': 185, 'key_prefix': 239,
'hrp': '', 'hrp': '',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,

@ -92,7 +92,7 @@ def findOutput(tx, script_pk: bytes):
return None return None
def find_vout_for_address_from_txobj(tx_obj, addr) -> int: def find_vout_for_address_from_txobj(tx_obj, addr: str) -> int:
""" """
Locate the vout index of the given transaction sending to the Locate the vout index of the given transaction sending to the
given address. Raises runtime error exception if not found. given address. Raises runtime error exception if not found.
@ -1033,7 +1033,15 @@ class BTCInterface(CoinInterface):
return None return None
''' '''
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
witness_bytes = 109
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
pay_fee = int(fee_rate * vsize // 1000)
self._log.info(f'BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {vsize}, {pay_fee}.')
return pay_fee
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int) -> bytes: def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int) -> bytes:
self._log.info('spendBLockTx %s:\n', chain_b_lock_txid.hex())
wtx = self.rpc_callback('gettransaction', [chain_b_lock_txid.hex(), ]) wtx = self.rpc_callback('gettransaction', [chain_b_lock_txid.hex(), ])
lock_tx = self.loadTx(bytes.fromhex(wtx['hex'])) lock_tx = self.loadTx(bytes.fromhex(wtx['hex']))
@ -1054,12 +1062,8 @@ class BTCInterface(CoinInterface):
scriptSig=self.getScriptScriptSig(script_lock))) scriptSig=self.getScriptScriptSig(script_lock)))
tx.vout.append(self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to))) tx.vout.append(self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to)))
witness_bytes = 109 pay_fee = self.getBLockSpendTxFee(tx, b_fee)
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
pay_fee = int(b_fee * vsize // 1000)
tx.vout[0].nValue = cb_swap_value - pay_fee tx.vout[0].nValue = cb_swap_value - pay_fee
self._log.info('spendBLockTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
chain_b_lock_txid.hex(), b_fee, vsize, pay_fee)
b_lock_spend_tx = tx.serialize() b_lock_spend_tx = tx.serialize()
b_lock_spend_tx = self.signTxWithKey(b_lock_spend_tx, kbs) b_lock_spend_tx = self.signTxWithKey(b_lock_spend_tx, kbs)
@ -1366,10 +1370,8 @@ class BTCInterface(CoinInterface):
except Exception as ex: except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex)) self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed: if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
return {'txid': txid_hex, 'amount': 0, 'height': rv['blockheight']} return {'txid': txid_hex, 'amount': 0, 'height': rv['blockheight']}
return None return None

@ -9,6 +9,10 @@ from .btc import BTCInterface
from basicswap.chainparams import Coins from basicswap.chainparams import Coins
from basicswap.util.address import decodeAddress from basicswap.util.address import decodeAddress
from mnemonic import Mnemonic from mnemonic import Mnemonic
from basicswap.contrib.test_framework.script import (
CScript,
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
)
class DASHInterface(BTCInterface): class DASHInterface(BTCInterface):
@ -37,8 +41,32 @@ class DASHInterface(BTCInterface):
return False return False
def withdrawCoin(self, value, addr_to, subfee): def withdrawCoin(self, value, addr_to, subfee):
params = [addr_to, value, '', '', subfee] params = [addr_to, value, '', '', subfee, False, False, self._conf_target]
return self.rpc_callback('sendtoaddress', params) return self.rpc_callback('sendtoaddress', params)
def getSpendableBalance(self): def getSpendableBalance(self):
return self.make_int(self.rpc_callback('getwalletinfo')['balance']) return self.make_int(self.rpc_callback('getwalletinfo')['balance'])
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
# Return P2PKH
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
add_bytes = 107
size = len(tx.serialize_with_witness()) + add_bytes
pay_fee = int(fee_rate * size // 1000)
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
return pay_fee
def findTxnByHash(self, txid_hex: str):
# Only works for wallet txns
try:
rv = self.rpc_callback('gettransaction', [txid_hex])
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
block_height = self.getBlockHeader(rv['blockhash'])['height']
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None

@ -151,7 +151,6 @@ class FIROInterface(BTCInterface):
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray: def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
# Return P2PKH # Return P2PKH
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]) return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
def getScriptDest(self, script: bytearray) -> bytearray: def getScriptDest(self, script: bytearray) -> bytearray:
@ -184,3 +183,27 @@ class FIROInterface(BTCInterface):
def getSpendableBalance(self): def getSpendableBalance(self):
return self.make_int(self.rpc_callback('getwalletinfo')['balance']) return self.make_int(self.rpc_callback('getwalletinfo')['balance'])
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
add_bytes = 107
size = len(tx.serialize_with_witness()) + add_bytes
pay_fee = int(fee_rate * size // 1000)
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
return pay_fee
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
key_wif = self.encodeKey(key)
rv = self.rpc_callback('signrawtransaction', [tx.hex(), [], [key_wif, ]])
return bytes.fromhex(rv['hex'])
def findTxnByHash(self, txid_hex: str):
# Only works for wallet txns
try:
rv = self.rpc_callback('gettransaction', [txid_hex])
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
block_height = self.getBlockHeader(rv['blockhash'])['height']
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None

@ -15,6 +15,13 @@ from .contrib.pivx_test_framework.messages import (
ToHex, ToHex,
FromHex, FromHex,
CTransaction) CTransaction)
from basicswap.contrib.test_framework.script import (
CScript,
OP_DUP,
OP_HASH160,
OP_CHECKSIG,
OP_EQUALVERIFY,
)
class PIVXInterface(BTCInterface): class PIVXInterface(BTCInterface):
@ -80,3 +87,31 @@ class PIVXInterface(BTCInterface):
tx = CTransaction() tx = CTransaction()
tx.deserialize(BytesIO(tx_bytes)) tx.deserialize(BytesIO(tx_bytes))
return tx return tx
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
# Return P2PKH
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
add_bytes = 107
size = len(tx.serialize_with_witness()) + add_bytes
pay_fee = int(fee_rate * size // 1000)
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
return pay_fee
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
key_wif = self.encodeKey(key)
rv = self.rpc_callback('signrawtransaction', [tx.hex(), [], [key_wif, ]])
return bytes.fromhex(rv['hex'])
def findTxnByHash(self, txid_hex: str):
# Only works for wallet txns
try:
rv = self.rpc_callback('gettransaction', [txid_hex])
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
block_height = self.getBlockHeader(rv['blockhash'])['height']
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None

@ -7,6 +7,9 @@
from basicswap.script import ( from basicswap.script import (
OpCodes, OpCodes,
) )
from basicswap.util.script import (
getP2WSH,
)
class ProtocolInterface: class ProtocolInterface:
@ -22,3 +25,7 @@ class ProtocolInterface:
def getMockScriptScriptPubkey(self, ci) -> bytearray: def getMockScriptScriptPubkey(self, ci) -> bytearray:
script = self.getMockScript() script = self.getMockScript()
return ci.get_p2wsh_script_pubkey(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script) return ci.get_p2wsh_script_pubkey(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
def getMockAddrTo(self, ci):
script = self.getMockScript()
return ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)

@ -10,9 +10,6 @@ from basicswap.db import (
from basicswap.util import ( from basicswap.util import (
SerialiseNum, SerialiseNum,
) )
from basicswap.util.script import (
getP2WSH,
)
from basicswap.script import ( from basicswap.script import (
OpCodes, OpCodes,
) )
@ -77,8 +74,7 @@ class AtomicSwapInterface(ProtocolInterface):
swap_type = SwapTypes.SELLER_FIRST swap_type = SwapTypes.SELLER_FIRST
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes: def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
script = self.getMockScript() addr_to = self.getMockAddrTo(ci)
addr_to = ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False) funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
return bytes.fromhex(funded_tx) return bytes.fromhex(funded_tx)

@ -9,9 +9,6 @@ from sqlalchemy.orm import scoped_session
from basicswap.util import ( from basicswap.util import (
ensure, ensure,
) )
from basicswap.util.script import (
getP2WSH,
)
from basicswap.chainparams import ( from basicswap.chainparams import (
Coins, Coins,
) )
@ -104,8 +101,7 @@ class XmrSwapInterface(ProtocolInterface):
return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)]) return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)])
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes: def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
script = self.getMockScript() addr_to = self.getMockAddrTo(ci)
addr_to = ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False) funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
return bytes.fromhex(funded_tx) return bytes.fromhex(funded_tx)

@ -0,0 +1,15 @@
nvm use 14
npm install -g mscgenjs-cli
mscgenjs -T svg -i bidder.alt.xu -o bidder.alt.xu.svg
mscgenjs -T svg -i offerer.alt.xu -o offerer.alt.xu.svg
mscgenjs -T svg -i xmr.bidder.alt.xu -o xmr.bidder.alt.xu.svg
mscgenjs -T svg -i xmr.offerer.alt.xu -o xmr.offerer.alt.xu.svg
npm -g install svgo
svgo --pretty bidder.alt.xu.svg -o bidder.alt.xu.min.svg
svgo --pretty offerer.alt.xu.svg -o offerer.alt.xu.min.svg
svgo --pretty xmr.bidder.alt.xu.svg -o xmr.bidder.alt.xu.min.svg
svgo --pretty xmr.offerer.alt.xu.svg -o xmr.offerer.alt.xu.min.svg

@ -25,9 +25,10 @@ import basicswap.config as cfg
from basicswap.basicswap import ( from basicswap.basicswap import (
BasicSwap, BasicSwap,
Coins, Coins,
TxStates,
SwapTypes, SwapTypes,
BidStates, BidStates,
TxStates, DebugTypes,
) )
from basicswap.util import ( from basicswap.util import (
COIN, COIN,
@ -56,6 +57,8 @@ from tests.basicswap.common import (
stopDaemons, stopDaemons,
wait_for_offer, wait_for_offer,
wait_for_bid, wait_for_bid,
wait_for_balance,
wait_for_unspent,
wait_for_bid_tx_state, wait_for_bid_tx_state,
wait_for_in_progress, wait_for_in_progress,
TEST_HTTP_HOST, TEST_HTTP_HOST,
@ -192,7 +195,15 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
}, },
'check_progress_seconds': 2, 'check_progress_seconds': 2,
'check_watched_seconds': 4, 'check_watched_seconds': 4,
'check_expired_seconds': 60 'check_expired_seconds': 60,
'check_events_seconds': 1,
'check_xmr_swaps_seconds': 1,
'min_delay_event': 1,
'max_delay_event': 3,
'min_delay_event_short': 1,
'max_delay_event_short': 3,
'min_delay_retry': 2,
'max_delay_retry': 10
} }
with open(settings_path, 'w') as fp: with open(settings_path, 'w') as fp:
json.dump(settings, fp, indent=4) json.dump(settings, fp, indent=4)
@ -305,8 +316,8 @@ class Test(unittest.TestCase):
rpc('rescanblockchain') rpc('rescanblockchain')
else: else:
rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
rpc('reservebalance', [False]) rpc('reservebalance', ['false'])
basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap') basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
@ -564,6 +575,133 @@ class Test(unittest.TestCase):
dashRpc('unloadwallet', wallet=new_wallet_name) dashRpc('unloadwallet', wallet=new_wallet_name)
assert (addr_test == addr) assert (addr_test == addr)
def test_10_prefunded_itx(self):
logging.info('---------- Test prefunded itx offer')
swap_clients = self.swap_clients
coin_from = Coins.DASH
coin_to = Coins.BTC
swap_type = SwapTypes.SELLER_FIRST
ci_from = swap_clients[2].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
tla_from = coin_from.name
# Prepare balance
js_w2 = read_json_api(1802, 'wallets')
if float(js_w2[tla_from]['balance']) < 100.0:
post_json = {
'value': 100,
'address': js_w2[tla_from]['deposit_address'],
'subfee': False,
}
json_rv = read_json_api(1800, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
assert (len(json_rv['txid']) == 64)
wait_for_balance(delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 100.0)
js_w2 = read_json_api(1802, 'wallets')
assert (float(js_w2[tla_from]['balance']) >= 100.0)
js_w2 = read_json_api(1802, 'wallets')
post_json = {
'value': 100.0,
'address': read_json_api(1802, 'wallets/{}/nextdepositaddr'.format(tla_from.lower())),
'subfee': True,
}
json_rv = read_json_api(1802, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
wait_for_balance(delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 10.0)
assert (len(json_rv['txid']) == 64)
# Create prefunded ITX
pi = swap_clients[2].pi(SwapTypes.XMR_SWAP)
js_w2 = read_json_api(1802, 'wallets')
swap_value = 100.0
if float(js_w2[tla_from]['balance']) < swap_value:
swap_value = js_w2[tla_from]['balance']
swap_value = ci_from.make_int(swap_value)
assert (swap_value > ci_from.make_int(95))
itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
itx_decoded = ci_from.describeTx(itx.hex())
value_after_subfee = ci_from.make_int(itx_decoded['vout'][0]['value'])
assert (value_after_subfee < swap_value)
swap_value = value_after_subfee
wait_for_unspent(delay_event, ci_from, swap_value)
extra_options = {'prefunded_itx': itx}
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[2].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type, extra_options=extra_options)
wait_for_offer(delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED)
swap_clients[2].acceptBid(bid_id)
wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
# Verify expected inputs were used
bid, offer = swap_clients[2].getBidAndOffer(bid_id)
assert (bid.initiate_tx)
wtx = ci_from.rpc_callback('gettransaction', [bid.initiate_tx.txid.hex(),])
itx_after = ci_from.describeTx(wtx['hex'])
assert (len(itx_after['vin']) == len(itx_decoded['vin']))
for i, txin in enumerate(itx_decoded['vin']):
assert (txin['txid'] == itx_after['vin'][i]['txid'])
assert (txin['vout'] == itx_after['vin'][i]['vout'])
def test_11_xmrswap_to(self):
logging.info('---------- Test xmr swap protocol to')
swap_clients = self.swap_clients
coin_from = Coins.BTC
coin_to = Coins.DASH
swap_type = SwapTypes.XMR_SWAP
ci_from = swap_clients[0].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
swap_value = ci_from.make_int(random.uniform(0.2, 20.0), r=1)
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[0].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type)
wait_for_offer(delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptBid(bid_id)
wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
def test_12_xmrswap_to_recover_b_lock_tx(self):
coin_from = Coins.BTC
coin_to = Coins.DASH
logging.info('---------- Test {} to {} follower recovers coin b lock tx'.format(coin_from.name, coin_to.name))
swap_clients = self.swap_clients
ci_from = swap_clients[0].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[0].postOffer(
coin_from, coin_to, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP,
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=32)
wait_for_offer(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(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

@ -16,6 +16,7 @@ from basicswap.basicswap import (
TxStates, TxStates,
SwapTypes, SwapTypes,
BidStates, BidStates,
DebugTypes,
) )
from basicswap.basicswap_util import ( from basicswap.basicswap_util import (
TxLockTypes, TxLockTypes,
@ -38,6 +39,8 @@ from tests.basicswap.common import (
make_rpc_func, make_rpc_func,
TEST_HTTP_PORT, TEST_HTTP_PORT,
wait_for_offer, wait_for_offer,
wait_for_balance,
wait_for_unspent,
wait_for_in_progress, wait_for_in_progress,
wait_for_bid_tx_state, wait_for_bid_tx_state,
) )
@ -420,6 +423,135 @@ class Test(BaseTest):
json_rv = read_json_api(TEST_HTTP_PORT + 0, 'wallets/{}/createutxo'.format(self.test_coin_from.name.lower()), post_json) json_rv = read_json_api(TEST_HTTP_PORT + 0, 'wallets/{}/createutxo'.format(self.test_coin_from.name.lower()), post_json)
assert (len(json_rv['txid']) == 64) assert (len(json_rv['txid']) == 64)
def ensure_balance(self, coin_type, node_id, amount):
tla = coin_type.name
js_w = read_json_api(1800 + node_id, 'wallets')
if float(js_w[tla]['balance']) < amount:
post_json = {
'value': amount,
'address': js_w[tla]['deposit_address'],
'subfee': False,
}
json_rv = read_json_api(1800, 'wallets/{}/withdraw'.format(tla.lower()), post_json)
assert (len(json_rv['txid']) == 64)
wait_for_balance(test_delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(1800 + node_id, tla.lower()), 'balance', amount)
def test_10_prefunded_itx(self):
logging.info('---------- Test prefunded itx offer')
swap_clients = self.swap_clients
coin_from = Coins.FIRO
coin_to = Coins.BTC
swap_type = SwapTypes.SELLER_FIRST
ci_from = swap_clients[2].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
tla_from = coin_from.name
# Prepare balance
self.ensure_balance(coin_from, 2, 10.0)
self.ensure_balance(coin_to, 1, 100.0)
js_w2 = read_json_api(1802, 'wallets')
post_json = {
'value': 10.0,
'address': read_json_api(1802, 'wallets/{}/nextdepositaddr'.format(tla_from.lower())),
'subfee': True,
}
json_rv = read_json_api(1802, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
wait_for_balance(test_delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 9.0)
assert (len(json_rv['txid']) == 64)
# Create prefunded ITX
pi = swap_clients[2].pi(SwapTypes.XMR_SWAP)
js_w2 = read_json_api(1802, 'wallets')
swap_value = 10.0
if float(js_w2[tla_from]['balance']) < swap_value:
swap_value = js_w2[tla_from]['balance']
swap_value = ci_from.make_int(swap_value)
assert (swap_value > ci_from.make_int(9))
itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
itx_decoded = ci_from.describeTx(itx.hex())
value_after_subfee = ci_from.make_int(itx_decoded['vout'][0]['value'])
assert (value_after_subfee < swap_value)
swap_value = value_after_subfee
wait_for_unspent(test_delay_event, ci_from, swap_value)
extra_options = {'prefunded_itx': itx}
rate_swap = ci_to.make_int(random.uniform(0.2, 10.0), r=1)
offer_id = swap_clients[2].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type, extra_options=extra_options)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED)
swap_clients[2].acceptBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[2], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
# Verify expected inputs were used
bid, offer = swap_clients[2].getBidAndOffer(bid_id)
assert (bid.initiate_tx)
wtx = ci_from.rpc_callback('gettransaction', [bid.initiate_tx.txid.hex(),])
itx_after = ci_from.describeTx(wtx['hex'])
assert (len(itx_after['vin']) == len(itx_decoded['vin']))
for i, txin in enumerate(itx_decoded['vin']):
assert (txin['txid'] == itx_after['vin'][i]['txid'])
assert (txin['vout'] == itx_after['vin'][i]['vout'])
def test_11_xmrswap_to(self):
logging.info('---------- Test xmr swap protocol to')
swap_clients = self.swap_clients
coin_from = Coins.BTC
coin_to = Coins.FIRO
swap_type = SwapTypes.XMR_SWAP
ci_from = swap_clients[0].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
swap_value = ci_from.make_int(random.uniform(0.2, 20.0), r=1)
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[0].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
def test_12_xmrswap_to_recover_b_lock_tx(self):
coin_from = Coins.BTC
coin_to = Coins.FIRO
logging.info('---------- Test {} to {} follower recovers coin b lock tx'.format(coin_from.name, coin_to.name))
swap_clients = self.swap_clients
ci_from = swap_clients[0].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[0].postOffer(
coin_from, coin_to, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP,
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=32)
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)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
def test_101_full_swap(self): def test_101_full_swap(self):
logging.info('---------- Test {} to XMR'.format(self.test_coin_from.name)) logging.info('---------- Test {} to XMR'.format(self.test_coin_from.name))
if not self.test_xmr: if not self.test_xmr:

@ -220,8 +220,8 @@ class Test(unittest.TestCase):
else: else:
rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
# Lower output split threshold for more stakeable outputs # Lower output split threshold for more stakeable outputs
rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
rpc('reservebalance', [False]) rpc('reservebalance', ['false'])
for i in range(NUM_BTC_NODES): for i in range(NUM_BTC_NODES):
data_dir = prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT) data_dir = prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT)

@ -297,8 +297,8 @@ class Test(unittest.TestCase):
rpc('rescanblockchain') rpc('rescanblockchain')
else: else:
rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
rpc('reservebalance', [False]) rpc('reservebalance', ['false'])
basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap') basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)

@ -14,6 +14,7 @@ import os
import sys import sys
import json import json
import time import time
import random
import shutil import shutil
import signal import signal
import logging import logging
@ -27,6 +28,7 @@ from basicswap.basicswap import (
SwapTypes, SwapTypes,
BidStates, BidStates,
TxStates, TxStates,
DebugTypes,
) )
from basicswap.util import ( from basicswap.util import (
COIN, COIN,
@ -47,16 +49,21 @@ from basicswap.contrib.key import (
from basicswap.http_server import ( from basicswap.http_server import (
HttpThread, HttpThread,
) )
from basicswap.interface.btc import (
find_vout_for_address_from_txobj,
)
from tests.basicswap.util import ( from tests.basicswap.util import (
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
checkForks, checkForks,
stopDaemons, stopDaemons,
wait_for_offer,
wait_for_bid, wait_for_bid,
wait_for_bid_tx_state, wait_for_offer,
wait_for_balance,
wait_for_unspent,
wait_for_in_progress, wait_for_in_progress,
wait_for_bid_tx_state,
TEST_HTTP_HOST, TEST_HTTP_HOST,
TEST_HTTP_PORT, TEST_HTTP_PORT,
BASE_PORT, BASE_PORT,
@ -197,7 +204,15 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
}, },
'check_progress_seconds': 2, 'check_progress_seconds': 2,
'check_watched_seconds': 4, 'check_watched_seconds': 4,
'check_expired_seconds': 60 'check_expired_seconds': 60,
'check_events_seconds': 1,
'check_xmr_swaps_seconds': 1,
'min_delay_event': 1,
'max_delay_event': 3,
'min_delay_event_short': 1,
'max_delay_event_short': 3,
'min_delay_retry': 2,
'max_delay_retry': 10
} }
with open(settings_path, 'w') as fp: with open(settings_path, 'w') as fp:
json.dump(settings, fp, indent=4) json.dump(settings, fp, indent=4)
@ -317,8 +332,8 @@ class Test(unittest.TestCase):
rpc('rescanblockchain') rpc('rescanblockchain')
else: else:
rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']]) rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}]) rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
rpc('reservebalance', [False]) rpc('reservebalance', ['false'])
basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap') basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
@ -581,6 +596,138 @@ class Test(unittest.TestCase):
break break
assert found assert found
def ensure_balance(self, coin_type, node_id, amount):
tla = coin_type.name
js_w = read_json_api(1800 + node_id, 'wallets')
if float(js_w[tla]['balance']) < amount:
post_json = {
'value': amount,
'address': js_w[tla]['deposit_address'],
'subfee': False,
}
json_rv = read_json_api(1800, 'wallets/{}/withdraw'.format(tla.lower()), post_json)
assert (len(json_rv['txid']) == 64)
wait_for_balance(delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(1800 + node_id, tla.lower()), 'balance', amount)
def test_10_prefunded_itx(self):
logging.info('---------- Test prefunded itx offer')
swap_clients = self.swap_clients
coin_from = Coins.PIVX
coin_to = Coins.BTC
swap_type = SwapTypes.SELLER_FIRST
ci_from = swap_clients[2].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
tla_from = coin_from.name
# Prepare balance
self.ensure_balance(coin_from, 2, 10.0)
self.ensure_balance(coin_to, 1, 100.0)
js_w2 = read_json_api(1802, 'wallets')
post_json = {
'value': 10.0,
'address': read_json_api(1802, 'wallets/{}/nextdepositaddr'.format(tla_from.lower())),
'subfee': True,
}
json_rv = read_json_api(1802, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
wait_for_balance(delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 9.0)
assert (len(json_rv['txid']) == 64)
# Create prefunded ITX
pi = swap_clients[2].pi(SwapTypes.XMR_SWAP)
js_w2 = read_json_api(1802, 'wallets')
swap_value = 10.0
if float(js_w2[tla_from]['balance']) < swap_value:
swap_value = js_w2[tla_from]['balance']
swap_value = ci_from.make_int(swap_value)
assert (swap_value > ci_from.make_int(9))
itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
itx_decoded = ci_from.describeTx(itx.hex())
mock_addr = pi.getMockAddrTo(ci_from)
n = find_vout_for_address_from_txobj(itx_decoded, mock_addr)
value_after_subfee = ci_from.make_int(itx_decoded['vout'][n]['value'])
assert (value_after_subfee < swap_value)
swap_value = value_after_subfee
wait_for_unspent(delay_event, ci_from, swap_value)
extra_options = {'prefunded_itx': itx}
rate_swap = ci_to.make_int(random.uniform(0.2, 10.0), r=1)
offer_id = swap_clients[2].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type, TxLockTypes.ABS_LOCK_TIME, extra_options=extra_options)
wait_for_offer(delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED)
swap_clients[2].acceptBid(bid_id)
wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
# Verify expected inputs were used
bid, offer = swap_clients[2].getBidAndOffer(bid_id)
assert (bid.initiate_tx)
wtx = ci_from.rpc_callback('gettransaction', [bid.initiate_tx.txid.hex(),])
itx_after = ci_from.describeTx(wtx['hex'])
assert (len(itx_after['vin']) == len(itx_decoded['vin']))
for i, txin in enumerate(itx_decoded['vin']):
assert (txin['txid'] == itx_after['vin'][i]['txid'])
assert (txin['vout'] == itx_after['vin'][i]['vout'])
def test_11_xmrswap_to(self):
logging.info('---------- Test xmr swap protocol to')
swap_clients = self.swap_clients
coin_from = Coins.BTC
coin_to = Coins.PIVX
swap_type = SwapTypes.XMR_SWAP
ci_from = swap_clients[0].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
swap_value = ci_from.make_int(random.uniform(0.2, 20.0), r=1)
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[0].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type)
wait_for_offer(delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptBid(bid_id)
wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
def test_12_xmrswap_to_recover_b_lock_tx(self):
coin_from = Coins.BTC
coin_to = Coins.PIVX
logging.info('---------- Test {} to {} follower recovers coin b lock tx'.format(coin_from.name, coin_to.name))
swap_clients = self.swap_clients
ci_from = swap_clients[0].ci(coin_from)
ci_to = swap_clients[1].ci(coin_to)
amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
offer_id = swap_clients[0].postOffer(
coin_from, coin_to, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP,
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=32)
wait_for_offer(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(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

Loading…
Cancel
Save