// 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 (
	"math/big"
	"testing"

	"next.orly.dev/pkg/crypto/ec"
	"next.orly.dev/pkg/crypto/ec/secp256k1"
	"next.orly.dev/pkg/crypto/sha256"
	"next.orly.dev/pkg/encoders/hex"
)

// 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.Dec(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.Dec(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.Dec(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 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 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

// BenchmarkSign benchmarks how long it takes to sign a message.
func BenchmarkSign(b *testing.B) {
	// Randomly generated keypair.
	d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
	privKey := secp256k1.NewSecretKey(d)
	// blake256 of by{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
}

// 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.NewSecretKey(d)
	pubKey := privKey.PubKey()
	// Double sha256 of by{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
)

// BenchmarkSignRfc6979 benchmarks how long it takes to sign a message.
func BenchmarkSignRfc6979(b *testing.B) {
	// Randomly generated keypair.
	d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
	privKey := secp256k1.NewSecretKey(d)
	// blake256 of by{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
}

// BenchmarkSigSerialize benchmarks how long it takes to serialize Schnorr
// signatures.
func BenchmarkSigSerialize(b *testing.B) {
	// From randomly generated keypair.
	d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
	secKey := secp256k1.NewSecretKey(d)
	// blake256 of by{0x01, 0x02, 0x03, 0x04}.
	msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")
	// Generate the signature.
	sig, _ := Sign(secKey, msgHash)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sig.Serialize()
	}
}
