Commit Graph

11248 Commits

Author SHA1 Message Date
Claude 20bab17440 perf: pre-allocate P-side tables and batch inversion temps in PointScratch
Eliminates ~80 LongArray allocations per mul/mulDoubleG call by
pre-allocating the P-side Jacobian and affine tables, the doubling
temp, and the batch inversion scratch buffers in PointScratch
(thread-local, allocated once per thread, reused across calls).

Before: mul() allocated 8 MutablePoint (24 LongArray) + 8 MutablePoint
(24 LongArray) + 16 AffinePoint (32 LongArray) + batch temps = ~92
LongArray per call. After: 0 allocations in the table construction path.

Also fixes minor allocation in addMixed degenerate case (use t[5]
scratch instead of new LongArray(4)).

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 19:22:38 +00:00
Vitor Pamplona 121eabd51f Merge pull request #2161 from vitorpamplona/claude/add-relay-drag-drop-uFrmj
Add drag-to-reorder functionality for relay lists
2026-04-06 15:10:28 -04:00
Vitor Pamplona 2b200538f0 Adjusting elevation borders 2026-04-06 15:07:08 -04:00
Claude 09427fe6cf perf: direct Math.unsignedMultiplyHigh call — eliminate MethodHandle boxing
The JVM target is Java 21, so Math.unsignedMultiplyHigh (Java 18+) can
be called directly without MethodHandle reflection. The previous approach
used MethodHandle.invokeExact which Kotlin compiles with Object return
type, causing Long boxing on every call (3 box/unbox per invocation ×
16 calls per field multiply = 48 boxed objects per mul).

Direct call compiles to a single UMULH instruction with zero overhead.
This is the most performance-critical function: called ~12,000× per
signature verification.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 19:01:38 +00:00
Claude 7e8a060f17 perf: optimize reduceSelf for secp256k1, pre-allocate wNAF scratch
Two microoptimizations:

1. reduceSelf: exploit P's structure (P[1..3] = 0xFFFFFFFFFFFFFFFF).
   a >= P only if all top 3 limbs are max AND a[0] >= P[0]. The first
   check (a[3] == -1) fails >99.99% of the time, making this a single
   branch miss prediction instead of a 4-limb comparison loop.
   Called ~1,300× per verify, ~500× per ECDH.

2. Pre-allocate wNAF IntArrays and scratch MutablePoint/LongArray in
   PointScratch. Eliminates 8-12 IntArray(145) + 8-12 LongArray(4)
   allocations per mul/mulDoubleG call. Adds wnafInto() to Glv that
   writes into caller-provided arrays.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 18:33:15 +00:00
Claude 8a64e218b8 fix: prevent double-swap ping-pong during relay drag
After swapping with an adjacent item, the dragOffset adjustment can
flip its sign (e.g. negative becomes positive), which immediately
triggers the second if-block to swap back in the opposite direction
within the same onDrag call. This causes the dragged item to jump
and the user to end up dragging a different item. Fix by returning
after each successful swap so only one swap occurs per drag event.

https://claude.ai/code/session_01RVM5kEJGrCaJTaP3GmVHDd
2026-04-06 18:29:25 +00:00
Claude e256fca772 perf: shared Z inversion for GLV table pairs — saves one full inversion
pOdd and pLamOdd always have identical Z coordinates (the GLV
endomorphism λ(X,Y,Z) = (β·X, Y, Z) preserves Z). Previously we
called batchToAffine separately for each table, paying two full field
inversions (~270 field ops each). Now batchToAffinePair uses a single
batch inversion and reuses the Z⁻¹ values for both tables.

Saves ~270 field ops per mul/mulDoubleG call (~12% of ECDH cost).

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 18:18:48 +00:00
Claude a092e78e8b fix: preserve relay order from event instead of sorting by bytes
Remove the sortedBy(receivedBytes) from relayListBuilder and
Nip65RelayListViewModel.clear() so relays keep their order as
stored in the Nostr event. This makes drag-and-drop reordering
meaningful since the saved order is now preserved on reload.

https://claude.ai/code/session_01RVM5kEJGrCaJTaP3GmVHDd
2026-04-06 18:15:21 +00:00
Claude cd860472ec fix: prevent drag gesture cancellation on item swap
When items swap during drag, the composable gets a new index value.
Using pointerInput(index) caused the gesture scope to restart, killing
the in-progress drag and leaving the UI stuck. Fix by using
rememberUpdatedState for the index so the pointerInput scope stays
alive across recompositions while always reading the current index.

