From b01c6979f18e3aa3426dc550f3b5b954ba1dc031 Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Sat, 26 Aug 2017 17:33:10 +0300 Subject: [PATCH] allow non-fast sync in recovery in Qt --- jmclient/jmclient/blockchaininterface.py | 42 ++++++++++++++++-------- scripts/joinmarket-qt.py | 29 ++++++++++------ 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/jmclient/jmclient/blockchaininterface.py b/jmclient/jmclient/blockchaininterface.py index c358a90..d3e9894 100644 --- a/jmclient/jmclient/blockchaininterface.py +++ b/jmclient/jmclient/blockchaininterface.py @@ -45,8 +45,8 @@ class BlockchainInterface(object): def __init__(self): pass - def sync_wallet(self, wallet): - self.sync_addresses(wallet) + def sync_wallet(self, wallet, restart_cb=None): + self.sync_addresses(wallet, restart_cb) self.sync_unspent(wallet) @abc.abstractmethod @@ -234,7 +234,7 @@ class BitcoinCoreInterface(BlockchainInterface): for addr in addr_list: self.rpc('importaddress', [addr, wallet_name, False]) - def add_watchonly_addresses(self, addr_list, wallet_name): + def add_watchonly_addresses(self, addr_list, wallet_name, restart_cb=None): """For backwards compatibility, this fn name is preserved as the case where we quit the program if a rescan is required; but in some cases a rescan is not required (if the address is known @@ -244,19 +244,23 @@ class BitcoinCoreInterface(BlockchainInterface): if jm_single().config.get("BLOCKCHAIN", "blockchain_source") != 'regtest': #pragma: no cover #Exit conditions cannot be included in tests - print('restart Bitcoin Core with -rescan if you\'re ' - 'recovering an existing wallet from backup seed') - print(' otherwise just restart this joinmarket script') - sys.exit(0) + restart_msg = ("restart Bitcoin Core with -rescan if you're " + "recovering an existing wallet from backup seed\n" + "Otherwise just restart this joinmarket application.") + if restart_cb: + restart_cb(restart_msg) + else: + print(restart_msg) + sys.exit(0) - def sync_wallet(self, wallet, fast=False): + def sync_wallet(self, wallet, fast=False, restart_cb=None): #trigger fast sync if the index_cache is available #(and not specifically disabled). if fast: self.sync_wallet_fast(wallet) self.fast_sync_called = True return - super(BitcoinCoreInterface, self).sync_wallet(wallet) + super(BitcoinCoreInterface, self).sync_wallet(wallet, restart_cb=restart_cb) self.fast_sync_called = False def sync_wallet_fast(self, wallet): @@ -290,7 +294,19 @@ class BitcoinCoreInterface(BlockchainInterface): if len(addr_info) < 3 or addr_info[2] != wallet_name: continue used_address_dict[addr_info[0]] = (addr_info[1], addr_info[2]) - + #for a first run, import first chunk + if len(used_address_dict.keys()) == 0: + log.info("Detected new wallet, performing initial import") + for i in range(wallet.max_mix_depth): + for j in [0, 1]: + addrs_to_import = [] + for k in range(wallet.gaplimit + 10): # a few more for safety + addrs_to_import.append(wallet.get_addr(i, j, k)) + self.import_addresses(addrs_to_import, wallet_name) + wallet.index[i][j] = 0 + self.wallet_synced = True + return + #Wallet has been used; scan forwards. log.debug("Fast sync in progress. Got this many used addresses: " + str( len(used_address_dict))) #Need to have wallet.index point to the last used address @@ -353,7 +369,7 @@ class BitcoinCoreInterface(BlockchainInterface): self.wallet_synced = True - def sync_addresses(self, wallet): + def sync_addresses(self, wallet, restart_cb=None): from jmclient.wallet import BitcoinCoreWallet if isinstance(wallet, BitcoinCoreWallet): @@ -401,7 +417,7 @@ class BitcoinCoreInterface(BlockchainInterface): wallet_addr_list.append(imported_addr) imported_addr_list = self.rpc('getaddressesbyaccount', [wallet_name]) if not set(wallet_addr_list).issubset(set(imported_addr_list)): - self.add_watchonly_addresses(wallet_addr_list, wallet_name) + self.add_watchonly_addresses(wallet_addr_list, wallet_name, restart_cb) return buf = self.rpc('listtransactions', [wallet_name, 1000, 0, True]) @@ -478,7 +494,7 @@ class BitcoinCoreInterface(BlockchainInterface): for _ in range(addr_req_count * 3) ] - self.add_watchonly_addresses(wallet_addr_list, wallet_name) + self.add_watchonly_addresses(wallet_addr_list, wallet_name, restart_cb) return self.wallet_synced = True diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index 93d6551..7b4cb3c 100644 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -1298,6 +1298,11 @@ class JMMainWindow(QMainWindow): return None return str(message_e.toPlainText()) + def restartForScan(self, msg): + JMQtMessageBox(self, msg, mbtype='info', + title="Restart") + self.close() + def recoverWallet(self): success = wallet_generate_recover_bip39("recover", "wallets", "wallet.json", @@ -1312,9 +1317,9 @@ class JMMainWindow(QMainWindow): return JMQtMessageBox(self, 'Wallet saved to ' + self.walletname, title="Wallet created") - self.initWallet(seed=self.walletname) + self.initWallet(seed=self.walletname, restart_cb=self.restartForScan) - def selectWallet(self, testnet_seed=None): + def selectWallet(self, testnet_seed=None, restart_cb=None): if jm_single().config.get("BLOCKCHAIN", "blockchain_source") != "regtest": current_path = os.path.dirname(os.path.realpath(__file__)) if os.path.isdir(os.path.join(current_path, 'wallets')): @@ -1335,7 +1340,7 @@ class JMMainWindow(QMainWindow): if not ok: return pwd = str(text).strip() - decrypted = self.loadWalletFromBlockchain(firstarg, pwd) + decrypted = self.loadWalletFromBlockchain(firstarg, pwd, restart_cb) else: if not testnet_seed: testnet_seed, ok = QInputDialog.getText(self, @@ -1347,9 +1352,9 @@ class JMMainWindow(QMainWindow): firstarg = str(testnet_seed) pwd = None #ignore return value as there is no decryption failure possible - self.loadWalletFromBlockchain(firstarg, pwd) + self.loadWalletFromBlockchain(firstarg, pwd, restart_cb) - def loadWalletFromBlockchain(self, firstarg=None, pwd=None): + def loadWalletFromBlockchain(self, firstarg=None, pwd=None, restart_cb=None): if (firstarg and pwd) or (firstarg and get_network() == 'testnet'): try: self.wallet = SegwitWallet( @@ -1367,12 +1372,15 @@ class JMMainWindow(QMainWindow): if 'listunspent_args' not in jm_single().config.options('POLICY'): jm_single().config.set('POLICY', 'listunspent_args', '[0]') assert self.wallet, "No wallet loaded" - reactor.callLater(0, self.syncWalletUpdate, True) + reactor.callLater(0, self.syncWalletUpdate, True, restart_cb) self.statusBar().showMessage("Reading wallet from blockchain ...") return True - def syncWalletUpdate(self, fast): - sync_wallet(self.wallet, fast=fast) + def syncWalletUpdate(self, fast, restart_cb=None): + if restart_cb: + fast=False + jm_single().bc_interface.sync_wallet(self.wallet, fast=fast, + restart_cb=restart_cb) self.updateWalletInfo() def updateWalletInfo(self): @@ -1452,7 +1460,7 @@ class JMMainWindow(QMainWindow): mb.setStandardButtons(QMessageBox.Ok) ret = mb.exec_() - def initWallet(self, seed=None): + def initWallet(self, seed=None, restart_cb=None): '''Creates a new wallet if seed not provided. Initializes by syncing. ''' @@ -1470,7 +1478,8 @@ class JMMainWindow(QMainWindow): return JMQtMessageBox(self, 'Wallet saved to ' + self.walletname, title="Wallet created") - self.loadWalletFromBlockchain(self.walletname, pwd=self.textpassword) + self.loadWalletFromBlockchain(self.walletname, pwd=self.textpassword, + restart_cb=restart_cb) def get_wallet_printout(wallet): """Given a joinmarket wallet, retrieve the list of