Make DescriptorImpl support multiple subscripts

So far, no descriptor exists that supports more than one sub-script
descriptor. This will change with taproot, so prepare for this by
changing the m_subdescriptor_arg from a unique_ptr to a vector of
unique_ptr's.
pull/826/head
Pieter Wuille 4 years ago
parent a917478db0
commit 4441c6f3c0

@ -481,11 +481,11 @@ class DescriptorImpl : public Descriptor
const std::string m_name;
protected:
//! The sub-descriptor argument (nullptr for everything but SH and WSH).
//! The sub-descriptor arguments (empty for everything but SH and WSH).
//! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
//! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
//! Subdescriptors can only ever generate a single script.
const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg;
const std::vector<std::unique_ptr<DescriptorImpl>> m_subdescriptor_args;
//! Return a serialization of anything except pubkey and script arguments, to be prepended to those.
virtual std::string ToStringExtra() const { return ""; }
@ -493,22 +493,23 @@ protected:
/** A helper function to construct the scripts for this descriptor.
*
* This function is invoked once by ExpandHelper.
*
* @param pubkeys The evaluations of the m_pubkey_args field.
* @param script The evaluation of m_subdescriptor_arg (or nullptr when m_subdescriptor_arg is nullptr).
* @param script The evaluations of m_subdescriptor_args (one for each m_subdescriptor_args element).
* @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
* The origin info of the provided pubkeys is automatically added.
* @return A vector with scriptPubKeys for this descriptor.
*/
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, Span<const CScript> scripts, FlatSigningProvider& out) const = 0;
public:
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_arg(std::move(script)) {}
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args() {}
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {}
bool IsSolvable() const override
{
if (m_subdescriptor_arg) {
if (!m_subdescriptor_arg->IsSolvable()) return false;
for (const auto& arg : m_subdescriptor_args) {
if (!arg->IsSolvable()) return false;
}
return true;
}
@ -518,8 +519,8 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pubkey->IsRange()) return true;
}
if (m_subdescriptor_arg) {
if (m_subdescriptor_arg->IsRange()) return true;
for (const auto& arg : m_subdescriptor_args) {
if (arg->IsRange()) return true;
}
return false;
}
@ -541,10 +542,10 @@ public:
}
ret += std::move(tmp);
}
if (m_subdescriptor_arg) {
for (const auto& scriptarg : m_subdescriptor_args) {
if (pos++) ret += ",";
std::string tmp;
if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv, normalized)) return false;
if (!scriptarg->ToStringHelper(arg, tmp, priv, normalized)) return false;
ret += std::move(tmp);
}
out = std::move(ret) + ")";
@ -577,18 +578,20 @@ public:
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
entries.reserve(m_pubkey_args.size());
// Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
// Construct temporary data in `entries`, `subscripts`, and `subprovider` to avoid producing output in case of failure.
for (const auto& p : m_pubkey_args) {
entries.emplace_back();
if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second, read_cache, write_cache)) return false;
}
std::vector<CScript> subscripts;
if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
if (!m_subdescriptor_arg->ExpandHelper(pos, arg, read_cache, subscripts, subprovider, write_cache)) return false;
assert(subscripts.size() == 1);
out = Merge(out, subprovider);
FlatSigningProvider subprovider;
for (const auto& subarg : m_subdescriptor_args) {
std::vector<CScript> outscripts;
if (!subarg->ExpandHelper(pos, arg, read_cache, outscripts, subprovider, write_cache)) return false;
assert(outscripts.size() == 1);
subscripts.emplace_back(std::move(outscripts[0]));
}
out = Merge(std::move(out), std::move(subprovider));
std::vector<CPubKey> pubkeys;
pubkeys.reserve(entries.size());
@ -596,11 +599,8 @@ public:
pubkeys.push_back(entry.first);
out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
if (m_subdescriptor_arg) {
output_scripts = MakeScripts(pubkeys, &subscripts[0], out);
} else {
output_scripts = MakeScripts(pubkeys, nullptr, out);
}
output_scripts = MakeScripts(pubkeys, MakeSpan(subscripts), out);
return true;
}
@ -621,10 +621,8 @@ public:
if (!p->GetPrivKey(pos, provider, key)) continue;
out.keys.emplace(key.GetPubKey().GetID(), key);
}
if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider);
out = Merge(out, subprovider);
for (const auto& arg : m_subdescriptor_args) {
arg->ExpandPrivate(pos, provider, out);
}
}
@ -637,9 +635,9 @@ class AddressDescriptor final : public DescriptorImpl
const CTxDestination m_destination;
protected:
std::string ToStringExtra() const override { return EncodeDestination(m_destination); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); }
public:
AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, "addr"), m_destination(std::move(destination)) {}
bool IsSolvable() const final { return false; }
std::optional<OutputType> GetOutputType() const override
@ -663,9 +661,9 @@ class RawDescriptor final : public DescriptorImpl
const CScript m_script;
protected:
std::string ToStringExtra() const override { return HexStr(m_script); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(m_script); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(m_script); }
public:
RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
RawDescriptor(CScript script) : DescriptorImpl({}, "raw"), m_script(std::move(script)) {}
bool IsSolvable() const final { return false; }
std::optional<OutputType> GetOutputType() const override
@ -689,9 +687,9 @@ public:
class PKDescriptor final : public DescriptorImpl
{
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); }
public:
PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pk") {}
PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pk") {}
bool IsSingleType() const final { return true; }
};
@ -699,14 +697,14 @@ public:
class PKHDescriptor final : public DescriptorImpl
{
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
return Vector(GetScriptForDestination(PKHash(id)));
}
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {}
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pkh") {}
std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
bool IsSingleType() const final { return true; }
};
@ -715,14 +713,14 @@ public:
class WPKHDescriptor final : public DescriptorImpl
{
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
return Vector(GetScriptForDestination(WitnessV0KeyHash(id)));
}
public:
WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {}
WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "wpkh") {}
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
bool IsSingleType() const final { return true; }
};
@ -731,7 +729,7 @@ public:
class ComboDescriptor final : public DescriptorImpl
{
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override
{
std::vector<CScript> ret;
CKeyID id = keys[0].GetID();
@ -747,7 +745,7 @@ protected:
return ret;
}
public:
ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "combo") {}
ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "combo") {}
bool IsSingleType() const final { return false; }
};
@ -758,7 +756,7 @@ class MultisigDescriptor final : public DescriptorImpl
const bool m_sorted;
protected:
std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override {
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override {
if (m_sorted) {
std::vector<CPubKey> sorted_keys(keys);
std::sort(sorted_keys.begin(), sorted_keys.end());
@ -767,7 +765,7 @@ protected:
return Vector(GetScriptForMultisig(m_threshold, keys));
}
public:
MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), {}, sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
bool IsSingleType() const final { return true; }
};
@ -775,10 +773,10 @@ public:
class SHDescriptor final : public DescriptorImpl
{
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider& out) const override
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript> scripts, FlatSigningProvider& out) const override
{
auto ret = Vector(GetScriptForDestination(ScriptHash(*script)));
if (ret.size()) out.scripts.emplace(CScriptID(*script), *script);
auto ret = Vector(GetScriptForDestination(ScriptHash(scripts[0])));
if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]);
return ret;
}
public:
@ -786,8 +784,8 @@ public:
std::optional<OutputType> GetOutputType() const override
{
assert(m_subdescriptor_arg);
if (m_subdescriptor_arg->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
assert(m_subdescriptor_args.size() == 1);
if (m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
return OutputType::LEGACY;
}
bool IsSingleType() const final { return true; }
@ -797,10 +795,10 @@ public:
class WSHDescriptor final : public DescriptorImpl
{
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider& out) const override
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript> scripts, FlatSigningProvider& out) const override
{
auto ret = Vector(GetScriptForDestination(WitnessV0ScriptHash(*script)));
if (ret.size()) out.scripts.emplace(CScriptID(*script), *script);
auto ret = Vector(GetScriptForDestination(WitnessV0ScriptHash(scripts[0])));
if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]);
return ret;
}
public:

Loading…
Cancel
Save