feat(nostr): decentralized layer Phase A — identity, relays, zaps, NWC wallet

Additive Wavlake-inspired layer; zero existing features touched.

Crypto/protocol (pure-Kotlin core, fr.acinq secp256k1-kmp 0.17.3):
- Bech32 + NIP-19 (npub/nsec/nprofile/nevent/naddr)
- NIP-01 events: canonical serialization, BIP-340 schnorr sign/verify
- NIP-04 ECDH AES-CBC (raw x-coord via pubKeyTweakMul)
- Wavlake music NIP kinds 30440-30443 (Track/Album/Artist/Playlist)
- 4 conformance unit tests vs official spec vectors (all pass)

NostrService: multi-relay OkHttp websocket client, publish/subscribe/
query/getProfile/publishProfile/publishReaction/publishTrack/publishRaw,
best-effort failure posture (relays down = silent, app unaffected).

Identity: NostrKeys — AndroidKeyStore-encrypted private key at rest,
npub-only display. Verified on-device: create → npub chip → survives
force-stop + relaunch.

Payments (PaymentService abstraction, NWC provider):
- NIP-47 Nostr Wallet Connect over the pairing relay, pairing string
  keystore-encrypted, 15s timeout, never hangs playback
- LUD-06/LUD-16 lightning-address invoice resolution
- NIP-57 zaps: ephemeral-key zap request → callback nostr= → pay
- payment history records (no secrets)

UI (design-system tokens, progressive disclosure):
- ⚡ bolt on mini-player → TipSheet (21/100/500/1000/custom, recent
  addresses)
- Appearance → ⚡ Decentralized sheet: identity create/import, NWC
  connect, advanced relays/profile
This commit is contained in:
avi 2026-09-08 11:49:09 -05:00
commit 5ff5318db4
19 changed files with 2255 additions and 4 deletions

View file

@ -104,9 +104,15 @@ dependencies {
// not exist on Maven Central), which made the build fail during
// dependency resolution before Kotlin was ever compiled.
implementation("net.jthink:jaudiotagger:2.2.5")
// Nostr/Lightning crypto: BIP-340 schnorr + ECDH via ACINQ's secp256k1
// Kotlin-multiplatform binding (prebuilt .so for all Android ABIs).
implementation("fr.acinq.secp256k1:secp256k1-kmp:0.17.3")
implementation("fr.acinq.secp256k1:secp256k1-kmp-jni-android:0.17.3")
// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm-extract:0.17.3")
testImplementation("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm-linux:0.17.3")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation(composeBom)
androidTestImplementation("androidx.compose.ui:ui-test-junit4")