mirror of
https://github.com/basicswap/basicswap.git
synced 2025-05-20 11:34:40 +00:00
Merge pull request #252 from tecnovert/descriptors
Add BTC descriptor wallet support.
This commit is contained in:
commit
71fd3d10aa
9 changed files with 418 additions and 54 deletions
basicswap
tests/basicswap
|
@ -599,7 +599,12 @@ class BasicSwap(BaseApp):
|
||||||
}
|
}
|
||||||
|
|
||||||
# Passthrough settings
|
# Passthrough settings
|
||||||
for setting_name in ("wallet_name", "mweb_wallet_name"):
|
for setting_name in (
|
||||||
|
"use_descriptors",
|
||||||
|
"wallet_name",
|
||||||
|
"watch_wallet_name",
|
||||||
|
"mweb_wallet_name",
|
||||||
|
):
|
||||||
if setting_name in chain_client_settings:
|
if setting_name in chain_client_settings:
|
||||||
self.coin_clients[coin][setting_name] = chain_client_settings[
|
self.coin_clients[coin][setting_name] = chain_client_settings[
|
||||||
setting_name
|
setting_name
|
||||||
|
|
|
@ -1884,6 +1884,10 @@ def initialise_wallets(
|
||||||
|
|
||||||
if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
|
if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
|
||||||
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
|
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
|
||||||
|
|
||||||
|
use_descriptors = coin_settings.get(
|
||||||
|
"use_descriptors", False
|
||||||
|
)
|
||||||
swap_client.callcoinrpc(
|
swap_client.callcoinrpc(
|
||||||
c,
|
c,
|
||||||
"createwallet",
|
"createwallet",
|
||||||
|
@ -1893,9 +1897,22 @@ def initialise_wallets(
|
||||||
True,
|
True,
|
||||||
WALLET_ENCRYPTION_PWD,
|
WALLET_ENCRYPTION_PWD,
|
||||||
False,
|
False,
|
||||||
False,
|
use_descriptors,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
if use_descriptors:
|
||||||
|
swap_client.callcoinrpc(
|
||||||
|
c,
|
||||||
|
"createwallet",
|
||||||
|
[
|
||||||
|
coin_settings["watch_wallet_name"],
|
||||||
|
True,
|
||||||
|
True,
|
||||||
|
"",
|
||||||
|
False,
|
||||||
|
use_descriptors,
|
||||||
|
],
|
||||||
|
)
|
||||||
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
|
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
|
||||||
else:
|
else:
|
||||||
swap_client.callcoinrpc(
|
swap_client.callcoinrpc(
|
||||||
|
@ -2538,6 +2555,17 @@ def main():
|
||||||
if set_name != default_name:
|
if set_name != default_name:
|
||||||
coin_settings["wallet_name"] = set_name
|
coin_settings["wallet_name"] = set_name
|
||||||
|
|
||||||
|
ticker: str = coin_params["ticker"]
|
||||||
|
if toBool(os.getenv(ticker + "_USE_DESCRIPTORS", False)):
|
||||||
|
|
||||||
|
if coin_id not in (Coins.BTC,):
|
||||||
|
raise ValueError(f"Descriptor wallet unavailable for {coin_name}")
|
||||||
|
|
||||||
|
coin_settings["use_descriptors"] = True
|
||||||
|
coin_settings["watch_wallet_name"] = getWalletName(
|
||||||
|
coin_params, "bsx_watch", prefix_override=f"{ticker}_WATCH"
|
||||||
|
)
|
||||||
|
|
||||||
if PART_RPC_USER != "":
|
if PART_RPC_USER != "":
|
||||||
chainclients["particl"]["rpcuser"] = PART_RPC_USER
|
chainclients["particl"]["rpcuser"] = PART_RPC_USER
|
||||||
chainclients["particl"]["rpcpassword"] = PART_RPC_PWD
|
chainclients["particl"]["rpcpassword"] = PART_RPC_PWD
|
||||||
|
|
|
@ -91,6 +91,8 @@ chainparams = {
|
||||||
"bip44": 0,
|
"bip44": 0,
|
||||||
"min_amount": 100000,
|
"min_amount": 100000,
|
||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
|
"ext_public_key_prefix": 0x0488B21E,
|
||||||
|
"ext_secret_key_prefix": 0x0488ADE4,
|
||||||
},
|
},
|
||||||
"testnet": {
|
"testnet": {
|
||||||
"rpcport": 18332,
|
"rpcport": 18332,
|
||||||
|
@ -102,6 +104,8 @@ chainparams = {
|
||||||
"min_amount": 100000,
|
"min_amount": 100000,
|
||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
"name": "testnet3",
|
"name": "testnet3",
|
||||||
|
"ext_public_key_prefix": 0x043587CF,
|
||||||
|
"ext_secret_key_prefix": 0x04358394,
|
||||||
},
|
},
|
||||||
"regtest": {
|
"regtest": {
|
||||||
"rpcport": 18443,
|
"rpcport": 18443,
|
||||||
|
@ -112,6 +116,8 @@ chainparams = {
|
||||||
"bip44": 1,
|
"bip44": 1,
|
||||||
"min_amount": 100000,
|
"min_amount": 100000,
|
||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
|
"ext_public_key_prefix": 0x043587CF,
|
||||||
|
"ext_secret_key_prefix": 0x04358394,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Coins.LTC: {
|
Coins.LTC: {
|
||||||
|
|
64
basicswap/contrib/test_framework/descriptors.py
Normal file
64
basicswap/contrib/test_framework/descriptors.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2019 Pieter Wuille
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
"""Utility functions related to output descriptors"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
|
||||||
|
CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||||
|
GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd]
|
||||||
|
|
||||||
|
def descsum_polymod(symbols):
|
||||||
|
"""Internal function that computes the descriptor checksum."""
|
||||||
|
chk = 1
|
||||||
|
for value in symbols:
|
||||||
|
top = chk >> 35
|
||||||
|
chk = (chk & 0x7ffffffff) << 5 ^ value
|
||||||
|
for i in range(5):
|
||||||
|
chk ^= GENERATOR[i] if ((top >> i) & 1) else 0
|
||||||
|
return chk
|
||||||
|
|
||||||
|
def descsum_expand(s):
|
||||||
|
"""Internal function that does the character to symbol expansion"""
|
||||||
|
groups = []
|
||||||
|
symbols = []
|
||||||
|
for c in s:
|
||||||
|
if not c in INPUT_CHARSET:
|
||||||
|
return None
|
||||||
|
v = INPUT_CHARSET.find(c)
|
||||||
|
symbols.append(v & 31)
|
||||||
|
groups.append(v >> 5)
|
||||||
|
if len(groups) == 3:
|
||||||
|
symbols.append(groups[0] * 9 + groups[1] * 3 + groups[2])
|
||||||
|
groups = []
|
||||||
|
if len(groups) == 1:
|
||||||
|
symbols.append(groups[0])
|
||||||
|
elif len(groups) == 2:
|
||||||
|
symbols.append(groups[0] * 3 + groups[1])
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
def descsum_create(s):
|
||||||
|
"""Add a checksum to a descriptor without"""
|
||||||
|
symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
checksum = descsum_polymod(symbols) ^ 1
|
||||||
|
return s + '#' + ''.join(CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31] for i in range(8))
|
||||||
|
|
||||||
|
def descsum_check(s, require=True):
|
||||||
|
"""Verify that the checksum is correct in a descriptor"""
|
||||||
|
if not '#' in s:
|
||||||
|
return not require
|
||||||
|
if s[-9] != '#':
|
||||||
|
return False
|
||||||
|
if not all(x in CHECKSUM_CHARSET for x in s[-8:]):
|
||||||
|
return False
|
||||||
|
symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
|
||||||
|
return descsum_polymod(symbols) == 1
|
||||||
|
|
||||||
|
def drop_origins(s):
|
||||||
|
'''Drop the key origins from a descriptor'''
|
||||||
|
desc = re.sub(r'\[.+?\]', '', s)
|
||||||
|
if '#' in s:
|
||||||
|
desc = desc[:desc.index('#')]
|
||||||
|
return descsum_create(desc)
|
|
@ -18,12 +18,7 @@ from basicswap.basicswap_util import (
|
||||||
getVoutByAddress,
|
getVoutByAddress,
|
||||||
getVoutByScriptPubKey,
|
getVoutByScriptPubKey,
|
||||||
)
|
)
|
||||||
from basicswap.contrib.test_framework import (
|
from basicswap.interface.base import Secp256k1Interface
|
||||||
segwit_addr,
|
|
||||||
)
|
|
||||||
from basicswap.interface.base import (
|
|
||||||
Secp256k1Interface,
|
|
||||||
)
|
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
ensure,
|
ensure,
|
||||||
b2h,
|
b2h,
|
||||||
|
@ -35,6 +30,7 @@ from basicswap.util.ecc import (
|
||||||
pointToCPK,
|
pointToCPK,
|
||||||
CPKToPoint,
|
CPKToPoint,
|
||||||
)
|
)
|
||||||
|
from basicswap.util.extkey import ExtKeyPair
|
||||||
from basicswap.util.script import (
|
from basicswap.util.script import (
|
||||||
decodeScriptNum,
|
decodeScriptNum,
|
||||||
getCompactSizeLen,
|
getCompactSizeLen,
|
||||||
|
@ -44,6 +40,7 @@ from basicswap.util.script import (
|
||||||
from basicswap.util.address import (
|
from basicswap.util.address import (
|
||||||
toWIF,
|
toWIF,
|
||||||
b58encode,
|
b58encode,
|
||||||
|
b58decode,
|
||||||
decodeWif,
|
decodeWif,
|
||||||
decodeAddress,
|
decodeAddress,
|
||||||
pubkeyToAddress,
|
pubkeyToAddress,
|
||||||
|
@ -63,6 +60,8 @@ from coincurve.ecdsaotves import (
|
||||||
ecdsaotves_rec_enc_key,
|
ecdsaotves_rec_enc_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from basicswap.contrib.test_framework import segwit_addr
|
||||||
|
from basicswap.contrib.test_framework.descriptors import descsum_create
|
||||||
from basicswap.contrib.test_framework.messages import (
|
from basicswap.contrib.test_framework.messages import (
|
||||||
COIN,
|
COIN,
|
||||||
COutPoint,
|
COutPoint,
|
||||||
|
@ -267,9 +266,21 @@ class BTCInterface(Secp256k1Interface):
|
||||||
self._rpcauth = coin_settings["rpcauth"]
|
self._rpcauth = coin_settings["rpcauth"]
|
||||||
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||||
self._rpc_wallet = coin_settings.get("wallet_name", "wallet.dat")
|
self._rpc_wallet = coin_settings.get("wallet_name", "wallet.dat")
|
||||||
|
self._rpc_wallet_watch = coin_settings.get(
|
||||||
|
"watch_wallet_name", self._rpc_wallet
|
||||||
|
)
|
||||||
self.rpc_wallet = make_rpc_func(
|
self.rpc_wallet = make_rpc_func(
|
||||||
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
||||||
)
|
)
|
||||||
|
if self._rpc_wallet_watch == self._rpc_wallet:
|
||||||
|
self.rpc_wallet_watch = self.rpc_wallet
|
||||||
|
else:
|
||||||
|
self.rpc_wallet_watch = make_rpc_func(
|
||||||
|
self._rpcport,
|
||||||
|
self._rpcauth,
|
||||||
|
host=self._rpc_host,
|
||||||
|
wallet=self._rpc_wallet_watch,
|
||||||
|
)
|
||||||
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
||||||
self.setConfTarget(coin_settings["conf_target"])
|
self.setConfTarget(coin_settings["conf_target"])
|
||||||
self._use_segwit = coin_settings["use_segwit"]
|
self._use_segwit = coin_settings["use_segwit"]
|
||||||
|
@ -278,6 +289,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||||
self._expect_seedid_hex = None
|
self._expect_seedid_hex = None
|
||||||
self._altruistic = coin_settings.get("altruistic", True)
|
self._altruistic = coin_settings.get("altruistic", True)
|
||||||
|
self._use_descriptors = coin_settings.get("use_descriptors", False)
|
||||||
|
|
||||||
def open_rpc(self, wallet=None):
|
def open_rpc(self, wallet=None):
|
||||||
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
||||||
|
@ -360,9 +372,40 @@ class BTCInterface(Secp256k1Interface):
|
||||||
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) -> None:
|
||||||
key_wif = self.encodeKey(key_bytes)
|
assert len(key_bytes) == 32
|
||||||
self.rpc_wallet("sethdseed", [True, key_wif])
|
|
||||||
self._have_checked_seed = False
|
self._have_checked_seed = False
|
||||||
|
if self._use_descriptors:
|
||||||
|
self._log.info("Importing descriptors")
|
||||||
|
ek = ExtKeyPair()
|
||||||
|
ek.set_seed(key_bytes)
|
||||||
|
ek_encoded: str = self.encode_secret_extkey(ek.encode_v())
|
||||||
|
desc_external = descsum_create(f"wpkh({ek_encoded}/0h/0h/*h)")
|
||||||
|
desc_internal = descsum_create(f"wpkh({ek_encoded}/0h/1h/*h)")
|
||||||
|
rv = self.rpc_wallet(
|
||||||
|
"importdescriptors",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{"desc": desc_external, "timestamp": "now", "active": True},
|
||||||
|
{
|
||||||
|
"desc": desc_internal,
|
||||||
|
"timestamp": "now",
|
||||||
|
"active": True,
|
||||||
|
"internal": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
num_successful: int = 0
|
||||||
|
for entry in rv:
|
||||||
|
if entry.get("success", False) is True:
|
||||||
|
num_successful += 1
|
||||||
|
if num_successful != 2:
|
||||||
|
self._log.error(f"Failed to import descriptors: {rv}.")
|
||||||
|
raise ValueError("Failed to import descriptors.")
|
||||||
|
else:
|
||||||
|
key_wif = self.encodeKey(key_bytes)
|
||||||
|
self.rpc_wallet("sethdseed", [True, key_wif])
|
||||||
|
|
||||||
def getWalletInfo(self):
|
def getWalletInfo(self):
|
||||||
rv = self.rpc_wallet("getwalletinfo")
|
rv = self.rpc_wallet("getwalletinfo")
|
||||||
|
@ -372,7 +415,14 @@ class BTCInterface(Secp256k1Interface):
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def getWalletRestoreHeight(self) -> int:
|
def getWalletRestoreHeight(self) -> int:
|
||||||
start_time = self.rpc_wallet("getwalletinfo")["keypoololdest"]
|
if self._use_descriptors:
|
||||||
|
descriptor = self.getActiveDescriptor()
|
||||||
|
if descriptor is None:
|
||||||
|
start_time = 0
|
||||||
|
else:
|
||||||
|
start_time = descriptor["timestamp"]
|
||||||
|
else:
|
||||||
|
start_time = self.rpc_wallet("getwalletinfo")["keypoololdest"]
|
||||||
|
|
||||||
blockchaininfo = self.getBlockchainInfo()
|
blockchaininfo = self.getBlockchainInfo()
|
||||||
best_block = blockchaininfo["bestblockhash"]
|
best_block = blockchaininfo["bestblockhash"]
|
||||||
|
@ -392,6 +442,8 @@ class BTCInterface(Secp256k1Interface):
|
||||||
)
|
)
|
||||||
if block_header["time"] < start_time:
|
if block_header["time"] < start_time:
|
||||||
return block_header["height"]
|
return block_header["height"]
|
||||||
|
if "previousblockhash" not in block_header: # Genesis block
|
||||||
|
return block_header["height"]
|
||||||
block_hash = block_header["previousblockhash"]
|
block_hash = block_header["previousblockhash"]
|
||||||
finally:
|
finally:
|
||||||
self.close_rpc(rpc_conn)
|
self.close_rpc(rpc_conn)
|
||||||
|
@ -401,7 +453,32 @@ class BTCInterface(Secp256k1Interface):
|
||||||
wi = self.rpc_wallet("getwalletinfo")
|
wi = self.rpc_wallet("getwalletinfo")
|
||||||
return "Not found" if "hdseedid" not in wi else wi["hdseedid"]
|
return "Not found" if "hdseedid" not in wi else wi["hdseedid"]
|
||||||
|
|
||||||
|
def getActiveDescriptor(self):
|
||||||
|
descriptors = self.rpc_wallet("listdescriptors")["descriptors"]
|
||||||
|
for descriptor in descriptors:
|
||||||
|
if (
|
||||||
|
descriptor["desc"].startswith("wpkh")
|
||||||
|
and descriptor["active"] is True
|
||||||
|
and descriptor["internal"] is False
|
||||||
|
):
|
||||||
|
return descriptor
|
||||||
|
return None
|
||||||
|
|
||||||
def checkExpectedSeed(self, expect_seedid: str) -> bool:
|
def checkExpectedSeed(self, expect_seedid: str) -> bool:
|
||||||
|
if self._use_descriptors:
|
||||||
|
descriptor = self.getActiveDescriptor()
|
||||||
|
if descriptor is None:
|
||||||
|
self._log.debug("Could not find active descriptor.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
end = descriptor["desc"].find("/")
|
||||||
|
if end < 10:
|
||||||
|
return False
|
||||||
|
extkey = descriptor["desc"][5:end]
|
||||||
|
extkey_data = b58decode(extkey)[4:-4]
|
||||||
|
extkey_data_hash: bytes = hash160(extkey_data)
|
||||||
|
return True if extkey_data_hash.hex() == expect_seedid else False
|
||||||
|
|
||||||
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
|
||||||
|
@ -426,6 +503,10 @@ class BTCInterface(Secp256k1Interface):
|
||||||
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
||||||
if not or_watch_only:
|
if not or_watch_only:
|
||||||
return addr_info["ismine"]
|
return addr_info["ismine"]
|
||||||
|
|
||||||
|
if self._use_descriptors:
|
||||||
|
addr_info = self.rpc_wallet_watch("getaddressinfo", [address])
|
||||||
|
|
||||||
return addr_info["ismine"] or addr_info["iswatchonly"]
|
return addr_info["ismine"] or addr_info["iswatchonly"]
|
||||||
|
|
||||||
def checkAddressMine(self, address: str) -> None:
|
def checkAddressMine(self, address: str) -> None:
|
||||||
|
@ -493,6 +574,20 @@ class BTCInterface(Secp256k1Interface):
|
||||||
pkh = hash160(pk)
|
pkh = hash160(pk)
|
||||||
return segwit_addr.encode(bech32_prefix, version, pkh)
|
return segwit_addr.encode(bech32_prefix, version, pkh)
|
||||||
|
|
||||||
|
def encode_secret_extkey(self, ek_data: bytes) -> str:
|
||||||
|
assert len(ek_data) == 74
|
||||||
|
prefix = self.chainparams_network()["ext_secret_key_prefix"]
|
||||||
|
data: bytes = prefix.to_bytes(4, "big") + ek_data
|
||||||
|
checksum = sha256(sha256(data))
|
||||||
|
return b58encode(data + checksum[0:4])
|
||||||
|
|
||||||
|
def encode_public_extkey(self, ek_data: bytes) -> str:
|
||||||
|
assert len(ek_data) == 74
|
||||||
|
prefix = self.chainparams_network()["ext_public_key_prefix"]
|
||||||
|
data: bytes = prefix.to_bytes(4, "big") + ek_data
|
||||||
|
checksum = sha256(sha256(data))
|
||||||
|
return b58encode(data + checksum[0:4])
|
||||||
|
|
||||||
def pkh_to_address(self, pkh: bytes) -> str:
|
def pkh_to_address(self, pkh: bytes) -> str:
|
||||||
# pkh is ripemd160(sha256(pk))
|
# pkh is ripemd160(sha256(pk))
|
||||||
assert len(pkh) == 20
|
assert len(pkh) == 20
|
||||||
|
@ -528,7 +623,12 @@ class BTCInterface(Secp256k1Interface):
|
||||||
pk = self.getPubkey(key)
|
pk = self.getPubkey(key)
|
||||||
return hash160(pk)
|
return hash160(pk)
|
||||||
|
|
||||||
def getSeedHash(self, seed) -> bytes:
|
def getSeedHash(self, seed: bytes) -> bytes:
|
||||||
|
if self._use_descriptors:
|
||||||
|
ek = ExtKeyPair()
|
||||||
|
ek.set_seed(seed)
|
||||||
|
return hash160(ek.encode_p())
|
||||||
|
|
||||||
return self.getAddressHashFromKey(seed)[::-1]
|
return self.getAddressHashFromKey(seed)[::-1]
|
||||||
|
|
||||||
def encodeKey(self, key_bytes: bytes) -> str:
|
def encodeKey(self, key_bytes: bytes) -> str:
|
||||||
|
@ -1411,7 +1511,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
script_pk = self.getPkDest(Kbs)
|
script_pk = self.getPkDest(Kbs)
|
||||||
|
|
||||||
if locked_n is None:
|
if locked_n is None:
|
||||||
wtx = self.rpc_wallet(
|
wtx = self.rpc_wallet_watch(
|
||||||
"gettransaction",
|
"gettransaction",
|
||||||
[
|
[
|
||||||
chain_b_lock_txid.hex(),
|
chain_b_lock_txid.hex(),
|
||||||
|
@ -1448,10 +1548,23 @@ class BTCInterface(Secp256k1Interface):
|
||||||
|
|
||||||
return bytes.fromhex(self.publishTx(b_lock_spend_tx))
|
return bytes.fromhex(self.publishTx(b_lock_spend_tx))
|
||||||
|
|
||||||
def importWatchOnlyAddress(self, address: str, label: str):
|
def importWatchOnlyAddress(self, address: str, label: str) -> None:
|
||||||
|
if self._use_descriptors:
|
||||||
|
desc_watch = descsum_create(f"addr({address})")
|
||||||
|
rv = self.rpc_wallet_watch(
|
||||||
|
"importdescriptors",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{"desc": desc_watch, "timestamp": "now", "active": False},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
ensure(rv[0]["success"] is True, "importdescriptors failed for watchonly")
|
||||||
|
return
|
||||||
|
|
||||||
self.rpc_wallet("importaddress", [address, label, False])
|
self.rpc_wallet("importaddress", [address, label, False])
|
||||||
|
|
||||||
def isWatchOnlyAddress(self, address: str):
|
def isWatchOnlyAddress(self, address: str) -> bool:
|
||||||
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
||||||
return addr_info["iswatchonly"]
|
return addr_info["iswatchonly"]
|
||||||
|
|
||||||
|
@ -1481,7 +1594,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
|
|
||||||
return_txid = True if txid is None else False
|
return_txid = True if txid is None else False
|
||||||
if txid is None:
|
if txid is None:
|
||||||
txns = self.rpc_wallet(
|
txns = self.rpc_wallet_watch(
|
||||||
"listunspent",
|
"listunspent",
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
|
@ -1502,7 +1615,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# set `include_watchonly` explicitly to `True` to get transactions for watchonly addresses also in BCH
|
# set `include_watchonly` explicitly to `True` to get transactions for watchonly addresses also in BCH
|
||||||
tx = self.rpc_wallet("gettransaction", [txid.hex(), True])
|
tx = self.rpc_wallet_watch("gettransaction", [txid.hex(), True])
|
||||||
|
|
||||||
block_height = 0
|
block_height = 0
|
||||||
if "blockhash" in tx:
|
if "blockhash" in tx:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
# Copyright (c) 2024 The Basicswap developers
|
# Copyright (c) 2024-2025 The Basicswap developers
|
||||||
# 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.
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ from urllib.request import urlopen
|
||||||
|
|
||||||
from .util import read_json_api
|
from .util import read_json_api
|
||||||
from basicswap.rpc import callrpc
|
from basicswap.rpc import callrpc
|
||||||
|
from basicswap.util import toBool
|
||||||
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
||||||
from basicswap.bin.prepare import downloadPIVXParams
|
from basicswap.bin.prepare import downloadPIVXParams
|
||||||
|
|
||||||
|
@ -44,6 +45,8 @@ PIVX_BASE_ZMQ_PORT = 36892
|
||||||
|
|
||||||
PREFIX_SECRET_KEY_REGTEST = 0x2E
|
PREFIX_SECRET_KEY_REGTEST = 0x2E
|
||||||
|
|
||||||
|
BTC_USE_DESCRIPTORS = toBool(os.getenv("BTC_USE_DESCRIPTORS", False))
|
||||||
|
|
||||||
|
|
||||||
def prepareDataDir(
|
def prepareDataDir(
|
||||||
datadir,
|
datadir,
|
||||||
|
|
|
@ -19,13 +19,10 @@ from io import StringIO
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from basicswap.rpc_xmr import (
|
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
||||||
callrpc_xmr,
|
from basicswap.rpc_xmr import callrpc_xmr
|
||||||
)
|
|
||||||
from tests.basicswap.mnemonics import mnemonics
|
from tests.basicswap.mnemonics import mnemonics
|
||||||
from tests.basicswap.util import (
|
from tests.basicswap.util import waitForServer
|
||||||
waitForServer,
|
|
||||||
)
|
|
||||||
from tests.basicswap.common import (
|
from tests.basicswap.common import (
|
||||||
BASE_PORT,
|
BASE_PORT,
|
||||||
BASE_RPC_PORT,
|
BASE_RPC_PORT,
|
||||||
|
@ -35,6 +32,7 @@ from tests.basicswap.common import (
|
||||||
LTC_BASE_PORT,
|
LTC_BASE_PORT,
|
||||||
LTC_BASE_RPC_PORT,
|
LTC_BASE_RPC_PORT,
|
||||||
PIVX_BASE_PORT,
|
PIVX_BASE_PORT,
|
||||||
|
BTC_USE_DESCRIPTORS,
|
||||||
)
|
)
|
||||||
from tests.basicswap.extended.test_dcr import (
|
from tests.basicswap.extended.test_dcr import (
|
||||||
DCR_BASE_PORT,
|
DCR_BASE_PORT,
|
||||||
|
@ -49,8 +47,6 @@ from tests.basicswap.extended.test_doge import (
|
||||||
DOGE_BASE_RPC_PORT,
|
DOGE_BASE_RPC_PORT,
|
||||||
)
|
)
|
||||||
|
|
||||||
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
|
||||||
|
|
||||||
import basicswap.config as cfg
|
import basicswap.config as cfg
|
||||||
import basicswap.bin.run as runSystem
|
import basicswap.bin.run as runSystem
|
||||||
|
|
||||||
|
@ -133,6 +129,7 @@ def run_prepare(
|
||||||
os.environ["PART_RPC_PORT"] = str(PARTICL_RPC_PORT_BASE)
|
os.environ["PART_RPC_PORT"] = str(PARTICL_RPC_PORT_BASE)
|
||||||
os.environ["BTC_RPC_PORT"] = str(BITCOIN_RPC_PORT_BASE)
|
os.environ["BTC_RPC_PORT"] = str(BITCOIN_RPC_PORT_BASE)
|
||||||
os.environ["BTC_PORT"] = str(BITCOIN_PORT_BASE)
|
os.environ["BTC_PORT"] = str(BITCOIN_PORT_BASE)
|
||||||
|
os.environ["BTC_USE_DESCRIPTORS"] = str(BTC_USE_DESCRIPTORS)
|
||||||
os.environ["LTC_RPC_PORT"] = str(LITECOIN_RPC_PORT_BASE)
|
os.environ["LTC_RPC_PORT"] = str(LITECOIN_RPC_PORT_BASE)
|
||||||
os.environ["DCR_RPC_PORT"] = str(DECRED_RPC_PORT_BASE)
|
os.environ["DCR_RPC_PORT"] = str(DECRED_RPC_PORT_BASE)
|
||||||
os.environ["FIRO_RPC_PORT"] = str(FIRO_RPC_PORT_BASE)
|
os.environ["FIRO_RPC_PORT"] = str(FIRO_RPC_PORT_BASE)
|
||||||
|
|
|
@ -26,6 +26,7 @@ from basicswap.db import (
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
make_int,
|
make_int,
|
||||||
)
|
)
|
||||||
|
from basicswap.util.extkey import ExtKeyPair
|
||||||
from basicswap.interface.base import Curves
|
from basicswap.interface.base import Curves
|
||||||
from tests.basicswap.util import (
|
from tests.basicswap.util import (
|
||||||
read_json_api,
|
read_json_api,
|
||||||
|
@ -40,6 +41,7 @@ from tests.basicswap.common import (
|
||||||
wait_for_none_active,
|
wait_for_none_active,
|
||||||
BTC_BASE_RPC_PORT,
|
BTC_BASE_RPC_PORT,
|
||||||
)
|
)
|
||||||
|
from basicswap.contrib.test_framework.descriptors import descsum_create
|
||||||
from basicswap.contrib.test_framework.messages import (
|
from basicswap.contrib.test_framework.messages import (
|
||||||
ToHex,
|
ToHex,
|
||||||
FromHex,
|
FromHex,
|
||||||
|
@ -58,6 +60,8 @@ from .test_xmr import BaseTest, test_delay_event, callnoderpc
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
||||||
|
|
||||||
|
|
||||||
class TestFunctions(BaseTest):
|
class TestFunctions(BaseTest):
|
||||||
base_rpc_port = None
|
base_rpc_port = None
|
||||||
|
@ -1166,7 +1170,6 @@ class BasicSwapTest(TestFunctions):
|
||||||
logging.info("---------- Test {} hdwallet".format(self.test_coin_from.name))
|
logging.info("---------- Test {} hdwallet".format(self.test_coin_from.name))
|
||||||
ci = self.swap_clients[0].ci(self.test_coin_from)
|
ci = self.swap_clients[0].ci(self.test_coin_from)
|
||||||
|
|
||||||
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
|
||||||
test_wif = (
|
test_wif = (
|
||||||
self.swap_clients[0]
|
self.swap_clients[0]
|
||||||
.ci(self.test_coin_from)
|
.ci(self.test_coin_from)
|
||||||
|
@ -1178,10 +1181,35 @@ class BasicSwapTest(TestFunctions):
|
||||||
"createwallet", [new_wallet_name, False, True, "", False, False]
|
"createwallet", [new_wallet_name, False, True, "", False, False]
|
||||||
)
|
)
|
||||||
self.callnoderpc("sethdseed", [True, test_wif], wallet=new_wallet_name)
|
self.callnoderpc("sethdseed", [True, test_wif], wallet=new_wallet_name)
|
||||||
|
|
||||||
|
wi = self.callnoderpc("getwalletinfo", wallet=new_wallet_name)
|
||||||
|
assert wi["hdseedid"] == "3da5c0af91879e8ce97d9a843874601c08688078"
|
||||||
|
|
||||||
addr = self.callnoderpc("getnewaddress", wallet=new_wallet_name)
|
addr = self.callnoderpc("getnewaddress", wallet=new_wallet_name)
|
||||||
self.callnoderpc("unloadwallet", [new_wallet_name])
|
addr_info = self.callnoderpc(
|
||||||
|
"getaddressinfo",
|
||||||
|
[
|
||||||
|
addr,
|
||||||
|
],
|
||||||
|
wallet=new_wallet_name,
|
||||||
|
)
|
||||||
|
assert addr_info["hdmasterfingerprint"] == "a55b7ea9"
|
||||||
|
assert addr_info["hdkeypath"] == "m/0'/0'/0'"
|
||||||
assert addr == "bcrt1qps7hnjd866e9ynxadgseprkc2l56m00dvwargr"
|
assert addr == "bcrt1qps7hnjd866e9ynxadgseprkc2l56m00dvwargr"
|
||||||
|
|
||||||
|
addr_change = self.callnoderpc("getrawchangeaddress", wallet=new_wallet_name)
|
||||||
|
addr_info = self.callnoderpc(
|
||||||
|
"getaddressinfo",
|
||||||
|
[
|
||||||
|
addr_change,
|
||||||
|
],
|
||||||
|
wallet=new_wallet_name,
|
||||||
|
)
|
||||||
|
assert addr_info["hdmasterfingerprint"] == "a55b7ea9"
|
||||||
|
assert addr_info["hdkeypath"] == "m/0'/1'/0'"
|
||||||
|
assert addr_change == "bcrt1qdl9ryxkqjltv42lhfnqgdjf9tagxsjpp2xak9a"
|
||||||
|
self.callnoderpc("unloadwallet", [new_wallet_name])
|
||||||
|
|
||||||
self.swap_clients[0].initialiseWallet(Coins.BTC, raise_errors=True)
|
self.swap_clients[0].initialiseWallet(Coins.BTC, raise_errors=True)
|
||||||
assert self.swap_clients[0].checkWalletSeed(Coins.BTC) is True
|
assert self.swap_clients[0].checkWalletSeed(Coins.BTC) is True
|
||||||
for i in range(1500):
|
for i in range(1500):
|
||||||
|
@ -1561,6 +1589,97 @@ class BasicSwapTest(TestFunctions):
|
||||||
)
|
)
|
||||||
assert len(tx_wallet["blockhash"]) == 64
|
assert len(tx_wallet["blockhash"]) == 64
|
||||||
|
|
||||||
|
def test_013_descriptor_wallet(self):
|
||||||
|
logging.info(f"---------- Test {self.test_coin_from.name} descriptor wallet")
|
||||||
|
|
||||||
|
ci = self.swap_clients[0].ci(self.test_coin_from)
|
||||||
|
|
||||||
|
ek = ExtKeyPair()
|
||||||
|
ek.set_seed(bytes.fromhex(test_seed))
|
||||||
|
ek_encoded: str = ci.encode_secret_extkey(ek.encode_v())
|
||||||
|
new_wallet_name = "descriptors_" + random.randbytes(10).hex()
|
||||||
|
new_watch_wallet_name = "watch_descriptors_" + random.randbytes(10).hex()
|
||||||
|
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
|
||||||
|
ci.rpc("createwallet", [new_wallet_name, False, True, "", False, True])
|
||||||
|
ci.rpc("createwallet", [new_watch_wallet_name, True, True, "", False, True])
|
||||||
|
|
||||||
|
desc_external = descsum_create(f"wpkh({ek_encoded}/0h/0h/*h)")
|
||||||
|
desc_internal = descsum_create(f"wpkh({ek_encoded}/0h/1h/*h)")
|
||||||
|
self.callnoderpc(
|
||||||
|
"importdescriptors",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"desc": desc_external,
|
||||||
|
"timestamp": "now",
|
||||||
|
"active": True,
|
||||||
|
"range": [0, 10],
|
||||||
|
"next_index": 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": desc_internal,
|
||||||
|
"timestamp": "now",
|
||||||
|
"active": True,
|
||||||
|
"internal": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
wallet=new_wallet_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
addr = self.callnoderpc("getnewaddress", wallet=new_wallet_name)
|
||||||
|
addr_info = self.callnoderpc(
|
||||||
|
"getaddressinfo",
|
||||||
|
[
|
||||||
|
addr,
|
||||||
|
],
|
||||||
|
wallet=new_wallet_name,
|
||||||
|
)
|
||||||
|
assert addr_info["hdmasterfingerprint"] == "a55b7ea9"
|
||||||
|
assert addr_info["hdkeypath"] == "m/0h/0h/0h"
|
||||||
|
assert addr == "bcrt1qps7hnjd866e9ynxadgseprkc2l56m00dvwargr"
|
||||||
|
|
||||||
|
addr_change = self.callnoderpc("getrawchangeaddress", wallet=new_wallet_name)
|
||||||
|
addr_info = self.callnoderpc(
|
||||||
|
"getaddressinfo",
|
||||||
|
[
|
||||||
|
addr_change,
|
||||||
|
],
|
||||||
|
wallet=new_wallet_name,
|
||||||
|
)
|
||||||
|
assert addr_info["hdmasterfingerprint"] == "a55b7ea9"
|
||||||
|
assert addr_info["hdkeypath"] == "m/0h/1h/0h"
|
||||||
|
assert addr_change == "bcrt1qdl9ryxkqjltv42lhfnqgdjf9tagxsjpp2xak9a"
|
||||||
|
|
||||||
|
desc_watch = descsum_create(f"addr({addr})")
|
||||||
|
self.callnoderpc(
|
||||||
|
"importdescriptors",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{"desc": desc_watch, "timestamp": "now", "active": False},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
wallet=new_watch_wallet_name,
|
||||||
|
)
|
||||||
|
ci.rpc_wallet("sendtoaddress", [addr, 1])
|
||||||
|
found: bool = False
|
||||||
|
for i in range(10):
|
||||||
|
txn_list = self.callnoderpc(
|
||||||
|
"listtransactions", ["*", 100, 0, True], wallet=new_watch_wallet_name
|
||||||
|
)
|
||||||
|
test_delay_event.wait(1)
|
||||||
|
if len(txn_list) > 0:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
assert found
|
||||||
|
|
||||||
|
# Test that addresses can be generated beyond range in listdescriptors
|
||||||
|
for i in range(2000):
|
||||||
|
self.callnoderpc("getnewaddress", wallet=new_wallet_name)
|
||||||
|
|
||||||
|
self.callnoderpc("unloadwallet", [new_wallet_name])
|
||||||
|
self.callnoderpc("unloadwallet", [new_watch_wallet_name])
|
||||||
|
|
||||||
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)
|
||||||
|
@ -1862,11 +1981,11 @@ class TestBTC(BasicSwapTest):
|
||||||
assert "seed is set from the Basicswap mnemonic" in rv["error"]
|
assert "seed is set from the Basicswap mnemonic" in rv["error"]
|
||||||
|
|
||||||
rv = read_json_api(1800, "getcoinseed", {"coin": "BTC"})
|
rv = read_json_api(1800, "getcoinseed", {"coin": "BTC"})
|
||||||
assert (
|
assert rv["seed"] == test_seed
|
||||||
rv["seed"]
|
assert rv["seed_id"] in (
|
||||||
== "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
"3da5c0af91879e8ce97d9a843874601c08688078",
|
||||||
|
"4a231080ec6f4078e543d39cc6dcf0b922c9b16b",
|
||||||
)
|
)
|
||||||
assert rv["seed_id"] == "3da5c0af91879e8ce97d9a843874601c08688078"
|
|
||||||
assert rv["seed_id"] == rv["expected_seed_id"]
|
assert rv["seed_id"] == rv["expected_seed_id"]
|
||||||
|
|
||||||
rv = read_json_api(
|
rv = read_json_api(
|
||||||
|
|
|
@ -83,6 +83,7 @@ from tests.basicswap.common import (
|
||||||
LTC_BASE_PORT,
|
LTC_BASE_PORT,
|
||||||
LTC_BASE_RPC_PORT,
|
LTC_BASE_RPC_PORT,
|
||||||
PREFIX_SECRET_KEY_REGTEST,
|
PREFIX_SECRET_KEY_REGTEST,
|
||||||
|
BTC_USE_DESCRIPTORS,
|
||||||
)
|
)
|
||||||
from basicswap.db_util import (
|
from basicswap.db_util import (
|
||||||
remove_expired_data,
|
remove_expired_data,
|
||||||
|
@ -172,6 +173,7 @@ def prepare_swapclient_dir(
|
||||||
"datadir": os.path.join(datadir, "btc_" + str(node_id)),
|
"datadir": os.path.join(datadir, "btc_" + str(node_id)),
|
||||||
"bindir": cfg.BITCOIN_BINDIR,
|
"bindir": cfg.BITCOIN_BINDIR,
|
||||||
"use_segwit": True,
|
"use_segwit": True,
|
||||||
|
"use_descriptors": BTC_USE_DESCRIPTORS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"check_progress_seconds": 2,
|
"check_progress_seconds": 2,
|
||||||
|
@ -189,6 +191,9 @@ def prepare_swapclient_dir(
|
||||||
"restrict_unknown_seed_wallets": False,
|
"restrict_unknown_seed_wallets": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if BTC_USE_DESCRIPTORS:
|
||||||
|
settings["chainclients"]["bitcoin"]["watch_wallet_name"] = "bsx_watch"
|
||||||
|
|
||||||
if Coins.XMR in with_coins:
|
if Coins.XMR in with_coins:
|
||||||
settings["chainclients"]["monero"] = {
|
settings["chainclients"]["monero"] = {
|
||||||
"connection_type": "rpc",
|
"connection_type": "rpc",
|
||||||
|
@ -474,25 +479,29 @@ class BaseTest(unittest.TestCase):
|
||||||
if os.path.exists(
|
if os.path.exists(
|
||||||
os.path.join(cfg.BITCOIN_BINDIR, "bitcoin-wallet")
|
os.path.join(cfg.BITCOIN_BINDIR, "bitcoin-wallet")
|
||||||
):
|
):
|
||||||
try:
|
if BTC_USE_DESCRIPTORS:
|
||||||
callrpc_cli(
|
# How to set blank and disable_private_keys with wallet util?
|
||||||
cfg.BITCOIN_BINDIR,
|
pass
|
||||||
data_dir,
|
else:
|
||||||
"regtest",
|
try:
|
||||||
"-wallet=wallet.dat -legacy create",
|
callrpc_cli(
|
||||||
"bitcoin-wallet",
|
cfg.BITCOIN_BINDIR,
|
||||||
)
|
data_dir,
|
||||||
except Exception as e:
|
"regtest",
|
||||||
logging.warning(
|
"-wallet=wallet.dat -legacy create",
|
||||||
f"bitcoin-wallet create failed {e}, retrying without -legacy"
|
"bitcoin-wallet",
|
||||||
)
|
)
|
||||||
callrpc_cli(
|
except Exception as e:
|
||||||
cfg.BITCOIN_BINDIR,
|
logging.warning(
|
||||||
data_dir,
|
f"bitcoin-wallet create failed {e}, retrying without -legacy"
|
||||||
"regtest",
|
)
|
||||||
"-wallet=wallet.dat create",
|
callrpc_cli(
|
||||||
"bitcoin-wallet",
|
cfg.BITCOIN_BINDIR,
|
||||||
)
|
data_dir,
|
||||||
|
"regtest",
|
||||||
|
"-wallet=wallet.dat create",
|
||||||
|
"bitcoin-wallet",
|
||||||
|
)
|
||||||
|
|
||||||
cls.btc_daemons.append(
|
cls.btc_daemons.append(
|
||||||
startDaemon(
|
startDaemon(
|
||||||
|
@ -505,9 +514,21 @@ class BaseTest(unittest.TestCase):
|
||||||
"Started %s %d", cfg.BITCOIND, cls.part_daemons[-1].handle.pid
|
"Started %s %d", cfg.BITCOIND, cls.part_daemons[-1].handle.pid
|
||||||
)
|
)
|
||||||
|
|
||||||
waitForRPC(
|
if BTC_USE_DESCRIPTORS:
|
||||||
make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT), test_delay_event
|
rpc_func = make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT)
|
||||||
)
|
waitForRPC(
|
||||||
|
rpc_func, test_delay_event, rpc_command="getblockchaininfo"
|
||||||
|
)
|
||||||
|
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
|
||||||
|
rpc_func(
|
||||||
|
"createwallet", ["wallet.dat", False, True, "", False, True]
|
||||||
|
)
|
||||||
|
rpc_func("createwallet", ["bsx_watch", True, True, "", False, True])
|
||||||
|
else:
|
||||||
|
waitForRPC(
|
||||||
|
make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT),
|
||||||
|
test_delay_event,
|
||||||
|
)
|
||||||
|
|
||||||
if cls.start_ltc_nodes:
|
if cls.start_ltc_nodes:
|
||||||
for i in range(NUM_LTC_NODES):
|
for i in range(NUM_LTC_NODES):
|
||||||
|
@ -658,6 +679,11 @@ class BaseTest(unittest.TestCase):
|
||||||
xmr_ci.getMainWalletAddress(),
|
xmr_ci.getMainWalletAddress(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if BTC_USE_DESCRIPTORS:
|
||||||
|
# sc.initialiseWallet(Coins.BTC)
|
||||||
|
# Import a random seed to keep the existing test behaviour. BTC core rescans even with timestamp: now.
|
||||||
|
sc.ci(Coins.BTC).initialiseWallet(random.randbytes(32))
|
||||||
|
|
||||||
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
|
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
|
||||||
cls.http_threads.append(t)
|
cls.http_threads.append(t)
|
||||||
t.start()
|
t.start()
|
||||||
|
@ -685,6 +711,7 @@ class BaseTest(unittest.TestCase):
|
||||||
"getnewaddress",
|
"getnewaddress",
|
||||||
["mining_addr", "bech32"],
|
["mining_addr", "bech32"],
|
||||||
base_rpc_port=BTC_BASE_RPC_PORT,
|
base_rpc_port=BTC_BASE_RPC_PORT,
|
||||||
|
wallet="wallet.dat",
|
||||||
)
|
)
|
||||||
num_blocks = 400 # Mine enough to activate segwit
|
num_blocks = 400 # Mine enough to activate segwit
|
||||||
logging.info("Mining %d Bitcoin blocks to %s", num_blocks, cls.btc_addr)
|
logging.info("Mining %d Bitcoin blocks to %s", num_blocks, cls.btc_addr)
|
||||||
|
@ -700,6 +727,7 @@ class BaseTest(unittest.TestCase):
|
||||||
"getnewaddress",
|
"getnewaddress",
|
||||||
["initial addr"],
|
["initial addr"],
|
||||||
base_rpc_port=BTC_BASE_RPC_PORT,
|
base_rpc_port=BTC_BASE_RPC_PORT,
|
||||||
|
wallet="wallet.dat",
|
||||||
)
|
)
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
callnoderpc(
|
callnoderpc(
|
||||||
|
@ -707,6 +735,7 @@ class BaseTest(unittest.TestCase):
|
||||||
"sendtoaddress",
|
"sendtoaddress",
|
||||||
[btc_addr1, 100],
|
[btc_addr1, 100],
|
||||||
base_rpc_port=BTC_BASE_RPC_PORT,
|
base_rpc_port=BTC_BASE_RPC_PORT,
|
||||||
|
wallet="wallet.dat",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Switch addresses so wallet amounts stay constant
|
# Switch addresses so wallet amounts stay constant
|
||||||
|
|
Loading…
Reference in a new issue