mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-20 13:37:28 +02:00
Merge db283a6b6f into merged_master (Bitcoin PR bitcoin/bitcoin#27255)
This commit is contained in:
commit
d24651b7c4
14 changed files with 1424 additions and 546 deletions
|
|
@ -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()};
|
||||
}
|
||||
};
|
||||
|
|
@ -1164,8 +1181,15 @@ protected:
|
|||
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts,
|
||||
FlatSigningProvider& provider) const override
|
||||
{
|
||||
for (const auto& key : keys) provider.pubkeys.emplace(key.GetID(), key);
|
||||
return Vector(m_node->ToScript(ScriptMaker(keys)));
|
||||
const auto script_ctx{m_node->GetMsCtx()};
|
||||
for (const auto& key : keys) {
|
||||
if (miniscript::IsTapscript(script_ctx)) {
|
||||
provider.pubkeys.emplace(Hash160(XOnlyPubKey{key}), key);
|
||||
} else {
|
||||
provider.pubkeys.emplace(key.GetID(), key);
|
||||
}
|
||||
}
|
||||
return Vector(m_node->ToScript(ScriptMaker(keys, script_ctx)));
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1401,9 +1425,7 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo
|
|||
|
||||
std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseScriptContext ctx, const SigningProvider& provider)
|
||||
{
|
||||
unsigned char full_key[CPubKey::COMPRESSED_SIZE] = {0x02};
|
||||
std::copy(xkey.begin(), xkey.end(), full_key + 1);
|
||||
CPubKey pubkey(full_key);
|
||||
CPubKey pubkey{xkey.GetEvenCorrespondingCPubKey()};
|
||||
std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true);
|
||||
KeyOriginInfo info;
|
||||
if (provider.GetKeyOriginByXOnly(xkey, info)) {
|
||||
|
|
@ -1426,18 +1448,32 @@ struct KeyParser {
|
|||
mutable std::vector<std::unique_ptr<PubkeyProvider>> m_keys;
|
||||
//! Used to detect key parsing errors within a Miniscript.
|
||||
mutable std::string m_key_parsing_error;
|
||||
//! The script context we're operating within (Tapscript or P2WSH).
|
||||
const miniscript::MiniscriptContext m_script_ctx;
|
||||
//! The number of keys that were parsed before starting to parse this Miniscript descriptor.
|
||||
uint32_t m_offset;
|
||||
|
||||
KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND) : m_out(out), m_in(in) {}
|
||||
KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND,
|
||||
miniscript::MiniscriptContext ctx, uint32_t offset = 0)
|
||||
: m_out(out), m_in(in), m_script_ctx(ctx), m_offset(offset) {}
|
||||
|
||||
bool KeyCompare(const Key& a, const Key& b) const {
|
||||
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(m_offset + key, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error);
|
||||
if (!pk) return {};
|
||||
m_keys.push_back(std::move(pk));
|
||||
return key;
|
||||
|
|
@ -1451,11 +1487,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 {};
|
||||
}
|
||||
|
|
@ -1470,11 +1513,15 @@ 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 {};
|
||||
}
|
||||
|
||||
miniscript::MiniscriptContext MsContext() const {
|
||||
return m_script_ctx;
|
||||
}
|
||||
};
|
||||
|
||||
/** Parse a script in a particular context. */
|
||||
|
|
@ -1500,8 +1547,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
|||
}
|
||||
++key_exp_index;
|
||||
return std::make_unique<PKHDescriptor>(std::move(pubkey));
|
||||
} else if (Func("pkh", expr)) {
|
||||
error = "Can only have pkh at top level, in sh(), or in wsh()";
|
||||
} else if (ctx != ParseScriptContext::P2TR && Func("pkh", expr)) {
|
||||
// Under Taproot, always the Miniscript parser deal with it.
|
||||
error = "Can only have pkh at top level, in sh(), wsh(), or in tr()";
|
||||
return nullptr;
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
|
||||
|
|
@ -1714,11 +1762,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
|||
}
|
||||
// Process miniscript expressions.
|
||||
{
|
||||
KeyParser parser(&out, nullptr);
|
||||
const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT};
|
||||
KeyParser parser(/*out = */&out, /* in = */nullptr, /* ctx = */script_ctx, key_exp_index);
|
||||
auto node = miniscript::FromString(std::string(expr.begin(), expr.end()), parser);
|
||||
if (node) {
|
||||
if (ctx != ParseScriptContext::P2WSH) {
|
||||
error = "Miniscript expressions can only be used in wsh";
|
||||
if (ctx != ParseScriptContext::P2WSH && ctx != ParseScriptContext::P2TR) {
|
||||
error = "Miniscript expressions can only be used in wsh or tr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (parser.m_key_parsing_error != "") {
|
||||
|
|
@ -1753,6 +1802,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
|||
// A signature check is required for a miniscript to be sane. Therefore no sane miniscript
|
||||
// may have an empty list of public keys.
|
||||
CHECK_NONFATAL(!parser.m_keys.empty());
|
||||
key_exp_index += parser.m_keys.size();
|
||||
return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node));
|
||||
}
|
||||
}
|
||||
|
|
@ -1886,8 +1936,9 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
|
|||
}
|
||||
}
|
||||
|
||||
if (ctx == ParseScriptContext::P2WSH) {
|
||||
KeyParser parser(nullptr, &provider);
|
||||
if (ctx == ParseScriptContext::P2WSH || ctx == ParseScriptContext::P2TR) {
|
||||
const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT};
|
||||
KeyParser parser(/* out = */nullptr, /* in = */&provider, /* ctx = */script_ctx);
|
||||
auto node = miniscript::FromScript(script, parser);
|
||||
if (node && node->IsSane()) {
|
||||
return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node));
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <vector>
|
||||
#include <script/script.h>
|
||||
#include <script/miniscript.h>
|
||||
#include <serialize.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
|
@ -32,7 +33,8 @@ Type SanitizeType(Type e) {
|
|||
return e;
|
||||
}
|
||||
|
||||
Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) {
|
||||
Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k,
|
||||
size_t data_size, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx) {
|
||||
// Sanity check on data
|
||||
if (fragment == Fragment::SHA256 || fragment == Fragment::HASH256) {
|
||||
assert(data_size == 32);
|
||||
|
|
@ -44,7 +46,7 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
|
|||
// Sanity check on k
|
||||
if (fragment == Fragment::OLDER || fragment == Fragment::AFTER) {
|
||||
assert(k >= 1 && k < 0x80000000UL);
|
||||
} else if (fragment == Fragment::MULTI) {
|
||||
} else if (fragment == Fragment::MULTI || fragment == Fragment::MULTI_A) {
|
||||
assert(k >= 1 && k <= n_keys);
|
||||
} else if (fragment == Fragment::THRESH) {
|
||||
assert(k >= 1 && k <= n_subs);
|
||||
|
|
@ -68,7 +70,11 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
|
|||
if (fragment == Fragment::PK_K || fragment == Fragment::PK_H) {
|
||||
assert(n_keys == 1);
|
||||
} else if (fragment == Fragment::MULTI) {
|
||||
assert(n_keys >= 1 && n_keys <= 20);
|
||||
assert(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTISIG);
|
||||
assert(!IsTapscript(ms_ctx));
|
||||
} else if (fragment == Fragment::MULTI_A) {
|
||||
assert(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTI_A);
|
||||
assert(IsTapscript(ms_ctx));
|
||||
} else {
|
||||
assert(n_keys == 0);
|
||||
}
|
||||
|
|
@ -113,7 +119,8 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
|
|||
"e"_mst.If(x << "f"_mst) | // e=f_x
|
||||
(x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x
|
||||
(x & "ms"_mst) | // m=m_x, s=s_x
|
||||
// NOTE: 'd:' is not 'u' under P2WSH as MINIMALIF is only a policy rule there.
|
||||
// NOTE: 'd:' is 'u' under Tapscript but not P2WSH as MINIMALIF is only a policy rule there.
|
||||
"u"_mst.If(IsTapscript(ms_ctx)) |
|
||||
"ndx"_mst; // n, d, x
|
||||
case Fragment::WRAP_V: return
|
||||
"V"_mst.If(x << "B"_mst) | // V=B_x
|
||||
|
|
@ -210,7 +217,12 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
|
|||
((x << "h"_mst) && (y << "g"_mst)) ||
|
||||
((x << "i"_mst) && (y << "j"_mst)) ||
|
||||
((x << "j"_mst) && (y << "i"_mst)))); // k=k_x*k_y*k_z* !(g_x*h_y + h_x*g_y + i_x*j_y + j_x*i_y)
|
||||
case Fragment::MULTI: return "Bnudemsk"_mst;
|
||||
case Fragment::MULTI: {
|
||||
return "Bnudemsk"_mst;
|
||||
}
|
||||
case Fragment::MULTI_A: {
|
||||
return "Budemsk"_mst;
|
||||
}
|
||||
case Fragment::THRESH: {
|
||||
bool all_e = true;
|
||||
bool all_m = true;
|
||||
|
|
@ -246,11 +258,12 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
|
|||
assert(false);
|
||||
}
|
||||
|
||||
size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys) {
|
||||
size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs,
|
||||
size_t n_keys, MiniscriptContext ms_ctx) {
|
||||
switch (fragment) {
|
||||
case Fragment::JUST_1:
|
||||
case Fragment::JUST_0: return 1;
|
||||
case Fragment::PK_K: return 34;
|
||||
case Fragment::PK_K: return IsTapscript(ms_ctx) ? 33 : 34;
|
||||
case Fragment::PK_H: return 3 + 21;
|
||||
case Fragment::OLDER:
|
||||
case Fragment::AFTER: return 1 + BuildScript(k).size();
|
||||
|
|
@ -259,6 +272,7 @@ size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_
|
|||
case Fragment::HASH160:
|
||||
case Fragment::RIPEMD160: return 4 + 2 + 21;
|
||||
case Fragment::MULTI: return 1 + BuildScript(n_keys).size() + BuildScript(k).size() + 34 * n_keys;
|
||||
case Fragment::MULTI_A: return (1 + 32 + 1) * n_keys + BuildScript(k).size() + 1;
|
||||
case Fragment::AND_V: return subsize;
|
||||
case Fragment::WRAP_V: return subsize + (sub0typ << "x"_mst);
|
||||
case Fragment::WRAP_S:
|
||||
|
|
@ -372,9 +386,13 @@ std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script)
|
|||
// Decompose OP_EQUALVERIFY into OP_EQUAL OP_VERIFY
|
||||
out.emplace_back(OP_EQUAL, std::vector<unsigned char>());
|
||||
opcode = OP_VERIFY;
|
||||
} else if (opcode == OP_NUMEQUALVERIFY) {
|
||||
// Decompose OP_NUMEQUALVERIFY into OP_NUMEQUAL OP_VERIFY
|
||||
out.emplace_back(OP_NUMEQUAL, std::vector<unsigned char>());
|
||||
opcode = OP_VERIFY;
|
||||
} else if (IsPushdataOp(opcode)) {
|
||||
if (!CheckMinimalPush(push_data, opcode)) return {};
|
||||
} else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL) && (*it == OP_VERIFY)) {
|
||||
} else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL || opcode == OP_NUMEQUAL) && (*it == OP_VERIFY)) {
|
||||
// Rule out non minimal VERIFY sequences
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -118,12 +118,17 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd
|
|||
pubkey = it->second.first;
|
||||
return true;
|
||||
}
|
||||
// Look for pubkey in pubkey list
|
||||
// Look for pubkey in pubkey lists
|
||||
const auto& pk_it = sigdata.misc_pubkeys.find(address);
|
||||
if (pk_it != sigdata.misc_pubkeys.end()) {
|
||||
pubkey = pk_it->second.first;
|
||||
return true;
|
||||
}
|
||||
const auto& tap_pk_it = sigdata.tap_pubkeys.find(address);
|
||||
if (tap_pk_it != sigdata.tap_pubkeys.end()) {
|
||||
pubkey = tap_pk_it->second.GetEvenCorrespondingCPubKey();
|
||||
return true;
|
||||
}
|
||||
// Query the underlying provider
|
||||
return provider.GetPubKey(address, pubkey);
|
||||
}
|
||||
|
|
@ -175,49 +180,158 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename M, typename K, typename V>
|
||||
miniscript::Availability MsLookupHelper(const M& map, const K& key, V& value)
|
||||
{
|
||||
auto it = map.find(key);
|
||||
if (it != map.end()) {
|
||||
value = it->second;
|
||||
return miniscript::Availability::YES;
|
||||
}
|
||||
return miniscript::Availability::NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Context for solving a Miniscript.
|
||||
* If enough material (access to keys, hash preimages, ..) is given, produces a valid satisfaction.
|
||||
*/
|
||||
template<typename Pk>
|
||||
struct Satisfier {
|
||||
using Key = Pk;
|
||||
|
||||
const SigningProvider& m_provider;
|
||||
SignatureData& m_sig_data;
|
||||
const BaseSignatureCreator& m_creator;
|
||||
const CScript& m_witness_script;
|
||||
//! The context of the script we are satisfying (either P2WSH or Tapscript).
|
||||
const miniscript::MiniscriptContext m_script_ctx;
|
||||
|
||||
explicit Satisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND,
|
||||
const BaseSignatureCreator& creator LIFETIMEBOUND,
|
||||
const CScript& witscript LIFETIMEBOUND,
|
||||
miniscript::MiniscriptContext script_ctx) : m_provider(provider),
|
||||
m_sig_data(sig_data),
|
||||
m_creator(creator),
|
||||
m_witness_script(witscript),
|
||||
m_script_ctx(script_ctx) {}
|
||||
|
||||
static bool KeyCompare(const Key& a, const Key& b) {
|
||||
return a < b;
|
||||
}
|
||||
|
||||
//! Get a CPubKey from a key hash. Note the key hash may be of an xonly pubkey.
|
||||
template<typename I>
|
||||
std::optional<CPubKey> CPubFromPKHBytes(I first, I last) const {
|
||||
assert(last - first == 20);
|
||||
CPubKey pubkey;
|
||||
CKeyID key_id;
|
||||
std::copy(first, last, key_id.begin());
|
||||
if (GetPubKey(m_provider, m_sig_data, key_id, pubkey)) return pubkey;
|
||||
m_sig_data.missing_pubkeys.push_back(key_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
//! Conversion to raw public key.
|
||||
std::vector<unsigned char> ToPKBytes(const Key& key) const { return {key.begin(), key.end()}; }
|
||||
|
||||
//! Time lock satisfactions.
|
||||
bool CheckAfter(uint32_t value) const { return m_creator.Checker().CheckLockTime(CScriptNum(value)); }
|
||||
bool CheckOlder(uint32_t value) const { return m_creator.Checker().CheckSequence(CScriptNum(value)); }
|
||||
|
||||
//! Hash preimage satisfactions.
|
||||
miniscript::Availability SatSHA256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.sha256_preimages, hash, preimage);
|
||||
}
|
||||
miniscript::Availability SatRIPEMD160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.ripemd160_preimages, hash, preimage);
|
||||
}
|
||||
miniscript::Availability SatHASH256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.hash256_preimages, hash, preimage);
|
||||
}
|
||||
miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.hash160_preimages, hash, preimage);
|
||||
}
|
||||
|
||||
miniscript::MiniscriptContext MsContext() const {
|
||||
return m_script_ctx;
|
||||
}
|
||||
};
|
||||
|
||||
/** Miniscript satisfier specific to P2WSH context. */
|
||||
struct WshSatisfier: Satisfier<CPubKey> {
|
||||
explicit WshSatisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND,
|
||||
const BaseSignatureCreator& creator LIFETIMEBOUND, const CScript& witscript LIFETIMEBOUND)
|
||||
: Satisfier(provider, sig_data, creator, witscript, miniscript::MiniscriptContext::P2WSH) {}
|
||||
|
||||
//! Conversion from a raw compressed public key.
|
||||
template <typename I>
|
||||
std::optional<CPubKey> FromPKBytes(I first, I last) const {
|
||||
CPubKey pubkey{first, last};
|
||||
if (pubkey.IsValid()) return pubkey;
|
||||
return {};
|
||||
}
|
||||
|
||||
//! Conversion from a raw compressed public key hash.
|
||||
template<typename I>
|
||||
std::optional<CPubKey> FromPKHBytes(I first, I last) const {
|
||||
return Satisfier::CPubFromPKHBytes(first, last);
|
||||
}
|
||||
|
||||
//! Satisfy an ECDSA signature check.
|
||||
miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const {
|
||||
if (CreateSig(m_creator, m_sig_data, m_provider, sig, key, m_witness_script, SigVersion::WITNESS_V0, SIGHASH_RANGEPROOF)) { // ELEMENTS FIXME: check flags
|
||||
return miniscript::Availability::YES;
|
||||
}
|
||||
return miniscript::Availability::NO;
|
||||
}
|
||||
};
|
||||
|
||||
/** Miniscript satisfier specific to Tapscript context. */
|
||||
struct TapSatisfier: Satisfier<XOnlyPubKey> {
|
||||
const uint256& m_leaf_hash;
|
||||
|
||||
explicit TapSatisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND,
|
||||
const BaseSignatureCreator& creator LIFETIMEBOUND, const CScript& script LIFETIMEBOUND,
|
||||
const uint256& leaf_hash LIFETIMEBOUND)
|
||||
: Satisfier(provider, sig_data, creator, script, miniscript::MiniscriptContext::TAPSCRIPT),
|
||||
m_leaf_hash(leaf_hash) {}
|
||||
|
||||
//! Conversion from a raw xonly public key.
|
||||
template <typename I>
|
||||
std::optional<XOnlyPubKey> FromPKBytes(I first, I last) const {
|
||||
CHECK_NONFATAL(last - first == 32);
|
||||
XOnlyPubKey pubkey;
|
||||
std::copy(first, last, pubkey.begin());
|
||||
return pubkey;
|
||||
}
|
||||
|
||||
//! Conversion from a raw xonly public key hash.
|
||||
template<typename I>
|
||||
std::optional<XOnlyPubKey> FromPKHBytes(I first, I last) const {
|
||||
if (auto pubkey = Satisfier::CPubFromPKHBytes(first, last)) return XOnlyPubKey{*pubkey};
|
||||
return {};
|
||||
}
|
||||
|
||||
//! Satisfy a BIP340 signature check.
|
||||
miniscript::Availability Sign(const XOnlyPubKey& key, std::vector<unsigned char>& sig) const {
|
||||
if (CreateTaprootScriptSig(m_creator, m_sig_data, m_provider, sig, key, m_leaf_hash, SigVersion::TAPSCRIPT)) {
|
||||
return miniscript::Availability::YES;
|
||||
}
|
||||
return miniscript::Availability::NO;
|
||||
}
|
||||
};
|
||||
|
||||
static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, Span<const unsigned char> script_bytes, std::vector<valtype>& result)
|
||||
{
|
||||
// Only BIP342 tapscript signing is supported for now.
|
||||
if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false;
|
||||
SigVersion sigversion = SigVersion::TAPSCRIPT;
|
||||
|
||||
uint256 leaf_hash = ComputeTapleafHash(leaf_version, script_bytes);
|
||||
CScript script = CScript(script_bytes.begin(), script_bytes.end());
|
||||
|
||||
// <xonly pubkey> OP_CHECKSIG
|
||||
if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
|
||||
XOnlyPubKey pubkey{Span{script}.subspan(1, 32)};
|
||||
std::vector<unsigned char> sig;
|
||||
if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) {
|
||||
result = Vector(std::move(sig));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// multi_a scripts (<key> OP_CHECKSIG <key> OP_CHECKSIGADD <key> OP_CHECKSIGADD <k> OP_NUMEQUAL)
|
||||
if (auto match = MatchMultiA(script)) {
|
||||
std::vector<std::vector<unsigned char>> sigs;
|
||||
int good_sigs = 0;
|
||||
for (size_t i = 0; i < match->second.size(); ++i) {
|
||||
XOnlyPubKey pubkey{*(match->second.rbegin() + i)};
|
||||
std::vector<unsigned char> sig;
|
||||
bool good_sig = CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion);
|
||||
if (good_sig && good_sigs < match->first) {
|
||||
++good_sigs;
|
||||
sigs.push_back(std::move(sig));
|
||||
} else {
|
||||
sigs.emplace_back();
|
||||
}
|
||||
}
|
||||
if (good_sigs == match->first) {
|
||||
result = std::move(sigs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
TapSatisfier ms_satisfier{provider, sigdata, creator, script, leaf_hash};
|
||||
const auto ms = miniscript::FromScript(script, ms_satisfier);
|
||||
return ms && ms->Satisfy(ms_satisfier, result) == miniscript::Availability::YES;
|
||||
}
|
||||
|
||||
static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result)
|
||||
|
|
@ -395,92 +509,6 @@ static CScript PushAll(const std::vector<valtype>& values)
|
|||
return result;
|
||||
}
|
||||
|
||||
template<typename M, typename K, typename V>
|
||||
miniscript::Availability MsLookupHelper(const M& map, const K& key, V& value)
|
||||
{
|
||||
auto it = map.find(key);
|
||||
if (it != map.end()) {
|
||||
value = it->second;
|
||||
return miniscript::Availability::YES;
|
||||
}
|
||||
return miniscript::Availability::NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Context for solving a Miniscript.
|
||||
* If enough material (access to keys, hash preimages, ..) is given, produces a valid satisfaction.
|
||||
*/
|
||||
struct Satisfier {
|
||||
typedef CPubKey Key;
|
||||
|
||||
const SigningProvider& m_provider;
|
||||
SignatureData& m_sig_data;
|
||||
const BaseSignatureCreator& m_creator;
|
||||
const CScript& m_witness_script;
|
||||
|
||||
explicit Satisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND,
|
||||
const BaseSignatureCreator& creator LIFETIMEBOUND,
|
||||
const CScript& witscript LIFETIMEBOUND) : m_provider(provider),
|
||||
m_sig_data(sig_data),
|
||||
m_creator(creator),
|
||||
m_witness_script(witscript) {}
|
||||
|
||||
static bool KeyCompare(const Key& a, const Key& b) {
|
||||
return a < b;
|
||||
}
|
||||
|
||||
//! Conversion from a raw public key.
|
||||
template <typename I>
|
||||
std::optional<Key> FromPKBytes(I first, I last) const
|
||||
{
|
||||
Key pubkey{first, last};
|
||||
if (pubkey.IsValid()) return pubkey;
|
||||
return {};
|
||||
}
|
||||
|
||||
//! Conversion from a raw public key hash.
|
||||
template<typename I>
|
||||
std::optional<Key> FromPKHBytes(I first, I last) const {
|
||||
assert(last - first == 20);
|
||||
Key pubkey;
|
||||
CKeyID key_id;
|
||||
std::copy(first, last, key_id.begin());
|
||||
if (GetPubKey(m_provider, m_sig_data, key_id, pubkey)) return pubkey;
|
||||
m_sig_data.missing_pubkeys.push_back(key_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
//! Conversion to raw public key.
|
||||
std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; }
|
||||
|
||||
//! Satisfy a signature check.
|
||||
miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const {
|
||||
if (CreateSig(m_creator, m_sig_data, m_provider, sig, key, m_witness_script, SigVersion::WITNESS_V0, SIGHASH_RANGEPROOF)) { // ELEMENTS FIXME: check flags
|
||||
return miniscript::Availability::YES;
|
||||
}
|
||||
return miniscript::Availability::NO;
|
||||
}
|
||||
|
||||
//! Time lock satisfactions.
|
||||
bool CheckAfter(uint32_t value) const { return m_creator.Checker().CheckLockTime(CScriptNum(value)); }
|
||||
bool CheckOlder(uint32_t value) const { return m_creator.Checker().CheckSequence(CScriptNum(value)); }
|
||||
|
||||
|
||||
//! Hash preimage satisfactions.
|
||||
miniscript::Availability SatSHA256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.sha256_preimages, hash, preimage);
|
||||
}
|
||||
miniscript::Availability SatRIPEMD160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.ripemd160_preimages, hash, preimage);
|
||||
}
|
||||
miniscript::Availability SatHASH256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.hash256_preimages, hash, preimage);
|
||||
}
|
||||
miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
|
||||
return MsLookupHelper(m_sig_data.hash160_preimages, hash, preimage);
|
||||
}
|
||||
};
|
||||
|
||||
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata, unsigned int additional_flags)
|
||||
{
|
||||
if (sigdata.complete) return true;
|
||||
|
|
@ -530,7 +558,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
|||
// isn't fully solved. For instance the CHECKMULTISIG satisfaction in SignStep() pushes partial signatures
|
||||
// and the extractor relies on this behaviour to combine witnesses.
|
||||
if (!solved && result.empty()) {
|
||||
Satisfier ms_satisfier{provider, sigdata, creator, witnessscript};
|
||||
WshSatisfier ms_satisfier{provider, sigdata, creator, witnessscript};
|
||||
const auto ms = miniscript::FromScript(witnessscript, ms_satisfier);
|
||||
solved = ms && ms->Satisfy(ms_satisfier, result) == miniscript::Availability::YES;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ struct SignatureData {
|
|||
std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending
|
||||
std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash.
|
||||
std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> taproot_misc_pubkeys; ///< Miscellaneous Taproot pubkeys involved in this input along with their leaf script hashes and key origin data. Also includes the Taproot internal key (may have no leaf script hashes).
|
||||
std::map<CKeyID, XOnlyPubKey> tap_pubkeys; ///< Misc Taproot pubkeys involved in this input, by hash. (Equivalent of misc_pubkeys but for Taproot.)
|
||||
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
|
||||
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
|
||||
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue