Move to a cache model where we defer the event's verification until the last moment before adding to cache

This commit is contained in:
Vitor Pamplona
2025-05-06 09:45:01 -04:00
parent a05a465f69
commit fbe20029f7
12 changed files with 565 additions and 490 deletions
@@ -126,7 +126,7 @@ class ThreadDualAxisChartAssemblerTest {
var counter = 0 var counter = 0
eventArray.forEach { eventArray.forEach {
TestCase.assertTrue("${it.id} failed signature check", it.verify()) TestCase.assertTrue("${it.id} failed signature check", it.verify())
LocalCache.verifyAndConsume(it, null) LocalCache.justConsume(it, null)
counter++ counter++
} }
@@ -2000,10 +2000,10 @@ class Account(
if (!isWriteable()) return null if (!isWriteable()) return null
Amethyst.instance.client.send(data, relayList = relayList) Amethyst.instance.client.send(data, relayList = relayList)
LocalCache.consume(data, null) LocalCache.justConsume(data, null)
Amethyst.instance.client.send(signedEvent, relayList = relayList) Amethyst.instance.client.send(signedEvent, relayList = relayList)
LocalCache.consume(signedEvent, null) LocalCache.justConsume(signedEvent, null)
return LocalCache.getNoteIfExists(signedEvent.id) return LocalCache.getNoteIfExists(signedEvent.id)
} }
@@ -2012,8 +2012,8 @@ class Account(
data: FileStorageEvent, data: FileStorageEvent,
signedEvent: FileStorageHeaderEvent, signedEvent: FileStorageHeaderEvent,
): Note? { ): Note? {
LocalCache.consume(data, null) LocalCache.justConsume(data, null)
LocalCache.consume(signedEvent, null) LocalCache.justConsume(signedEvent, null)
return LocalCache.getNoteIfExists(signedEvent.id) return LocalCache.getNoteIfExists(signedEvent.id)
} }
@@ -2695,7 +2695,7 @@ class Account(
LocalCache.justConsume(gift, null) LocalCache.justConsume(gift, null)
} }
LocalCache.consume(giftWrap, null) LocalCache.justConsume(giftWrap, null)
} }
val id = mine.firstOrNull()?.id val id = mine.firstOrNull()?.id
@@ -2839,7 +2839,7 @@ class Account(
}, },
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
@@ -2996,7 +2996,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} else { } else {
MuteListEvent.createListWithWord( MuteListEvent.createListWithWord(
@@ -3005,7 +3005,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
} }
@@ -3020,7 +3020,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
@@ -3033,7 +3033,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
} }
@@ -3049,7 +3049,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} else { } else {
MuteListEvent.createListWithUser( MuteListEvent.createListWithUser(
@@ -3058,7 +3058,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
} }
@@ -3073,7 +3073,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
@@ -3086,7 +3086,7 @@ class Account(
signer = signer, signer = signer,
) { ) {
Amethyst.instance.client.send(it) Amethyst.instance.client.send(it)
LocalCache.consume(it, null) LocalCache.justConsume(it, null)
} }
} }
@@ -3709,25 +3709,25 @@ class Account(
Log.d("AccountRegisterObservers", "Loading saved user metadata ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved user metadata ${it.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.consume(it, null) } GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsume(it, null) }
} }
settings.backupDMRelayList?.let { settings.backupDMRelayList?.let {
Log.d("AccountRegisterObservers", "Loading saved DM Relay List ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved DM Relay List ${it.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) } GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsume(it, null) }
} }
settings.backupNIP65RelayList?.let { settings.backupNIP65RelayList?.let {
Log.d("AccountRegisterObservers", "Loading saved nip65 relay list ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved nip65 relay list ${it.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) } GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsume(it, null) }
} }
settings.backupSearchRelayList?.let { settings.backupSearchRelayList?.let {
Log.d("AccountRegisterObservers", "Loading saved search relay list ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved search relay list ${it.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) } GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsume(it, null) }
} }
settings.backupPrivateHomeRelayList?.let { event -> settings.backupPrivateHomeRelayList?.let { event ->
@@ -3735,7 +3735,7 @@ class Account(
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
event.privateTags(signer) { event.privateTags(signer) {
LocalCache.verifyAndConsume(event, null) LocalCache.justConsume(event, null)
} }
} }
} }
@@ -3744,7 +3744,7 @@ class Account(
Log.d("AccountRegisterObservers", "Loading saved app specific data ${event.toJson()}") Log.d("AccountRegisterObservers", "Loading saved app specific data ${event.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
LocalCache.verifyAndConsume(event, null) LocalCache.justConsume(event, null)
signer.decrypt(event.content, event.pubKey) { decrypted -> signer.decrypt(event.content, event.pubKey) { decrypted ->
try { try {
val syncedSettings = EventMapper.mapper.readValue<AccountSyncedSettingsInternal>(decrypted) val syncedSettings = EventMapper.mapper.readValue<AccountSyncedSettingsInternal>(decrypted)
@@ -3759,22 +3759,34 @@ class Account(
} }
} }
settings.backupMuteList?.let { settings.backupMuteList?.let { event ->
Log.d("AccountRegisterObservers", "Loading saved mute list ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved mute list ${event.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) } GlobalScope.launch(Dispatchers.IO) {
event.privateTags(signer) {
LocalCache.justConsume(event, null)
}
}
} }
settings.backupEphemeralChatList?.let { settings.backupEphemeralChatList?.let { event ->
Log.d("AccountRegisterObservers", "Loading saved ephemeral chat list ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved ephemeral chat list ${event.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) } GlobalScope.launch(Dispatchers.IO) {
event.privateTags(signer) {
LocalCache.justConsume(event, null)
}
}
} }
settings.backupChannelList?.let { settings.backupChannelList?.let { event ->
Log.d("AccountRegisterObservers", "Loading saved channel list ${it.toJson()}") Log.d("AccountRegisterObservers", "Loading saved channel list ${event.toJson()}")
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) } GlobalScope.launch(Dispatchers.IO) {
event.privateTags(signer) {
LocalCache.justConsume(event, null)
}
}
} }
// saves contact list for the next time. // saves contact list for the next time.
File diff suppressed because it is too large Load Diff
@@ -29,7 +29,6 @@ import com.vitorpamplona.amethyst.service.replace
import com.vitorpamplona.amethyst.tryAndWait import com.vitorpamplona.amethyst.tryAndWait
import com.vitorpamplona.amethyst.ui.note.toShortenHex import com.vitorpamplona.amethyst.ui.note.toShortenHex
import com.vitorpamplona.ammolite.relays.BundledUpdate import com.vitorpamplona.ammolite.relays.BundledUpdate
import com.vitorpamplona.ammolite.relays.Relay
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
import com.vitorpamplona.ammolite.relays.filters.EOSETime import com.vitorpamplona.ammolite.relays.filters.EOSETime
import com.vitorpamplona.quartz.experimental.bounties.addedRewardValue import com.vitorpamplona.quartz.experimental.bounties.addedRewardValue
@@ -448,7 +447,7 @@ open class Note(
} }
} }
fun hasRelay(relay: Relay) = relay.brief !in relays fun hasRelay(relay: RelayBriefInfoCache.RelayBriefInfo) = relay !in relays
fun addRelay(brief: RelayBriefInfoCache.RelayBriefInfo) { fun addRelay(brief: RelayBriefInfoCache.RelayBriefInfo) {
if (brief !in relays) { if (brief !in relays) {
@@ -62,7 +62,6 @@ class EventNotificationConsumer(
suspend fun consume(event: GiftWrapEvent) { suspend fun consume(event: GiftWrapEvent) {
Log.d(TAG, "New Notification Arrived") Log.d(TAG, "New Notification Arrived")
if (!LocalCache.justVerify(event)) return
// PushNotification Wraps don't include a receiver. // PushNotification Wraps don't include a receiver.
// Test with all logged in accounts // Test with all logged in accounts
@@ -108,9 +107,8 @@ class EventNotificationConsumer(
account: AccountSettings, account: AccountSettings,
) { ) {
val consumed = LocalCache.hasConsumed(notificationEvent) val consumed = LocalCache.hasConsumed(notificationEvent)
val verified = LocalCache.justVerify(notificationEvent) Log.d(TAG, "New Notification ${notificationEvent.kind} ${notificationEvent.id} Arrived for ${signer.pubKey} consumed= $consumed")
Log.d(TAG, "New Notification ${notificationEvent.kind} ${notificationEvent.id} Arrived for ${signer.pubKey} consumed= $consumed && verified= $verified") if (!consumed) {
if (!consumed && verified) {
Log.d(TAG, "New Notification was verified") Log.d(TAG, "New Notification was verified")
unwrapAndConsume(notificationEvent, signer) { innerEvent -> unwrapAndConsume(notificationEvent, signer) { innerEvent ->
if (!notificationManager().areNotificationsEnabled()) return@unwrapAndConsume if (!notificationManager().areNotificationsEnabled()) return@unwrapAndConsume
@@ -135,8 +133,6 @@ class EventNotificationConsumer(
suspend fun findAccountAndConsume(event: Event) { suspend fun findAccountAndConsume(event: Event) {
Log.d(TAG, "New Notification Arrived") Log.d(TAG, "New Notification Arrived")
if (!LocalCache.justVerify(event)) return
val users = event.taggedUserIds().map { LocalCache.getOrCreateUser(it) } val users = event.taggedUserIds().map { LocalCache.getOrCreateUser(it) }
val npubs = users.map { it.pubkeyNpub() }.toSet() val npubs = users.map { it.pubkeyNpub() }.toSet()
@@ -174,24 +170,27 @@ class EventNotificationConsumer(
signer: NostrSigner, signer: NostrSigner,
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
) { ) {
if (!LocalCache.justVerify(event)) return
if (LocalCache.hasConsumed(event)) return if (LocalCache.hasConsumed(event)) return
when (event) { when (event) {
is GiftWrapEvent -> { is GiftWrapEvent -> {
event.unwrap(signer) { if (LocalCache.justConsume(event, null)) {
unwrapAndConsume(it, signer, onReady) // new event
LocalCache.justConsume(event, null) event.unwrap(signer) {
unwrapAndConsume(it, signer, onReady)
}
} }
} }
is SealedRumorEvent -> { is SealedRumorEvent -> {
event.unseal(signer) { if (LocalCache.justConsume(event, null)) {
if (!LocalCache.hasConsumed(it)) { // new event
event.unseal(signer) {
// this is not verifiable // this is not verifiable
LocalCache.justConsume(it, null) if (!LocalCache.hasConsumed(it)) {
onReady(it) LocalCache.justConsume(it, null)
onReady(it)
}
} }
LocalCache.justConsume(event, null)
} }
} }
else -> { else -> {
@@ -33,7 +33,7 @@ class CacheClientConnector(
) { ) {
val receiver = val receiver =
EventCollector(client) { event, relay -> EventCollector(client) { event, relay ->
cache.verifyAndConsume(event, relay) cache.justConsume(event, relay)
} }
val confirmationWatcher = val confirmationWatcher =
@@ -155,7 +155,7 @@ class SearchFilterAssembler(
is Note -> filterByEvent(parsed.hex) is Note -> filterByEvent(parsed.hex)
is NEvent -> filterByEvent(parsed.hex) is NEvent -> filterByEvent(parsed.hex)
is NEmbed -> { is NEmbed -> {
cache.verifyAndConsume(parsed.event, null) cache.justConsume(parsed.event, null)
emptyList() emptyList()
} }
@@ -183,7 +183,7 @@ fun uriToRoute(uri: String?): Route? =
is NEmbed -> { is NEmbed -> {
if (LocalCache.getNoteIfExists(nip19.event.id) == null) { if (LocalCache.getNoteIfExists(nip19.event.id) == null) {
LocalCache.verifyAndConsume(nip19.event, null) LocalCache.justConsume(nip19.event, null)
} }
Route.EventRedirect(nip19.event.id) Route.EventRedirect(nip19.event.id)
} }
@@ -155,7 +155,7 @@ fun PollNotePreview() {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
LocalCache.justConsume(event, null) LocalCache.justConsume(event, null)
LocalCache.consume(zapVote.zapRequest!!) LocalCache.consume(zapVote.zapRequest!!)
LocalCache.consume(zapVote, null) LocalCache.justConsume(zapVote, null)
baseNote = LocalCache.getOrCreateNote("6ff9bc13d27490f6e3953325260bd996901a143de89886a0608c39e7d0160a72") baseNote = LocalCache.getOrCreateNote("6ff9bc13d27490f6e3953325260bd996901a143de89886a0608c39e7d0160a72")
} }
} }
@@ -1091,7 +1091,7 @@ class AccountViewModel(
var note = checkGetOrCreateNote(event.id) var note = checkGetOrCreateNote(event.id)
if (note == null) { if (note == null) {
LocalCache.verifyAndConsume(event, null) LocalCache.justConsume(event, null)
note = checkGetOrCreateNote(event.id) note = checkGetOrCreateNote(event.id)
} }
@@ -1471,8 +1471,9 @@ class AccountViewModel(
unwrapIfNeeded(existingNote.event, onReady) unwrapIfNeeded(existingNote.event, onReady)
} else { } else {
event.unwrap(account.signer) { event.unwrap(account.signer) {
LocalCache.verifyAndConsume(it, null) if (LocalCache.justConsume(it, null)) {
unwrapIfNeeded(it, onReady) unwrapIfNeeded(it, onReady)
}
} }
} }
} ?: run { } ?: run {
@@ -1481,8 +1482,9 @@ class AccountViewModel(
if (existingNote != null) { if (existingNote != null) {
unwrapIfNeeded(existingNote.event, onReady) unwrapIfNeeded(existingNote.event, onReady)
} else { } else {
LocalCache.verifyAndConsume(it, null) if (LocalCache.justConsume(it, null)) {
unwrapIfNeeded(it, onReady) unwrapIfNeeded(it, onReady)
}
} }
} }
} }
@@ -1495,8 +1497,9 @@ class AccountViewModel(
} else { } else {
event.unseal(account.signer) { event.unseal(account.signer) {
// this is not verifiable // this is not verifiable
LocalCache.justConsume(it, null) if (LocalCache.justConsume(it, null)) {
unwrapIfNeeded(it, onReady) unwrapIfNeeded(it, onReady)
}
} }
} }
} ?: run { } ?: run {
@@ -1506,8 +1509,9 @@ class AccountViewModel(
unwrapIfNeeded(existingNote.event, onReady) unwrapIfNeeded(existingNote.event, onReady)
} else { } else {
// this is not verifiable // this is not verifiable
LocalCache.justConsume(it, null) if (LocalCache.justConsume(it, null)) {
unwrapIfNeeded(it, onReady) unwrapIfNeeded(it, onReady)
}
} }
} }
} }
@@ -1755,7 +1759,7 @@ class AccountViewModel(
val baseNote = LocalCache.getOrCreateNote(parsed.event) val baseNote = LocalCache.getOrCreateNote(parsed.event)
if (baseNote.event == null) { if (baseNote.event == null) {
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
LocalCache.verifyAndConsume(parsed.event, null) LocalCache.justConsume(parsed.event, null)
} }
} }
@@ -78,8 +78,7 @@ class PrecacheNewNotesProcessor(
val inner = event.innerEventId val inner = event.innerEventId
if (inner == null) { if (inner == null) {
event.unwrap(account.signer) { event.unwrap(account.signer) {
if (cache.justVerify(it)) { if (cache.justConsume(it, null)) {
cache.justConsume(it, null)
cache.copyRelaysFromTo(note, it) cache.copyRelaysFromTo(note, it)
consumeAlreadyVerified(it, note) consumeAlreadyVerified(it, note)
} }
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip09Deletions
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.verify
import com.vitorpamplona.quartz.utils.LargeCache import com.vitorpamplona.quartz.utils.LargeCache
class DeletionIndex { class DeletionIndex {
@@ -45,14 +46,24 @@ class DeletionIndex {
// Anything newer than the date should not be deleted. // Anything newer than the date should not be deleted.
private val deletedReferencesBefore = LargeCache<DeletionRequest, DeletionEvent>() private val deletedReferencesBefore = LargeCache<DeletionRequest, DeletionEvent>()
fun add(event: DeletionEvent): Boolean { fun add(
event: DeletionEvent,
wasVerified: Boolean,
): Boolean {
var atLeastOne = false var atLeastOne = false
var myWasVerified = wasVerified
event.tags.forEach { event.deleteEventIds().forEach { toDelete ->
if (it.size > 1 && (it[0] == "a" || it[0] == "e")) { if (add(toDelete, event.pubKey, event, myWasVerified)) {
if (add(it[1], event.pubKey, event)) { myWasVerified = true
atLeastOne = true atLeastOne = true
} }
}
event.deleteAddressIds().forEach { toDelete ->
if (add(toDelete, event.pubKey, event, myWasVerified)) {
myWasVerified = true
atLeastOne = true
} }
} }
@@ -63,13 +74,16 @@ class DeletionIndex {
ref: String, ref: String,
byPubKey: HexKey, byPubKey: HexKey,
deletionEvent: DeletionEvent, deletionEvent: DeletionEvent,
wasVerified: Boolean,
): Boolean { ): Boolean {
val key = DeletionRequest(ref, byPubKey) val key = DeletionRequest(ref, byPubKey)
val previousDeletionEvent = deletedReferencesBefore.get(key) val previousDeletionEvent = deletedReferencesBefore.get(key)
if (previousDeletionEvent == null || deletionEvent.createdAt > previousDeletionEvent.createdAt) { if (previousDeletionEvent == null || deletionEvent.createdAt > previousDeletionEvent.createdAt) {
deletedReferencesBefore.put(key, deletionEvent) if (wasVerified || deletionEvent.verify()) {
return true deletedReferencesBefore.put(key, deletionEvent)
return true
}
} }
return false return false
} }