diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index e072859..0a326f7 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -511,8 +511,8 @@ class BasicSwap(): coin_from = Coins(offer.coin_from) coin_to = Coins(offer.coin_to) - if bid.initiate_txid: - self.addWatchedOutput(coin_from, bid.bid_id, bid.initiate_txid.hex(), bid.initiate_txn_n, BidStates.SWAP_INITIATED) + if bid.initiate_tx: + self.addWatchedOutput(coin_from, bid.bid_id, bid.initiate_tx.txid.hex(), bid.initiate_tx.vout, BidStates.SWAP_INITIATED) if bid.participate_txid: self.addWatchedOutput(coin_to, bid.bid_id, bid.participate_txid.hex(), bid.participate_txn_n, BidStates.SWAP_PARTICIPATING) @@ -1046,9 +1046,9 @@ class BasicSwap(): try: txid = self.submitTxn(coin_from, bid.initiate_txn_refund.hex()) self.log.error('Submit refund_txn unexpectedly worked: ' + txid) - except Exception as e: - if 'non-BIP68-final' not in str(e) and 'non-final' not in str(e): - self.log.error('Submit refund_txn unexpected error' + str(e)) + except Exception as ex: + if 'non-BIP68-final' not in str(ex) and 'non-final' not in str(ex): + self.log.error('Submit refund_txn unexpected error' + str(ex)) if txid is not None: msg_buf = BidAcceptMessage() @@ -1064,7 +1064,7 @@ class BasicSwap(): accept_msg_id = bytes.fromhex(msg_id) bid.accept_msg_id = accept_msg_id - bid.initiate_txid = bytes.fromhex(txid) + #bid.initiate_txid = bytes.fromhex(txid) bid.setState(BidStates.BID_ACCEPTED) self.log.info('Sent BID_ACCEPT %s', accept_msg_id.hex()) @@ -1270,8 +1270,8 @@ class BasicSwap(): txn_script = bid.participate_script prev_amount = bid.amount_to else: - prev_txnid = bid.initiate_txid.hex() - prev_n = bid.initiate_txn_n + prev_txnid = bid.initiate_tx.txid.hex() + prev_n = bid.initiate_tx.vout txn_script = bid.initiate_tx.script prev_amount = bid.amount @@ -1582,11 +1582,11 @@ class BasicSwap(): # TODO: timeouts if state == BidStates.BID_ACCEPTED: # Waiting for initiate txn to be confirmed in 'from' chain - initiate_txnid_hex = bid.initiate_txid.hex() + initiate_txnid_hex = bid.initiate_tx.txid.hex() p2sh = self.getScriptAddress(coin_from, bid.initiate_tx.script) index = None tx_height = None - last_initiate_txn_conf = bid.initiate_txn_conf + last_initiate_txn_conf = bid.initiate_tx.conf if coin_from == Coins.PART: # Has txindex try: initiate_txn = self.callcoinrpc(coin_from, 'getrawtransaction', [initiate_txnid_hex, True]) @@ -1594,7 +1594,7 @@ class BasicSwap(): vout = getVoutByAddress(initiate_txn, p2sh) assert(int(initiate_txn['vout'][vout]['value'] * COIN) == int(bid.amount)), 'Incorrect output amount in initiate txn.' - bid.initiate_txn_conf = initiate_txn['confirmations'] + bid.initiate_tx.conf = initiate_txn['confirmations'] try: tx_height = initiate_txn['height'] except Exception: @@ -1609,31 +1609,31 @@ class BasicSwap(): addr = p2sh found = self.lookupUnspentByAddress(coin_from, addr, assert_amount=bid.amount, assert_txid=initiate_txnid_hex) if found: - bid.initiate_txn_conf = found['n_conf'] + bid.initiate_tx.conf = found['n_conf'] index = found['index'] tx_height = found['height'] - if bid.initiate_txn_conf != last_initiate_txn_conf: + if bid.initiate_tx.conf != last_initiate_txn_conf: save_bid = True - if bid.initiate_txn_conf is not None: - self.log.debug('initiate_txnid %s confirms %d', initiate_txnid_hex, bid.initiate_txn_conf) + if bid.initiate_tx.conf is not None: + self.log.debug('initiate_txnid %s confirms %d', initiate_txnid_hex, bid.initiate_tx.conf) - if bid.initiate_txn_n is None: - bid.initiate_txn_n = index + if bid.initiate_tx.vout is None: + bid.initiate_tx.vout = index # Start checking for spends of initiate_txn before fully confirmed bid.initiate_txn_height = self.setLastHeightChecked(coin_from, tx_height) - self.addWatchedOutput(coin_from, bid_id, initiate_txnid_hex, bid.initiate_txn_n, BidStates.SWAP_INITIATED) + self.addWatchedOutput(coin_from, bid_id, initiate_txnid_hex, bid.initiate_tx.vout, BidStates.SWAP_INITIATED) if bid.initiate_txn_state is None or bid.initiate_txn_state < TxStates.TX_SENT: bid.setITXState(TxStates.TX_SENT) save_bid = True - if bid.initiate_txn_conf >= self.coin_clients[coin_from]['blocks_confirmed']: + if bid.initiate_tx.conf >= self.coin_clients[coin_from]['blocks_confirmed']: self.initiateTxnConfirmed(bid_id, bid, offer) save_bid = True # Bid times out if buyer doesn't see tx in chain within INITIATE_TX_TIMEOUT seconds - if bid.initiate_txid is None and \ + if bid.initiate_tx is None and \ bid.state_time + INITIATE_TX_TIMEOUT < int(time.time()): self.log.info('Swap timed out waiting for initiate tx for bid %s', bid_id.hex()) bid.setState(BidStates.SWAP_TIMEDOUT) @@ -1698,18 +1698,18 @@ class BasicSwap(): txid = self.submitTxn(coin_from, bid.initiate_txn_refund.hex()) self.log.debug('Submitted initiate refund txn %s to %s chain for bid %s', txid, chainparams[coin_from]['name'], bid_id.hex()) # State will update when spend is detected - except Exception as e: - if 'non-BIP68-final (code 64)' not in str(e) and 'non-final' not in str(e): - self.log.warning('Error trying to submit initiate refund txn: %s', str(e)) + except Exception as ex: + if 'non-BIP68-final (code 64)' not in str(ex) and 'non-final' not in str(ex): + self.log.warning('Error trying to submit initiate refund txn: %s', str(ex)) if (bid.participate_txn_state == TxStates.TX_SENT or bid.participate_txn_state == TxStates.TX_CONFIRMED) \ and bid.participate_txn_refund is not None: try: txid = self.submitTxn(coin_to, bid.participate_txn_refund.hex()) self.log.debug('Submitted participate refund txn %s to %s chain for bid %s', txid, chainparams[coin_to]['name'], bid_id.hex()) # State will update when spend is detected - except Exception as e: - if 'non-BIP68-final (code 64)' not in str(e) and 'non-final' not in str(e): - self.log.warning('Error trying to submit participate refund txn: %s', str(e)) + except Exception as ex: + if 'non-BIP68-final (code 64)' not in str(ex) and 'non-final' not in str(ex): + self.log.warning('Error trying to submit participate refund txn: %s', str(ex)) return False # Bid is still active def extractSecret(self, coin_type, bid, spend_in): @@ -1762,7 +1762,7 @@ class BasicSwap(): # TODO: Wait for depth? bid.setITXState(TxStates.TX_REDEEMED) - self.removeWatchedOutput(coin_from, bid_id, bid.initiate_txid.hex()) + self.removeWatchedOutput(coin_from, bid_id, bid.initiate_tx.txid.hex()) self.saveBid(bid_id, bid) def participateTxnSpent(self, bid_id, spend_txid, spend_n, spend_txn): @@ -1813,9 +1813,9 @@ class BasicSwap(): found_spend = None try: found_spend = self.callcoinrpc(Coins.PART, 'getspentinfo', [{'txid': o[1], 'index': o[2]}]) - except Exception as e: - if 'Unable to get spent info' not in str(e): - self.log.warning('getspentinfo %s', str(e)) + except Exception as ex: + if 'Unable to get spent info' not in str(ex): + self.log.warning('getspentinfo %s', str(ex)) if found_spend is not None: self.log.debug('Found spend in spentindex %s %d in %s %d', o[1], o[2], found_spend['txid'], found_spend['index']) bid_id = o[0] @@ -2075,7 +2075,7 @@ class BasicSwap(): assert(bid.accept_msg_id is None), 'Bid already accepted' bid.accept_msg_id = bytes.fromhex(msg['msgid']) - bid.initiate_txid = bid_accept_data.initiate_txid + #bid.initiate_txid = bid_accept_data.initiate_txid #bid.initiate_script = bid_accept_data.contract_script bid.initiate_tx = SwapTx( bid_id=bid_id, @@ -2129,10 +2129,10 @@ class BasicSwap(): message = self.zmqSubscriber.recv(flags=zmq.NOBLOCK) if message == b'smsg': self.processZmqSmsg() - except zmq.Again as e: + except zmq.Again as ex: pass - except Exception as e: - self.log.error('smsg zmq %s', str(e)) + except Exception as ex: + self.log.error('smsg zmq %s', str(ex)) traceback.print_exc() self.mxDB.acquire() @@ -2165,8 +2165,8 @@ class BasicSwap(): # Expire messages if int(time.time()) - self.last_checked_expired > self.check_expired_seconds: self.expireMessages() - except Exception as e: - self.log.error('update %s', str(e)) + except Exception as ex: + self.log.error('update %s', str(ex)) traceback.print_exc() finally: self.mxDB.release() diff --git a/basicswap/db.py b/basicswap/db.py index 7641ca7..78782d4 100644 --- a/basicswap/db.py +++ b/basicswap/db.py @@ -90,9 +90,9 @@ class Bid(Base): pkhash_seller = sa.Column(sa.LargeBinary) #initiate_script = sa.Column(sa.LargeBinary) # contract_script - initiate_txid = sa.Column(sa.LargeBinary) - initiate_txn_n = sa.Column(sa.Integer) - initiate_txn_conf = sa.Column(sa.Integer) + #initiate_txid = sa.Column(sa.LargeBinary) + #initiate_txn_n = sa.Column(sa.Integer) + #initiate_txn_conf = sa.Column(sa.Integer) initiate_txn_refund = sa.Column(sa.LargeBinary) initiate_spend_txid = sa.Column(sa.LargeBinary) diff --git a/basicswap/http_server.py b/basicswap/http_server.py index 023b21c..d9c457d 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -330,14 +330,14 @@ class HttpHandler(BaseHTTPRequestHandler): try: swap_client.abandonBid(bid_id) messages.append('Bid abandoned') - except Exception as e: - messages.append('Error ' + str(e)) + except Exception as ex: + messages.append('Error ' + str(ex)) if b'accept_bid' in form_data: try: swap_client.acceptBid(bid_id) messages.append('Bid accepted') - except Exception as e: - messages.append('Error ' + str(e)) + except Exception as ex: + messages.append('Error ' + str(ex)) if b'show_txns' in form_data: show_txns = True @@ -354,7 +354,7 @@ class HttpHandler(BaseHTTPRequestHandler): elif bid.state == BidStates.BID_RECEIVED: state_description = 'Waiting for seller to accept.' elif bid.state == BidStates.BID_ACCEPTED: - if not bid.initiate_txid: + if not bid.initiate_tx: state_description = 'Waiting for seller to send initiate tx.' else: state_description = 'Waiting for initiate tx to confirm.' @@ -393,9 +393,9 @@ class HttpHandler(BaseHTTPRequestHandler): 'expired_at': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.expire_at)), 'was_sent': 'True' if bid.was_sent else 'False', 'was_received': 'True' if bid.was_received else 'False', - 'initiate_tx': 'None' if not bid.initiate_txid else (bid.initiate_txid.hex() + ' ' + ticker_from), - 'initiate_conf': 'None' if not bid.initiate_txn_conf else bid.initiate_txn_conf, - 'participate_tx': 'None' if not bid.participate_txid else (bid.participate_txid.hex() + ' ' + ticker_to), + 'initiate_tx': 'None' if not bid.initiate_tx else (bid.initiate_tx.txid.hex() + ' ' + ticker_from), + 'initiate_conf': 'None' if (not bid.initiate_tx or not bid.initiate_tx.conf) else bid.initiate_tx.conf, + 'participate_tx': 'None' if not bid.participate_tx else (bid.participate_tx.txid.hex() + ' ' + ticker_to), 'participate_conf': 'None' if not bid.participate_txn_conf else bid.participate_txn_conf, 'show_txns': show_txns, } @@ -495,8 +495,8 @@ class HttpHandler(BaseHTTPRequestHandler): 'sentbids': self.js_sentbids, }.get(url_split[2], self.js_index) return func(url_split) - except Exception as e: - return self.js_error(str(e)) + except Exception as ex: + return self.js_error(str(ex)) try: self.putHeaders(status_code, 'text/html') if len(url_split) > 1: @@ -523,9 +523,9 @@ class HttpHandler(BaseHTTPRequestHandler): if url_split[1] == 'watched': return self.page_watched(url_split, post_string) return self.page_index(url_split) - except Exception as e: + except Exception as ex: traceback.print_exc() # TODO: Remove - return self.page_error(str(e)) + return self.page_error(str(ex)) def do_GET(self): response = self.handle_http(200, self.path) diff --git a/basicswap/util.py b/basicswap/util.py index d63aec2..c1969d3 100644 --- a/basicswap/util.py +++ b/basicswap/util.py @@ -264,7 +264,7 @@ def callrpc(rpc_port, auth, method, params=[], wallet=None): v = x.json_request(method, params) x.close() r = json.loads(v.decode('utf-8')) - except Exception as e: + except Exception as ex: traceback.print_exc() raise ValueError('RPC Server Error') diff --git a/bin/basicswap_prepare.py b/bin/basicswap_prepare.py index 3e05ca5..1078bd4 100644 --- a/bin/basicswap_prepare.py +++ b/bin/basicswap_prepare.py @@ -193,6 +193,8 @@ def prepareDataDir(coin, settings, data_dir, chain, particl_mnemonic): fp.write(chain + '=1\n') if chain == 'testnet': fp.write('[test]\n\n') + if chain == 'regtest': + fp.write('[regtest]\n\n') else: logger.warning('Unknown chain %s', chain) @@ -355,6 +357,7 @@ def main(): os.makedirs(data_dir) config_path = os.path.join(data_dir, 'basicswap.json') + withchainclients = {} chainclients = { 'particl': { 'connection_type': 'rpc', @@ -452,6 +455,9 @@ def main(): if os.path.exists(config_path): exitWithError('{} exists'.format(config_path)) + for c in with_coins: + withchainclients[c] = chainclients[c] + settings = { 'debug': True, 'zmqhost': 'tcp://127.0.0.1', @@ -460,7 +466,7 @@ def main(): 'htmlport': 12700 + port_offset, 'network_key': '7sW2UEcHXvuqEjkpE5mD584zRaQYs6WXYohue4jLFZPTvMSxwvgs', 'network_pubkey': '035758c4a22d7dd59165db02a56156e790224361eb3191f02197addcb3bde903d2', - 'chainclients': chainclients, + 'chainclients': withchainclients, 'check_progress_seconds': 60, 'check_watched_seconds': 60, 'check_expired_seconds': 60 diff --git a/bin/basicswap_run.py b/bin/basicswap_run.py index 77a9e0a..fedd42d 100644 --- a/bin/basicswap_run.py +++ b/bin/basicswap_run.py @@ -8,9 +8,6 @@ """ Atomic Swap Client - Proof of Concept -Dependencies: - $ pacman -S python-pyzmq python-plyvel protobuf - """ import sys @@ -50,7 +47,7 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[]): return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) -def runClient(fp, data_dir, chain): +def runClient(fp, data_dir, chain, test_mode): global swap_client settings_path = os.path.join(data_dir, 'basicswap.json') pids_path = os.path.join(data_dir, '.pids') @@ -87,8 +84,10 @@ def runClient(fp, data_dir, chain): swap_client = BasicSwap(fp, data_dir, settings, chain) - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) + if not test_mode: + # signal only works in main thread + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) swap_client.start() threads = [] @@ -148,12 +147,19 @@ def printVersion(): def printHelp(): - logger.info('basicswap-run --datadir=path -testnet') + logger.info('Usage: basicswap-run ') + logger.info('\n--help, -h Print help.') + logger.info('--version, -v Print version.') + logger.info('--datadir=PATH Path to basicswap data directory, default:~/.basicswap.') + logger.info('--mainnet Run in mainnet mode.') + logger.info('--testnet Run in testnet mode.') + logger.info('--regtest Run in regtest mode.') def main(): data_dir = None chain = 'mainnet' + test_mode = False for v in sys.argv[1:]: if len(v) < 2 or v[0] != '-': @@ -173,6 +179,10 @@ def main(): if name == 'h' or name == 'help': printHelp() return 0 + + if name == 'testmode': + test_mode = True + continue if name == 'testnet': chain = 'testnet' continue @@ -198,7 +208,7 @@ def main(): with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp: logger.info(os.path.basename(sys.argv[0]) + ', version: ' + __version__ + '\n\n') - runClient(fp, data_dir, chain) + runClient(fp, data_dir, chain, test_mode) logger.info('Done.') return swap_client.fail_code if swap_client is not None else 0 diff --git a/tests/basicswap/test_prepare.py b/tests/basicswap/test_prepare.py index 757af95..98d0191 100644 --- a/tests/basicswap/test_prepare.py +++ b/tests/basicswap/test_prepare.py @@ -28,8 +28,9 @@ class Test(unittest.TestCase): def tearDownClass(self): try: shutil.rmtree(test_path) - except Exception as e: - logger.warning('tearDownClass %s', str(e)) + except Exception as ex: + logger.warning('tearDownClass %s', str(ex)) + super(Test, cls).tearDownClass() def test(self): testargs = ['basicswap-prepare', '-datadir=' + test_path] diff --git a/tests/basicswap/test_reload.py b/tests/basicswap/test_reload.py new file mode 100644 index 0000000..bf434da --- /dev/null +++ b/tests/basicswap/test_reload.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 tecnovert +# Distributed under the MIT software license, see the accompanying +# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php. + +""" + +mkdir -p /tmp/test_basicswap/bin/{particl,bitcoin} +cp ~/tmp/particl-0.18.1.0-x86_64-linux-gnu.tar.gz /tmp/test_basicswap/bin/particl +cp ~/tmp/bitcoin-0.18.0-x86_64-linux-gnu.tar.gz /tmp/test_basicswap/bin/bitcoin + + + +""" + +import os +import sys +import time +import unittest +from unittest.mock import patch +from io import StringIO +import logging +import shutil +import json +import threading + +import bin.basicswap_prepare as prepareSystem +import bin.basicswap_run as runSystem +test_path = os.path.expanduser('/tmp/test_basicswap') + +logger = logging.getLogger() +logger.level = logging.DEBUG +if not len(logger.handlers): + logger.addHandler(logging.StreamHandler(sys.stdout)) + + +class Test(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(Test, cls).setUpClass() + + config_path = os.path.join(test_path, 'basicswap.json') + try: + os.remove(config_path) + shutil.rmtree(os.path.join(test_path, 'particl')) + shutil.rmtree(os.path.join(test_path, 'bitcoin')) + except Exception as ex: + logger.warning('setUpClass %s', str(ex)) + + testargs = ['basicswap-prepare', '-datadir=' + test_path, '-regtest', '-withoutcoin=litecoin', '-withcoin=bitcoin'] + with patch.object(sys, 'argv', testargs): + prepareSystem.main() + + assert(os.path.exists(config_path)) + + def run_thread(self): + testargs = ['basicswap-run', '-datadir=' + test_path, '-regtest', '-testmode'] + with patch.object(sys, 'argv', testargs): + runSystem.main() + + def test_reload(self): + + thread = threading.Thread(target=self.run_thread) + thread.start() + + time.sleep(5) + + runSystem.swap_client.stopRunning() + + thread.join() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/basicswap/test_run.py b/tests/basicswap/test_run.py index 71d14ab..9da8878 100644 --- a/tests/basicswap/test_run.py +++ b/tests/basicswap/test_run.py @@ -405,9 +405,9 @@ class Test(unittest.TestCase): assert(ro['validscripts'] == 1) def test_02_part_ltc(self): + logging.info('---------- Test PART to LTC') swap_clients = self.swap_clients - logging.info('---------- Test PART to LTC') offer_id = swap_clients[0].postOffer(Coins.PART, Coins.LTC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST) self.wait_for_offer(swap_clients[1], offer_id) @@ -432,9 +432,9 @@ class Test(unittest.TestCase): assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0) def test_03_ltc_part(self): + logging.info('---------- Test LTC to PART') swap_clients = self.swap_clients - logging.info('---------- Test LTC to PART') offer_id = swap_clients[1].postOffer(Coins.LTC, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) self.wait_for_offer(swap_clients[0], offer_id) @@ -457,9 +457,9 @@ class Test(unittest.TestCase): assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0) def test_04_ltc_btc(self): + logging.info('---------- Test LTC to BTC') swap_clients = self.swap_clients - logging.info('---------- Test LTC to BTC') offer_id = swap_clients[0].postOffer(Coins.LTC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) self.wait_for_offer(swap_clients[1], offer_id) @@ -486,9 +486,9 @@ class Test(unittest.TestCase): def test_05_refund(self): # Seller submits initiate txn, buyer doesn't respond + logging.info('---------- Test refund, LTC to BTC') swap_clients = self.swap_clients - logging.info('---------- Test refund, LTC to BTC') offer_id = swap_clients[0].postOffer(Coins.LTC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, SEQUENCE_LOCK_BLOCKS, 10) @@ -511,13 +511,12 @@ class Test(unittest.TestCase): assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0) def test_06_self_bid(self): - swap_clients = self.swap_clients - logging.info('---------- Test same client, BTC to LTC') + swap_clients = self.swap_clients js_0_before = json.loads(urlopen('http://localhost:1800/json').read()) - offer_id = swap_clients[0].postOffer(Coins.LTC, Coins.BTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) + offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.LTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST) self.wait_for_offer(swap_clients[0], offer_id) offers = swap_clients[0].listOffers() @@ -536,13 +535,12 @@ class Test(unittest.TestCase): assert(js_0['num_recv_bids'] == js_0_before['num_recv_bids'] + 1 and js_0['num_sent_bids'] == js_0_before['num_sent_bids'] + 1) def test_07_error(self): - swap_clients = self.swap_clients - logging.info('---------- Test error, BTC to LTC, set fee above bid value') + swap_clients = self.swap_clients js_0_before = json.loads(urlopen('http://localhost:1800/json').read()) - offer_id = swap_clients[0].postOffer(Coins.LTC, Coins.BTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST) + offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.LTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST) self.wait_for_offer(swap_clients[0], offer_id) offers = swap_clients[0].listOffers()