Improves caching of encrypted DMs:

- Migrates caching of decrypted value outside of the Event class
- Removes encrypted parts of NIP-17 from the cache
- Removes old NIP-04 messages from the cache
- Avoids deleting new NIP-17 plain text chats from memory
This commit is contained in:
Vitor Pamplona
2024-08-22 17:01:37 -04:00
parent 71b111ce8b
commit 448ea796ef
8 changed files with 263 additions and 183 deletions

View File

@@ -2290,12 +2290,14 @@ class Account(
val mine = signedEvents.wraps.filter { (it.recipientPubKey() == signer.pubKey) } val mine = signedEvents.wraps.filter { (it.recipientPubKey() == signer.pubKey) }
mine.forEach { giftWrap -> mine.forEach { giftWrap ->
giftWrap.cachedGift(signer) { gift -> giftWrap.unwrap(signer) { gift ->
if (gift is SealedGossipEvent) { if (gift is SealedGossipEvent) {
gift.cachedGossip(signer) { gossip -> LocalCache.justConsume(gossip, null) } gift.unseal(signer) { gossip ->
} else { LocalCache.justConsume(gossip, null)
LocalCache.justConsume(gift, null) }
} }
LocalCache.justConsume(gift, null)
} }
LocalCache.consume(giftWrap, null) LocalCache.consume(giftWrap, null)
@@ -2842,7 +2844,7 @@ class Account(
) { ) {
if (!isWriteable()) return if (!isWriteable()) return
return event.cachedGift(signer, onReady) return event.unwrap(signer, onReady)
} }
fun unseal( fun unseal(
@@ -2851,7 +2853,7 @@ class Account(
) { ) {
if (!isWriteable()) return if (!isWriteable()) return
return event.cachedGossip(signer, onReady) return event.unseal(signer, onReady)
} }
fun cachedDecryptContent(note: Note): String? = cachedDecryptContent(note.event) fun cachedDecryptContent(note: Note): String? = cachedDecryptContent(note.event)

View File

@@ -22,7 +22,9 @@ package com.vitorpamplona.amethyst.model
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.PrivateDmEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@Stable @Stable
@@ -75,7 +77,7 @@ class Chatroom {
fun senderIntersects(keySet: Set<HexKey>): Boolean = authors.any { it.pubkeyHex in keySet } fun senderIntersects(keySet: Set<HexKey>): Boolean = authors.any { it.pubkeyHex in keySet }
fun pruneMessagesToTheLatestOnly(): Set<Note> { fun pruneMessagesToTheLatestOnly(): Set<Note> {
val sorted = roomMessages.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed() val sorted = roomMessages.sortedWith(DefaultFeedOrder)
val toKeep = val toKeep =
if ((sorted.firstOrNull()?.createdAt() ?: 0) > TimeUtils.oneWeekAgo()) { if ((sorted.firstOrNull()?.createdAt() ?: 0) > TimeUtils.oneWeekAgo()) {
@@ -84,7 +86,7 @@ class Chatroom {
} else { } else {
// Old messages, keep the last one. // Old messages, keep the last one.
sorted.take(1).toSet() sorted.take(1).toSet()
} + sorted.filter { it.liveSet?.isInUse() ?: false } } + sorted.filter { it.liveSet?.isInUse() ?: false } + sorted.filter { it.event !is PrivateDmEvent }
val toRemove = roomMessages.minus(toKeep) val toRemove = roomMessages.minus(toKeep)
roomMessages = toKeep roomMessages = toKeep

View File

@@ -299,99 +299,121 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
override fun consume( override fun consume(
event: Event, event: Event,
relay: Relay, relay: Relay,
) {
if (LocalCache.justVerify(event)) {
consumeAlreadyVerified(event, relay)
}
}
fun consumeAlreadyVerified(
event: Event,
relay: Relay,
) { ) {
checkNotInMainThread() checkNotInMainThread()
if (LocalCache.justVerify(event)) { when (event) {
when (event) { is PrivateOutboxRelayListEvent -> {
is PrivateOutboxRelayListEvent -> { val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
val noteEvent = note?.event
if (noteEvent == null || event.createdAt > noteEvent.createdAt()) {
event.privateTags(account.signer) {
LocalCache.justConsume(event, relay)
}
}
}
is DraftEvent -> {
// Avoid decrypting over and over again if the event already exist.
if (!event.isDeleted()) {
val note = LocalCache.getAddressableNoteIfExists(event.addressTag()) val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
val noteEvent = note?.event val noteEvent = note?.event
if (noteEvent == null || event.createdAt > noteEvent.createdAt()) {
event.privateTags(account.signer) {
LocalCache.justConsume(event, relay)
}
}
}
is DraftEvent -> {
// Avoid decrypting over and over again if the event already exist.
if (!event.isDeleted()) {
val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
val noteEvent = note?.event
if (noteEvent != null) {
if (event.createdAt > noteEvent.createdAt() || relay.brief !in note.relays) {
LocalCache.consume(event, relay)
}
} else {
// decrypts
event.cachedDraft(account.signer) {}
LocalCache.justConsume(event, relay)
}
}
}
is GiftWrapEvent -> {
// Avoid decrypting over and over again if the event already exist.
val note = LocalCache.getNoteIfExists(event.id)
val noteEvent = note?.event as? GiftWrapEvent
if (noteEvent != null) { if (noteEvent != null) {
if (relay.brief !in note.relays) { if (event.createdAt > noteEvent.createdAt() || relay.brief !in note.relays) {
noteEvent.cachedGift(account.signer) { LocalCache.consume(event, relay)
LocalCache.justConsume(noteEvent, relay)
this.consume(it, relay)
}
} }
} else { } else {
// new event // decrypts
event.cachedGift(account.signer) { event.cachedDraft(account.signer) {}
LocalCache.justConsume(event, relay)
this.consume(it, relay)
}
}
}
is SealedGossipEvent -> {
// Avoid decrypting over and over again if the event already exist.
val note = LocalCache.getNoteIfExists(event.id)
val noteEvent = note?.event as? SealedGossipEvent
if (noteEvent != null) {
if (relay.brief !in note.relays) {
// adds the relay to seal and inner chat
noteEvent.cachedGossip(account.signer) {
LocalCache.consume(noteEvent, relay)
LocalCache.justConsume(it, relay)
}
}
} else {
// new event
event.cachedGossip(account.signer) {
LocalCache.justConsume(event, relay)
LocalCache.justConsume(it, relay)
}
}
}
is LnZapEvent -> {
// Avoid decrypting over and over again if the event already exist.
val note = LocalCache.getNoteIfExists(event.id)
if (note?.event == null) {
event.zapRequest?.let {
if (it.isPrivateZap()) {
it.decryptPrivateZap(account.signer) {}
}
}
LocalCache.justConsume(event, relay) LocalCache.justConsume(event, relay)
} }
} }
}
is GiftWrapEvent -> {
// Avoid decrypting over and over again if the event already exist.
val note = LocalCache.getNoteIfExists(event.id)
val noteEvent = note?.event as? GiftWrapEvent
if (noteEvent != null) {
if (relay.brief !in note.relays) {
LocalCache.justConsume(noteEvent, relay)
noteEvent.innerEventId?.let {
(LocalCache.getNoteIfExists(it)?.event as? Event)?.let {
this.consumeAlreadyVerified(it, relay)
}
} ?: run {
event.unwrap(account.signer) {
this.consume(it, relay)
noteEvent.innerEventId = it.id
}
}
}
} else {
// new event
event.unwrap(account.signer) {
LocalCache.justConsume(event, relay)
this.consume(it, relay)
}
}
}
is SealedGossipEvent -> {
// Avoid decrypting over and over again if the event already exist.
val note = LocalCache.getNoteIfExists(event.id)
val noteEvent = note?.event as? SealedGossipEvent
if (noteEvent != null) {
if (relay.brief !in note.relays) {
LocalCache.justConsume(noteEvent, relay)
noteEvent.innerEventId?.let {
(LocalCache.getNoteIfExists(it)?.event as? Event)?.let {
LocalCache.justConsume(it, relay)
}
} ?: run {
event.unseal(account.signer) {
LocalCache.justConsume(it, relay)
noteEvent.innerEventId = it.id
}
}
}
} else {
// new event
event.unseal(account.signer) {
LocalCache.justConsume(event, relay)
LocalCache.justConsume(it, relay)
}
}
}
is LnZapEvent -> {
// Avoid decrypting over and over again if the event already exist.
val note = LocalCache.getNoteIfExists(event.id)
if (note?.event == null) {
event.zapRequest?.let {
if (it.isPrivateZap()) {
it.decryptPrivateZap(account.signer) {}
}
}
else -> {
LocalCache.justConsume(event, relay) LocalCache.justConsume(event, relay)
} }
} }
else -> {
LocalCache.justConsume(event, relay)
}
} }
} }
@@ -409,16 +431,30 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
} }
private fun markInnerAsSeenOnRelay( private fun markInnerAsSeenOnRelay(
noteEvent: EventInterface, newNoteEvent: EventInterface,
relay: Relay, relay: Relay,
) { ) {
LocalCache.getNoteIfExists(noteEvent.id())?.addRelay(relay) markInnerAsSeenOnRelay(newNoteEvent.id(), relay)
}
if (noteEvent is GiftWrapEvent) { private fun markInnerAsSeenOnRelay(
noteEvent.cachedGift(account.signer) { gift -> markInnerAsSeenOnRelay(gift, relay) } eventId: HexKey,
} else if (noteEvent is SealedGossipEvent) { relay: Relay,
noteEvent.cachedGossip(account.signer) { rumor -> ) {
markInnerAsSeenOnRelay(rumor, relay) val note = LocalCache.getNoteIfExists(eventId)
if (note != null) {
note.addRelay(relay)
val noteEvent = note.event
if (noteEvent is GiftWrapEvent) {
noteEvent.innerEventId?.let {
markInnerAsSeenOnRelay(it, relay)
}
} else if (noteEvent is SealedGossipEvent) {
noteEvent.innerEventId?.let {
markInnerAsSeenOnRelay(it, relay)
}
} }
} }
} }

View File

@@ -75,7 +75,8 @@ class EventNotificationConsumer(
pushWrappedEvent: GiftWrapEvent, pushWrappedEvent: GiftWrapEvent,
account: Account, account: Account,
) { ) {
pushWrappedEvent.cachedGift(account.signer) { notificationEvent -> // no need to cache
pushWrappedEvent.unwrap(account.signer) { notificationEvent ->
val consumed = LocalCache.hasConsumed(notificationEvent) val consumed = LocalCache.hasConsumed(notificationEvent)
val verified = LocalCache.justVerify(notificationEvent) val verified = LocalCache.justVerify(notificationEvent)
Log.d("EventNotificationConsumer", "New Notification ${notificationEvent.kind} ${notificationEvent.id} Arrived for ${account.userProfile().toBestDisplayName()} consumed= $consumed && verified= $verified") Log.d("EventNotificationConsumer", "New Notification ${notificationEvent.kind} ${notificationEvent.id} Arrived for ${account.userProfile().toBestDisplayName()} consumed= $consumed && verified= $verified")
@@ -111,15 +112,19 @@ class EventNotificationConsumer(
when (event) { when (event) {
is GiftWrapEvent -> { is GiftWrapEvent -> {
event.cachedGift(account.signer) { unwrapAndConsume(it, account, onReady) } event.unwrap(account.signer) {
unwrapAndConsume(it, account, onReady)
LocalCache.justConsume(event, null)
}
} }
is SealedGossipEvent -> { is SealedGossipEvent -> {
event.cachedGossip(account.signer) { event.unseal(account.signer) {
if (!LocalCache.hasConsumed(it)) { if (!LocalCache.hasConsumed(it)) {
// this is not verifiable // this is not verifiable
LocalCache.justConsume(it, null) LocalCache.justConsume(it, null)
onReady(it) onReady(it)
} }
LocalCache.justConsume(event, null)
} }
} }
else -> { else -> {

View File

@@ -1364,25 +1364,50 @@ class AccountViewModel(
) { ) {
when (event) { when (event) {
is GiftWrapEvent -> { is GiftWrapEvent -> {
event.cachedGift(account.signer) { event.innerEventId?.let {
val existingNote = LocalCache.getNoteIfExists(it.id) val existingNote = LocalCache.getNoteIfExists(it)
if (existingNote != null) { if (existingNote != null) {
unwrapIfNeeded(existingNote.event, onReady) unwrapIfNeeded(existingNote.event, onReady)
} else { } else {
LocalCache.verifyAndConsume(it, null) event.unwrap(account.signer) {
unwrapIfNeeded(it, onReady) LocalCache.verifyAndConsume(it, null)
unwrapIfNeeded(it, onReady)
}
}
} ?: run {
event.unwrap(account.signer) {
val existingNote = LocalCache.getNoteIfExists(it.id)
if (existingNote != null) {
unwrapIfNeeded(existingNote.event, onReady)
} else {
LocalCache.verifyAndConsume(it, null)
unwrapIfNeeded(it, onReady)
}
} }
} }
} }
is SealedGossipEvent -> { is SealedGossipEvent -> {
event.cachedGossip(account.signer) { event.innerEventId?.let {
val existingNote = LocalCache.getNoteIfExists(it.id) val existingNote = LocalCache.getNoteIfExists(it)
if (existingNote != null) { if (existingNote != null) {
unwrapIfNeeded(existingNote.event, onReady) unwrapIfNeeded(existingNote.event, onReady)
} else { } else {
// this is not verifiable event.unseal(account.signer) {
LocalCache.justConsume(it, null) // this is not verifiable
unwrapIfNeeded(it, onReady) LocalCache.justConsume(it, null)
unwrapIfNeeded(it, onReady)
}
}
} ?: run {
event.unseal(account.signer) {
val existingNote = LocalCache.getNoteIfExists(it.id)
if (existingNote != null) {
unwrapIfNeeded(existingNote.event, onReady)
} else {
// this is not verifiable
LocalCache.justConsume(it, null)
unwrapIfNeeded(it, onReady)
}
} }
} }
} }

View File

@@ -43,10 +43,15 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.ChannelCreateEvent import com.vitorpamplona.quartz.events.ChannelCreateEvent
import com.vitorpamplona.quartz.events.ChannelMessageEvent
import com.vitorpamplona.quartz.events.ChannelMetadataEvent
import com.vitorpamplona.quartz.events.ChatroomKeyable import com.vitorpamplona.quartz.events.ChatroomKeyable
import com.vitorpamplona.quartz.events.EventInterface import com.vitorpamplona.quartz.events.EventInterface
import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.GiftWrapEvent
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.events.LiveActivitiesEvent
import com.vitorpamplona.quartz.events.SealedGossipEvent import com.vitorpamplona.quartz.events.SealedGossipEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -104,7 +109,7 @@ fun LoadRedirectScreen(
val event = note.event val event = note.event
if (event != null) { if (event != null) {
withContext(Dispatchers.IO) { redirect(event, note, accountViewModel, nav) } withContext(Dispatchers.IO) { redirect(event, accountViewModel, nav) }
} }
} }
@@ -118,35 +123,60 @@ fun LoadRedirectScreen(
} }
fun redirect( fun redirect(
event: EventInterface, eventId: HexKey,
note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val channelHex = note.channelHex() LocalCache.getNoteIfExists(eventId)?.event?.let {
redirect(it, accountViewModel, nav)
}
}
fun redirect(
event: EventInterface,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val channelHex =
if (
event is ChannelMessageEvent ||
event is ChannelMetadataEvent ||
event is ChannelCreateEvent ||
event is LiveActivitiesChatMessageEvent ||
event is LiveActivitiesEvent
) {
(event as? ChannelMessageEvent)?.channel()
?: (event as? ChannelMetadataEvent)?.channel()
?: (event as? ChannelCreateEvent)?.id
?: (event as? LiveActivitiesChatMessageEvent)?.activity()?.toTag()
?: (event as? LiveActivitiesEvent)?.address()?.toTag()
} else {
null
}
if (event is GiftWrapEvent) { if (event is GiftWrapEvent) {
accountViewModel.unwrap(event) { redirect(it, note, accountViewModel, nav) } event.innerEventId?.let {
redirect(it, accountViewModel, nav)
} ?: run {
accountViewModel.unwrap(event) { redirect(it, accountViewModel, nav) }
}
} else if (event is SealedGossipEvent) { } else if (event is SealedGossipEvent) {
accountViewModel.unseal(event) { redirect(it, note, accountViewModel, nav) } event.innerEventId?.let {
redirect(it, accountViewModel, nav)
} ?: run {
accountViewModel.unseal(event) { redirect(it, accountViewModel, nav) }
}
} else { } else {
if (event == null) { if (event is ChannelCreateEvent) {
// stay here, loading nav("Channel/${event.id()}")
} else if (event is ChannelCreateEvent) {
nav("Channel/${note.idHex}")
} else if (event is ChatroomKeyable) { } else if (event is ChatroomKeyable) {
note.author?.let { val withKey = event.chatroomKey(accountViewModel.userProfile().pubkeyHex)
val withKey = accountViewModel.userProfile().createChatroom(withKey)
(event as ChatroomKeyable).chatroomKey(accountViewModel.userProfile().pubkeyHex) nav("Room/${withKey.hashCode()}")
accountViewModel.userProfile().createChatroom(withKey)
nav("Room/${withKey.hashCode()}")
}
} else if (channelHex != null) { } else if (channelHex != null) {
nav("Channel/$channelHex") nav("Channel/$channelHex")
} else { } else {
nav("Note/${note.idHex}") nav("Note/${event.id()}")
} }
} }
} }

View File

@@ -20,12 +20,14 @@
*/ */
package com.vitorpamplona.quartz.events package com.vitorpamplona.quartz.events
import android.util.Log
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.signers.NostrSignerInternal
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
@@ -37,11 +39,11 @@ class GiftWrapEvent(
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf() @Transient var innerEventId: HexKey? = null
override fun countMemory(): Long = override fun countMemory(): Long =
super.countMemory() + super.countMemory() +
32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }) // rough calculation pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0)
fun copyNoContent(): GiftWrapEvent { fun copyNoContent(): GiftWrapEvent {
val copy = val copy =
@@ -54,48 +56,38 @@ class GiftWrapEvent(
sig, sig,
) )
copy.cachedInnerEvent = cachedInnerEvent copy.innerEventId = innerEventId
return copy return copy
} }
override fun isContentEncoded() = true override fun isContentEncoded() = true
fun preCachedGift(signer: NostrSigner): Event? = cachedInnerEvent[signer.pubKey] @Deprecated(
message = "Heavy caching was removed from this class due to high memory use. Cache it separatedly",
fun addToCache( replaceWith = ReplaceWith("unwrap"),
pubKey: HexKey, )
gift: Event,
) {
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
}
fun cachedGift( fun cachedGift(
signer: NostrSigner, signer: NostrSigner,
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
) { ) = unwrap(signer, onReady)
cachedInnerEvent[signer.pubKey]?.let {
onReady(it)
return
}
unwrap(signer) { gift ->
if (gift is WrappedEvent) {
gift.host = HostStub(this.id, this.pubKey, this.kind)
}
addToCache(signer.pubKey, gift)
onReady(gift) fun unwrap(
}
}
private fun unwrap(
signer: NostrSigner, signer: NostrSigner,
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
) { ) {
try { try {
plainContent(signer) { onReady(fromJson(it)) } plainContent(signer) { giftStr ->
val gift = fromJson(giftStr)
if (gift is WrappedEvent) {
gift.host = HostStub(this.id, this.pubKey, this.kind)
}
innerEventId = gift.id
onReady(gift)
}
} catch (e: Exception) { } catch (e: Exception) {
// Log.e("UnwrapError", "Couldn't Decrypt the content", e) Log.w("GiftWrapEvent", "Couldn't Decrypt the content", e)
} }
} }

View File

@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
@@ -38,11 +39,11 @@ class SealedGossipEvent(
content: String, content: String,
sig: HexKey, sig: HexKey,
) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf() @Transient var innerEventId: HexKey? = null
override fun countMemory(): Long = override fun countMemory(): Long =
super.countMemory() + super.countMemory() +
pointerSizeInBytes + cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) } pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0)
fun copyNoContent(): SealedGossipEvent { fun copyNoContent(): SealedGossipEvent {
val copy = val copy =
@@ -55,51 +56,38 @@ class SealedGossipEvent(
sig, sig,
) )
copy.cachedInnerEvent = cachedInnerEvent
copy.host = host copy.host = host
copy.innerEventId = innerEventId
return copy return copy
} }
override fun isContentEncoded() = true override fun isContentEncoded() = true
fun preCachedGossip(signer: NostrSigner): Event? = cachedInnerEvent[signer.pubKey] @Deprecated(
message = "Heavy caching was removed from this class due to high memory use. Cache it separatedly",
fun addToCache( replaceWith = ReplaceWith("unseal"),
pubKey: HexKey, )
gift: Event,
) {
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
}
fun cachedGossip( fun cachedGossip(
signer: NostrSigner, signer: NostrSigner,
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
) { ) = unseal(signer, onReady)
cachedInnerEvent[signer.pubKey]?.let {
onReady(it)
return
}
unseal(signer) { gossip -> fun unseal(
val event = gossip.mergeWith(this)
if (event is WrappedEvent) {
event.host = host ?: HostStub(this.id, this.pubKey, this.kind)
}
addToCache(signer.pubKey, event)
onReady(event)
}
}
private fun unseal(
signer: NostrSigner, signer: NostrSigner,
onReady: (Gossip) -> Unit, onReady: (Event) -> Unit,
) { ) {
try { try {
plainContent(signer) { plainContent(signer) {
try { try {
onReady(Gossip.fromJson(it)) val gossip = Gossip.fromJson(it)
val event = gossip.mergeWith(this)
if (event is WrappedEvent) {
event.host = host ?: HostStub(this.id, this.pubKey, this.kind)
}
innerEventId = event.id
onReady(event)
} catch (e: Exception) { } catch (e: Exception) {
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e) Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
} }