mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-13 11:35:20 +01:00
Merge 53ebaf66b2
into a50af6e4c4
This commit is contained in:
commit
f104ded83b
4 changed files with 238 additions and 21 deletions
|
@ -91,6 +91,8 @@ Descriptors consist of several types of expressions. The top level expression is
|
|||
- `addr(ADDR)` (top level only): the script which ADDR expands to.
|
||||
- `raw(HEX)` (top level only): the script whose hex encoding is HEX.
|
||||
- `rawtr(KEY)` (top level only): P2TR output with the specified key as output key. NOTE: while it's possible to use this to construct wallets, it has several downsides, like being unable to prove no hidden script path exists. Use at your own risk.
|
||||
- `rawnode(HEX)` (inside `tr` only): specify tree or branch merkle root in HEX encoding. NOTE: this hides script paths which are only revealed after spending. Use at your own risk
|
||||
- `rawleaf(HEX[,LEAF_VERSION_HEX])`(inside `tr` only): specify raw leaf script and leaf version in HEX encoding. `LEAF_VERSION_HEX` defaults to `c0`
|
||||
|
||||
`KEY` expressions:
|
||||
- Optionally, key origin information, consisting of:
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <hash.h>
|
||||
#include <key_io.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/miniscript.h>
|
||||
#include <script/parsing.h>
|
||||
#include <script/script.h>
|
||||
|
@ -15,6 +16,7 @@
|
|||
#include <uint256.h>
|
||||
|
||||
#include <common/args.h>
|
||||
#include <crypto/common.h>
|
||||
#include <span.h>
|
||||
#include <util/bip32.h>
|
||||
#include <util/check.h>
|
||||
|
@ -1156,17 +1158,41 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/** Represents the type of a node in taproot descriptor script tree */
|
||||
enum TaprootNodeType {
|
||||
LEAF_SCRIPT,
|
||||
NODE_HASH
|
||||
};
|
||||
|
||||
/** A struct hold information on a node taproot descriptor script tree */
|
||||
struct TaprootNode {
|
||||
int m_depth;
|
||||
uint8_t m_leaf_version;
|
||||
TaprootNodeType m_type;
|
||||
TaprootNode(
|
||||
int depth,
|
||||
uint8_t leaf_version,
|
||||
TaprootNodeType type = TaprootNodeType::LEAF_SCRIPT) : m_depth(depth), m_leaf_version(leaf_version), m_type(type) {}
|
||||
};
|
||||
|
||||
/** A parsed tr(...) descriptor. */
|
||||
class TRDescriptor final : public DescriptorImpl
|
||||
{
|
||||
std::vector<int> m_depths;
|
||||
std::vector<TaprootNode> m_nodes;
|
||||
|
||||
protected:
|
||||
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override
|
||||
{
|
||||
TaprootBuilder builder;
|
||||
assert(m_depths.size() == scripts.size());
|
||||
for (size_t pos = 0; pos < m_depths.size(); ++pos) {
|
||||
builder.Add(m_depths[pos], scripts[pos], TAPROOT_LEAF_TAPSCRIPT);
|
||||
assert(m_nodes.size() == scripts.size());
|
||||
for (size_t pos = 0; pos < m_nodes.size(); ++pos) {
|
||||
if (m_nodes[pos].m_type == TaprootNodeType::NODE_HASH) {
|
||||
builder.AddOmitted(m_nodes[pos].m_depth, uint256(Span(scripts[pos])));
|
||||
} else if (m_nodes[pos].m_type == TaprootNodeType::LEAF_SCRIPT) {
|
||||
builder.Add(m_nodes[pos].m_depth, scripts[pos], m_nodes[pos].m_leaf_version);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
if (!builder.IsComplete()) return {};
|
||||
assert(keys.size() == 1);
|
||||
|
@ -1180,11 +1206,11 @@ protected:
|
|||
}
|
||||
bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const override
|
||||
{
|
||||
if (m_depths.empty()) return true;
|
||||
if (m_nodes.empty()) return true;
|
||||
std::vector<bool> path;
|
||||
for (size_t pos = 0; pos < m_depths.size(); ++pos) {
|
||||
for (size_t pos = 0; pos < m_nodes.size(); ++pos) {
|
||||
if (pos) ret += ',';
|
||||
while ((int)path.size() <= m_depths[pos]) {
|
||||
while ((int)path.size() <= m_nodes[pos].m_depth) {
|
||||
if (path.size()) ret += '{';
|
||||
path.push_back(false);
|
||||
}
|
||||
|
@ -1200,10 +1226,9 @@ protected:
|
|||
return true;
|
||||
}
|
||||
public:
|
||||
TRDescriptor(std::unique_ptr<PubkeyProvider> internal_key, std::vector<std::unique_ptr<DescriptorImpl>> descs, std::vector<int> depths) :
|
||||
DescriptorImpl(Vector(std::move(internal_key)), std::move(descs), "tr"), m_depths(std::move(depths))
|
||||
TRDescriptor(std::unique_ptr<PubkeyProvider> internal_key, std::vector<std::unique_ptr<DescriptorImpl>> descs, std::vector<TaprootNode> nodes) : DescriptorImpl(Vector(std::move(internal_key)), std::move(descs), "tr"), m_nodes(std::move(nodes))
|
||||
{
|
||||
assert(m_subdescriptor_args.size() == m_depths.size());
|
||||
assert(m_subdescriptor_args.size() == m_nodes.size());
|
||||
}
|
||||
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; }
|
||||
bool IsSingleType() const final { return true; }
|
||||
|
@ -1225,7 +1250,7 @@ public:
|
|||
std::vector<std::unique_ptr<DescriptorImpl>> subdescs;
|
||||
subdescs.reserve(m_subdescriptor_args.size());
|
||||
std::transform(m_subdescriptor_args.begin(), m_subdescriptor_args.end(), subdescs.begin(), [](const std::unique_ptr<DescriptorImpl>& d) { return d->Clone(); });
|
||||
return std::make_unique<TRDescriptor>(m_pubkey_args.at(0)->Clone(), std::move(subdescs), m_depths);
|
||||
return std::make_unique<TRDescriptor>(m_pubkey_args.at(0)->Clone(), std::move(subdescs), m_nodes);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1392,6 +1417,64 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/** A parsed rawnode(...) descriptor */
|
||||
class RawNodeDescriptor final : public DescriptorImpl
|
||||
{
|
||||
std::vector<unsigned char> m_bytes;
|
||||
|
||||
protected:
|
||||
std::string ToStringExtra() const override { return HexStr(m_bytes); }
|
||||
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(CScript(m_bytes.begin(), m_bytes.end())); }
|
||||
|
||||
public:
|
||||
RawNodeDescriptor(std::vector<unsigned char> bytes) : DescriptorImpl({}, "rawnode"), m_bytes(std::move(bytes)) {}
|
||||
|
||||
bool IsSolvable() const final { return false; }
|
||||
|
||||
bool IsSingleType() const final { return true; }
|
||||
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; }
|
||||
|
||||
std::optional<int64_t> ScriptSize() const override { return m_bytes.size(); }
|
||||
|
||||
std::unique_ptr<DescriptorImpl> Clone() const override
|
||||
{
|
||||
return std::make_unique<RawNodeDescriptor>(m_bytes);
|
||||
}
|
||||
};
|
||||
|
||||
/** A parsed rawleaf(...) descriptor */
|
||||
class RawLeafDescriptor final : public DescriptorImpl
|
||||
{
|
||||
CScript m_leaf_script;
|
||||
uint8_t m_leaf_version;
|
||||
|
||||
protected:
|
||||
std::string ToStringExtra() const override
|
||||
{
|
||||
return strprintf("%s,%x", HexStr(m_leaf_script), m_leaf_version);
|
||||
}
|
||||
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(m_leaf_script); }
|
||||
|
||||
public:
|
||||
RawLeafDescriptor(CScript leaf_script, uint8_t leaf_version) : DescriptorImpl({}, "rawleaf"), m_leaf_script(leaf_script), m_leaf_version(leaf_version) {}
|
||||
|
||||
bool IsSolvable() const final { return false; }
|
||||
|
||||
bool IsSingleType() const final { return true; }
|
||||
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; }
|
||||
|
||||
std::optional<int64_t> ScriptSize() const override { return m_leaf_script.size(); }
|
||||
|
||||
uint8_t GetLeafVersion() const { return m_leaf_version; }
|
||||
|
||||
void SetLeafVersion(uint8_t version) { m_leaf_version = version; }
|
||||
|
||||
std::unique_ptr<DescriptorImpl> Clone() const override
|
||||
{
|
||||
return std::make_unique<RawLeafDescriptor>(m_leaf_script, m_leaf_version);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Parser //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1977,6 +2060,7 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
|
|||
if (subscripts.back().empty()) return {};
|
||||
max_providers_len = std::max(max_providers_len, subscripts.back().size());
|
||||
depths.push_back(branches.size());
|
||||
|
||||
// Process closing braces; one is expected for every right branch we were in.
|
||||
while (branches.size() && branches.back()) {
|
||||
if (!Const("}", expr)) {
|
||||
|
@ -2028,11 +2112,22 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
|
|||
for (size_t i = 0; i < max_providers_len; ++i) {
|
||||
// Build final subscripts vectors by retrieving the i'th subscript for each vector in subscripts
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> this_subs;
|
||||
std::vector<TaprootNode> this_nodes;
|
||||
this_subs.reserve(subscripts.size());
|
||||
for (auto& subs : subscripts) {
|
||||
this_subs.emplace_back(std::move(subs.at(i)));
|
||||
this_nodes.reserve(subscripts.size());
|
||||
for (size_t pos = 0; pos < subscripts.size(); pos++) {
|
||||
this_subs.emplace_back(std::move(subscripts[pos].at(i)));
|
||||
auto leaf_version{TAPROOT_LEAF_TAPSCRIPT};
|
||||
auto type{TaprootNodeType::LEAF_SCRIPT};
|
||||
if (dynamic_cast<RawNodeDescriptor*>(this_subs.back().get())) {
|
||||
type = TaprootNodeType::NODE_HASH;
|
||||
}
|
||||
if (auto rawleaf = dynamic_cast<RawLeafDescriptor*>(this_subs.back().get())) {
|
||||
leaf_version = rawleaf->GetLeafVersion();
|
||||
}
|
||||
this_nodes.emplace_back(depths[pos], leaf_version, type);
|
||||
}
|
||||
ret.emplace_back(std::make_unique<TRDescriptor>(std::move(internal_keys.at(i)), std::move(this_subs), depths));
|
||||
ret.emplace_back(std::make_unique<TRDescriptor>(std::move(internal_keys.at(i)), std::move(this_subs), std::move(this_nodes)));
|
||||
}
|
||||
return ret;
|
||||
|
||||
|
@ -2074,6 +2169,66 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
|
|||
error = "Can only have raw() at top level";
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::P2TR && Func("rawnode", expr)) {
|
||||
std::string str(expr.begin(), expr.end());
|
||||
auto bytes = TryParseHex<uint8_t>(str);
|
||||
if (!bytes.has_value()) {
|
||||
error = "Rawnode hash is not hex";
|
||||
return {};
|
||||
}
|
||||
if (bytes->size() != 32) {
|
||||
error = "256 bits digest expected";
|
||||
return {};
|
||||
}
|
||||
ret.emplace_back(std::make_unique<RawNodeDescriptor>(bytes.value()));
|
||||
return ret;
|
||||
} else if (Func("rawnode", expr)) {
|
||||
error = "Can only have rawnode() inside tr()";
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::P2TR && Func("rawleaf", expr)) {
|
||||
auto arg1 = Expr(expr);
|
||||
std::string leaf_script_str(arg1.begin(), arg1.end());
|
||||
if (!IsHex(leaf_script_str)) {
|
||||
error = "Leaf Script is not hex";
|
||||
return {};
|
||||
}
|
||||
auto leaf_script_bytes = ParseHex(leaf_script_str);
|
||||
CScript leaf_script(leaf_script_bytes.begin(), leaf_script_bytes.end());
|
||||
|
||||
if (!Const(",", expr)) {
|
||||
// Leaf version not specified, return early
|
||||
ret.emplace_back(std::make_unique<RawLeafDescriptor>(leaf_script, TAPROOT_LEAF_TAPSCRIPT));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read and process leaf version
|
||||
auto arg2 = Expr(expr);
|
||||
std::string leaf_version_str(arg2.begin(), arg2.end());
|
||||
auto leaf_version_hex_vec = TryParseHex<uint8_t>(leaf_version_str);
|
||||
if (!leaf_version_hex_vec.has_value()) {
|
||||
error = "Leaf Version is not hex";
|
||||
return {};
|
||||
}
|
||||
if (leaf_version_hex_vec->size() > 1) {
|
||||
error = "Leaf Version is too large";
|
||||
return {};
|
||||
}
|
||||
if (leaf_version_hex_vec->size() == 0) {
|
||||
error = "Expected Leaf Version but not provided";
|
||||
return {};
|
||||
}
|
||||
uint8_t leaf_version = (*leaf_version_hex_vec)[0];
|
||||
if ((leaf_version & ~TAPROOT_LEAF_MASK) == 1) {
|
||||
error = "Leaf Version is invalid";
|
||||
return {};
|
||||
}
|
||||
ret.emplace_back(std::make_unique<RawLeafDescriptor>(leaf_script, leaf_version));
|
||||
return ret;
|
||||
} else if (Func("rawleaf", expr)) {
|
||||
error = "Can only have rawleaf() inside tr()";
|
||||
return {};
|
||||
}
|
||||
// Process miniscript expressions.
|
||||
{
|
||||
const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT};
|
||||
|
@ -2260,25 +2415,38 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
|
|||
// If that works, try to infer subdescriptors for all leaves.
|
||||
bool ok = true;
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions
|
||||
std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
|
||||
std::vector<TaprootNode> nodes;
|
||||
for (const auto& [depth, script, leaf_ver] : *tree) {
|
||||
std::unique_ptr<DescriptorImpl> subdesc;
|
||||
if (leaf_ver == TAPROOT_LEAF_TAPSCRIPT) {
|
||||
subdesc = InferScript(CScript(script.begin(), script.end()), ParseScriptContext::P2TR, provider);
|
||||
|
||||
subdesc = InferScript(CScript(script.begin(), script.end()), ParseScriptContext::P2TR, provider);
|
||||
if (auto rawleaf = dynamic_cast<RawLeafDescriptor*>(subdesc.get())) {
|
||||
rawleaf->SetLeafVersion(leaf_ver);
|
||||
}
|
||||
if (!subdesc) {
|
||||
ok = false;
|
||||
break;
|
||||
} else {
|
||||
subscripts.push_back(std::move(subdesc));
|
||||
depths.push_back(depth);
|
||||
nodes.emplace_back(depth, leaf_ver);
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
auto key = InferXOnlyPubkey(tap.internal_key, ParseScriptContext::P2TR, provider);
|
||||
return std::make_unique<TRDescriptor>(std::move(key), std::move(subscripts), std::move(depths));
|
||||
return std::make_unique<TRDescriptor>(std::move(key), std::move(subscripts), std::move(nodes));
|
||||
}
|
||||
}
|
||||
// If the tree is empty but it has a merkle root, infer the merkle root as a rawnode()
|
||||
if (!tap.merkle_root.IsNull()) {
|
||||
auto key = InferXOnlyPubkey(tap.internal_key, ParseScriptContext::P2TR, provider);
|
||||
std::vector<unsigned char> merkle_root_bytes;
|
||||
std::copy(tap.merkle_root.begin(), tap.merkle_root.end(), std::back_inserter(merkle_root_bytes));
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> descs;
|
||||
descs.push_back(std::make_unique<RawNodeDescriptor>(merkle_root_bytes));
|
||||
std::vector<TaprootNode> nodes;
|
||||
nodes.emplace_back(0, 0, TaprootNodeType::NODE_HASH);
|
||||
return std::make_unique<TRDescriptor>(std::move(key), std::move(descs), std::move(nodes));
|
||||
}
|
||||
}
|
||||
// If the above doesn't work, construct a rawtr() descriptor with just the encoded x-only pubkey.
|
||||
if (pubkey.IsFullyValid()) {
|
||||
|
@ -2303,6 +2471,10 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
|
|||
}
|
||||
}
|
||||
|
||||
if (ctx == ParseScriptContext::P2TR) {
|
||||
return std::make_unique<RawLeafDescriptor>(script, 0);
|
||||
}
|
||||
|
||||
// The following descriptors are all top-level only descriptors.
|
||||
// So if we are not at the top level, return early.
|
||||
if (ctx != ParseScriptContext::TOP) return nullptr;
|
||||
|
|
|
@ -1059,6 +1059,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
|
|||
Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE | SIGNABLE_FAILS, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M);
|
||||
Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/42, /*preimages=*/{{"ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"_hex_v_u8, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"_hex_v_u8}});
|
||||
|
||||
// Can have rawnode under tr()
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b))", XONLY_KEYS | UNSOLVABLE, {{"512085d8d2f800c2fdf8b671f16c3ec75aa6e134aa6ae98c08b5351408abef957452"}}, OutputType::BECH32M);
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,{rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b),rawnode(fe59e853cc46a633e556950a65a47b61098b669775b876e517339076a4970c23)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b),rawnode(fe59e853cc46a633e556950a65a47b61098b669775b876e517339076a4970c23)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b),rawnode(fe59e853cc46a633e556950a65a47b61098b669775b876e517339076a4970c23)})", XONLY_KEYS | UNSOLVABLE, {{"5120117ee4eb20c920ef1f1c088536903624997626423c9ce6737c2d0476f8ca27e3"}}, OutputType::BECH32M);
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,{rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b),pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b),pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b),pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)})", XONLY_KEYS | UNSOLVABLE, {{"512013ed7d909ab69e6581906f54cb9e6f564fbd102d3a2f6e2bbeb2824bb9a68344"}}, OutputType::BECH32M);
|
||||
|
||||
CheckUnparsable("rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b)", "rawnode(e960f9fcfb646b5a6eb3a091d9270497738f7bcd99c2dda549acc699f02b043b)", "Can only have rawnode() inside tr()");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawnode(e9))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawnode(e9))", "256 bits digest expected");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawnode(e9))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawnode(zzzz))", "Rawnode hash is not hex");
|
||||
|
||||
// Can have a rawleaf under tr()
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,c0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,c0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,c0))", XONLY_KEYS | UNSOLVABLE, {{"51209eff65f093c70a94a43bbf6fe508fa557dc1b81551759d3fb15e09b521a42a2f"}}, OutputType::BECH32M);
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,cc))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,cc))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,cc))", XONLY_KEYS | UNSOLVABLE, {{"5120ed4f8863f232246c6bb9c6e8877a3f586ea9c52203b35658e5a15776ce60aaa6"}}, OutputType::BECH32M);
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,66))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,66))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,66))", XONLY_KEYS | UNSOLVABLE, {{"5120ab7116360411e36c55ba4f6e7a47a26b4725f55b87c31dca5ecf2b7b5d9dc294"}}, OutputType::BECH32M);
|
||||
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,{rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,c0),pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,c0),pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac,c0),pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)})", XONLY_KEYS | UNSOLVABLE, {{"51203ae76d659be52428698fc98f897b9c492c413e8cadc8115038ba0684b2050dea"}}, OutputType::BECH32M);
|
||||
|
||||
CheckUnparsable("rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac)", "rawleaf(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac)", "Can only have rawleaf() inside tr()");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(z,c0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(z,c0))", "Leaf Script is not hex");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(a0,ca00))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(a0,ca00))", "Leaf Version is too large");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(a0,z0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(a0,z0))", "Leaf Version is not hex");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(a0,))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(a0,))", "Expected Leaf Version but not provided");
|
||||
// Check leaf version constraints
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(a0,z0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(a0,bf))", "Leaf Version is invalid");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(a0,z0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(a0,c1))", "Leaf Version is invalid");
|
||||
CheckUnparsable("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,rawleaf(a0,z0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,rawleaf(a0,ff))", "Leaf Version is invalid");
|
||||
|
||||
// Basic sh(pkh()) with key origin
|
||||
CheckInferDescriptor("a9141a31ad23bf49c247dd531a623c2ef57da3c400c587", "sh(pkh([deadbeef/0h/0h/0]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", {"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}, {{"03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd", "deadbeef/0h/0h/0"}});
|
||||
// p2pk script with hybrid key must infer as raw()
|
||||
|
|
|
@ -55,6 +55,14 @@ DESCS = [
|
|||
f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}},{P2WSH_MINISCRIPTS[2].replace('multi', 'multi_a')}}})",
|
||||
# A Taproot with all above scripts in its tree.
|
||||
f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}},{{{P2WSH_MINISCRIPTS[2].replace('multi', 'multi_a')},{P2WSH_MINISCRIPTS[3]}}}}})",
|
||||
# A Taproot with rawnode as root in its tree
|
||||
f"tr({TPUBS[0]}/*,rawnode(8a62dc0a100c4156cc2a4b7c2a97747ce0dfe90562673fd662678aaae93121fb))",
|
||||
# A Taproot with rawnode and multipath descriptor
|
||||
f"tr({TPUBS[0]}/*,{{rawnode(8a62dc0a100c4156cc2a4b7c2a97747ce0dfe90562673fd662678aaae93121fb),pk({TPUBS[0]}/<1;2>/*)}})",
|
||||
# A Taproot with rawnode as branch root in its tree
|
||||
f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{P2WSH_MINISCRIPTS[0]},rawnode(8a62dc0a100c4156cc2a4b7c2a97747ce0dfe90562673fd662678aaae93121fb)}})",
|
||||
# A Taproot with rawleaf in its tree
|
||||
f"tr({TPUBS[0]}/*,rawleaf(aa2061e33e9dbfefc45f6a194187684d278f789fd4d5e207a357e79971b6519a8b128821{PUBKEYS[0]}ac))"
|
||||
]
|
||||
|
||||
DESCS_PRIV = [
|
||||
|
@ -199,6 +207,14 @@ DESCS_PRIV = [
|
|||
"sigs_count": 2,
|
||||
"stack_size": 8,
|
||||
},
|
||||
# Taproot with a rawnode in the tree
|
||||
{
|
||||
"desc": f"tr({TPUBS[0]}/*,{{pk({TPRVS[0]}/*),rawnode(8a62dc0a100c4156cc2a4b7c2a97747ce0dfe90562673fd662678aaae93121fb)}})",
|
||||
"sequence": None,
|
||||
"locktime": None,
|
||||
"sigs_count": 1,
|
||||
"stack_size": 3,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
@ -231,13 +247,15 @@ class WalletMiniscriptTest(BitcoinTestFramework):
|
|||
|
||||
self.log.info("Testing we derive new addresses for it")
|
||||
addr_type = "bech32m" if desc.startswith("tr(") else "bech32"
|
||||
derived = [self.funder.deriveaddresses(desc, 0), self.funder.deriveaddresses(desc, 1)]
|
||||
is_multipath = desc.count("<1;2>") > 0
|
||||
assert_equal(
|
||||
self.ms_wo_wallet.getnewaddress(address_type=addr_type),
|
||||
self.funder.deriveaddresses(desc, 0)[0],
|
||||
derived[0][0][0] if is_multipath else derived[0][0],
|
||||
)
|
||||
assert_equal(
|
||||
self.ms_wo_wallet.getnewaddress(address_type=addr_type),
|
||||
self.funder.deriveaddresses(desc, 1)[1],
|
||||
derived[1][0][1] if is_multipath else derived[1][1],
|
||||
)
|
||||
|
||||
self.log.info("Testing we detect funds sent to one of them")
|
||||
|
|
Loading…
Add table
Reference in a new issue