https://claude.ai/code/session_01RVM5kEJGrCaJTaP3GmVHDd
2026-04-06 18:02:47 +00:00
Claude 1b9706c23f perf: use Math.unsignedMultiplyHigh on Java 18+ — up to 61% faster
On Java 18+, Math.unsignedMultiplyHigh compiles to a single UMULH
instruction, eliminating the 4-instruction signed→unsigned correction
(multiplyHigh + 2 AND + 1 SHR + 2 ADD) per 64×64 product. With 16
products per field multiply, this saves ~64 instructions per mul/sqr.

Detection uses MethodHandle resolved once at class init. On older JVMs
and Android, falls back to the signed+correction path automatically.

Results on Java 21 (vs previous):
  pubkeyCreate:  23,400 → 37,700 ops/s (+61%, 2.2× → 1.6× native)
  sign (cached): 16,700 → 27,400 ops/s (+64%, 1.5× → 1.1× native)
  verify:         5,600 →  7,300 ops/s (+31%, 4.4× → 3.6× native)
  ECDH:           7,100 → 10,000 ops/s (+41%, 3.9× → 3.5× native)
  ecdhXOnly:      5,600 → 10,500 ops/s (+88%, 4.4× → 2.6× native)

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 17:58:02 +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
Vitor Pamplona 4cda12b661 Merge pull request #2160 from vitorpamplona/claude/fix-webrtc-notification-permissions-wWe6i
Add permission handling for accepting calls from notifications
2026-04-06 13:17:05 -04:00
Claude 69ee4bd11d revert: remove safegcd — Fermat chain is faster on JVM
The safegcd (Bernstein-Yang divsteps) algorithm is faster than Fermat
in C due to native 128-bit integer support, but on JVM the 128-bit
arithmetic overhead via multiplyHigh + carry tracking in the inner
loop (12 rounds × matrix multiply on 5 limbs) is slower than the
Fermat addition chain (255 sqr + 15 mul of optimized field ops).

Benchmark showed 8.3x vs native (was 5.0x with Fermat), confirming
that the per-operation constant factor matters more than algorithmic
complexity for this problem size on JVM.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 16:50:33 +00:00
Claude 59df3162a6 perf: implement safegcd modular inverse (Bernstein-Yang 2019)
Replace Fermat's little theorem (a^(p-2), 255 sqr + 15 mul) with the
safegcd divsteps algorithm for field element inversion. This processes
62 divsteps per batch using a 2×2 transition matrix applied to
full-precision values via 128-bit arithmetic (multiplyHigh).

12 rounds × 62 steps = 744 total divsteps (≥741 needed for 256 bits).
Uses 5×62-bit signed limb representation for intermediate values and
Montgomery-style correction (precomputed p^{-1} mod 2^62) for the
modular reduction in updateDE.

The old Fermat chain is preserved as FieldP.invFermat for reference.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 16:47:17 +00:00
Claude 36d153150c fix: request microphone/camera permissions when accepting call from notification
When accepting an incoming WebRTC call via the notification's Accept
button, CallActivity now checks for RECORD_AUDIO and CAMERA permissions
before proceeding. If permissions are missing, the system permission
dialog is shown and the call is accepted only after permissions are
granted. This prevents the crash that occurred when accepting calls
from the notification without prior permission grants.

https://claude.ai/code/session_0193KQShzBh4puYh74dHgSbE
2026-04-06 16:38:32 +00:00
Claude 76899a417a fix: disable LazyColumn scroll during relay drag
Set userScrollEnabled = !isDragging on all LazyColumns that host
draggable relay items so the scroll gesture doesn't fight the drag
gesture. In AllRelayListScreen, scroll is disabled when any of the
12 category drag states is active.

https://claude.ai/code/session_01RVM5kEJGrCaJTaP3GmVHDd
2026-04-06 16:34:47 +00:00
Vitor Pamplona a649ab7b0e Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-04-06 12:03:57 -04:00
Vitor Pamplona 5a36d0b7ae Code clean up and building a common parent class for WebRTC calls 2026-04-06 12:02:08 -04:00
Claude 5b95a47e0e refactor: keep lazy itemsIndexed for relay drag-and-drop
Replace the single-item Column approach with RelayDragState that works
within itemsIndexed. Each relay item remains a separate lazy item for
better performance with large lists. The drag handle icon on each row
captures drag gestures while the item modifier applies visual feedback
(elevation, scale, translation).

