diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt index 8617b1e61..6b7a0b978 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt @@ -20,19 +20,28 @@ */ package com.vitorpamplona.quartz.nip01Core.signers +import com.fasterxml.jackson.annotation.JsonProperty import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.TagArray import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.builder import com.vitorpamplona.quartz.nip01Core.core.tagArray +import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper import com.vitorpamplona.quartz.utils.TimeUtils class EventTemplate( + @JsonProperty("created_at") val createdAt: Long, val kind: Int, val tags: TagArray, val content: String, -) +) { + fun toJson(): String = EventMapper.mapper.writeValueAsString(this) + + companion object { + fun fromJson(json: String): EventTemplate = EventTemplateManualDeserializer.fromJson(EventMapper.mapper.readTree(json)) + } +} inline fun eventTemplate( kind: Int, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplateDeserializer.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplateDeserializer.kt new file mode 100644 index 000000000..94e69cf39 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplateDeserializer.kt @@ -0,0 +1,33 @@ +/** + * 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.nip01Core.signers + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.vitorpamplona.quartz.nip01Core.core.Event + +class EventTemplateDeserializer : StdDeserializer>(EventTemplate::class.java) { + override fun deserialize( + jp: JsonParser, + ctxt: DeserializationContext, + ): EventTemplate = EventTemplateManualDeserializer.fromJson(jp.codec.readTree(jp)) +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplateManualDeserializer.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplateManualDeserializer.kt new file mode 100644 index 000000000..abdded6bd --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplateManualDeserializer.kt @@ -0,0 +1,40 @@ +/** + * 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.nip01Core.signers + +import com.fasterxml.jackson.databind.JsonNode +import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.jackson.toTypedArray + +class EventTemplateManualDeserializer { + companion object { + fun fromJson(jsonObject: JsonNode): EventTemplate = + EventTemplate( + createdAt = jsonObject.get("created_at").asLong(), + kind = jsonObject.get("kind").asInt(), + tags = + jsonObject.get("tags").toTypedArray { + it.toTypedArray { s -> if (s.isNull) "" else s.asText().intern() } + }, + content = jsonObject.get("content").asText(), + ) + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestSign.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestSign.kt index 8c755f1d3..4629e489b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestSign.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestSign.kt @@ -21,11 +21,12 @@ package com.vitorpamplona.quartz.nip46RemoteSigner import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate import java.util.UUID class BunkerRequestSign( id: String = UUID.randomUUID().toString(), - val event: Event, + val event: EventTemplate, ) : BunkerRequest(id, METHOD_NAME, arrayOf(event.toJson())) { companion object { val METHOD_NAME = "sign_event" @@ -33,6 +34,6 @@ class BunkerRequestSign( fun parse( id: String, params: Array, - ): BunkerRequestSign = BunkerRequestSign(id, Event.fromJson(params[0])) + ): BunkerRequestSign = BunkerRequestSign(id, EventTemplate.fromJson(params[0])) } } diff --git a/quartz/src/test/java/com/vitorpamplona/quartz/nip01Core/jackson/EventDeserializerTest.kt b/quartz/src/test/java/com/vitorpamplona/quartz/nip01Core/jackson/EventDeserializerTest.kt new file mode 100644 index 000000000..cb22c26ba --- /dev/null +++ b/quartz/src/test/java/com/vitorpamplona/quartz/nip01Core/jackson/EventDeserializerTest.kt @@ -0,0 +1,65 @@ +/** + * 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.nip01Core.jackson + +import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair +import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate +import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal +import com.vitorpamplona.quartz.nip01Core.verify +import org.junit.Test + +class EventDeserializerTest { + @Test + fun testEventTemplateToJson() { + val templateJson = """{"created_at":1234,"kind":1,"tags":[],"content":"This is an unsigned event."}""" + val template = EventTemplate.fromJson(templateJson) + + val json = template.toJson() + + assert(json == templateJson) + } + + @Test + fun testEventTemplate() { + val templateJson = """{"kind":"1","content":"This is an unsigned event.","created_at":1234,"tags":[]}""" + val template = EventTemplate.fromJson(templateJson) + + assert(template.kind == 1) + assert(template.content == "This is an unsigned event.") + assert(template.tags.isEmpty()) + } + + @Test + fun testSignedEvent() { + val keyPair = KeyPair() + val signer = NostrSignerInternal(keyPair) + + val templateJson = """{"kind":"1","content":"This is an unsigned event.","created_at":1234,"tags":[]}""" + val template = EventTemplate.fromJson(templateJson) + + val signedEvent = signer.signerSync.sign(template)!! + + assert(signedEvent.id.isNotEmpty()) + assert(signedEvent.sig.isNotEmpty()) + assert(signedEvent.pubKey.isNotEmpty()) + assert(signedEvent.verify()) + } +} diff --git a/quartz/src/test/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestTest.kt b/quartz/src/test/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestTest.kt new file mode 100644 index 000000000..3d94e0c4b --- /dev/null +++ b/quartz/src/test/java/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequestTest.kt @@ -0,0 +1,35 @@ +/** + * 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.nip46RemoteSigner + +import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper +import org.junit.Test + +class BunkerRequestTest { + @Test + fun testBunkerRequestDeSerialization() { + val requestJson = """{"id":"123","method":"sign_event","params":["{\"created_at\":1234,\"kind\":1,\"tags\":[],\"content\":\"This is an unsigned event.\"}"]}""" + val bunkerRequest = EventMapper.mapper.readValue(requestJson, BunkerRequest::class.java) + + assert(bunkerRequest is BunkerRequestSign) + assert((bunkerRequest as BunkerRequestSign).event.kind == 1) + } +}