mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
Revert active hdchains.
This commit is contained in:
@@ -3115,8 +3115,6 @@ def main():
|
||||
for c in with_coins:
|
||||
prepareDataDir(c, settings, chain, particl_wallet_mnemonic, extra_opts)
|
||||
|
||||
save_config(config_path, settings)
|
||||
|
||||
if particl_wallet_mnemonic == "none":
|
||||
save_config(config_path, settings)
|
||||
logger.info("Done.")
|
||||
|
||||
@@ -10,9 +10,13 @@ import base64
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import mmap
|
||||
import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
import traceback
|
||||
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
from basicswap.basicswap_util import (
|
||||
@@ -413,7 +417,18 @@ class BTCInterface(Secp256k1Interface):
|
||||
raise ValueError("Failed to import descriptors.")
|
||||
else:
|
||||
key_wif = self.encodeKey(key_bytes)
|
||||
self.rpc_wallet("sethdseed", [True, key_wif])
|
||||
try:
|
||||
self.rpc_wallet("sethdseed", [True, key_wif])
|
||||
except Exception as e:
|
||||
self._log.debug(f"sethdseed failed: {e}")
|
||||
"""
|
||||
# TODO: Find derived key counts
|
||||
if "Already have this key" in str(e):
|
||||
key_id: bytes = self.getSeedHash(key_bytes)
|
||||
self.setActiveKeyChain(key_id)
|
||||
else:
|
||||
"""
|
||||
raise (e)
|
||||
|
||||
def getWalletInfo(self):
|
||||
rv = self.rpc_wallet("getwalletinfo")
|
||||
@@ -1984,12 +1999,95 @@ class BTCInterface(Secp256k1Interface):
|
||||
|
||||
def encryptWallet(self, password: str, check_seed: bool = True):
|
||||
# Watchonly wallets are not encrypted
|
||||
|
||||
# Workaround for https://github.com/bitcoin/bitcoin/issues/26607
|
||||
seed_id_before: str = self.getWalletSeedID()
|
||||
|
||||
chain_client_settings = self._sc.getChainClientSettings(
|
||||
self.coin_type()
|
||||
) # basicswap.json
|
||||
if (
|
||||
chain_client_settings.get("manage_daemon", False)
|
||||
and check_seed is True
|
||||
and seed_id_before != "Not found"
|
||||
):
|
||||
self.rpc("unloadwallet", [self._rpc_wallet])
|
||||
|
||||
# Store active keys
|
||||
seedid_bytes: bytes = bytes.fromhex(seed_id_before)[::-1]
|
||||
orig_active_descriptors = []
|
||||
orig_hdchain_bytes = None
|
||||
walletpath = None
|
||||
max_hdchain_key_count: int = 4000000 # Arbitrary
|
||||
|
||||
datadir = chain_client_settings["datadir"]
|
||||
if self._network != "mainnet":
|
||||
datadir = os.path.join(datadir, self._network)
|
||||
try_wallet_path = os.path.join(datadir, self._rpc_wallet)
|
||||
if os.path.exists(try_wallet_path):
|
||||
walletpath = try_wallet_path
|
||||
else:
|
||||
try_wallet_path = os.path.join(datadir, "wallets", self._rpc_wallet)
|
||||
if os.path.exists(try_wallet_path):
|
||||
walletpath = try_wallet_path
|
||||
|
||||
walletfilepath = walletpath
|
||||
if os.path.isdir(walletpath):
|
||||
walletfilepath = os.path.join(walletpath, "wallet.dat")
|
||||
|
||||
if walletpath is None:
|
||||
self._log.warning(f"Unable to find {self.ticker()} wallet path.")
|
||||
else:
|
||||
if self._use_descriptors:
|
||||
orig_active_descriptors = []
|
||||
with sqlite3.connect(walletfilepath) as conn:
|
||||
c = conn.cursor()
|
||||
rows = c.execute(
|
||||
"SELECT * FROM main WHERE key in (:kext, :kint)",
|
||||
{
|
||||
"kext": bytes.fromhex(
|
||||
"1161637469766565787465726e616c73706b02"
|
||||
),
|
||||
"kint": bytes.fromhex(
|
||||
"11616374697665696e7465726e616c73706b02"
|
||||
),
|
||||
},
|
||||
)
|
||||
for row in rows:
|
||||
k, v = row
|
||||
orig_active_descriptors.append({"k": k, "v": v})
|
||||
else:
|
||||
with open(walletfilepath, "rb") as fp:
|
||||
with mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) as mm:
|
||||
pos = mm.find(seedid_bytes)
|
||||
while pos != -1:
|
||||
mm.seek(pos - 8)
|
||||
hdchain_bytes = mm.read(12 + 20)
|
||||
version = int.from_bytes(hdchain_bytes[:4], "little")
|
||||
if version == 2:
|
||||
external_counter = int.from_bytes(
|
||||
hdchain_bytes[4:8], "little"
|
||||
)
|
||||
internal_counter = int.from_bytes(
|
||||
hdchain_bytes[-4:], "little"
|
||||
)
|
||||
if (
|
||||
external_counter > 0
|
||||
and external_counter <= max_hdchain_key_count
|
||||
and internal_counter > 0
|
||||
and internal_counter <= max_hdchain_key_count
|
||||
):
|
||||
orig_hdchain_bytes = hdchain_bytes
|
||||
self._log.debug(
|
||||
f"Found hdchain for: {seed_id_before} external_counter: {external_counter}, internal_counter: {internal_counter}."
|
||||
)
|
||||
break
|
||||
pos = mm.find(seedid_bytes, pos + 1)
|
||||
|
||||
self.rpc("loadwallet", [self._rpc_wallet])
|
||||
|
||||
self.rpc_wallet("encryptwallet", [password])
|
||||
|
||||
if check_seed is False or seed_id_before == "Not found":
|
||||
if check_seed is False or seed_id_before == "Not found" or walletpath is None:
|
||||
return
|
||||
seed_id_after: str = self.getWalletSeedID()
|
||||
|
||||
@@ -2000,42 +2098,104 @@ class BTCInterface(Secp256k1Interface):
|
||||
f"seed_id_before: {seed_id_before} seed_id_after: {seed_id_after}."
|
||||
)
|
||||
self.setWalletSeedWarning(True)
|
||||
# Workaround for https://github.com/bitcoin/bitcoin/issues/26607
|
||||
chain_client_settings = self._sc.getChainClientSettings(
|
||||
self.coin_type()
|
||||
) # basicswap.json
|
||||
|
||||
if chain_client_settings.get("manage_daemon", False) is False:
|
||||
self._log.warning(
|
||||
f"{self.ticker()} manage_daemon is false. Please manually stop BSX, remove the wallet, start BSX, and reseed."
|
||||
f"{self.ticker()} manage_daemon is false. Can't attempt to fix."
|
||||
)
|
||||
return
|
||||
if self._use_descriptors:
|
||||
if len(orig_active_descriptors) < 2:
|
||||
self._log.error(
|
||||
"Could not find original active descriptors for wallet."
|
||||
)
|
||||
return
|
||||
self._log.info("Attempting to revert to last descriptors.")
|
||||
else:
|
||||
if orig_hdchain_bytes is None:
|
||||
self._log.error("Could not find hdchain for wallet.")
|
||||
return
|
||||
self._log.info("Attempting to revert to last hdchain.")
|
||||
try:
|
||||
self.rpc_wallet("unloadwallet", [self._rpc_wallet])
|
||||
datadir = chain_client_settings["datadir"]
|
||||
if self._network != "mainnet":
|
||||
datadir = os.path.join(datadir, self._network)
|
||||
# Make a copy of the encrypted wallet before modifying it
|
||||
bkp_path = walletpath + ".bkp"
|
||||
for i in range(100):
|
||||
if not os.path.exists(bkp_path):
|
||||
break
|
||||
bkp_path = walletpath + f".bkp{i}"
|
||||
|
||||
try_wallet_path = os.path.join(datadir, self._rpc_wallet)
|
||||
if os.path.exists(try_wallet_path):
|
||||
new_wallet_path = os.path.join(datadir, self._rpc_wallet + ".old")
|
||||
os.rename(try_wallet_path, new_wallet_path)
|
||||
if os.path.exists(bkp_path):
|
||||
self._log.error("Could not find backup path for wallet.")
|
||||
return
|
||||
|
||||
self.rpc("unloadwallet", [self._rpc_wallet])
|
||||
|
||||
if os.path.isfile(walletpath):
|
||||
shutil.copy(walletpath, bkp_path)
|
||||
else:
|
||||
try_wallet_path = os.path.join(datadir, "wallets", self._rpc_wallet)
|
||||
if os.path.exists(try_wallet_path):
|
||||
new_wallet_path = os.path.join(
|
||||
datadir, "wallets", self._rpc_wallet + ".old"
|
||||
)
|
||||
os.rename(try_wallet_path, new_wallet_path)
|
||||
else:
|
||||
raise ValueError("Can't find old wallet path.")
|
||||
shutil.copytree(walletpath, bkp_path)
|
||||
|
||||
self.createWallet(self._rpc_wallet, password=password)
|
||||
self._sc.ci(Coins.PART).unlockWallet(password, check_seed=False)
|
||||
self.unlockWallet(password, check_seed=False)
|
||||
restore_time = chain_client_settings.get("restore_time", 0)
|
||||
self._sc.initialiseWallet(self.coin_type(), True, restore_time=restore_time)
|
||||
self._sc.checkWalletSeed(self.coin_type())
|
||||
hdchain_replaced: bool = False
|
||||
if self._use_descriptors:
|
||||
with sqlite3.connect(walletfilepath) as conn:
|
||||
c = conn.cursor()
|
||||
c.executemany(
|
||||
"UPDATE main SET value = :v WHERE key = :k",
|
||||
orig_active_descriptors,
|
||||
)
|
||||
conn.commit()
|
||||
else:
|
||||
seedid_after_bytes: bytes = bytes.fromhex(seed_id_after)[::-1]
|
||||
with open(walletfilepath, "r+b") as fp:
|
||||
with mmap.mmap(fp.fileno(), 0) as mm:
|
||||
pos = mm.find(seedid_after_bytes)
|
||||
while pos != -1:
|
||||
mm.seek(pos - 8)
|
||||
hdchain_bytes = mm.read(12 + 20)
|
||||
version = int.from_bytes(hdchain_bytes[:4], "little")
|
||||
if version == 2:
|
||||
external_counter = int.from_bytes(
|
||||
hdchain_bytes[4:8], "little"
|
||||
)
|
||||
internal_counter = int.from_bytes(
|
||||
hdchain_bytes[-4:], "little"
|
||||
)
|
||||
if (
|
||||
external_counter > 0
|
||||
and external_counter <= max_hdchain_key_count
|
||||
and internal_counter > 0
|
||||
and internal_counter <= max_hdchain_key_count
|
||||
):
|
||||
self._log.debug(
|
||||
f"Replacing hdchain for: {seed_id_after} external_counter: {external_counter}, internal_counter: {internal_counter}."
|
||||
)
|
||||
offset: int = pos - 8
|
||||
mm.seek(offset)
|
||||
mm.write(orig_hdchain_bytes)
|
||||
self._log.debug(
|
||||
f"hdchain replaced at offset: {offset}."
|
||||
)
|
||||
hdchain_replaced = True
|
||||
# Can appear multiple times in file, replace all.
|
||||
pos = mm.find(seedid_after_bytes, pos + 1)
|
||||
|
||||
if hdchain_replaced is False:
|
||||
self._log.error("Could not find new hdchain in wallet.")
|
||||
|
||||
self.rpc("loadwallet", [self._rpc_wallet])
|
||||
|
||||
if hdchain_replaced:
|
||||
self.unlockWallet(password, check_seed=False)
|
||||
seed_id_after_restore: str = self.getWalletSeedID()
|
||||
if seed_id_after_restore == seed_id_before:
|
||||
self._log.debug("Running newkeypool.")
|
||||
self.rpc_wallet("newkeypool")
|
||||
else:
|
||||
self._log.warning(
|
||||
f"Expected seed id not found: {seed_id_before}, have {seed_id_after_restore}."
|
||||
)
|
||||
|
||||
self.lockWallet()
|
||||
|
||||
except Exception as e:
|
||||
self._log.error(f"{self.ticker()} recreating wallet failed: {e}.")
|
||||
|
||||
@@ -58,6 +58,25 @@ class FIROInterface(BTCInterface):
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def encryptWallet(self, password: str, check_seed: bool = True):
|
||||
# Watchonly wallets are not encrypted
|
||||
# Firo shuts down after encryptwallet
|
||||
seed_id_before: str = self.getWalletSeedID() if check_seed else "Not found"
|
||||
|
||||
self.rpc_wallet("encryptwallet", [password])
|
||||
|
||||
if check_seed is False or seed_id_before == "Not found":
|
||||
return
|
||||
seed_id_after: str = self.getWalletSeedID()
|
||||
|
||||
if seed_id_before == seed_id_after:
|
||||
return
|
||||
self._log.warning(f"{self.ticker()} wallet seed changed after encryption.")
|
||||
self._log.debug(
|
||||
f"seed_id_before: {seed_id_before} seed_id_after: {seed_id_after}."
|
||||
)
|
||||
self.setWalletSeedWarning(True)
|
||||
|
||||
def getNewAddress(self, use_segwit, label="swap_receive"):
|
||||
return self.rpc("getnewaddress", [label])
|
||||
# addr_plain = self.rpc('getnewaddress', [label])
|
||||
|
||||
@@ -59,7 +59,7 @@ class PIVXInterface(BTCInterface):
|
||||
|
||||
if chain_client_settings.get("manage_daemon", False) is False:
|
||||
self._log.warning(
|
||||
f"{self.ticker()} manage_daemon is false. Please manually stop BSX, remove the wallet, start BSX, and reseed."
|
||||
f"{self.ticker()} manage_daemon is false. Can't attempt to fix."
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user