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

@@ -20,12 +20,14 @@
*/
package com.vitorpamplona.quartz.events
import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerInternal
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@@ -37,11 +39,11 @@ class GiftWrapEvent(
content: String,
sig: HexKey,
) : 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 =
super.countMemory() +
32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }) // rough calculation
pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0)
fun copyNoContent(): GiftWrapEvent {
val copy =
@@ -54,48 +56,38 @@ class GiftWrapEvent(
sig,
)
copy.cachedInnerEvent = cachedInnerEvent
copy.innerEventId = innerEventId
return copy
}
override fun isContentEncoded() = true
fun preCachedGift(signer: NostrSigner): Event? = cachedInnerEvent[signer.pubKey]
fun addToCache(
pubKey: HexKey,
gift: Event,
) {
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
}
@Deprecated(
message = "Heavy caching was removed from this class due to high memory use. Cache it separatedly",
replaceWith = ReplaceWith("unwrap"),
)
fun cachedGift(
signer: NostrSigner,
onReady: (Event) -> Unit,
) {
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)
) = unwrap(signer, onReady)
onReady(gift)
}
}
private fun unwrap(
fun unwrap(
signer: NostrSigner,
onReady: (Event) -> Unit,
) {
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) {
// 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.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@@ -38,11 +39,11 @@ class SealedGossipEvent(
content: String,
sig: HexKey,
) : 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 =
super.countMemory() +
pointerSizeInBytes + cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }
pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0)
fun copyNoContent(): SealedGossipEvent {
val copy =
@@ -55,51 +56,38 @@ class SealedGossipEvent(
sig,
)
copy.cachedInnerEvent = cachedInnerEvent
copy.host = host
copy.innerEventId = innerEventId
return copy
}
override fun isContentEncoded() = true
fun preCachedGossip(signer: NostrSigner): Event? = cachedInnerEvent[signer.pubKey]
fun addToCache(
pubKey: HexKey,
gift: Event,
) {
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
}
@Deprecated(
message = "Heavy caching was removed from this class due to high memory use. Cache it separatedly",
replaceWith = ReplaceWith("unseal"),
)
fun cachedGossip(
signer: NostrSigner,
onReady: (Event) -> Unit,
) {
cachedInnerEvent[signer.pubKey]?.let {
onReady(it)
return
}
) = unseal(signer, onReady)
unseal(signer) { gossip ->
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(
fun unseal(
signer: NostrSigner,
onReady: (Gossip) -> Unit,
onReady: (Event) -> Unit,
) {
try {
plainContent(signer) {
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) {
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
}