Skip to content

Commit 5ee4e79

Browse files
committed
Merge bitcoin/bitcoin#31244: descriptors: MuSig2
5fe7915 doc: Add musig() example (Ava Chow) d576079 tests: Test musig() parsing (Ava Chow) a53924b descriptor: Parse musig() key expressions (Ava Chow) 9473e96 descriptors: Move DeriveType parsing into its own function (Ava Chow) 4af0dca descriptor: Add MuSigPubkeyProvider (Ava Chow) d00d954 Add MuSig2 Keyagg Cache helper functions (Ava Chow) 8ecea91 sign: Add GetMuSig2ParticipantPubkeys to SigningProvider (Ava Chow) fac0ee0 build: Enable secp256k1 musig module (Ava Chow) 1894f97 descriptors: Add PubkeyProvider::IsBIP32() (Ava Chow) 12bc1d0 util/string: Allow Split to include the separator (Ava Chow) 8811312 script/parsing: Allow Const to not skip the found constant (Ava Chow) 5fe4c66 XOnlyPubKey: Add GetCPubKeys (Ava Chow) Pull request description: Implements parsing of BIP 390 `musig()` descriptors. Split from #29675 ACKs for top commit: w0xlt: reACK bitcoin/bitcoin@5fe7915 rkrux: ACK 5fe7915 theStack: re-ACK 5fe7915 🎹 Sjors: ACK 5fe7915 Tree-SHA512: a5be6288e277187fb9a1e2adf4e9822b46b1b8380d732b2fabd53f317c839aecb1971b04410486cbd65047fbc20956675d4d676f56caa37a44ff0e4d12b9b081
2 parents aef2dbb + 5fe7915 commit 5ee4e79

14 files changed

+752
-51
lines changed

doc/descriptors.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Output descriptors currently support:
6969
- `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,sortedmulti_a(2,2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc))` describes a P2TR output with the `c6...` x-only pubkey as internal key, and a single `multi_a` script that needs 2 signatures with 2 specified x-only keys, which will be sorted lexicographically.
7070
- `wsh(sortedmulti(2,[6f53d49c/44h/1h/0h]tpubDDjsCRDQ9YzyaAq9rspCfq8RZFrWoBpYnLxK6sS2hS2yukqSczgcYiur8Scx4Hd5AZatxTuzMtJQJhchufv1FRFanLqUP7JHwusSSpfcEp2/0/*,[e6807791/44h/1h/0h]tpubDDAfvogaaAxaFJ6c15ht7Tq6ZmiqFYfrSmZsHu7tHXBgnjMZSHAeHSwhvjARNA6Qybon4ksPksjRbPDVp7yXA1KjTjSd5x18KHqbppnXP1s/0/*,[367c9cfa/44h/1h/0h]tpubDDtPnSgWYk8dDnaDwnof4ehcnjuL5VoUt1eW2MoAed1grPHuXPDnkX1fWMvXfcz3NqFxPbhqNZ3QBdYjLz2hABeM9Z2oqMR1Gt2HHYDoCgh/0/*))#av0kxgw0` describes a *2-of-3* multisig. For brevity, the internal "change" descriptor accompanying the above external "receiving" descriptor is not included here, but it typically differs only in the xpub derivation steps, ending in `/1/*` for change addresses.
7171
- `wsh(thresh(4,pk([7258e4f9/44h/1h/0h]tpubDCZrkQoEU3845aFKUu9VQBYWZtrTwxMzcxnBwKFCYXHD6gEXvtFcxddCCLFsEwmxQaG15izcHxj48SXg1QS5FQGMBx5Ak6deXKPAL7wauBU/0/*),s:pk([c80b1469/44h/1h/0h]tpubDD3UwwHoNUF4F3Vi5PiUVTc3ji1uThuRfFyBexTSHoAcHuWW2z8qEE2YujegcLtgthr3wMp3ZauvNG9eT9xfJyxXCfNty8h6rDBYU8UU1qq/0/*),s:pk([4e5024fe/44h/1h/0h]tpubDDLrpPymPLSCJyCMLQdmcWxrAWwsqqssm5NdxT2WSdEBPSXNXxwbeKtsHAyXPpLkhUyKovtZgCi47QxVpw9iVkg95UUgeevyAqtJ9dqBqa1/0/*),s:pk([3b1d1ee9/44h/1h/0h]tpubDCmDTANBWPzf6d8Ap1J5Ku7J1Ay92MpHMrEV7M5muWxCrTBN1g5f1NPcjMEL6dJHxbvEKNZtYCdowaSTN81DAyLsmv6w6xjJHCQNkxrsrfu/0/*),sln:after(840000),sln:after(1050000),sln:after(1260000)))#k28080kv` describes a Miniscript multisig with spending policy: `thresh(4,pk(key_1),pk(key_2),pk(key_3),pk(key_4),after(t1),after(t2),after(t3))` that starts as 4-of-4 and "decays" to 3-of-4, 2-of-4, and finally 1-of-4 at each future halvening block height. For brevity, the internal "change" descriptor accompanying the above external "receiving" descriptor is not included here, but it typically differs only in the xpub derivation steps, ending in `/1/*` for change addresses.
72+
- `tr(musig(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y)/0/*)` describes a MuSig2 multisig with key derivation. The internal keys are derived at `m/0/*` from the aggregate key computed from the 2 participants.
7273

7374
## Reference
7475

