feat: add batch verify to Android benchmark + expose in Secp256k1InstanceOurs

Add verifySchnorrBatch to Secp256k1InstanceOurs wrapper and add
Android benchmark tests for batch(8) and batch(16).

To get per-event cost: ns/op ÷ batchSize.
JVM benchmark showed 4-7× speedup over individual verify.
Android results TBD.

https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
This commit is contained in:
Claude
2026-04-09 14:45:14 +00:00
parent 245212727a
commit b3206b8e48
2 changed files with 44 additions and 0 deletions
@@ -164,4 +164,38 @@ class Secp256k1Benchmark {
assertNotNull(Secp256k1InstanceOurs.pubKeyTweakMulCompact(pubKey2XOnly, privKey))
}
}
// ==================== Batch verification (Pure Kotlin) ====================
// Same pubkey, n events — the typical Nostr pattern (feed from one author).
// Each iteration verifies the entire batch. Compare per-event cost:
// ns/op ÷ batchSize = per-event cost
// JVM benchmark showed 4-7× speedup over individual verify.
private fun buildBatch(size: Int): Pair<List<ByteArray>, List<ByteArray>> {
val sigs = mutableListOf<ByteArray>()
val msgs = mutableListOf<ByteArray>()
for (i in 0 until size) {
val m = ByteArray(32) { (i * 7 + it).toByte() }
sigs.add(Secp256k1InstanceOurs.signSchnorr(m, privKey, auxRand))
msgs.add(m)
}
return Pair(sigs, msgs)
}
private val batch8 = buildBatch(8)
private val batch16 = buildBatch(16)
@Test
fun verifyBatch8Ours() {
benchmarkRule.measureRepeated {
assertTrue(Secp256k1InstanceOurs.verifySchnorrBatch(xOnlyPub, batch8.first, batch8.second))
}
}
@Test
fun verifyBatch16Ours() {
benchmarkRule.measureRepeated {
assertTrue(Secp256k1InstanceOurs.verifySchnorrBatch(xOnlyPub, batch16.first, batch16.second))
}
}
}
@@ -72,6 +72,16 @@ object Secp256k1InstanceOurs {
pubKey: ByteArray,
): Boolean = Secp256k1.verifySchnorrFast(signature, hash, pubKey)
/**
* Batch-verify multiple signatures from the same pubkey.
* Uses scalar+point summation — one mulDoubleG for the whole batch.
*/
fun verifySchnorrBatch(
pubKey: ByteArray,
signatures: List<ByteArray>,
messages: List<ByteArray>,
): Boolean = Secp256k1.verifySchnorrBatch(pubKey, signatures, messages)
fun privateKeyAdd(
first: ByteArray,
second: ByteArray,