diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt index 138a46c33..bf76e2ab6 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt @@ -36,7 +36,7 @@ import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair -import com.vitorpamplona.quartz.utils.sha256 +import com.vitorpamplona.quartz.utils.sha256.sha256 import junit.framework.TestCase.assertEquals import junit.framework.TestCase.fail import kotlinx.coroutines.CoroutineScope diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index b1593b09d..778f85b9a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -99,8 +99,8 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds import com.vitorpamplona.quartz.nip01Core.tags.references.references import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent -import com.vitorpamplona.quartz.nip02FollowList.ContactTag import com.vitorpamplona.quartz.nip02FollowList.ReadWrite +import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.reply @@ -3542,7 +3542,7 @@ class Account( val event = (addressableNote.event as? PeopleListEvent) event != null && event.pubKey == pubkey && - (event.hasAnyTaggedUser() || event.publicAndPrivateUserCache?.isNotEmpty() == true) + (event.hasAnyTaggedUser() || event.cachedPrivateTags()?.isNotEmpty() == true) } fun markAsRead( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index e29ec5337..3abeb310d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -683,11 +683,11 @@ object LocalCache { is BadgeAwardEvent -> event.awardDefinition().map { getOrCreateAddressableNote(it) } is PrivateDmEvent -> event.taggedEvents().mapNotNull { checkGetOrCreateNote(it) } is RepostEvent -> - event.boostedPost().mapNotNull { checkGetOrCreateNote(it) } + - event.taggedAddresses().map { getOrCreateAddressableNote(it) } + event.boostedEventIds().mapNotNull { checkGetOrCreateNote(it) } + + event.boostedAddresses().map { getOrCreateAddressableNote(it) } is GenericRepostEvent -> - event.boostedPost().mapNotNull { checkGetOrCreateNote(it) } + - event.taggedAddresses().map { getOrCreateAddressableNote(it) } + event.boostedEventIds().mapNotNull { checkGetOrCreateNote(it) } + + event.boostedAddresses().map { getOrCreateAddressableNote(it) } is CommunityPostApprovalEvent -> event.approvedEvents().mapNotNull { checkGetOrCreateNote(it) } + event.approvedAddresses().map { getOrCreateAddressableNote(it) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PeopleList.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PeopleList.kt index 0da9a10ac..4b47074ed 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PeopleList.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PeopleList.kt @@ -54,6 +54,7 @@ import com.vitorpamplona.amethyst.ui.note.UserCompose import com.vitorpamplona.amethyst.ui.note.getGradient import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.DividerThickness +import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -94,7 +95,7 @@ fun DisplayPeopleList( ) LaunchedEffect(Unit) { - accountViewModel.loadUsers(noteEvent.bookmarkedPeopleIds()) { + accountViewModel.loadUsers(noteEvent.taggedUserIds()) { members = it } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt index 4e796e5df..5763ea36f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt @@ -42,8 +42,8 @@ import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent -import com.vitorpamplona.quartz.nip02FollowList.ContactTag import com.vitorpamplona.quartz.nip02FollowList.ReadWrite +import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06 import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EnsureTest.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EnsureTest.kt new file mode 100644 index 000000000..00ca68592 --- /dev/null +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EnsureTest.kt @@ -0,0 +1,143 @@ +/** + * 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.benchmark + +import androidx.benchmark.junit4.BenchmarkRule +import androidx.benchmark.junit4.measureRepeated +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.vitorpamplona.quartz.utils.ensure +import junit.framework.TestCase.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class EnsureTest { + @get:Rule + val scope = BenchmarkRule() + + private val testArgs = arrayOf("0", "1f") + + companion object { + const val TAG = "0" + const val KEY_SIZE = 2 + + private val isHexChar = + BooleanArray(256).apply { + "0123456789abcdefABCDEF".forEach { this[it.code] = true } + } + + @JvmStatic + fun String.isHex() = all { isHexChar[it.code] } + } + + fun directCheckElse(args: Array) = + if (args[0] != TAG || args[1].length != KEY_SIZE || !args[1].isHex()) { + null + } else { + args[0] + args[1] + } + + fun directCheck(args: Array): String? { + if (args[0] != TAG || args[1].length != KEY_SIZE || !args[1].isHex()) return null + return args[0] + args[1] + } + + fun directCheck2(args: Array): String? { + if (args[0] == TAG && args[1].length == KEY_SIZE && args[1].isHex()) return args[0] + args[1] + return null + } + + fun linedCheck(args: Array): String? { + if (args[0] != TAG) return null + if (args[1].length != KEY_SIZE) return null + if (!args[1].isHex()) return null + return args[0] + args[1] + } + + fun ensureCheck(args: Array): String? { + ensure(args[0] == TAG) { return null } + ensure(args[1].length == KEY_SIZE) { return null } + ensure(args[1].isHex()) { return null } + return args[0] + args[1] + } + + fun checkCatch(args: Array): String? = + runCatching { + check(args[0] == TAG) + check(args[1].length == KEY_SIZE) + check(args[1].isHex()) + args[0] + args[1] + }.getOrNull() + + fun whenCheck(args: Array) = + when (false) { + (args[0] == TAG), + (args[1].length == KEY_SIZE), + (args[1].isHex()), + -> null + else -> args[0] + args[1] + } + + fun ensureAll(args: Array): String? { + ensureAll { + ensure { args[0] == TAG } + ensure { args[1].length == KEY_SIZE } + ensure { args[1].isHex() } + }.ifFail { return null } + return args[0] + args[1] + } + + @Test + fun directCheckElse() = scope.measureRepeated { assertTrue(directCheckElse(testArgs) != null) } + + @Test + fun directCheck() = scope.measureRepeated { assertTrue(directCheck(testArgs) != null) } + + @Test + fun linedCheck() = scope.measureRepeated { assertTrue(linedCheck(testArgs) != null) } + + @Test + fun ensureCheck() = scope.measureRepeated { assertTrue(ensureCheck(testArgs) != null) } + + @Test + fun checkCatch() = scope.measureRepeated { assertTrue(checkCatch(testArgs) != null) } + + @Test + fun whenCheck() = scope.measureRepeated { assertTrue(whenCheck(testArgs) != null) } + + @Test + fun ensureAll() = scope.measureRepeated { assertTrue(ensureAll(testArgs) != null) } +} + +class EnsureScope( + var passing: Boolean = true, +) { + inline fun ensure(predicate: () -> Boolean) { + if (passing) passing = predicate() + } + + inline fun ifFail(ifFail: () -> Unit) { + if (!passing) ifFail() + } +} + +inline fun ensureAll(block: EnsureScope.() -> Unit) = EnsureScope().apply(block) diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HexBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HexBenchmark.kt index b18ca104f..beb557372 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HexBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HexBenchmark.kt @@ -91,4 +91,19 @@ class HexBenchmark { fun isHex() { r.measureRepeated { Hex.isHex(hex) } } + + @Test + fun newIsHex() { + val isHexChar = + BooleanArray(256).apply { + "0123456789abcdefABCDEF".forEach { this[it.code] = true } + } + + r.measureRepeated { + for (c in hex.indices) { + if (!isHexChar[hex[c].code]) return@measureRepeated + } + return@measureRepeated + } + } } diff --git a/commons/src/main/java/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt b/commons/src/main/java/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt index a3325657b..ee473f7fa 100644 --- a/commons/src/main/java/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt +++ b/commons/src/main/java/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt @@ -33,7 +33,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTag import com.vitorpamplona.quartz.nip92IMeta.imetasByUrl import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -79,7 +79,7 @@ class RichTextParser { MediaUrlImage( url = fullUrl, description = description ?: frags[AltTag.TAG_NAME] ?: tags[AltTag.TAG_NAME]?.firstOrNull(), - hash = frags[HashTag.TAG_NAME] ?: tags[HashTag.TAG_NAME]?.firstOrNull(), + hash = frags[HashSha256Tag.TAG_NAME] ?: tags[HashSha256Tag.TAG_NAME]?.firstOrNull(), blurhash = frags[BlurhashTag.TAG_NAME] ?: tags[BlurhashTag.TAG_NAME]?.firstOrNull(), dim = frags[DimensionTag.TAG_NAME]?.let { DimensionTag.parse(it) } ?: tags[DimensionTag.TAG_NAME]?.firstOrNull()?.let { DimensionTag.parse(it) }, contentWarning = frags[ContentWarningTag.TAG_NAME] ?: tags[ContentWarningTag.TAG_NAME]?.firstOrNull(), @@ -90,7 +90,7 @@ class RichTextParser { MediaUrlVideo( url = fullUrl, description = description ?: frags[AltTag.TAG_NAME] ?: tags[AltTag.TAG_NAME]?.firstOrNull(), - hash = frags[HashTag.TAG_NAME] ?: tags[HashTag.TAG_NAME]?.firstOrNull(), + hash = frags[HashSha256Tag.TAG_NAME] ?: tags[HashSha256Tag.TAG_NAME]?.firstOrNull(), blurhash = frags[BlurhashTag.TAG_NAME] ?: tags[BlurhashTag.TAG_NAME]?.firstOrNull(), dim = frags[DimensionTag.TAG_NAME]?.let { DimensionTag.parse(it) } ?: tags[DimensionTag.TAG_NAME]?.firstOrNull()?.let { DimensionTag.parse(it) }, contentWarning = frags[ContentWarningTag.TAG_NAME] ?: tags[ContentWarningTag.TAG_NAME]?.firstOrNull(), diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/FileStorageHeaderEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/FileStorageHeaderEvent.kt index 621c9f852..064257892 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/FileStorageHeaderEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/FileStorageHeaderEvent.kt @@ -34,7 +34,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -62,7 +62,7 @@ class FileStorageHeaderEvent( fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse) - fun hash() = tags.firstNotNullOfOrNull(HashTag::parse) + fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse) fun size() = tags.firstNotNullOfOrNull(SizeTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/TagArrayBuilderExt.kt index 5e0d11845..f4ec26cbe 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/nip95/header/TagArrayBuilderExt.kt @@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip94FileMetadata.tags.TorrentInfoHash fun TagArrayBuilder.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType)) -fun TagArrayBuilder.hash(hash: HexKey) = add(HashTag.assemble(hash)) +fun TagArrayBuilder.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash)) fun TagArrayBuilder.fileSize(size: Int) = add(SizeTag.assemble(size)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/ProfileGalleryEntryEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/ProfileGalleryEntryEvent.kt index 2d6777604..124b4fd9b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/ProfileGalleryEntryEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/ProfileGalleryEntryEvent.kt @@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -58,7 +58,7 @@ class ProfileGalleryEntryEvent( fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse) - fun hash() = tags.firstNotNullOfOrNull(HashTag::parse) + fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse) fun size() = tags.firstNotNullOfOrNull(SizeTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/TagArrayBuilderExt.kt index 3a0d22a4a..afa46548b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/profileGallery/TagArrayBuilderExt.kt @@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -42,7 +42,7 @@ fun TagArrayBuilder.url(url: String) = add(UrlTag.asse fun TagArrayBuilder.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType)) -fun TagArrayBuilder.hash(hash: HexKey) = add(HashTag.assemble(hash)) +fun TagArrayBuilder.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash)) fun TagArrayBuilder.fileSize(size: Int) = add(SizeTag.assemble(size)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/relationshipStatus/RelationshipStatusEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/relationshipStatus/RelationshipStatusEvent.kt index 46fe9fe74..23792c2cb 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/relationshipStatus/RelationshipStatusEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/relationshipStatus/RelationshipStatusEvent.kt @@ -30,8 +30,8 @@ import com.vitorpamplona.quartz.nip01Core.core.tagArray import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag import com.vitorpamplona.quartz.nip31Alts.alt -import com.vitorpamplona.quartz.nip51Lists.Nip51PrivateTags import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent +import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -77,7 +77,7 @@ class RelationshipStatusEvent( privateInitializer() } - Nip51PrivateTags.encryptNip44(privateTags, signer) { content -> + PrivateTagsInContent.encryptNip44(privateTags, signer) { content -> signer.sign(createdAt, KIND, publicTags, content, onReady) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/HexKey.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/HexKey.kt index caa4536f6..133075778 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/HexKey.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/HexKey.kt @@ -28,3 +28,7 @@ typealias HexKey = String fun ByteArray.toHexKey(): HexKey = Hex.encode(this) fun HexKey.hexToByteArray(): ByteArray = Hex.decode(this) + +const val PUBKEY_LENGTH = 64 + +const val EVENT_ID_LENGTH = 64 diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Tag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Tag.kt index 71e22c657..d97acc9c1 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Tag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Tag.kt @@ -22,6 +22,16 @@ package com.vitorpamplona.quartz.nip01Core.core typealias Tag = Array +/** + * Returns if the Tag has at least $index elements. + */ +fun Tag.has(index: Int) = size > index + +/** + * Returns if the Tag has less than $index elements. + */ +fun Tag.lacks(index: Int) = size <= index + fun Tag.name() = this[0] fun Tag.value() = this[1] diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/hints/HintProvider.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/hints/HintProviders.kt similarity index 86% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/hints/HintProvider.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/hints/HintProviders.kt index 80e952189..aa94e3ca5 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/hints/HintProvider.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/hints/HintProviders.kt @@ -24,10 +24,14 @@ import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint -interface HintProvider { - fun eventHints(): EventIdHint - - fun addressIdHints(): AddressHint - - fun pubKeyHints(): PubKeyHint +interface EventHintProvider { + fun eventHints(): List +} + +interface AddressHintProvider { + fun addressHints(): List +} + +interface PubKeyHintProvider { + fun pubKeyHints(): List } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt index 36eba88da..b174d3ff8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.match import com.vitorpamplona.quartz.nip01Core.core.name import com.vitorpamplona.quartz.nip01Core.core.value +import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.pointerSizeInBytes @@ -128,6 +129,12 @@ data class ATag( return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): AddressHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || !tag[1].contains(':') || tag[2].isEmpty()) return null + return AddressHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( aTagId: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt index d4d58791c..0f59c7897 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip01Core.tags.events import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.bytesUsedInMemory @@ -58,7 +59,7 @@ data class ETag( const val TAG_SIZE = 2 @JvmStatic - fun isTagged(tag: Array) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty() + fun isTagged(tag: Array) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].length == 64 @JvmStatic fun isTagged( @@ -68,16 +69,22 @@ data class ETag( @JvmStatic fun parse(tag: Array): ETag? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null return ETag(tag[1], tag.getOrNull(2), tag.getOrNull(3)) } @JvmStatic fun parseId(tag: Array): String? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): EventIdHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return EventIdHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( eventId: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/HashTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/HashtagTag.kt similarity index 95% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/HashTag.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/HashtagTag.kt index 68e46b97e..773b14db2 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/HashTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/HashtagTag.kt @@ -20,7 +20,7 @@ */ package com.vitorpamplona.quartz.nip01Core.tags.hashtags -class HashTag { +class HashtagTag { companion object { const val TAG_NAME = "t" const val TAG_SIZE = 2 @@ -30,7 +30,7 @@ class HashTag { @JvmStatic fun parse(tag: Array): String? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null return tag[1] } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayBuilderExt.kt index ec68e5877..a6f7cee6d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayBuilderExt.kt @@ -23,6 +23,6 @@ package com.vitorpamplona.quartz.nip01Core.tags.hashtags import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder -fun TagArrayBuilder.hashtag(tag: String) = addAll(HashTag.assembleDualCase(tag)) +fun TagArrayBuilder.hashtag(tag: String) = addAll(HashtagTag.assembleDualCase(tag)) -fun TagArrayBuilder.hashtags(tag: List) = addAll(HashTag.assemble(tag)) +fun TagArrayBuilder.hashtags(tag: List) = addAll(HashtagTag.assemble(tag)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayExt.kt index 01466ff1b..849e38167 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/hashtags/TagArrayExt.kt @@ -30,18 +30,18 @@ import com.vitorpamplona.quartz.nip01Core.core.isAnyLowercaseTagged import com.vitorpamplona.quartz.nip01Core.core.isTagged import com.vitorpamplona.quartz.nip01Core.core.mapValues -fun TagArray.forEachHashTag(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(HashTag.TAG_NAME, onEach) +fun TagArray.forEachHashTag(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(HashtagTag.TAG_NAME, onEach) -fun TagArray.anyHashTag(onEach: (str: String) -> Boolean) = this.anyTagged(HashTag.TAG_NAME, onEach) +fun TagArray.anyHashTag(onEach: (str: String) -> Boolean) = this.anyTagged(HashtagTag.TAG_NAME, onEach) -fun TagArray.hasHashtags() = this.hasTagWithContent(HashTag.TAG_NAME) +fun TagArray.hasHashtags() = this.hasTagWithContent(HashtagTag.TAG_NAME) -fun TagArray.hashtags() = this.mapValues(HashTag.TAG_NAME) +fun TagArray.hashtags() = this.mapValues(HashtagTag.TAG_NAME) -fun TagArray.countHashtags() = this.count(HashTag::isTagged) +fun TagArray.countHashtags() = this.count(HashtagTag::isTagged) -fun TagArray.isTaggedHash(hashtag: String) = this.isTagged(HashTag.TAG_NAME, hashtag, true) +fun TagArray.isTaggedHash(hashtag: String) = this.isTagged(HashtagTag.TAG_NAME, hashtag, true) -fun TagArray.isTaggedHashes(hashtags: Set) = this.isAnyLowercaseTagged(HashTag.TAG_NAME, hashtags) +fun TagArray.isTaggedHashes(hashtags: Set) = this.isAnyLowercaseTagged(HashtagTag.TAG_NAME, hashtags) -fun TagArray.firstIsTaggedHashes(hashtags: Set) = this.firstAnyLowercaseTaggedValue(HashTag.TAG_NAME, hashtags) +fun TagArray.firstIsTaggedHashes(hashtags: Set) = this.firstAnyLowercaseTaggedValue(HashtagTag.TAG_NAME, hashtags) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt index 9064ff6b0..55a2270ac 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt @@ -25,7 +25,7 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray -import com.vitorpamplona.quartz.nip01Core.core.isNotName +import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile import com.vitorpamplona.quartz.nip19Bech32.toNpub import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -59,14 +59,14 @@ data class PTag( @JvmStatic fun parse(tag: Tag): PTag? { - if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null if (tag[1].length != 64) return null return PTag(tag[1], tag.getOrNull(2)) } @JvmStatic fun parseKey(tag: Array): HexKey? { - if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null if (tag[1].length != 64) { Log.w("PTag", "Invalid `$TAG_NAME` value ${tag.joinToString(", ")}") return null @@ -74,6 +74,12 @@ data class PTag( return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): PubKeyHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return PubKeyHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( pubkey: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactListEvent.kt index 8669f730e..a5d995afb 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactListEvent.kt @@ -23,6 +23,8 @@ package com.vitorpamplona.quartz.nip02FollowList import androidx.compose.runtime.Stable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag @@ -32,8 +34,9 @@ import com.vitorpamplona.quartz.nip01Core.tags.geohash.isTaggedGeoHash import com.vitorpamplona.quartz.nip01Core.tags.hashtags.countHashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.isTaggedHash -import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser +import com.vitorpamplona.quartz.nip02FollowList.tags.AddressFollowTag +import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.utils.TimeUtils @@ -45,7 +48,13 @@ class ContactListEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + AddressHintProvider, + PubKeyHintProvider { + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + + override fun pubKeyHints() = tags.mapNotNull(ContactTag::parseAsHint) + /** * Returns a list of p-tags that are verified as hex keys. */ @@ -54,9 +63,9 @@ class ContactListEvent( /** * Returns a list of a-tags that are verified as correct. */ - fun verifiedFollowAddressSet(): Set = tags.mapNotNullTo(HashSet(), ATag::parseValidAddress) + fun verifiedFollowAddressSet(): Set = tags.mapNotNullTo(HashSet(), AddressFollowTag::parseValidAddress) - fun unverifiedFollowKeySet() = tags.mapNotNull(PTag::parseKey) + fun unverifiedFollowKeySet() = tags.mapNotNull(ContactTag::parseKey) fun unverifiedFollowTagSet() = tags.hashtags() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/tags/AddressFollowTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/tags/AddressFollowTag.kt new file mode 100644 index 000000000..cfe10b3a0 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/tags/AddressFollowTag.kt @@ -0,0 +1,25 @@ +/** + * 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.nip02FollowList.tags + +import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag + +typealias AddressFollowTag = ATag diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt similarity index 80% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactTag.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt index d3c26d2ee..eab66cef3 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/ContactTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt @@ -18,12 +18,13 @@ * 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.nip02FollowList +package com.vitorpamplona.quartz.nip02FollowList.tags import android.util.Log import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.toHexKey +import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint import com.vitorpamplona.quartz.nip19Bech32.decodePublicKey import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.bytesUsedInMemory @@ -62,13 +63,13 @@ data class ContactTag( @JvmStatic fun parse(tag: Array): ContactTag? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null return ContactTag(tag[1], tag.getOrNull(2), tag.getOrNull(3)) } @JvmStatic fun parseValid(tag: Array): ContactTag? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null return try { ContactTag(decodePublicKey(tag[1]).toHexKey(), tag.getOrNull(2), tag.getOrNull(3)) } catch (e: Exception) { @@ -77,9 +78,15 @@ data class ContactTag( } } + @JvmStatic + fun parseKey(tag: Array): String? { + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null + return tag[1] + } + @JvmStatic fun parseValidKey(tag: Array): String? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null return try { decodePublicKey(tag[1]).toHexKey() } catch (e: Exception) { @@ -88,6 +95,12 @@ data class ContactTag( } } + @JvmStatic + fun parseAsHint(tag: Array): PubKeyHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return PubKeyHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( pubkey: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/OtsEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/OtsEvent.kt index f11057a24..bcd82b3fb 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/OtsEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/OtsEvent.kt @@ -26,10 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate -import com.vitorpamplona.quartz.nip01Core.tags.events.ETag -import com.vitorpamplona.quartz.nip01Core.tags.events.eTag -import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind +import com.vitorpamplona.quartz.nip03Timestamp.tags.TargetEventTag import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils import java.util.Base64 @@ -42,10 +41,13 @@ class OtsEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider { + override fun eventHints() = tags.mapNotNull(TargetEventTag::parseAsHint) + override fun isContentEncoded() = true - fun digestEventId() = tags.firstNotNullOfOrNull(ETag::parseId) + fun digestEventId() = tags.firstNotNullOfOrNull(TargetEventTag::parseId) fun otsByteArray(): ByteArray = decodeOtsState(content) @@ -82,7 +84,7 @@ class OtsEvent( initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, encodeOtsState(otsState), createdAt) { alt(ALT) - eTag(ETag(eventId)) + targetEvent(TargetEventTag(eventId)) initializer() } @@ -95,8 +97,8 @@ class OtsEvent( ) = eventTemplate(KIND, encodeOtsState(otsState), createdAt) { alt(ALT) - eTag(event.toETag()) - kind(event.event.kind) + targetEvent(event.toETag()) + targetKind(event.event.kind) initializer() } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/TagArrayBuilderExt.kt new file mode 100644 index 000000000..c42a2296a --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/TagArrayBuilderExt.kt @@ -0,0 +1,36 @@ +/** + * 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.nip03Timestamp + +import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip03Timestamp.tags.TargetEventKindTag +import com.vitorpamplona.quartz.nip03Timestamp.tags.TargetEventTag + +fun TagArrayBuilder.targetEvent(tag: TargetEventTag) = add(tag.toTagArray()) + +fun TagArrayBuilder.targetEvents(tag: List) = addAll(tag.map { it.toTagArray() }) + +fun TagArrayBuilder.targetKind(kind: Int) = add(TargetEventKindTag.assemble(kind)) + +fun TagArrayBuilder.targetKinds(kinds: List) = addAll(TargetEventKindTag.assemble(kinds)) + +fun TagArrayBuilder.targetKinds(kinds: Set) = addAll(TargetEventKindTag.assemble(kinds)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/tags/TargetEventKindTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/tags/TargetEventKindTag.kt new file mode 100644 index 000000000..6f2955a3d --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/tags/TargetEventKindTag.kt @@ -0,0 +1,25 @@ +/** + * 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.nip03Timestamp.tags + +import com.vitorpamplona.quartz.nip01Core.tags.kinds.KindTag + +typealias TargetEventKindTag = KindTag diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/tags/TargetEventTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/tags/TargetEventTag.kt new file mode 100644 index 000000000..10b3270b7 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip03Timestamp/tags/TargetEventTag.kt @@ -0,0 +1,25 @@ +/** + * 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.nip03Timestamp.tags + +import com.vitorpamplona.quartz.nip01Core.tags.events.ETag + +typealias TargetEventTag = ETag diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip09Deletions/DeletionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip09Deletions/DeletionEvent.kt index 1249a06e5..7e79e267a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip09Deletions/DeletionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip09Deletions/DeletionEvent.kt @@ -25,6 +25,8 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag @@ -48,7 +50,13 @@ class DeletionEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider, + AddressHintProvider { + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + fun deleteEvents() = taggedEvents() fun deleteEventIds() = taggedEventIds() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/BaseThreadedEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/BaseThreadedEvent.kt index 5123f0c07..72db040c0 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/BaseThreadedEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/BaseThreadedEvent.kt @@ -23,12 +23,18 @@ package com.vitorpamplona.quartz.nip10Notes import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider +import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithEventsOrAddresses import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithPeople import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag +import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent @@ -48,7 +54,16 @@ open class BaseThreadedEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, kind, tags, content, sig) { +) : Event(id, pubKey, createdAt, kind, tags, content, sig), + EventHintProvider, + AddressHintProvider, + PubKeyHintProvider { + override fun eventHints() = tags.mapNotNull(MarkedETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint) + + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + fun mentions() = taggedUsers() fun markedRoot() = tags.firstNotNullOfOrNull(MarkedETag::parseRoot) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/tags/MarkedETag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/tags/MarkedETag.kt index 1c3ca6065..dc150479c 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/tags/MarkedETag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip10Notes/tags/MarkedETag.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip10Notes.tags import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -180,6 +181,12 @@ data class MarkedETag( null } + @JvmStatic + fun parseAsHint(tag: Array): EventIdHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return EventIdHint(tag[1], tag[2]) + } + @JvmStatic fun parseRoot(tag: Array): MarkedETag? { if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/base/BaseDMGroupEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/base/BaseDMGroupEvent.kt index b12f689f3..975328dd7 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/base/BaseDMGroupEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/base/BaseDMGroupEvent.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip17Dm.base import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent import kotlinx.collections.immutable.toImmutableSet @@ -37,7 +38,10 @@ open class BaseDMGroupEvent( sig: HexKey, ) : WrappedEvent(id, pubKey, createdAt, kind, tags, content, sig), ChatroomKeyable, - NIP17Group { + NIP17Group, + PubKeyHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + /** Recipients intended to receive this conversation */ fun recipients() = tags.mapNotNull(PTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/ChatMessageEncryptedFileHeaderEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/ChatMessageEncryptedFileHeaderEvent.kt index 0d2e61623..13ebd81fe 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/ChatMessageEncryptedFileHeaderEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/ChatMessageEncryptedFileHeaderEvent.kt @@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.OriginalHashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.SizeTag import com.vitorpamplona.quartz.utils.TimeUtils @@ -57,7 +57,7 @@ class ChatMessageEncryptedFileHeaderEvent( fun mimeType() = tags.firstNotNullOfOrNull(FileTypeTag::parse) - fun hash() = tags.firstNotNullOfOrNull(HashTag::parse) + fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse) fun size() = tags.firstNotNullOfOrNull(SizeTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/TagArrayBuilderExt.kt index 36d4001f0..4ea99d060 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/TagArrayBuilderExt.kt @@ -35,7 +35,7 @@ import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.OriginalHashTag @@ -61,7 +61,7 @@ fun TagArrayBuilder.encryptionNonce(nonce: fun TagArrayBuilder.mimeType(mimeType: String) = add(FileTypeTag.assemble(mimeType)) -fun TagArrayBuilder.hash(hash: HexKey) = add(HashTag.assemble(hash)) +fun TagArrayBuilder.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash)) fun TagArrayBuilder.fileSize(size: Int) = add(SizeTag.assemble(size)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/tags/EncryptionNonce.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/tags/EncryptionNonce.kt index 6abc59ce9..b34eb627e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/tags/EncryptionNonce.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/tags/EncryptionNonce.kt @@ -38,6 +38,6 @@ class EncryptionNonce { } @JvmStatic - fun assemble(nonce: ByteArray) = arrayOf(EncryptionKey.TAG_NAME, nonce.toHexKey()) + fun assemble(nonce: ByteArray) = arrayOf(TAG_NAME, nonce.toHexKey()) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/GenericRepostEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/GenericRepostEvent.kt index b392485b2..581e66a5d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/GenericRepostEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/GenericRepostEvent.kt @@ -25,8 +25,12 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.events.eTag @@ -45,10 +49,29 @@ class GenericRepostEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { - fun boostedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) } +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) - fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) } + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + + fun boostedEvents() = tags.mapNotNull(ETag::parse) + + fun boostedATags() = tags.mapNotNull(ATag::parse) + + fun boostedAddresses() = tags.mapNotNull(ATag::parseAddress) + + fun originalAuthors() = tags.mapNotNull(PTag::parse) + + fun boostedEventIds() = tags.mapNotNull(ETag::parseId) + + fun boostedAddressIds() = tags.mapNotNull(ATag::parseAddressId) + + fun originalAuthorKeys() = tags.mapNotNull(PTag::parseKey) fun containedPost() = try { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/RepostEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/RepostEvent.kt index 97efab801..691ec35c6 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/RepostEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/RepostEvent.kt @@ -25,15 +25,17 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.events.eTag -import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag -import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils @@ -45,10 +47,29 @@ class RepostEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { - fun boostedPost() = taggedEvents() +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) - fun originalAuthor() = taggedUsers() + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + + fun boostedEvents() = tags.mapNotNull(ETag::parse) + + fun boostedATags() = tags.mapNotNull(ATag::parse) + + fun boostedAddresses() = tags.mapNotNull(ATag::parseAddress) + + fun originalAuthors() = tags.mapNotNull(PTag::parse) + + fun boostedEventIds() = tags.mapNotNull(ETag::parseId) + + fun boostedAddressIds() = tags.mapNotNull(ATag::parseAddressId) + + fun originalAuthorKeys() = tags.mapNotNull(PTag::parseKey) fun containedPost() = try { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/quotes/QTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/quotes/QTag.kt index f600ecf2e..52e6d1490 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/quotes/QTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip18Reposts/quotes/QTag.kt @@ -20,6 +20,9 @@ */ package com.vitorpamplona.quartz.nip18Reposts.quotes +import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint + interface QTag { fun toTagArray(): Array @@ -36,5 +39,17 @@ interface QTag { QAddressableTag.parse(tag) } } + + @JvmStatic + fun parseEventAsHint(tag: Array): EventIdHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return EventIdHint(tag[1], tag[2]) + } + + @JvmStatic + fun parseAddressAsHint(tag: Array): AddressHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null + return AddressHint(tag[1], tag[2]) + } } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/bech32/Bech32Util.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/bech32/Bech32Util.kt index 77654be97..093121ae5 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/bech32/Bech32Util.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/bech32/Bech32Util.kt @@ -51,6 +51,12 @@ object Bech32 { const val ALPHABET: String = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" const val ALPHABET_UPPERCASE: String = "QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L" + private const val GEN0 = 0x3b6a57b2 + private const val GEN1 = 0x26508e6d + private const val GEN2 = 0x1ea119fa + private const val GEN3 = 0x3d4233dd + private const val GEN4 = 0x2a1462b3 + enum class Encoding( val constant: Int, ) { @@ -74,17 +80,17 @@ object Bech32 { fun expand(hrp: String): Array { val half = hrp.length + 1 val size = half + hrp.length + val firstPart = hrp.indices + val secondPart = half until size return Array(size) { when (it) { - in hrp.indices -> hrp[it].code.shr(5).toByte() - in half until size -> (hrp[it - half].code and 31).toByte() + in firstPart -> hrp[it].code.shr(5).toByte() + in secondPart -> (hrp[it - half].code and 31).toByte() else -> 0 } } } - private val GEN = arrayOf(0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3) - fun polymod( values: Array, values1: Array, @@ -93,16 +99,20 @@ object Bech32 { values.forEach { v -> val b = chk shr 25 chk = ((chk and 0x1ffffff) shl 5) xor v.toInt() - for (i in 0..4) { - if (((b shr i) and 1) != 0) chk = chk xor GEN[i] - } + if (((b shr 0) and 1) != 0) chk = chk xor GEN0 + if (((b shr 1) and 1) != 0) chk = chk xor GEN1 + if (((b shr 2) and 1) != 0) chk = chk xor GEN2 + if (((b shr 3) and 1) != 0) chk = chk xor GEN3 + if (((b shr 4) and 1) != 0) chk = chk xor GEN4 } values1.forEach { v -> val b = chk shr 25 chk = ((chk and 0x1ffffff) shl 5) xor v.toInt() - for (i in 0..4) { - if (((b shr i) and 1) != 0) chk = chk xor GEN[i] - } + if (((b shr 0) and 1) != 0) chk = chk xor GEN0 + if (((b shr 1) and 1) != 0) chk = chk xor GEN1 + if (((b shr 2) and 1) != 0) chk = chk xor GEN2 + if (((b shr 3) and 1) != 0) chk = chk xor GEN3 + if (((b shr 4) and 1) != 0) chk = chk xor GEN4 } return chk } @@ -128,8 +138,7 @@ object Bech32 { else -> addChecksum(hrp, int5s, encoding) } - val charArray = - CharArray(dataWithChecksum.size) { ALPHABET[dataWithChecksum[it].toInt()] }.concatToString() + val charArray = CharArray(dataWithChecksum.size) { ALPHABET[dataWithChecksum[it].toInt()] }.concatToString() return hrp + "1" + charArray } @@ -231,8 +240,8 @@ object Bech32 { @JvmStatic public fun eight2five(input: ByteArray): ArrayList { var buffer = 0L - val output = - ArrayList(input.size * 2) // larger array on purpose. Checksum is added later. + // larger array on purpose. Checksum is added later. + val output = ArrayList(input.size * 2) var count = 0 input.forEach { b -> buffer = (buffer shl 8) or (b.toLong() and 0xff) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/CommentEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/CommentEvent.kt index c65fd1a34..83e94ac49 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/CommentEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/CommentEvent.kt @@ -25,7 +25,10 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri @@ -53,7 +56,16 @@ class CommentEvent( content: String, sig: HexKey, ) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), - RootScope { + RootScope, + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(RootAuthorTag::parseAsHint) + tags.mapNotNull(ReplyAuthorTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(RootEventTag::parseAsHint) + tags.mapNotNull(ReplyEventTag::parseAsHint) + + override fun addressHints() = tags.mapNotNull(RootAddressTag::parseAsHint) + tags.mapNotNull(ReplyAddressTag::parseAsHint) + fun rootAuthor() = tags.firstNotNullOfOrNull(RootAuthorTag::parse) fun replyAuthor() = tags.firstNotNullOfOrNull(ReplyAuthorTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAddressTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAddressTag.kt index 7550a5e88..9e930bbf3 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAddressTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAddressTag.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.match import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches +import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -51,6 +52,12 @@ class ReplyAddressTag( @JvmStatic fun parseAddress(tag: Array) = tag.valueIfMatches(TAG_NAME, TAG_SIZE) + @JvmStatic + fun parseAsHint(tag: Array): AddressHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null + return AddressHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( addressId: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAuthorTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAuthorTag.kt index 48b6b1ade..1f647c2d5 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAuthorTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyAuthorTag.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.match +import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -55,6 +56,12 @@ data class ReplyAuthorTag( return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): PubKeyHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return PubKeyHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( pubkey: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyEventTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyEventTag.kt index ac4c9da01..77f68fbc4 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyEventTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/ReplyEventTag.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.match import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -71,6 +72,12 @@ class ReplyEventTag( return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): EventIdHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return EventIdHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( eventId: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAddressTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAddressTag.kt index c46dcf865..32d643151 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAddressTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAddressTag.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.match import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches +import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -69,6 +70,12 @@ class RootAddressTag( @JvmStatic fun parseAddress(tag: Array) = tag.valueIfMatches(TAG_NAME, TAG_SIZE) + @JvmStatic + fun parseAsHint(tag: Array): AddressHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null + return AddressHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( addressId: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAuthorTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAuthorTag.kt index a24430433..dcae0d9f9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAuthorTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootAuthorTag.kt @@ -22,10 +22,13 @@ package com.vitorpamplona.quartz.nip22Comments.tags import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.PUBKEY_LENGTH import com.vitorpamplona.quartz.nip01Core.core.Tag -import com.vitorpamplona.quartz.nip01Core.core.match +import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag import com.vitorpamplona.quartz.utils.arrayOfNotNull +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract @Immutable data class RootAuthorTag( @@ -36,25 +39,45 @@ data class RootAuthorTag( companion object { const val TAG_NAME = "P" - const val TAG_SIZE = 2 @JvmStatic - fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE) + fun match(tag: Tag) = tag.size > 1 && tag[0] == TAG_NAME @JvmStatic fun parse(tag: Tag): ReplyAuthorTag? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null - if (tag[1].length != 64) return null + if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH) return null + return ReplyAuthorTag(tag[1], tag.getOrNull(2)) + } + + @JvmStatic + fun parse3(tag: Tag): ReplyAuthorTag? { + ensure(tag.size >= 2) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == PUBKEY_LENGTH) { return null } return ReplyAuthorTag(tag[1], tag.getOrNull(2)) } @JvmStatic fun parseKey(tag: Tag): HexKey? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null - if (tag[1].length != 64) return null + if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH) return null return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): PubKeyHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH || tag[2].isEmpty()) return null + return PubKeyHint(tag[1], tag[2]) + } + + @OptIn(ExperimentalContracts::class) + inline fun ensure( + condition: Boolean, + exit: () -> Nothing, + ) { + contract { returns() implies condition } + if (!condition) exit() + } + @JvmStatic fun assemble( pubkey: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootEventTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootEventTag.kt index b15c2401e..c0d546f92 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootEventTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip22Comments/tags/RootEventTag.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.match import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference import com.vitorpamplona.quartz.utils.arrayOfNotNull @@ -71,6 +72,12 @@ class RootEventTag( return tag[1] } + @JvmStatic + fun parseAsHint(tag: Array): EventIdHint? { + if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null + return EventIdHint(tag[1], tag[2]) + } + @JvmStatic fun assemble( eventId: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt index f5195f464..2c3d47f9a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt @@ -24,12 +24,17 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent +import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag @@ -47,7 +52,16 @@ class LongTextNoteEvent( content: String, sig: HexKey, ) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), - AddressableEvent { + AddressableEvent, + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(QTag::parseEventAsHint) + + override fun addressHints() = tags.mapNotNull(QTag::parseAddressAsHint) + override fun dTag() = tags.dTag() override fun aTag(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip25Reactions/ReactionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip25Reactions/ReactionEvent.kt index 9f5920354..6abac5717 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip25Reactions/ReactionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip25Reactions/ReactionEvent.kt @@ -24,8 +24,12 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.events.eTag @@ -44,7 +48,16 @@ class ReactionEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + fun originalPost() = tags.mapNotNull(ETag::parseId) fun originalAuthor() = tags.mapNotNull(PTag::parseKey) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelCreateEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelCreateEvent.kt index 1b1e015e0..fc7f93afd 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelCreateEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelCreateEvent.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip28PublicChat.base.ChannelData import com.vitorpamplona.quartz.nip31Alts.alt @@ -37,7 +39,10 @@ class ChannelCreateEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider { + override fun eventHints() = channelInfo().relays?.map { EventIdHint(id, it) } ?: emptyList() + fun channelInfo() = ChannelData.parse(content) ?: ChannelData() companion object { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelHideMessageEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelHideMessageEvent.kt index 612c2625a..1c4c3e0d8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelHideMessageEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelHideMessageEvent.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.events.eTags @@ -41,7 +42,10 @@ class ChannelHideMessageEvent( tags: Array>, content: String, sig: HexKey, -) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider { + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + fun eventsToHide() = tags.taggedEventIds() companion object { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMetadataEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMetadataEvent.kt index 2caac83ed..bc98fc090 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMetadataEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMetadataEvent.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip28PublicChat.base.BasePublicChatEvent @@ -40,7 +42,10 @@ class ChannelMetadataEvent( tags: Array>, content: String, sig: HexKey, -) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider { + override fun eventHints() = channelInfo().relays?.map { EventIdHint(id, it) } ?: emptyList() + fun channelInfo() = ChannelData.parse(content) ?: ChannelData() companion object { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMuteUserEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMuteUserEvent.kt index 101c848ad..2211dbd17 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMuteUserEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip28PublicChat/admin/ChannelMuteUserEvent.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTags @@ -41,7 +42,10 @@ class ChannelMuteUserEvent( tags: Array>, content: String, sig: HexKey, -) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig), + PubKeyHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + fun usersToMute() = tags.taggedUserIds() companion object { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip30CustomEmoji/selection/EmojiPackSelectionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip30CustomEmoji/selection/EmojiPackSelectionEvent.kt index 80cbb9139..10c565b70 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip30CustomEmoji/selection/EmojiPackSelectionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip30CustomEmoji/selection/EmojiPackSelectionEvent.kt @@ -24,9 +24,11 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate +import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent @@ -41,7 +43,10 @@ class EmojiPackSelectionEvent( tags: Array>, content: String, sig: HexKey, -) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig), + AddressHintProvider { + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + fun emojiPacks() = taggedATags() companion object { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/issue/GitIssueEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/issue/GitIssueEvent.kt index bf59e8710..063aafcf2 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/issue/GitIssueEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/issue/GitIssueEvent.kt @@ -23,13 +23,17 @@ package com.vitorpamplona.quartz.nip34Git.issue import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip14Subject.SubjectTag +import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent import com.vitorpamplona.quartz.utils.TimeUtils @@ -42,7 +46,16 @@ class GitIssueEvent( tags: Array>, content: String, sig: HexKey, -) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), + PubKeyHintProvider, + EventHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(QTag::parseEventAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint) + fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId) fun repositoryAddress() = tags.firstNotNullOfOrNull(ATag::parseAddress) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/patch/GitPatchEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/patch/GitPatchEvent.kt index d0a91b5b2..b9366cbd5 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/patch/GitPatchEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/patch/GitPatchEvent.kt @@ -23,9 +23,14 @@ package com.vitorpamplona.quartz.nip34Git.patch import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag +import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.utils.TimeUtils @@ -37,7 +42,16 @@ class GitPatchEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + PubKeyHintProvider, + EventHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(MarkedETag::parseAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + private fun innerRepository() = tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "root" } ?: tags.firstOrNull { it.size > 1 && it[0] == "a" } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/reply/GitReplyEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/reply/GitReplyEvent.kt index f7dd71412..430b4a597 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/reply/GitReplyEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip34Git/reply/GitReplyEvent.kt @@ -23,13 +23,18 @@ package com.vitorpamplona.quartz.nip34Git.reply import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag import com.vitorpamplona.quartz.nip10Notes.tags.markedETags import com.vitorpamplona.quartz.nip10Notes.tags.prepareMarkedETagsAsReplyTo +import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent import com.vitorpamplona.quartz.utils.TimeUtils @@ -43,7 +48,16 @@ class GitReplyEvent( tags: Array>, content: String, sig: HexKey, -) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), + PubKeyHintProvider, + EventHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(MarkedETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint) + fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId) fun repository() = tags.firstNotNullOfOrNull(ATag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip35Torrents/TorrentCommentEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip35Torrents/TorrentCommentEvent.kt index 9389eab68..a5cabb134 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip35Torrents/TorrentCommentEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip35Torrents/TorrentCommentEvent.kt @@ -23,15 +23,20 @@ package com.vitorpamplona.quartz.nip35Torrents import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.events.eTags import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag import com.vitorpamplona.quartz.nip10Notes.tags.positionalMarkedTags +import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils @@ -43,7 +48,16 @@ class TorrentCommentEvent( tags: Array>, content: String, sig: HexKey, -) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint) + + override fun addressHints() = tags.mapNotNull(QTag::parseAddressAsHint) + fun torrent() = tags.firstNotNullOfOrNull(MarkedETag::parseRoot) ?: tags.firstNotNullOfOrNull(ETag::parse) fun torrentIds() = tags.firstNotNullOfOrNull(MarkedETag::parseRootId) ?: tags.firstNotNullOfOrNull(ETag::parseId) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/GeneralListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/GeneralListEvent.kt index 1f1f16f51..aa3827a90 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/GeneralListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/GeneralListEvent.kt @@ -23,14 +23,19 @@ package com.vitorpamplona.quartz.nip51Lists import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.isTagged +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address -import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents +import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes -import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds -import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers +import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag +import com.vitorpamplona.quartz.nip51Lists.tags.NameTag +import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.toImmutableSet @@ -43,20 +48,22 @@ abstract class GeneralListEvent( tags: Array>, content: String, sig: HexKey, -) : PrivateTagArrayEvent(id, pubKey, createdAt, kind, tags, content, sig) { - fun category() = dTag() +) : PrivateTagArrayEvent(id, pubKey, createdAt, kind, tags, content, sig), + EventHintProvider, + AddressHintProvider, + PubKeyHintProvider { + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + (cachedPrivateTags()?.mapNotNull(ETag::parseAsHint) ?: emptyList()) - fun bookmarkedPosts() = taggedEvents() + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + (cachedPrivateTags()?.mapNotNull(ATag::parseAsHint) ?: emptyList()) - fun bookmarkedPeople() = taggedUsers() + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + (cachedPrivateTags()?.mapNotNull(PTag::parseAsHint) ?: emptyList()) - fun bookmarkedPeopleIds() = taggedUserIds() + fun name() = tags.firstNotNullOfOrNull(NameTag::parse) - fun name() = tags.firstOrNull { it.size > 1 && it[0] == "name" }?.get(1) + @Deprecated("NIP-51 has deprecated Title. Use name instead", ReplaceWith("name()")) + fun title() = tags.firstNotNullOfOrNull(TitleTag::parse) - fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1) - - fun nameOrTitle() = name()?.ifBlank { null } ?: title()?.ifBlank { null } + fun nameOrTitle() = name() ?: title() fun filterTagList( key: String, @@ -124,13 +131,13 @@ abstract class GeneralListEvent( onReady: (List
) -> Unit, ) = privateTags(signer) { onReady(filterAddresses(it)) } - fun filterUsers(tags: Array>): List = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] } + fun filterUsers(tags: Array>): List = tags.mapNotNull(PTag::parseKey) - fun filterHashtags(tags: Array>): List = tags.filter { it.size > 1 && it[0] == "t" }.map { it[1] } + fun filterHashtags(tags: Array>): List = tags.mapNotNull(HashtagTag::parse) fun filterGeohashes(tags: Array>): List = tags.geohashes() - fun filterEvents(tags: Array>): List = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] } + fun filterEvents(tags: Array>): List = tags.mapNotNull(ETag::parseId) fun filterATags(tags: Array>): List = tags.mapNotNull(ATag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/MuteListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/MuteListEvent.kt index 1148fb1b8..844026e78 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/MuteListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/MuteListEvent.kt @@ -26,9 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.utils.TimeUtils -import com.vitorpamplona.quartz.utils.bytesUsedInMemory -import com.vitorpamplona.quartz.utils.pointerSizeInBytes -import kotlinx.collections.immutable.ImmutableSet @Immutable class MuteListEvent( @@ -39,39 +36,16 @@ class MuteListEvent( content: String, sig: HexKey, ) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - @Transient var publicAndPrivateUserCache: ImmutableSet? = null - - @Transient var publicAndPrivateWordCache: ImmutableSet? = null - - override fun countMemory(): Long = - super.countMemory() + - pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) + - pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) - override fun dTag() = FIXED_D_TAG fun publicAndPrivateUsersAndWords( signer: NostrSigner, onReady: (PeopleListEvent.UsersAndWords) -> Unit, ) { - publicAndPrivateUserCache?.let { userList -> - publicAndPrivateWordCache?.let { wordList -> - onReady(PeopleListEvent.UsersAndWords(userList, wordList)) - return - } - } - privateTagsOrEmpty(signer) { - publicAndPrivateUserCache = filterTagList("p", it) - publicAndPrivateWordCache = filterTagList("word", it) - - publicAndPrivateUserCache?.let { userList -> - publicAndPrivateWordCache?.let { wordList -> - onReady( - PeopleListEvent.UsersAndWords(userList, wordList), - ) - } - } + onReady( + PeopleListEvent.UsersAndWords(filterTagList("p", it), filterTagList("word", it)), + ) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PeopleListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PeopleListEvent.kt index 1fa5058e5..6cd6ad7e9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PeopleListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PeopleListEvent.kt @@ -26,9 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.utils.TimeUtils -import com.vitorpamplona.quartz.utils.bytesUsedInMemory -import com.vitorpamplona.quartz.utils.pointerSizeInBytes -import kotlinx.collections.immutable.ImmutableSet @Immutable class PeopleListEvent( @@ -39,45 +36,6 @@ class PeopleListEvent( content: String, sig: HexKey, ) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - @Transient var publicAndPrivateUserCache: ImmutableSet? = null - - @Transient var publicAndPrivateWordCache: ImmutableSet? = null - - override fun countMemory(): Long = - super.countMemory() + - pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) + - pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) - - fun publicAndPrivateWords( - signer: NostrSigner, - onReady: (ImmutableSet) -> Unit, - ) { - publicAndPrivateWordCache?.let { - onReady(it) - return - } - - privateTagsOrEmpty(signer) { - publicAndPrivateWordCache = filterTagList("word", it) - publicAndPrivateWordCache?.let { onReady(it) } - } - } - - fun publicAndPrivateUsers( - signer: NostrSigner, - onReady: (ImmutableSet) -> Unit, - ) { - publicAndPrivateUserCache?.let { - onReady(it) - return - } - - privateTagsOrEmpty(signer) { - publicAndPrivateUserCache = filterTagList("p", it) - publicAndPrivateUserCache?.let { onReady(it) } - } - } - @Immutable class UsersAndWords( val users: Set = setOf(), @@ -88,24 +46,13 @@ class PeopleListEvent( signer: NostrSigner, onReady: (UsersAndWords) -> Unit, ) { - publicAndPrivateUserCache?.let { userList -> - publicAndPrivateWordCache?.let { wordList -> - onReady(UsersAndWords(userList, wordList)) - return - } - } - privateTagsOrEmpty(signer) { - publicAndPrivateUserCache = filterTagList("p", it) - publicAndPrivateWordCache = filterTagList("word", it) - - publicAndPrivateUserCache?.let { userList -> - publicAndPrivateWordCache?.let { wordList -> - onReady( - UsersAndWords(userList, wordList), - ) - } - } + onReady( + UsersAndWords( + filterTagList("p", it), + filterTagList("word", it), + ), + ) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PrivateTagArrayEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PrivateTagArrayEvent.kt index cf8ea9147..47ce039fa 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PrivateTagArrayEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/PrivateTagArrayEvent.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner +import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.pointerSizeInBytes @@ -63,7 +64,7 @@ abstract class PrivateTagArrayEvent( } try { - Nip51PrivateTags.decrypt(content, signer) { + PrivateTagsInContent.decrypt(content, signer) { privateTagsCache = it privateTagsCache?.let { onReady(it) } } @@ -78,7 +79,7 @@ abstract class PrivateTagArrayEvent( onReady: (content: String) -> Unit, ) { privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = change(privateTags), signer = signer, ) { encryptedTags -> @@ -97,7 +98,7 @@ abstract class PrivateTagArrayEvent( ) { if (toPrivate) { current.privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = privateTags.plus(newTag), signer = signer, ) { encryptedTags -> @@ -118,7 +119,7 @@ abstract class PrivateTagArrayEvent( ) { if (toPrivate) { current.privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = privateTags.plus(newTag), signer = signer, ) { encryptedTags -> @@ -168,7 +169,7 @@ abstract class PrivateTagArrayEvent( onReady: (content: String, tags: Array>) -> Unit, ) { current.privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = privateTags.replaceAll(oldTagStartsWith, newTag), signer = signer, ) { encryptedTags -> @@ -185,7 +186,7 @@ abstract class PrivateTagArrayEvent( onReady: (content: String, tags: Array>) -> Unit, ) { current.privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = privateTags.remove(oldTagStartsWith), signer = signer, ) { encryptedTags -> @@ -201,7 +202,7 @@ abstract class PrivateTagArrayEvent( onReady: (content: String, tags: Array>) -> Unit, ) { current.privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = privateTags.remove(oldTagStartsWith), signer = signer, ) { encryptedTags -> @@ -224,7 +225,7 @@ abstract class PrivateTagArrayEvent( onReady: (content: String, tags: Array>) -> Unit, ) { current.privateTags(signer) { privateTags -> - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = privateTags.remove(oldTagStartsWith), signer = signer, ) { encryptedTags -> @@ -239,7 +240,7 @@ abstract class PrivateTagArrayEvent( signer: NostrSigner, onReady: (content: String, tags: Array>) -> Unit, ) { - Nip51PrivateTags.encryptNip04( + PrivateTagsInContent.encryptNip04( privateTags = arrayOf(newTag), signer = signer, ) { encryptedTags -> diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/Nip51PrivateTags.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/encryption/PrivateTagsInContent.kt similarity index 96% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/Nip51PrivateTags.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/encryption/PrivateTagsInContent.kt index 78a72d1f8..4e91e5e7a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/Nip51PrivateTags.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/encryption/PrivateTagsInContent.kt @@ -18,13 +18,13 @@ * 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.nip51Lists +package com.vitorpamplona.quartz.nip51Lists.encryption import com.fasterxml.jackson.module.kotlin.readValue import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner -class Nip51PrivateTags { +class PrivateTagsInContent { companion object { fun decode(content: String) = EventMapper.mapper.readValue>>(content) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/tags/NameTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/tags/NameTag.kt new file mode 100644 index 000000000..6935c0782 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/tags/NameTag.kt @@ -0,0 +1,36 @@ +/** + * 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.nip51Lists.tags + +class NameTag { + companion object { + const val TAG_NAME = "name" + + @JvmStatic + fun parse(tag: Array): String? { + if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].isEmpty()) return null + return tag[1] + } + + @JvmStatic + fun assemble(name: String) = arrayOf(TAG_NAME, name) + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/tags/TitleTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/tags/TitleTag.kt new file mode 100644 index 000000000..9b46e5ef5 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip51Lists/tags/TitleTag.kt @@ -0,0 +1,37 @@ +/** + * 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.nip51Lists.tags + +@Deprecated("Use NameTag Instead") +class TitleTag { + companion object { + const val TAG_NAME = "title" + + @JvmStatic + fun parse(tag: Array): String? { + if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].isEmpty()) return null + return tag[1] + } + + @JvmStatic + fun assemble(name: String) = arrayOf(TAG_NAME, name) + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip53LiveActivities/chat/LiveActivitiesChatMessageEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip53LiveActivities/chat/LiveActivitiesChatMessageEvent.kt index 8eba6cb10..65f82c8c3 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip53LiveActivities/chat/LiveActivitiesChatMessageEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip53LiveActivities/chat/LiveActivitiesChatMessageEvent.kt @@ -23,10 +23,16 @@ package com.vitorpamplona.quartz.nip53LiveActivities.chat import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider +import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag +import com.vitorpamplona.quartz.nip01Core.tags.events.ETag +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent +import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.utils.TimeUtils @@ -38,7 +44,16 @@ class LiveActivitiesChatMessageEvent( tags: Array>, content: String, sig: HexKey, -) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider, + PubKeyHintProvider, + AddressHintProvider { + override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + + override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint) + + override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint) + private fun activityHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId) fun activity() = tags.firstNotNullOfOrNull(ATag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagBuilderExt.kt index 1ab4d044c..620754dd7 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagBuilderExt.kt @@ -28,7 +28,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -48,7 +48,7 @@ fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime) fun IMetaTagBuilder.alt(alt: String) = add(AltTag.TAG_NAME, alt) -fun IMetaTagBuilder.hash(hash: HexKey) = add(HashTag.TAG_NAME, hash) +fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash) fun IMetaTagBuilder.size(size: Int) = add(SizeTag.TAG_NAME, size.toString()) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagExt.kt index a4a48131d..30fe0ee2d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/IMetaTagExt.kt @@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -47,7 +47,7 @@ fun IMetaTag.mimeType() = properties.get(MimeTypeTag.TAG_NAME) fun IMetaTag.alt() = properties.get(AltTag.TAG_NAME) -fun IMetaTag.hash() = properties.get(HashTag.TAG_NAME) +fun IMetaTag.hash() = properties.get(HashSha256Tag.TAG_NAME) fun IMetaTag.size() = properties.get(SizeTag.TAG_NAME) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/PictureEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/PictureEvent.kt index bfa141b93..d14368859 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/PictureEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/PictureEvent.kt @@ -33,7 +33,7 @@ import com.vitorpamplona.quartz.nip92IMeta.imetas import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -68,7 +68,7 @@ class PictureEvent( private fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse) - private fun hash() = tags.firstNotNullOfOrNull(HashTag::parse) + private fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse) private fun size() = tags.firstNotNullOfOrNull(SizeTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/TagArrayBuilderExt.kt index 4458d1649..4c7c8cb5e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip68Picture/TagArrayBuilderExt.kt @@ -24,7 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag fun TagArrayBuilder.title(title: String) = addUnique(TitleTag.assemble(title)) @@ -43,7 +43,7 @@ fun TagArrayBuilder.pictureIMeta( fun TagArrayBuilder.pictureIMeta(imeta: PictureMeta): TagArrayBuilder { add(imeta.toIMetaArray()) - imeta.hash?.let { add(HashTag.assemble(it)) } + imeta.hash?.let { add(HashSha256Tag.assemble(it)) } imeta.mimeType?.let { add(MimeTypeTag.assemble(it)) } return this } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagBuilderExt.kt index f8bbe904b..c902f1f58 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagBuilderExt.kt @@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -47,7 +47,7 @@ fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime) fun IMetaTagBuilder.alt(alt: String) = add(AltTag.TAG_NAME, alt) -fun IMetaTagBuilder.hash(hash: HexKey) = add(HashTag.TAG_NAME, hash) +fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash) fun IMetaTagBuilder.size(size: Int) = add(SizeTag.TAG_NAME, size.toString()) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagExt.kt index 923d79029..aa17ec54a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/IMetaTagExt.kt @@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -46,7 +46,7 @@ fun IMetaTag.mimeType() = properties.get(MimeTypeTag.TAG_NAME) fun IMetaTag.alt() = properties.get(AltTag.TAG_NAME) -fun IMetaTag.hash() = properties.get(HashTag.TAG_NAME) +fun IMetaTag.hash() = properties.get(HashSha256Tag.TAG_NAME) fun IMetaTag.size() = properties.get(SizeTag.TAG_NAME) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/VideoEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/VideoEvent.kt index b2790e9b9..6b7588e5d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/VideoEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip71Video/VideoEvent.kt @@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip92IMeta.imetas import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -65,7 +65,7 @@ abstract class VideoEvent( private fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse) - private fun hash() = tags.firstNotNullOfOrNull(HashTag::parse) + private fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse) private fun size() = tags.firstNotNullOfOrNull(SizeTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/FileHeaderEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/FileHeaderEvent.kt index 32f1b2cad..d7abfef1a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/FileHeaderEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/FileHeaderEvent.kt @@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -57,7 +57,7 @@ class FileHeaderEvent( fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse) - fun hash() = tags.firstNotNullOfOrNull(HashTag::parse) + fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse) fun size() = tags.firstNotNullOfOrNull(SizeTag::parse) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/IMetaTagBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/IMetaTagBuilderExt.kt index 6cf3ca491..6e9d4d3ab 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/IMetaTagBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/IMetaTagBuilderExt.kt @@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -47,7 +47,7 @@ fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime) fun IMetaTagBuilder.alt(alt: String) = add(AltTag.TAG_NAME, alt) -fun IMetaTagBuilder.hash(hash: HexKey) = add(HashTag.TAG_NAME, hash) +fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash) fun IMetaTagBuilder.size(size: Int) = add(SizeTag.TAG_NAME, size.toString()) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/TagArrayBuilderExt.kt index 3a5c207fd..98f29ae70 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/TagArrayBuilderExt.kt @@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag -import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag +import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag @@ -41,7 +41,7 @@ fun TagArrayBuilder.url(url: String) = add(UrlTag.assemble(url) fun TagArrayBuilder.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType)) -fun TagArrayBuilder.hash(hash: HexKey) = add(HashTag.assemble(hash)) +fun TagArrayBuilder.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash)) fun TagArrayBuilder.fileSize(size: Int) = add(SizeTag.assemble(size)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/tags/HashTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/tags/HashSha256Tag.kt similarity index 92% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/tags/HashTag.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/tags/HashSha256Tag.kt index 04f7d4a9a..9880e9162 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/tags/HashTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip94FileMetadata/tags/HashSha256Tag.kt @@ -20,14 +20,14 @@ */ package com.vitorpamplona.quartz.nip94FileMetadata.tags -class HashTag { +class HashSha256Tag { companion object { const val TAG_NAME = "x" const val TAG_SIZE = 2 @JvmStatic fun parse(tag: Array): String? { - if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null + if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null return tag[1] } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Ensure.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Ensure.kt new file mode 100644 index 000000000..c1ec0bce2 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Ensure.kt @@ -0,0 +1,35 @@ +/** + * 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.utils + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +@OptIn(ExperimentalContracts::class) +inline fun ensure( + condition: Boolean, + exit: () -> Nothing, +) { + contract { + returns() implies condition + } + if (!condition) exit() +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hex.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hex.kt index 9aeb6aa41..e1ae0e500 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hex.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hex.kt @@ -21,24 +21,25 @@ package com.vitorpamplona.quartz.utils object Hex { - private val lowerCaseHex = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') - private val upperCaseHex = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') + private const val LOWER_CASE_HEX = "0123456789abcdef" + private const val UPPER_CASE_HEX = "0123456789ABCDEF" private val hexToByte: IntArray = IntArray(256) { -1 }.apply { - lowerCaseHex.forEachIndexed { index, char -> this[char.code] = index } - upperCaseHex.forEachIndexed { index, char -> this[char.code] = index } + LOWER_CASE_HEX.forEachIndexed { index, char -> this[char.code] = index } + UPPER_CASE_HEX.forEachIndexed { index, char -> this[char.code] = index } } // Encodes both chars in a single Int variable private val byteToHex = IntArray(256) { - (lowerCaseHex[(it shr 4)].code shl 8) or lowerCaseHex[(it and 0xF)].code + (LOWER_CASE_HEX[(it shr 4)].code shl 8) or LOWER_CASE_HEX[(it and 0xF)].code } @JvmStatic fun isHex(hex: String?): Boolean { - if (hex.isNullOrEmpty() || hex.length and 1 != 0) return false + if (hex.isNullOrEmpty()) return false + if (hex.length and 1 != 0) return false try { for (c in hex.indices) {