btcec/schnorr: add benchmarks for sign/verify

Benchmarks run w/o fast sign (always verify after you generate a sig):
```
goos: darwin
goarch: amd64
pkg: github.com/btcsuite/btcd/btcec/v2/schnorr
cpu: VirtualApple @ 2.50GHz
BenchmarkSigVerify-8     	    8000	    152468 ns/op	     960 B/op	      16 allocs/op
BenchmarkSign-8          	    4939	    215489 ns/op	    1408 B/op	      27 allocs/op
BenchmarkSignRfc6979-8   	    5106	    217416 ns/op	    2129 B/op	      37 allocs/op
PASS
ok  	github.com/btcsuite/btcd/btcec/v2/schnorr	4.629s
```

Benchmarks w/ fast sign:
```
goos: darwin
goarch: amd64
pkg: github.com/btcsuite/btcd/btcec/v2/schnorr
cpu: VirtualApple @ 2.50GHz
BenchmarkSigVerify-8     	    7982	    142826 ns/op	     960 B/op	      16 allocs/op
BenchmarkSign-8          	   18210	     65908 ns/op	     496 B/op	      12 allocs/op
BenchmarkSignRfc6979-8   	   16537	     78161 ns/op	    1216 B/op	      22 allocs/op
PASS
ok  	github.com/btcsuite/btcd/btcec/v2/schnorr	5.418s
```
This commit is contained in:
Olaoluwa Osuntokun 2022-01-31 14:39:32 -08:00
parent 0bbc831040
commit 973fb37600
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306

169
btcec/schnorr/bench_test.go Normal file
View File

@ -0,0 +1,169 @@
// 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 schnorr
import (
"crypto/sha256"
"encoding/hex"
"math/big"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
)
// hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
// hexToModNScalar converts the passed hex string into a ModNScalar and will
// panic if there is an error. This is only provided for the hard-coded
// constants so errors in the source code can be detected. It will only (and
// must only) be called with hard-coded values.
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
}
// hexToFieldVal converts the passed hex string into a FieldVal and will panic
// if there is an error. This is only provided for the hard-coded constants so
// errors in the source code can be detected. It will only (and must only) be
// called with hard-coded values.
func hexToFieldVal(s string) *btcec.FieldVal {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
var f btcec.FieldVal
if overflow := f.SetByteSlice(b); overflow {
panic("hex in source file overflows mod P: " + s)
}
return &f
}
// fromHex converts the passed hex string into a big integer pointer and will
// panic is there is an error. This is only provided for the hard-coded
// constants so errors in the source code can bet detected. It will only (and
// must only) be called for initialization purposes.
func fromHex(s string) *big.Int {
if s == "" {
return big.NewInt(0)
}
r, ok := new(big.Int).SetString(s, 16)
if !ok {
panic("invalid hex in source file: " + s)
}
return r
}
var testOk bool
// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to
// verify signatures.
func BenchmarkSigVerify(b *testing.B) {
// Randomly generated keypair.
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
privKey := secp256k1.NewPrivateKey(d)
pubKey := privKey.PubKey()
// Double sha256 of []byte{0x01, 0x02, 0x03, 0x04}
msgHash := sha256.Sum256([]byte("benchmark"))
sig, err := Sign(privKey, msgHash[:])
if err != nil {
b.Fatalf("unable to sign: %v", err)
}
if !sig.Verify(msgHash[:], pubKey) {
b.Errorf("Signature failed to verify")
return
}
var ok bool
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ok = sig.Verify(msgHash[:], pubKey)
}
testOk = ok
}
// Used to ensure the compiler doesn't optimize away the benchmark.
var (
testSig *Signature
testErr error
)
// BenchmarkSign benchmarks how long it takes to sign a message.
func BenchmarkSign(b *testing.B) {
// Randomly generated keypair.
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
privKey := secp256k1.NewPrivateKey(d)
// blake256 of []byte{0x01, 0x02, 0x03, 0x04}.
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")
var auxBytes [32]byte
copy(auxBytes[:], msgHash)
auxBytes[0] ^= 1
var (
sig *Signature
err error
)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
sig, err = Sign(
privKey, msgHash, CustomNonce(auxBytes), FastSign(),
)
}
testSig = sig
testErr = err
}
// BenchmarkSignRfc6979 benchmarks how long it takes to sign a message.
func BenchmarkSignRfc6979(b *testing.B) {
// Randomly generated keypair.
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
privKey := secp256k1.NewPrivateKey(d)
// blake256 of []byte{0x01, 0x02, 0x03, 0x04}.
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")
var (
sig *Signature
err error
)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
sig, err = Sign(privKey, msgHash, FastSign())
}
testSig = sig
testErr = err
}