Commit Graph

33 Commits

Author SHA1 Message Date
Claude e07090d4fa feat(quartz): FsEventStore indexes + query planner (step 2)
Hardlink indexes under idx/ and a minimal query planner bring the
file-backed store up to parity with SQLite on filtered lookups.

- FsLayout — path helpers, .seed file (8 random bytes, salts all hashes),
  entry filename format <zero-padded ts>-<id>.
- FsIndexer — on insert creates hardlinks at idx/kind/<k>/, idx/author/
  <pk>/, idx/owner/<owner_hex>/, idx/tag/<name>/<hash_hex>/. Owner hash
  matches SQLite's pubkey_owner_hash (recipient for GiftWrap). Honours
  DefaultIndexingStrategy: single-letter tag names only. On delete
  unlinks every known path so the inode can be reclaimed.
- FsQueryPlanner — picks a driver (ids / first tag / kinds / authors /
  all kinds) and yields candidates sorted by createdAt DESC. Final
  predicate check runs through Filter.match so any driver is
  correctness-safe.
- FsEventStore — sets mtime to event.createdAt on write, runs queries
  through the planner with post-filter, applies limit, dedupes by id
  across multi-filter unions, and unlinks indexes on delete.

Tests: 16 new in FsQueryTest covering order, limit, author / kind /
tag drivers, tag OR within a key, tagsAll AND across keys, non-
single-letter-tag behaviour, since/until, count, index hardlink
maintenance, and reopen persistence. All 26 fs tests green.
2026-04-24 21:22:55 +00:00
Claude 3550044ae0 feat(quartz): FsEventStore skeleton — insert, query by id, delete (step 1)
First slice of the file-backed IEventStore planned in
cli/plans/2026-04-24-file-event-store-*. Each event is stored as
events/<aa>/<bb>/<id>.json with atomic tmp+rename writes. Ephemeral
kinds are dropped. Duplicate inserts are no-ops (id-level uniqueness).
Staging leftovers are swept on open.

Only id-based query and delete are wired; the query planner, indexes,
replaceable/addressable slots, tombstones, vanish, expiration sweep,
FTS, and transactions land in later steps.

Tests: 10 new tests under quartz jvmTest, all passing.
2026-04-24 21:10:50 +00:00
Claude 566a0997a2 fix(secp256k1): harden C/Kotlin field and scalar arithmetic; add dedicated fe_sqr
Correctness (C):
- scalar_mul: rewrite with a loop-driven fold so the 512-bit product is
  fully reduced regardless of the pre-existing third-fold carry-drop bug;
  also fixes the portable (!HAVE_INT128) fallback which was returning
  (a*b) mod 2^256 instead of (a*b) mod n.
- fe_mul / reduce_wide: loop the final carry fold in a while(carry) rather
  than a single if(carry), so a secondary carry-out is never silently
  dropped for adversarial or deeply lazy-reduced inputs.
- scalar_add: reuse the precomputed SCALAR_NC constant instead of
  recomputing n's two's-complement arithmetically each call.
- fe_negate: remove the data-dependent early-return for a == 0; compute
  P - a unconditionally and fold P back to 0 with a final fe_normalize,
  matching the Kotlin FieldP.neg path and dropping a branch.

Performance (C):
- Add a dedicated 10-mul fe_sqr_inline using __int128 (4 diagonal + 6
  doubled cross products, three-pass structure mirroring Kotlin
  U256.sqrWide). The previous fe_sqr delegated to fe_mul(a, a) using all
  16 schoolbook products; the new path saves ~37% of the multiplications
  at every squaring, and doublePoint/addPoints do ~9 sqrs each in the hot
  Jacobian loop. Col-3 mixes two products so the accumulator is split
  explicitly to avoid a uint128 overflow; the bug was caught by the C
  benchmark's self-test.
- Enable LTO (CMAKE_INTERPROCEDURAL_OPTIMIZATION) when the toolchain
  supports it, recovering cross-TU inlining of fe_mul/fe_sqr into point.c
  and schnorr.c.
- jni_bridge: replace the pinning GetByteArrayElements path (which blocks
  GC compaction) with a stack-or-heap copy_msg_bytes helper that uses
  GetByteArrayRegion. Short messages (32 B event digests, the common case
  for Nostr) hit a 512 B on-stack buffer.

Correctness (Kotlin):
- FieldP.neg: remove the early-return on zero for the same reasons as the
  C side, with a trailing reduceSelf(out) to collapse the P result back
  to 0 when the input was zero.
