Moves Event ID hashing to a new class

This commit is contained in:
Vitor Pamplona
2025-01-06 15:13:14 -05:00
parent 5f577df819
commit 1430ba4745
13 changed files with 437 additions and 233 deletions
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2024 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.crypto.nip01
import com.fasterxml.jackson.databind.node.JsonNodeFactory
import com.vitorpamplona.quartz.crypto.sha256Hash
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.Event.Companion.mapper
class EventHasher {
companion object {
fun makeJsonForId(
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
): String {
val factory = JsonNodeFactory.instance
val rawEvent =
factory.arrayNode(6).apply {
add(0)
add(pubKey)
add(createdAt)
add(kind)
add(
factory.arrayNode(tags.size).apply {
tags.forEach { tag ->
add(
factory.arrayNode(tag.size).apply { tag.forEach { add(it) } },
)
}
},
)
add(content)
}
return mapper.writeValueAsString(rawEvent)
}
fun hashIdBytes(
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
): ByteArray = sha256Hash(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray())
fun hashId(
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
): String = hashIdBytes(pubKey, createdAt, kind, tags, content).toHexKey()
}
}
@@ -35,13 +35,13 @@ import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.sha256Hash
import com.vitorpamplona.quartz.crypto.nip01.EventHasher
import com.vitorpamplona.quartz.crypto.nip01.EventHasher.Companion.hashId
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.Nip19Bech32
import com.vitorpamplona.quartz.encoders.PoWRank
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.nip46.BunkerMessage
import com.vitorpamplona.quartz.events.nip46.BunkerRequest
import com.vitorpamplona.quartz.events.nip46.BunkerResponse
@@ -50,7 +50,6 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.remove
import com.vitorpamplona.quartz.utils.startsWith
import java.math.BigDecimal
@Immutable
@@ -341,9 +340,7 @@ open class Event(
false
}
fun makeJsonForId(): String = makeJsonForId(pubKey, createdAt, kind, tags, content)
fun generateId(): String = sha256Hash(makeJsonForId().toByteArray()).toHexKey()
fun generateId(): String = EventHasher.hashId(pubKey, createdAt, kind, tags, content)
private class EventDeserializer : StdDeserializer<Event>(Event::class.java) {
override fun deserialize(
@@ -476,42 +473,21 @@ open class Event(
fun toJson(event: Event): String = mapper.writeValueAsString(event)
fun makeJsonForId(
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
): String {
val factory = mapper.nodeFactory
val rawEvent =
factory.arrayNode(6).apply {
add(0)
add(pubKey)
add(createdAt)
add(kind)
add(
factory.arrayNode(tags.size).apply {
tags.forEach { tag ->
add(
factory.arrayNode(tag.size).apply { tag.forEach { add(it) } },
)
}
},
)
add(content)
}
return mapper.writeValueAsString(rawEvent)
}
fun generateId(
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
): ByteArray = CryptoUtils.sha256(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray())
): HexKey = EventHasher.hashId(pubKey, createdAt, kind, tags, content)
fun generateIdBytes(
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
): ByteArray = EventHasher.hashIdBytes(pubKey, createdAt, kind, tags, content)
fun create(
signer: NostrSigner,
@@ -21,7 +21,6 @@
package com.vitorpamplona.quartz.events
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.nip46.NostrConnectEvent
class EventFactory {
@@ -62,7 +61,7 @@ class EventFactory {
ChatMessageEncryptedFileHeaderEvent.KIND -> {
if (id.isBlank()) {
ChatMessageEncryptedFileHeaderEvent(
Event.generateId(pubKey, createdAt, kind, tags, content).toHexKey(),
Event.generateId(pubKey, createdAt, kind, tags, content),
pubKey,
createdAt,
tags,
@@ -76,7 +75,7 @@ class EventFactory {
ChatMessageEvent.KIND -> {
if (id.isBlank()) {
ChatMessageEvent(
Event.generateId(pubKey, createdAt, kind, tags, content).toHexKey(),
Event.generateId(pubKey, createdAt, kind, tags, content),
pubKey,
createdAt,
tags,
@@ -24,7 +24,6 @@ import android.util.Log
import androidx.compose.runtime.Immutable
import com.fasterxml.jackson.annotation.JsonProperty
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
@@ -152,7 +151,7 @@ class Gossip(
val newContent = content ?: ""
val newID =
id?.ifBlank { null }
?: Event.generateId(newPubKey, newCreatedAt, newKind, newTags, newContent).toHexKey()
?: Event.generateId(newPubKey, newCreatedAt, newKind, newTags, newContent)
val sig = ""
return EventFactory.create(newID, newPubKey, newCreatedAt, newKind, newTags, newContent, sig)
@@ -22,7 +22,6 @@ package com.vitorpamplona.quartz.signers
import com.vitorpamplona.quartz.crypto.nip04.Nip04
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.EventFactory
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
@@ -87,7 +86,7 @@ abstract class NostrSigner(
content: String,
onReady: (T) -> Unit,
) {
val id = Event.generateId(pubKey, createdAt, kind, tags, content).toHexKey()
val id = Event.generateId(pubKey, createdAt, kind, tags, content)
onReady(
EventFactory.create(
@@ -23,7 +23,6 @@ package com.vitorpamplona.quartz.signers
import android.util.Log
import com.goterl.lazysodium.BuildConfig
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.EventFactory
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
@@ -40,7 +39,7 @@ class NostrSignerExternal(
content: String,
onReady: (T) -> Unit,
) {
val id = Event.generateId(pubKey, createdAt, kind, tags, content).toHexKey()
val id = Event.generateId(pubKey, createdAt, kind, tags, content)
val event =
Event(
@@ -66,7 +66,7 @@ class NostrSignerSync(
): T? {
if (keyPair.privKey == null) return null
val id = Event.generateId(pubKey, createdAt, kind, tags, content)
val id = Event.generateIdBytes(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, keyPair.privKey).toHexKey()
return EventFactory.create(
@@ -21,6 +21,7 @@
package com.vitorpamplona.quartz.encoders
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.vitorpamplona.quartz.crypto.nip01.EventHasher
import com.vitorpamplona.quartz.events.Event
import junit.framework.TestCase.assertEquals
import org.junit.Test
@@ -107,14 +108,14 @@ class Nip01SerializerTest {
@Test()
fun fastEventSerializerTest() {
val event = Event.fromJson(payload2)
val mapper = Nip01Serializer.StringWriter()
Nip01Serializer().serializeEventInto(event, mapper)
val encoded = mapper.toString()
val eventJson = EventHasher.makeJsonForId(event.pubKey, event.createdAt, event.kind, event.tags, event.content)
assertEquals(event.makeJsonForId(), encoded)
assertEquals(eventJson, encoded)
}
@Test()
@@ -149,8 +150,9 @@ class Nip01SerializerTest {
Nip01Serializer().serializeEventInto(event, mapper)
val encoded = mapper.toString()
val eventJson = EventHasher.makeJsonForId(event.pubKey, event.createdAt, event.kind, event.tags, event.content)
assertEquals(event.makeJsonForId(), encoded)
assertEquals(eventJson, encoded)
}
@Test()
@@ -171,8 +173,9 @@ class Nip01SerializerTest {
Nip01Serializer().serializeEventInto(event, mapper)
val encoded = mapper.toString()
val eventJson = EventHasher.makeJsonForId(event.pubKey, event.createdAt, event.kind, event.tags, event.content)
assertEquals(event.makeJsonForId(), encoded)
assertEquals(eventJson, encoded)
}
@Test()
@@ -194,15 +197,15 @@ class Nip01SerializerTest {
Nip01Serializer().serializeEventInto(event, mapper)
val encoded = mapper.toString()
val eventJson = EventHasher.makeJsonForId(event.pubKey, event.createdAt, event.kind, event.tags, event.content)
assertEquals(event.makeJsonForId(), encoded)
assertEquals(eventJson, encoded)
}
@Test()
fun fastEventIdCheckTestPayload5() {
val event = Event.fromJson(payload5)
// assertEquals(event.generateId(), event.generateId2())
assertEquals("d1f097d3d9fcfb00df0c8ab5469be6484b14707d1e947c574ed636281d8dfd26", event.generateId())
}
}