https://claude.ai/code/session_01RVM5kEJGrCaJTaP3GmVHDd
2026-04-06 15:29:19 +00:00
Vitor Pamplona da953ae9c3 Merge pull request #2159 from vitorpamplona/claude/add-danger-zone-section-PYwIs
Reorganize settings: move sensitive options to "Danger Zone" section
2026-04-06 11:17:11 -04:00
Claude 04dd2b5dce feat: add Danger Zone section to settings screen
Move Backup Keys, Request to Vanish, and Vanish History to a new
"Danger Zone" section at the bottom of the settings screen.

https://claude.ai/code/session_01UMEzMjGBZkTpNVebFA6XXN
2026-04-06 15:12:17 +00:00
Claude ce11edc352 feat: add drag-and-drop reordering to all relay list settings
Add DraggableRelayList composable with gesture-based drag-and-drop,
following the same pattern used in ReactionsSettingsScreen. Each relay
category (DM, NIP65 home/notif, search, blocked, trusted, local,
broadcast, indexer, proxy, private outbox, favorites) now shows a
drag handle and supports reordering via drag gestures.

https://claude.ai/code/session_01RVM5kEJGrCaJTaP3GmVHDd
2026-04-06 15:10:33 +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 47c92eb209 chore: ignore agent worktree directories
https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 14:56:12 +00:00
Claude 80dc164c7c perf: increase G window to 12, add batch inversion for table building
- Increase WINDOW_G from 8 to 12 for mulDoubleG (verify): reduces
  G-side additions from ~32 to ~22 per verification (saves ~110 field ops)
- Add batchToAffine using Montgomery's trick: 1 inversion + 3(n-1) muls
  instead of n individual inversions. Critical for the 1024-entry table.
- Table size: 1024 entries × 64 bytes = ~128KB (lazy, built on first use)

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 14:55:14 +00:00
Vitor Pamplona 2e3de795b6 Merge pull request #2158 from vitorpamplona/claude/fix-keypackage-status-jK921
Add KeyPackage publication status tracking for Marmot groups
2026-04-06 10:46:16 -04:00
Claude 66e6b0b726 fix: track KeyPackage published status and subscribe to own key packages
The publish KeyPackage button was always active because the app didn't
track whether a key package had already been published. This adds:

- hasActiveKeyPackages() to KeyPackageRotationManager and MarmotManager
- hasPublishedKeyPackage() to Account, checking both in-memory bundles
  and the local cache for existing kind:30443 events
- Own key package filter in MarmotSubscriptionManager and the EOSE
  manager so previously published key packages are downloaded from
  relays on app restart
- UI feedback: primary-colored key icon when published, contextual
  empty-state message, and a spinner during publishing

https://claude.ai/code/session_01BVe7aSEWd2KLi5Ks6RZkcc
2026-04-06 14:43:06 +00:00
Vitor Pamplona b1f54a5c07 Removing padding when 0 2026-04-06 10:28:21 -04: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
Vitor Pamplona 6251653951 Merge pull request #2157 from vitorpamplona/claude/clarify-keypackage-publishing-l9gb0
Refactor Marmot group chat UI with improved message composer
2026-04-06 10:20:20 -04:00
Claude a162f0d5cc feat: align Marmot chat message input with other chat screens
Replace the raw OutlinedTextField + IconButton Row with
ThinPaddingTextField, ThinSendButton, EditFieldBorder, and
EditFieldModifier to match the private DM and channel chat layouts.

https://claude.ai/code/session_013dhfy18dfuSYN8khXiLLBb
2026-04-06 14:17:57 +00:00
Claude 3e19143e2c feat: make FAB circular on Marmot Groups screen
Matches the CircleShape used by FABs on other screens.

https://claude.ai/code/session_013dhfy18dfuSYN8khXiLLBb
2026-04-06 14:16:06 +00:00
Claude 754568cf13 feat: add feedback when publishing KeyPackage on Marmot Groups screen
The publish KeyPackage button had no visual feedback, making it unclear
whether anything happened. Added a Snackbar to confirm success or show
errors, disabled the button during publishing, and updated the hint text
to direct users to the key icon.

https://claude.ai/code/session_013dhfy18dfuSYN8khXiLLBb
2026-04-06 14:13:52 +00:00
Claude 547be89577 perf: eliminate ThreadLocal.get() from hot paths — 27-52% faster across all EC ops
FieldP.mul/sqr were calling ThreadLocal.get() for every invocation (~500+
times per scalar multiplication, ~20-30ns each on JVM). Point operations
(doublePoint, addMixed, addPoints) each did an additional ThreadLocal.get()
for their scratch buffers.

