Per Amber maintainer: connect never returns a pubkey — response is
"ack" or the secret string. Rewrote ConnectResponse.parse() to check
result/error fields directly on base BunkerResponse (matching Jackson
deserialization). connect() is now a pure protocol handshake; callers
call getPublicKey() separately as a domain concern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs in quartz's NIP-46 remote signer:
1. FixedKey.getEncoded() returned the raw byte array reference. javax.crypto.Mac
zeroes the key via destroy() after HMAC computation, corrupting the NIP-44
saltPrefix ("nip44-v2") after the first computeConversationKey call. All
subsequent NIP-44 decryptions with different key pairs fail with Invalid Mac.
Fix: return key.copyOf() instead of key.
2. NostrSignerRemote.connect() only handled pubkey responses. Amber (and other
signers) may respond with "ack" or "already connected" error. Added
ConnectResponse parser that maps these to success and falls back to
getPublicKey() to retrieve the actual pubkey.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- UrlDetector: simplify space handler in readDefault() — both branches
of if/else were calling the same readEnd(InvalidUrl), making the
conditional dead code; now we always call readEnd after attempting
to read the domain
- UrlDetector: remove redundant inner buffer.isNotEmpty() check in
readEnd(), already guarded by the outer condition
- UrlDetector: rename processColon() parameter from length to
startLength to eliminate the var length = length self-shadowing
- UrlDetector: replace ArrayList<Url>() with mutableListOf<Url>()
for idiomatic Kotlin
- DomainNameReader: replace lastSection.deleteRange(0, lastSection.length)
with lastSection.clear() which expresses the intent directly
- DomainNameReader: move isDotPercent() and isXn() extension functions
from inside the class body to private file-level functions — extension
functions defined on a class instance are unusual and confusing
https://claude.ai/code/session_01Pci2AC45yQxQw6F6g7jWdd
* 'main' of https://github.com/vitorpamplona/amethyst: (35 commits)
New Crowdin translations by GitHub Action
fix chess tests
Replace subsequent checks with '!isNullOrEmpty()' call
use forEach: no intermediate list created.
Use filterIsInstance to avoid dual iteration
Lambda argument should be moved out of parentheses
replace null checks with Elvis
merge call chain with flatMap (slight performance improvement)
New Crowdin translations by GitHub Action
New Crowdin translations by GitHub Action
update cz, pt, de, sv
was this meant to be withContext?
add names to boolean params
remove unused imports
Explicit type arguments can be inferred
remove redundant modifiers
correct package name
If-Null return/break/... foldable to '?:'
Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.
Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.
...
# Conflicts:
# amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ReactionsSettingsScreen.kt
Add NamecoinLookupException sealed class to distinguish:
- NameNotFound: name queried successfully but doesn't exist on blockchain
- NameExpired: name exists but expired (>36000 blocks since last update)
- ServersUnreachable: all ElectrumX servers failed with connection errors
Previously, all three cases returned null from nameShowWithFallback,
making it impossible for the UI to show meaningful feedback.
Changes:
- ElectrumxClient.connectAndQuery: throws NameNotFound/NameExpired
instead of returning null for definitive blockchain answers
- ElectrumxClient.nameShow: lets NamecoinLookupException propagate
(only catches connection/IO errors as null)
- ElectrumxClient.nameShowWithFallback: short-circuits on NameNotFound/
NameExpired (no point trying other servers for definitive answers),
throws ServersUnreachable when all servers fail with connection errors
- SearchBarViewModel: new namecoinSearchState flow with typed states
(Idle/Loading/Resolved/NotFound/Expired/ServersUnreachable) for UI
to show loading spinners, resolved profiles, and specific error
messages. Derived namecoinResolvedUser from this flow.
Discovered while porting to notedeck (damus-io/notedeck#1314).
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.
- CalendarDateSlotEvent (31922): Add title, summary, image, geohash,
hashtags, participants, references, multiple locations accessors and
build() method with DSL builder pattern
- CalendarTimeSlotEvent (31923): Fix timezone bug (startTzId/endTzId
were parsed as Long instead of String), add all accessors like
DateSlot, rename startTmz/endTmz to startTzId/endTzId, add build()
- CalendarRSVPEvent (31925): Remove incorrect location/start/end
accessors (not in spec), add status/freebusy/calendarEventAddress/
calendarEventId/calendarEventAuthor accessors, update from outdated
L/l label format to current status/fb tag format, add build()
- CalendarEvent (31924): Add title and calendarEventAddresses accessors,
add build() method
- Create NIP-52 tag classes: LocationTag, RSVPStatusTag (with enum),
FreeBusyTag (with enum)
- Create TagArrayBuilderExt.kt with DSL builder extensions for all
four calendar event types
https://claude.ai/code/session_01UMXgE3tNftmqNZqcYwF2kL
Reorganize nip64Chess event classes into individual packages with
dedicated tag classes, TagArrayBuilderExt, and TagArrayExt files,
following the same structure used by nip88Polls.
New package structure:
- challenge/ - LiveChessGameChallengeEvent with PlayerColorTag, TimeControlTag
- accept/ - LiveChessGameAcceptEvent with ChallengeEventTag
- move/ - LiveChessMoveEvent with GameIdTag, MoveNumberTag, SanTag, FenTag
- end/ - LiveChessGameEndEvent with ResultTag, TerminationTag, WinnerTag
- draw/ - LiveChessDrawOfferEvent
- game/ - ChessGameEvent (Kind 64)
- jester/ - JesterEvent, JesterProtocol, JesterContent, JesterGameEvents
Each tag class follows the companion object pattern with isTag(),
parse(), and assemble() methods. Each event package includes
TagArrayBuilderExt for building and TagArrayExt for parsing.
https://claude.ai/code/session_01Qtzhka3p5N3Hbns4xrRbsv