Skip to content

Commit

Permalink
Merge pull request #1820 from Roasbeef/musig2
Browse files Browse the repository at this point in the history
btcec/schnorr/musig2: add new musig2 implementation based on musig2 draft BIP
  • Loading branch information
Roasbeef authored Apr 28, 2022
2 parents 8c5bfee + 1da361b commit a86222c
Show file tree
Hide file tree
Showing 9 changed files with 3,354 additions and 7 deletions.
3 changes: 2 additions & 1 deletion btcec/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ module github.com/btcsuite/btcd/btcec/v2
go 1.17

require (
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5
)

require github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
6 changes: 4 additions & 2 deletions btcec/go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 h1:MSskdM4/xJYcFzy0altH/C/xHopifpWzHUi1JeVI34Q=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 h1:rxKZ2gOnYxjfmakvUUqh9Gyb6KXfrj7JWTxORTYqb0E=
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
313 changes: 313 additions & 0 deletions btcec/schnorr/musig2/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
// Copyright 2013-2022 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package musig2

import (
"encoding/hex"
"fmt"
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
)

var (
testPrivBytes = hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")

testMsg = hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")
)

func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}

func hexToModNScalar(s string) *btcec.ModNScalar {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
var scalar btcec.ModNScalar
if overflow := scalar.SetByteSlice(b); overflow {
panic("hex in source file overflows mod N scalar: " + s)
}
return &scalar
}

func genSigner(t *testing.B) signer {
privKey, err := btcec.NewPrivateKey()
if err != nil {
t.Fatalf("unable to gen priv key: %v", err)
}

pubKey, err := schnorr.ParsePubKey(
schnorr.SerializePubKey(privKey.PubKey()),
)
if err != nil {
t.Fatalf("unable to gen key: %v", err)
}

nonces, err := GenNonces()
if err != nil {
t.Fatalf("unable to gen nonces: %v", err)
}

return signer{
privKey: privKey,
pubKey: pubKey,
nonces: nonces,
}
}

var (
testSig *PartialSignature
testErr error
)

// BenchmarkPartialSign benchmarks how long it takes to generate a partial
// signature factoring in if the keys are sorted and also if we're in fast sign
// mode.
func BenchmarkPartialSign(b *testing.B) {
for _, numSigners := range []int{10, 100} {
for _, fastSign := range []bool{true, false} {
for _, sortKeys := range []bool{true, false} {
name := fmt.Sprintf("num_signers=%v/fast_sign=%v/sort=%v",
numSigners, fastSign, sortKeys)

signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}

combinedNonce, err := AggregateNonces(signers.pubNonces())
if err != nil {
b.Fatalf("unable to generate combined nonce: %v", err)
}

var sig *PartialSignature

var msg [32]byte
copy(msg[:], testMsg[:])

keys := signers.keys()

b.Run(name, func(b *testing.B) {
var signOpts []SignOption
if fastSign {
signOpts = append(signOpts, WithFastSign())
}
if sortKeys {
signOpts = append(signOpts, WithSortedKeys())
}

b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
sig, err = Sign(
signers[0].nonces.SecNonce, signers[0].privKey,
combinedNonce, keys, msg, signOpts...,
)
if err != nil {
b.Fatalf("unable to generate sig: %v", err)
}
}

testSig = sig
testErr = err
})
}
}
}
}

// TODO(roasbeef): add impact of sorting ^

var sigOk bool

// BenchmarkPartialVerify benchmarks how long it takes to verify a partial
// signature.
func BenchmarkPartialVerify(b *testing.B) {
for _, numSigners := range []int{10, 100} {
for _, sortKeys := range []bool{true, false} {
name := fmt.Sprintf("sort_keys=%v/num_signers=%v",
sortKeys, numSigners)

signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}

combinedNonce, err := AggregateNonces(
signers.pubNonces(),
)
if err != nil {
b.Fatalf("unable to generate combined "+
"nonce: %v", err)
}

var sig *PartialSignature

var msg [32]byte
copy(msg[:], testMsg[:])

b.ReportAllocs()
b.ResetTimer()

sig, err = Sign(
signers[0].nonces.SecNonce, signers[0].privKey,
combinedNonce, signers.keys(), msg,
)
if err != nil {
b.Fatalf("unable to generate sig: %v", err)
}

keys := signers.keys()
pubKey := signers[0].pubKey

b.Run(name, func(b *testing.B) {
var signOpts []SignOption
if sortKeys {
signOpts = append(
signOpts, WithSortedKeys(),
)
}

b.ResetTimer()
b.ReportAllocs()

var ok bool
for i := 0; i < b.N; i++ {
ok = sig.Verify(
signers[0].nonces.PubNonce, combinedNonce,
keys, pubKey, msg,
)
if !ok {
b.Fatalf("generated invalid sig!")
}
}
sigOk = ok
})

}
}
}

var finalSchnorrSig *schnorr.Signature

// BenchmarkCombineSigs benchmarks how long it takes to combine a set amount of
// signatures.
func BenchmarkCombineSigs(b *testing.B) {

for _, numSigners := range []int{10, 100} {
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}

combinedNonce, err := AggregateNonces(signers.pubNonces())
if err != nil {
b.Fatalf("unable to generate combined nonce: %v", err)
}

var msg [32]byte
copy(msg[:], testMsg[:])

var finalNonce *btcec.PublicKey
for i := range signers {
signer := signers[i]
partialSig, err := Sign(
signer.nonces.SecNonce, signer.privKey,
combinedNonce, signers.keys(), msg,
)
if err != nil {
b.Fatalf("unable to generate partial sig: %v",
err)
}

signers[i].partialSig = partialSig

if finalNonce == nil {
finalNonce = partialSig.R
}
}

sigs := signers.partialSigs()

name := fmt.Sprintf("num_signers=%v", numSigners)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()

finalSig := CombineSigs(finalNonce, sigs)

finalSchnorrSig = finalSig
})
}
}

var testNonce [PubNonceSize]byte

// BenchmarkAggregateNonces benchmarks how long it takes to combine nonces.
func BenchmarkAggregateNonces(b *testing.B) {
for _, numSigners := range []int{10, 100} {
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}

nonces := signers.pubNonces()

name := fmt.Sprintf("num_signers=%v", numSigners)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()

pubNonce, err := AggregateNonces(nonces)
if err != nil {
b.Fatalf("unable to generate nonces: %v", err)
}

testNonce = pubNonce
})
}
}

var testKey *btcec.PublicKey

// BenchmarkAggregateKeys benchmarks how long it takes to aggregate public
// keys.
func BenchmarkAggregateKeys(b *testing.B) {
for _, numSigners := range []int{10, 100} {
for _, sortKeys := range []bool{true, false} {
signers := make(signerSet, numSigners)
for i := 0; i < numSigners; i++ {
signers[i] = genSigner(b)
}

signerKeys := signers.keys()

name := fmt.Sprintf("num_signers=%v/sort_keys=%v",
numSigners, sortKeys)

uniqueKeyIndex := secondUniqueKeyIndex(signerKeys, false)

b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()

aggKey, _, _, _ := AggregateKeys(
signerKeys, sortKeys,
WithUniqueKeyIndex(uniqueKeyIndex),
)

testKey = aggKey.FinalKey
})
}
}
}
Loading

0 comments on commit a86222c

Please sign in to comment.