src/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ message("Configuring secp256k1 subtree...")
3131
set(SECP256K1_DISABLE_SHARED ON CACHE BOOL "" FORCE)
3232
set(SECP256K1_ENABLE_MODULE_ECDH OFF CACHE BOOL "" FORCE)
3333
set(SECP256K1_ENABLE_MODULE_RECOVERY ON CACHE BOOL "" FORCE)
34-
set(SECP256K1_ENABLE_MODULE_MUSIG OFF CACHE BOOL "" FORCE)
34+
set(SECP256K1_ENABLE_MODULE_MUSIG ON CACHE BOOL "" FORCE)
3535
set(SECP256K1_BUILD_BENCHMARK OFF CACHE BOOL "" FORCE)
3636
set(SECP256K1_BUILD_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE)
3737
set(SECP256K1_BUILD_EXHAUSTIVE_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE)
@@ -161,6 +161,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
161161
key.cpp
162162
key_io.cpp
163163
merkleblock.cpp
164+
musig.cpp
164165
net_permissions.cpp
165166
net_types.cpp
166167
netaddress.cpp

src/musig.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) 2024-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <musig.h>
6+
7+
#include <secp256k1_musig.h>
8+
9+
bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
10+
{
11+
// Parse the pubkeys
12+
std::vector<secp256k1_pubkey> secp_pubkeys;
13+
std::vector<const secp256k1_pubkey*> pubkey_ptrs;
14+
for (const CPubKey& pubkey : pubkeys) {
15+
if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {
16+
return false;
17+
}
18+
}
19+
pubkey_ptrs.reserve(secp_pubkeys.size());
20+
for (const secp256k1_pubkey& p : secp_pubkeys) {
21+
pubkey_ptrs.push_back(&p);
22+
}
23+
24+
// Aggregate the pubkey
25+
if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) {
26+
return false;
27+
}
28+
return true;
29+
}
30+
31+
std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
32+
{
33+
// Get the plain aggregated pubkey
34+
secp256k1_pubkey agg_pubkey;
35+
if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {
36+
return std::nullopt;
37+
}
38+
39+
// Turn into CPubKey
40+
unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];
41+
size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;
42+
secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);
43+
return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
44+
}
45+
46+
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
47+
{
48+
secp256k1_musig_keyagg_cache keyagg_cache;
49+
if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
50+
return std::nullopt;
51+
}
52+
return GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
53+
}

src/musig.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2024-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or https://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_MUSIG_H
6+
#define BITCOIN_MUSIG_H
7+
8+
#include <pubkey.h>
9+
10+
#include <optional>
11+
#include <vector>
12+
13+
struct secp256k1_musig_keyagg_cache;
14+
15+
//! MuSig2 chaincode as defined by BIP 328
16+
using namespace util::hex_literals;
17+
constexpr uint256 MUSIG_CHAINCODE{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8};
18+
19+
//! Create a secp256k1_musig_keyagg_cache from the pubkeys in their current order. This is necessary for most MuSig2 operations
20+
bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache);
21+
//! Retrieve the full aggregate pubkey from the secp256k1_musig_keyagg_cache
22+
std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& cache);
23+
//! Compute the full aggregate pubkey from the given participant pubkeys in their current order
24+
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys);
25+
26+
#endif // BITCOIN_MUSIG_H

src/pubkey.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,20 +197,26 @@ constexpr XOnlyPubKey XOnlyPubKey::NUMS_H{
197197
[]() consteval { return XOnlyPubKey{"50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"_hex_u8}; }(),
198198
};
199199

200-
std::vector<CKeyID> XOnlyPubKey::GetKeyIDs() const
200+
std::vector<CPubKey> XOnlyPubKey::GetCPubKeys() const
201201
{
202-
std::vector<CKeyID> out;
203-
// For now, use the old full pubkey-based key derivation logic. As it is indexed by
204-
// Hash160(full pubkey), we need to return both a version prefixed with 0x02, and one
205-
// with 0x03.
202+
std::vector<CPubKey> out;
206203
unsigned char b[33] = {0x02};
207204
std::copy(m_keydata.begin(), m_keydata.end(), b + 1);
208205
CPubKey fullpubkey;
209206
fullpubkey.Set(b, b + 33);
210-
out.push_back(fullpubkey.GetID());
207+
out.push_back(fullpubkey);
211208
b[0] = 0x03;
212209
fullpubkey.Set(b, b + 33);
213-
out.push_back(fullpubkey.GetID());
210+
out.push_back(fullpubkey);
211+
return out;
212+
}
213+
214+
std::vector<CKeyID> XOnlyPubKey::GetKeyIDs() const
215+
{
216+
std::vector<CKeyID> out;
217+
for (const CPubKey& pk : GetCPubKeys()) {
218+
out.push_back(pk.GetID());
219+
}
214220
return out;
215221
}
216222

src/pubkey.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,13 @@ class XOnlyPubKey
283283
std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const;
284284

285285
/** Returns a list of CKeyIDs for the CPubKeys that could have been used to create this XOnlyPubKey.
286+
* As the CKeyID is the Hash160(full pubkey), the produced CKeyIDs are for the versions of this
287+
* XOnlyPubKey with 0x02 and 0x03 prefixes.
286288
* This is needed for key lookups since keys are indexed by CKeyID.
287289
*/
288290
std::vector<CKeyID> GetKeyIDs() const;
291+
/** Returns this XOnlyPubKey with 0x02 and 0x03 prefixes */
292+
std::vector<CPubKey> GetCPubKeys() const;
289293

290294
CPubKey GetEvenCorrespondingCPubKey() const;
291295

0 commit comments

Comments
 (0)