diff --git a/basicswap/__init__.py b/basicswap/__init__.py index e9cf488..1bf9331 100644 --- a/basicswap/__init__.py +++ b/basicswap/__init__.py @@ -1,3 +1,3 @@ name = "basicswap" -__version__ = "0.0.30" +__version__ = "0.0.31" diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 3524840..73c0584 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -1474,7 +1474,7 @@ class BasicSwap(BaseApp): wrh = session.query(DBKVInt).filter_by(key=key_str).first().value except Exception: wrh = ci.getWalletRestoreHeight() - self.log.info('Found restore height for %s', ci.coin_name()) + self.log.info('Found restore height for %s, block %d', ci.coin_name(), wrh) session.add(DBKVInt( key=key_str, value=wrh diff --git a/basicswap/interface_btc.py b/basicswap/interface_btc.py index c447951..8afcfb7 100644 --- a/basicswap/interface_btc.py +++ b/basicswap/interface_btc.py @@ -5,10 +5,12 @@ # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. +import json import time import base64 import hashlib import logging +import traceback from io import BytesIO from basicswap.contrib.test_framework import segwit_addr @@ -67,7 +69,7 @@ from .basicswap_util import ( TxLockTypes) from .chainparams import CoinInterface, Coins -from .rpc import make_rpc_func +from .rpc import make_rpc_func, openrpc SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds @@ -168,13 +170,34 @@ class BTCInterface(CoinInterface): def __init__(self, coin_settings, network, swap_client=None): super().__init__(network) - rpc_host = coin_settings.get('rpchost', '127.0.0.1') - self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth'], host=rpc_host) + self._rpc_host = coin_settings.get('rpchost', '127.0.0.1') + self._rpcport = coin_settings['rpcport'] + self._rpcauth = coin_settings['rpcauth'] + self.rpc_callback = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host) self.blocks_confirmed = coin_settings['blocks_confirmed'] self.setConfTarget(coin_settings['conf_target']) self._sc = swap_client self._log = self._sc.log if self._sc and self._sc.log else logging + def open_rpc(self, wallet=None): + return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host) + + def json_request(self, rpc_conn, method, params): + try: + v = rpc_conn.json_request(method, params) + r = json.loads(v.decode('utf-8')) + except Exception as ex: + traceback.print_exc() + raise ValueError('RPC Server Error ' + str(ex)) + + if 'error' in r and r['error'] is not None: + raise ValueError('RPC error ' + str(r['error'])) + + return r['result'] + + def close_rpc(self, rpc_conn): + rpc_conn.close() + def setConfTarget(self, new_conf_target): assert(new_conf_target >= 1 and new_conf_target < 33), 'Invalid conf_target value' self._conf_target = new_conf_target @@ -226,12 +249,18 @@ class BTCInterface(CoinInterface): if chain_synced < 1.0: raise ValueError('{} chain isn\'t synced.'.format(self.coin_name())) - block_hash = best_block - while True: - block_header = self.rpc_callback('getblockheader', [block_hash]) - if block_header['time'] < start_time: - return block_header['height'] - block_hash = block_header['previousblockhash'] + self._log.debug('Finding block at time: {}'.format(start_time)) + + rpc_conn = self.open_rpc() + try: + block_hash = best_block + while True: + block_header = self.json_request(rpc_conn, 'getblockheader', [block_hash]) + if block_header['time'] < start_time: + return block_header['height'] + block_hash = block_header['previousblockhash'] + finally: + self.close_rpc(rpc_conn) def getWalletSeedID(self): return self.rpc_callback('getwalletinfo')['hdseedid'] diff --git a/basicswap/interface_part.py b/basicswap/interface_part.py index ed10fec..321d2c4 100644 --- a/basicswap/interface_part.py +++ b/basicswap/interface_part.py @@ -109,6 +109,21 @@ class PARTInterface(BTCInterface): length += getWitnessElementLen(len(e) // 2) # hex -> bytes return length + def getWalletRestoreHeight(self): + start_time = self.rpc_callback('getwalletinfo')['keypoololdest'] + + blockchaininfo = self.rpc_callback('getblockchaininfo') + best_block = blockchaininfo['bestblockhash'] + + chain_synced = round(blockchaininfo['verificationprogress'], 3) + if chain_synced < 1.0: + raise ValueError('{} chain isn\'t synced.'.format(self.coin_name())) + + self._log.debug('Finding block at time: {}'.format(start_time)) + block_hash = self.rpc_callback('getblockhashafter', [start_time]) + block_header = self.rpc_callback('getblockheader', [block_hash]) + return block_header['height'] + class PARTInterfaceBlind(PARTInterface): @staticmethod diff --git a/basicswap/rpc.py b/basicswap/rpc.py index 4986d51..2fcd641 100644 --- a/basicswap/rpc.py +++ b/basicswap/rpc.py @@ -107,7 +107,7 @@ def callrpc(rpc_port, auth, method, params=[], wallet=None, host='127.0.0.1'): r = json.loads(v.decode('utf-8')) except Exception as ex: traceback.print_exc() - raise ValueError('RPC Server Error ' + str(ex)) + raise ValueError('RPC server error ' + str(ex)) if 'error' in r and r['error'] is not None: raise ValueError('RPC error ' + str(r['error'])) @@ -115,6 +115,17 @@ def callrpc(rpc_port, auth, method, params=[], wallet=None, host='127.0.0.1'): return r['result'] +def openrpc(rpc_port, auth, wallet=None, host='127.0.0.1'): + try: + url = 'http://{}@{}:{}/'.format(auth, host, rpc_port) + if wallet is not None: + url += 'wallet/' + urllib.parse.quote(wallet) + return Jsonrpc(url) + except Exception as ex: + traceback.print_exc() + raise ValueError('RPC error ' + str(ex)) + + def callrpc_cli(bindir, datadir, chain, cmd, cli_bin='particl-cli'): cli_bin = os.path.join(bindir, cli_bin) diff --git a/doc/release-notes.md b/doc/release-notes.md index a55aa51..9aa581a 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -3,6 +3,15 @@ ============== +0.0.31 +============== + +- XMR swaps: Coin to balance is checked before sending a bid. +- Use getblockhashafter command in getWalletRestoreHeight where possible. + - Avoids rpc Errno 49 issue + - Reuse rpc connection when getblockhashafter is not available. + + 0.0.30 ==============