mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 16:33:27 +00:00
refactor(clink): clink_version as a shared ClinkVersionTag class
Models the protocol-version tag the way other tags are modeled, instead of a loose helper on the Clink object: - New ClinkVersionTag (TAG_NAME/CURRENT/assemble/parse) under clink/tags, with a clinkVersion() TagArrayBuilder DSL extension, reused by all three events. - OfferEvent/DebitEvent/ManageEvent read version() via ClinkVersionTag::parse and build via clinkVersion() in their templates. - Retires the now-empty Clink object (its KDoc moved to the tag class). Behavior-preserving: assemble() emits the identical ["clink_version", "1"] tag in the same position. All CLINK tests pass.
This commit is contained in:
+5
-4
@@ -21,7 +21,8 @@
|
||||
package com.vitorpamplona.quartz.experimental.clink.debits
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.clink.Clink
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.ClinkVersionTag
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.clinkVersion
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
|
||||
@@ -58,7 +59,7 @@ class DebitEvent(
|
||||
|
||||
fun isResponse() = requestId() != null
|
||||
|
||||
fun version() = tags.firstOrNull { it.size > 1 && it[0] == Clink.VERSION_TAG_NAME }?.get(1)
|
||||
fun version() = tags.firstNotNullOfOrNull(ClinkVersionTag::parse)
|
||||
|
||||
private fun talkingWith(oneSideHex: String): HexKey = if (pubKey == oneSideHex) recipientPubKey() ?: pubKey else pubKey
|
||||
|
||||
@@ -88,7 +89,7 @@ class DebitEvent(
|
||||
return signer.sign(
|
||||
eventTemplate(KIND, encrypted, createdAt) {
|
||||
pTag(servicePubKey, null)
|
||||
add(Clink.versionTag())
|
||||
clinkVersion()
|
||||
alt(ALT)
|
||||
},
|
||||
)
|
||||
@@ -107,7 +108,7 @@ class DebitEvent(
|
||||
eventTemplate(KIND, encrypted, createdAt) {
|
||||
pTag(requestorPubKey, null)
|
||||
add(ETag.assemble(requestEvent.id, null, null))
|
||||
add(Clink.versionTag())
|
||||
clinkVersion()
|
||||
alt(ALT)
|
||||
},
|
||||
)
|
||||
|
||||
+5
-4
@@ -21,7 +21,8 @@
|
||||
package com.vitorpamplona.quartz.experimental.clink.manage
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.clink.Clink
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.ClinkVersionTag
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.clinkVersion
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
|
||||
@@ -58,7 +59,7 @@ class ManageEvent(
|
||||
|
||||
fun isResponse() = requestId() != null
|
||||
|
||||
fun version() = tags.firstOrNull { it.size > 1 && it[0] == Clink.VERSION_TAG_NAME }?.get(1)
|
||||
fun version() = tags.firstNotNullOfOrNull(ClinkVersionTag::parse)
|
||||
|
||||
private fun talkingWith(oneSideHex: String): HexKey = if (pubKey == oneSideHex) recipientPubKey() ?: pubKey else pubKey
|
||||
|
||||
@@ -88,7 +89,7 @@ class ManageEvent(
|
||||
return signer.sign(
|
||||
eventTemplate(KIND, encrypted, createdAt) {
|
||||
pTag(serverPubKey, null)
|
||||
add(Clink.versionTag())
|
||||
clinkVersion()
|
||||
alt(ALT)
|
||||
},
|
||||
)
|
||||
@@ -107,7 +108,7 @@ class ManageEvent(
|
||||
eventTemplate(KIND, encrypted, createdAt) {
|
||||
pTag(appPubKey, null)
|
||||
add(ETag.assemble(requestEvent.id, null, null))
|
||||
add(Clink.versionTag())
|
||||
clinkVersion()
|
||||
alt(ALT)
|
||||
},
|
||||
)
|
||||
|
||||
+5
-4
@@ -21,7 +21,8 @@
|
||||
package com.vitorpamplona.quartz.experimental.clink.offers
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.clink.Clink
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.ClinkVersionTag
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.clinkVersion
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
|
||||
@@ -60,7 +61,7 @@ class OfferEvent(
|
||||
|
||||
fun isResponse() = requestId() != null
|
||||
|
||||
fun version() = tags.firstOrNull { it.size > 1 && it[0] == Clink.VERSION_TAG_NAME }?.get(1)
|
||||
fun version() = tags.firstNotNullOfOrNull(ClinkVersionTag::parse)
|
||||
|
||||
private fun talkingWith(oneSideHex: String): HexKey = if (pubKey == oneSideHex) recipientPubKey() ?: pubKey else pubKey
|
||||
|
||||
@@ -90,7 +91,7 @@ class OfferEvent(
|
||||
return signer.sign(
|
||||
eventTemplate(KIND, encrypted, createdAt) {
|
||||
pTag(servicePubKey, null)
|
||||
add(Clink.versionTag())
|
||||
clinkVersion()
|
||||
alt(ALT)
|
||||
},
|
||||
)
|
||||
@@ -109,7 +110,7 @@ class OfferEvent(
|
||||
eventTemplate(KIND, encrypted, createdAt) {
|
||||
pTag(payerPubKey, null)
|
||||
add(ETag.assemble(requestEvent.id, null, null))
|
||||
add(Clink.versionTag())
|
||||
clinkVersion()
|
||||
alt(ALT)
|
||||
},
|
||||
)
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.clink.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
/**
|
||||
* Protocol-version tag shared by all three CLINK message kinds (21001-3). Every CLINK
|
||||
* request and response carries a `["clink_version", "1"]` tag alongside its `p` tag,
|
||||
* with the content NIP-44 encrypted between the two parties.
|
||||
*/
|
||||
class ClinkVersionTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "clink_version"
|
||||
const val CURRENT = "1"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(version: String = CURRENT) = arrayOf(TAG_NAME, version)
|
||||
}
|
||||
}
|
||||
+4
-11
@@ -18,16 +18,9 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.clink
|
||||
package com.vitorpamplona.quartz.experimental.clink.tags
|
||||
|
||||
/**
|
||||
* Constants shared by all three CLINK message kinds. Every CLINK request and
|
||||
* response carries a `["clink_version", "1"]` tag and a `["p", recipient]` tag,
|
||||
* and its content is a NIP-44 encrypted JSON payload.
|
||||
*/
|
||||
object Clink {
|
||||
const val VERSION = "1"
|
||||
const val VERSION_TAG_NAME = "clink_version"
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun versionTag(): Array<String> = arrayOf(VERSION_TAG_NAME, VERSION)
|
||||
}
|
||||
fun <T : Event> TagArrayBuilder<T>.clinkVersion(version: String = ClinkVersionTag.CURRENT) = addUnique(ClinkVersionTag.assemble(version))
|
||||
+3
-2
@@ -24,6 +24,7 @@ import com.vitorpamplona.quartz.experimental.clink.debits.DebitResponse
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferEvent
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferRequest
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferResponse
|
||||
import com.vitorpamplona.quartz.experimental.clink.tags.ClinkVersionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
@@ -55,7 +56,7 @@ class ClinkEventTest {
|
||||
buildList {
|
||||
add(arrayOf("p", recipient))
|
||||
if (requestId != null) add(arrayOf("e", requestId))
|
||||
add(Clink.versionTag())
|
||||
add(ClinkVersionTag.assemble())
|
||||
}.toTypedArray(),
|
||||
content = "encrypted-placeholder",
|
||||
sig = "b".repeat(128),
|
||||
@@ -69,7 +70,7 @@ class ClinkEventTest {
|
||||
assertFalse(request.isResponse())
|
||||
assertNull(request.requestId())
|
||||
assertEquals(service.pubKey, request.recipientPubKey())
|
||||
assertEquals(Clink.VERSION, request.version())
|
||||
assertEquals(ClinkVersionTag.CURRENT, request.version())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user