mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 06:43:45 +01:00
Merge #19136: wallet: add parent_desc to getaddressinfo
de6b389d5dtests: Test getaddressinfo parent_desc (Andrew Chow)e4ac869a0arpc: Add parent descriptor to getaddressinfo output (Andrew Chow)bbe4a36152wallet: Add GetDescriptorString to DescriptorScriptPubKeyMan (Andrew Chow)9be1437c49descriptors: Add ToNormalizedString and tests (Andrew Chow) Pull request description: Adds `parent_desc` field to the `getaddressinfo` RPC to export a public descriptor. Using the given address, `getaddressinfo` will look up which `DescriptorScriptPubKeyMan` can be used to produce that address. It will then return the descriptor for that `DescriptorScriptPubKeyMan` in the `parent_desc` field. The descriptor will be in a normalized form where the xpub at the last hardened step is derived so that the descriptor can be imported to other wallets. Tests are added to check that the correct descriptor is being returned for the wallet's addresses and that these descriptors can be imported and used in other wallets. As part of this PR, a `ToNormalizedString` function is added to the descriptor classes. This really only has an effect on `BIP32PubkeyProvider`s that have hardened derivation steps. Tests are added to check that normalized descriptors are returned. ACKs for top commit: Sjors: utACKde6b389d5dS3RK: Tested ACKde6b389jonatack: Tested ACKde6b389d5dmodulo a few minor comments fjahr: Code review ACKde6b389d5dmeshcollider: Tested ACKde6b389d5dTree-SHA512: a633e4a39f2abbd95afd7488484cfa66fdd2651dac59fe59f2b80a0940a2a4a13acf889c534a6948903d701484a2ba1218e3081feafe0b9a720dccfa9e43ca2b
This commit is contained in:
@@ -179,6 +179,9 @@ public:
|
||||
/** Get the descriptor string form including private data (if available in arg). */
|
||||
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
|
||||
|
||||
/** Get the descriptor string form with the xpub at the last hardened derivation */
|
||||
virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const = 0;
|
||||
|
||||
/** Derive a private key, if private data is available in arg. */
|
||||
virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
|
||||
};
|
||||
@@ -212,6 +215,21 @@ public:
|
||||
ret = "[" + OriginString() + "]" + std::move(sub);
|
||||
return true;
|
||||
}
|
||||
bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override
|
||||
{
|
||||
std::string sub;
|
||||
if (!m_provider->ToNormalizedString(arg, sub, priv)) return false;
|
||||
// If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider
|
||||
// In that case, we need to strip out the leading square bracket and fingerprint from the substring,
|
||||
// and append that to our own origin string.
|
||||
if (sub[0] == '[') {
|
||||
sub = sub.substr(9);
|
||||
ret = "[" + OriginString() + std::move(sub);
|
||||
} else {
|
||||
ret = "[" + OriginString() + "]" + std::move(sub);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||
{
|
||||
return m_provider->GetPrivKey(pos, arg, key);
|
||||
@@ -243,6 +261,12 @@ public:
|
||||
ret = EncodeSecret(key);
|
||||
return true;
|
||||
}
|
||||
bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override
|
||||
{
|
||||
if (priv) return ToPrivateString(arg, ret);
|
||||
ret = ToString();
|
||||
return true;
|
||||
}
|
||||
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||
{
|
||||
return arg.GetKey(m_pubkey.GetID(), key);
|
||||
@@ -386,6 +410,56 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override
|
||||
{
|
||||
// For hardened derivation type, just return the typical string, nothing to normalize
|
||||
if (m_derive == DeriveType::HARDENED) {
|
||||
if (priv) return ToPrivateString(arg, out);
|
||||
out = ToString();
|
||||
return true;
|
||||
}
|
||||
// Step backwards to find the last hardened step in the path
|
||||
int i = (int)m_path.size() - 1;
|
||||
for (; i >= 0; --i) {
|
||||
if (m_path.at(i) >> 31) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Either no derivation or all unhardened derivation
|
||||
if (i == -1) {
|
||||
if (priv) return ToPrivateString(arg, out);
|
||||
out = ToString();
|
||||
return true;
|
||||
}
|
||||
// Derive the xpub at the last hardened step
|
||||
CExtKey xprv;
|
||||
if (!GetExtKey(arg, xprv)) return false;
|
||||
KeyOriginInfo origin;
|
||||
int k = 0;
|
||||
for (; k <= i; ++k) {
|
||||
// Derive
|
||||
xprv.Derive(xprv, m_path.at(k));
|
||||
// Add to the path
|
||||
origin.path.push_back(m_path.at(k));
|
||||
// First derivation element, get the fingerprint for origin
|
||||
if (k == 0) {
|
||||
std::copy(xprv.vchFingerprint, xprv.vchFingerprint + 4, origin.fingerprint);
|
||||
}
|
||||
}
|
||||
// Build the remaining path
|
||||
KeyPath end_path;
|
||||
for (; k < (int)m_path.size(); ++k) {
|
||||
end_path.push_back(m_path.at(k));
|
||||
}
|
||||
// Build the string
|
||||
std::string origin_str = HexStr(origin.fingerprint) + FormatHDKeypath(origin.path);
|
||||
out = "[" + origin_str + "]" + (priv ? EncodeExtKey(xprv) : EncodeExtPubKey(xprv.Neuter())) + FormatHDKeypath(end_path);
|
||||
if (IsRange()) {
|
||||
out += "/*";
|
||||
assert(m_derive == DeriveType::UNHARDENED);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||
{
|
||||
CExtKey extkey;
|
||||
@@ -449,7 +523,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv) const
|
||||
bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv, bool normalized) const
|
||||
{
|
||||
std::string extra = ToStringExtra();
|
||||
size_t pos = extra.size() > 0 ? 1 : 0;
|
||||
@@ -457,7 +531,9 @@ public:
|
||||
for (const auto& pubkey : m_pubkey_args) {
|
||||
if (pos++) ret += ",";
|
||||
std::string tmp;
|
||||
if (priv) {
|
||||
if (normalized) {
|
||||
if (!pubkey->ToNormalizedString(*arg, tmp, priv)) return false;
|
||||
} else if (priv) {
|
||||
if (!pubkey->ToPrivateString(*arg, tmp)) return false;
|
||||
} else {
|
||||
tmp = pubkey->ToString();
|
||||
@@ -467,7 +543,7 @@ public:
|
||||
if (m_subdescriptor_arg) {
|
||||
if (pos++) ret += ",";
|
||||
std::string tmp;
|
||||
if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv)) return false;
|
||||
if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv, normalized)) return false;
|
||||
ret += std::move(tmp);
|
||||
}
|
||||
out = std::move(ret) + ")";
|
||||
@@ -477,13 +553,20 @@ public:
|
||||
std::string ToString() const final
|
||||
{
|
||||
std::string ret;
|
||||
ToStringHelper(nullptr, ret, false);
|
||||
ToStringHelper(nullptr, ret, false, false);
|
||||
return AddChecksum(ret);
|
||||
}
|
||||
|
||||
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final
|
||||
{
|
||||
bool ret = ToStringHelper(&arg, out, true);
|
||||
bool ret = ToStringHelper(&arg, out, true, false);
|
||||
out = AddChecksum(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override final
|
||||
{
|
||||
bool ret = ToStringHelper(&arg, out, priv, true);
|
||||
out = AddChecksum(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -93,6 +93,9 @@ struct Descriptor {
|
||||
/** Convert the descriptor to a private string. This fails if the provided provider does not have the relevant private keys. */
|
||||
virtual bool ToPrivateString(const SigningProvider& provider, std::string& out) const = 0;
|
||||
|
||||
/** Convert the descriptor to a normalized string. Normalized descriptors have the xpub at the last hardened step. This fails if the provided provider does not have the private keys to derive that xpub. */
|
||||
virtual bool ToNormalizedString(const SigningProvider& provider, std::string& out, bool priv) const = 0;
|
||||
|
||||
/** Expand a descriptor at a specified position.
|
||||
*
|
||||
* @param[in] pos The position at which to expand the descriptor. If IsRange() is false, this is ignored.
|
||||
|
||||
Reference in New Issue
Block a user