descriptors: add HavePrivateKeys()

Previously, to determine if a desc is watchonly, `ToPrivateString()`, was used.
It returns `false` if there is at least one pubkey in the descriptor for which
the provider  does not have a private key.

ToPrivateString() behaviour will change in the following commits to only
return `false` if no priv keys could be found for the pub keys in the descriptor.

HavePrivateKeys() is added here to replace the use of ToPrivateString() for determining
if a descriptor is 'watchonly'.

Co-authored-by: rkrux <rkrux.connect@gmail.com>
This commit is contained in:
Novo
2025-07-22 07:46:06 +01:00
parent 509dc91db1
commit e842eb90bb
4 changed files with 31 additions and 1 deletions

View File

@@ -835,6 +835,25 @@ public:
return true;
}
// NOLINTNEXTLINE(misc-no-recursion)
bool HavePrivateKeys(const SigningProvider& arg) const override
{
if (m_pubkey_args.empty() && m_subdescriptor_args.empty()) return false;
for (const auto& sub: m_subdescriptor_args) {
if (!sub->HavePrivateKeys(arg)) return false;
}
FlatSigningProvider tmp_provider;
for (const auto& pubkey : m_pubkey_args) {
tmp_provider.keys.clear();
pubkey->GetPrivKey(0, arg, tmp_provider);
if (tmp_provider.keys.empty()) return false;
}
return true;
}
// NOLINTNEXTLINE(misc-no-recursion)
bool IsRange() const final
{

View File

@@ -111,6 +111,13 @@ struct Descriptor {
/** Whether this descriptor will return one scriptPubKey or multiple (aka is or is not combo) */
virtual bool IsSingleType() const = 0;
/** Whether the given provider has all private keys required by this descriptor.
* @return `false` if the descriptor doesn't have any keys or subdescriptors,
* or if the provider does not have all private keys required by
* the descriptor.
*/
virtual bool HavePrivateKeys(const SigningProvider& provider) const = 0;
/** 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;

View File

@@ -49,7 +49,7 @@ constexpr int SIGNABLE = 1 << 3; // We can sign with this descriptor (this is no
constexpr int DERIVE_HARDENED = 1 << 4; // The final derivation is hardened, i.e. ends with *' or *h
constexpr int MIXED_PUBKEYS = 1 << 5;
constexpr int XONLY_KEYS = 1 << 6; // X-only pubkeys are in use (and thus inferring/caching may swap parity of pubkeys/keyids)
constexpr int MISSING_PRIVKEYS = 1 << 7; // Not all private keys are available, so ToPrivateString will fail.
constexpr int MISSING_PRIVKEYS = 1 << 7; // Not all private keys are available. ToPrivateString() will fail and HavePrivateKeys() will return `false`.
constexpr int SIGNABLE_FAILS = 1 << 8; // We can sign with this descriptor, but actually trying to sign will fail
constexpr int MUSIG = 1 << 9; // This is a MuSig so key counts will have an extra key
constexpr int MUSIG_DERIVATION = 1 << 10; // MuSig with BIP 328 derivation from the aggregate key
@@ -243,6 +243,9 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int
} else {
BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv);
}
BOOST_CHECK(!parse_priv->HavePrivateKeys(keys_pub));
BOOST_CHECK(parse_pub->HavePrivateKeys(keys_priv));
BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1));
BOOST_CHECK(parse_pub->ToPrivateString(keys_priv, prv1));
if (expected_prv) {

View File

@@ -26,6 +26,7 @@ public:
bool IsRange() const override { return false; }
bool IsSolvable() const override { return false; }
bool IsSingleType() const override { return true; }
bool HavePrivateKeys(const SigningProvider&) const override { return false; }
bool ToPrivateString(const SigningProvider& provider, std::string& out) const override { return false; }
bool ToNormalizedString(const SigningProvider& provider, std::string& out, const DescriptorCache* cache = nullptr) const override { return false; }
bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const override { return false; };