From 1998711e60f2ce7027d702e523ee443e6cdf1108 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 8 Mar 2023 12:53:39 +0100 Subject: [PATCH 1/4] Extract TlvTypes to nip19 module --- .../vitorpamplona/amethyst/service/Nip19.kt | 35 ++++++++----------- .../amethyst/service/model/ATag.kt | 18 +++++----- .../amethyst/service/nip19/TlvTypes.kt | 8 +++++ 3 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt index 73b0a56bc..e2602a24d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt @@ -1,6 +1,7 @@ package com.vitorpamplona.amethyst.service import com.vitorpamplona.amethyst.model.toHexKey +import com.vitorpamplona.amethyst.service.nip19.TlvTypes import nostr.postr.bechToBytes import java.nio.ByteBuffer import java.nio.ByteOrder @@ -11,7 +12,7 @@ class Nip19 { USER, NOTE, RELAY, ADDRESS } - data class Return(val type: Type, val hex: String, val relay: String?) + data class Return(val type: Type, val hex: String, val relay: String? = null) fun uriToRoute(uri: String?): Return? { try { @@ -39,21 +40,21 @@ class Nip19 { } private fun npub(bytes: ByteArray): Return { - return Return(Type.USER, bytes.toHexKey(), null) + return Return(Type.USER, bytes.toHexKey()) } private fun note(bytes: ByteArray): Return { - return Return(Type.NOTE, bytes.toHexKey(), null) + return Return(Type.NOTE, bytes.toHexKey()) } private fun nprofile(bytes: ByteArray): Return? { val tlv = parseTLV(bytes) - val hex = tlv.get(NIP19TLVTypes.SPECIAL.id) + val hex = tlv.get(TlvTypes.SPECIAL.id) ?.get(0) ?.toHexKey() ?: return null - val relay = tlv.get(NIP19TLVTypes.RELAY.id) + val relay = tlv.get(TlvTypes.RELAY.id) ?.get(0) ?.toString(Charsets.UTF_8) @@ -63,11 +64,11 @@ class Nip19 { private fun nevent(bytes: ByteArray): Return? { val tlv = parseTLV(bytes) - val hex = tlv.get(NIP19TLVTypes.SPECIAL.id) + val hex = tlv.get(TlvTypes.SPECIAL.id) ?.get(0) ?.toHexKey() ?: return null - val relay = tlv.get(NIP19TLVTypes.RELAY.id) + val relay = tlv.get(TlvTypes.RELAY.id) ?.get(0) ?.toString(Charsets.UTF_8) @@ -76,29 +77,29 @@ class Nip19 { private fun nrelay(bytes: ByteArray): Return? { val relayUrl = parseTLV(bytes) - .get(NIP19TLVTypes.SPECIAL.id) + .get(TlvTypes.SPECIAL.id) ?.get(0) ?.toString(Charsets.UTF_8) ?: return null - return Return(Type.RELAY, relayUrl, null) + return Return(Type.RELAY, relayUrl) } private fun naddr(bytes: ByteArray): Return? { val tlv = parseTLV(bytes) - val d = tlv.get(NIP19TLVTypes.SPECIAL.id) + val d = tlv.get(TlvTypes.SPECIAL.id) ?.get(0) ?.toString(Charsets.UTF_8) ?: return null - val relay = tlv.get(NIP19TLVTypes.RELAY.id) + val relay = tlv.get(TlvTypes.RELAY.id) ?.get(0) ?.toString(Charsets.UTF_8) - val author = tlv.get(NIP19TLVTypes.AUTHOR.id) + val author = tlv.get(TlvTypes.AUTHOR.id) ?.get(0) ?.toHexKey() - val kind = tlv.get(NIP19TLVTypes.KIND.id) + val kind = tlv.get(TlvTypes.KIND.id) ?.get(0) ?.let { toInt32(it) } @@ -106,14 +107,6 @@ class Nip19 { } } -// Classes should start with an uppercase letter in kotlin -enum class NIP19TLVTypes(val id: Byte) { - SPECIAL(0), - RELAY(1), - AUTHOR(2), - KIND(3); -} - fun toInt32(bytes: ByteArray): Int { require(bytes.size == 4) { "length must be 4, got: ${bytes.size}" } return ByteBuffer.wrap(bytes, 0, 4).order(ByteOrder.BIG_ENDIAN).int diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt index f54a4c886..948d3b090 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt @@ -3,7 +3,7 @@ package com.vitorpamplona.amethyst.service.model import android.util.Log import com.vitorpamplona.amethyst.model.toByteArray import com.vitorpamplona.amethyst.model.toHexKey -import com.vitorpamplona.amethyst.service.NIP19TLVTypes +import com.vitorpamplona.amethyst.service.nip19.TlvTypes import com.vitorpamplona.amethyst.service.parseTLV import com.vitorpamplona.amethyst.service.toInt32 import fr.acinq.secp256k1.Hex @@ -21,15 +21,15 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela val relay = relay?.toByteArray(Charsets.UTF_8) var fullArray = - byteArrayOf(NIP19TLVTypes.SPECIAL.id, dTag.size.toByte()) + dTag + byteArrayOf(TlvTypes.SPECIAL.id, dTag.size.toByte()) + dTag if (relay != null) { - fullArray = fullArray + byteArrayOf(NIP19TLVTypes.RELAY.id, relay.size.toByte()) + relay + fullArray = fullArray + byteArrayOf(TlvTypes.RELAY.id, relay.size.toByte()) + relay } fullArray = fullArray + - byteArrayOf(NIP19TLVTypes.AUTHOR.id, author.size.toByte()) + author + - byteArrayOf(NIP19TLVTypes.KIND.id, kind.size.toByte()) + kind + byteArrayOf(TlvTypes.AUTHOR.id, author.size.toByte()) + author + + byteArrayOf(TlvTypes.KIND.id, kind.size.toByte()) + kind return Bech32.encodeBytes(hrp = "naddr", fullArray, Bech32.Encoding.Bech32) } @@ -64,10 +64,10 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela if (key.startsWith("naddr")) { val tlv = parseTLV(key.bechToBytes()) - 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) } + val d = tlv.get(TlvTypes.SPECIAL.id)?.get(0)?.toString(Charsets.UTF_8) ?: "" + val relay = tlv.get(TlvTypes.RELAY.id)?.get(0)?.toString(Charsets.UTF_8) + val author = tlv.get(TlvTypes.AUTHOR.id)?.get(0)?.toHexKey() + val kind = tlv.get(TlvTypes.KIND.id)?.get(0)?.let { toInt32(it) } if (kind != null && author != null) { return ATag(kind, author, d, relay) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt new file mode 100644 index 000000000..7199cc6e3 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt @@ -0,0 +1,8 @@ +package com.vitorpamplona.amethyst.service.nip19 + +enum class TlvTypes(val id: Byte) { + SPECIAL(0), + RELAY(1), + AUTHOR(2), + KIND(3); +} From 30fe861853492832e0ee941a7559ac0f385bd421 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 8 Mar 2023 12:58:28 +0100 Subject: [PATCH 2/4] Extract from Nip19 global funcs into another script --- .../vitorpamplona/amethyst/service/Nip19.kt | 27 ++----------------- .../amethyst/service/model/ATag.kt | 4 +-- .../amethyst/service/nip19/Utils.kt | 27 +++++++++++++++++++ 3 files changed, 31 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt index e2602a24d..43aa391e3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt @@ -2,9 +2,9 @@ package com.vitorpamplona.amethyst.service import com.vitorpamplona.amethyst.model.toHexKey import com.vitorpamplona.amethyst.service.nip19.TlvTypes +import com.vitorpamplona.amethyst.service.nip19.parseTLV +import com.vitorpamplona.amethyst.service.nip19.toInt32 import nostr.postr.bechToBytes -import java.nio.ByteBuffer -import java.nio.ByteOrder class Nip19 { @@ -106,26 +106,3 @@ class Nip19 { return Return(Type.ADDRESS, "$kind:$author:$d", relay) } } - -fun toInt32(bytes: ByteArray): Int { - require(bytes.size == 4) { "length must be 4, got: ${bytes.size}" } - return ByteBuffer.wrap(bytes, 0, 4).order(ByteOrder.BIG_ENDIAN).int -} - -fun parseTLV(data: ByteArray): Map> { - val result = mutableMapOf>() - 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)) - if (v.size < l) continue - - if (!result.containsKey(t)) { - result[t] = mutableListOf() - } - result[t]?.add(v) - } - return result -} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt index 948d3b090..08f436d7b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt @@ -4,8 +4,8 @@ import android.util.Log import com.vitorpamplona.amethyst.model.toByteArray import com.vitorpamplona.amethyst.model.toHexKey import com.vitorpamplona.amethyst.service.nip19.TlvTypes -import com.vitorpamplona.amethyst.service.parseTLV -import com.vitorpamplona.amethyst.service.toInt32 +import com.vitorpamplona.amethyst.service.nip19.parseTLV +import com.vitorpamplona.amethyst.service.nip19.toInt32 import fr.acinq.secp256k1.Hex import nostr.postr.Bech32 import nostr.postr.bechToBytes diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt new file mode 100644 index 000000000..01f0f1c04 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt @@ -0,0 +1,27 @@ +package com.vitorpamplona.amethyst.service.nip19 + +import java.nio.ByteBuffer +import java.nio.ByteOrder + +fun toInt32(bytes: ByteArray): Int { + require(bytes.size == 4) { "length must be 4, got: ${bytes.size}" } + return ByteBuffer.wrap(bytes, 0, 4).order(ByteOrder.BIG_ENDIAN).int +} + +fun parseTLV(data: ByteArray): Map> { + val result = mutableMapOf>() + 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)) + if (v.size < l) continue + + if (!result.containsKey(t)) { + result[t] = mutableListOf() + } + result[t]?.add(v) + } + return result +} From 49199fe892ef16f701e20d04e38cd84829f977b1 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 8 Mar 2023 13:07:33 +0100 Subject: [PATCH 3/4] Change Nip19 from class to object --- .../amethyst/service/model/ATag.kt | 3 +- .../amethyst/service/{ => nip19}/Nip19.kt | 8 +--- .../vitorpamplona/amethyst/ui/MainActivity.kt | 4 +- .../amethyst/ui/components/ClickableRoute.kt | 2 +- .../amethyst/ui/components/RichTextViewer.kt | 6 +-- .../amethyst/ui/qrcode/QrCodeScanner.kt | 4 +- .../vitorpamplona/amethyst/NIP19ParserTest.kt | 8 ++-- .../amethyst/service/Nip19Test.kt | 45 ++++--------------- .../amethyst/service/UtilsTest.kt | 35 +++++++++++++++ 9 files changed, 58 insertions(+), 57 deletions(-) rename app/src/main/java/com/vitorpamplona/amethyst/service/{ => nip19}/Nip19.kt (92%) create mode 100644 app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt index 08f436d7b..8ad632241 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt @@ -20,8 +20,7 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela val dTag = dTag.toByteArray(Charsets.UTF_8) val relay = relay?.toByteArray(Charsets.UTF_8) - var fullArray = - byteArrayOf(TlvTypes.SPECIAL.id, dTag.size.toByte()) + dTag + var fullArray = byteArrayOf(TlvTypes.SPECIAL.id, dTag.size.toByte()) + dTag if (relay != null) { fullArray = fullArray + byteArrayOf(TlvTypes.RELAY.id, relay.size.toByte()) + relay diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt similarity index 92% rename from app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt rename to app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt index 43aa391e3..5967dda1e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt @@ -1,13 +1,9 @@ -package com.vitorpamplona.amethyst.service +package com.vitorpamplona.amethyst.service.nip19 import com.vitorpamplona.amethyst.model.toHexKey -import com.vitorpamplona.amethyst.service.nip19.TlvTypes -import com.vitorpamplona.amethyst.service.nip19.parseTLV -import com.vitorpamplona.amethyst.service.nip19.toInt32 import nostr.postr.bechToBytes -class Nip19 { - +object Nip19 { enum class Type { USER, NOTE, RELAY, ADDRESS } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index 557beabdc..9ea473a80 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -16,7 +16,7 @@ import coil.decode.ImageDecoderDecoder import coil.decode.SvgDecoder import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.ServiceManager -import com.vitorpamplona.amethyst.service.Nip19 +import com.vitorpamplona.amethyst.service.nip19.Nip19 import com.vitorpamplona.amethyst.service.relays.Client import com.vitorpamplona.amethyst.ui.screen.AccountScreen import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel @@ -26,7 +26,7 @@ class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val nip19 = Nip19().uriToRoute(intent?.data?.toString()) + val nip19 = Nip19.uriToRoute(intent?.data?.toString()) val startingPage = when (nip19?.type) { Nip19.Type.USER -> "User/${nip19.hex}" Nip19.Type.NOTE -> "Note/${nip19.hex}" diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index 353de6161..af4cc78c7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -10,8 +10,8 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.text.AnnotatedString import androidx.navigation.NavController import com.vitorpamplona.amethyst.model.LocalCache -import com.vitorpamplona.amethyst.service.Nip19 import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent +import com.vitorpamplona.amethyst.service.nip19.Nip19 @Composable fun ClickableRoute( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index e658be1c2..7ef0dafce 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -12,9 +12,7 @@ import androidx.compose.material.LocalTextStyle import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -34,8 +32,8 @@ import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.material.MaterialRichText import com.halilibo.richtext.ui.resolveDefaults import com.vitorpamplona.amethyst.model.LocalCache -import com.vitorpamplona.amethyst.service.Nip19 import com.vitorpamplona.amethyst.service.lnurl.LnInvoiceUtil +import com.vitorpamplona.amethyst.service.nip19.Nip19 import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import java.net.MalformedURLException @@ -210,7 +208,7 @@ fun BechLink(word: String, navController: NavController) { } val nip19Route = try { - Nip19().uriToRoute(uri) + Nip19.uriToRoute(uri) } catch (e: Exception) { null } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt index 0b8ff04ea..1d3aa37d4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt @@ -21,7 +21,7 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage -import com.vitorpamplona.amethyst.service.Nip19 +import com.vitorpamplona.amethyst.service.nip19.Nip19 import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -52,7 +52,7 @@ fun QrCodeScanner(onScan: (String) -> Unit) { val analyzer = QRCodeAnalyzer { result -> result?.let { try { - val nip19 = Nip19().uriToRoute(it) + val nip19 = Nip19.uriToRoute(it) val startingPage = when (nip19?.type) { Nip19.Type.USER -> "User/${nip19.hex}" Nip19.Type.NOTE -> "Note/${nip19.hex}" diff --git a/app/src/test/java/com/vitorpamplona/amethyst/NIP19ParserTest.kt b/app/src/test/java/com/vitorpamplona/amethyst/NIP19ParserTest.kt index 0fa7ec5b1..0146d4056 100644 --- a/app/src/test/java/com/vitorpamplona/amethyst/NIP19ParserTest.kt +++ b/app/src/test/java/com/vitorpamplona/amethyst/NIP19ParserTest.kt @@ -1,26 +1,26 @@ package com.vitorpamplona.amethyst -import com.vitorpamplona.amethyst.service.Nip19 import com.vitorpamplona.amethyst.service.model.ATag +import com.vitorpamplona.amethyst.service.nip19.Nip19 import org.junit.Assert.assertEquals import org.junit.Test class NIP19ParserTest { @Test fun nAddrParser() { - val result = Nip19().uriToRoute("nostr:naddr1qqqqygzxpsj7dqha57pjk5k37gkn6g4nzakewtmqmnwryyhd3jfwlpgxtspsgqqqw4rs3xyxus") + val result = Nip19.uriToRoute("nostr:naddr1qqqqygzxpsj7dqha57pjk5k37gkn6g4nzakewtmqmnwryyhd3jfwlpgxtspsgqqqw4rs3xyxus") assertEquals("30023:460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c:", result?.hex) } @Test fun nAddrParser2() { - val result = Nip19().uriToRoute("nostr:naddr1qq8kwatfv3jj6amfwfjkwatpwfjqygxsm6lelvfda7qlg0tud9pfhduysy4vrexj65azqtdk4tr75j6xdspsgqqqw4rsg32ag8") + val result = Nip19.uriToRoute("nostr:naddr1qq8kwatfv3jj6amfwfjkwatpwfjqygxsm6lelvfda7qlg0tud9pfhduysy4vrexj65azqtdk4tr75j6xdspsgqqqw4rsg32ag8") assertEquals("30023:d0debf9fb12def81f43d7c69429bb784812ac1e4d2d53a202db6aac7ea4b466c:guide-wireguard", result?.hex) } @Test fun nAddrParse3() { - val result = Nip19().uriToRoute("naddr1qqyrswtyv5mnjv3sqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsygx3uczxts4hwue9ayfn7ggq62anzstde2qs749pm9tx2csuthhpjvpsgqqqw4rs8pmj38") + val result = Nip19.uriToRoute("naddr1qqyrswtyv5mnjv3sqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsygx3uczxts4hwue9ayfn7ggq62anzstde2qs749pm9tx2csuthhpjvpsgqqqw4rs8pmj38") assertEquals(Nip19.Type.ADDRESS, result?.type) assertEquals("30023:d1e60465c2b777325e9133f2100d2bb31416dca810f54a1d95665621c5dee193:89de7920", result?.hex) assertEquals("wss://relay.damus.io", result?.relay) diff --git a/app/src/test/java/com/vitorpamplona/amethyst/service/Nip19Test.kt b/app/src/test/java/com/vitorpamplona/amethyst/service/Nip19Test.kt index a5fdf395c..0c9c3aa0a 100644 --- a/app/src/test/java/com/vitorpamplona/amethyst/service/Nip19Test.kt +++ b/app/src/test/java/com/vitorpamplona/amethyst/service/Nip19Test.kt @@ -1,46 +1,21 @@ package com.vitorpamplona.amethyst.service +import com.vitorpamplona.amethyst.service.nip19.Nip19 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) + val actual = Nip19.uriToRoute(null) Assert.assertEquals(null, actual) } @Test() fun uri_to_route_unknown() { - val actual = nip19.uriToRoute("nostr:unknown") + val actual = Nip19.uriToRoute("nostr:unknown") Assert.assertEquals(null, actual) } @@ -48,7 +23,7 @@ class Nip19Test { @Test() fun uri_to_route_npub() { val actual = - nip19.uriToRoute("nostr:npub1hv7k2s755n697sptva8vkh9jz40lzfzklnwj6ekewfmxp5crwdjs27007y") + Nip19.uriToRoute("nostr:npub1hv7k2s755n697sptva8vkh9jz40lzfzklnwj6ekewfmxp5crwdjs27007y") Assert.assertEquals(Nip19.Type.USER, actual?.type) Assert.assertEquals( @@ -60,7 +35,7 @@ class Nip19Test { @Test() fun uri_to_route_note() { val actual = - nip19.uriToRoute("nostr:note1stqea6wmwezg9x6yyr6qkukw95ewtdukyaztycws65l8wppjmtpscawevv") + Nip19.uriToRoute("nostr:note1stqea6wmwezg9x6yyr6qkukw95ewtdukyaztycws65l8wppjmtpscawevv") Assert.assertEquals(Nip19.Type.NOTE, actual?.type) Assert.assertEquals( @@ -72,7 +47,7 @@ class Nip19Test { @Ignore("Test not implemented yet") @Test() fun uri_to_route_nprofile() { - val actual = nip19.uriToRoute("nostr:nprofile") + val actual = Nip19.uriToRoute("nostr:nprofile") Assert.assertEquals(Nip19.Type.USER, actual?.type) Assert.assertEquals("*", actual?.hex) @@ -81,7 +56,7 @@ class Nip19Test { @Ignore("Test not implemented yet") @Test() fun uri_to_route_nevent() { - val actual = nip19.uriToRoute("nostr:nevent") + val actual = Nip19.uriToRoute("nostr:nevent") Assert.assertEquals(Nip19.Type.USER, actual?.type) Assert.assertEquals("*", actual?.hex) @@ -90,7 +65,7 @@ class Nip19Test { @Ignore("Test not implemented yet") @Test() fun uri_to_route_nrelay() { - val actual = nip19.uriToRoute("nostr:nrelay") + val actual = Nip19.uriToRoute("nostr:nrelay") Assert.assertEquals(Nip19.Type.RELAY, actual?.type) Assert.assertEquals("*", actual?.hex) @@ -99,11 +74,9 @@ class Nip19Test { @Ignore("Test not implemented yet") @Test() fun uri_to_route_naddr() { - val actual = nip19.uriToRoute("nostr: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() } } diff --git a/app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt b/app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt new file mode 100644 index 000000000..bf791042d --- /dev/null +++ b/app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt @@ -0,0 +1,35 @@ +package com.vitorpamplona.amethyst.service + +import com.vitorpamplona.amethyst.service.nip19.toInt32 +import org.junit.Assert +import org.junit.Ignore +import org.junit.Test + +class UtilsTest { + + @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 (?) + } + + private fun byteArrayOfInts(vararg ints: Int) = + ByteArray(ints.size) { pos -> ints[pos].toByte() } +} From 26b5061042662b92b8af3b4f0cbcfb13eaf8fe3e Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 8 Mar 2023 13:26:23 +0100 Subject: [PATCH 4/4] Rename Utils to Tlv --- .../amethyst/service/model/ATag.kt | 22 ++++++------ .../amethyst/service/nip19/Nip19.kt | 28 +++++++-------- .../amethyst/service/nip19/Tlv.kt | 36 +++++++++++++++++++ .../amethyst/service/nip19/TlvTypes.kt | 8 ----- .../amethyst/service/nip19/Utils.kt | 27 -------------- .../service/{UtilsTest.kt => TlvTest.kt} | 10 +++--- 6 files changed, 65 insertions(+), 66 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Tlv.kt delete mode 100644 app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt delete mode 100644 app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt rename app/src/test/java/com/vitorpamplona/amethyst/service/{UtilsTest.kt => TlvTest.kt} (74%) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt index 8ad632241..27a39b4d8 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ATag.kt @@ -3,9 +3,7 @@ package com.vitorpamplona.amethyst.service.model import android.util.Log import com.vitorpamplona.amethyst.model.toByteArray import com.vitorpamplona.amethyst.model.toHexKey -import com.vitorpamplona.amethyst.service.nip19.TlvTypes -import com.vitorpamplona.amethyst.service.nip19.parseTLV -import com.vitorpamplona.amethyst.service.nip19.toInt32 +import com.vitorpamplona.amethyst.service.nip19.Tlv import fr.acinq.secp256k1.Hex import nostr.postr.Bech32 import nostr.postr.bechToBytes @@ -20,15 +18,15 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela val dTag = dTag.toByteArray(Charsets.UTF_8) val relay = relay?.toByteArray(Charsets.UTF_8) - var fullArray = byteArrayOf(TlvTypes.SPECIAL.id, dTag.size.toByte()) + dTag + var fullArray = byteArrayOf(Tlv.Type.SPECIAL.id, dTag.size.toByte()) + dTag if (relay != null) { - fullArray = fullArray + byteArrayOf(TlvTypes.RELAY.id, relay.size.toByte()) + relay + fullArray = fullArray + byteArrayOf(Tlv.Type.RELAY.id, relay.size.toByte()) + relay } fullArray = fullArray + - byteArrayOf(TlvTypes.AUTHOR.id, author.size.toByte()) + author + - byteArrayOf(TlvTypes.KIND.id, kind.size.toByte()) + kind + byteArrayOf(Tlv.Type.AUTHOR.id, author.size.toByte()) + author + + byteArrayOf(Tlv.Type.KIND.id, kind.size.toByte()) + kind return Bech32.encodeBytes(hrp = "naddr", fullArray, Bech32.Encoding.Bech32) } @@ -62,11 +60,11 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela val key = naddr.removePrefix("nostr:") if (key.startsWith("naddr")) { - val tlv = parseTLV(key.bechToBytes()) - val d = tlv.get(TlvTypes.SPECIAL.id)?.get(0)?.toString(Charsets.UTF_8) ?: "" - val relay = tlv.get(TlvTypes.RELAY.id)?.get(0)?.toString(Charsets.UTF_8) - val author = tlv.get(TlvTypes.AUTHOR.id)?.get(0)?.toHexKey() - val kind = tlv.get(TlvTypes.KIND.id)?.get(0)?.let { toInt32(it) } + val tlv = Tlv.parse(key.bechToBytes()) + val d = tlv.get(Tlv.Type.SPECIAL.id)?.get(0)?.toString(Charsets.UTF_8) ?: "" + val relay = tlv.get(Tlv.Type.RELAY.id)?.get(0)?.toString(Charsets.UTF_8) + val author = tlv.get(Tlv.Type.AUTHOR.id)?.get(0)?.toHexKey() + val kind = tlv.get(Tlv.Type.KIND.id)?.get(0)?.let { Tlv.toInt32(it) } if (kind != null && author != null) { return ATag(kind, author, d, relay) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt index 5967dda1e..796936331 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Nip19.kt @@ -44,13 +44,13 @@ object Nip19 { } private fun nprofile(bytes: ByteArray): Return? { - val tlv = parseTLV(bytes) + val tlv = Tlv.parse(bytes) - val hex = tlv.get(TlvTypes.SPECIAL.id) + val hex = tlv.get(Tlv.Type.SPECIAL.id) ?.get(0) ?.toHexKey() ?: return null - val relay = tlv.get(TlvTypes.RELAY.id) + val relay = tlv.get(Tlv.Type.RELAY.id) ?.get(0) ?.toString(Charsets.UTF_8) @@ -58,13 +58,13 @@ object Nip19 { } private fun nevent(bytes: ByteArray): Return? { - val tlv = parseTLV(bytes) + val tlv = Tlv.parse(bytes) - val hex = tlv.get(TlvTypes.SPECIAL.id) + val hex = tlv.get(Tlv.Type.SPECIAL.id) ?.get(0) ?.toHexKey() ?: return null - val relay = tlv.get(TlvTypes.RELAY.id) + val relay = tlv.get(Tlv.Type.RELAY.id) ?.get(0) ?.toString(Charsets.UTF_8) @@ -72,8 +72,8 @@ object Nip19 { } private fun nrelay(bytes: ByteArray): Return? { - val relayUrl = parseTLV(bytes) - .get(TlvTypes.SPECIAL.id) + val relayUrl = Tlv.parse(bytes) + .get(Tlv.Type.SPECIAL.id) ?.get(0) ?.toString(Charsets.UTF_8) ?: return null @@ -81,23 +81,23 @@ object Nip19 { } private fun naddr(bytes: ByteArray): Return? { - val tlv = parseTLV(bytes) + val tlv = Tlv.parse(bytes) - val d = tlv.get(TlvTypes.SPECIAL.id) + val d = tlv.get(Tlv.Type.SPECIAL.id) ?.get(0) ?.toString(Charsets.UTF_8) ?: return null - val relay = tlv.get(TlvTypes.RELAY.id) + val relay = tlv.get(Tlv.Type.RELAY.id) ?.get(0) ?.toString(Charsets.UTF_8) - val author = tlv.get(TlvTypes.AUTHOR.id) + val author = tlv.get(Tlv.Type.AUTHOR.id) ?.get(0) ?.toHexKey() - val kind = tlv.get(TlvTypes.KIND.id) + val kind = tlv.get(Tlv.Type.KIND.id) ?.get(0) - ?.let { toInt32(it) } + ?.let { Tlv.toInt32(it) } return Return(Type.ADDRESS, "$kind:$author:$d", relay) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Tlv.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Tlv.kt new file mode 100644 index 000000000..ef285d627 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Tlv.kt @@ -0,0 +1,36 @@ +package com.vitorpamplona.amethyst.service.nip19 + +import java.nio.ByteBuffer +import java.nio.ByteOrder + +object Tlv { + enum class Type(val id: Byte) { + SPECIAL(0), + RELAY(1), + AUTHOR(2), + KIND(3); + } + + fun toInt32(bytes: ByteArray): Int { + require(bytes.size == 4) { "length must be 4, got: ${bytes.size}" } + return ByteBuffer.wrap(bytes, 0, 4).order(ByteOrder.BIG_ENDIAN).int + } + + fun parse(data: ByteArray): Map> { + val result = mutableMapOf>() + 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)) + if (v.size < l) continue + + if (!result.containsKey(t)) { + result[t] = mutableListOf() + } + result[t]?.add(v) + } + return result + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt deleted file mode 100644 index 7199cc6e3..000000000 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/TlvTypes.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.vitorpamplona.amethyst.service.nip19 - -enum class TlvTypes(val id: Byte) { - SPECIAL(0), - RELAY(1), - AUTHOR(2), - KIND(3); -} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt deleted file mode 100644 index 01f0f1c04..000000000 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/nip19/Utils.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.vitorpamplona.amethyst.service.nip19 - -import java.nio.ByteBuffer -import java.nio.ByteOrder - -fun toInt32(bytes: ByteArray): Int { - require(bytes.size == 4) { "length must be 4, got: ${bytes.size}" } - return ByteBuffer.wrap(bytes, 0, 4).order(ByteOrder.BIG_ENDIAN).int -} - -fun parseTLV(data: ByteArray): Map> { - val result = mutableMapOf>() - 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)) - if (v.size < l) continue - - if (!result.containsKey(t)) { - result[t] = mutableListOf() - } - result[t]?.add(v) - } - return result -} diff --git a/app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt b/app/src/test/java/com/vitorpamplona/amethyst/service/TlvTest.kt similarity index 74% rename from app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt rename to app/src/test/java/com/vitorpamplona/amethyst/service/TlvTest.kt index bf791042d..9f9087036 100644 --- a/app/src/test/java/com/vitorpamplona/amethyst/service/UtilsTest.kt +++ b/app/src/test/java/com/vitorpamplona/amethyst/service/TlvTest.kt @@ -1,25 +1,25 @@ package com.vitorpamplona.amethyst.service -import com.vitorpamplona.amethyst.service.nip19.toInt32 +import com.vitorpamplona.amethyst.service.nip19.Tlv import org.junit.Assert import org.junit.Ignore import org.junit.Test -class UtilsTest { +class TlvTest { @Test(expected = IllegalArgumentException::class) fun to_int_32_length_smaller_than_4() { - toInt32(byteArrayOfInts(1, 2, 3)) + Tlv.toInt32(byteArrayOfInts(1, 2, 3)) } @Test(expected = IllegalArgumentException::class) fun to_int_32_length_bigger_than_4() { - toInt32(byteArrayOfInts(1, 2, 3, 4, 5)) + Tlv.toInt32(byteArrayOfInts(1, 2, 3, 4, 5)) } @Test() fun to_int_32_length_4() { - val actual = toInt32(byteArrayOfInts(1, 2, 3, 4)) + val actual = Tlv.toInt32(byteArrayOfInts(1, 2, 3, 4)) Assert.assertEquals(16909060, actual) }