bench_test.go raw
1 package bench
2
3 import (
4 "crypto/rand"
5 "crypto/sha256"
6 "testing"
7
8 "github.com/btcsuite/btcd/btcec/v2"
9 "github.com/btcsuite/btcd/btcec/v2/schnorr"
10 "github.com/decred/dcrd/dcrec/secp256k1/v4"
11
12 p256k1 "p256k1.mleku.dev"
13
14 p8k "p8k.mleku.dev"
15 signer "p8k.mleku.dev/p8k"
16 )
17
18 // Shared test data
19 var (
20 benchPrivKey [32]byte
21 benchMsg []byte
22 benchMsgHash [32]byte
23 )
24
25 func init() {
26 // Generate deterministic test data
27 rand.Read(benchPrivKey[:])
28 benchMsg = make([]byte, 32)
29 rand.Read(benchMsg)
30 benchMsgHash = sha256.Sum256(benchMsg)
31 }
32
33 // =============================================================================
34 // BTCEC Benchmarks
35 // =============================================================================
36
37 func BenchmarkBTCEC_PubkeyDerivation(b *testing.B) {
38 privKey, _ := btcec.PrivKeyFromBytes(benchPrivKey[:])
39 b.ResetTimer()
40
41 for i := 0; i < b.N; i++ {
42 _ = privKey.PubKey()
43 }
44 }
45
46 func BenchmarkBTCEC_SchnorrSign(b *testing.B) {
47 privKey, _ := btcec.PrivKeyFromBytes(benchPrivKey[:])
48
49 b.ResetTimer()
50 for i := 0; i < b.N; i++ {
51 _, err := schnorr.Sign(privKey, benchMsgHash[:])
52 if err != nil {
53 b.Fatal(err)
54 }
55 }
56 }
57
58 func BenchmarkBTCEC_SchnorrVerify(b *testing.B) {
59 privKey, _ := btcec.PrivKeyFromBytes(benchPrivKey[:])
60 pubKey := privKey.PubKey()
61 sig, _ := schnorr.Sign(privKey, benchMsgHash[:])
62
63 b.ResetTimer()
64 for i := 0; i < b.N; i++ {
65 valid := sig.Verify(benchMsgHash[:], pubKey)
66 if !valid {
67 b.Fatal("signature verification failed")
68 }
69 }
70 }
71
72 func BenchmarkBTCEC_ECDH(b *testing.B) {
73 privKey1, _ := btcec.PrivKeyFromBytes(benchPrivKey[:])
74
75 var privKey2Bytes [32]byte
76 rand.Read(privKey2Bytes[:])
77 privKey2, _ := btcec.PrivKeyFromBytes(privKey2Bytes[:])
78 pubKey2 := privKey2.PubKey()
79
80 b.ResetTimer()
81 for i := 0; i < b.N; i++ {
82 _ = secp256k1.GenerateSharedSecret(privKey1, pubKey2)
83 }
84 }
85
86 // =============================================================================
87 // P256K1 (Pure Go) Benchmarks
88 // =============================================================================
89
90 func BenchmarkP256K1_PubkeyDerivation(b *testing.B) {
91 ctx := p256k1.ContextCreate(p256k1.ContextSign)
92 defer p256k1.ContextDestroy(ctx)
93
94 b.ResetTimer()
95 for i := 0; i < b.N; i++ {
96 var pubkey p256k1.PublicKey
97 err := p256k1.ECPubkeyCreate(&pubkey, benchPrivKey[:])
98 if err != nil {
99 b.Fatal(err)
100 }
101 }
102 }
103
104 func BenchmarkP256K1_SchnorrSign(b *testing.B) {
105 keypair, err := p256k1.KeyPairCreate(benchPrivKey[:])
106 if err != nil {
107 b.Fatal(err)
108 }
109
110 auxRand := make([]byte, 32)
111 rand.Read(auxRand)
112
113 b.ResetTimer()
114 for i := 0; i < b.N; i++ {
115 var sig [64]byte
116 err := p256k1.SchnorrSign(sig[:], benchMsgHash[:], keypair, auxRand)
117 if err != nil {
118 b.Fatal(err)
119 }
120 }
121 }
122
123 func BenchmarkP256K1_SchnorrVerify(b *testing.B) {
124 keypair, err := p256k1.KeyPairCreate(benchPrivKey[:])
125 if err != nil {
126 b.Fatal(err)
127 }
128
129 xonlyPubkey, err := keypair.XOnlyPubkey()
130 if err != nil {
131 b.Fatal(err)
132 }
133
134 auxRand := make([]byte, 32)
135 rand.Read(auxRand)
136
137 var sig [64]byte
138 err = p256k1.SchnorrSign(sig[:], benchMsgHash[:], keypair, auxRand)
139 if err != nil {
140 b.Fatal(err)
141 }
142
143 b.ResetTimer()
144 for i := 0; i < b.N; i++ {
145 if !p256k1.SchnorrVerify(sig[:], benchMsgHash[:], xonlyPubkey) {
146 b.Fatal("verification failed")
147 }
148 }
149 }
150
151 func BenchmarkP256K1_ECDH(b *testing.B) {
152 var privKey2Bytes [32]byte
153 rand.Read(privKey2Bytes[:])
154
155 var pubkey2 p256k1.PublicKey
156 err := p256k1.ECPubkeyCreate(&pubkey2, privKey2Bytes[:])
157 if err != nil {
158 b.Fatal(err)
159 }
160
161 b.ResetTimer()
162 for i := 0; i < b.N; i++ {
163 var output [32]byte
164 err := p256k1.ECDHXOnly(output[:], &pubkey2, benchPrivKey[:])
165 if err != nil {
166 b.Fatal(err)
167 }
168 }
169 }
170
171 // =============================================================================
172 // P8K (Purego) Benchmarks
173 // =============================================================================
174
175 func BenchmarkP8K_PubkeyDerivation(b *testing.B) {
176 ctx, err := p8k.NewContext(p8k.ContextSign)
177 if err != nil {
178 b.Skip("libsecp256k1 not available:", err)
179 }
180 defer ctx.Destroy()
181
182 b.ResetTimer()
183 for i := 0; i < b.N; i++ {
184 _, err := ctx.CreatePublicKey(benchPrivKey[:])
185 if err != nil {
186 b.Fatal(err)
187 }
188 }
189 }
190
191 func BenchmarkP8K_SchnorrSign(b *testing.B) {
192 ctx, err := p8k.NewContext(p8k.ContextSign)
193 if err != nil {
194 b.Skip("libsecp256k1 not available:", err)
195 }
196 defer ctx.Destroy()
197
198 keypair, err := ctx.CreateKeypair(benchPrivKey[:])
199 if err != nil {
200 b.Skip("schnorr module not available:", err)
201 }
202
203 auxRand := make([]byte, 32)
204 rand.Read(auxRand)
205
206 b.ResetTimer()
207 for i := 0; i < b.N; i++ {
208 _, err := ctx.SchnorrSign(benchMsgHash[:], keypair, auxRand)
209 if err != nil {
210 b.Fatal(err)
211 }
212 }
213 }
214
215 func BenchmarkP8K_SchnorrVerify(b *testing.B) {
216 ctx, err := p8k.NewContext(p8k.ContextSign | p8k.ContextVerify)
217 if err != nil {
218 b.Skip("libsecp256k1 not available:", err)
219 }
220 defer ctx.Destroy()
221
222 keypair, err := ctx.CreateKeypair(benchPrivKey[:])
223 if err != nil {
224 b.Skip("schnorr module not available:", err)
225 }
226
227 xonly, _, err := ctx.KeypairXOnlyPub(keypair)
228 if err != nil {
229 b.Fatal(err)
230 }
231
232 auxRand := make([]byte, 32)
233 rand.Read(auxRand)
234
235 sig, err := ctx.SchnorrSign(benchMsgHash[:], keypair, auxRand)
236 if err != nil {
237 b.Fatal(err)
238 }
239
240 b.ResetTimer()
241 for i := 0; i < b.N; i++ {
242 valid, err := ctx.SchnorrVerify(sig, benchMsgHash[:], xonly[:])
243 if err != nil {
244 b.Fatal(err)
245 }
246 if !valid {
247 b.Fatal("verification failed")
248 }
249 }
250 }
251
252 func BenchmarkP8K_ECDH(b *testing.B) {
253 ctx, err := p8k.NewContext(p8k.ContextSign)
254 if err != nil {
255 b.Skip("libsecp256k1 not available:", err)
256 }
257 defer ctx.Destroy()
258
259 var privKey2Bytes [32]byte
260 rand.Read(privKey2Bytes[:])
261
262 pubkey2, err := ctx.CreatePublicKey(privKey2Bytes[:])
263 if err != nil {
264 b.Fatal(err)
265 }
266
267 b.ResetTimer()
268 for i := 0; i < b.N; i++ {
269 _, err := ctx.ECDH(pubkey2, benchPrivKey[:])
270 if err != nil {
271 b.Fatal(err)
272 }
273 }
274 }
275
276 // =============================================================================
277 // P8K Signer Interface Benchmarks (with automatic fallback)
278 // =============================================================================
279
280 func BenchmarkSigner_Generate(b *testing.B) {
281 b.ResetTimer()
282 for i := 0; i < b.N; i++ {
283 sig, err := signer.NewSigner()
284 if err != nil {
285 b.Fatal(err)
286 }
287 if err := sig.Generate(); err != nil {
288 b.Fatal(err)
289 }
290 sig.Zero()
291 }
292 }
293
294 func BenchmarkSigner_SchnorrSign(b *testing.B) {
295 sig, err := signer.NewSigner()
296 if err != nil {
297 b.Fatal(err)
298 }
299 defer sig.Zero()
300
301 if err := sig.InitSec(benchPrivKey[:]); err != nil {
302 b.Fatal(err)
303 }
304
305 b.ResetTimer()
306 for i := 0; i < b.N; i++ {
307 _, err := sig.Sign(benchMsgHash[:])
308 if err != nil {
309 b.Fatal(err)
310 }
311 }
312 }
313
314 func BenchmarkSigner_SchnorrVerify(b *testing.B) {
315 sig, err := signer.NewSigner()
316 if err != nil {
317 b.Fatal(err)
318 }
319 defer sig.Zero()
320
321 if err := sig.InitSec(benchPrivKey[:]); err != nil {
322 b.Fatal(err)
323 }
324
325 signature, err := sig.Sign(benchMsgHash[:])
326 if err != nil {
327 b.Fatal(err)
328 }
329
330 b.ResetTimer()
331 for i := 0; i < b.N; i++ {
332 valid, err := sig.Verify(benchMsgHash[:], signature)
333 if err != nil {
334 b.Fatal(err)
335 }
336 if !valid {
337 b.Fatal("verification failed")
338 }
339 }
340 }
341
342 func BenchmarkSigner_ECDH(b *testing.B) {
343 sig, err := signer.NewSigner()
344 if err != nil {
345 b.Fatal(err)
346 }
347 defer sig.Zero()
348
349 if err := sig.InitSec(benchPrivKey[:]); err != nil {
350 b.Fatal(err)
351 }
352
353 var privKey2Bytes [32]byte
354 rand.Read(privKey2Bytes[:])
355
356 sig2, err := signer.NewSigner()
357 if err != nil {
358 b.Fatal(err)
359 }
360 defer sig2.Zero()
361
362 if err := sig2.InitSec(privKey2Bytes[:]); err != nil {
363 b.Fatal(err)
364 }
365
366 pubkey2 := sig2.Pub()
367
368 b.ResetTimer()
369 for i := 0; i < b.N; i++ {
370 _, err := sig.ECDH(pubkey2)
371 if err != nil {
372 b.Fatal(err)
373 }
374 }
375 }
376
377 // =============================================================================
378 // Comparative Benchmarks (All Implementations)
379 // =============================================================================
380
381 func BenchmarkComparative_SchnorrSign(b *testing.B) {
382 b.Run("BTCEC", BenchmarkBTCEC_SchnorrSign)
383 b.Run("P256K1", BenchmarkP256K1_SchnorrSign)
384 b.Run("P8K", BenchmarkP8K_SchnorrSign)
385 b.Run("Signer", BenchmarkSigner_SchnorrSign)
386 }
387
388 func BenchmarkComparative_SchnorrVerify(b *testing.B) {
389 b.Run("BTCEC", BenchmarkBTCEC_SchnorrVerify)
390 b.Run("P256K1", BenchmarkP256K1_SchnorrVerify)
391 b.Run("P8K", BenchmarkP8K_SchnorrVerify)
392 b.Run("Signer", BenchmarkSigner_SchnorrVerify)
393 }
394
395 func BenchmarkComparative_ECDH(b *testing.B) {
396 b.Run("BTCEC", BenchmarkBTCEC_ECDH)
397 b.Run("P256K1", BenchmarkP256K1_ECDH)
398 b.Run("P8K", BenchmarkP8K_ECDH)
399 b.Run("Signer", BenchmarkSigner_ECDH)
400 }
401
402 // Run all comparative benchmarks
403 func BenchmarkAll(b *testing.B) {
404 b.Run("SchnorrSign", BenchmarkComparative_SchnorrSign)
405 b.Run("SchnorrVerify", BenchmarkComparative_SchnorrVerify)
406 b.Run("ECDH", BenchmarkComparative_ECDH)
407 }
408
409 // Benchmark to show signer initialization overhead
410 func BenchmarkSigner_Initialization(b *testing.B) {
411 b.ResetTimer()
412 for i := 0; i < b.N; i++ {
413 sig, err := signer.NewSigner()
414 if err != nil {
415 b.Fatal(err)
416 }
417 sig.Zero()
418 }
419 }
420
421 // Benchmark to show status check overhead
422 func BenchmarkSigner_GetModuleStatus(b *testing.B) {
423 sig, err := signer.NewSigner()
424 if err != nil {
425 b.Fatal(err)
426 }
427 defer sig.Zero()
428
429 b.ResetTimer()
430 for i := 0; i < b.N; i++ {
431 _ = sig.GetModuleStatus()
432 }
433 }
434