Merge bitcoin/bitcoin#29675: wallet: Be able to receive and spend inputs involving MuSig2 aggregate keys

ac599c4a9c test: Test MuSig2 in the wallet (Ava Chow)
68ef954c4c wallet: Keep secnonces in DescriptorScriptPubKeyMan (Ava Chow)
4a273edda0 sign: Create MuSig2 signatures for known MuSig2 aggregate keys (Ava Chow)
258db93889 sign: Add CreateMuSig2AggregateSig (Ava Chow)
bf69442b3f sign: Add CreateMuSig2PartialSig (Ava Chow)
512b17fc56 sign: Add CreateMuSig2Nonce (Ava Chow)
82ea67c607 musig: Add MuSig2AggregatePubkeys variant that validates the aggregate (Ava Chow)
d99a081679 psbt: MuSig2 data in Fill/FromSignatureData (Ava Chow)
4d8b4f5336 signingprovider: Add musig2 secnonces (Ava Chow)
c06a1dc86f Add MuSig2SecNonce class for secure allocation of musig nonces (Ava Chow)
9baff05e49 sign: Include taproot output key's KeyOriginInfo in sigdata (Ava Chow)
4b24bfeab9 pubkey: Return tweaks from BIP32 derivation (Ava Chow)
f14876213a musig: Move synthetic xpub construction to its own function (Ava Chow)
fb8720f1e0 sign: Refactor Schnorr sighash computation out of CreateSchnorrSig (Ava Chow)
a4cfddda64 tests: Clarify why musig derivation adds a pubkey and xpub (Ava Chow)
39a63bf2e7 descriptors: Add a doxygen comment for has_hardened output_parameter (Ava Chow)
2320184d0e descriptors: Fix meaning of any_key_parsed (Ava Chow)

Pull request description:

  This PR implements MuSig2 signing so that the wallet can receive and spend from imported `musig(0` descriptors.

  The libsecp musig module is enabled so that it can be used for all of the MuSig2 cryptography.

  Secnonces are handled in a separate class which holds the libsecp secnonce object in a `secure_unique_ptr`. Since secnonces must not be used, this class has no serialization and will only live in memory. A restart of the software will require a restart of the MuSig2 signing process.

ACKs for top commit:
  fjahr:
    tACK ac599c4a9c
  rkrux:
    lgtm tACK ac599c4a9c
  theStack:
    Code-review ACK ac599c4a9c 🗝️

Tree-SHA512: 626b9adc42ed2403e2f4405321eb9ce009a829c07d968e95ab288fe4940b195b0af35ca279a4a7fa51af76e55382bad6f63a23bca14a84140559b3c667e7041e
This commit is contained in:
merge-script
2025-10-14 16:25:52 -04:00
18 changed files with 977 additions and 48 deletions

View File

