Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly manage addresses caching using Core native descriptor wallets #1193

Merged
82 changes: 39 additions & 43 deletions src/cryptoadvance/specter/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@


class Wallet:
# if the wallet is old we import 300 addresses
IMPORT_KEYPOOL = 300
# a gap of 20 addresses is what many wallets do (not used with descriptor wallets)
GAP_LIMIT = 20
# minimal fee rate is slightly above 1 sat/vbyte
Expand Down Expand Up @@ -1150,12 +1148,6 @@ def get_balance(self):
return self.balance

def keypoolrefill(self, start, end=None, change=False):
# Descriptor wallets were introduced in v0.21.0, but upgraded nodes may
# still have legacy wallets. Use getwalletinfo to check the wallet type.
# The "keypool" for descriptor wallets is automatically refilled
if self.use_descriptors and start > 0:
return

if end is None:
# end is ignored for descriptor wallets
end = start + self.GAP_LIMIT
Expand Down Expand Up @@ -1185,43 +1177,47 @@ def keypoolrefill(self, start, end=None, change=False):
]
self._addresses.add(addresses, check_rpc=False)

if not self.is_multisig:
if self.use_descriptors:
r = self.rpc.importdescriptors(args)
else:
r = self.rpc.importmulti(args, {"rescan": False})
# bip67 requires sorted public keys for multisig addresses
else:
if self.use_descriptors:
self.rpc.importdescriptors(args)
# Descriptor wallets were introduced in v0.21.0, but upgraded nodes may
# still have legacy wallets. Use getwalletinfo to check the wallet type.
# The "keypool" for descriptor wallets is automatically refilled
if not self.use_descriptors or start > 0:
if not self.is_multisig:
if self.use_descriptors:
r = self.rpc.importdescriptors(args)
else:
r = self.rpc.importmulti(args, {"rescan": False})
# bip67 requires sorted public keys for multisig addresses
else:
# try if sortedmulti is supported
r = self.rpc.importmulti(args, {"rescan": False})
# doesn't raise, but instead returns "success": False
if not r[0]["success"]:
# first import normal multi
# remove checksum
desc = desc.split("#")[0]
# switch to multi
desc = desc.replace("sortedmulti", "multi")
# add checksum
desc = AddChecksum(desc)
# update descriptor
args[0]["desc"] = desc
if self.use_descriptors:
self.rpc.importdescriptors(args)
else:
# try if sortedmulti is supported
r = self.rpc.importmulti(args, {"rescan": False})
# make a batch of single addresses to import
arg = args[0]
# remove range key
arg.pop("range")
batch = []
for i in range(start, end):
sorted_desc = sort_descriptor(desc, index=i)
# create fresh object
obj = {}
obj.update(arg)
obj.update({"desc": sorted_desc})
batch.append(obj)
r = self.rpc.importmulti(batch, {"rescan": False})
# doesn't raise, but instead returns "success": False
if not r[0]["success"]:
# first import normal multi
# remove checksum
desc = desc.split("#")[0]
# switch to multi
desc = desc.replace("sortedmulti", "multi")
# add checksum
desc = AddChecksum(desc)
# update descriptor
args[0]["desc"] = desc
r = self.rpc.importmulti(args, {"rescan": False})
# make a batch of single addresses to import
arg = args[0]
# remove range key
arg.pop("range")
batch = []
for i in range(start, end):
sorted_desc = sort_descriptor(desc, index=i)
# create fresh object
obj = {}
obj.update(arg)
obj.update({"desc": sorted_desc})
batch.append(obj)
r = self.rpc.importmulti(batch, {"rescan": False})
if change:
self.change_keypool = end
else:
Expand Down