diff --git a/.cirrus.yml b/.cirrus.yml index 942792d..835ba4b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,7 +7,7 @@ lint_task: - pip install codespell script: - PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,messages_pb2.py,.eggs - - codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,gitianpubkeys,*.pyc,*mnemonics.py,basicswap/contrib + - codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,gitianpubkeys,*.pyc,*basicswap/contrib,*mnemonics.py test_task: environment: diff --git a/Dockerfile b/Dockerfile index 078329c..0e9e8e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,19 @@ -FROM ubuntu:18.10 +FROM ubuntu:20.04 + +ARG WITH_COINS +ARG WITHOUT_COINS ENV LANG=C.UTF-8 \ + DEBIAN_FRONTEND=noninteractive \ DATADIRS="/coindata" RUN apt-get update; \ - apt-get install -y wget python3-pip gnupg unzip protobuf-compiler; + apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config; + +RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/anonswap.zip && \ + unzip coincurve-anonswap.zip && \ + cd coincurve-anonswap && \ + python3 setup.py install --force # TODO: move coindata dir out of src dir COPY . basicswap-master @@ -13,7 +22,7 @@ RUN cd basicswap-master; \ pip3 install .; # Download binaries, these will be part of the docker image -RUN basicswap-prepare -datadir=/opt -preparebinonly +RUN basicswap-prepare -datadir=/opt -preparebinonly ${WITH_COINS} ${WITHOUT_COINS} RUN useradd -ms /bin/bash user && \ mkdir /coindata && chown user -R /coindata diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index e9ab1b1..05afbf0 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -211,6 +211,8 @@ def strOfferState(state): def strBidState(state): if state == BidStates.BID_SENT: return 'Sent' + if state == BidStates.BID_RECEIVING: + return 'Receiving' if state == BidStates.BID_RECEIVED: return 'Received' if state == BidStates.BID_ACCEPTED: @@ -227,6 +229,28 @@ def strBidState(state): return 'Abandoned' if state == BidStates.BID_ERROR: return 'Error' + if state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED: + return 'Script coin locked' + if state == BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX: + return 'Script coin spend tx valid' + if state == BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED: + return 'Scriptless coin locked' + if state == BidStates.XMR_SWAP_SECRET_SHARED: + return 'Secret shared' + if state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED: + return 'Script tx redeemed' + if state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED: + return 'Scriptless tx redeemed' + if state == BidStates.XMR_SWAP_NOSCRIPT_TX_RECOVERED: + return 'Scriptless tx recovered' + if state == BidStates.XMR_SWAP_FAILED_REFUNDED: + return 'Failed, refunded' + if state == BidStates.XMR_SWAP_FAILED_SWIPED: + return 'Failed, swiped' + if state == BidStates.XMR_SWAP_FAILED: + return 'Failed' + if state == BidStates.SWAP_DELAYING: + return 'Delaying' return 'Unknown' @@ -2359,7 +2383,7 @@ class BasicSwap(BaseApp): found_tx = ci_to.findTxB(xmr_swap.vkbv, xmr_swap.pkbs, bid.amount_to, ci_to.blocks_confirmed, xmr_swap.b_restore_height) if found_tx is not None: - + self.log.debug('Found {} lock tx in chain'.format(ci_to.coin_name())) if bid.xmr_b_lock_tx is None: b_lock_tx_id = bytes.fromhex(found_tx['txid']) bid.xmr_b_lock_tx = SwapTx( @@ -2383,7 +2407,7 @@ class BasicSwap(BaseApp): # Wait for script spend tx to confirm # TODO: Use explorer to get tx / block hash for getrawtransaction pass - elif state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED: + elif state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED: txid_hex = bid.xmr_b_lock_tx.spend_txid.hex() found_tx = ci_to.findTxnByHash(txid_hex) @@ -2741,6 +2765,7 @@ class BasicSwap(BaseApp): else: self.log.info('Coin a lock refund spent by unknown tx, bid {}'.format(bid_id.hex())) + bid.setState(BidStates.XMR_SWAP_FAILED_SWIPED) self.saveBidInSession(bid_id, bid, session, xmr_swap) # Update copy of bid in swaps_in_progress @@ -2835,7 +2860,7 @@ class BasicSwap(BaseApp): try: session = scoped_session(self.session_factory) - q = session.query(EventQueue).filter(EventQueue.trigger_at >= now) + q = session.query(EventQueue).filter(sa.and_(EventQueue.active_ind == 1, EventQueue.trigger_at <= now)) for row in q: try: if row.event_type == EventTypes.ACCEPT_BID: @@ -2860,7 +2885,11 @@ class BasicSwap(BaseApp): if self.debug: traceback.print_exc() self.log.error('checkEvents failed: {}'.format(str(ex))) - session.delete(row) + + if self.debug: + session.execute('UPDATE eventqueue SET active_ind = 2 WHERE trigger_at <= {}'.format(now)) + else: + session.execute('DELETE FROM eventqueue WHERE trigger_at <= {}'.format(now)) session.commit() finally: @@ -3683,6 +3712,7 @@ class BasicSwap(BaseApp): txid = ci_to.spendBLockTx(address_to, xmr_swap.vkbv, vkbs, bid.amount_to, xmr_offer.b_fee_rate, xmr_swap.b_restore_height) bid.xmr_b_lock_tx.spend_txid = txid + bid.setState(BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED) # TODO: Why does using bid.txns error here? self.saveBidInSession(bid_id, bid, session, xmr_swap) # Update copy of bid in swaps_in_progress diff --git a/basicswap/chainparams.py b/basicswap/chainparams.py index e13a12d..389277f 100644 --- a/basicswap/chainparams.py +++ b/basicswap/chainparams.py @@ -198,3 +198,6 @@ class CoinInterface: def coin_name(self): return chainparams[self.coin_type()]['name'].capitalize() + + def ticker(self): + return chainparams[self.coin_type()]['ticker'] diff --git a/basicswap/contrib/__init__.py b/basicswap/contrib/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/basicswap/contrib/__init__.py @@ -0,0 +1 @@ + diff --git a/basicswap/interface_xmr.py b/basicswap/interface_xmr.py index 8f0778b..31ea246 100644 --- a/basicswap/interface_xmr.py +++ b/basicswap/interface_xmr.py @@ -166,6 +166,8 @@ class XMRInterface(CoinInterface): params = {'out': True, 'pending': True, 'failed': True, 'pool': True, } rv = self.rpc_wallet_cb('get_transfers', params) logging.info('[rm] get_transfers {}'.format(dumpj(rv))) + if 'pending' not in rv: + break time.sleep(1) return tx_hash diff --git a/basicswap/ui.py b/basicswap/ui.py index 4896f79..e86bbc5 100644 --- a/basicswap/ui.py +++ b/basicswap/ui.py @@ -48,7 +48,7 @@ def setCoinFilter(form_data, field_name): raise ValueError('Unknown Coin Type {}'.format(str(field_name))) -def getTxIdHex(bid, tx_type, prefix): +def getTxIdHex(bid, tx_type, suffix): if tx_type == TxTypes.ITX: obj = bid.initiate_tx elif tx_type == TxTypes.PTX: @@ -60,7 +60,7 @@ def getTxIdHex(bid, tx_type, prefix): return 'None' if not obj.txid: return 'None' - return obj.txid.hex() + prefix + return obj.txid.hex() + suffix def getTxSpendHex(bid, tx_type): @@ -86,13 +86,10 @@ def listBidStates(): def describeBid(swap_client, bid, offer, edit_bid, show_txns): - - coin_from = Coins(offer.coin_from) - coin_to = Coins(offer.coin_to) - ci_from = swap_client.ci(coin_from) - ci_to = swap_client.ci(coin_to) - ticker_from = swap_client.getTicker(coin_from) - ticker_to = swap_client.getTicker(coin_to) + ci_from = swap_client.ci(Coins(offer.coin_from)) + ci_to = swap_client.ci(Coins(offer.coin_to)) + ticker_from = ci_from.ticker() + ticker_to = ci_to.ticker() if bid.state == BidStates.BID_SENT: state_description = 'Waiting for seller to accept.' diff --git a/bin/basicswap_prepare.py b/bin/basicswap_prepare.py index 55e384b..01e318b 100755 --- a/bin/basicswap_prepare.py +++ b/bin/basicswap_prepare.py @@ -227,8 +227,8 @@ def prepareCore(coin, version, settings, data_dir): pubkeyurl = 'https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc' logger.info('Importing public key from url: ' + pubkeyurl) rv = gpg.import_keys(urllib.request.urlopen(pubkeyurl).read()) - assert('F0AF4D462A0BDF92' in rv) print('import_keys', rv) + assert('F0AF4D462A0BDF92' in rv.fingerprints[0]) with open(assert_path, 'rb') as fp: verified = gpg.verify_file(fp) else: @@ -395,15 +395,19 @@ def main(): if name == 'particl_mnemonic': particl_wallet_mnemonic = s[1].strip('"') continue - if name == 'withcoin': - if s[1] not in known_coins: - exitWithError('Unknown coin {}'.format(s[1])) - with_coins.add(s[1]) + if name == 'withcoin' or name == 'withcoins': + coins = s[1].split(',') + for coin in coins: + if coin not in known_coins: + exitWithError('Unknown coin {}'.format(coin)) + with_coins.add(coin) continue - if name == 'withoutcoin': - if s[1] not in known_coins: - exitWithError('Unknown coin {}'.format(s[1])) - with_coins.discard(s[1]) + if name == 'withoutcoin' or name == 'withoutcoins': + coins = s[1].split(',') + for coin in coins: + if coin not in known_coins: + exitWithError('Unknown coin {}'.format(coin)) + with_coins.discard(coin) continue if name == 'addcoin': if s[1] not in known_coins: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3a4a8f6..8f5422b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -5,6 +5,9 @@ services: stop_grace_period: 5m build: context: ../ + args: + - WITH_COINS=--withcoins=monero + - WITHOUT_COINS=--withoutcoins=litecoin volumes: - ${COINDATA_PATH}:/coindata ports: