|
7 | 7 | #include <hash.h>
|
8 | 8 | #include <key_io.h>
|
9 | 9 | #include <pubkey.h>
|
| 10 | +#include <musig.h> |
10 | 11 | #include <script/miniscript.h>
|
11 | 12 | #include <script/parsing.h>
|
12 | 13 | #include <script/script.h>
|
@@ -583,6 +584,219 @@ class BIP32PubkeyProvider final : public PubkeyProvider
|
583 | 584 | }
|
584 | 585 | };
|
585 | 586 |
|
| 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 | + |
586 | 800 | /** Base class for all Descriptor implementations. */
|
587 | 801 | class DescriptorImpl : public Descriptor
|
588 | 802 | {
|
|
0 commit comments