refactor(commons): share NIP-59 gift-wrap+seal unwrap helper

Three call sites did the same two-layer peel —
  kind:1059 → (nip44 decrypt) → kind:13 sealed rumor → (nip44 decrypt) → rumor

Extracted as `GiftWrapEvent.unwrapAndUnsealOrNull(signer)` in
commons/relayClient/nip17Dm/, and collapsed:

- commons/marmot/MarmotIngest.kt — was an inline `when` switch; now a
  one-liner. Behaviour unchanged (still passes through if the inner
  isn't a seal, matching the previous defensive `else -> inner` branch).
- desktopApp/Main.kt — dropped the nested unwrap/unseal block in the
  kind:1059 case of the LocalCache dispatcher.
- cli/DmCommands.kt — replaced the manual chain in `decryptChatMessages`.

Android's DecryptAndIndexProcessor keeps its two separate handlers
(processNewGiftWrap / processNewSealedRumor) because the LocalCache
pipeline re-enters on each peel — that's a different shape and not
touched.

No behaviour change on any platform.
This commit is contained in:
Claude
2026-04-23 18:40:35 +00:00
parent 15205c24b5
commit 1e5b5d6276
4 changed files with 58 additions and 23 deletions
@@ -26,13 +26,13 @@ import com.vitorpamplona.amethyst.cli.Context
import com.vitorpamplona.amethyst.cli.DataDir
import com.vitorpamplona.amethyst.cli.Json
import com.vitorpamplona.amethyst.commons.relayClient.nip17Dm.filterGiftWrapsToPubkey
import com.vitorpamplona.amethyst.commons.relayClient.nip17Dm.unwrapAndUnsealOrNull
import com.vitorpamplona.quartz.marmot.RecipientRelayFetcher
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip17Dm.NIP17Factory
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import kotlinx.coroutines.delay
@@ -258,8 +258,7 @@ object DmCommands {
val out = mutableListOf<DecryptedDm>()
for ((relay, event) in raw) {
if (event !is GiftWrapEvent) continue
val sealed = event.unwrapOrNull(ctx.signer) as? SealedRumorEvent ?: continue
val inner = sealed.unsealOrNull(ctx.signer) as? ChatMessageEvent ?: continue
val inner = event.unwrapAndUnsealOrNull(ctx.signer) as? ChatMessageEvent ?: continue
if (!seen.add(inner.id)) continue
val members = inner.groupMembers()
if (peerHex != null && peerHex !in members) continue