Commit Graph

149 Commits

Author SHA1 Message Date
Claude 59fd1f2244 build: use directories.add for benchmark androidTest resources
srcDirs(vararg) on AGP 9 source-set Resources is deprecated; switch to
the directories MutableSet API. Preserves the original behavior of
including quartz/src/androidDeviceTest/resources in :benchmark's
androidTest java resources (verified bip39.vectors.json shows up in
processBenchmarkAndroidTestJavaRes output).
2026-05-20 15:56:53 +00:00
Claude ebfd066d5c build: convert remaining .gradle files to Kotlin DSL
Converts the last four Groovy build scripts to Kotlin DSL so the entire
build is consistent with the rest of the modules (commons, quartz, etc.).

- settings.gradle -> settings.gradle.kts
- build.gradle (root) -> build.gradle.kts
- amethyst/build.gradle -> amethyst/build.gradle.kts
- benchmark/build.gradle -> benchmark/build.gradle.kts

Notes:
- Switched the per-subproject spotless format from groovyGradle to
  kotlinGradle now that the only gradle scripts are .gradle.kts.
- benchmark module: moved targetSdk from defaultConfig to testOptions —
  AGP 9 removed targetSdk from library defaultConfig.
- Reimplemented getCurrentBranch() with ProcessBuilder (Groovy's
  String.execute() isn't available in Kotlin DSL).
2026-05-20 14:17:47 +00:00
Claude 2e8bf0d45d build: remove unused :ammolite module
The :ammolite module contained no production Kotlin/Java sources (just
a manifest, build.gradle, and proguard stubs) and no module in the
codebase imports com.vitorpamplona.ammolite.*.

Removes:
- ammolite/ directory (5 files)
- :ammolite project include in settings.gradle
- implementation project(':ammolite') from :amethyst
- androidTestImplementation project(':ammolite') from :benchmark
- :ammolite:testDebugUnitTest from CI workflow and pre-push hook
- -keep class com.vitorpamplona.ammolite.** rules from
  :amethyst, :commons, and :desktopApp proguard files
- Stale references in CONTRIBUTING.md, CLAUDE.md, and the
  gradle-expert skill dependency-graph doc

Small build-graph win: one fewer module to configure, compile, lint,
and spotless-check on every build, and one fewer unit-test target in
both CI and the local pre-push hook.
2026-05-16 14:40:03 +00:00
Vitor Pamplona 3751d6dc28 Fixes warnings 2026-05-12 19:27:12 -04:00
Claude 2ad1a48123 refactor(quartz): migrate in-tree C secp256k1 to libschnorr256k1-kmp
The custom C secp256k1 implementation under quartz/src/main/c/ was never
wired into Gradle (no externalNativeBuild block) and only existed for the
3-way benchmark + cross-validation test against ACINQ on JVM/Android.
The C library has been split out to vitorpamplona/libschnorr256k1{,-kmp},
so the in-repo copy is dead weight.

This change replaces it with the published Maven Central artifact
`com.vitorpamplona:schnorr256k1-kmp:1.0.0`, scoped to test/benchmark
configurations only — production crypto continues to use ACINQ
secp256k1-kmp on JVM/Android and the pure-Kotlin Secp256k1 on native.

The Android AAR ships libschnorr256k1_jni.so for arm64-v8a and x86_64,
so the Android benchmark now exercises the C row automatically (no more
manual build_android.sh). On JVM the .so still has to be installed by the
developer; the cross-validation test and triple-benchmark gracefully skip
the C row when System.loadLibrary fails, matching prior behavior.

Verified on JVM: all 13 secp256k1 jvmTest classes pass (188 tests), and
ACINQ vs pure-Kotlin benchmark numbers match the documented baseline
within sandbox noise (verifySchnorr ~15k ops/s, signSchnorr cached
~33k ops/s, pubkeyCreate+Compress ~36k ops/s).

Removes ~5,100 LOC: quartz/src/main/c/ (4,500), Secp256k1InstanceC.*
expect/actual shim (560), Secp256k1C JNI declaration object.

https://claude.ai/code/session_01KnvpK2amcVZKfFiZJvHjVe
2026-04-28 15:36:25 +00:00
Claude 63da850e3b perf(thumbhash): cache cosine tables and flatten hot loop in decoder
The reference port recomputed the inverse DCT cosine tables once per output
pixel — for a 32x24 decode that's ~43k redundant cos() calls. This change
lifts the tables out of the inner loop and caches them across decodes
keyed by (size, componentCount) so subsequent placeholders at the same
target size skip cosine evaluation entirely.

Additional wins:
- AC coefficients unpack into pre-sized DoubleArrays (no ArrayList<Double>
  boxing, no post-hoc copy)
- truncated hashes are rejected up-front via a single length check instead
  of mid-stream
- LPQA -> sRGB uses an inline branch clamp instead of a
  min/max/round/coerceIn chain

Tests: 12 unit tests cover determinism across repeated decodes, cache
clearing, alpha preservation, aspect-ratio preservation both directions,
average-color drift bounds, truncated input rejection, and round-tripping
base64 vs. raw bytes. All green.

Bench: new ThumbHashBenchmark exercises opaque decode, alpha decode,
warm- and cold-cache decode, aspect-ratio probe, and the full
decodeKeepAspectRatio pipeline so the perf delta is visible on device.
2026-04-20 00:33:26 +00:00
Claude 944562ddc8 feat: add SHA-256 benchmark comparing Android native vs our C vs Kotlin
Three SHA-256 implementations benchmarked on the same phone:
- sha256Android: Android's MessageDigest (BoringSSL + ARM64 CE hardware)
- sha256OurC: Our C SHA-256 (ARM64 CE hardware via JNI)
- sha256Kotlin: Kotlin's sha256 (platform MessageDigest on Android)

Also add nativeSha256 JNI method to expose our hardware SHA-256.

Why our C matches ACINQ on ARM64 despite different architectures:
- Our 4x64: 16 MUL+UMULH pairs per field mul
- ACINQ 5x52: 25 MUL+UMULH pairs per field mul
- We save 9 multiply pairs (~27ns on Cortex-X3) per fe_mul
- ACINQ saves ~6 normalizations with lazy reduction (~6ns)
- Net advantage: ~21ns per mul × 500 muls/verify > 0
- Plus our SHA-256 uses hardware CE, ACINQ's is software

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-11 15:02:27 +00:00
Claude 29b678ab14 feat: add custom C secp256k1 implementation for maximum platform performance
Add a complete C implementation of secp256k1 elliptic curve operations
alongside the existing Kotlin implementation, enabling direct comparison
and extraction of maximum performance from each platform (ARM64, x86_64).

C Implementation (quartz/src/main/c/secp256k1/):
- field.h/c: 5x52-bit limb field arithmetic with __int128 support and
  lazy reduction (12-bit headroom per limb vs Kotlin's fully-packed 4x64)
- scalar.h/c: Scalar mod n arithmetic, GLV decomposition, wNAF encoding
- point.h/c: Jacobian point operations (3M+4S double, 8M+3S mixed add),
  GLV+wNAF scalar multiplication, Strauss/Shamir dual scalar multiply,
  Montgomery batch-to-affine, precomputed G tables (wNAF-12)
- schnorr.c: BIP-340 Schnorr sign/verify/verifyFast/verifyBatch with
  pubkey decompression cache and precomputed tag hash prefixes
- sha256.c: Self-contained SHA-256 for BIP-340 tagged hashes
- secp256k1_c.h: Public API matching the Kotlin Secp256k1 object
- jni_bridge.c: JNI bridge for JVM/Android integration
- benchmark.c: Standalone C benchmark (cmake build)
- CMakeLists.txt: Build system with ARM64/x86_64 optimization flags

Kotlin Integration:
- Secp256k1InstanceC: expect/actual wrapper (commonMain/jvmMain/androidMain/nativeMain)
- Secp256k1C: JVM JNI binding class
- Secp256k1TripleBenchmark: Three-way JVM benchmark (ACINQ vs Kotlin vs Custom C)
- Secp256k1CBenchmark: Android benchmark for the C implementation

Current status: sign works correctly (verified against BIP-340 test vectors),
verify path needs ecmult_double_g debugging (GLV wNAF-12 table issue). The
comb table for ecmult_gen also needs fixing (currently falls back to GLV+wNAF).
Field arithmetic is fully verified: 5x52 limbs with R=0x1000003D10 fold.

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-11 02:36:58 +00:00
Vitor Pamplona a9e59d817f Improves benchmark names, options and formatting 2026-04-09 19:25:47 -04:00
Claude da64a4a4d3 fix: align all 3 benchmarks to identical operation set
All 3 platform benchmarks (Android, JVM, K/Native) now measure the
same core operations for cross-platform comparability:

  verifySchnorr        — strict BIP-340
  verifySchnorrFast    — Nostr x-check only (no y-parity inversion)
  signSchnorr          — derives pubkey each time
  signSchnorr(cached)  — pre-computed x-only pubkey
  compressedPubKeyFor  — create + compress
  secKeyVerify         — key validation
  privKeyTweakAdd      — BIP-32 key derivation
  ecdhXOnly            — Nostr ECDH (NIP-04/44)
  batch verify (8, 16) — same-pubkey batch

Changes:
- Android: added signSchnorrCachedPkOurs, ecdhXOnly/ecdhXOnlyOurs
  (replaced pubKeyTweakMulCompact naming)
- JVM: removed pubkeyCreate and pubKeyCompress (subsets of
  compressedPubKeyFor, not measured on other platforms)
- K/Native: added verifySchnorrFast

K/Native retains field micro-benchmarks (FieldP.mul/sqr/add/sub/inv,
unsignedMultiplyHigh, uLt) as a K/N-specific diagnostic tool.

https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
2026-04-09 15:02:39 +00:00
Claude b3206b8e48 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
2026-04-09 14:45:14 +00:00
Claude fbcd0d6326 feat: expose verifySchnorrFast in Secp256k1InstanceOurs + Android benchmark
Wire verifySchnorrFast (x-check only, no y-parity inversion) into:
- Secp256k1InstanceOurs wrapper for app-level usage
- Android benchmark as verifySchnorrFastOurs for Pixel 8 measurement

Expected: ~15% faster than verifySchnorrOurs (~100μs vs ~120μs on Pixel 8)

https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
2026-04-09 12:33:13 +00:00
Claude 0c27ed89bc fix: align all 3 secp256k1 benchmarks for fairness and comparability
JVM benchmark fixes:
- signSchnorr(cached pk): document that native baseline still derives
  pubkey (apples-to-oranges by design — shows what caching enables)
- privKeyTweakAdd: document .copyOf() penalty on native side (ACINQ
  wrapper mutates input, requiring defensive copy)
- Remove duplicate tweakMulCompact test (redundant with ecdhXOnly)
- Add cache-warming note to benchmark KDoc
- Change output "slower" → neutral "x" (0.7x isn't "slower")

Android benchmark fixes:
- Remove misnamed pubKeyCompressOurs (was identical to compressedPubKeyForOurs)
- Remove duplicate pubKeyCompress (was also doing create+compress)
- Clean up structure: matched Native/Ours pairs with clear section headers
- Add cache-warming note to benchmark KDoc
- Standardize test data comment for cross-platform comparability

K/Native benchmark fixes:
- Fix DCE risk in micro-benchmarks: use result-dependent chains
  (out→out for field ops, xor hiSink for multiplyHigh) so LLVM can't
  hoist constant computations out of the loop
- Add batch verify (was missing — JVM had it, Android/K/N didn't)
- Add -opt documentation warning in KDoc
- Remove unused pubkeyCreate/pubKeyCompress standalone benchmarks
  (compressedPubKeyFor already covers create+compress)
- Extract printResults helper to reduce duplication

All 3 benchmarks now:
- Use the same test data (same hex keys across JVM/Android/K/N)
- Measure the same core operations (verify, sign, compressedPubKeyFor,
  secKeyVerify, privKeyTweakAdd, ecdhXOnly/pubKeyTweakMulCompact)
- Document cache-warming effects in their KDoc
- Include batch verify (JVM + K/N; Android uses framework-managed tests)

https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
2026-04-09 01:58:54 +00:00
Vitor Pamplona 9b4d6247f0 Adds an android benchmark comparison for the new Secp 2026-04-06 18:00:46 -04:00
Claude 78d8914ee1 test: add Android microbenchmark for secp256k1 operations
Creates Secp256k1Benchmark.kt in the benchmark module using AndroidX
Benchmark (Jetpack Microbenchmark) to measure all secp256k1 operations
on real Android hardware/emulator with proper warmup and statistics.

Benchmarks: verifySchnorr, signSchnorr, compressedPubKeyFor,
secKeyVerify, pubKeyCompress, privateKeyAdd, pubKeyTweakMulCompact.

Run with: ./gradlew :benchmark:connectedAndroidTest

The existing SignVerifyBenchmark already tests sign/verify through
Nip01Crypto, but this new benchmark tests the Secp256k1Instance
operations directly, matching the JVM benchmark for comparison.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-05 20:32:56 +00:00
davotoula c40c1ed383 perf: convert remaining 12 interpolated log calls to lambda overloads
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 13:07:34 +02:00
Vitor Pamplona 75a8e461ad 20x Faster Rfc3986Normalizer and way less objects being created. 2026-03-26 19:04:18 -04:00
Vitor Pamplona 7c8d44559e Benchmark for the new Rfc3986 Normalizer 2026-03-26 17:32:07 -04:00
Vitor Pamplona e2ac534be0 spotless 2026-03-25 12:55:31 -04:00
Vitor Pamplona d84d146981 Merge branch 'main' into claude/remove-libsodium-dependency-DjtWa 2026-03-25 11:24:55 -04:00
Vitor Pamplona 617db3613f Re-enables benchmark with native android ChaCha functions 2026-03-25 11:17:17 -04:00
Claude d586a0dc25 feat: replace libsodium with pure Kotlin ChaCha20/Poly1305 implementation
Remove LazySodium/JNA/libsodium native dependencies and replace with
pure Kotlin implementations of ChaCha20, HChaCha20, XChaCha20,
Poly1305 MAC, and XChaCha20-Poly1305 AEAD. This eliminates platform-
specific native binaries while maintaining full NIP-44 compatibility.

- Add ChaCha20Core with RFC 8439 block function, IETF stream XOR,
  HChaCha20, and XChaCha20
- Add Poly1305 MAC (RFC 8439 §2.5)
- Add XChaCha20Poly1305 AEAD encrypt/decrypt
- Convert LibSodiumInstance from expect/actual to commonMain object
- Delete platform-specific LibSodiumInstance actuals (android/jvm/ios)
- Remove iOS native interop (cinterop, linker opts, .a libraries)
- Remove lazysodium-java, lazysodium-android, JNA from build deps
- Clean up ProGuard rules (remove JNA/LazySodium keep rules)
- Add RFC 8439 + libsodium test vectors for ChaCha20 and XChaCha20
- Update benchmark to use simplified ChaCha20 API

https://claude.ai/code/session_01YHBwqnb3Z7Q7PX31tJ2G3i
2026-03-23 17:40:29 +00:00
Vitor Pamplona d431b12f94 Migrates EventStore from Android's SQLLite to KMP
Fixes testing of libsodium between java and android
2026-03-20 16:00:17 -04:00
Vitor Pamplona f4d401bc56 Switches to our own version of the Url Detector 2026-03-06 10:58:59 -05:00
Vitor Pamplona 64c8c0edfd Improves the name of the NIP01 Crypto object 2026-02-18 11:29:56 -05:00
Vitor Pamplona c755908db5 Removes string-concat compiler argument since it is not supported anymore 2026-02-17 19:02:53 -05:00
Vitor Pamplona cf7bdef028 Adds missing actual implementations on iOS 2026-02-17 19:00:24 -05:00
Vitor Pamplona eb66182211 - Updates several libraries
- Reformats code to the newest Klint
- Fixes isEmpty bug on Filters
2026-02-09 14:06:07 -05:00
Vitor Pamplona 710f15f790 - Fixes copyright using KDoc patterns instead of simpler comments
- Moves spotless and git-hooks to hidden folders
2026-02-09 13:06:41 -05:00
Vitor Pamplona 6125394354 Migrates to AGP 9 2026-02-08 16:33:39 -05:00
Vitor Pamplona 7bc7265757 Refactors the old NIP-05 code on Quartz
New Caching system for User metadata
New Caching system for NIP-05 verifications
2026-02-04 14:31:30 -05:00
Vitor Pamplona aa2f969be6 Faster event serializer -> sha256 -> id comparison without creating intermediary buffers. 2025-12-23 09:39:30 -05:00
Vitor Pamplona 79d6745ee6 Adds faster parsers for Messages and Commands
Unwraps inline runCatching because it seems faster for some reason.
2025-12-22 14:52:02 -05:00
Vitor Pamplona eefca1e3f4 Removes unnecessary tests 2025-12-22 08:11:55 -05:00
Vitor Pamplona 8cbc5f1e93 Faster Array Deserializer 2025-12-19 18:57:12 -05:00
Vitor Pamplona 664f0d0d59 Adds a Benchmark to simulate a real use on storage operators 2025-12-19 17:29:23 -05:00
Vitor Pamplona 8d15f0478f Faster MurMur hash for 64bit results. 2025-12-18 17:30:53 -05:00
Vitor Pamplona 7840bc3318 Uses in memory DB because that's where the performance hits 2025-12-18 11:57:38 -05:00
Vitor Pamplona 5df019e485 adds kotlin ktx to benchmark 2025-12-18 09:21:40 -05:00
Vitor Pamplona 625a12f4e2 Better way to clear the test db 2025-12-16 18:03:50 -05:00
Vitor Pamplona 0fb64c5ff7 Fixes new generics on the benchmark classes 2025-12-16 16:57:02 -05:00
Vitor Pamplona 83d14bd0e3 Minor adjustments and new benchmarks 2025-12-12 19:51:41 -05:00
Vitor Pamplona 95723479d6 Adds MurMur3128bits for x64 2025-12-12 16:54:34 -05:00
Vitor Pamplona 3eb662851b Adds a flatten to set utility 2025-10-31 18:36:05 -04:00
Vitor Pamplona bf664ea0c5 Avoids interning of large strings and objects where the duplication is not common 2025-10-28 12:18:19 -04:00
Vitor Pamplona c4653b147c Adds new benchmark cases 2025-10-20 17:10:08 -04:00
Vitor Pamplona 7994945209 30% Faster isHex for strings with 64 bytes. 2025-10-03 15:58:01 -04:00
Vitor Pamplona 7a280a3cde - Migrates Quartz from Android to CommonMain
- Fully converts OpenTimestamp Java codebase to Kotlin, migrating the sync and async HTTP call interfaces to OkHttp and coroutines
- Redesigns parsing of relay commands, messages and filters for performance in Jackson.
- Starts the use of KotlinX Serialization when speed is not a requirement
- Migrates all Jackson field annotations to Kotlin Serialization
- Migrates Regex use in Quarts to Kotlin's Regex class
- Migrates Base64 from Android to Kotlin
- Migrates UUID from Android/Java to Kotlin
- Migrates LRUCache usage from Android/Java to Kotlin collections
- Migrates all String to bytearray conversions to Kotlin methods
- Migrates all System.arraycopy calls to kotlin native ones.
- Separates parsing code from the data classes Companion objects
- Exposes Rfc3986 normalizations to each platform.
- Exposes URI parsing classes to each platform.
- Exposes URL Encoders to each platform.
- Exposes BigDecimal to each platform.
- Exposes the Url Detector to each platform.
- Exposes MacInstances to each platform
- Exposes Diggest instances to each platform.
- Exposes a BitSet to each platform.
- Exposes GZip to each platform.
- Exposes Secp256k1 to each platform.
- Exposes SecureRandom to each platform.
- Exposes Time in seconds to each platform.
- Exposes the LargeCache to each platform.
- Exposes AES CBC and AES GCM encryption/decryption to each platform
- Migrate test assertions to Kotlin Tests
- Exposes Address class to each platform because of the Parceleable
- Creates our own ByteArrayOutputStream.
- Removes Lock features inside the Bloomfilters because we don't need data consistency/
- Migrates UserMetadata parser from Jackson to Kotlin serialization
- Removes the need for Static methods in each tag.
- Adds an event template serializer
- Adds KotlinX Datetime to migrate some of the date-based logs
- Adds support for LibSodium in the JVM platform
- Creates a shared test build for iOS targets
- Fixes several usages of Reflection when serializing classes
- Fixes a bug on loading RelayDB for the HintBloom filter test
- Increases the Bloom filter space to better use hints in the app.
- Removes support for iOS in x86
- Creates a Jackson mapper just for NIP-55, which stays in the Android build only.
- Keeps the event store in the android build as well.
- Removes @Syncronized tags in favor of Mutexes.
- Improved sendAndWaitForResponse NostrClient method to properly account for returns from each relay.
- Removes the need for GlobalScope and async calls in the downloadFirstEvent method.
- Restructures the parser and serialization of the relay messages and commands for performance with Jackson
- Removes the dependency on Jackson's error classes across the codebase.
- Moves the hint to quote tag extension methods to their own packages.
- Speeds up the generation of Bech32 addresses
- Migrates NIP-6 and Blossom uploads to use Kotlin Serialization
2025-09-22 19:44:51 -04:00
Vitor Pamplona faffe2a936 About 30-40% event hashing performance boost 2025-09-17 08:40:06 -04:00
Vitor Pamplona 83e87c2cf4 Adds an event id hash check that doesn't create a separate bytearray to compare between the incoming hex from the relay and the recalculated byte array from SHA256. 2025-09-17 08:40:06 -04:00