Fix: add overloads that accept a pre-fetched wide buffer (LongArray(8))
and PointScratch. Top-level entry points (mulG, mul, mulDoubleG) fetch
the ThreadLocal once and thread it through all inner calls.

Results (ops/s, vs native JNI):
- pubkeyCreate:       19,163 → 29,205 (+52%, 3.0x → 2.2x)
- signSchnorr cached: 13,007 → 18,397 (+41%, 2.1x → 1.5x)
- signSchnorr:         5,365 →  7,490 (+40%, 5.7x → 3.7x)
- verifySchnorr:       3,840 →  4,873 (+27%, 7.2x → 5.4x)
- ECDH:                5,569 →  7,870 (+41%, 5.5x → 3.8x)

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 13:38:01 +00:00
Vitor Pamplona 1d12ae36e5 Removing more warnings 2026-04-06 09:27:37 -04:00
Vitor Pamplona 014e08fa0d Fixes some warnings 2026-04-06 09:26:04 -04:00
Vitor Pamplona a1145e2a28 Fixing hex and time functions 2026-04-06 09:23:51 -04:00
Vitor Pamplona 5727d50fce Fixes test cases 2026-04-06 09:22:21 -04:00
Claude 36a7ae147e fix: reduceWide round-2 carry overflow — fixes NIP-44 conversation key for edge-case scalars
The 4×64-bit reduceWide in FieldP had a bug: round 2 carry propagation
could overflow past 256 bits when out[0..3] were all 0xFF...FF, silently
dropping the overflow. This caused field multiplication results to be
off by exactly C = 2^32 + 977, corrupting point arithmetic for specific
intermediate values (e.g. ECDH with scalar n-2 on small x-coordinates).

Fix: detect round-2 overflow and fold the extra bit (≡ C mod p) back in.
Also fix ktlint violations in ScalarN and update documentation.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
2026-04-06 13:12:18 +00:00
Vitor Pamplona 2dc260d696 Switching icons to the new packages 2026-04-06 09:10:17 -04:00
Vitor Pamplona fb8802369f Merge pull request #2155 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-06 08:22:45 -04:00
Crowdin Bot f455d7338c New Crowdin translations by GitHub Action 2026-04-06 12:18:35 +00:00
Vitor Pamplona 2372af0b32 Merge pull request #2152 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-06 08:17:24 -04:00
Vitor Pamplona 814df2fd70 Merge pull request #2154 from vitorpamplona/claude/fix-polls-notification-bug-C4p48
Add periodic ticker to re-evaluate open polls after close date
2026-04-06 08:17:09 -04:00
Crowdin Bot 1f9ef8b560 New Crowdin translations by GitHub Action 2026-04-06 12:15:47 +00:00
Vitor Pamplona 9350dac3a1 Merge pull request #2151 from davotoula/claude/fix-share-dialog-autohide-XrQZk
fix: prevent share dialog from being dismissed by control auto-hide
2026-04-06 08:14:12 -04:00
Claude 7e32c314f4 fix: change poll re-evaluation ticker from 1 minute to 1 hour
https://claude.ai/code/session_01VY9FRuzHzwTVrePaJiHuHx
2026-04-06 12:11:55 +00:00
Claude 7d92386080 fix: prevent share dialog from being dismissed by control auto-hide
The share dialog in zoomable/fullscreen media view was auto-hiding
before users could select an option. This was caused by:

1. A 2-second timer in ZoomableContentDialog that hides all controls
   (including the share dialog) via AnimatedVisibility
2. A LaunchedEffect in RenderTopButtons that explicitly closed the
   share dialog whenever video controls auto-hid
3. Tap-to-toggle on the background also hiding controls while dialog
   was open

Fix: Hoist the share popup state and guard the auto-hide timer,
tap-to-toggle, and video control hide from dismissing when the
share dialog is open.

https://claude.ai/code/session_01YLujgxRDuujKCS3vgHc6Gq
2026-04-06 13:12:37 +02:00
Claude 7d0a234fd8 fix: remove expired polls from notification cards via periodic re-evaluation
The open polls notification flow only re-evaluated when new notes arrived
or dismissed IDs changed. Polls that passed their close date remained
visible until something else triggered the flow. Adding a 1-minute ticker
to the combine ensures expired polls are filtered out promptly.

https://claude.ai/code/session_01VY9FRuzHzwTVrePaJiHuHx
2026-04-06 02:33:49 +00:00