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