@@ -641,13 +641,7 @@ public:
// Make our pubkey provider
if (IsRangedDerivation() || !m_path.empty()) {
// Make the synthetic xpub and construct the BIP32PubkeyProvider
CExtPubKey extpub;
extpub.nDepth = 0;
std::memset(extpub.vchFingerprint, 0, 4);
extpub.nChild = 0;
extpub.chaincode = MUSIG_CHAINCODE;
extpub.pubkey = m_aggregate_pubkey.value();
CExtPubKey extpub = CreateMuSig2SyntheticXpub(m_aggregate_pubkey.value());
m_aggregate_provider = std::make_unique<BIP32PubkeyProvider>(m_expr_index, extpub, m_path, m_derive, /*apostrophe=*/false);
} else {
m_aggregate_provider = std::make_unique<ConstPubkeyProvider>(m_expr_index, m_aggregate_pubkey.value(), /*xonly=*/false);
@@ -1647,6 +1641,7 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
* @param[out] apostrophe only updated if hardened derivation is found
* @param[out] error parsing error message
* @param[in] allow_multipath Allows the parsed path to use the multipath specifier
* @param[out] has_hardened Records whether the path contains any hardened derivation
* @returns false if parsing failed
**/
[[nodiscard]] bool ParseKeyPath(const std::vector<std::span<const char>>& split, std::vector<KeyPath>& out, bool& apostrophe, std::string& error, bool allow_multipath, bool& has_hardened)
@@ -1843,20 +1838,20 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t& key_exp_index
bool any_ranged = false;
bool all_bip32 = true;
std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> providers;
bool any_key_parsed = true;
bool any_key_parsed = false;
size_t max_multipath_len = 0;
while (expr.size()) {
if (!any_key_parsed && !Const(",", expr)) {
if (any_key_parsed && !Const(",", expr)) {
error = strprintf("musig(): expected ',', got '%c'", expr[0]);
return {};
}
any_key_parsed = false;
auto arg = Expr(expr);
auto pk = ParsePubkey(key_exp_index, arg, ParseScriptContext::MUSIG, out, error);
if (pk.empty()) {
error = strprintf("musig(): %s", error);
return {};
}
any_key_parsed = true;
any_ranged = any_ranged || pk.at(0)->IsRange();
all_bip32 = all_bip32 && pk.at(0)->IsBIP32();
@@ -1866,7 +1861,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t& key_exp_index
providers.emplace_back(std::move(pk));
key_exp_index++;
}
if (any_key_parsed) {
if (!any_key_parsed) {
error = "musig(): Must contain key expressions";
return {};
}

View File

@@ -7,8 +7,10 @@
#include <consensus/amount.h>
#include <key.h>
#include <musig.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <random.h>
#include <script/keyorigin.h>
#include <script/miniscript.h>
#include <script/script.h>
@@ -59,17 +61,14 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
return true;
}
bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const
std::optional<uint256> MutableTransactionSignatureCreator::ComputeSchnorrSignatureHash(const uint256* leaf_hash, SigVersion sigversion) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
CKey key;
if (!provider.GetKeyByXOnly(pubkey, key)) return false;
// BIP341/BIP342 signing needs lots of precomputed transaction data. While some
// (non-SIGHASH_DEFAULT) sighash modes exist that can work with just some subset
// of data present, for now, only support signing when everything is provided.
if (!m_txdata || !m_txdata->m_bip341_taproot_ready || !m_txdata->m_spent_outputs_ready) return false;
if (!m_txdata || !m_txdata->m_bip341_taproot_ready || !m_txdata->m_spent_outputs_ready) return std::nullopt;
ScriptExecutionData execdata;
execdata.m_annex_init = true;
@@ -77,19 +76,134 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
if (sigversion == SigVersion::TAPSCRIPT) {
execdata.m_codeseparator_pos_init = true;
execdata.m_codeseparator_pos = 0xFFFFFFFF; // Only support non-OP_CODESEPARATOR BIP342 signing for now.
if (!leaf_hash) return false; // BIP342 signing needs leaf hash.
if (!leaf_hash) return std::nullopt; // BIP342 signing needs leaf hash.
execdata.m_tapleaf_hash_init = true;
execdata.m_tapleaf_hash = *leaf_hash;
}
uint256 hash;
if (!SignatureHashSchnorr(hash, execdata, m_txto, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
if (!SignatureHashSchnorr(hash, execdata, m_txto, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return std::nullopt;
return hash;
}
bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
CKey key;
if (!provider.GetKeyByXOnly(pubkey, key)) return false;
std::optional<uint256> hash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
if (!hash.has_value()) return false;
sig.resize(64);
// Use uint256{} as aux_rnd for now.
if (!key.SignSchnorr(hash, sig, merkle_root, {})) return false;
if (!key.SignSchnorr(*hash, sig, merkle_root, {})) return false;
if (nHashType) sig.push_back(nHashType);
return true;
}
std::vector<uint8_t> MutableTransactionSignatureCreator::CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
// Retrieve the private key
CKey key;
if (!provider.GetKey(part_pubkey.GetID(), key)) return {};
// Retrieve participant pubkeys
auto it = sigdata.musig2_pubkeys.find(aggregate_pubkey);
if (it == sigdata.musig2_pubkeys.end()) return {};
const std::vector<CPubKey>& pubkeys = it->second;
if (std::find(pubkeys.begin(), pubkeys.end(), part_pubkey) == pubkeys.end()) return {};
// Compute sighash
std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
if (!sighash.has_value()) return {};
MuSig2SecNonce secnonce;
std::vector<uint8_t> out = key.CreateMuSig2Nonce(secnonce, *sighash, aggregate_pubkey, pubkeys);
if (out.empty()) return {};
// Store the secnonce in the SigningProvider
provider.SetMuSig2SecNonce(MuSig2SessionID(script_pubkey, part_pubkey, *sighash), std::move(secnonce));
return out;
}
bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
// Retrieve private key
CKey key;
if (!provider.GetKey(part_pubkey.GetID(), key)) return false;
// Retrieve participant pubkeys
auto it = sigdata.musig2_pubkeys.find(aggregate_pubkey);
if (it == sigdata.musig2_pubkeys.end()) return false;
const std::vector<CPubKey>& pubkeys = it->second;
if (std::find(pubkeys.begin(), pubkeys.end(), part_pubkey) == pubkeys.end()) return {};
// Retrieve pubnonces
auto this_leaf_aggkey = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256());
auto pubnonce_it = sigdata.musig2_pubnonces.find(this_leaf_aggkey);
if (pubnonce_it == sigdata.musig2_pubnonces.end()) return false;
const std::map<CPubKey, std::vector<uint8_t>>& pubnonces = pubnonce_it->second;
// Check if enough pubnonces
if (pubnonces.size() != pubkeys.size()) return false;
// Compute sighash
std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
if (!sighash.has_value()) return false;
// Retrieve the secnonce
uint256 session_id = MuSig2SessionID(script_pubkey, part_pubkey, *sighash);
std::optional<std::reference_wrapper<MuSig2SecNonce>> secnonce = provider.GetMuSig2SecNonce(session_id);
if (!secnonce || !secnonce->get().IsValid()) return false;
// Compute the sig
std::optional<uint256> sig = key.CreateMuSig2PartialSig(*sighash, aggregate_pubkey, pubkeys, pubnonces, *secnonce, tweaks);
if (!sig) return false;
partial_sig = std::move(*sig);
// Delete the secnonce now that we're done with it
assert(!secnonce->get().IsValid());
provider.DeleteMuSig2Session(session_id);
return true;
}
bool MutableTransactionSignatureCreator::CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
if (!participants.size()) return false;
// Retrieve pubnonces and partial sigs
auto this_leaf_aggkey = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256());
auto pubnonce_it = sigdata.musig2_pubnonces.find(this_leaf_aggkey);
if (pubnonce_it == sigdata.musig2_pubnonces.end()) return false;
const std::map<CPubKey, std::vector<uint8_t>>& pubnonces = pubnonce_it->second;
auto partial_sigs_it = sigdata.musig2_partial_sigs.find(this_leaf_aggkey);
if (partial_sigs_it == sigdata.musig2_partial_sigs.end()) return false;
const std::map<CPubKey, uint256>& partial_sigs = partial_sigs_it->second;
// Check if enough pubnonces and partial sigs
if (pubnonces.size() != participants.size()) return false;
if (partial_sigs.size() != participants.size()) return false;
// Compute sighash
std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
if (!sighash.has_value()) return false;
std::optional<std::vector<uint8_t>> res = ::CreateMuSig2AggregateSig(participants, aggregate_pubkey, tweaks, *sighash, pubnonces, partial_sigs);
if (!res) return false;
sig = res.value();
if (nHashType) sig.push_back(nHashType);
return true;
}
static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
{
if (provider.GetCScript(scriptid, script)) {
@@ -151,6 +265,100 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
return false;
}
static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& script_pubkey, const uint256* merkle_root, const uint256* leaf_hash, SigVersion sigversion)
{
Assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
// Lookup derivation paths for the script pubkey
KeyOriginInfo agg_info;
auto misc_pk_it = sigdata.taproot_misc_pubkeys.find(script_pubkey);
if (misc_pk_it != sigdata.taproot_misc_pubkeys.end()) {
agg_info = misc_pk_it->second.second;
}
for (const auto& [agg_pub, part_pks] : sigdata.musig2_pubkeys) {
if (part_pks.empty()) continue;
// Fill participant derivation path info
for (const auto& part_pk : part_pks) {
KeyOriginInfo part_info;
if (provider.GetKeyOrigin(part_pk.GetID(), part_info)) {
XOnlyPubKey xonly_part(part_pk);
auto it = sigdata.taproot_misc_pubkeys.find(xonly_part);
if (it == sigdata.taproot_misc_pubkeys.end()) {
it = sigdata.taproot_misc_pubkeys.emplace(xonly_part, std::make_pair(std::set<uint256>(), part_info)).first;
}
if (leaf_hash) it->second.first.insert(*leaf_hash);
}
}
// The pubkey in the script may not be the actual aggregate of the participants, but derived from it.
// Check the derivation, and compute the BIP 32 derivation tweaks
std::vector<std::pair<uint256, bool>> tweaks;
CPubKey plain_pub = agg_pub;
if (XOnlyPubKey(agg_pub) != script_pubkey) {
if (agg_info.path.empty()) continue;
// Compute and compare fingerprint
CKeyID keyid = agg_pub.GetID();
if (!std::equal(agg_info.fingerprint, agg_info.fingerprint + sizeof(agg_info.fingerprint), keyid.data())) {
continue;
}
// Get the BIP32 derivation tweaks
CExtPubKey extpub = CreateMuSig2SyntheticXpub(agg_pub);
for (const int i : agg_info.path) {
auto& [t, xonly] = tweaks.emplace_back();
xonly = false;
if (!extpub.Derive(extpub, i, &t)) {
return false;
}
}
Assert(XOnlyPubKey(extpub.pubkey) == script_pubkey);
plain_pub = extpub.pubkey;
}
// Add the merkle root tweak
if (sigversion == SigVersion::TAPROOT && merkle_root) {
tweaks.emplace_back(script_pubkey.ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root), true);
std::optional<std::pair<XOnlyPubKey, bool>> tweaked = script_pubkey.CreateTapTweak(merkle_root->IsNull() ? nullptr : merkle_root);
if (!Assume(tweaked)) return false;
plain_pub = tweaked->first.GetCPubKeys().at(tweaked->second ? 1 : 0);
}
// First try to aggregate
if (creator.CreateMuSig2AggregateSig(part_pks, sig_out, agg_pub, plain_pub, leaf_hash, tweaks, sigversion, sigdata)) {
if (sigversion == SigVersion::TAPROOT) {
sigdata.taproot_key_path_sig = sig_out;
} else {
auto lookup_key = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256());
sigdata.taproot_script_sigs[lookup_key] = sig_out;
}
continue;
}
// Cannot aggregate, try making partial sigs for every participant
auto pub_key_leaf_hash = std::make_pair(plain_pub, leaf_hash ? *leaf_hash : uint256());
for (const CPubKey& part_pk : part_pks) {
uint256 partial_sig;
if (creator.CreateMuSig2PartialSig(provider, partial_sig, agg_pub, plain_pub, part_pk, leaf_hash, tweaks, sigversion, sigdata) && Assume(!partial_sig.IsNull())) {
sigdata.musig2_partial_sigs[pub_key_leaf_hash].emplace(part_pk, partial_sig);
}
}
// If there are any partial signatures, exit early
auto partial_sigs_it = sigdata.musig2_partial_sigs.find(pub_key_leaf_hash);
if (partial_sigs_it != sigdata.musig2_partial_sigs.end() && !partial_sigs_it->second.empty()) {
continue;
}
// No partial sigs, try to make pubnonces
std::map<CPubKey, std::vector<uint8_t>>& pubnonces = sigdata.musig2_pubnonces[pub_key_leaf_hash];
for (const CPubKey& part_pk : part_pks) {
if (pubnonces.contains(part_pk)) continue;
std::vector<uint8_t> pubnonce = creator.CreateMuSig2Nonce(provider, agg_pub, plain_pub, part_pk, leaf_hash, merkle_root, sigversion, sigdata);
if (pubnonce.empty()) continue;
pubnonces[part_pk] = std::move(pubnonce);
}
}
return true;
}
static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion)
{
KeyOriginInfo info;
@@ -169,11 +377,14 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur
sig_out = it->second;
return true;
}
if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
sigdata.taproot_script_sigs[lookup_key] = sig_out;
return true;
} else if (!SignMuSig2(creator, sigdata, provider, sig_out, pubkey, /*merkle_root=*/nullptr, &leaf_hash, sigversion)) {
return false;
}
return false;
return sigdata.taproot_script_sigs.contains(lookup_key);
}
template<typename M, typename K, typename V>
@@ -342,27 +553,45 @@ static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCrea
if (provider.GetTaprootBuilder(output, builder)) {
sigdata.tr_builder = builder;
}
if (auto agg_keys = provider.GetAllMuSig2ParticipantPubkeys(); !agg_keys.empty()) {
sigdata.musig2_pubkeys.insert(agg_keys.begin(), agg_keys.end());
}
// Try key path spending.
{
KeyOriginInfo info;
if (provider.GetKeyOriginByXOnly(sigdata.tr_spenddata.internal_key, info)) {
KeyOriginInfo internal_key_info;
if (provider.GetKeyOriginByXOnly(sigdata.tr_spenddata.internal_key, internal_key_info)) {
auto it = sigdata.taproot_misc_pubkeys.find(sigdata.tr_spenddata.internal_key);
if (it == sigdata.taproot_misc_pubkeys.end()) {
sigdata.taproot_misc_pubkeys.emplace(sigdata.tr_spenddata.internal_key, std::make_pair(std::set<uint256>(), info));
sigdata.taproot_misc_pubkeys.emplace(sigdata.tr_spenddata.internal_key, std::make_pair(std::set<uint256>(), internal_key_info));
}
}
std::vector<unsigned char> sig;
if (sigdata.taproot_key_path_sig.size() == 0) {
if (creator.CreateSchnorrSig(provider, sig, sigdata.tr_spenddata.internal_key, nullptr, &sigdata.tr_spenddata.merkle_root, SigVersion::TAPROOT)) {
sigdata.taproot_key_path_sig = sig;
KeyOriginInfo output_key_info;
if (provider.GetKeyOriginByXOnly(output, output_key_info)) {
auto it = sigdata.taproot_misc_pubkeys.find(output);
if (it == sigdata.taproot_misc_pubkeys.end()) {
sigdata.taproot_misc_pubkeys.emplace(output, std::make_pair(std::set<uint256>(), output_key_info));
}
}
if (sigdata.taproot_key_path_sig.size() == 0) {
if (creator.CreateSchnorrSig(provider, sig, output, nullptr, nullptr, SigVersion::TAPROOT)) {
auto make_keypath_sig = [&](const XOnlyPubKey& pk, const uint256* merkle_root) {
std::vector<unsigned char> sig;
if (creator.CreateSchnorrSig(provider, sig, pk, nullptr, merkle_root, SigVersion::TAPROOT)) {
sigdata.taproot_key_path_sig = sig;
} else {
SignMuSig2(creator, sigdata, provider, sig, pk, merkle_root, /*leaf_hash=*/nullptr, SigVersion::TAPROOT);
}
};
// First try signing with internal key
if (sigdata.taproot_key_path_sig.size() == 0) {
make_keypath_sig(sigdata.tr_spenddata.internal_key, &sigdata.tr_spenddata.merkle_root);
}
// Try signing with output key if still no signature
if (sigdata.taproot_key_path_sig.size() == 0) {
make_keypath_sig(output, nullptr);
}
if (sigdata.taproot_key_path_sig.size()) {
result = Vector(sigdata.taproot_key_path_sig);
@@ -737,6 +966,22 @@ public:
sig.assign(64, '\000');
return true;
}
std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const override
{
std::vector<uint8_t> out;
out.assign(MUSIG2_PUBNONCE_SIZE, '\000');
return out;
}
bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override
{
partial_sig = uint256::ONE;
return true;
}
bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override
{
sig.assign(64, '\000');
return true;
}
};
}

View File

@@ -23,6 +23,7 @@ class SigningProvider;
struct bilingual_str;
struct CMutableTransaction;
struct SignatureData;
/** Interface for signature creators. */
class BaseSignatureCreator {
@@ -33,6 +34,9 @@ public:
/** Create a singular (non-script) signature. */
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
virtual std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const =0;
virtual bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0;
virtual bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0;
};
/** A signature creator for transactions. */
@@ -45,12 +49,17 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator
const MutableTransactionSignatureChecker checker;
const PrecomputedTransactionData* m_txdata;
std::optional<uint256> ComputeSchnorrSignatureHash(const uint256* leaf_hash, SigVersion sigversion) const;
public:
MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, int hash_type);
MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const override;
bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override;
bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override;
};
/** A signature checker that accepts all signatures */
@@ -78,7 +87,7 @@ struct SignatureData {
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
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<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 and output keys (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
@@ -88,6 +97,12 @@ struct SignatureData {
std::map<std::vector<uint8_t>, std::vector<uint8_t>> hash256_preimages; ///< Mapping from a HASH256 hash to its preimage provided to solve a Script
std::map<std::vector<uint8_t>, std::vector<uint8_t>> ripemd160_preimages; ///< Mapping from a RIPEMD160 hash to its preimage provided to solve a Script
std::map<std::vector<uint8_t>, std::vector<uint8_t>> hash160_preimages; ///< Mapping from a HASH160 hash to its preimage provided to solve a Script
//! Map MuSig2 aggregate pubkeys to its participants
std::map<CPubKey, std::vector<CPubKey>> musig2_pubkeys;
//! Mapping from pair of MuSig2 aggregate pubkey, and tapleaf hash to map of MuSig2 participant pubkeys to MuSig2 public nonce
std::map<std::pair<CPubKey, uint256>, std::map<CPubKey, std::vector<uint8_t>>> musig2_pubnonces;
//! Mapping from pair of MuSig2 aggregate pubkey, and tapleaf hash to map of MuSig2 participant pubkeys to MuSig2 partial signature
std::map<std::pair<CPubKey, uint256>, std::map<CPubKey, uint256>> musig2_partial_sigs;
SignatureData() = default;
explicit SignatureData(const CScript& script) : scriptSig(script) {}

View File

@@ -58,6 +58,26 @@ std::vector<CPubKey> HidingSigningProvider::GetMuSig2ParticipantPubkeys(const CP
return m_provider->GetMuSig2ParticipantPubkeys(pubkey);
}
std::map<CPubKey, std::vector<CPubKey>> HidingSigningProvider::GetAllMuSig2ParticipantPubkeys() const
{
return m_provider->GetAllMuSig2ParticipantPubkeys();
}
void HidingSigningProvider::SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const
{
m_provider->SetMuSig2SecNonce(id, std::move(nonce));
}
std::optional<std::reference_wrapper<MuSig2SecNonce>> HidingSigningProvider::GetMuSig2SecNonce(const uint256& session_id) const
{
return m_provider->GetMuSig2SecNonce(session_id);
}
void HidingSigningProvider::DeleteMuSig2Session(const uint256& session_id) const
{
m_provider->DeleteMuSig2Session(session_id);
}
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
@@ -94,6 +114,31 @@ std::vector<CPubKey> FlatSigningProvider::GetMuSig2ParticipantPubkeys(const CPub
return participant_pubkeys;
}
std::map<CPubKey, std::vector<CPubKey>> FlatSigningProvider::GetAllMuSig2ParticipantPubkeys() const
{
return aggregate_pubkeys;
}
void FlatSigningProvider::SetMuSig2SecNonce(const uint256& session_id, MuSig2SecNonce&& nonce) const
{
if (!Assume(musig2_secnonces)) return;
musig2_secnonces->emplace(session_id, std::move(nonce));
}
std::optional<std::reference_wrapper<MuSig2SecNonce>> FlatSigningProvider::GetMuSig2SecNonce(const uint256& session_id) const
{
if (!Assume(musig2_secnonces)) return std::nullopt;
const auto& it = musig2_secnonces->find(session_id);
if (it == musig2_secnonces->end()) return std::nullopt;
return it->second;
}
void FlatSigningProvider::DeleteMuSig2Session(const uint256& session_id) const
{
if (!Assume(musig2_secnonces)) return;
musig2_secnonces->erase(session_id);
}
FlatSigningProvider& FlatSigningProvider::Merge(FlatSigningProvider&& b)
{
scripts.merge(b.scripts);
@@ -102,6 +147,8 @@ FlatSigningProvider& FlatSigningProvider::Merge(FlatSigningProvider&& b)
origins.merge(b.origins);
tr_trees.merge(b.tr_trees);
aggregate_pubkeys.merge(b.aggregate_pubkeys);
// We shouldn't be merging 2 different sessions, just overwrite with b's sessions.
if (!musig2_secnonces) musig2_secnonces = b.musig2_secnonces;
return *this;
}

View File

@@ -9,11 +9,15 @@
#include <addresstype.h>
#include <attributes.h>
#include <key.h>
#include <musig.h>
#include <pubkey.h>
#include <script/keyorigin.h>
#include <script/script.h>
#include <sync.h>
#include <functional>
#include <optional>
struct ShortestVectorFirstComparator
{
bool operator()(const std::vector<unsigned char>& a, const std::vector<unsigned char>& b) const
@@ -162,6 +166,10 @@ public:
virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; }
virtual bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const { return false; }
virtual std::vector<CPubKey> GetMuSig2ParticipantPubkeys(const CPubKey& pubkey) const { return {}; }
virtual std::map<CPubKey, std::vector<CPubKey>> GetAllMuSig2ParticipantPubkeys() const {return {}; }
virtual void SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const {}
virtual std::optional<std::reference_wrapper<MuSig2SecNonce>> GetMuSig2SecNonce(const uint256& session_id) const { return std::nullopt; }
virtual void DeleteMuSig2Session(const uint256& session_id) const {}
bool GetKeyByXOnly(const XOnlyPubKey& pubkey, CKey& key) const
{
@@ -206,6 +214,10 @@ public:
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;
std::vector<CPubKey> GetMuSig2ParticipantPubkeys(const CPubKey& pubkey) const override;
std::map<CPubKey, std::vector<CPubKey>> GetAllMuSig2ParticipantPubkeys() const override;
void SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const override;
std::optional<std::reference_wrapper<MuSig2SecNonce>> GetMuSig2SecNonce(const uint256& session_id) const override;
void DeleteMuSig2Session(const uint256& session_id) const override;
};
struct FlatSigningProvider final : public SigningProvider
@@ -216,6 +228,7 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CKeyID, CKey> keys;
std::map<XOnlyPubKey, TaprootBuilder> tr_trees; /** Map from output key to Taproot tree (which can then make the TaprootSpendData */
std::map<CPubKey, std::vector<CPubKey>> aggregate_pubkeys; /** MuSig2 aggregate pubkeys */
std::map<uint256, MuSig2SecNonce>* musig2_secnonces{nullptr};
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
@@ -225,6 +238,10 @@ struct FlatSigningProvider final : public SigningProvider
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;
std::vector<CPubKey> GetMuSig2ParticipantPubkeys(const CPubKey& pubkey) const override;
std::map<CPubKey, std::vector<CPubKey>> GetAllMuSig2ParticipantPubkeys() const override;
void SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const override;
std::optional<std::reference_wrapper<MuSig2SecNonce>> GetMuSig2SecNonce(const uint256& session_id) const override;
void DeleteMuSig2Session(const uint256& session_id) const override;
FlatSigningProvider& Merge(FlatSigningProvider&& b) LIFETIMEBOUND;
};