diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 5014cbd..513086e 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -1942,7 +1942,7 @@ class BasicSwap(BaseApp): refund_txn = self.createRefundTxn(coin_from, txn, offer, bid, script) bid.initiate_txn_refund = bytes.fromhex(refund_txn) - txid = self.submitTxn(coin_from, txn) + txid = ci_from.publishTx(bytes.fromhex(txn)) self.log.debug('Submitted initiate txn %s to %s chain for bid %s', txid, ci_from.coin_name(), bid_id.hex()) bid.initiate_tx = SwapTx( bid_id=bid_id, @@ -1955,7 +1955,7 @@ class BasicSwap(BaseApp): # Check non-bip68 final try: - txid = self.submitTxn(coin_from, bid.initiate_txn_refund.hex()) + txid = ci_from.publishTx(bid.initiate_txn_refund) self.log.error('Submit refund_txn unexpectedly worked: ' + txid) except Exception as ex: if 'non-BIP68-final' not in str(ex) and 'non-final' not in str(ex): @@ -2664,14 +2664,6 @@ class BasicSwap(BaseApp): return refund_txn - def submitTxn(self, coin_type, txn): - # self.log.debug('submitTxn %s', str(coin_type)) - if txn is None: - return None - if self.coin_clients[coin_type]['connection_type'] != 'rpc': - return None - return self.callcoinrpc(coin_type, 'sendrawtransaction', [txn]) - def initiateTxnConfirmed(self, bid_id, bid, offer): self.log.debug('initiateTxnConfirmed for bid %s', bid_id.hex()) bid.setState(BidStates.SWAP_INITIATED) @@ -2693,7 +2685,7 @@ class BasicSwap(BaseApp): coin_to = Coins(offer.coin_to) txn = self.createParticipateTxn(bid_id, bid, offer, participate_script) - txid = self.submitTxn(coin_to, txn) + txid = self.ci(coin_to).publishTx(bytes.fromhex(txn)) self.log.debug('Submitted participate txn %s to %s chain for bid %s', txid, chainparams[coin_to]['name'], bid_id.hex()) bid.setPTxState(TxStates.TX_SENT) else: @@ -2745,10 +2737,10 @@ class BasicSwap(BaseApp): # Seller redeems from participate txn if bid.was_received: - coin_to = Coins(offer.coin_to) - txn = self.createRedeemTxn(coin_to, bid) - txid = self.submitTxn(coin_to, txn) - self.log.debug('Submitted participate redeem txn %s to %s chain for bid %s', txid, chainparams[coin_to]['name'], bid_id.hex()) + ci_to = self.ci(offer.coin_to) + txn = self.createRedeemTxn(ci_to.coin_type(), bid) + txid = ci_to.publishTx(bytes.fromhex(txn)) + self.log.debug('Submitted participate redeem txn %s to %s chain for bid %s', txid, ci_to.coin_name(), bid_id.hex()) # TX_REDEEMED will be set when spend is detected # TODO: Wait for depth? @@ -3230,7 +3222,7 @@ class BasicSwap(BaseApp): if (bid.getITxState() == TxStates.TX_SENT or bid.getITxState() == TxStates.TX_CONFIRMED) \ and bid.initiate_txn_refund is not None: try: - txid = self.submitTxn(coin_from, bid.initiate_txn_refund.hex()) + txid = ci_from.publishTx(bid.initiate_txn_refund) 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 ex: @@ -3240,7 +3232,7 @@ class BasicSwap(BaseApp): if (bid.getPTxState() == TxStates.TX_SENT or bid.getPTxState() == TxStates.TX_CONFIRMED) \ and bid.participate_txn_refund is not None: try: - txid = self.submitTxn(coin_to, bid.participate_txn_refund.hex()) + txid = ci_to.publishTx(bid.participate_txn_refund) 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 ex: diff --git a/basicswap/interface_btc.py b/basicswap/interface_btc.py index 438afff..ffcffbd 100644 --- a/basicswap/interface_btc.py +++ b/basicswap/interface_btc.py @@ -988,7 +988,7 @@ class BTCInterface(CoinInterface): if not addr_info['iswatchonly']: ro = self.rpc_callback('importaddress', [dest_address, 'bid', False]) self._log.info('Imported watch-only addr: {}'.format(dest_address)) - self._log.info('Rescanning chain from height: {}'.format(rescan_from)) + self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from)) self.rpc_callback('rescanblockchain', [rescan_from]) return_txid = True if txid is None else False diff --git a/basicswap/interface_nmc.py b/basicswap/interface_nmc.py index ec81393..cdb756b 100644 --- a/basicswap/interface_nmc.py +++ b/basicswap/interface_nmc.py @@ -1,12 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2020-2021 tecnovert +# Copyright (c) 2020-2022 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. from .interface_btc import BTCInterface from .chainparams import Coins +from .util import ( + make_int, +) class NMCInterface(BTCInterface): @@ -15,4 +18,25 @@ class NMCInterface(BTCInterface): return Coins.NMC def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index=False): - raise ValueError('TODO: Use scantxoutset') + self._log.debug('[rm] scantxoutset start') # scantxoutset is slow + ro = self.rpc_callback('scantxoutset', ['start', ['addr({})'.format(dest_address)]]) # TODO: Use combo(address) where possible + self._log.debug('[rm] scantxoutset end') + return_txid = True if txid is None else False + for o in ro['unspents']: + if txid and o['txid'] != txid.hex(): + continue + # Verify amount + if make_int(o['amount']) != int(bid_amount): + self._log.warning('Found output to lock tx address of incorrect value: %s, %s', str(o['amount']), o['txid']) + continue + + rv = { + 'depth': 0, + 'height': o['height']} + if o['height'] > 0: + rv['depth'] = ro['height'] - o['height'] + if find_index: + rv['index'] = o['vout'] + if return_txid: + rv['txid'] = o['txid'] + return rv diff --git a/basicswap/interface_part.py b/basicswap/interface_part.py index 0a863a7..6a680c8 100644 --- a/basicswap/interface_part.py +++ b/basicswap/interface_part.py @@ -667,7 +667,7 @@ class PARTInterfaceAnon(PARTInterface): wif_scan_key = toWIF(wif_prefix, kbv) self.rpc_callback('importstealthaddress', [wif_scan_key, Kbs.hex()]) self._log.info('Imported watch-only sx_addr: {}'.format(sx_addr)) - self._log.info('Rescanning chain from height: {}'.format(restore_height)) + self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), restore_height)) self.rpc_callback('rescanblockchain', [restore_height]) params = [{'include_watchonly': True, 'search': sx_addr}] @@ -700,7 +700,7 @@ class PARTInterfaceAnon(PARTInterface): wif_spend_key = toWIF(wif_prefix, kbs) self.rpc_callback('importstealthaddress', [wif_scan_key, wif_spend_key]) self._log.info('Imported spend key for sx_addr: {}'.format(sx_addr)) - self._log.info('Rescanning chain from height: {}'.format(restore_height)) + self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), restore_height)) self.rpc_callback('rescanblockchain', [restore_height]) autxos = self.rpc_callback('listunspentanon', [1, 9999999, [sx_addr]]) diff --git a/basicswap/protocols/atomic_swap_1.py b/basicswap/protocols/atomic_swap_1.py index 963bedb..8d9c0a7 100644 --- a/basicswap/protocols/atomic_swap_1.py +++ b/basicswap/protocols/atomic_swap_1.py @@ -54,7 +54,7 @@ def redeemITx(self, bid_id, session): ci_from = self.ci(offer.coin_from) txn = self.createRedeemTxn(ci_from.coin_type(), bid, for_txn_type='initiate') - txid = self.submitTxn(ci_from.coin_type(), txn) + txid = ci_from.publishTx(bytes.fromhex(txn)) bid.initiate_tx.spend_txid = bytes.fromhex(txid) self.log.debug('Submitted initiate redeem txn %s to %s chain for bid %s', txid, ci_from.coin_name(), bid_id.hex()) diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py index b7793f3..4176867 100644 --- a/tests/basicswap/extended/test_nmc.py +++ b/tests/basicswap/extended/test_nmc.py @@ -27,12 +27,13 @@ from basicswap.basicswap import ( SwapTypes, BidStates, TxStates, - ABS_LOCK_BLOCKS, - ABS_LOCK_TIME, ) from basicswap.util import ( COIN, ) +from basicswap.basicswap_util import ( + TxLockTypes, +) from basicswap.util.address import ( toWIF, ) @@ -99,8 +100,12 @@ def prepareOtherDir(datadir, nodeId, conf_file='namecoin.conf'): fp.write('debug=1\n') fp.write('debugexclude=libevent\n') + fp.write('fallbackfee=0.01\n') fp.write('acceptnonstdtxn=0\n') + if conf_file == 'bitcoin.conf': + fp.write('wallet=wallet.dat\n') + def prepareDir(datadir, nodeId, network_key, network_pubkey): node_dir = os.path.join(datadir, str(nodeId)) @@ -124,6 +129,8 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey): fp.write('debug=1\n') fp.write('debugexclude=libevent\n') fp.write('zmqpubsmsg=tcp://127.0.0.1:' + str(BASE_ZMQ_PORT + nodeId) + '\n') + fp.write('wallet=wallet.dat\n') + fp.write('fallbackfee=0.01\n') fp.write('acceptnonstdtxn=0\n') fp.write('minstakeinterval=5\n') @@ -262,13 +269,19 @@ class Test(unittest.TestCase): cls.swap_clients = [] cls.http_threads = [] - cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)), cfg.BITCOIN_BINDIR, cfg.BITCOIND)) + btc_data_dir = os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)) + if os.path.exists(os.path.join(cfg.BITCOIN_BINDIR, 'bitcoin-wallet')): + callrpc_cli(cfg.BITCOIN_BINDIR, btc_data_dir, 'regtest', '-wallet=wallet.dat create', 'bitcoin-wallet') + cls.daemons.append(startDaemon(btc_data_dir, cfg.BITCOIN_BINDIR, cfg.BITCOIND)) logging.info('Started %s %d', cfg.BITCOIND, cls.daemons[-1].pid) cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)), cfg.NAMECOIN_BINDIR, cfg.NAMECOIND)) logging.info('Started %s %d', cfg.NAMECOIND, cls.daemons[-1].pid) for i in range(NUM_NODES): - cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD)) + data_dir = os.path.join(cfg.TEST_DATADIRS, str(i)) + if os.path.exists(os.path.join(cfg.PARTICL_BINDIR, 'particl-wallet')): + callrpc_cli(cfg.PARTICL_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'particl-wallet') + cls.daemons.append(startDaemon(data_dir, cfg.PARTICL_BINDIR, cfg.PARTICLD)) logging.info('Started %s %d', cfg.PARTICLD, cls.daemons[-1].pid) for i in range(NUM_NODES): @@ -361,11 +374,11 @@ class Test(unittest.TestCase): super(Test, cls).tearDownClass() - def test_02_part_ltc(self): + def test_02_part_nmc(self): logging.info('---------- Test PART to NMC') swap_clients = self.swap_clients - offer_id = swap_clients[0].postOffer(Coins.PART, Coins.NMC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) + offer_id = swap_clients[0].postOffer(Coins.PART, Coins.NMC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST, TxLockTypes.ABS_LOCK_TIME) wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() @@ -392,7 +405,7 @@ class Test(unittest.TestCase): logging.info('---------- Test NMC to PART') swap_clients = self.swap_clients - offer_id = swap_clients[1].postOffer(Coins.NMC, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) + offer_id = swap_clients[1].postOffer(Coins.NMC, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, TxLockTypes.ABS_LOCK_TIME) wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() @@ -417,7 +430,7 @@ class Test(unittest.TestCase): logging.info('---------- Test NMC to BTC') swap_clients = self.swap_clients - offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) + offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, TxLockTypes.ABS_LOCK_TIME) wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() @@ -447,7 +460,7 @@ class Test(unittest.TestCase): swap_clients = self.swap_clients offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, - ABS_LOCK_BLOCKS, 10) + TxLockTypes.ABS_LOCK_BLOCKS, 10) wait_for_offer(delay_event, swap_clients[1], offer_id) offers = swap_clients[1].listOffers() @@ -473,7 +486,7 @@ class Test(unittest.TestCase): js_0_before = read_json_api(1800) - offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) + offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, TxLockTypes.ABS_LOCK_TIME) wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() @@ -497,7 +510,7 @@ class Test(unittest.TestCase): js_0_before = read_json_api(1800) - offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME) + offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST, TxLockTypes.ABS_LOCK_TIME) wait_for_offer(delay_event, swap_clients[0], offer_id) offers = swap_clients[0].listOffers() @@ -507,9 +520,8 @@ class Test(unittest.TestCase): wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - swap_clients[0].coin_clients[Coins.BTC]['override_feerate'] = 10.0 - swap_clients[0].coin_clients[Coins.NMC]['override_feerate'] = 10.0 - + swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 + swap_clients[0].getChainClientSettings(Coins.NMC)['override_feerate'] = 10.0 wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) def pass_99_delay(self): diff --git a/tox.ini b/tox.ini index df0a19a..f99fc0c 100644 --- a/tox.ini +++ b/tox.ini @@ -22,6 +22,6 @@ commands = [pytest] addopts = -v -s -norecursedirs = tests/basicswap/extended +norecursedirs = tests/basicswap/extended tests/basicswap/selenium testpaths = tests