aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
authorAntoine Poinsot <darosior@protonmail.com>2023-01-21 19:27:05 +0100
committerAntoine Poinsot <darosior@protonmail.com>2023-10-08 02:43:22 +0200
commit8ff9489422009284967aeb9ff4232b135f5ddad8 (patch)
tree6af67723724788f9fb4af9995f526151edb7d65f /src/script
parent5e76f3f0ddbce513c2b626b5ab45242f931d5d60 (diff)
downloadbitcoin-8ff9489422009284967aeb9ff4232b135f5ddad8.tar.xz
descriptor: Tapscript-specific Miniscript key serialization / parsing
64-hex-characters public keys are valid in Miniscript key expressions within a Tapscript context. Keys under a Tapscript context always serialize as 32-bytes x-only public keys (and that's what get hashed by OP_HASH160 on the stack too).
Diffstat (limited to 'src/script')
-rw-r--r--src/script/descriptor.cpp52
-rw-r--r--src/script/miniscript.h3
2 files changed, 45 insertions, 10 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 22177504fc..104990abb7 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1114,16 +1114,33 @@ public:
class ScriptMaker {
//! Keys contained in the Miniscript (the evaluation of DescriptorImpl::m_pubkey_args).
const std::vector<CPubKey>& m_keys;
+ //! The script context we're operating within (Tapscript or P2WSH).
+ const miniscript::MiniscriptContext m_script_ctx;
+
+ //! Get the ripemd160(sha256()) hash of this key.
+ //! Any key that is valid in a descriptor serializes as 32 bytes within a Tapscript context. So we
+ //! must not hash the sign-bit byte in this case.
+ uint160 GetHash160(uint32_t key) const {
+ if (miniscript::IsTapscript(m_script_ctx)) {
+ return Hash160(XOnlyPubKey{m_keys[key]});
+ }
+ return m_keys[key].GetID();
+ }
public:
- ScriptMaker(const std::vector<CPubKey>& keys LIFETIMEBOUND) : m_keys(keys) {}
+ ScriptMaker(const std::vector<CPubKey>& keys LIFETIMEBOUND, const miniscript::MiniscriptContext script_ctx) : m_keys(keys), m_script_ctx{script_ctx} {}
std::vector<unsigned char> ToPKBytes(uint32_t key) const {
- return {m_keys[key].begin(), m_keys[key].end()};
+ // In Tapscript keys always serialize as x-only, whether an x-only key was used in the descriptor or not.
+ if (!miniscript::IsTapscript(m_script_ctx)) {
+ return {m_keys[key].begin(), m_keys[key].end()};
+ }
+ const XOnlyPubKey xonly_pubkey{m_keys[key]};
+ return {xonly_pubkey.begin(), xonly_pubkey.end()};
}
std::vector<unsigned char> ToPKHBytes(uint32_t key) const {
- auto id = m_keys[key].GetID();
+ auto id = GetHash160(key);
return {id.begin(), id.end()};
}
};
@@ -1165,7 +1182,7 @@ protected:
FlatSigningProvider& provider) const override
{
for (const auto& key : keys) provider.pubkeys.emplace(key.GetID(), key);
- return Vector(m_node->ToScript(ScriptMaker(keys)));
+ return Vector(m_node->ToScript(ScriptMaker(keys, m_node->GetMsCtx())));
}
public:
@@ -1434,11 +1451,19 @@ struct KeyParser {
return *m_keys.at(a) < *m_keys.at(b);
}
+ ParseScriptContext ParseContext() const {
+ switch (m_script_ctx) {
+ case miniscript::MiniscriptContext::P2WSH: return ParseScriptContext::P2WSH;
+ case miniscript::MiniscriptContext::TAPSCRIPT: return ParseScriptContext::P2TR;
+ }
+ assert(false);
+ }
+
template<typename I> std::optional<Key> FromString(I begin, I end) const
{
assert(m_out);
Key key = m_keys.size();
- auto pk = ParsePubkey(key, {&*begin, &*end}, ParseScriptContext::P2WSH, *m_out, m_key_parsing_error);
+ auto pk = ParsePubkey(key, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error);
if (!pk) return {};
m_keys.push_back(std::move(pk));
return key;
@@ -1452,11 +1477,18 @@ struct KeyParser {
template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const
{
assert(m_in);
- CPubKey pubkey(begin, end);
- if (pubkey.IsValidNonHybrid()) {
- Key key = m_keys.size();
- m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in));
+ Key key = m_keys.size();
+ if (miniscript::IsTapscript(m_script_ctx) && end - begin == 32) {
+ XOnlyPubKey pubkey;
+ std::copy(begin, end, pubkey.begin());
+ m_keys.push_back(InferPubkey(pubkey.GetEvenCorrespondingCPubKey(), ParseContext(), *m_in));
return key;
+ } else if (!miniscript::IsTapscript(m_script_ctx)) {
+ CPubKey pubkey{begin, end};
+ if (pubkey.IsValidNonHybrid()) {
+ m_keys.push_back(InferPubkey(pubkey, ParseContext(), *m_in));
+ return key;
+ }
}
return {};
}
@@ -1471,7 +1503,7 @@ struct KeyParser {
CPubKey pubkey;
if (m_in->GetPubKey(keyid, pubkey)) {
Key key = m_keys.size();
- m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in));
+ m_keys.push_back(InferPubkey(pubkey, ParseContext(), *m_in));
return key;
}
return {};
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index 1923329cbe..7585168ec2 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -1516,6 +1516,9 @@ public:
//! Return the expression type.
Type GetType() const { return typ; }
+ //! Return the script context for this node.
+ MiniscriptContext GetMsCtx() const { return m_script_ctx; }
+
//! Find an insane subnode which has no insane children. Nullptr if there is none.
const Node* FindInsaneSub() const {
return TreeEval<const Node*>([](const Node& node, Span<const Node*> subs) -> const Node* {