Merge pull request #187 from Chemaclass/refactor-Nip19

Refactor Nip19
This commit is contained in:
Vitor Pamplona 2023-03-03 18:03:58 -05:00 committed by GitHub
commit a84be7fc61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 188 additions and 47 deletions

View File

@ -1,71 +1,103 @@
package com.vitorpamplona.amethyst.service
import com.vitorpamplona.amethyst.model.toByteArray
import com.vitorpamplona.amethyst.model.toHexKey
import com.vitorpamplona.amethyst.service.model.ATag
import nostr.postr.bechToBytes
import java.nio.ByteBuffer
import java.nio.ByteOrder
import nostr.postr.Bech32
import nostr.postr.bechToBytes
import nostr.postr.toByteArray
class Nip19 {
enum class Type {
USER, NOTE, RELAY, ADDRESS
}
data class Return(val type: Type, val hex: String)
fun uriToRoute(uri: String?): Return? {
try {
val key = uri?.removePrefix("nostr:")
val key = uri?.removePrefix("nostr:") ?: return null
if (key != null) {
val bytes = key.bechToBytes()
if (key.startsWith("npub")) {
return Return(Type.USER, bytes.toHexKey())
}
if (key.startsWith("note")) {
return Return(Type.NOTE, bytes.toHexKey())
}
if (key.startsWith("nprofile")) {
val tlv = parseTLV(bytes)
val hex = tlv.get(NIP19TLVTypes.SPECIAL.id)?.get(0)?.toHexKey()
if (hex != null)
return Return(Type.USER, hex)
}
if (key.startsWith("nevent")) {
val tlv = parseTLV(bytes)
val hex = tlv.get(NIP19TLVTypes.SPECIAL.id)?.get(0)?.toHexKey()
if (hex != null)
return Return(Type.USER, hex)
}
if (key.startsWith("nrelay")) {
val tlv = parseTLV(bytes)
val relayUrl = tlv.get(NIP19TLVTypes.SPECIAL.id)?.get(0)?.toString(Charsets.UTF_8)
if (relayUrl != null)
return Return(Type.RELAY, relayUrl)
}
if (key.startsWith("naddr")) {
val tlv = parseTLV(bytes)
val d = tlv.get(NIP19TLVTypes.SPECIAL.id)?.get(0)?.toString(Charsets.UTF_8)
val relay = tlv.get(NIP19TLVTypes.RELAY.id)?.get(0)?.toString(Charsets.UTF_8)
val author = tlv.get(NIP19TLVTypes.AUTHOR.id)?.get(0)?.toHexKey()
val kind = tlv.get(NIP19TLVTypes.KIND.id)?.get(0)?.let { toInt32(it) }
if (d != null)
return Return(Type.ADDRESS, "$kind:$author:$d")
}
val bytes = key.bechToBytes()
if (key.startsWith("npub")) {
return npub(bytes)
} else if (key.startsWith("note")) {
return note(bytes)
} else if (key.startsWith("nprofile")) {
return nprofile(bytes)
} else if (key.startsWith("nevent")) {
return nevent(bytes)
} else if (key.startsWith("nrelay")) {
return nrelay(bytes)
} else if (key.startsWith("naddr")) {
return naddr(bytes)
}
} catch (e: Throwable) {
println("Issue trying to Decode NIP19 ${uri}: ${e.message}")
//e.printStackTrace()
}
return null
}
private fun npub(bytes: ByteArray): Return {
return Return(Type.USER, bytes.toHexKey())
}
private fun note(bytes: ByteArray): Return {
return Return(Type.NOTE, bytes.toHexKey());
}
private fun nprofile(bytes: ByteArray): Return? {
val hex = parseTLV(bytes)
.get(NIP19TLVTypes.SPECIAL.id)
?.get(0)
?.toHexKey() ?: return null
return Return(Type.USER, hex)
}
private fun nevent(bytes: ByteArray): Return? {
val hex = parseTLV(bytes)
.get(NIP19TLVTypes.SPECIAL.id)
?.get(0)
?.toHexKey() ?: return null
return Return(Type.USER, hex)
}
private fun nrelay(bytes: ByteArray): Return? {
val relayUrl = parseTLV(bytes)
.get(NIP19TLVTypes.SPECIAL.id)
?.get(0)
?.toString(Charsets.UTF_8) ?: return null
return Return(Type.RELAY, relayUrl)
}
private fun naddr(bytes: ByteArray): Return? {
val tlv = parseTLV(bytes)
val d = tlv.get(NIP19TLVTypes.SPECIAL.id)
?.get(0)
?.toString(Charsets.UTF_8) ?: return null
val relay = tlv.get(NIP19TLVTypes.RELAY.id)
?.get(0)
?.toString(Charsets.UTF_8)
val author = tlv.get(NIP19TLVTypes.AUTHOR.id)
?.get(0)
?.toHexKey()
val kind = tlv.get(NIP19TLVTypes.KIND.id)
?.get(0)
?.let { toInt32(it) }
return Return(Type.ADDRESS, "$kind:$author:$d")
}
}
enum class NIP19TLVTypes(val id: Byte) { //classes should start with an uppercase letter in kotlin
// Classes should start with an uppercase letter in kotlin
enum class NIP19TLVTypes(val id: Byte) {
SPECIAL(0),
RELAY(1),
AUTHOR(2),
@ -78,19 +110,19 @@ fun toInt32(bytes: ByteArray): Int {
}
fun parseTLV(data: ByteArray): Map<Byte, List<ByteArray>> {
var result = mutableMapOf<Byte, MutableList<ByteArray>>()
val result = mutableMapOf<Byte, MutableList<ByteArray>>()
var rest = data
while (rest.isNotEmpty()) {
val t = rest[0]
val l = rest[1]
val v = rest.sliceArray(IntRange(2, (2 + l) - 1))
rest = rest.sliceArray(IntRange(2 + l, rest.size-1))
rest = rest.sliceArray(IntRange(2 + l, rest.size - 1))
if (v.size < l) continue
if (!result.containsKey(t)) {
result.put(t, mutableListOf())
result[t] = mutableListOf()
}
result.get(t)?.add(v)
result[t]?.add(v)
}
return result
}

View File

@ -0,0 +1,109 @@
package com.vitorpamplona.amethyst.service
import org.junit.Assert
import org.junit.Ignore
import org.junit.Test
class Nip19Test {
private val nip19 = Nip19();
@Test(expected = IllegalArgumentException::class)
fun to_int_32_length_smaller_than_4() {
toInt32(byteArrayOfInts(1, 2, 3))
}
@Test(expected = IllegalArgumentException::class)
fun to_int_32_length_bigger_than_4() {
toInt32(byteArrayOfInts(1, 2, 3, 4, 5))
}
@Test()
fun to_int_32_length_4() {
val actual = toInt32(byteArrayOfInts(1, 2, 3, 4))
Assert.assertEquals(16909060, actual)
}
@Ignore("Test not implemented yet")
@Test()
fun parse_TLV() {
// TODO: I don't know how to test this (?)
}
@Test()
fun uri_to_route_null() {
val actual = nip19.uriToRoute(null)
Assert.assertEquals(null, actual)
}
@Test()
fun uri_to_route_unknown() {
val actual = nip19.uriToRoute("nostr:unknown")
Assert.assertEquals(null, actual)
}
@Test()
fun uri_to_route_npub() {
val actual =
nip19.uriToRoute("nostr:npub1hv7k2s755n697sptva8vkh9jz40lzfzklnwj6ekewfmxp5crwdjs27007y")
Assert.assertEquals(Nip19.Type.USER, actual?.type)
Assert.assertEquals(
"bb3d6543d4a4f45f402b674ecb5cb2155ff12456fcdd2d66d9727660d3037365",
actual?.hex
)
}
@Test()
fun uri_to_route_note() {
val actual =
nip19.uriToRoute("nostr:note1stqea6wmwezg9x6yyr6qkukw95ewtdukyaztycws65l8wppjmtpscawevv")
Assert.assertEquals(Nip19.Type.NOTE, actual?.type)
Assert.assertEquals(
"82c19ee9db7644829b4420f40b72ce2d32e5b7962744b261d0d53e770432dac3",
actual?.hex
)
}
@Ignore("Test not implemented yet")
@Test()
fun uri_to_route_nprofile() {
val actual = nip19.uriToRoute("nostr:nprofile")
Assert.assertEquals(Nip19.Type.USER, actual?.type)
Assert.assertEquals("*", actual?.hex)
}
@Ignore("Test not implemented yet")
@Test()
fun uri_to_route_nevent() {
val actual = nip19.uriToRoute("nostr:nevent")
Assert.assertEquals(Nip19.Type.USER, actual?.type)
Assert.assertEquals("*", actual?.hex)
}
@Ignore("Test not implemented yet")
@Test()
fun uri_to_route_nrelay() {
val actual = nip19.uriToRoute("nostr:nrelay")
Assert.assertEquals(Nip19.Type.RELAY, actual?.type)
Assert.assertEquals("*", actual?.hex)
}
@Ignore("Test not implemented yet")
@Test()
fun uri_to_route_naddr() {
val actual = nip19.uriToRoute("nostr:naddr")
Assert.assertEquals(Nip19.Type.ADDRESS, actual?.type)
Assert.assertEquals("*", actual?.hex)
}
private fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }
}