- Secp256k1.signSchnorrInternal / privKeyTweakAdd: switch the crypto-
  edge-case failure modes from generic require() to check() with clear
  messages, documenting them as invariants rather than argument errors
  and matching the C side's return-code semantics.

Tests & benchmarks:
- Add Secp256k1CrossValidationTest (jvmTest) that byte-for-byte compares
  Kotlin, ACINQ, and the custom C implementation across pubkey creation,
  Schnorr signing (incl. variable message lengths on the Kotlin side),
  privKeyTweakAdd, and x-only ECDH. This is the strongest parity check we
  can run without a third reference, and it's deterministic for
  reproducibility (fixed LCG seed).
- Add adversarial FieldPTest cases that chain lazy adds into mul/sqr/inv
  to exercise the new fe_mul fold loop and the dedicated fe_sqr path.
- Fix pre-existing FieldPTest/GlvTest failures (addNearP, addNegIsZero,
  halfOfOdd, invMulIsOne, invOfTwo, reduceWideWithMaxValues, betaCubedIsOne)
  that were asserting on raw limbs of lazy-reduced values; they now
  reduceSelf before comparing, consistent with the rest of the suite.
- Secp256k1Benchmark: document the intentional apples-to-apples
  asymmetries (signSchnorrWithPubKey vs signSchnorr, ecdhXOnly vs
  pubKeyTweakMul, privKeyTweakAdd's copyOf() penalty) and add a
  taggedHash benchmark since NIP-44 leans heavily on it.

All 188 secp256k1 Kotlin tests pass; the C library builds cleanly with
LTO enabled and the secp256k1_bench self-verification succeeds.

https://claude.ai/code/session_01KExJURZATpL59ZKXP6AVP6
2026-04-12 19:58:34 +00:00
Claude 09fb48e75c fix: change crossinline to noinline for nullable lambda parameter
Kotlin does not allow crossinline parameters to be nullable. The cOp
parameter in benchTriple needs to be nullable (null when C library is
not available), so use noinline instead.

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-11 03:50:08 +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
Claude a2ba64baba feat: migrate secp256k1 from LongArray to Fe4/Wide8 structs
Full migration of the secp256k1 library from LongArray(4)/LongArray(8)
to Fe4/Wide8 struct types with @JvmField named Long fields. This
eliminates all array bounds checks from the hot path.

Files migrated (13 source + 7 test + 2 benchmark):
  - U256.kt, FieldP.kt, ScalarN.kt, Glv.kt, ECPoint.kt
  - FieldMulPlatform.kt (expect + 3 actuals), FieldMulFused.kt
  - PointTypes.kt (MutablePoint, AffinePoint, PointScratch)
  - KeyCodec.kt, Secp256k1.kt
  - All test files and benchmarks

Bytecode impact:
  Before: 464 laload/lastore (bounds-checked) in core arithmetic
  After:  0 laload/lastore, all getfield/putfield (no checks)

The public API (Secp256k1 object) is unchanged - it still accepts
and returns ByteArray. Fe4 conversion happens at the API boundary
via U256.fromBytes()/U256.toBytes().

All secp256k1 unit tests pass on JVM.

https://claude.ai/code/session_01Sxi6Gpxbstuj3Y8TBY7XrU
2026-04-10 03:30:53 +00:00
Claude ea8b693785 feat: add Fe4 struct-based field elements to eliminate array bounds checks
Prototype Fe4/Wide8 classes that replace LongArray(4)/LongArray(8) with
named @JvmField Long fields. Every LongArray access compiles to
laload/lastore (3 bytecode insns + implicit bounds check); Fe4 field
access compiles to getfield/putfield (2 insns, zero checks).

Bytecode impact (core arithmetic files):
  LongArray: 464 bounds-checked array ops (U256: 150, FieldP: 119, Fused: 195)
  Fe4:         0 bounds-checked ops, 203 direct field accesses

JVM benchmark results (HotSpot C2, best of 3 rounds, alternating order):
  FieldP.mul:  40 ns → 41 ns  (~equal, C2 fully optimizes hot mul)
  FieldP.sqr:  48 ns → 30 ns  (+60% faster)
  FieldP.add:   9 ns →  6 ns  (+34% faster)
  FieldP.sub:  10 ns →  6 ns  (+55% faster)
  U256.sqrWide: 37 ns → 17 ns (+114% faster)

The gains are largest for sqr/add/sub which have high ratios of
array read-modify-write patterns. Expected to be even larger on
Android ART (limited inlining) and Kotlin/Native LLVM (AOT, no
profile-guided bounds check elimination).

https://claude.ai/code/session_01Sxi6Gpxbstuj3Y8TBY7XrU
2026-04-10 02:31:55 +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 e872a37e86 feat: add verifySchnorrFast for Nostr — skip y-parity inversion
BIP-340 verification checks both R.x == r AND R.y is even. The y-parity
check requires a full field inversion (~270 field ops, ~14% of verify).

verifySchnorrFast skips the y-parity check, verifying only the
x-coordinate in Jacobian coordinates (2 field ops, no inversion).

WHY THIS IS SAFE FOR NOSTR:
For a given x on secp256k1, there are exactly 2 points: (x, y_even) and
(x, y_odd). A signature producing the correct x but wrong y-parity would
require solving the discrete log — equivalent to forging the signature.
The y-parity check is defense-in-depth, not a distinct security boundary.

DO NOT use for Bitcoin/financial protocols — use verifySchnorr for strict
BIP-340 compliance.

JVM benchmark:
  verifySchnorrFast:  20,706 ops/s  (1.4× vs native C)
  verifySchnorr:      18,038 ops/s  (1.6× vs native C)
  Improvement: ~15% faster

https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
2026-04-09 03:00:41 +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
Claude df9c8a9f4b feat: add same-pubkey batch Schnorr verification (5-6x throughput)
Add verifySchnorrBatch(pub, signatures, messages) that verifies n
signatures from the same public key using scalar and point summation
instead of n individual mulDoubleG calls.

The batch equation exploits linearity of Schnorr signatures:
  (Σ sᵢ)·G = (Σ Rᵢ) + (Σ eᵢ)·P
This combines n verifications into:
  - n scalar additions for S = Σsᵢ and E = Σeᵢ (trivial)
  - n liftX + (n-1) addMixed for R_sum = ΣRᵢ (cheap point additions)
  - ONE mulDoubleG(S, P, -E) (the expensive EC operation, done once)
  - Check result - R_sum = O (no toAffine needed)

JVM benchmark results (same pubkey, 1000 iterations, 500 warmup):
  batch( 4):  4.1x faster than individual  (40,121 events/s)
  batch( 8):  5.3x faster                  (49,637 events/s)
  batch(16):  5.5x faster                  (49,815 events/s)
  batch(32):  5.9x faster                  (51,272 events/s)

This fits the Nostr pattern perfectly: when loading a profile or
connecting to a relay, many events from the same followed author
arrive together and can be batch-verified.

If the batch fails (returns false), the caller falls back to
individual verification to identify which signature(s) are invalid.

Security: by linearity, individual errors cannot cancel without
solving the discrete log. For extra hardening, duplicate events
from multiple relays can be verified individually as cross-checks.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 02:56:45 +00:00
Claude 8a6d1f1d44 perf: increase benchmark warmup and iterations for stable results
The previous counts (200 warmup / 500 iterations for verify) were too
low for reliable measurements on noisy VMs. Increase to 2000+ warmup
and 3000-50000 iterations depending on operation cost, giving the JIT
time to fully optimize and reducing variance between runs.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-07 23:46:59 +00:00
Claude ef7790b775 docs: update secp256k1 documentation with benchmark results and architecture notes
Update KDoc and file headers across Secp256k1.kt, Point.kt, FieldP.kt,
and U256.kt with:
- Accurate benchmark numbers (well-warmed: verify 4.4x, sign 1.5x,
  pubCreate 2.2x, ECDH 3.9x, compress 2x FASTER)
- Detailed comparison with C libsecp256k1 architecture choices
- Explanation of why certain C optimizations don't port to JVM:
  * Lazy reduction: 4x64 limbs have no headroom (C's 5x52 has 12 bits)
  * safegcd: slower on JVM due to 128-bit arithmetic overhead
  * WINDOW_G=15: cache pressure from heap-allocated objects (w=12 optimal)
- Document effective-affine technique and batch inversion
- Increase all benchmark warmup/iterations for stable measurements

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 17:32:50 +00:00
Claude beaf452d55 perf: keep WINDOW_G=12, increase benchmark warmup
Tested WINDOW_G=15 (matching C libsecp256k1): 5.6x slower than native.
WINDOW_G=12 is faster at 4.3x because the 8192-entry table (1MB) at
w=15 causes cache pressure on JVM — heap-allocated AffinePoint objects
are scattered in memory, unlike C's contiguous compile-time .rodata.
WINDOW_G=12 (1024 entries, ~128KB) fits comfortably in L2 cache.

Increased verify benchmark warmup to 200+500 for stable measurements
(first call builds the lazy table).

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 17:23:13 +00:00
Claude cb78f1a34e bench: increase warmup for verify/ECDH benchmarks
More warmup iterations needed for WINDOW_G=12 (1024-entry table
built lazily on first call). Avoids table-build cost contaminating
measured iterations.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 15:09:33 +00:00
Claude 2661bf783d perf: add toAffineX for x-only ECDH, benchmark ecdhXOnly path
- Add ECPoint.toAffineX that computes only x = X/Z² (saves 2M vs full
  toAffine which also computes Y/Z³)
- Use toAffineX in ecdhXOnly since only the x-coordinate is needed
- Add ecdhXOnly benchmark measuring the actual Nostr ECDH production
  path (Secp256k1Instance.pubKeyTweakMulCompact delegates to ecdhXOnly)
- Fix ktlint KDoc-inside-class-body violations

The old benchmark measured pubKeyTweakMul(02||x, key) which pays for:
array allocation, compressed key parsing (sqrt), full toAffine, and
re-serialization. ecdhXOnly avoids the array overhead and serialization.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 14:27:30 +00:00
Claude 629450fed2 perf: signSchnorr 4.8x faster — remove self-verify, add cached-pubkey path
Analysis of the C library's schnorrsig_sign showed three key differences:
1. C takes a pre-computed keypair (no pubkey derivation during signing)
2. C does NOT self-verify the signature after signing
3. C does 1 EC multiplication total; we were doing 3

Changes:
- signSchnorrInternal: extracted core signing logic without pubkey derivation
  or self-verification. Does exactly 1 mulG (for R = k·G) matching the C library.
- signSchnorr: convenience overload that derives pubkey then calls internal.
  Now ~2x faster since self-verify is removed.
- signSchnorrWithPubKey: fast path accepting a 33-byte compressed pubkey
  (includes y-parity in the 02/03 prefix). Skips the pubkey G multiplication
  entirely. ~4.8x faster than the previous signSchnorr.
- Secp256k1Instance: added signSchnorrWithPubKey forwarding method.
- Benchmark: added "signSchnorr (cached pk)" test comparing both paths.

The self-verify removal is safe: the BIP-340 test vectors (which include
the exact expected signatures) validate correctness, and the C reference
library does not self-verify either.

Benchmark:
  signSchnorr:            1,516 → 2,915 ops/s (1.9x faster, 17x → 9.5x)
  signSchnorr (cached pk):  N/A → 7,261 ops/s (3.9x vs native — new!)

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-05 21:12:59 +00:00
Claude fbd145557e test: add secp256k1 benchmark comparing native JNI vs pure Kotlin
Benchmarks all Secp256k1Instance operations: verifySchnorr, signSchnorr,
pubkeyCreate, pubKeyCompress, pubKeyTweakMul (ECDH), privKeyTweakAdd,
secKeyVerify, and the combined patterns used by the codebase.

Results on JVM:
  verifySchnorr    ~1,940 ops/s (Kotlin) vs ~25,000 ops/s (native) = ~13x
  signSchnorr      ~  820 ops/s (Kotlin) vs ~27,000 ops/s (native) = ~33x
  pubkeyCreate     ~3,130 ops/s (Kotlin) vs ~55,000 ops/s (native) = ~18x
  pubKeyTweakMul   ~2,740 ops/s (Kotlin) vs ~29,000 ops/s (native) = ~11x
  privKeyTweakAdd  ~1.66M ops/s (Kotlin) vs ~1.46M ops/s (native)  = 0.9x (faster!)
  secKeyVerify     ~1.98M ops/s (Kotlin) vs ~3.88M ops/s (native)  = 2x

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-05 20:32:54 +00:00
Vitor Pamplona 7583f7f8e1 Moves some of the nip-44 based tests to commonTest 2026-03-25 08:33:04 -04: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
Claude 5029db2302 refactor: reorganize nip47 wallet connect package into subpackages
Split the flat nip47WalletConnect package into logical subpackages:
- rpc/ - JSON-RPC protocol models (Request, Response, Notification, NwcMethod, NwcErrorCode, NwcTransaction, etc.)
- events/ - Nostr event types (LnZapPaymentRequestEvent, LnZapPaymentResponseEvent, NwcInfoEvent, NwcNotificationEvent)
- cache/ - Decryption caches (NostrWalletConnectRequestCache, NostrWalletConnectResponseCache)

Root level keeps the entry points: Nip47WalletConnect (URI parsing), Nip47Client, Nip47Server.
Existing subpackages (tags/, kotlinSerialization/, jackson/) remain unchanged.

https://claude.ai/code/session_018YcwMeTHPXmeqpbZHcBEUg
2026-03-17 19:04:24 +00:00
Claude 883a2f995e style: apply ktlint formatting fixes
https://claude.ai/code/session_01HeWiRdKDTnMGfEBUegk3d1
2026-03-17 03:43:19 +00:00
Vitor Pamplona 56cce46858 Solving all problems with Quartz tests 2026-03-16 19:22:28 -04:00
Vitor Pamplona 962da3280b Moves the N44 test to Jvmtest 2026-03-16 18:57:00 -04:00
M ee418bdd0a fix: address race conditions and improve Namecoin NIP-05 resolution
Fixes discovered while porting Namecoin NIP-05 to Primal Android
(PrimalHQ/primal-android-app#934):

1. Race condition: stale intermediate lookups overwrite final result
   - Changed map to mapLatest in SearchBarViewModel so that previous
     in-flight ElectrumX lookups are cancelled when the query changes
   - Without this, typing 'd/testls' character by character causes
     stale results from 'd/test', 'd/testl' etc. to arrive after
     the correct 'd/testls' result and overwrite it with null

2. CancellationException swallowed in catch block
   - mapLatest cancels previous coroutines via CancellationException
   - The generic catch was catching it and emitting null, wiping the
     valid result. Now re-throws CancellationException

3. Root lookup fails when names object has no underscore key
   - extractFromDomainValue tried names[localPart] then names[underscore]
     which is the same thing for root lookups
   - Now falls back to first available entry for root lookups when
     no underscore key exists
   - Non-root lookups still fail correctly (no false matches)

4. Global Mutex serializes all lookups
   - Single Mutex in ElectrumxClient blocked unrelated lookups to
     different servers. A 25s timeout on one server stalled everything
   - Now uses per-server mutexes via ConcurrentHashMap

5. customServers set but never read
   - NamecoinNameService.setCustomServers() stored the list but the
     resolver was constructed with the default serverListProvider
   - Now wires customServers through to the resolver

6. resolveLive uses orphaned CoroutineScope
   - Now accepts an optional external scope parameter so callers can
     tie resolution to their own lifecycle

Adds 2 new tests: root fallback to first entry, non-root no-fallback.
2026-03-06 14:14:29 +11:00
Vitor Pamplona d662cd6b64 Merge pull request #1757 from KotlinGeekDev/kmp-completeness
KMP Completeness, Part 1.
2026-03-04 10:27:24 -05:00
Vitor Pamplona 470b1680d0 Moves electrum client to the jvmAndroid scope 2026-03-04 10:00:33 -05:00
KotlinGeekDev ff7aa3df79 Merge branch 'vitorpamplona:main' into kmp-completeness 2026-03-04 14:40:12 +01:00
M d82ace2f63 feat: Namecoin NIP-05 identity verification via ElectrumX
Add censorship-resistant NIP-05 verification using the Namecoin blockchain.
Users can set their nip05 field to a .bit domain (e.g. alice@example.bit)
or direct Namecoin name (d/example, id/alice) and Amethyst will resolve
the pubkey mapping via ElectrumX instead of HTTP.

Resolution uses the standard Electrum protocol (scripthash-based lookups):
- Build canonical name index script matching ElectrumX-NMC indexing
- Query blockchain.scripthash.get_history for the name's tx history
- Parse NAME_UPDATE script from the latest transaction output
- Extract Nostr pubkey from the name's JSON value

Supports both d/ (domain) and id/ (identity) Namecoin namespaces,
simple and extended NIP-05-like value formats with relay hints,
LRU caching with 1h TTL, and self-signed TLS certificates.

New files:
- quartz: ElectrumxClient, NamecoinNameResolver, NamecoinLookupCache
- amethyst: NamecoinNameService, Nip05NamecoinAdapter, NamecoinVerificationDisplay
- docs: namecoin-nip05-design.md
- tests: NamecoinNameResolverTest

Modified:
- Nip05Client: optional namecoinResolver routes .bit to blockchain
- AppModules: wire up resolver

See docs/namecoin-nip05-design.md for full architecture and protocol details.
2026-03-02 13:07:46 +11:00
KotlinGeekDev bb5d785cb3 Enable running tests with resources for the androidHostTest sourceset, specifically HintIndexerTest. Replace non-null assertion with exception throw, to precisely identify test resource errors. 2026-02-12 15:02:02 +01: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 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