Skip to content

Commit f19462e

Browse files
committed
descriptor: Add MuSigPubkeyProvider
1 parent e8f76f6 commit f19462e

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed

src/script/descriptor.cpp

+214
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <hash.h>
88
#include <key_io.h>
99
#include <pubkey.h>
10+
#include <musig.h>
1011
#include <script/miniscript.h>
1112
#include <script/parsing.h>
1213
#include <script/script.h>
@@ -583,6 +584,219 @@ class BIP32PubkeyProvider final : public PubkeyProvider
583584
}
584585
};
585586

587+
/** PubkeyProvider for a musig() expression */
588+
class MuSigPubkeyProvider final : public PubkeyProvider
589+
{
590+
private:
591+
//! PubkeyProvider for the participants
592+
const std::vector<std::unique_ptr<PubkeyProvider>> m_participants;
593+
//! Derivation path if this is ranged
594+
const KeyPath m_path;
595+
//! PubkeyProvider for the aggregate pubkey if it can be cached (i.e. participants are not ranged)
596+
mutable std::unique_ptr<PubkeyProvider> m_aggregate_provider;
597+
mutable std::optional<CPubKey> m_aggregate_pubkey;
598+
const DeriveType m_derive;
599+
600+
bool IsRangedDerivation() const { return m_derive != DeriveType::NO; }
601+
bool IsRangedParticipants() const
602+
{
603+
for (const auto& pubkey : m_participants) {
604+
if (pubkey->IsRange()) return true;
605+
}
606+
return false;
607+
}
608+
609+
public:
610+
MuSigPubkeyProvider(
611+
uint32_t exp_index,
612+
std::vector<std::unique_ptr<PubkeyProvider>> providers,
613+
KeyPath path,
614+
DeriveType derive
615+
)
616+
: PubkeyProvider(exp_index),
617+
m_participants(std::move(providers)),
618+
m_path(std::move(path)),
619+
m_derive(derive)
620+
{}
621+
622+
std::optional<CPubKey> GetPubKey(int pos, const SigningProvider& arg, FlatSigningProvider& out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
623+
{
624+
// If the participants are not ranged, we can compute and cache the aggregate pubkey by creating a PubkeyProvider for it
625+
if (!m_aggregate_provider && !IsRangedParticipants()) {
626+
// Retrieve the pubkeys from the providers
627+
std::vector<CPubKey> pubkeys;
628+
for (const auto& prov : m_participants) {
629+
FlatSigningProvider dummy;
630+
std::optional<CPubKey> pubkey = prov->GetPubKey(0, arg, dummy, read_cache, write_cache);
631+
if (!pubkey.has_value()) {
632+
return std::nullopt;
633+
}
634+
pubkeys.push_back(pubkey.value());
635+
}
636+
std::sort(pubkeys.begin(), pubkeys.end());
637+
638+
// Aggregate the pubkey
639+
m_aggregate_pubkey = MuSig2AggregatePubkeys(pubkeys);
640+
Assert(m_aggregate_pubkey.has_value());
641+
642+
// Make our pubkey provider
643+
if (m_derive != DeriveType::NO || !m_path.empty()) {
644+
// Make the synthetic xpub and construct the BIP32PubkeyProvider
645+
CExtPubKey extpub;
646+
extpub.nDepth = 0;
647+
std::memset(extpub.vchFingerprint, 0, 4);
648+
extpub.nChild = 0;
649+
extpub.chaincode.FromHex("6589e367712c6200e367717145cb322d76576bc3248959c474f9a602ca878086");
650+
extpub.pubkey = m_aggregate_pubkey.value();
651+
652+
m_aggregate_provider = std::make_unique<BIP32PubkeyProvider>(m_expr_index, extpub, m_path, m_derive, /*apostrophe=*/false);
653+
} else {
654+
m_aggregate_provider = std::make_unique<ConstPubkeyProvider>(m_expr_index, m_aggregate_pubkey.value(), /*xonly=*/false);
655+
}
656+
}
657+
658+
// Retrieve all participant pubkeys
659+
std::vector<CPubKey> pubkeys;
660+
for (const auto& prov : m_participants) {
661+
std::optional<CPubKey> pub = prov->GetPubKey(pos, arg, out, read_cache, write_cache);
662+
if (!pub) return std::nullopt;
663+
pubkeys.emplace_back(*pub);
664+
}
665+
std::sort(pubkeys.begin(), pubkeys.end());
666+
667+
CPubKey pubout;
668+
if (m_aggregate_provider) {
669+
// When we have a cached aggregate key, we are either returning it or deriving from it
670+
// Either way, we can passthrough to it's GetPubKey
671+
std::optional<CPubKey> pub = m_aggregate_provider->GetPubKey(pos, arg, out, read_cache, write_cache);
672+
if (!pub) return std::nullopt;
673+
pubout = *pub;
674+
out.aggregate_pubkeys.emplace(m_aggregate_pubkey.value(), pubkeys);
675+
} else if (IsRangedParticipants()) {
676+
// Derive participants and compute new aggregate key
677+
std::optional<CPubKey> aggregate_pubkey = MuSig2AggregatePubkeys(pubkeys);
678+
if (!aggregate_pubkey) return std::nullopt;
679+
pubout = *aggregate_pubkey;
680+
681+
KeyOriginInfo info;
682+
CKeyID keyid = aggregate_pubkey->GetID();
683+
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
684+
out.origins.emplace(keyid, std::make_pair(*aggregate_pubkey, info));
685+
out.pubkeys.emplace(aggregate_pubkey->GetID(), *aggregate_pubkey);
686+
out.aggregate_pubkeys.emplace(pubout, pubkeys);
687+
}
688+
689+
Assert(pubout.IsValid());
690+
return pubout;
691+
}
692+
bool IsRange() const override { return IsRangedDerivation() || IsRangedParticipants(); }
693+
// musig() expressions can only be used in tr() contexts which have 32 byte xonly pubkeys
694+
size_t GetSize() const override { return 32; }
695+
696+
std::string ToString(StringType type=StringType::PUBLIC) const override
697+
{
698+
std::string out = "musig(";
699+
for (size_t i = 0; i < m_participants.size(); ++i) {
700+
const auto& pubkey = m_participants.at(i);
701+
if (i) out += ",";
702+
std::string tmp;
703+
switch (type) {
704+
case StringType::PUBLIC:
705+
tmp = pubkey->ToString();
706+
break;
707+
case StringType::COMPAT:
708+
tmp = pubkey->ToString(PubkeyProvider::StringType::COMPAT);
709+
break;
710+
}
711+
out += tmp;
712+
}
713+
out += ")";
714+
out += FormatHDKeypath(m_path, /*apostrophe=*/true);
715+
if (IsRangedDerivation()) {
716+
out += "/*";
717+
}
718+
return out;
719+
}
720+
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
721+
{
722+
bool any_privkeys = false;
723+
out = "musig(";
724+
for (size_t i = 0; i < m_participants.size(); ++i) {
725+
const auto& pubkey = m_participants.at(i);
726+
if (i) out += ",";
727+
std::string tmp;
728+
if (pubkey->ToPrivateString(arg, tmp)) {
729+
any_privkeys = true;
730+
out += tmp;
731+
} else {
732+
out += pubkey->ToString();
733+
}
734+
}
735+
out += ")";
736+
out += FormatHDKeypath(m_path, /*apostrophe=*/true);
737+
if (IsRangedDerivation()) {
738+
out += "/*";
739+
}
740+
if (!any_privkeys) out.clear();
741+
return any_privkeys;
742+
}
743+
bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr) const override
744+
{
745+
out = "musig(";
746+
for (size_t i = 0; i < m_participants.size(); ++i) {
747+
const auto& pubkey = m_participants.at(i);
748+
if (i) out += ",";
749+
std::string tmp;
750+
if (!pubkey->ToNormalizedString(arg, tmp)) {
751+
return false;
752+
}
753+
out += tmp;
754+
}
755+
out += ")";
756+
out += FormatHDKeypath(m_path, /*apostrophe=*/true);
757+
if (IsRangedDerivation()) {
758+
out += "/*";
759+
}
760+
return true;
761+
}
762+
763+
void GetPrivKey(int pos, const SigningProvider& arg, FlatSigningProvider& out) const override
764+
{
765+
// Get the private keys for all participants
766+
// If there is participant derivation, it will be done.
767+
// If there is not, then the participant privkeys will be included directly
768+
for (const auto& prov : m_participants) {
769+
prov->GetPrivKey(pos, arg, out);
770+
}
771+
}
772+
std::optional<CPubKey> GetRootPubKey() const override
773+
{
774+
return std::nullopt;
775+
}
776+
std::optional<CExtPubKey> GetRootExtPubKey() const override
777+
{
778+
return std::nullopt;
779+
}
780+
std::unique_ptr<PubkeyProvider> Clone() const override
781+
{
782+
std::vector<std::unique_ptr<PubkeyProvider>> providers;
783+
providers.reserve(m_participants.size());
784+
for (const std::unique_ptr<PubkeyProvider>& p : m_participants) {
785+
providers.emplace_back(p->Clone());
786+
}
787+
return std::make_unique<MuSigPubkeyProvider>(m_expr_index, std::move(providers), m_path, m_derive);
788+
}
789+
bool IsBIP32() const override
790+
{
791+
// musig() can only be a BIP 32 key if all participants are bip32 too
792+
bool all_bip32 = true;
793+
for (const auto& pk : m_participants) {
794+
all_bip32 &= pk->IsBIP32();
795+
}
796+
return all_bip32;
797+
}
798+
};
799+
586800
/** Base class for all Descriptor implementations. */
587801
class DescriptorImpl : public Descriptor
588802
{

0 commit comments

Comments
 (0)