mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 20:03:34 -03:00
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).
This commit is contained in:
parent
5e76f3f0dd
commit
8ff9489422
2 changed files with 45 additions and 10 deletions
|
@ -1114,16 +1114,33 @@ public:
|
||||||
class ScriptMaker {
|
class ScriptMaker {
|
||||||
//! Keys contained in the Miniscript (the evaluation of DescriptorImpl::m_pubkey_args).
|
//! Keys contained in the Miniscript (the evaluation of DescriptorImpl::m_pubkey_args).
|
||||||
const std::vector<CPubKey>& m_keys;
|
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:
|
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 {
|
std::vector<unsigned char> ToPKBytes(uint32_t key) const {
|
||||||
|
// 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()};
|
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 {
|
std::vector<unsigned char> ToPKHBytes(uint32_t key) const {
|
||||||
auto id = m_keys[key].GetID();
|
auto id = GetHash160(key);
|
||||||
return {id.begin(), id.end()};
|
return {id.begin(), id.end()};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1165,7 +1182,7 @@ protected:
|
||||||
FlatSigningProvider& provider) const override
|
FlatSigningProvider& provider) const override
|
||||||
{
|
{
|
||||||
for (const auto& key : keys) provider.pubkeys.emplace(key.GetID(), key);
|
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:
|
public:
|
||||||
|
@ -1434,11 +1451,19 @@ struct KeyParser {
|
||||||
return *m_keys.at(a) < *m_keys.at(b);
|
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
|
template<typename I> std::optional<Key> FromString(I begin, I end) const
|
||||||
{
|
{
|
||||||
assert(m_out);
|
assert(m_out);
|
||||||
Key key = m_keys.size();
|
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 {};
|
if (!pk) return {};
|
||||||
m_keys.push_back(std::move(pk));
|
m_keys.push_back(std::move(pk));
|
||||||
return key;
|
return key;
|
||||||
|
@ -1452,11 +1477,18 @@ struct KeyParser {
|
||||||
template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const
|
template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const
|
||||||
{
|
{
|
||||||
assert(m_in);
|
assert(m_in);
|
||||||
CPubKey pubkey(begin, end);
|
|
||||||
if (pubkey.IsValidNonHybrid()) {
|
|
||||||
Key key = m_keys.size();
|
Key key = m_keys.size();
|
||||||
m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in));
|
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;
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1471,7 +1503,7 @@ struct KeyParser {
|
||||||
CPubKey pubkey;
|
CPubKey pubkey;
|
||||||
if (m_in->GetPubKey(keyid, pubkey)) {
|
if (m_in->GetPubKey(keyid, pubkey)) {
|
||||||
Key key = m_keys.size();
|
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 key;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -1516,6 +1516,9 @@ public:
|
||||||
//! Return the expression type.
|
//! Return the expression type.
|
||||||
Type GetType() const { return typ; }
|
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.
|
//! Find an insane subnode which has no insane children. Nullptr if there is none.
|
||||||
const Node* FindInsaneSub() const {
|
const Node* FindInsaneSub() const {
|
||||||
return TreeEval<const Node*>([](const Node& node, Span<const Node*> subs) -> const Node* {
|
return TreeEval<const Node*>([](const Node& node, Span<const Node*> subs) -> const Node* {
|
||||||
|
|
Loading…
Reference in a new issue