mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 00:16:59 +00:00
refactor(concord): community-list, control, invite-bundle events own their kind literals
Removes the CONTROL/COMMUNITY_LIST/INVITE_BUNDLE constants from ConcordKinds now that each has a dedicated Event class. Callers reference ControlEditionEvent.KIND, ConcordCommunityListEvent.KIND, and ConcordInviteBundleEvent.KIND directly, matching the nip88Polls per-kind event convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
This commit is contained in:
@@ -67,7 +67,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.njumpLink
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.bundle.ConcordInviteBundleEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
@@ -224,7 +224,7 @@ private fun DisplayAddress(
|
||||
// needs the 16-byte unlock token that only lives in the full invite link's #fragment —
|
||||
// a naddr alone can't be joined. Show an informative label instead of the generic
|
||||
// (and here always-empty) addressable-note card.
|
||||
if (nip19.kind == ConcordKinds.INVITE_BUNDLE) {
|
||||
if (nip19.kind == ConcordInviteBundleEvent.KIND) {
|
||||
Text(
|
||||
text = stringRes(R.string.concord_invite_naddr_label) + (additionalChars ?: ""),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
|
||||
+2
-1
@@ -31,6 +31,7 @@ import com.vitorpamplona.quartz.concord.cord05Invites.ConcordInviteBundle
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.ConcordInviteLink
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.MintedInviteLink
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.ParsedInviteLink
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.bundle.ConcordInviteBundleEvent
|
||||
import com.vitorpamplona.quartz.concord.crypto.ConcordKeyDerivation
|
||||
import com.vitorpamplona.quartz.concord.crypto.GroupKey
|
||||
import com.vitorpamplona.quartz.concord.envelope.ConcordStreamEnvelope
|
||||
@@ -86,7 +87,7 @@ object ConcordActions {
|
||||
fun planeFilterFor(planePubKeysHex: List<HexKey>): Filter = Filter(kinds = listOf(ConcordKinds.WRAP), authors = planePubKeysHex)
|
||||
|
||||
/** The public invite bundle for a link signer. */
|
||||
fun bundleFilter(linkSignerPubKeyHex: HexKey): Filter = Filter(kinds = listOf(ConcordKinds.INVITE_BUNDLE), authors = listOf(linkSignerPubKeyHex))
|
||||
fun bundleFilter(linkSignerPubKeyHex: HexKey): Filter = Filter(kinds = listOf(ConcordInviteBundleEvent.KIND), authors = listOf(linkSignerPubKeyHex))
|
||||
|
||||
/** Pending direct invites addressed to the given member (indexed by k=3313). */
|
||||
fun directInvitesFilter(memberPubKeyHex: HexKey): Filter = Filter(kinds = listOf(ConcordKinds.WRAP), tags = mapOf("p" to listOf(memberPubKeyHex), "k" to listOf(ConcordKinds.DIRECT_INVITE.toString())))
|
||||
|
||||
+3
-4
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.quartz.concord.cord02Community
|
||||
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.ConcordJson
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -64,7 +63,7 @@ class ConcordCommunityListEntry(
|
||||
|
||||
/**
|
||||
* The member's private, self-encrypted list of joined Concord communities
|
||||
* (kind [ConcordKinds.COMMUNITY_LIST] = 13302, CORD-05) — the NIP-51 analog that
|
||||
* (kind [ConcordCommunityListEvent.KIND] = 13302, CORD-05) — the NIP-51 analog that
|
||||
* lets a client return to the groups the user signed up for. Replaceable and
|
||||
* NIP-44-encrypted to the member's own key, so relays store only ciphertext.
|
||||
*
|
||||
@@ -79,7 +78,7 @@ object ConcordCommunityList {
|
||||
createdAt: Long,
|
||||
): Event {
|
||||
val content = signer.nip44Encrypt(encode(entries), signer.pubKey)
|
||||
return signer.sign(createdAt, ConcordKinds.COMMUNITY_LIST, emptyArray(), content)
|
||||
return signer.sign(createdAt, ConcordCommunityListEvent.KIND, emptyArray(), content)
|
||||
}
|
||||
|
||||
/** Serializes [entries] to the plaintext JSON that gets NIP-44 self-encrypted. */
|
||||
@@ -98,7 +97,7 @@ object ConcordCommunityList {
|
||||
event: Event,
|
||||
signer: NostrSigner,
|
||||
): List<ConcordCommunityListEntry> {
|
||||
if (event.kind != ConcordKinds.COMMUNITY_LIST) return emptyList()
|
||||
if (event.kind != ConcordCommunityListEvent.KIND) return emptyList()
|
||||
return try {
|
||||
decode(signer.nip44Decrypt(event.content, signer.pubKey))
|
||||
} catch (_: Exception) {
|
||||
|
||||
+2
-3
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.quartz.concord.cord02Community
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
@@ -30,7 +29,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
/**
|
||||
* The member's private, self-encrypted list of joined Concord communities (kind
|
||||
* [ConcordKinds.COMMUNITY_LIST] = 13302, CORD-05). A replaceable event whose
|
||||
* 13302, CORD-05). A replaceable event whose
|
||||
* `content` is the NIP-44 self-encryption of the [ConcordCommunityListEntry] JSON
|
||||
* — including each community's secrets (`community_root`, salt, epoch,
|
||||
* private-channel keys), so a single event both syncs membership across devices
|
||||
@@ -60,7 +59,7 @@ class ConcordCommunityListEvent(
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KIND = ConcordKinds.COMMUNITY_LIST
|
||||
const val KIND = 13302
|
||||
const val ALT = "Private list of joined Concord communities"
|
||||
|
||||
/** The replaceable coordinate for a member's list: `(13302, pubkey, "")`. */
|
||||
|
||||
+1
-2
@@ -22,7 +22,6 @@ package com.vitorpamplona.quartz.concord.cord04Roles.control
|
||||
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.AuthorityCitation
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.ControlEntityKind
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
@@ -60,7 +59,7 @@ class ControlEditionEvent(
|
||||
fun authorityCitation() = tags.vac()
|
||||
|
||||
companion object {
|
||||
const val KIND = ConcordKinds.CONTROL
|
||||
const val KIND = 3308
|
||||
|
||||
/**
|
||||
* Builds the edition template for [entityKind]/[entityId] at [version].
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.concord.cord05Invites
|
||||
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.bundle.ConcordInviteBundleEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
@@ -164,7 +164,7 @@ object ConcordInviteLink {
|
||||
token: ByteArray,
|
||||
relays: List<String>? = null,
|
||||
): String {
|
||||
val naddr = NAddress.create(ConcordKinds.INVITE_BUNDLE, linkSignerPubKey, "", null)
|
||||
val naddr = NAddress.create(ConcordInviteBundleEvent.KIND, linkSignerPubKey, "", null)
|
||||
val trimmed = base.trimEnd('/')
|
||||
return "$trimmed/invite/$naddr#${encodeFragment(token, relays)}"
|
||||
}
|
||||
@@ -183,7 +183,7 @@ object ConcordInviteLink {
|
||||
if (marker < 0) return null
|
||||
val naddr = url.substring(marker + "/invite/".length, hash)
|
||||
val parsed = NAddress.parse(naddr) ?: return null
|
||||
if (parsed.kind != ConcordKinds.INVITE_BUNDLE) return null
|
||||
if (parsed.kind != ConcordInviteBundleEvent.KIND) return null
|
||||
return ParsedInviteLink(naddr, parsed.author, parsed.kind, fragment)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -23,7 +23,6 @@ package com.vitorpamplona.quartz.concord.cord05Invites.bundle
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.ControlEntityKind
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.control.tags.VskTag
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.control.vsk
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
@@ -57,7 +56,7 @@ class ConcordInviteBundleEvent(
|
||||
fun versionedSubKind() = tags.vsk()
|
||||
|
||||
companion object {
|
||||
const val KIND = ConcordKinds.INVITE_BUNDLE
|
||||
const val KIND = 33301
|
||||
|
||||
/** Builds the addressable bundle template carrying the already-encrypted [encryptedInvite]. */
|
||||
fun build(
|
||||
|
||||
+3
-5
@@ -55,12 +55,10 @@ object ConcordKinds {
|
||||
// Person-addressed rumor (CORD-05)
|
||||
const val DIRECT_INVITE = 3313
|
||||
|
||||
// Control / rekey rumors (CORD-02/04/06)
|
||||
const val CONTROL = 3308
|
||||
// Rekey rumor (CORD-06). Control 3308 now lives on ControlEditionEvent.KIND.
|
||||
const val REKEY = 3303
|
||||
|
||||
// Bare bookkeeping events
|
||||
const val INVITE_BUNDLE = 33301 // addressable, CORD-05
|
||||
const val COMMUNITY_LIST = 13302 // private self-list, CORD-05
|
||||
// Bare bookkeeping events. Community-list 13302 (ConcordCommunityListEvent.KIND) and
|
||||
// invite-bundle 33301 (ConcordInviteBundleEvent.KIND) now own their literals.
|
||||
const val INVITE_LIST = 13303 // private self-list, CORD-05
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.concord.cord02Community
|
||||
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -53,7 +53,7 @@ class ConcordCommunityListTest {
|
||||
val entries = listOf(entry("11".repeat(32), "Gamers"), entry("22".repeat(32), "Nostrichs"))
|
||||
val event = ConcordCommunityList.build(signer, entries, createdAt = 1_700_000_000L)
|
||||
|
||||
assertEquals(ConcordKinds.COMMUNITY_LIST, event.kind)
|
||||
assertEquals(ConcordCommunityListEvent.KIND, event.kind)
|
||||
assertFalse(event.content.contains("Gamers")) // encrypted on the wire
|
||||
|
||||
val parsed = ConcordCommunityList.parse(event, signer)
|
||||
|
||||
+5
-5
@@ -20,8 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.concord.cord04Roles
|
||||
|
||||
import com.vitorpamplona.quartz.concord.cord04Roles.control.ControlEditionEvent
|
||||
import com.vitorpamplona.quartz.concord.crypto.EditionHash
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
@@ -69,7 +69,7 @@ class ControlEditionTest {
|
||||
arrayOf("ep", prev.toHexKey()),
|
||||
arrayOf("vac", grantId.toHexKey(), "2", grantHash.toHexKey()),
|
||||
)
|
||||
val rumor = RumorAssembler.assembleRumor<Event>(author, 1_700_000_000L, ConcordKinds.CONTROL, tags, content)
|
||||
val rumor = RumorAssembler.assembleRumor<Event>(author, 1_700_000_000L, ControlEditionEvent.KIND, tags, content)
|
||||
|
||||
val ed = ControlEdition.fromRumor(rumor)
|
||||
assertNotNull(ed)
|
||||
@@ -92,7 +92,7 @@ class ControlEditionTest {
|
||||
// missing eid
|
||||
assertNull(
|
||||
ControlEdition.fromRumor(
|
||||
RumorAssembler.assembleRumor<Event>(author, 1L, ConcordKinds.CONTROL, arrayOf(arrayOf("vsk", "0"), arrayOf("ev", "0")), "{}"),
|
||||
RumorAssembler.assembleRumor<Event>(author, 1L, ControlEditionEvent.KIND, arrayOf(arrayOf("vsk", "0"), arrayOf("ev", "0")), "{}"),
|
||||
),
|
||||
)
|
||||
// unknown vsk (bit 7 retired)
|
||||
@@ -101,7 +101,7 @@ class ControlEditionTest {
|
||||
RumorAssembler.assembleRumor<Event>(
|
||||
author,
|
||||
1L,
|
||||
ConcordKinds.CONTROL,
|
||||
ControlEditionEvent.KIND,
|
||||
arrayOf(arrayOf("vsk", "7"), arrayOf("eid", eid.toHexKey()), arrayOf("ev", "0")),
|
||||
"{}",
|
||||
),
|
||||
@@ -112,7 +112,7 @@ class ControlEditionTest {
|
||||
@Test
|
||||
fun genesisHasNullPrevWhenEpAbsent() {
|
||||
val tags = arrayOf(arrayOf("vsk", "2"), arrayOf("eid", eid.toHexKey()), arrayOf("ev", "0"))
|
||||
val ed = ControlEdition.fromRumor(RumorAssembler.assembleRumor<Event>(author, 1L, ConcordKinds.CONTROL, tags, """{"name":"general"}"""))
|
||||
val ed = ControlEdition.fromRumor(RumorAssembler.assembleRumor<Event>(author, 1L, ControlEditionEvent.KIND, tags, """{"name":"general"}"""))
|
||||
assertNotNull(ed)
|
||||
assertNull(ed.prevHash)
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.concord.cord05Invites
|
||||
|
||||
import com.vitorpamplona.quartz.concord.cord05Invites.bundle.ConcordInviteBundleEvent
|
||||
import com.vitorpamplona.quartz.concord.crypto.ConcordKeyDerivation
|
||||
import com.vitorpamplona.quartz.concord.events.ConcordKinds
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import kotlin.io.encoding.Base64
|
||||
@@ -83,7 +83,7 @@ class ConcordInviteLinkTest {
|
||||
val parsed = ConcordInviteLink.parseUrl(url)
|
||||
assertNotNull(parsed)
|
||||
assertEquals(signer, parsed.linkSignerPubKey)
|
||||
assertEquals(ConcordKinds.INVITE_BUNDLE, parsed.kind)
|
||||
assertEquals(ConcordInviteBundleEvent.KIND, parsed.kind)
|
||||
assertContentEquals(token, parsed.fragment.token)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user