This would have caught it, at the expense of making Clone() public:
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 67770ada78..8a7447e666 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1037,5 +1037,7 @@ public:
}
- virtual std::unique_ptr<DescriptorImpl> Clone() const = 0;
+ std::unique_ptr<Descriptor> Clone() const final { return CloneImpl(); }
+
+ virtual std::unique_ptr<DescriptorImpl> CloneImpl() const = 0;
bool HasScripts() const override { return true; }
@@ -1105,5 +1107,5 @@ public:
std::optional<int64_t> ScriptSize() const override { return GetScriptForDestination(m_destination).size(); }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<AddressDescriptor>(m_destination);
@@ -1133,5 +1135,5 @@ public:
std::optional<int64_t> ScriptSize() const override { return m_script.size(); }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<RawDescriptor>(m_script);
@@ -1173,5 +1175,5 @@ public:
std::optional<int64_t> MaxSatisfactionElems() const override { return 1; }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<PKDescriptor>(m_pubkey_args.at(0)->Clone(), m_xonly);
@@ -1206,5 +1208,5 @@ public:
std::optional<int64_t> MaxSatisfactionElems() const override { return 2; }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<PKHDescriptor>(m_pubkey_args.at(0)->Clone());
@@ -1239,5 +1241,5 @@ public:
std::optional<int64_t> MaxSatisfactionElems() const override { return 2; }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<WPKHDescriptor>(m_pubkey_args.at(0)->Clone());
@@ -1266,5 +1268,5 @@ public:
ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "combo") {}
bool IsSingleType() const final { return false; }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<ComboDescriptor>(m_pubkey_args.at(0)->Clone());
@@ -1309,5 +1311,5 @@ public:
std::optional<int64_t> MaxSatisfactionElems() const override { return 1 + m_threshold; }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -1353,5 +1355,5 @@ public:
std::optional<int64_t> MaxSatisfactionElems() const override { return m_pubkey_args.size(); }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -1408,7 +1410,7 @@ public:
}
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
- return std::make_unique<SHDescriptor>(m_subdescriptor_args.at(0)->Clone());
+ return std::make_unique<SHDescriptor>(m_subdescriptor_args.at(0)->CloneImpl());
}
};
@@ -1449,7 +1451,7 @@ public:
}
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
- return std::make_unique<WSHDescriptor>(m_subdescriptor_args.at(0)->Clone());
+ return std::make_unique<WSHDescriptor>(m_subdescriptor_args.at(0)->CloneImpl());
}
};
@@ -1532,9 +1534,9 @@ public:
}
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
std::vector<std::unique_ptr<DescriptorImpl>> subdescs;
subdescs.reserve(m_subdescriptor_args.size());
- std::transform(m_subdescriptor_args.begin(), m_subdescriptor_args.end(), std::back_inserter(subdescs), [](const std::unique_ptr<DescriptorImpl>& d) { return d->Clone(); });
+ std::transform(m_subdescriptor_args.begin(), m_subdescriptor_args.end(), std::back_inserter(subdescs), [](const std::unique_ptr<DescriptorImpl>& d) { return d->CloneImpl(); });
return std::make_unique<TRDescriptor>(m_pubkey_args.at(0)->Clone(), std::move(subdescs), m_depths);
}
@@ -1695,5 +1697,5 @@ public:
}
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -1735,5 +1737,5 @@ public:
}
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<RawTRDescriptor>(m_pubkey_args.at(0)->Clone());
@@ -1751,5 +1753,5 @@ public:
bool HasScripts() const override { return false; }
- std::unique_ptr<DescriptorImpl> Clone() const override
+ std::unique_ptr<DescriptorImpl> CloneImpl() const override
{
return std::make_unique<UnusedDescriptor>(m_pubkey_args.at(0)->Clone());
@@ -2542,5 +2544,5 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
if (vec.size() == 1) {
for (size_t i = 1; i < max_providers_len; ++i) {
- vec.emplace_back(vec.at(0)->Clone());
+ vec.emplace_back(vec.at(0)->CloneImpl());
}
} else if (vec.size() != max_providers_len) {
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 0f1e799e8f..e987d33d01 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -109,4 +109,7 @@ struct Descriptor {
virtual std::string ToString(bool compat_format=false) const = 0;
+ /** Make a deep copy of this descriptor. */
+ virtual std::unique_ptr<Descriptor> Clone() const = 0;
+
/** Whether this descriptor will return at most one scriptPubKey or multiple (aka is or is not combo) */
virtual bool IsSingleType() const = 0;
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 347efd7744..3f5266e3f9 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -1369,4 +1369,13 @@ void CheckUnused(const std::string& prv, const std::string& pub)
BOOST_CHECK_MESSAGE(EqualDescriptor(pub, pub2), "Public ser: " + pub2 + " Public desc: " + pub);
+ // Verify clone.
+ for (const auto& desc : {parse_priv.get(), parse_pub.get()}) {
+ std::unique_ptr<Descriptor> clone = desc->Clone();
+ const std::string clone_str{clone->ToString()};
+ BOOST_CHECK_MESSAGE(EqualDescriptor(pub, clone_str), "Clone ser: " + clone_str + " Public desc: " + pub);
+ BOOST_CHECK(clone->GetOutputType() == std::nullopt);
+ BOOST_CHECK(!clone->HasScripts());
+ }
+
// Check both only have one pubkey
std::set<CPubKey> prv_pubkeys;
diff --git a/src/wallet/test/walletload_tests.cpp b/src/wallet/test/walletload_tests.cpp
index 8648af6b6c..9ed807fb85 100644
--- a/src/wallet/test/walletload_tests.cpp
+++ b/src/wallet/test/walletload_tests.cpp
@@ -23,4 +23,5 @@ public:
std::string ToString(bool compat_format) const override { return desc; }
+ std::unique_ptr<Descriptor> Clone() const override { return std::make_unique<DummyDescriptor>(desc); }
std::optional<OutputType> GetOutputType() const override { return OutputType::UNKNOWN; }