btcd/btcec/schnorr/musig2/bench_test.go
Olaoluwa Osuntokun e85e7c3ac7
btcec/schnorr/musig2: optimize signing+verification
In this commit, we optimize signing+verification mainly by only
computing values once, and reducing allocations when possible.

The following optimizations have been implemented:
  * Use a single buffer allocation in keyHashFingerprint to avoid
    dynamic buffer growth+re-sizing
  * Remove the isSecondKey computation and replace that with a single
    routine that computes the index of the second unique key.
  * Optimize keyHashFingerprint usage by only computing it once during
    signing +verification.

A further optimization is possible: use the x coordinate of a key for
comparisons instead of computing the full sexualision. We need to do
the latter atm, as the X() method of the public key struct will allocate
more memory as it allocate and sets the buffer in place.

The final benchmarks of before and after this commit:
benchmark                                                             old ns/op     new ns/op     delta
BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8        1227374       1194047       -2.72%
BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8       1217743       1191468       -2.16%
BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8       2755544       2698827       -2.06%
BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8      2754749       2694547       -2.19%
BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8       12382654      10561204      -14.71%
BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8      12260134      10315376      -15.86%
BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8      24832061      22009935      -11.36%
BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8     24650086      21022833      -14.71%
BenchmarkPartialVerify/sort_keys=true/num_signers=10-8                1485787       1473377       -0.84%
BenchmarkPartialVerify/sort_keys=false/num_signers=10-8               1447275       1465139       +1.23%
BenchmarkPartialVerify/sort_keys=true/num_signers=100-8               12503482      10672618      -14.64%
BenchmarkPartialVerify/sort_keys=false/num_signers=100-8              12388289      10581398      -14.59%
BenchmarkCombineSigs/num_signers=10-8                                 0.00          0.00          +0.00%
BenchmarkCombineSigs/num_signers=100-8                                0.00          0.00          -1.95%
BenchmarkAggregateNonces/num_signers=10-8                             0.00          0.00          -0.76%
BenchmarkAggregateNonces/num_signers=100-8                            0.00          0.00          +1.13%
BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8                0.00          0.00          -0.09%
BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8               0.00          0.01          +559.94%
BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8               0.01          0.01          -11.30%
BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8              0.01          0.01          -11.66%

benchmark                                                             old allocs     new allocs     delta
BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8        458            269            -41.27%
BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8       409            222            -45.72%
BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8       892            524            -41.26%
BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8      841            467            -44.47%
BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8       14366          3089           -78.50%
BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8      13143          1842           -85.98%
BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8      27596          4964           -82.01%
BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8     26309          3707           -85.91%
BenchmarkPartialVerify/sort_keys=true/num_signers=10-8                430            243            -43.49%
BenchmarkPartialVerify/sort_keys=false/num_signers=10-8               430            243            -43.49%
BenchmarkPartialVerify/sort_keys=true/num_signers=100-8               13164          1863           -85.85%
BenchmarkPartialVerify/sort_keys=false/num_signers=100-8              13164          1863           -85.85%
BenchmarkCombineSigs/num_signers=10-8                                 0              0              +0.00%
BenchmarkCombineSigs/num_signers=100-8                                0              0              +0.00%
BenchmarkAggregateNonces/num_signers=10-8                             0              0              +0.00%
BenchmarkAggregateNonces/num_signers=100-8                            0              0              +0.00%
BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8                0              0              +0.00%
BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8               0              0              +0.00%
BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8               0              0              +0.00%
BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8              0              0              +0.00%

benchmark                                                             old bytes     new bytes     delta
BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8        27854         14878         -46.59%
BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8       25508         12605         -50.58%
BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8       54982         29476         -46.39%
BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8      52581         26805         -49.02%
BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8       1880138       166996        -91.12%
BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8      1820561       106295        -94.16%
BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8      3706291       275344        -92.57%
BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8     3642725       214122        -94.12%
BenchmarkPartialVerify/sort_keys=true/num_signers=10-8                26995         14078         -47.85%
BenchmarkPartialVerify/sort_keys=false/num_signers=10-8               26980         14078         -47.82%
BenchmarkPartialVerify/sort_keys=true/num_signers=100-8               1822043       107767        -94.09%
BenchmarkPartialVerify/sort_keys=false/num_signers=100-8              1822046       107752        -94.09%
BenchmarkCombineSigs/num_signers=10-8                                 0             0             +0.00%
BenchmarkCombineSigs/num_signers=100-8                                0             0             +0.00%
BenchmarkAggregateNonces/num_signers=10-8                             0             0             +0.00%
BenchmarkAggregateNonces/num_signers=100-8                            0             0             +0.00%
BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8                0             0             +0.00%
BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8               0             0             +0.00%
BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8               0             0             +0.00%
BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8              0             0             +0.00%
2022-04-28 16:19:51 -07:00

319 lines
6.8 KiB
Go

// Copyright 2013-2016 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"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
)
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) {
privKey := secp256k1.NewPrivateKey(testPrivBytes)
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)
nonces, err := GenNonces()
if err != nil {
b.Fatalf("unable to generate nonces: %v", err)
}
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(
nonces.SecNonce, privKey, combinedNonce,
keys, msg, signOpts...,
)
}
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) {
privKey := secp256k1.NewPrivateKey(testPrivBytes)
for _, numSigners := range []int{10, 100} {
for _, sortKeys := range []bool{true, false} {
name := fmt.Sprintf("sort_keys=%v/num_signers=%v",
sortKeys, numSigners)
nonces, err := GenNonces()
if err != nil {
b.Fatalf("unable to generate nonces: %v", err)
}
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(
nonces.SecNonce, privKey, combinedNonce,
signers.keys(), msg, WithFastSign(),
)
keys := signers.keys()
pubKey := privKey.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(
nonces.PubNonce, combinedNonce,
keys, pubKey, msg,
)
}
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)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
aggKey := AggregateKeys(
signerKeys, sortKeys,
WithUniqueKeyIndex(uniqueKeyIndex),
)
testKey = aggKey
})
}
}
}