Merge pull request #297 from tecnovert/wallet_encryption

Add workaround for btc seed changing after encrypting wallet.
This commit is contained in:
tecnovert
2025-04-14 18:03:29 +00:00
committed by GitHub
26 changed files with 1903 additions and 123 deletions

2
.gitignore vendored
View File

@@ -8,6 +8,8 @@ __pycache__
/*.eggs /*.eggs
.tox .tox
.eggs .eggs
.ruff_cache
.pytest_cache
*~ *~
# geckodriver.log # geckodriver.log

View File

@@ -1358,7 +1358,9 @@ class BasicSwap(BaseApp):
legacy_root_hash = ci.getSeedHash(root_key, 20) legacy_root_hash = ci.getSeedHash(root_key, 20)
self.setStringKV(key_str, legacy_root_hash.hex(), cursor) self.setStringKV(key_str, legacy_root_hash.hex(), cursor)
def initialiseWallet(self, interface_type, raise_errors: bool = False) -> None: def initialiseWallet(
self, interface_type, raise_errors: bool = False, restore_time: int = -1
) -> None:
if interface_type == Coins.PART: if interface_type == Coins.PART:
return return
ci = self.ci(interface_type) ci = self.ci(interface_type)
@@ -1377,7 +1379,7 @@ class BasicSwap(BaseApp):
root_key = self.getWalletKey(interface_type, 1) root_key = self.getWalletKey(interface_type, 1)
try: try:
ci.initialiseWallet(root_key) ci.initialiseWallet(root_key, restore_time)
except Exception as e: except Exception as e:
# < 0.21: sethdseed cannot set a new HD seed while still in Initial Block Download. # < 0.21: sethdseed cannot set a new HD seed while still in Initial Block Download.
self.log.error(f"initialiseWallet failed: {e}") self.log.error(f"initialiseWallet failed: {e}")

View File

@@ -182,6 +182,7 @@ BSX_UPDATE_UNMANAGED = toBool(
UI_HTML_PORT = int(os.getenv("UI_HTML_PORT", 12700)) UI_HTML_PORT = int(os.getenv("UI_HTML_PORT", 12700))
UI_WS_PORT = int(os.getenv("UI_WS_PORT", 11700)) UI_WS_PORT = int(os.getenv("UI_WS_PORT", 11700))
COINS_RPCBIND_IP = os.getenv("COINS_RPCBIND_IP", "127.0.0.1") COINS_RPCBIND_IP = os.getenv("COINS_RPCBIND_IP", "127.0.0.1")
DEFAULT_RESTORE_TIME = int(os.getenv("DEFAULT_RESTORE_TIME", 1577833261)) # 2020
PART_ZMQ_PORT = int(os.getenv("PART_ZMQ_PORT", 20792)) PART_ZMQ_PORT = int(os.getenv("PART_ZMQ_PORT", 20792))
PART_RPC_HOST = os.getenv("PART_RPC_HOST", "127.0.0.1") PART_RPC_HOST = os.getenv("PART_RPC_HOST", "127.0.0.1")
@@ -1707,6 +1708,11 @@ def printHelp():
DEFAULT_WOW_RESTORE_HEIGHT DEFAULT_WOW_RESTORE_HEIGHT
) )
) )
print(
"--walletrestoretime=n Time to restore wallets from, default:{}, -1 for now.".format(
DEFAULT_RESTORE_TIME
)
)
print( print(
"--trustremotenode Set trusted-daemon for XMR, defaults to auto: true when daemon rpchost value is a private ip address else false" "--trustremotenode Set trusted-daemon for XMR, defaults to auto: true when daemon rpchost value is a private ip address else false"
) )
@@ -1808,12 +1814,18 @@ def test_particl_encryption(data_dir, settings, chain, use_tor_proxy):
def encrypt_wallet(swap_client, coin_type) -> None: def encrypt_wallet(swap_client, coin_type) -> None:
ci = swap_client.ci(coin_type) ci = swap_client.ci(coin_type)
ci.changeWalletPassword("", WALLET_ENCRYPTION_PWD) ci.changeWalletPassword("", WALLET_ENCRYPTION_PWD, check_seed_if_encrypt=False)
ci.unlockWallet(WALLET_ENCRYPTION_PWD) ci.unlockWallet(WALLET_ENCRYPTION_PWD)
def initialise_wallets( def initialise_wallets(
particl_wallet_mnemonic, with_coins, data_dir, settings, chain, use_tor_proxy particl_wallet_mnemonic,
with_coins,
data_dir,
settings,
chain,
use_tor_proxy,
extra_opts={},
): ):
swap_client = None swap_client = None
daemons = [] daemons = []
@@ -1922,7 +1934,7 @@ def initialise_wallets(
if WALLET_ENCRYPTION_PWD == "" if WALLET_ENCRYPTION_PWD == ""
else WALLET_ENCRYPTION_PWD else WALLET_ENCRYPTION_PWD
) )
extra_opts = [ extra_args = [
'--appdata="{}"'.format(coin_settings["datadir"]), '--appdata="{}"'.format(coin_settings["datadir"]),
"--pass={}".format(dcr_password), "--pass={}".format(dcr_password),
] ]
@@ -1931,7 +1943,7 @@ def initialise_wallets(
args = [ args = [
os.path.join(coin_settings["bindir"], filename), os.path.join(coin_settings["bindir"], filename),
"--create", "--create",
] + extra_opts ] + extra_args
hex_seed = swap_client.getWalletKey(Coins.DCR, 1).hex() hex_seed = swap_client.getWalletKey(Coins.DCR, 1).hex()
createDCRWallet(args, hex_seed, logger, threading.Event()) createDCRWallet(args, hex_seed, logger, threading.Event())
continue continue
@@ -2028,6 +2040,7 @@ def initialise_wallets(
) )
for coin_name in with_coins: for coin_name in with_coins:
coin_settings = settings["chainclients"][coin_name]
c = swap_client.getCoinIdFromName(coin_name) c = swap_client.getCoinIdFromName(coin_name)
if c in (Coins.PART,): if c in (Coins.PART,):
continue continue
@@ -2035,14 +2048,29 @@ def initialise_wallets(
# initialiseWallet only sets main_wallet_seedid_ # initialiseWallet only sets main_wallet_seedid_
swap_client.waitForDaemonRPC(c) swap_client.waitForDaemonRPC(c)
try: try:
swap_client.initialiseWallet(c, raise_errors=True) default_restore_time = (
-1 if generated_mnemonic else DEFAULT_RESTORE_TIME
) # Set to -1 (now) if key is newly generated
restore_time: int = extra_opts.get(
"walletrestoretime", default_restore_time
)
swap_client.initialiseWallet(
c, raise_errors=True, restore_time=restore_time
)
if c not in (Coins.XMR, Coins.WOW):
if restore_time == -1:
restore_time = int(time.time())
coin_settings["restore_time"] = restore_time
except Exception as e: except Exception as e:
coins_failed_to_initialise.append((c, e)) coins_failed_to_initialise.append((c, e))
if WALLET_ENCRYPTION_PWD != "" and ( if WALLET_ENCRYPTION_PWD != "" and (
c not in coins_to_create_wallets_for or c in (Coins.DASH,) c not in coins_to_create_wallets_for or c in (Coins.DASH,)
): # TODO: Remove DASH workaround ): # TODO: Remove DASH workaround
try: try:
swap_client.ci(c).changeWalletPassword("", WALLET_ENCRYPTION_PWD) swap_client.ci(c).changeWalletPassword(
"", WALLET_ENCRYPTION_PWD, check_seed_if_encrypt=False
)
except Exception as e: # noqa: F841 except Exception as e: # noqa: F841
logger.warning(f"changeWalletPassword failed for {coin_name}.") logger.warning(f"changeWalletPassword failed for {coin_name}.")
@@ -2363,6 +2391,9 @@ def main():
if name == "wowrestoreheight": if name == "wowrestoreheight":
wow_restore_height = int(s[1]) wow_restore_height = int(s[1])
continue continue
if name == "walletrestoretime":
extra_opts["walletrestoretime"] = int(s[1])
continue
if name == "keysdirpath": if name == "keysdirpath":
extra_opts["keysdirpath"] = os.path.expanduser(s[1].strip('"')) extra_opts["keysdirpath"] = os.path.expanduser(s[1].strip('"'))
continue continue
@@ -2823,6 +2854,7 @@ def main():
settings, settings,
chain, chain,
use_tor_proxy, use_tor_proxy,
extra_opts=extra_opts,
) )
print("Done.") print("Done.")
@@ -2944,6 +2976,7 @@ def main():
settings, settings,
chain, chain,
use_tor_proxy, use_tor_proxy,
extra_opts=extra_opts,
) )
save_config(config_path, settings) save_config(config_path, settings)
@@ -3082,15 +3115,21 @@ def main():
for c in with_coins: for c in with_coins:
prepareDataDir(c, settings, chain, particl_wallet_mnemonic, extra_opts) prepareDataDir(c, settings, chain, particl_wallet_mnemonic, extra_opts)
save_config(config_path, settings)
if particl_wallet_mnemonic == "none": if particl_wallet_mnemonic == "none":
save_config(config_path, settings)
logger.info("Done.") logger.info("Done.")
return 0 return 0
initialise_wallets( initialise_wallets(
particl_wallet_mnemonic, with_coins, data_dir, settings, chain, use_tor_proxy particl_wallet_mnemonic,
with_coins,
data_dir,
settings,
chain,
use_tor_proxy,
extra_opts=extra_opts,
) )
save_config(config_path, settings)
print("Done.") print("Done.")

View File

@@ -106,6 +106,31 @@ class BCHInterface(BTCInterface):
) + self.make_int(u["amount"], r=1) ) + self.make_int(u["amount"], r=1)
return unspent_addr return unspent_addr
def createWallet(self, wallet_name: str, password: str = ""):
self.rpc("createwallet", [wallet_name, False])
if password != "":
self.rpc(
"encryptwallet",
[
password,
],
override_wallet=wallet_name,
)
def newKeypool(self) -> None:
self._log.debug("Refreshing keypool.")
# Use up current keypool
wi = self.rpc_wallet("getwalletinfo")
keypool_size: int = wi["keypoolsize"]
for i in range(keypool_size):
_ = self.rpc_wallet("getnewaddress")
keypoolsize_hd_internal: int = wi["keypoolsize_hd_internal"]
for i in range(keypoolsize_hd_internal):
_ = self.rpc_wallet("getrawchangeaddress")
self.rpc_wallet("keypoolrefill")
# returns pkh # returns pkh
def decodeAddress(self, address: str) -> bytes: def decodeAddress(self, address: str) -> bytes:
return bytes(Address.from_string(address).payload) return bytes(Address.from_string(address).payload)

View File

@@ -10,8 +10,13 @@ import base64
import hashlib import hashlib
import json import json
import logging import logging
import mmap
import os
import shutil
import sqlite3
import traceback import traceback
from io import BytesIO from io import BytesIO
from basicswap.basicswap_util import ( from basicswap.basicswap_util import (
@@ -377,7 +382,7 @@ class BTCInterface(Secp256k1Interface):
last_block_header = prev_block_header last_block_header = prev_block_header
raise ValueError(f"Block header not found at time: {time}") raise ValueError(f"Block header not found at time: {time}")
def initialiseWallet(self, key_bytes: bytes) -> None: def initialiseWallet(self, key_bytes: bytes, restore_time: int = -1) -> None:
assert len(key_bytes) == 32 assert len(key_bytes) == 32
self._have_checked_seed = False self._have_checked_seed = False
if self._use_descriptors: if self._use_descriptors:
@@ -387,6 +392,7 @@ class BTCInterface(Secp256k1Interface):
ek_encoded: str = self.encode_secret_extkey(ek.encode_v()) ek_encoded: str = self.encode_secret_extkey(ek.encode_v())
desc_external = descsum_create(f"wpkh({ek_encoded}/0h/0h/*h)") desc_external = descsum_create(f"wpkh({ek_encoded}/0h/0h/*h)")
desc_internal = descsum_create(f"wpkh({ek_encoded}/0h/1h/*h)") desc_internal = descsum_create(f"wpkh({ek_encoded}/0h/1h/*h)")
rv = self.rpc_wallet( rv = self.rpc_wallet(
"importdescriptors", "importdescriptors",
[ [
@@ -394,7 +400,7 @@ class BTCInterface(Secp256k1Interface):
{"desc": desc_external, "timestamp": "now", "active": True}, {"desc": desc_external, "timestamp": "now", "active": True},
{ {
"desc": desc_internal, "desc": desc_internal,
"timestamp": "now", "timestamp": "now" if restore_time == -1 else restore_time,
"active": True, "active": True,
"internal": True, "internal": True,
}, },
@@ -411,7 +417,18 @@ class BTCInterface(Secp256k1Interface):
raise ValueError("Failed to import descriptors.") raise ValueError("Failed to import descriptors.")
else: else:
key_wif = self.encodeKey(key_bytes) key_wif = self.encodeKey(key_bytes)
try:
self.rpc_wallet("sethdseed", [True, key_wif]) 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): def getWalletInfo(self):
rv = self.rpc_wallet("getwalletinfo") rv = self.rpc_wallet("getwalletinfo")
@@ -455,10 +472,6 @@ class BTCInterface(Secp256k1Interface):
self.close_rpc(rpc_conn) self.close_rpc(rpc_conn)
raise ValueError(f"{self.coin_name()} wallet restore height not found.") raise ValueError(f"{self.coin_name()} wallet restore height not found.")
def getWalletSeedID(self) -> str:
wi = self.rpc_wallet("getwalletinfo")
return "Not found" if "hdseedid" not in wi else wi["hdseedid"]
def getActiveDescriptor(self): def getActiveDescriptor(self):
descriptors = self.rpc_wallet("listdescriptors")["descriptors"] descriptors = self.rpc_wallet("listdescriptors")["descriptors"]
for descriptor in descriptors: for descriptor in descriptors:
@@ -470,21 +483,24 @@ class BTCInterface(Secp256k1Interface):
return descriptor return descriptor
return None return None
def checkExpectedSeed(self, expect_seedid: str) -> bool: def getWalletSeedID(self) -> str:
if self._use_descriptors: if self._use_descriptors:
descriptor = self.getActiveDescriptor() descriptor = self.getActiveDescriptor()
if descriptor is None: if descriptor is None:
self._log.debug("Could not find active descriptor.") self._log.debug("Could not find active descriptor.")
return False return "Not found"
end = descriptor["desc"].find("/") end = descriptor["desc"].find("/")
if end < 10: if end < 10:
return False return "Not found"
extkey = descriptor["desc"][5:end] extkey = descriptor["desc"][5:end]
extkey_data = b58decode(extkey)[4:-4] extkey_data = b58decode(extkey)[4:-4]
extkey_data_hash: bytes = hash160(extkey_data) extkey_data_hash: bytes = hash160(extkey_data)
return True if extkey_data_hash.hex() == expect_seedid else False return extkey_data_hash.hex()
wi = self.rpc_wallet("getwalletinfo")
return "Not found" if "hdseedid" not in wi else wi["hdseedid"]
def checkExpectedSeed(self, expect_seedid: str) -> bool:
wallet_seed_id = self.getWalletSeedID() wallet_seed_id = self.getWalletSeedID()
self._expect_seedid_hex = expect_seedid self._expect_seedid_hex = expect_seedid
self._have_checked_seed = True self._have_checked_seed = True
@@ -1978,12 +1994,234 @@ class BTCInterface(Secp256k1Interface):
locked = encrypted and wallet_info["unlocked_until"] <= 0 locked = encrypted and wallet_info["unlocked_until"] <= 0
return encrypted, locked return encrypted, locked
def changeWalletPassword(self, old_password: str, new_password: str): def createWallet(self, wallet_name: str, password: str = "") -> None:
self.rpc(
"createwallet",
[wallet_name, False, True, password, False, self._use_descriptors],
)
def setActiveWallet(self, wallet_name: str) -> None:
# For debugging
self.rpc_wallet = make_rpc_func(
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=wallet_name
)
self._rpc_wallet = wallet_name
def newKeypool(self) -> None:
self._log.debug("Running newkeypool.")
self.rpc_wallet("newkeypool")
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()
orig_active_descriptors = []
orig_hdchain_bytes = None
walletpath = None
max_hdchain_key_count: int = 4000000 # Arbitrary
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"
):
# Store active keys
self.rpc("unloadwallet", [self._rpc_wallet])
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:
seedid_bytes: bytes = bytes.fromhex(seed_id_before)[::-1]
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" or walletpath is None:
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)
if chain_client_settings.get("manage_daemon", False) is False:
self._log.warning(
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:
# 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}"
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:
shutil.copytree(walletpath, bkp_path)
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.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}.")
if self._sc.debug:
self._log.error(traceback.format_exc())
def changeWalletPassword(
self, old_password: str, new_password: str, check_seed_if_encrypt: bool = True
):
self._log.info("changeWalletPassword - {}".format(self.ticker())) self._log.info("changeWalletPassword - {}".format(self.ticker()))
if old_password == "": if old_password == "":
if self.isWalletEncrypted(): if self.isWalletEncrypted():
raise ValueError("Old password must be set") raise ValueError("Old password must be set")
return self.rpc_wallet("encryptwallet", [new_password]) return self.encryptWallet(new_password, check_seed=check_seed_if_encrypt)
self.rpc_wallet("walletpassphrasechange", [old_password, new_password]) self.rpc_wallet("walletpassphrasechange", [old_password, new_password])
def unlockWallet(self, password: str, check_seed: bool = True) -> None: def unlockWallet(self, password: str, check_seed: bool = True) -> None:
@@ -2001,9 +2239,16 @@ class BTCInterface(Secp256k1Interface):
) )
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors # wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
self.rpc( self.rpc(
"createwallet", [self._rpc_wallet, False, True, "", False, False] "createwallet",
[
self._rpc_wallet,
False,
True,
password,
False,
self._use_descriptors,
],
) )
self.rpc_wallet("encryptwallet", [password])
# Max timeout value, ~3 years # Max timeout value, ~3 years
self.rpc_wallet("walletpassphrase", [password, 100000000]) self.rpc_wallet("walletpassphrase", [password, 100000000])

View File

@@ -47,7 +47,7 @@ class DASHInterface(BTCInterface):
def entropyToMnemonic(self, key: bytes) -> None: def entropyToMnemonic(self, key: bytes) -> None:
return Mnemonic("english").to_mnemonic(key) return Mnemonic("english").to_mnemonic(key)
def initialiseWallet(self, key_bytes: bytes) -> None: def initialiseWallet(self, key_bytes: bytes, restore_time: int = -1) -> None:
self._have_checked_seed = False self._have_checked_seed = False
if self._wallet_v20_compatible: if self._wallet_v20_compatible:
self._log.warning("Generating wallet compatible with v20 seed.") self._log.warning("Generating wallet compatible with v20 seed.")
@@ -66,7 +66,11 @@ class DASHInterface(BTCInterface):
def checkExpectedSeed(self, expect_seedid: str) -> bool: def checkExpectedSeed(self, expect_seedid: str) -> bool:
self._expect_seedid_hex = expect_seedid self._expect_seedid_hex = expect_seedid
try:
rv = self.rpc_wallet("dumphdinfo") rv = self.rpc_wallet("dumphdinfo")
except Exception as e:
self._log.debug(f"DASH dumphdinfo failed {e}.")
return False
if rv["mnemonic"] != "": if rv["mnemonic"] != "":
entropy = Mnemonic("english").to_entropy(rv["mnemonic"].split(" ")) entropy = Mnemonic("english").to_entropy(rv["mnemonic"].split(" "))
entropy_hash = self.getAddressHashFromKey(entropy)[::-1].hex() entropy_hash = self.getAddressHashFromKey(entropy)[::-1].hex()
@@ -120,3 +124,36 @@ class DASHInterface(BTCInterface):
def lockWallet(self): def lockWallet(self):
super().lockWallet() super().lockWallet()
self._wallet_passphrase = "" self._wallet_passphrase = ""
def encryptWallet(
self, old_password: str, new_password: str, check_seed: bool = True
):
if old_password != "":
self.unlockWallet(old_password, check_seed=False)
seed_id_before: str = self.getWalletSeedID()
self.rpc_wallet("encryptwallet", [new_password])
if check_seed is False or seed_id_before == "Not found":
return
self.unlockWallet(new_password, check_seed=False)
seed_id_after: str = self.getWalletSeedID()
self.lockWallet()
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 changeWalletPassword(
self, old_password: str, new_password: str, check_seed_if_encrypt: bool = True
):
self._log.info("changeWalletPassword - {}".format(self.ticker()))
if old_password == "":
if self.isWalletEncrypted():
raise ValueError("Old password must be set")
return self.encryptWallet(old_password, new_password, check_seed_if_encrypt)
self.rpc_wallet("walletpassphrasechange", [old_password, new_password])

View File

@@ -332,14 +332,14 @@ class DCRInterface(Secp256k1Interface):
def testDaemonRPC(self, with_wallet=True) -> None: def testDaemonRPC(self, with_wallet=True) -> None:
if with_wallet: if with_wallet:
self.rpc_wallet("getinfo") self.rpc_wallet("walletislocked")
else: else:
self.rpc("getblockchaininfo") self.rpc("getblockchaininfo")
def getChainHeight(self) -> int: def getChainHeight(self) -> int:
return self.rpc("getblockcount") return self.rpc("getblockcount")
def initialiseWallet(self, key: bytes) -> None: def initialiseWallet(self, key: bytes, restore_time: int = -1) -> None:
# Load with --create # Load with --create
pass pass
@@ -354,7 +354,9 @@ class DCRInterface(Secp256k1Interface):
walletislocked = self.rpc_wallet("walletislocked") walletislocked = self.rpc_wallet("walletislocked")
return True, walletislocked return True, walletislocked
def changeWalletPassword(self, old_password: str, new_password: str): def changeWalletPassword(
self, old_password: str, new_password: str, check_seed_if_encrypt: bool = True
):
self._log.info("changeWalletPassword - {}".format(self.ticker())) self._log.info("changeWalletPassword - {}".format(self.ticker()))
if old_password == "": if old_password == "":
# Read initial pwd from settings # Read initial pwd from settings

View File

@@ -51,13 +51,32 @@ class FIROInterface(BTCInterface):
def getExchangeName(self, exchange_name: str) -> str: def getExchangeName(self, exchange_name: str) -> str:
return "zcoin" return "zcoin"
def initialiseWallet(self, key): def initialiseWallet(self, key, restore_time: int = -1):
# load with -hdseed= parameter # load with -hdseed= parameter
pass pass
def checkWallets(self) -> int: def checkWallets(self) -> int:
return 1 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"): def getNewAddress(self, use_segwit, label="swap_receive"):
return self.rpc("getnewaddress", [label]) return self.rpc("getnewaddress", [label])
# addr_plain = self.rpc('getnewaddress', [label]) # addr_plain = self.rpc('getnewaddress', [label])

View File

@@ -87,7 +87,7 @@ class NAVInterface(BTCInterface):
# p2sh-p2wsh # p2sh-p2wsh
return True return True
def initialiseWallet(self, key): def initialiseWallet(self, key, restore_time: int = -1):
# Load with -importmnemonic= parameter # Load with -importmnemonic= parameter
pass pass

View File

@@ -110,7 +110,7 @@ class PARTInterface(BTCInterface):
) )
return index_info["spentindex"] return index_info["spentindex"]
def initialiseWallet(self, key: bytes) -> None: def initialiseWallet(self, key: bytes, restore_time: int = -1) -> None:
raise ValueError("TODO") raise ValueError("TODO")
def withdrawCoin(self, value, addr_to, subfee): def withdrawCoin(self, value, addr_to, subfee):

View File

@@ -34,6 +34,35 @@ class PIVXInterface(BTCInterface):
self._rpcport, self._rpcauth, host=self._rpc_host self._rpcport, self._rpcauth, host=self._rpc_host
) )
def encryptWallet(self, password: str, check_seed: bool = True):
# Watchonly wallets are not encrypted
seed_id_before: str = self.getWalletSeedID()
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)
# 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. Can't attempt to fix."
)
return
def signTxWithWallet(self, tx): def signTxWithWallet(self, tx):
rv = self.rpc("signrawtransaction", [tx.hex()]) rv = self.rpc("signrawtransaction", [tx.hex()])
return bytes.fromhex(rv["hex"]) return bytes.fromhex(rv["hex"])

View File

@@ -766,7 +766,9 @@ class XMRInterface(CoinInterface):
balance_info = self.rpc_wallet("get_balance") balance_info = self.rpc_wallet("get_balance")
return balance_info["unlocked_balance"] return balance_info["unlocked_balance"]
def changeWalletPassword(self, old_password, new_password): def changeWalletPassword(
self, old_password, new_password, check_seed_if_encrypt: bool = True
):
self._log.info("changeWalletPassword - {}".format(self.ticker())) self._log.info("changeWalletPassword - {}".format(self.ticker()))
orig_password = self._wallet_password orig_password = self._wallet_password
if old_password != "": if old_password != "":

View File

@@ -850,7 +850,12 @@ def js_getcoinseed(self, url_split, post_string, is_json) -> bytes:
swap_client.checkSystemStatus() swap_client.checkSystemStatus()
post_data = getFormData(post_string, is_json) post_data = getFormData(post_string, is_json)
coin = getCoinType(get_data_entry(post_data, "coin")) coin_in = get_data_entry(post_data, "coin")
try:
coin = getCoinIdFromName(coin_in)
except Exception:
coin = getCoinType(coin_in)
if coin in (Coins.PART, Coins.PART_ANON, Coins.PART_BLIND): if coin in (Coins.PART, Coins.PART_ANON, Coins.PART_BLIND):
raise ValueError("Particl wallet seed is set from the Basicswap mnemonic.") raise ValueError("Particl wallet seed is set from the Basicswap mnemonic.")
@@ -878,12 +883,17 @@ def js_getcoinseed(self, url_split, post_string, is_json) -> bytes:
expect_seedid = swap_client.getStringKV( expect_seedid = swap_client.getStringKV(
"main_wallet_seedid_" + ci.coin_name().lower() "main_wallet_seedid_" + ci.coin_name().lower()
) )
try:
wallet_seed_id = ci.getWalletSeedID()
except Exception as e:
wallet_seed_id = f"Error: {e}"
rv.update( rv.update(
{ {
"seed": seed_key.hex(), "seed": seed_key.hex(),
"seed_id": seed_id.hex(), "seed_id": seed_id.hex(),
"expected_seed_id": "Unset" if expect_seedid is None else expect_seedid, "expected_seed_id": "Unset" if expect_seedid is None else expect_seedid,
"current_seed_id": wallet_seed_id,
} }
) )

View File

@@ -1,15 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2024 tecnovert # Copyright (c) 2020-2024 tecnovert
# Copyright (c) 2025 The Basicswap developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import json import json
import shlex
import urllib
import traceback import traceback
import subprocess import urllib
from xmlrpc.client import ( from xmlrpc.client import (
Fault, Fault,
Transport, Transport,
@@ -104,7 +102,7 @@ def callrpc(rpc_port, auth, method, params=[], wallet=None, host="127.0.0.1"):
r = json.loads(v.decode("utf-8")) r = json.loads(v.decode("utf-8"))
except Exception as ex: except Exception as ex:
traceback.print_exc() traceback.print_exc()
raise ValueError("RPC server error " + str(ex) + ", method: " + method) raise ValueError(f"RPC server error: {ex}, method: {method}")
if "error" in r and r["error"] is not None: if "error" in r and r["error"] is not None:
raise ValueError("RPC error " + str(r["error"])) raise ValueError("RPC error " + str(r["error"]))
@@ -120,36 +118,7 @@ def openrpc(rpc_port, auth, wallet=None, host="127.0.0.1"):
return Jsonrpc(url) return Jsonrpc(url)
except Exception as ex: except Exception as ex:
traceback.print_exc() traceback.print_exc()
raise ValueError("RPC error " + str(ex)) raise ValueError(f"RPC error: {ex}")
def callrpc_cli(bindir, datadir, chain, cmd, cli_bin="particl-cli", wallet=None):
cli_bin = os.path.join(bindir, cli_bin)
args = [
cli_bin,
]
if chain != "mainnet":
args.append("-" + chain)
args.append("-datadir=" + datadir)
if wallet is not None:
args.append("-rpcwallet=" + wallet)
args += shlex.split(cmd)
p = subprocess.Popen(
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
out = p.communicate()
if len(out[1]) > 0:
raise ValueError("RPC error " + str(out[1]))
r = out[0].decode("utf-8").strip()
try:
r = json.loads(r)
except Exception:
pass
return r
def make_rpc_func(port, auth, wallet=None, host="127.0.0.1"): def make_rpc_func(port, auth, wallet=None, host="127.0.0.1"):

View File

@@ -6,10 +6,12 @@
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php. # file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
import os
import json import json
import signal
import logging import logging
import os
import shlex
import signal
import subprocess
from urllib.request import urlopen from urllib.request import urlopen
from .util import read_json_api from .util import read_json_api
@@ -133,11 +135,12 @@ def checkForks(ro):
def stopDaemons(daemons): def stopDaemons(daemons):
for d in daemons: for d in daemons:
logging.info("Interrupting %d", d.handle.pid) logging.info(f"Interrupting {d.handle.pid}")
signal_type = signal.SIGTERM if os.name == "nt" else signal.SIGINT
try: try:
d.handle.send_signal(signal.SIGINT) d.handle.send_signal(signal_type)
except Exception as e: except Exception as e:
logging.info("Interrupting %d, error %s", d.handle.pid, str(e)) logging.info(f"Interrupting {d.handle.pid}, error: {e}")
for d in daemons: for d in daemons:
try: try:
d.handle.wait(timeout=20) d.handle.wait(timeout=20)
@@ -145,7 +148,7 @@ def stopDaemons(daemons):
if fp: if fp:
fp.close() fp.close()
except Exception as e: except Exception as e:
logging.info("Closing %d, error %s", d.handle.pid, str(e)) logging.info(f"Closing {d.handle.pid}, error: {e}")
def wait_for_bid( def wait_for_bid(
@@ -494,3 +497,38 @@ def compare_bid_states_unordered(states, expect_states, ignore_states=[]) -> boo
logging.info("Have states: {}".format(json.dumps(states, indent=4))) logging.info("Have states: {}".format(json.dumps(states, indent=4)))
raise e raise e
return True return True
def callrpc_cli(
bindir,
datadir,
chain,
cmd,
cli_bin="particl-cli" + (".exe" if os.name == "nt" else ""),
wallet=None,
):
cli_bin = os.path.join(bindir, cli_bin)
args = [
cli_bin,
]
if chain != "mainnet":
args.append("-" + chain)
args.append("-datadir=" + datadir)
if wallet is not None:
args.append("-rpcwallet=" + wallet)
args += shlex.split(cmd)
p = subprocess.Popen(
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
out = p.communicate()
if len(out[1]) > 0:
raise ValueError(f"RPC error: {out[1]}")
r = out[0].decode("utf-8").strip()
try:
r = json.loads(r)
except Exception:
pass
return r

View File

@@ -557,12 +557,12 @@ def prepare_nodes(
): ):
bins_path = os.path.join(TEST_PATH, "bin") bins_path = os.path.join(TEST_PATH, "bin")
for i in range(num_nodes): for i in range(num_nodes):
logging.info("Preparing node: %d.", i) logging.info(f"Preparing node: {i}.")
client_path = os.path.join(TEST_PATH, "client{}".format(i)) client_path = os.path.join(TEST_PATH, f"client{i}")
try: try:
shutil.rmtree(client_path) shutil.rmtree(client_path)
except Exception as ex: except Exception as ex:
logging.warning("setUpClass %s", str(ex)) logging.warning(f"setUpClass {ex}")
run_prepare( run_prepare(
i, i,

View File

@@ -40,9 +40,6 @@ from basicswap.basicswap_util import (
from basicswap.util.address import ( from basicswap.util.address import (
toWIF, toWIF,
) )
from basicswap.rpc import (
callrpc_cli,
)
from basicswap.contrib.key import ( from basicswap.contrib.key import (
ECKey, ECKey,
) )
@@ -53,6 +50,7 @@ from tests.basicswap.util import (
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
callrpc_cli,
checkForks, checkForks,
stopDaemons, stopDaemons,
wait_for_offer, wait_for_offer,

View File

@@ -25,13 +25,11 @@ from basicswap.util import (
make_int, make_int,
format_amount, format_amount,
) )
from basicswap.rpc import (
callrpc_cli,
)
from tests.basicswap.util import ( from tests.basicswap.util import (
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
callrpc_cli,
stopDaemons, stopDaemons,
wait_for_bid, wait_for_bid,
make_rpc_func, make_rpc_func,

View File

@@ -32,7 +32,6 @@ from basicswap.util.address import (
) )
from basicswap.rpc import ( from basicswap.rpc import (
callrpc, callrpc,
callrpc_cli,
) )
from basicswap.contrib.key import ( from basicswap.contrib.key import (
ECKey, ECKey,
@@ -44,19 +43,20 @@ from tests.basicswap.util import (
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
prepareDataDir,
make_rpc_func,
checkForks,
stopDaemons,
delay_for,
TEST_HTTP_HOST,
TEST_HTTP_PORT,
BASE_P2P_PORT, BASE_P2P_PORT,
BASE_RPC_PORT, BASE_RPC_PORT,
BASE_ZMQ_PORT, BASE_ZMQ_PORT,
BTC_BASE_PORT, BTC_BASE_PORT,
BTC_BASE_RPC_PORT, BTC_BASE_RPC_PORT,
callrpc_cli,
checkForks,
delay_for,
make_rpc_func,
PREFIX_SECRET_KEY_REGTEST, PREFIX_SECRET_KEY_REGTEST,
prepareDataDir,
stopDaemons,
TEST_HTTP_HOST,
TEST_HTTP_PORT,
waitForRPC, waitForRPC,
) )

View File

@@ -40,9 +40,6 @@ from basicswap.basicswap_util import (
from basicswap.util.address import ( from basicswap.util.address import (
toWIF, toWIF,
) )
from basicswap.rpc import (
callrpc_cli,
)
from basicswap.contrib.key import ( from basicswap.contrib.key import (
ECKey, ECKey,
) )
@@ -53,6 +50,7 @@ from tests.basicswap.util import (
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
callrpc_cli,
checkForks, checkForks,
stopDaemons, stopDaemons,
wait_for_bid, wait_for_bid,

File diff suppressed because it is too large Load Diff

View File

@@ -174,6 +174,7 @@ class Test(BaseTest):
"create_wallet", "create_wallet",
{"filename": "testwallet", "language": "English"}, {"filename": "testwallet", "language": "English"},
) )
else:
cls.callwownodewallet(cls, i, "open_wallet", {"filename": "testwallet"}) cls.callwownodewallet(cls, i, "open_wallet", {"filename": "testwallet"})
@classmethod @classmethod

View File

@@ -20,6 +20,7 @@ from basicswap.bin.run import startDaemon
from basicswap.util.crypto import sha256 from basicswap.util.crypto import sha256
from tests.basicswap.test_btc_xmr import BasicSwapTest from tests.basicswap.test_btc_xmr import BasicSwapTest
from tests.basicswap.common import ( from tests.basicswap.common import (
callrpc_cli,
make_rpc_func, make_rpc_func,
prepareDataDir, prepareDataDir,
stopDaemons, stopDaemons,
@@ -37,9 +38,6 @@ from basicswap.contrib.test_framework.script import (
OP_CHECKSEQUENCEVERIFY, OP_CHECKSEQUENCEVERIFY,
) )
from basicswap.interface.bch import BCHInterface from basicswap.interface.bch import BCHInterface
from basicswap.rpc import (
callrpc_cli,
)
from basicswap.util import ensure from basicswap.util import ensure
from .test_xmr import test_delay_event, callnoderpc from .test_xmr import test_delay_event, callnoderpc
@@ -134,14 +132,20 @@ class TestBCH(BasicSwapTest):
if not line.startswith("findpeers"): if not line.startswith("findpeers"):
fp.write(line) fp.write(line)
if os.path.exists(os.path.join(BITCOINCASH_BINDIR, "bitcoin-wallet")): bch_wallet_bin = "bitcoin-wallet" + (".exe" if os.name == "nt" else "")
if os.path.exists(
os.path.join(
BITCOINCASH_BINDIR,
bch_wallet_bin,
)
):
try: try:
callrpc_cli( callrpc_cli(
BITCOINCASH_BINDIR, BITCOINCASH_BINDIR,
data_dir, data_dir,
"regtest", "regtest",
"-wallet=wallet.dat create", "-wallet=wallet.dat create",
"bitcoin-wallet", bch_wallet_bin,
) )
except Exception as e: except Exception as e:
logging.warning("bch: bitcoin-wallet create failed") logging.warning("bch: bitcoin-wallet create failed")

View File

@@ -1740,6 +1740,74 @@ class BasicSwapTest(TestFunctions):
self.callnoderpc("unloadwallet", [new_wallet_name]) self.callnoderpc("unloadwallet", [new_wallet_name])
self.callnoderpc("unloadwallet", [new_watch_wallet_name]) self.callnoderpc("unloadwallet", [new_watch_wallet_name])
def test_014_encrypt_existing_wallet(self):
logging.info(
f"---------- Test {self.test_coin_from.name} encrypt existing wallet"
)
ci = self.swap_clients[0].ci(self.test_coin_from)
wallet_name = "encrypt_existing_wallet"
ci.createWallet(wallet_name)
ci.setActiveWallet(wallet_name)
chain_client_settings = self.swap_clients[0].getChainClientSettings(
self.test_coin_from
)
try:
chain_client_settings["manage_daemon"] = True
ci.initialiseWallet(ci.getNewRandomKey())
original_seed_id = ci.getWalletSeedID()
addr1 = ci.getNewAddress(True)
addr1_info = ci.rpc_wallet(
"getaddressinfo",
[
addr1,
],
)
addr_int1 = ci.rpc_wallet("getrawchangeaddress")
addr_int1_info = ci.rpc_wallet(
"getaddressinfo",
[
addr_int1,
],
)
ci.encryptWallet("test.123")
after_seed_id = ci.getWalletSeedID()
addr2 = ci.getNewAddress(True)
addr2_info = ci.rpc_wallet(
"getaddressinfo",
[
addr2,
],
)
addr_int2 = ci.rpc_wallet("getrawchangeaddress")
addr_int2_info = ci.rpc_wallet(
"getaddressinfo",
[
addr_int2,
],
)
key_id_field: str = (
"hdmasterkeyid"
if "hdmasterkeyid" in addr1_info
else "hdmasterfingerprint"
)
assert addr1_info[key_id_field] == addr2_info[key_id_field]
assert addr_int1_info[key_id_field] == addr_int2_info[key_id_field]
assert addr1_info[key_id_field] == addr_int1_info[key_id_field]
assert original_seed_id == after_seed_id
finally:
ci.setActiveWallet("wallet.dat")
chain_client_settings["manage_daemon"] = False
def test_01_0_lock_bad_prevouts(self): def test_01_0_lock_bad_prevouts(self):
logging.info( logging.info(
"---------- Test {} lock_bad_prevouts".format(self.test_coin_from.name) "---------- Test {} lock_bad_prevouts".format(self.test_coin_from.name)

View File

@@ -24,9 +24,6 @@ import threading
import multiprocessing import multiprocessing
from unittest.mock import patch from unittest.mock import patch
from basicswap.rpc import (
callrpc_cli,
)
from tests.basicswap.util import ( from tests.basicswap.util import (
read_json_api, read_json_api,
post_json_api, post_json_api,
@@ -38,6 +35,7 @@ from tests.basicswap.common import (
waitForNumSwapping, waitForNumSwapping,
) )
from tests.basicswap.common_xmr import ( from tests.basicswap.common_xmr import (
callrpc_cli,
prepare_nodes, prepare_nodes,
) )
import basicswap.bin.run as runSystem import basicswap.bin.run as runSystem

View File

@@ -39,7 +39,6 @@ from basicswap.util.address import (
) )
from basicswap.rpc import ( from basicswap.rpc import (
callrpc, callrpc,
callrpc_cli,
) )
from basicswap.rpc_xmr import ( from basicswap.rpc_xmr import (
callrpc_xmr, callrpc_xmr,
@@ -61,6 +60,7 @@ from tests.basicswap.util import (
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
callrpc_cli,
prepareDataDir, prepareDataDir,
make_rpc_func, make_rpc_func,
checkForks, checkForks,
@@ -376,7 +376,7 @@ class BaseTest(unittest.TestCase):
if os.path.isdir(TEST_DIR): if os.path.isdir(TEST_DIR):
if RESET_TEST: if RESET_TEST:
logging.info("Removing " + TEST_DIR) logging.info("Removing test dir " + TEST_DIR)
for name in os.listdir(TEST_DIR): for name in os.listdir(TEST_DIR):
if name == "pivx-params": if name == "pivx-params":
continue continue
@@ -388,6 +388,8 @@ class BaseTest(unittest.TestCase):
else: else:
logging.info("Restoring instance from " + TEST_DIR) logging.info("Restoring instance from " + TEST_DIR)
cls.restore_instance = True cls.restore_instance = True
else:
logging.info("Creating test dir " + TEST_DIR)
if not os.path.exists(TEST_DIR): if not os.path.exists(TEST_DIR):
os.makedirs(TEST_DIR) os.makedirs(TEST_DIR)
@@ -399,19 +401,25 @@ class BaseTest(unittest.TestCase):
try: try:
logging.info("Preparing coin nodes.") logging.info("Preparing coin nodes.")
part_wallet_bin = "particl-wallet" + (".exe" if os.name == "nt" else "")
for i in range(NUM_NODES): for i in range(NUM_NODES):
if not cls.restore_instance: if not cls.restore_instance:
data_dir = prepareDataDir(TEST_DIR, i, "particl.conf", "part_") data_dir = prepareDataDir(TEST_DIR, i, "particl.conf", "part_")
if os.path.exists( if not os.path.exists(
os.path.join(cfg.PARTICL_BINDIR, "particl-wallet") os.path.join(
cfg.PARTICL_BINDIR,
part_wallet_bin,
)
): ):
logging.warning(f"{part_wallet_bin} not found.")
else:
try: try:
callrpc_cli( callrpc_cli(
cfg.PARTICL_BINDIR, cfg.PARTICL_BINDIR,
data_dir, data_dir,
"regtest", "regtest",
"-wallet=wallet.dat -legacy create", "-wallet=wallet.dat -legacy create",
"particl-wallet", part_wallet_bin,
) )
except Exception as e: except Exception as e:
logging.warning( logging.warning(
@@ -422,7 +430,7 @@ class BaseTest(unittest.TestCase):
data_dir, data_dir,
"regtest", "regtest",
"-wallet=wallet.dat create", "-wallet=wallet.dat create",
"particl-wallet", part_wallet_bin,
) )
cls.part_daemons.append( cls.part_daemons.append(
@@ -471,6 +479,7 @@ class BaseTest(unittest.TestCase):
) )
rpc("reservebalance", [False]) rpc("reservebalance", [False])
btc_wallet_bin = "bitcoin-wallet" + (".exe" if os.name == "nt" else "")
for i in range(NUM_BTC_NODES): for i in range(NUM_BTC_NODES):
if not cls.restore_instance: if not cls.restore_instance:
data_dir = prepareDataDir( data_dir = prepareDataDir(
@@ -481,9 +490,14 @@ class BaseTest(unittest.TestCase):
base_p2p_port=BTC_BASE_PORT, base_p2p_port=BTC_BASE_PORT,
base_rpc_port=BTC_BASE_RPC_PORT, base_rpc_port=BTC_BASE_RPC_PORT,
) )
if os.path.exists( if not os.path.exists(
os.path.join(cfg.BITCOIN_BINDIR, "bitcoin-wallet") os.path.join(
cfg.BITCOIN_BINDIR,
btc_wallet_bin,
)
): ):
logging.warning(f"{btc_wallet_bin} not found.")
else:
if BTC_USE_DESCRIPTORS: if BTC_USE_DESCRIPTORS:
# How to set blank and disable_private_keys with wallet util? # How to set blank and disable_private_keys with wallet util?
pass pass
@@ -494,7 +508,7 @@ class BaseTest(unittest.TestCase):
data_dir, data_dir,
"regtest", "regtest",
"-wallet=wallet.dat -legacy create", "-wallet=wallet.dat -legacy create",
"bitcoin-wallet", btc_wallet_bin,
) )
except Exception as e: except Exception as e:
logging.warning( logging.warning(
@@ -505,7 +519,7 @@ class BaseTest(unittest.TestCase):
data_dir, data_dir,
"regtest", "regtest",
"-wallet=wallet.dat create", "-wallet=wallet.dat create",
"bitcoin-wallet", btc_wallet_bin,
) )
cls.btc_daemons.append( cls.btc_daemons.append(
@@ -536,6 +550,7 @@ class BaseTest(unittest.TestCase):
) )
if cls.start_ltc_nodes: if cls.start_ltc_nodes:
ltc_wallet_bin = "litecoin-wallet" + (".exe" if os.name == "nt" else "")
for i in range(NUM_LTC_NODES): for i in range(NUM_LTC_NODES):
if not cls.restore_instance: if not cls.restore_instance:
data_dir = prepareDataDir( data_dir = prepareDataDir(
@@ -547,14 +562,16 @@ class BaseTest(unittest.TestCase):
base_rpc_port=LTC_BASE_RPC_PORT, base_rpc_port=LTC_BASE_RPC_PORT,
) )
if os.path.exists( if os.path.exists(
os.path.join(cfg.LITECOIN_BINDIR, "litecoin-wallet") os.path.join(cfg.LITECOIN_BINDIR, ltc_wallet_bin)
): ):
logging.warning(f"{ltc_wallet_bin} not found.")
else:
callrpc_cli( callrpc_cli(
cfg.LITECOIN_BINDIR, cfg.LITECOIN_BINDIR,
data_dir, data_dir,
"regtest", "regtest",
"-wallet=wallet.dat create", "-wallet=wallet.dat create",
"litecoin-wallet", ltc_wallet_bin,
) )
cls.ltc_daemons.append( cls.ltc_daemons.append(
@@ -620,6 +637,7 @@ class BaseTest(unittest.TestCase):
"create_wallet", "create_wallet",
{"filename": "testwallet", "language": "English"}, {"filename": "testwallet", "language": "English"},
) )
else:
cls.callxmrnodewallet( cls.callxmrnodewallet(
cls, i, "open_wallet", {"filename": "testwallet"} cls, i, "open_wallet", {"filename": "testwallet"}
) )