mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
fix(concord): seal rumors without a sig field
The envelope serialized the rumor with Event.toJson, which writes "sig": "". A rumor is unsigned (NIP-59), and applesauce-based clients such as Accordion read an object with a string sig as a signed event, fail its signature and drop it, so Accordion never showed a single Concord message, Join or edition we sent. Serialize through Rumor, as NIP-59 seals already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9c2cccd68d
commit
99513a92b6
+6
-2
@@ -32,6 +32,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44v2
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.Rumor
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
/**
|
||||
@@ -78,11 +79,14 @@ object ConcordStreamEnvelope {
|
||||
authorSigner: NostrSigner,
|
||||
encrypted: Boolean,
|
||||
): Event {
|
||||
// A rumor is unsigned (NIP-59): serialize it without `sig`. applesauce clients read
|
||||
// `"sig": ""` as a signed event with a bad signature and drop it.
|
||||
val rumorJson = Rumor.toJson(Rumor.create(rumor))
|
||||
val content =
|
||||
if (encrypted) {
|
||||
encryptChecked(rumor.toJson(), stream.conversationKey)
|
||||
encryptChecked(rumorJson, stream.conversationKey)
|
||||
} else {
|
||||
rumor.toJson()
|
||||
rumorJson
|
||||
}
|
||||
val kind = if (encrypted) KIND_SEAL_ENCRYPTED else KIND_SEAL_PLAINTEXT
|
||||
return authorSigner.sign(rumor.createdAt, kind, EMPTY_TAGS, content)
|
||||
|
||||
+19
@@ -28,8 +28,12 @@ import com.vitorpamplona.quartz.nip01Core.crypto.verify
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.RumorAssembler
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@@ -69,6 +73,21 @@ class ConcordStreamEnvelopeTest {
|
||||
assertEquals(9, opened.rumor.kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* A rumor is unsigned (NIP-59): its JSON carries no `sig`. applesauce-based clients
|
||||
* (Accordion) treat an object with `"sig": ""` as a signed event whose signature fails,
|
||||
* and silently dropped every Concord message and Guestbook Join we sent.
|
||||
*/
|
||||
@Test
|
||||
fun sealedRumorJsonCarriesNoSig() =
|
||||
runTest {
|
||||
val seal = ConcordStreamEnvelope.seal(chatRumor("no sig"), stream, authorSigner, encrypted = false)
|
||||
val rumorJson = Json.parseToJsonElement(seal.content).jsonObject
|
||||
|
||||
assertFalse("sig" in rumorJson, "rumor JSON must not carry a sig: ${seal.content}")
|
||||
assertEquals(chatRumor("no sig").id, rumorJson["id"]?.jsonPrimitive?.content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptedSealRoundTrips() =
|
||||
runTest {
|
||||
|
||||
Reference in New Issue
Block a user