mirror of
https://github.com/btcsuite/btcd.git
synced 2025-03-11 17:57:50 +01:00
253 lines
6.5 KiB
Go
253 lines
6.5 KiB
Go
|
// Copyright 2013-2016 The btcsuite developers
|
||
|
// Copyright (c) 2015-2021 The Decred developers
|
||
|
// Use of this source code is governed by an ISC
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package musig2
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/sha256"
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||
|
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||
|
)
|
||
|
|
||
|
// TestMuSig2SgnTestVectors tests that this implementation of musig2 matches
|
||
|
// the secp256k1-zkp test vectors.
|
||
|
func TestMuSig2SignTestVectors(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
key1Bytes, _ = hex.DecodeString("F9308A019258C31049344F85F89D5229B53" +
|
||
|
"1C845836F99B08601F113BCE036F9")
|
||
|
key2Bytes, _ = hex.DecodeString("DFF1D77F2A671C5F36183726DB2341BE58F" +
|
||
|
"EAE1DA2DECED843240F7B502BA659")
|
||
|
key3Bytes, _ = hex.DecodeString("3590A94E768F8E1815C2F24B4D80A8E3149" +
|
||
|
"316C3518CE7B7AD338368D038CA66")
|
||
|
|
||
|
testKeys = [][]byte{key1Bytes, key2Bytes, key3Bytes}
|
||
|
|
||
|
keyCombo1, _ = hex.DecodeString("E5830140512195D74C8307E39637CBE5FB730EBEAB80EC514CF88A877CEEEE0B")
|
||
|
keyCombo2, _ = hex.DecodeString("D70CD69A2647F7390973DF48CBFA2CCC407B8B2D60B08C5F1641185C7998A290")
|
||
|
keyCombo3, _ = hex.DecodeString("81A8B093912C9E481408D09776CEFB48AEB8B65481B6BAAFB3C5810106717BEB")
|
||
|
keyCombo4, _ = hex.DecodeString("2EB18851887E7BDC5E830E89B19DDBC28078F1FA88AAD0AD01CA06FE4F80210B")
|
||
|
)
|
||
|
|
||
|
// TestMuSig2KeyAggTestVectors tests that this implementation of musig2 key
|
||
|
// aggregation lines up with the secp256k1-zkp test vectors.
|
||
|
func TestMuSig2KeyAggTestVectors(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
testCases := []struct {
|
||
|
keyOrder []int
|
||
|
expectedKey []byte
|
||
|
}{
|
||
|
// Keys in backwards lexicographical order.
|
||
|
{
|
||
|
keyOrder: []int{0, 1, 2},
|
||
|
expectedKey: keyCombo1,
|
||
|
},
|
||
|
|
||
|
// Keys in sorted order.
|
||
|
{
|
||
|
keyOrder: []int{2, 1, 0},
|
||
|
expectedKey: keyCombo2,
|
||
|
},
|
||
|
|
||
|
// Only the first key.
|
||
|
{
|
||
|
keyOrder: []int{0, 0, 0},
|
||
|
expectedKey: keyCombo3,
|
||
|
},
|
||
|
|
||
|
// Duplicate the first key and second keys.
|
||
|
{
|
||
|
keyOrder: []int{0, 0, 1, 1},
|
||
|
expectedKey: keyCombo4,
|
||
|
},
|
||
|
}
|
||
|
for i, testCase := range testCases {
|
||
|
testName := fmt.Sprintf("%v", testCase.keyOrder)
|
||
|
t.Run(testName, func(t *testing.T) {
|
||
|
var keys []*btcec.PublicKey
|
||
|
for _, keyIndex := range testCase.keyOrder {
|
||
|
keyBytes := testKeys[keyIndex]
|
||
|
pub, err := schnorr.ParsePubKey(keyBytes)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unable to parse pubkeys: %v", err)
|
||
|
}
|
||
|
|
||
|
keys = append(keys, pub)
|
||
|
}
|
||
|
|
||
|
combinedKey := AggregateKeys(keys, false)
|
||
|
combinedKeyBytes := schnorr.SerializePubKey(combinedKey)
|
||
|
if !bytes.Equal(combinedKeyBytes, testCase.expectedKey) {
|
||
|
t.Fatalf("case: #%v, invalid aggregation: "+
|
||
|
"expected %x, got %x", i, testCase.expectedKey,
|
||
|
combinedKeyBytes)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func mustParseHex(str string) []byte {
|
||
|
b, err := hex.DecodeString(str)
|
||
|
if err != nil {
|
||
|
panic(fmt.Errorf("unable to parse hex: %v", err))
|
||
|
}
|
||
|
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func parseKey(xHex string) *btcec.PublicKey {
|
||
|
xB, err := hex.DecodeString(xHex)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
var x, y btcec.FieldVal
|
||
|
x.SetByteSlice(xB)
|
||
|
if !btcec.DecompressY(&x, false, &y) {
|
||
|
panic("x not on curve")
|
||
|
}
|
||
|
y.Normalize()
|
||
|
|
||
|
return btcec.NewPublicKey(&x, &y)
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
signSetPrivKey, _ = btcec.PrivKeyFromBytes(
|
||
|
mustParseHex("7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671"),
|
||
|
)
|
||
|
signSetPubKey, _ = schnorr.ParsePubKey(schnorr.SerializePubKey(signSetPrivKey.PubKey()))
|
||
|
|
||
|
signTestMsg = mustParseHex("F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF")
|
||
|
|
||
|
signSetKey2, _ = schnorr.ParsePubKey(
|
||
|
mustParseHex("F9308A019258C31049344F85F89D5229B531C845836F99B086" +
|
||
|
"01F113BCE036F9"),
|
||
|
)
|
||
|
signSetKey3, _ = schnorr.ParsePubKey(
|
||
|
mustParseHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843" +
|
||
|
"240F7B502BA659"),
|
||
|
)
|
||
|
|
||
|
signSetKeys = []*btcec.PublicKey{signSetPubKey, signSetKey2, signSetKey3}
|
||
|
)
|
||
|
|
||
|
// TestMuSig2SigningTestVectors tests that the musig2 implementation produces
|
||
|
// the same set of signatures.
|
||
|
func TestMuSig2SigningTestVectors(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
var aggregatedNonce [PubNonceSize]byte
|
||
|
copy(
|
||
|
aggregatedNonce[:],
|
||
|
mustParseHex("028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61"),
|
||
|
)
|
||
|
copy(
|
||
|
aggregatedNonce[33:],
|
||
|
mustParseHex("037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9"),
|
||
|
)
|
||
|
|
||
|
var secNonce [SecNonceSize]byte
|
||
|
copy(secNonce[:], mustParseHex("508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61"))
|
||
|
copy(secNonce[32:], mustParseHex("FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7"))
|
||
|
|
||
|
testCases := []struct {
|
||
|
keyOrder []int
|
||
|
expectedPartialSig []byte
|
||
|
}{
|
||
|
{
|
||
|
keyOrder: []int{0, 1, 2},
|
||
|
expectedPartialSig: mustParseHex("68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B"),
|
||
|
},
|
||
|
{
|
||
|
keyOrder: []int{1, 0, 2},
|
||
|
expectedPartialSig: mustParseHex("2DF67BFFF18E3DE797E13C6475C963048138DAEC5CB20A357CECA7C8424295EA"),
|
||
|
},
|
||
|
|
||
|
{
|
||
|
keyOrder: []int{1, 2, 0},
|
||
|
expectedPartialSig: mustParseHex("0D5B651E6DE34A29A12DE7A8B4183B4AE6A7F7FBE15CDCAFA4A3D1BCAABC7517"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
var msg [32]byte
|
||
|
copy(msg[:], signTestMsg)
|
||
|
|
||
|
for _, testCase := range testCases {
|
||
|
testName := fmt.Sprintf("%v", testCase.keyOrder)
|
||
|
t.Run(testName, func(t *testing.T) {
|
||
|
keySet := make([]*btcec.PublicKey, 0, len(testCase.keyOrder))
|
||
|
for _, keyIndex := range testCase.keyOrder {
|
||
|
keySet = append(keySet, signSetKeys[keyIndex])
|
||
|
}
|
||
|
|
||
|
partialSig, err := Sign(
|
||
|
secNonce, signSetPrivKey, aggregatedNonce, keySet, msg,
|
||
|
)
|
||
|
if err != nil {
|
||
|
t.Fatalf("unable to generate partial sig: %v", err)
|
||
|
}
|
||
|
|
||
|
var partialSigBytes [32]byte
|
||
|
partialSig.S.PutBytesUnchecked(partialSigBytes[:])
|
||
|
|
||
|
if !bytes.Equal(partialSigBytes[:], testCase.expectedPartialSig) {
|
||
|
t.Fatalf("sigs don't match: expected %x, got %x",
|
||
|
testCase.expectedPartialSig, partialSigBytes,
|
||
|
)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type signer struct {
|
||
|
privKey *btcec.PrivateKey
|
||
|
pubKey *btcec.PublicKey
|
||
|
|
||
|
nonces *Nonces
|
||
|
|
||
|
partialSig *PartialSignature
|
||
|
}
|
||
|
|
||
|
type signerSet []signer
|
||
|
|
||
|
func (s signerSet) keys() []*btcec.PublicKey {
|
||
|
keys := make([]*btcec.PublicKey, len(s))
|
||
|
for i := 0; i < len(s); i++ {
|
||
|
keys[i] = s[i].pubKey
|
||
|
}
|
||
|
|
||
|
return keys
|
||
|
}
|
||
|
|
||
|
func (s signerSet) partialSigs() []*PartialSignature {
|
||
|
sigs := make([]*PartialSignature, len(s))
|
||
|
for i := 0; i < len(s); i++ {
|
||
|
sigs[i] = s[i].partialSig
|
||
|
}
|
||
|
|
||
|
return sigs
|
||
|
}
|
||
|
|
||
|
func (s signerSet) pubNonces() [][PubNonceSize]byte {
|
||
|
nonces := make([][PubNonceSize]byte, len(s))
|
||
|
for i := 0; i < len(s); i++ {
|
||
|
nonces[i] = s[i].nonces.PubNonce
|
||
|
}
|
||
|
|
||
|
return nonces
|
||
|
}
|
||
|
|
||
|
func (s signerSet) combinedKey() *btcec.PublicKey {
|
||
|
return AggregateKeys(s.keys(), false)
|
||
|
}
|