diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 2e4901f..1d517c5 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -656,7 +656,11 @@ class BasicSwap(BaseApp): self.log.error('Sanity checks failed: %s', str(e)) elif c == Coins.XMR: - ci.ensureWalletExists() + try: + ci.ensureWalletExists() + except Exception as e: + self.log.warning('Can\'t open XMR wallet, could be locked.') + continue self.checkWalletSeed(c) @@ -740,7 +744,7 @@ class BasicSwap(BaseApp): yield c def changeWalletPasswords(self, old_password, new_password): - + # Only the main wallet password is changed for monero, avoid issues by preventing until active swaps are complete if len(self.swaps_in_progress) > 0: raise ValueError('Can\'t change passwords while swaps are in progress') @@ -767,6 +771,7 @@ class BasicSwap(BaseApp): if coin_type == Coins.PART: return ci = self.ci(coin_type) + db_key_coin_name = ci.coin_name().lower() self.log.info('Initialising {} wallet.'.format(ci.coin_name())) if coin_type == Coins.XMR: @@ -775,7 +780,7 @@ class BasicSwap(BaseApp): ci.initialiseWallet(key_view, key_spend) root_address = ci.getAddressFromKeys(key_view, key_spend) - key_str = 'main_wallet_addr_' + ci.coin_name().lower() + key_str = 'main_wallet_addr_' + db_key_coin_name self.setStringKV(key_str, root_address) return @@ -789,9 +794,23 @@ class BasicSwap(BaseApp): self.log.error('initialiseWallet failed: {}'.format(str(e))) if raise_errors: raise e + return - key_str = 'main_wallet_seedid_' + ci.coin_name().lower() - self.setStringKV(key_str, root_hash.hex()) + try: + session = self.openSession() + key_str = 'main_wallet_seedid_' + db_key_coin_name + self.setStringKV(key_str, root_hash.hex(), session) + + # Clear any saved addresses + self.clearStringKV('receive_addr_' + db_key_coin_name, session) + self.clearStringKV('stealth_addr_' + db_key_coin_name, session) + + coin_id = int(coin_type) + info_type = 1 # wallet + query_str = f'DELETE FROM wallets WHERE coin_id = {coin_id} AND balance_type = {info_type}' + session.execute(query_str) + finally: + self.closeSession(session) def updateIdentityBidState(self, session, address, bid): identity_stats = session.query(KnownIdentity).filter_by(address=address).first() @@ -831,20 +850,18 @@ class BasicSwap(BaseApp): session.remove() self.mxDB.release() - def setStringKV(self, str_key, str_val): - with self.mxDB: - try: - session = scoped_session(self.session_factory) - kv = session.query(DBKVString).filter_by(key=str_key).first() - if not kv: - kv = DBKVString(key=str_key, value=str_val) - else: - kv.value = str_val - session.add(kv) - session.commit() - finally: - session.close() - session.remove() + def setStringKV(self, str_key, str_val, session=None): + try: + use_session = self.openSession(session) + kv = use_session.query(DBKVString).filter_by(key=str_key).first() + if not kv: + kv = DBKVString(key=str_key, value=str_val) + else: + kv.value = str_val + use_session.add(kv) + finally: + if session is None: + self.closeSession(use_session) def getStringKV(self, str_key): self.mxDB.acquire() @@ -859,6 +876,16 @@ class BasicSwap(BaseApp): session.remove() self.mxDB.release() + def clearStringKV(self, str_key, str_val): + with self.mxDB: + try: + session = scoped_session(self.session_factory) + session.execute('DELETE FROM kv_string WHERE key = "{}" '.format(str_key)) + session.commit() + finally: + session.close() + session.remove() + def activateBid(self, session, bid): if bid.bid_id in self.swaps_in_progress: self.log.debug('Bid %s is already in progress', bid.bid_id.hex()) @@ -1442,6 +1469,7 @@ class BasicSwap(BaseApp): record.bid_id = bid_id record.tx_type = tx_type addr = record.addr + ensure(self.ci(coin_type).isAddressMine(addr), 'Pool address not owned by wallet!') session.add(record) session.commit() finally: @@ -1539,6 +1567,7 @@ class BasicSwap(BaseApp): if expect_address is None: self.log.warning('Can\'t find expected main wallet address for coin {}'.format(ci.coin_name())) return False + ci._have_checked_seed = True if expect_address == ci.getMainWalletAddress(): ci.setWalletSeedWarning(False) return True diff --git a/basicswap/interface/btc.py b/basicswap/interface/btc.py index 0277bd2..89063e0 100644 --- a/basicswap/interface/btc.py +++ b/basicswap/interface/btc.py @@ -188,6 +188,7 @@ class BTCInterface(CoinInterface): self._connection_type = coin_settings['connection_type'] self._sc = swap_client self._log = self._sc.log if self._sc and self._sc.log else logging + self._expect_seedid_hex = None def using_segwit(self): return self._use_segwit @@ -297,6 +298,7 @@ class BTCInterface(CoinInterface): return self.rpc_callback('getwalletinfo')['hdseedid'] def checkExpectedSeed(self, expect_seedid): + self._expect_seedid_hex = expect_seedid return expect_seedid == self.getWalletSeedID() def getNewAddress(self, use_segwit, label='swap_receive'): @@ -305,6 +307,15 @@ class BTCInterface(CoinInterface): args.append('bech32') return self.rpc_callback('getnewaddress', args) + def isAddressMine(self, address): + addr_info = self.rpc_callback('getaddressinfo', [address]) + return addr_info['ismine'] + + def checkAddressMine(self, address): + addr_info = self.rpc_callback('getaddressinfo', [address]) + ensure(addr_info['ismine'], 'ismine is false') + ensure(addr_info['hdseedid'] == self._expect_seedid_hex, 'unexpected seedid') + def get_fee_rate(self, conf_target=2): try: fee_rate = self.rpc_callback('estimatesmartfee', [conf_target])['feerate'] diff --git a/basicswap/interface/firo.py b/basicswap/interface/firo.py index 0738853..20a3aec 100644 --- a/basicswap/interface/firo.py +++ b/basicswap/interface/firo.py @@ -55,6 +55,10 @@ class FIROInterface(BTCInterface): addr_info = self.rpc_callback('validateaddress', [address]) return addr_info['iswatchonly'] + def isAddressMine(self, address): + addr_info = self.rpc_callback('validateaddress', [address]) + return addr_info['ismine'] + def getSCLockScriptAddress(self, lock_script): lock_tx_dest = self.getScriptDest(lock_script) address = self.encodeScriptDest(lock_tx_dest) diff --git a/basicswap/interface/xmr.py b/basicswap/interface/xmr.py index 38e41d7..f472052 100644 --- a/basicswap/interface/xmr.py +++ b/basicswap/interface/xmr.py @@ -75,6 +75,7 @@ class XMRInterface(CoinInterface): self._sc = swap_client self._log = self._sc.log if self._sc and self._sc.log else logging self._wallet_password = None + self._have_checked_seed = False def setFeePriority(self, new_priority): ensure(new_priority >= 0 and new_priority < 4, 'Invalid fee_priority value') @@ -534,6 +535,13 @@ class XMRInterface(CoinInterface): self._log.info('unlockWallet - {}'.format(self.ticker())) self._wallet_password = password + if not self._have_checked_seed: + self._sc.checkWalletSeed(self.coin_type()) + def lockWallet(self): self._log.info('lockWallet - {}'.format(self.ticker())) self._wallet_password = None + + def isAddressMine(self, address): + # TODO + return True diff --git a/basicswap/js_server.py b/basicswap/js_server.py index 0285241..892a56b 100644 --- a/basicswap/js_server.py +++ b/basicswap/js_server.py @@ -32,6 +32,7 @@ from .ui.util import ( have_data_entry, tickerToCoinId, listOldBidStates, + checkAddressesOwned, ) from .ui.page_offers import postNewOffer from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey @@ -117,8 +118,10 @@ def js_wallets(self, url_split, post_string, is_json): rv = swap_client.getWalletInfo(coin_type) rv.update(swap_client.getBlockchainInfo(coin_type)) + ci = swap_client.ci(coin_type) + checkAddressesOwned(ci, rv) return bytes(json.dumps(rv), 'UTF-8') - return bytes(json.dumps(self.server.swap_client.getWalletsInfo({'ticker_key': True})), 'UTF-8') + return bytes(json.dumps(swap_client.getWalletsInfo({'ticker_key': True})), 'UTF-8') def js_offers(self, url_split, post_string, is_json, sent=False): diff --git a/basicswap/templates/wallet.html b/basicswap/templates/wallet.html index 9c6086d..72f494f 100644 --- a/basicswap/templates/wallet.html +++ b/basicswap/templates/wallet.html @@ -170,12 +170,18 @@