mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
Merge pull request #296 from nahuhh/xmr_open
xmr: detect corrupt wallets
This commit is contained in:
@@ -1067,7 +1067,9 @@ class BasicSwap(BaseApp):
|
|||||||
elif c in (Coins.XMR, Coins.WOW):
|
elif c in (Coins.XMR, Coins.WOW):
|
||||||
try:
|
try:
|
||||||
ci.ensureWalletExists()
|
ci.ensureWalletExists()
|
||||||
except Exception as e: # noqa: F841
|
except Exception as e:
|
||||||
|
if "invalid signature" in str(e): # wallet is corrupt
|
||||||
|
raise
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
f"Can't open {ci.coin_name()} wallet, could be locked."
|
f"Can't open {ci.coin_name()} wallet, could be locked."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,3 +30,27 @@ class WOWInterface(XMRInterface):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def depth_spendable() -> int:
|
def depth_spendable() -> int:
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
|
# below only needed until wow is rebased to monero v0.18.4.0+
|
||||||
|
def openWallet(self, filename):
|
||||||
|
params = {"filename": filename}
|
||||||
|
if self._wallet_password is not None:
|
||||||
|
params["password"] = self._wallet_password
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.rpc_wallet("open_wallet", params)
|
||||||
|
except Exception as e:
|
||||||
|
if "no connection to daemon" in str(e):
|
||||||
|
self._log.debug(f"{self.coin_name()} {e}")
|
||||||
|
return # bypass refresh error to allow startup with a busy daemon
|
||||||
|
|
||||||
|
try:
|
||||||
|
# TODO Remove `store` after upstream fix to autosave on close_wallet
|
||||||
|
self.rpc_wallet("store")
|
||||||
|
self.rpc_wallet("close_wallet")
|
||||||
|
self._log.debug(f"Attempt to save and close {self.coin_name()} wallet")
|
||||||
|
except Exception as e: # noqa: F841
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.rpc_wallet("open_wallet", params)
|
||||||
|
self._log.debug(f"Reattempt to open {self.coin_name()} wallet")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
import basicswap.contrib.ed25519_fast as edf
|
import basicswap.contrib.ed25519_fast as edf
|
||||||
import basicswap.ed25519_fast_util as edu
|
import basicswap.ed25519_fast_util as edu
|
||||||
@@ -204,18 +205,54 @@ class XMRInterface(CoinInterface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
if "no connection to daemon" in str(e):
|
if "no connection to daemon" in str(e):
|
||||||
self._log.debug(f"{self.coin_name()} {e}")
|
self._log.debug(f"{self.coin_name()} {e}")
|
||||||
return # bypass refresh error to allow startup with a busy daemon
|
return # Bypass refresh error to allow startup with a busy daemon
|
||||||
|
if any(
|
||||||
|
x in str(e)
|
||||||
|
for x in (
|
||||||
|
"invalid signature",
|
||||||
|
"std::bad_alloc",
|
||||||
|
"basic_string::_M_replace_aux",
|
||||||
|
)
|
||||||
|
):
|
||||||
|
self._log.error(f"{self.coin_name()} wallet is corrupt.")
|
||||||
|
chain_client_settings = self._sc.getChainClientSettings(
|
||||||
|
self.coin_type()
|
||||||
|
) # basicswap.json
|
||||||
|
if chain_client_settings.get("manage_wallet_daemon", False):
|
||||||
|
self._log.info(f"Renaming {self.coin_name()} wallet cache file.")
|
||||||
|
walletpath = os.path.join(
|
||||||
|
chain_client_settings.get("datadir", "none"),
|
||||||
|
"wallets",
|
||||||
|
filename,
|
||||||
|
)
|
||||||
|
if not os.path.isfile(walletpath):
|
||||||
|
self._log.warning(
|
||||||
|
f"Could not find {self.coin_name()} wallet cache file."
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
bkp_path = walletpath + ".corrupt"
|
||||||
|
for i in range(100):
|
||||||
|
if not os.path.exists(bkp_path):
|
||||||
|
break
|
||||||
|
bkp_path = walletpath + f".corrupt{i}"
|
||||||
|
if os.path.exists(bkp_path):
|
||||||
|
self._log.error(
|
||||||
|
f"Could not find backup path for {self.coin_name()} wallet."
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
os.rename(walletpath, bkp_path)
|
||||||
|
# Drop through to open_wallet
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
# TODO Remove `store` after upstream fix to autosave on close_wallet
|
|
||||||
self.rpc_wallet("store")
|
|
||||||
self.rpc_wallet("close_wallet")
|
self.rpc_wallet("close_wallet")
|
||||||
self._log.debug(f"Attempt to save and close {self.coin_name()} wallet")
|
self._log.debug(f"Closing {self.coin_name()} wallet")
|
||||||
except Exception as e: # noqa: F841
|
except Exception as e: # noqa: F841
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.rpc_wallet("open_wallet", params)
|
self.rpc_wallet("open_wallet", params)
|
||||||
self._log.debug(f"Reattempt to open {self.coin_name()} wallet")
|
self._log.debug(f"Attempting to open {self.coin_name()} wallet")
|
||||||
|
|
||||||
def initialiseWallet(
|
def initialiseWallet(
|
||||||
self, key_view: bytes, key_spend: bytes, restore_height=None
|
self, key_view: bytes, key_spend: bytes, restore_height=None
|
||||||
|
|||||||
@@ -1102,6 +1102,62 @@ class Test(BaseTest):
|
|||||||
def notest_00_delay(self):
|
def notest_00_delay(self):
|
||||||
test_delay_event.wait(100000)
|
test_delay_event.wait(100000)
|
||||||
|
|
||||||
|
def test_007_corrupt_wallet(self):
|
||||||
|
logging.info(f"---------- Test {Coins.XMR.name} corrupt wallet")
|
||||||
|
swap_clients = self.swap_clients
|
||||||
|
ci = swap_clients[0].ci(Coins.XMR)
|
||||||
|
|
||||||
|
chain_client_settings = swap_clients[0].getChainClientSettings(Coins.XMR)
|
||||||
|
wallet_name = chain_client_settings["wallet_name"]
|
||||||
|
try:
|
||||||
|
ci.rpc_wallet("close_wallet")
|
||||||
|
logging.info(f"Closing {ci.coin_name()} wallet")
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f"Closing {ci.coin_name()} wallet failed with: {e}")
|
||||||
|
|
||||||
|
walletpath = os.path.join(
|
||||||
|
chain_client_settings.get("datadir", "none"), "wallets", wallet_name
|
||||||
|
)
|
||||||
|
wallet_cache_bytes = os.path.getsize(walletpath)
|
||||||
|
logging.info(f"[rm] wallet_cache_bytes {wallet_cache_bytes}")
|
||||||
|
logging.info(f"[rm] walletpath {walletpath}")
|
||||||
|
shutil.copy(walletpath, walletpath + ".orig")
|
||||||
|
|
||||||
|
# Failed to open wallet : basic_string::_M_replace_aux
|
||||||
|
# with open(walletpath, "wb") as fp:
|
||||||
|
# fp.write(os.urandom(wallet_cache_bytes))
|
||||||
|
|
||||||
|
# Failed to open wallet : std::bad_alloc
|
||||||
|
with open(walletpath, "ab") as fp:
|
||||||
|
fp.write(os.urandom(1000))
|
||||||
|
|
||||||
|
# TODO: Get "invalid signature"
|
||||||
|
|
||||||
|
try:
|
||||||
|
ci.openWallet(wallet_name)
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f"Opening {ci.coin_name()} wallet failed with: {e}")
|
||||||
|
assert any(
|
||||||
|
x in str(e)
|
||||||
|
for x in (
|
||||||
|
"invalid signature",
|
||||||
|
"std::bad_alloc",
|
||||||
|
"basic_string::_M_replace_aux",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError("Should fail!")
|
||||||
|
|
||||||
|
try:
|
||||||
|
chain_client_settings["manage_wallet_daemon"] = True
|
||||||
|
try:
|
||||||
|
ci.openWallet(wallet_name)
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f"Opening {ci.coin_name()} wallet failed with: {e}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
chain_client_settings["manage_wallet_daemon"] = False
|
||||||
|
|
||||||
def test_010_txn_size(self):
|
def test_010_txn_size(self):
|
||||||
logging.info("---------- Test {} txn_size".format(Coins.PART))
|
logging.info("---------- Test {} txn_size".format(Coins.PART))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user