Merge branch 'main' into main
This commit is contained in:
@@ -208,7 +208,7 @@ class Account(
|
|||||||
val saveable: AccountLiveData = AccountLiveData(this)
|
val saveable: AccountLiveData = AccountLiveData(this)
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class LiveFollowLists(
|
class LiveFollowLists(
|
||||||
val users: ImmutableSet<String> = persistentSetOf(),
|
val users: ImmutableSet<String> = persistentSetOf(),
|
||||||
val hashtags: ImmutableSet<String> = persistentSetOf(),
|
val hashtags: ImmutableSet<String> = persistentSetOf(),
|
||||||
val geotags: ImmutableSet<String> = persistentSetOf(),
|
val geotags: ImmutableSet<String> = persistentSetOf(),
|
||||||
|
|||||||
@@ -108,10 +108,9 @@ class LiveActivitiesChannel(val address: ATag) : Channel(address.toTag()) {
|
|||||||
@Stable
|
@Stable
|
||||||
abstract class Channel(val idHex: String) {
|
abstract class Channel(val idHex: String) {
|
||||||
var creator: User? = null
|
var creator: User? = null
|
||||||
|
|
||||||
var updatedMetadataAt: Long = 0
|
var updatedMetadataAt: Long = 0
|
||||||
|
|
||||||
val notes = LargeCache<HexKey, Note>()
|
val notes = LargeCache<HexKey, Note>()
|
||||||
|
var lastNoteCreatedAt: Long = 0
|
||||||
|
|
||||||
open fun id() = Hex.decode(idHex)
|
open fun id() = Hex.decode(idHex)
|
||||||
|
|
||||||
@@ -147,6 +146,10 @@ abstract class Channel(val idHex: String) {
|
|||||||
|
|
||||||
fun addNote(note: Note) {
|
fun addNote(note: Note) {
|
||||||
notes.put(note.idHex, note)
|
notes.put(note.idHex, note)
|
||||||
|
|
||||||
|
if ((note.createdAt() ?: 0) > lastNoteCreatedAt) {
|
||||||
|
lastNoteCreatedAt = note.createdAt() ?: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeNote(note: Note) {
|
fun removeNote(note: Note) {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ object LocalCache {
|
|||||||
val notes = LargeCache<HexKey, Note>()
|
val notes = LargeCache<HexKey, Note>()
|
||||||
val addressables = LargeCache<String, AddressableNote>()
|
val addressables = LargeCache<String, AddressableNote>()
|
||||||
val drafts = ConcurrentHashMap<String, MutableList<Drafts>>()
|
val drafts = ConcurrentHashMap<String, MutableList<Drafts>>()
|
||||||
val channels = ConcurrentHashMap<HexKey, Channel>()
|
val channels = LargeCache<HexKey, Channel>()
|
||||||
val awaitingPaymentRequests = ConcurrentHashMap<HexKey, Pair<Note?, (LnZapPaymentResponseEvent) -> Unit>>(10)
|
val awaitingPaymentRequests = ConcurrentHashMap<HexKey, Pair<Note?, (LnZapPaymentResponseEvent) -> Unit>>(10)
|
||||||
|
|
||||||
fun checkGetOrCreateUser(key: String): User? {
|
fun checkGetOrCreateUser(key: String): User? {
|
||||||
@@ -193,7 +193,7 @@ object LocalCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getChannelIfExists(key: String): Channel? {
|
fun getChannelIfExists(key: String): Channel? {
|
||||||
return channels[key]
|
return channels.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkGetOrCreateNote(key: String): Note? {
|
fun checkGetOrCreateNote(key: String): Note? {
|
||||||
@@ -246,15 +246,24 @@ object LocalCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getOrCreateChannel(
|
||||||
|
key: String,
|
||||||
|
channelFactory: (String) -> Channel,
|
||||||
|
): Channel {
|
||||||
|
checkNotInMainThread()
|
||||||
|
|
||||||
|
return channels.getOrCreate(key, channelFactory)
|
||||||
|
}
|
||||||
|
|
||||||
fun checkGetOrCreateChannel(key: String): Channel? {
|
fun checkGetOrCreateChannel(key: String): Channel? {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
if (isValidHex(key)) {
|
if (isValidHex(key)) {
|
||||||
return getOrCreateChannel(key) { PublicChatChannel(key) }
|
return channels.getOrCreate(key) { PublicChatChannel(key) }
|
||||||
}
|
}
|
||||||
val aTag = ATag.parse(key, null)
|
val aTag = ATag.parse(key, null)
|
||||||
if (aTag != null) {
|
if (aTag != null) {
|
||||||
return getOrCreateChannel(aTag.toTag()) { LiveActivitiesChannel(aTag) }
|
return channels.getOrCreate(aTag.toTag()) { LiveActivitiesChannel(aTag) }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -266,19 +275,6 @@ object LocalCache {
|
|||||||
return HexValidator.isHex(key)
|
return HexValidator.isHex(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOrCreateChannel(
|
|
||||||
key: String,
|
|
||||||
channelFactory: (String) -> Channel,
|
|
||||||
): Channel {
|
|
||||||
checkNotInMainThread()
|
|
||||||
|
|
||||||
return channels[key]
|
|
||||||
?: run {
|
|
||||||
val newObject = channelFactory(key)
|
|
||||||
channels.putIfAbsent(key, newObject) ?: newObject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkGetOrCreateAddressableNote(key: String): AddressableNote? {
|
fun checkGetOrCreateAddressableNote(key: String): AddressableNote? {
|
||||||
return try {
|
return try {
|
||||||
val addr = ATag.parse(key, null) // relay doesn't matter for the index.
|
val addr = ATag.parse(key, null) // relay doesn't matter for the index.
|
||||||
@@ -970,10 +966,10 @@ object LocalCache {
|
|||||||
masterNote.removeReport(deleteNote)
|
masterNote.removeReport(deleteNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteNote.channelHex()?.let { channels[it]?.removeNote(deleteNote) }
|
deleteNote.channelHex()?.let { getChannelIfExists(it)?.removeNote(deleteNote) }
|
||||||
|
|
||||||
(deleteNote.event as? LiveActivitiesChatMessageEvent)?.activity()?.let {
|
(deleteNote.event as? LiveActivitiesChatMessageEvent)?.activity()?.let {
|
||||||
channels[it.toTag()]?.removeNote(deleteNote)
|
getChannelIfExists(it.toTag())?.removeNote(deleteNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteNote.event is PrivateDmEvent) {
|
if (deleteNote.event is PrivateDmEvent) {
|
||||||
@@ -1710,14 +1706,14 @@ object LocalCache {
|
|||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
val key = decodeEventIdAsHexOrNull(text)
|
val key = decodeEventIdAsHexOrNull(text)
|
||||||
if (key != null && channels[key] != null) {
|
if (key != null && getChannelIfExists(key) != null) {
|
||||||
return listOfNotNull(channels[key])
|
return listOfNotNull(getChannelIfExists(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels.values.filter {
|
return channels.filter { _, channel ->
|
||||||
it.anyNameStartsWith(text) ||
|
channel.anyNameStartsWith(text) ||
|
||||||
it.idHex.startsWith(text, true) ||
|
channel.idHex.startsWith(text, true) ||
|
||||||
it.idNote().startsWith(text, true)
|
channel.idNote().startsWith(text, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1796,8 +1792,8 @@ object LocalCache {
|
|||||||
fun pruneOldAndHiddenMessages(account: Account) {
|
fun pruneOldAndHiddenMessages(account: Account) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
channels.forEach { it ->
|
channels.forEach { _, channel ->
|
||||||
val toBeRemoved = it.value.pruneOldAndHiddenMessages(account)
|
val toBeRemoved = channel.pruneOldAndHiddenMessages(account)
|
||||||
|
|
||||||
val childrenToBeRemoved = mutableListOf<Note>()
|
val childrenToBeRemoved = mutableListOf<Note>()
|
||||||
|
|
||||||
@@ -1809,9 +1805,9 @@ object LocalCache {
|
|||||||
|
|
||||||
removeFromCache(childrenToBeRemoved)
|
removeFromCache(childrenToBeRemoved)
|
||||||
|
|
||||||
if (toBeRemoved.size > 100 || it.value.notes.size() > 100) {
|
if (toBeRemoved.size > 100 || channel.notes.size() > 100) {
|
||||||
println(
|
println(
|
||||||
"PRUNE: ${toBeRemoved.size} messages removed from ${it.value.toBestDisplayName()}. ${it.value.notes.size()} kept",
|
"PRUNE: ${toBeRemoved.size} messages removed from ${channel.toBestDisplayName()}. ${channel.notes.size()} kept",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,9 +178,8 @@ object NostrDiscoveryDataSource : NostrDataSource("DiscoveryFeed") {
|
|||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
authors = follows,
|
authors = follows,
|
||||||
kinds =
|
kinds = listOf(ChannelMessageEvent.KIND),
|
||||||
listOf(ChannelCreateEvent.KIND, ChannelMetadataEvent.KIND, ChannelMessageEvent.KIND),
|
limit = 500,
|
||||||
limit = 300,
|
|
||||||
since =
|
since =
|
||||||
latestEOSEs.users[account.userProfile()]
|
latestEOSEs.users[account.userProfile()]
|
||||||
?.followList
|
?.followList
|
||||||
@@ -194,7 +193,7 @@ object NostrDiscoveryDataSource : NostrDataSource("DiscoveryFeed") {
|
|||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
ids = followChats,
|
ids = followChats,
|
||||||
kinds = listOf(ChannelCreateEvent.KIND),
|
kinds = listOf(ChannelCreateEvent.KIND, ChannelMessageEvent.KIND),
|
||||||
limit = 300,
|
limit = 300,
|
||||||
since =
|
since =
|
||||||
latestEOSEs.users[account.userProfile()]
|
latestEOSEs.users[account.userProfile()]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.ui.dal
|
|||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.ParticipantListBuilder
|
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
||||||
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.events.IsInPublicChatChannel
|
import com.vitorpamplona.quartz.events.IsInPublicChatChannel
|
||||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||||
@@ -42,12 +42,25 @@ open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun feed(): List<Note> {
|
override fun feed(): List<Note> {
|
||||||
|
val params = buildFilterParams(account)
|
||||||
|
|
||||||
val allChannelNotes =
|
val allChannelNotes =
|
||||||
LocalCache.channels.values.mapNotNull { LocalCache.getNoteIfExists(it.idHex) }
|
LocalCache.channels.mapNotNullIntoSet { _, channel ->
|
||||||
|
if (channel is PublicChatChannel) {
|
||||||
|
val note = LocalCache.getNoteIfExists(channel.idHex)
|
||||||
|
val noteEvent = note?.event
|
||||||
|
|
||||||
val notes = innerApplyFilter(allChannelNotes)
|
if (noteEvent == null || params.match(noteEvent)) {
|
||||||
|
note
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sort(notes)
|
return sort(allChannelNotes)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||||
@@ -70,11 +83,21 @@ open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
|||||||
// note event here will never be null
|
// note event here will never be null
|
||||||
val noteEvent = note.event
|
val noteEvent = note.event
|
||||||
if (noteEvent is ChannelCreateEvent && params.match(noteEvent)) {
|
if (noteEvent is ChannelCreateEvent && params.match(noteEvent)) {
|
||||||
note
|
if ((LocalCache.getChannelIfExists(noteEvent.id)?.notes?.size() ?: 0) > 0) {
|
||||||
|
note
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
} else if (noteEvent is IsInPublicChatChannel) {
|
} else if (noteEvent is IsInPublicChatChannel) {
|
||||||
val channel = noteEvent.channel()?.let { LocalCache.checkGetOrCreateNote(it) }
|
val channel = noteEvent.channel()?.let { LocalCache.checkGetOrCreateNote(it) }
|
||||||
if (channel != null && (channel.event == null || params.match(channel.event))) {
|
if (channel != null &&
|
||||||
channel
|
(channel.event == null || (channel.event is ChannelCreateEvent && params.match(channel.event)))
|
||||||
|
) {
|
||||||
|
if ((LocalCache.getChannelIfExists(channel.idHex)?.notes?.size() ?: 0) > 0) {
|
||||||
|
channel
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -85,17 +108,15 @@ open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun sort(collection: Set<Note>): List<Note> {
|
override fun sort(collection: Set<Note>): List<Note> {
|
||||||
val followingKeySet =
|
val lastNote =
|
||||||
account.liveDiscoveryFollowLists.value?.users ?: account.liveKind3Follows.value.users
|
collection.associateWith { note ->
|
||||||
|
LocalCache.getChannelIfExists(note.idHex)?.lastNoteCreatedAt ?: 0
|
||||||
val counter = ParticipantListBuilder()
|
}
|
||||||
val participantCounts =
|
|
||||||
collection.associate { it to counter.countFollowsThatParticipateOn(it, followingKeySet) }
|
|
||||||
|
|
||||||
return collection
|
return collection
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
compareBy(
|
compareBy(
|
||||||
{ participantCounts[it] },
|
{ lastNote[it] },
|
||||||
{ it.createdAt() },
|
{ it.createdAt() },
|
||||||
{ it.idHex },
|
{ it.idHex },
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.dal
|
|||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.ParticipantListBuilder
|
|
||||||
import com.vitorpamplona.quartz.encoders.ATag
|
import com.vitorpamplona.quartz.encoders.ATag
|
||||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||||
@@ -112,21 +111,15 @@ open class DiscoverCommunityFeedFilter(val account: Account) : AdditiveFeedFilte
|
|||||||
) = aTag != null && aTag.kind == CommunityDefinitionEvent.KIND && params.match(aTag)
|
) = aTag != null && aTag.kind == CommunityDefinitionEvent.KIND && params.match(aTag)
|
||||||
|
|
||||||
override fun sort(collection: Set<Note>): List<Note> {
|
override fun sort(collection: Set<Note>): List<Note> {
|
||||||
val followingKeySet =
|
val lastNote =
|
||||||
account.liveDiscoveryFollowLists.value?.users ?: account.liveKind3Follows.value.users
|
collection.associateWith { note ->
|
||||||
|
note.boosts.maxOfOrNull { it.createdAt() ?: 0 } ?: 0
|
||||||
val counter = ParticipantListBuilder()
|
}
|
||||||
val participantCounts =
|
|
||||||
collection.associate { it to counter.countFollowsThatParticipateOn(it, followingKeySet) }
|
|
||||||
|
|
||||||
val allParticipants =
|
|
||||||
collection.associate { it to counter.countFollowsThatParticipateOn(it, null) }
|
|
||||||
|
|
||||||
return collection
|
return collection
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
compareBy(
|
compareBy(
|
||||||
{ participantCounts[it] },
|
{ lastNote[it] },
|
||||||
{ allParticipants[it] },
|
|
||||||
{ it.createdAt() },
|
{ it.createdAt() },
|
||||||
{ it.idHex },
|
{ it.idHex },
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ open class DiscoverLiveFeedFilter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun feed(): List<Note> {
|
override fun feed(): List<Note> {
|
||||||
val allChannelNotes = LocalCache.channels.values.mapNotNull { LocalCache.getNoteIfExists(it.idHex) }
|
val allChannelNotes = LocalCache.channels.mapNotNull { _, channel -> LocalCache.getNoteIfExists(channel.idHex) }
|
||||||
val allMessageNotes = LocalCache.channels.values.map { it.notes.filter { key, it -> it.event is LiveActivitiesEvent } }.flatten()
|
val allMessageNotes = LocalCache.channels.map { _, channel -> channel.notes.filter { key, it -> it.event is LiveActivitiesEvent } }.flatten()
|
||||||
|
|
||||||
val notes = innerApplyFilter(allChannelNotes + allMessageNotes)
|
val notes = innerApplyFilter(allChannelNotes + allMessageNotes)
|
||||||
|
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ fun WatchAccountForDiscoveryScreen(
|
|||||||
discoveryChatFeedViewModel: NostrDiscoverChatFeedViewModel,
|
discoveryChatFeedViewModel: NostrDiscoverChatFeedViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
val listState by accountViewModel.account.liveStoriesFollowLists.collectAsStateWithLifecycle()
|
val listState by accountViewModel.account.liveDiscoveryFollowLists.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(accountViewModel, listState) {
|
LaunchedEffect(accountViewModel, listState) {
|
||||||
NostrDiscoveryDataSource.resetFilters()
|
NostrDiscoveryDataSource.resetFilters()
|
||||||
@@ -348,7 +348,7 @@ private fun DiscoverFeedLoaded(
|
|||||||
ChannelCardCompose(
|
ChannelCardCompose(
|
||||||
baseNote = item,
|
baseNote = item,
|
||||||
routeForLastRead = routeForLastRead,
|
routeForLastRead = routeForLastRead,
|
||||||
modifier = Modifier,
|
modifier = Modifier.fillMaxWidth(),
|
||||||
forceEventKind = forceEventKind,
|
forceEventKind = forceEventKind,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
|
|||||||
@@ -441,6 +441,8 @@
|
|||||||
<string name="connectivity_type_always">Siempre</string>
|
<string name="connectivity_type_always">Siempre</string>
|
||||||
<string name="connectivity_type_wifi_only">Solo Wi-Fi</string>
|
<string name="connectivity_type_wifi_only">Solo Wi-Fi</string>
|
||||||
<string name="connectivity_type_never">Nunca</string>
|
<string name="connectivity_type_never">Nunca</string>
|
||||||
|
<string name="ui_feature_set_type_complete">Completo</string>
|
||||||
|
<string name="ui_feature_set_type_simplified">Simplificado</string>
|
||||||
<string name="system">Sistema</string>
|
<string name="system">Sistema</string>
|
||||||
<string name="light">Claro</string>
|
<string name="light">Claro</string>
|
||||||
<string name="dark">Oscuro</string>
|
<string name="dark">Oscuro</string>
|
||||||
@@ -452,6 +454,8 @@
|
|||||||
<string name="automatically_show_url_preview">Vista previa de URL</string>
|
<string name="automatically_show_url_preview">Vista previa de URL</string>
|
||||||
<string name="automatically_hide_nav_bars">Desplazamiento inmersivo</string>
|
<string name="automatically_hide_nav_bars">Desplazamiento inmersivo</string>
|
||||||
<string name="automatically_hide_nav_bars_description">Ocultar barras de navegación al desplazarse</string>
|
<string name="automatically_hide_nav_bars_description">Ocultar barras de navegación al desplazarse</string>
|
||||||
|
<string name="ui_style">Modo de interfaz</string>
|
||||||
|
<string name="ui_style_description">Elegir el estilo de publicación</string>
|
||||||
<string name="load_image">Cargar imagen</string>
|
<string name="load_image">Cargar imagen</string>
|
||||||
<string name="spamming_users">Spammers</string>
|
<string name="spamming_users">Spammers</string>
|
||||||
<string name="muted_button">Silenciado. Hacer clic para reactivar el sonido.</string>
|
<string name="muted_button">Silenciado. Hacer clic para reactivar el sonido.</string>
|
||||||
|
|||||||
@@ -38,16 +38,16 @@ import java.security.MessageDigest
|
|||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
public class NIP44v2Test {
|
class NIP44v2Test {
|
||||||
val vectors: VectorFile =
|
private val vectors: VectorFile =
|
||||||
jacksonObjectMapper()
|
jacksonObjectMapper()
|
||||||
.readValue(
|
.readValue(
|
||||||
getInstrumentation().context.assets.open("nip44.vectors.json"),
|
getInstrumentation().context.assets.open("nip44.vectors.json"),
|
||||||
VectorFile::class.java,
|
VectorFile::class.java,
|
||||||
)
|
)
|
||||||
|
|
||||||
val random = SecureRandom()
|
private val random = SecureRandom()
|
||||||
val nip44v2 = Nip44v2(Secp256k1.get(), random)
|
private val nip44v2 = Nip44v2(Secp256k1.get(), random)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun conversationKeyTest() {
|
fun conversationKeyTest() {
|
||||||
@@ -71,21 +71,25 @@ public class NIP44v2Test {
|
|||||||
fun encryptDecryptTest() {
|
fun encryptDecryptTest() {
|
||||||
for (v in vectors.v2?.valid?.encryptDecrypt!!) {
|
for (v in vectors.v2?.valid?.encryptDecrypt!!) {
|
||||||
val pub2 = com.vitorpamplona.quartz.crypto.KeyPair(v.sec2!!.hexToByteArray())
|
val pub2 = com.vitorpamplona.quartz.crypto.KeyPair(v.sec2!!.hexToByteArray())
|
||||||
val conversationKey = nip44v2.getConversationKey(v.sec1!!.hexToByteArray(), pub2.pubKey)
|
val conversationKey1 = nip44v2.getConversationKey(v.sec1!!.hexToByteArray(), pub2.pubKey)
|
||||||
assertEquals(v.conversationKey, conversationKey.toHexKey())
|
assertEquals(v.conversationKey, conversationKey1.toHexKey())
|
||||||
|
|
||||||
val ciphertext =
|
val ciphertext =
|
||||||
nip44v2
|
nip44v2
|
||||||
.encryptWithNonce(
|
.encryptWithNonce(
|
||||||
v.plaintext!!,
|
v.plaintext!!,
|
||||||
conversationKey,
|
conversationKey1,
|
||||||
v.nonce!!.hexToByteArray(),
|
v.nonce!!.hexToByteArray(),
|
||||||
)
|
)
|
||||||
.encodePayload()
|
.encodePayload()
|
||||||
|
|
||||||
assertEquals(v.payload, ciphertext)
|
assertEquals(v.payload, ciphertext)
|
||||||
|
|
||||||
val decrypted = nip44v2.decrypt(v.payload!!, conversationKey)
|
val pub1 = com.vitorpamplona.quartz.crypto.KeyPair(v.sec1.hexToByteArray())
|
||||||
|
val conversationKey2 = nip44v2.getConversationKey(v.sec2.hexToByteArray(), pub1.pubKey)
|
||||||
|
assertEquals(v.conversationKey, conversationKey2.toHexKey())
|
||||||
|
|
||||||
|
val decrypted = nip44v2.decrypt(v.payload!!, conversationKey2)
|
||||||
assertEquals(v.plaintext, decrypted)
|
assertEquals(v.plaintext, decrypted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +120,7 @@ public class NIP44v2Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun invalidMessageLenghts() {
|
fun invalidMessageLengths() {
|
||||||
for (v in vectors.v2?.invalid?.encryptMsgLengths!!) {
|
for (v in vectors.v2?.invalid?.encryptMsgLengths!!) {
|
||||||
val key = ByteArray(32)
|
val key = ByteArray(32)
|
||||||
random.nextBytes(key)
|
random.nextBytes(key)
|
||||||
@@ -154,7 +158,7 @@ public class NIP44v2Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sha256Hex(data: ByteArray): String {
|
private fun sha256Hex(data: ByteArray): String {
|
||||||
// Creates a new buffer every time
|
// Creates a new buffer every time
|
||||||
return MessageDigest.getInstance("SHA-256").digest(data).toHexKey()
|
return MessageDigest.getInstance("SHA-256").digest(data).toHexKey()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user