Merge bitcoin/bitcoin#31247: psbt: MuSig2 Fields

e261eb8d50 tests: Add BIP 373 test vectors (Ava Chow)
26370c68d0 rpc: Include MuSig2 fields in decodepsbt (Ava Chow)
ff3d460898 psbt: Implement un/ser of musig2 fields (Ava Chow)

Pull request description:

  Implements un/serialization of MuSig2 PSBT fields and prepares PSBT to be able to sign for MuSig2 inputs.

  Split from #29675

ACKs for top commit:
  fjahr:
    re-ACK e261eb8d50
  theStack:
    re-ACK e261eb8d50
  rkrux:
    tACK e261eb8d50

Tree-SHA512: bb852ad074978847ac4dc656332025e2d4d1025d4283537b89618c7cadd61a8ecd2eff24779b8a014bc8d7b431125060449768192fa05ad0577f29e3c64b2374
This commit is contained in:
merge-script
2025-04-18 16:44:33 -04:00
3 changed files with 319 additions and 1 deletions

View File

@@ -916,6 +916,37 @@ const RPCResult decodepsbt_inputs{
}},
{RPCResult::Type::STR_HEX, "taproot_internal_key", /*optional=*/ true, "The hex-encoded Taproot x-only internal key"},
{RPCResult::Type::STR_HEX, "taproot_merkle_root", /*optional=*/ true, "The hex-encoded Taproot merkle root"},
{RPCResult::Type::ARR, "musig2_participant_pubkeys", /*optional=*/true, "",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The compressed aggregate public key for which the participants create."},
{RPCResult::Type::ARR, "participant_pubkeys", "",
{
{RPCResult::Type::STR_HEX, "pubkey", "The compressed public keys that are aggregated for aggregate_pubkey."},
}},
}},
}},
{RPCResult::Type::ARR, "musig2_pubnonces", /*optional=*/true, "",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "participant_pubkey", "The compressed public key of the participant that created this pubnonce."},
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The compressed aggregate public key for which this pubnonce is for."},
{RPCResult::Type::STR_HEX, "leaf_hash", /*optional=*/true, "The hash of the leaf script that contains the aggregate pubkey being signed for. Omitted when signing for the internal key."},
{RPCResult::Type::STR_HEX, "pubnonce", "The public nonce itself."},
}},
}},
{RPCResult::Type::ARR, "musig2_partial_sigs", /*optional=*/true, "",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "participant_pubkey", "The compressed public key of the participant that created this partial signature."},
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The compressed aggregate public key for which this partial signature is for."},
{RPCResult::Type::STR_HEX, "leaf_hash", /*optional=*/true, "The hash of the leaf script that contains the aggregate pubkey being signed for. Omitted when signing for the internal key."},
{RPCResult::Type::STR_HEX, "partial_sig", "The partial signature itself."},
}},
}},
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
@@ -983,6 +1014,17 @@ const RPCResult decodepsbt_outputs{
}},
}},
}},
{RPCResult::Type::ARR, "musig2_participant_pubkeys", /*optional=*/true, "",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The compressed aggregate public key for which the participants create."},
{RPCResult::Type::ARR, "participant_pubkeys", "",
{
{RPCResult::Type::STR_HEX, "pubkey", "The compressed public keys that are aggregated for aggregate_pubkey."},
}},
}},
}},
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown output fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
@@ -1304,6 +1346,52 @@ static RPCHelpMan decodepsbt()
in.pushKV("taproot_merkle_root", HexStr(input.m_tap_merkle_root));
}
// Write MuSig2 fields
if (!input.m_musig2_participants.empty()) {
UniValue musig_pubkeys(UniValue::VARR);
for (const auto& [agg, parts] : input.m_musig2_participants) {
UniValue musig_part(UniValue::VOBJ);
musig_part.pushKV("aggregate_pubkey", HexStr(agg));
UniValue part_pubkeys(UniValue::VARR);
for (const auto& pub : parts) {
part_pubkeys.push_back(HexStr(pub));
}
musig_part.pushKV("participant_pubkeys", part_pubkeys);
musig_pubkeys.push_back(musig_part);
}
in.pushKV("musig2_participant_pubkeys", musig_pubkeys);
}
if (!input.m_musig2_pubnonces.empty()) {
UniValue musig_pubnonces(UniValue::VARR);
for (const auto& [agg_lh, part_pubnonce] : input.m_musig2_pubnonces) {
const auto& [agg, lh] = agg_lh;
for (const auto& [part, pubnonce] : part_pubnonce) {
UniValue info(UniValue::VOBJ);
info.pushKV("participant_pubkey", HexStr(part));
info.pushKV("aggregate_pubkey", HexStr(agg));
if (!lh.IsNull()) info.pushKV("leaf_hash", HexStr(lh));
info.pushKV("pubnonce", HexStr(pubnonce));
musig_pubnonces.push_back(info);
}
}
in.pushKV("musig2_pubnonces", musig_pubnonces);
}
if (!input.m_musig2_partial_sigs.empty()) {
UniValue musig_partial_sigs(UniValue::VARR);
for (const auto& [agg_lh, part_psig] : input.m_musig2_partial_sigs) {
const auto& [agg, lh] = agg_lh;
for (const auto& [part, psig] : part_psig) {
UniValue info(UniValue::VOBJ);
info.pushKV("participant_pubkey", HexStr(part));
info.pushKV("aggregate_pubkey", HexStr(agg));
if (!lh.IsNull()) info.pushKV("leaf_hash", HexStr(lh));
info.pushKV("partial_sig", HexStr(psig));
musig_partial_sigs.push_back(info);
}
}
in.pushKV("musig2_partial_sigs", musig_partial_sigs);
}
// Proprietary
if (!input.m_proprietary.empty()) {
UniValue proprietary(UniValue::VARR);
@@ -1399,6 +1487,22 @@ static RPCHelpMan decodepsbt()
out.pushKV("taproot_bip32_derivs", std::move(keypaths));
}
// Write MuSig2 fields
if (!output.m_musig2_participants.empty()) {
UniValue musig_pubkeys(UniValue::VARR);
for (const auto& [agg, parts] : output.m_musig2_participants) {
UniValue musig_part(UniValue::VOBJ);
musig_part.pushKV("aggregate_pubkey", HexStr(agg));
UniValue part_pubkeys(UniValue::VARR);
for (const auto& pub : parts) {
part_pubkeys.push_back(HexStr(pub));
}
musig_part.pushKV("participant_pubkeys", part_pubkeys);
musig_pubkeys.push_back(musig_part);
}
out.pushKV("musig2_participant_pubkeys", musig_pubkeys);
}
// Proprietary
if (!output.m_proprietary.empty()) {
UniValue proprietary(UniValue::VARR);