perf: secKeyVerify operates directly on bytes, avoids IntArray allocation

Compares the 32-byte key against n byte-by-byte (big-endian) instead of
decoding into limbs first. Eliminates one IntArray(8) allocation and the
fromBytes conversion. The non-zero check ORs all bytes in a single pass.

privKeyTweakAdd was also tested with a byte-based approach but reverted
because 32 byte-iterations are slower than 8 limb-iterations — the JVM
optimizes Long operations better than byte loops.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
This commit is contained in:
Claude
2026-04-05 20:26:11 +00:00
parent de3808416c
commit 5b7f8478aa
@@ -101,12 +101,64 @@ object Secp256k1 {
}
}
/** Verify that a byte array is a valid secret key (32 bytes, 0 < value < n). */
/**
* Verify that a byte array is a valid secret key (32 bytes, 0 < value < n).
* Operates directly on bytes without converting to limbs — avoids IntArray allocation.
*/
fun secKeyVerify(seckey: ByteArray): Boolean {
if (seckey.size != 32) return false
return ScalarN.isValid(U256.fromBytes(seckey))
// Check not zero (any non-zero byte means non-zero)
var nonZero = 0
for (b in seckey) nonZero = nonZero or b.toInt()
if (nonZero == 0) return false
// Check < n by comparing big-endian bytes against n
// n = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
for (i in 0 until 32) {
val si = seckey[i].toInt() and 0xFF
val ni = N_BYTES[i].toInt() and 0xFF
if (si < ni) return true // definitely less
if (si > ni) return false // definitely greater
}
return false // equal to n → invalid
}
// n as big-endian bytes for direct comparison
private val N_BYTES =
byteArrayOf(
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFE.toByte(),
0xBA.toByte(),
0xAE.toByte(),
0xDC.toByte(),
0xE6.toByte(),
0xAF.toByte(),
0x48.toByte(),
0xA0.toByte(),
0x3B.toByte(),
0xBF.toByte(),
0xD2.toByte(),
0x5E.toByte(),
0x8C.toByte(),
0xD0.toByte(),
0x36.toByte(),
0x41.toByte(),
0x41.toByte(),
)
// ==================== BIP-340 Schnorr Signatures ====================
/**