Skip to content

Have createwalletdescriptor auto-detect an unused(KEY) #32861

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

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/wallet/rpc/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,11 +883,37 @@ static RPCHelpMan createwalletdescriptor()

CExtPubKey xpub;
if (hdkey.isNull()) {
// First consider the HD key from active descriptors
std::set<CExtPubKey> active_xpubs = pwallet->GetActiveHDPubKeys();
if (active_xpubs.size() != 1) {
if (active_xpubs.size() == 1) {
xpub = *active_xpubs.begin();
} else if (active_xpubs.size() > 1) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
} else {
// Look for an unused(KEY) descriptor
std::set<DescriptorScriptPubKeyMan*> spkms{pwallet->GetScriptlessSPKMs()};

std::vector<CExtPubKey> wallet_xpubs;
for (auto* spkm : spkms) {
// Retrieve the pubkeys from the descriptor
LOCK(spkm->cs_desc_man);
std::set<CPubKey> pubkeys;
std::set<CExtPubKey> extpubs;
spkm->GetWalletDescriptor().descriptor->GetPubKeys(pubkeys, extpubs);

for (const CExtPubKey& xpub : extpubs) {
wallet_xpubs.emplace_back(xpub);
}
}

if (wallet_xpubs.empty()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No HD key found. Please generate one with 'addhdkey' or import an active descriptor.");
} else if (wallet_xpubs.size() > 1) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use. Please specify with 'hdkey'");
Comment on lines +911 to +912
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In e8b1440 "rpc: make createwalletdescriptor smarter"

Though this check here seems more thorough, but the presence of more than one spkm also seems sufficient to throw this error?

}

xpub = wallet_xpubs[0];
}
xpub = *active_xpubs.begin();
} else {
xpub = DecodeExtPubKey(hdkey.get_str());
if (!xpub.pubkey.IsValid()) {
Expand Down
23 changes: 22 additions & 1 deletion test/functional/wallet_createwalletdescriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def run_test(self):
self.test_basic()
self.test_imported_other_keys()
self.test_encrypted()
self.test_from_unused_desc()

def test_basic(self):
def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
Expand All @@ -39,7 +40,7 @@ def test_basic(self):
if desc["desc"].startswith("wpkh("):
expected_descs.append(desc["desc"])

assert_raises_rpc_error(-5, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'", wallet.createwalletdescriptor, "bech32")
assert_raises_rpc_error(-5, "No HD key found. Please generate one with 'addhdkey' or import an active descriptor.", wallet.createwalletdescriptor, "bech32")
assert_raises_rpc_error(-5, f"Private key for {xpub} is not known", wallet.createwalletdescriptor, type="bech32", hdkey=xpub)

self.log.info("Test createwalletdescriptor after importing active descriptor to blank wallet")
Expand Down Expand Up @@ -114,6 +115,26 @@ def test_encrypted(self):
with WalletUnlock(wallet, "pass"):
wallet.createwalletdescriptor(type="bech32m")

def test_from_unused_desc(self):
self.log.info("Test createwalletdescriptor from only an unused(KEY) descriptor")
self.nodes[0].createwallet("w1", blank=True)
w1 = self.nodes[0].get_wallet_rpc("w1")

# Wallet can't be completely empty
assert_raises_rpc_error(-5, "No HD key found. Please generate one with 'addhdkey' or import an active descriptor.", w1.createwalletdescriptor, "bech32")

# Create unused(KEY) descriptor and try again
w1.addhdkey()
w1.createwalletdescriptor(type="bech32")

self.nodes[0].createwallet("w2", blank=True)
w2 = self.nodes[0].get_wallet_rpc("w2")

# Multiple unused(KEY) descriptors require user to choose
w2.addhdkey()
w2.addhdkey()

assert_raises_rpc_error(-5, "Unable to determine which HD key to use. Please specify with 'hdkey'", w2.createwalletdescriptor, "bech32")


if __name__ == '__main__':
Expand Down
Loading