From 225b3adbf53e4dfde32a1f798cde30cc41998e3c Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 29 Jan 2024 17:32:02 -0500 Subject: [PATCH 01/10] XOnlyPubKey: Add GetCPubKeys We need to retrieve the even and odd compressed pubkeys for xonly pubkeys, so add a function to do that. Also reuse it in GetKeyIDs. --- src/pubkey.cpp | 25 +++++++++++++++++-------- src/pubkey.h | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pubkey.cpp b/src/pubkey.cpp index a4ca9a170a9..4c77f065b49 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -197,20 +197,29 @@ constexpr XOnlyPubKey XOnlyPubKey::NUMS_H{ []() consteval { return XOnlyPubKey{"50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"_hex_u8}; }(), }; +std::vector XOnlyPubKey::GetCPubKeys() const +{ + std::vector out; + unsigned char b[33] = {0x02}; + std::copy(m_keydata.begin(), m_keydata.end(), b + 1); + CPubKey fullpubkey; + fullpubkey.Set(b, b + 33); + out.push_back(fullpubkey); + b[0] = 0x03; + fullpubkey.Set(b, b + 33); + out.push_back(fullpubkey); + return out; +} + std::vector XOnlyPubKey::GetKeyIDs() const { std::vector out; // For now, use the old full pubkey-based key derivation logic. As it is indexed by // Hash160(full pubkey), we need to return both a version prefixed with 0x02, and one // with 0x03. - unsigned char b[33] = {0x02}; - std::copy(m_keydata.begin(), m_keydata.end(), b + 1); - CPubKey fullpubkey; - fullpubkey.Set(b, b + 33); - out.push_back(fullpubkey.GetID()); - b[0] = 0x03; - fullpubkey.Set(b, b + 33); - out.push_back(fullpubkey.GetID()); + for (const CPubKey& pk : GetCPubKeys()) { + out.push_back(pk.GetID()); + } return out; } diff --git a/src/pubkey.h b/src/pubkey.h index cbc827dc606..3a182f3a847 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -286,6 +286,7 @@ public: * This is needed for key lookups since keys are indexed by CKeyID. */ std::vector GetKeyIDs() const; + std::vector GetCPubKeys() const; CPubKey GetEvenCorrespondingCPubKey() const; From fe02d7cb237c00de6abe1776b3101342ffddf757 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 15 Jan 2024 17:08:47 -0500 Subject: [PATCH 02/10] script/parsing: Allow Const to not skip the found constant --- src/script/parsing.cpp | 4 ++-- src/script/parsing.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/script/parsing.cpp b/src/script/parsing.cpp index 254ced6f0b0..dd79f67e58e 100644 --- a/src/script/parsing.cpp +++ b/src/script/parsing.cpp @@ -12,10 +12,10 @@ namespace script { -bool Const(const std::string& str, std::span& sp) +bool Const(const std::string& str, std::span& sp, bool skip) { if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) { - sp = sp.subspan(str.size()); + if (skip) sp = sp.subspan(str.size()); return true; } return false; diff --git a/src/script/parsing.h b/src/script/parsing.h index 8986aa57d07..462a42e7b5e 100644 --- a/src/script/parsing.h +++ b/src/script/parsing.h @@ -13,10 +13,10 @@ namespace script { /** Parse a constant. * - * If sp's initial part matches str, sp is updated to skip that part, and true is returned. + * If sp's initial part matches str, sp is optionally updated to skip that part, and true is returned. * Otherwise sp is unmodified and false is returned. */ -bool Const(const std::string& str, std::span& sp); +bool Const(const std::string& str, std::span& sp, bool skip = true); /** Parse a function call. * From 4a1eeee27a64c4be7293740f8fff839879b88d86 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 14 Apr 2025 13:31:31 -0700 Subject: [PATCH 03/10] util/string: Allow Split to include the separator When splitting a string, sometimes the separator needs to be included. Split will now optionally include the separator on the left side of the splits, i.e. it appears at the end of the splits, except for the last one. --- src/util/string.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/util/string.h b/src/util/string.h index 3075e46abbf..f954d74dbae 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -100,18 +100,27 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin * * If sep does not occur in sp, a singleton with the entirety of sp is returned. * + * @param[in] include_sep Whether to include the separator at the end of the left side of the splits. + * * Note that this function does not care about braces, so splitting * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}. + * + * If include_sep == true, splitting "foo(bar(1),2),3) on ',' + * will return {"foo(bar(1),", "2),", "3)"}. */ template > -std::vector Split(const std::span& sp, std::string_view separators) +std::vector Split(const std::span& sp, std::string_view separators, bool include_sep = false) { std::vector ret; auto it = sp.begin(); auto start = it; while (it != sp.end()) { if (separators.find(*it) != std::string::npos) { - ret.emplace_back(start, it); + if (include_sep) { + ret.emplace_back(start, it + 1); + } else { + ret.emplace_back(start, it); + } start = it + 1; } ++it; @@ -128,9 +137,9 @@ std::vector Split(const std::span& sp, std::string_view separator * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}. */ template > -std::vector Split(const std::span& sp, char sep) +std::vector Split(const std::span& sp, char sep, bool include_sep = false) { - return Split(sp, std::string_view{&sep, 1}); + return Split(sp, std::string_view{&sep, 1}, include_sep); } [[nodiscard]] inline std::vector SplitString(std::string_view str, char sep) From 26c25fed919d2520f561a891236220d54880b079 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 15 Jan 2024 17:09:22 -0500 Subject: [PATCH 04/10] descriptors: Add PubkeyProvider::IsBIP32() --- src/script/descriptor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 0b7cd4a4dc1..8d94ef9c0cb 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -221,6 +221,9 @@ public: /** Make a deep copy of this PubkeyProvider */ virtual std::unique_ptr Clone() const = 0; + + /** Whether this PubkeyProvider can be a BIP 32 extended key that can be derived from */ + virtual bool IsBIP32() const = 0; }; class OriginPubkeyProvider final : public PubkeyProvider @@ -251,6 +254,7 @@ public: } bool IsRange() const override { return m_provider->IsRange(); } size_t GetSize() const override { return m_provider->GetSize(); } + bool IsBIP32() const override { return m_provider->IsBIP32(); } std::string ToString(StringType type) const override { return "[" + OriginString(type) + "]" + m_provider->ToString(type); } bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override { @@ -319,6 +323,7 @@ public: } bool IsRange() const override { return false; } size_t GetSize() const override { return m_pubkey.size(); } + bool IsBIP32() const override { return false; } std::string ToString(StringType type) const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); } bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override { @@ -406,6 +411,7 @@ public: BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive, bool apostrophe) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive), m_apostrophe(apostrophe) {} bool IsRange() const override { return m_derive != DeriveType::NO; } size_t GetSize() const override { return 33; } + bool IsBIP32() const override { return true; } std::optional GetPubKey(int pos, const SigningProvider& arg, FlatSigningProvider& out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override { KeyOriginInfo info; From 2d4f2f639f0f8fdabc83bbff4950f91738cb2e95 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Tue, 5 Nov 2024 15:09:55 -0500 Subject: [PATCH 05/10] build: Enable secp256k1 musig module --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dac8872080a..741a42ae975 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,7 +31,7 @@ message("Configuring secp256k1 subtree...") set(SECP256K1_DISABLE_SHARED ON CACHE BOOL "" FORCE) set(SECP256K1_ENABLE_MODULE_ECDH OFF CACHE BOOL "" FORCE) set(SECP256K1_ENABLE_MODULE_RECOVERY ON CACHE BOOL "" FORCE) -set(SECP256K1_ENABLE_MODULE_MUSIG OFF CACHE BOOL "" FORCE) +set(SECP256K1_ENABLE_MODULE_MUSIG ON CACHE BOOL "" FORCE) set(SECP256K1_BUILD_BENCHMARK OFF CACHE BOOL "" FORCE) set(SECP256K1_BUILD_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE) set(SECP256K1_BUILD_EXHAUSTIVE_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE) From 7da3e7bdd050d864a7b46fabb285aefa29ce61fc Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 22 Jan 2024 16:43:26 -0500 Subject: [PATCH 06/10] sign: Add GetAggregateParticipantPubkeys to SigningProvider --- src/script/signingprovider.cpp | 13 +++++++++++++ src/script/signingprovider.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index 07a1956eabc..a64f767d6e0 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -52,6 +52,11 @@ bool HidingSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, Tap { return m_provider->GetTaprootBuilder(output_key, builder); } +std::vector HidingSigningProvider::GetAggregateParticipantPubkeys(const CPubKey& pubkey) const +{ + if (m_hide_origin) return {}; + return m_provider->GetAggregateParticipantPubkeys(pubkey); +} 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); } @@ -82,6 +87,13 @@ bool FlatSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, Tapro return LookupHelper(tr_trees, output_key, builder); } +std::vector FlatSigningProvider::GetAggregateParticipantPubkeys(const CPubKey& pubkey) const +{ + const auto& it = aggregate_pubkeys.find(pubkey); + if (it == aggregate_pubkeys.end()) return {}; + return it->second; +} + FlatSigningProvider& FlatSigningProvider::Merge(FlatSigningProvider&& b) { scripts.merge(b.scripts); @@ -89,6 +101,7 @@ FlatSigningProvider& FlatSigningProvider::Merge(FlatSigningProvider&& b) keys.merge(b.keys); origins.merge(b.origins); tr_trees.merge(b.tr_trees); + aggregate_pubkeys.merge(b.aggregate_pubkeys); return *this; } diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index f4c823be393..f85d424f19f 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -161,6 +161,7 @@ public: virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; } 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 GetAggregateParticipantPubkeys(const CPubKey& pubkey) const { return {}; } bool GetKeyByXOnly(const XOnlyPubKey& pubkey, CKey& key) const { @@ -204,6 +205,7 @@ public: bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override; bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override; + std::vector GetAggregateParticipantPubkeys(const CPubKey& pubkey) const override; }; struct FlatSigningProvider final : public SigningProvider @@ -213,6 +215,7 @@ struct FlatSigningProvider final : public SigningProvider std::map> origins; std::map keys; std::map tr_trees; /** Map from output key to Taproot tree (which can then make the TaprootSpendData */ + std::map> aggregate_pubkeys; /** MuSig2 aggregate pubkeys */ bool GetCScript(const CScriptID& scriptid, CScript& script) const override; bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; @@ -221,6 +224,7 @@ struct FlatSigningProvider final : public SigningProvider bool GetKey(const CKeyID& keyid, CKey& key) const override; bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override; bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override; + std::vector GetAggregateParticipantPubkeys(const CPubKey& pubkey) const override; FlatSigningProvider& Merge(FlatSigningProvider&& b) LIFETIMEBOUND; }; From 2e6dcdbc8055660a2e20ba81b62b7d26ae0ccb05 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 22 Jan 2024 15:18:28 -0500 Subject: [PATCH 07/10] Add MuSig2 Keyagg Cache class and functions - MuSig2KeyAggCache contains a MuSig2KeyAggCacheImpl which has the secp256ke_musig_keyagg_cache object to avoid having to link libsecp256k1 everywhere. - GetMuSig2KeyAggCache creates the MuSig2KeyAggCache from a std::vector - GetCPubKeyFromMuSig2KeyAggCache creates a CPubKey from a cache created with GetMuSig2KeyAggCache - MuSig2AggregatePubKeys does the two above functions together. --- src/CMakeLists.txt | 1 + src/musig.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++ src/musig.h | 18 ++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/musig.cpp create mode 100644 src/musig.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 741a42ae975..ef4fc0bb0d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,6 +161,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL key.cpp key_io.cpp merkleblock.cpp + musig.cpp net_permissions.cpp net_types.cpp netaddress.cpp diff --git a/src/musig.cpp b/src/musig.cpp new file mode 100644 index 00000000000..b3329543127 --- /dev/null +++ b/src/musig.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2024-present The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +bool GetMuSig2KeyAggCache(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache) +{ + // Parse the pubkeys + std::vector secp_pubkeys; + std::vector pubkey_ptrs; + for (const CPubKey& pubkey : pubkeys) { + if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) { + return false; + } + } + pubkey_ptrs.reserve(secp_pubkeys.size()); + for (const secp256k1_pubkey& p : secp_pubkeys) { + pubkey_ptrs.push_back(&p); + } + + // Aggregate the pubkey + if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) { + return false; + } + return true; +} + +std::optional GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache) +{ + // Get the plain aggregated pubkey + secp256k1_pubkey agg_pubkey; + if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) { + return std::nullopt; + } + + // Turn into CPubKey + unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE]; + size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE; + secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED); + return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len); +} + +std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys) +{ + secp256k1_musig_keyagg_cache keyagg_cache; + if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) { + return std::nullopt; + } + return GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache); +} diff --git a/src/musig.h b/src/musig.h new file mode 100644 index 00000000000..a3f205bd3a0 --- /dev/null +++ b/src/musig.h @@ -0,0 +1,18 @@ +// Copyright (c) 2024-present The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_MUSIG_H +#define BITCOIN_MUSIG_H + +#include + +#include + +struct secp256k1_musig_keyagg_cache; + +bool GetMuSig2KeyAggCache(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache); +std::optional GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& cache); +std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys); + +#endif // BITCOIN_MUSIG_H From b70531cac94aa125116093c28c65f4c203dca772 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 15 Jan 2024 17:10:08 -0500 Subject: [PATCH 08/10] descriptor: Add MuSigPubkeyProvider --- src/script/descriptor.cpp | 214 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 8d94ef9c0cb..8672aae277e 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include