diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt index c3859d88..52301f6a 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt @@ -327,6 +327,7 @@ abstract class MarmotOutboundDao( // Step 2: Outer ChaCha20-Poly1305 encryption val exportKey = mlsGroup.exporterSecret() + logger.d("exporterKey: ${exportKey.toHex()}") val encryptedContent = GroupEventEncryption.encrypt( mlsMessageBytes = mlsCipherText, groupKey = exportKey diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/managers/MarmotInboundManager.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/managers/MarmotInboundManager.kt index fc681d26..d9fdc544 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/managers/MarmotInboundManager.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/managers/MarmotInboundManager.kt @@ -129,6 +129,7 @@ object MarmotInboundManager { mlsGroup, mlsMessage.toTlsBytes() ) + logger.d("decrypted: $decrypted") val innerJson = decrypted.content.decodeToString() // MIP-03: if the inner application payload is a Nostr event, @@ -465,12 +466,14 @@ object MarmotInboundManager { // Try current epoch key first try { val exporterKey = mlsGroup.exporterSecret() + logger.d("exporterKey: ${exporterKey.toHex()}") return GroupEventEncryption.decrypt(encryptedContent, exporterKey) } catch (_: Exception) { // Current epoch key failed — try retained epoch keys } // Try retained epoch exporter keys (most recent first) + logger.d("Try retained") val retainedKeys = retainedExporterSecrets( nostrGroupId = mlsGroup.currentMarmotData()?.nostrGroupId ?: mlsGroup.groupId.toHex() ) @@ -492,7 +495,6 @@ object MarmotInboundManager { val nostrGroupId = mlsGroup.currentMarmotData()?.nostrGroupId ?: mlsGroup.groupId.toHex() try { - val state = mlsGroup.saveState() val encoded = state.encodeTls() logger.d { "persistGroup($nostrGroupId): serialized ${encoded.size} bytes, calling store.save" } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/nostr/Relays.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/nostr/Relays.kt index 25f636a7..0fed7409 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/nostr/Relays.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/nostr/Relays.kt @@ -51,7 +51,7 @@ object Relays { val DefaultGlobalRelays = listOf(wine, news) - val DefaultDMRelayList = listOf(damus, primal, nos) + val DefaultDMRelayList = listOf(damus, nos) val DefaultSearchRelayList = setOf(wine, where, nostoday, antiprimal, ditto) diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/repository/NostrRepository.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/repository/NostrRepository.kt index 93b9622e..64ce8dad 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/repository/NostrRepository.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/repository/NostrRepository.kt @@ -1,6 +1,21 @@ package at.torch.compose.repository +import at.torch.compose.database.model.BroadcastNostrEventReceipt +import at.torch.compose.database.model.BroadcastNostrEventRequest +import at.torch.compose.database.model.Connection +import at.torch.compose.database.model.NegentropySynchronizeRequest +import at.torch.compose.database.model.NostrEvent +import at.torch.compose.database.model.Profile +import at.torch.compose.database.model.RecentSearch +import at.torch.compose.database.model.SynchronizeNostrEventRequest +import at.torch.compose.database.model.UnsignedNostrEvent import at.torch.compose.database.model.intermdiate.LocalAccount +import at.torch.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest +import at.torch.compose.database.model.intermdiate.LocalNostrEvent +import at.torch.compose.database.model.intermdiate.LocalProfile +import at.torch.compose.database.model.intermdiate.LocalProfileWithFollowers +import at.torch.compose.database.model.intermdiate.LocalProfileWithFollowing +import at.torch.compose.database.model.types.SynchronizationFilter import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair @@ -9,19 +24,19 @@ import kotlinx.coroutines.flow.Flow interface NostrRepository { suspend fun getLocalAccounts(): List - suspend fun observeProfile(publicKey: HexKey): Flow + suspend fun observeProfile(publicKey: HexKey): Flow - suspend fun observeRelation(publicKey: HexKey, activePublicKey: HexKey): Flow> + suspend fun observeRelation(publicKey: HexKey, activePublicKey: HexKey): Flow> - suspend fun deleteConnection(connection: at.torch.compose.database.model.Connection) + suspend fun deleteConnection(connection: Connection) - suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow + suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow - suspend fun observePendingBroadcastNostrEventRequests(): Flow> + suspend fun observePendingBroadcastNostrEventRequests(): Flow> - suspend fun observePendingSynchronizeNostrEventRequests(): Flow + suspend fun observePendingSynchronizeNostrEventRequests(): Flow - suspend fun observePendingNegentropySynchronizeRequests(): Flow + suspend fun observePendingNegentropySynchronizeRequests(): Flow suspend fun createNewProfile( publicKey: HexKey, @@ -37,89 +52,89 @@ interface NostrRepository { publicKey: HexKey, mentionedPublicKeys: List, textInput: String, - inReplyToNostrEvent: at.torch.compose.database.model.intermdiate.LocalNostrEvent?, - quotedNostrEvent: at.torch.compose.database.model.intermdiate.LocalNostrEvent?, + inReplyToNostrEvent: LocalNostrEvent?, + quotedNostrEvent: LocalNostrEvent?, onError: () -> Unit, onSuccess: () -> Unit ) - suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: at.torch.compose.database.model.UnsignedNostrEvent): at.torch.compose.database.model.UnsignedNostrEvent? + suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? - suspend fun saveUnsignedNostrEvents(unsignedNostrEvents: List) + suspend fun saveUnsignedNostrEvents(unsignedNostrEvents: List) suspend fun wipeDatabase() suspend fun publishNostrEvent( - unsignedNostrEvent: at.torch.compose.database.model.UnsignedNostrEvent, - nostrEvent: at.torch.compose.database.model.NostrEvent, + unsignedNostrEvent: UnsignedNostrEvent, + nostrEvent: NostrEvent, relayURLs: List = emptyList(), activeKeyPair: KeyPair ) - suspend fun broadcastProcessed(broadcastNostrEventRequest: at.torch.compose.database.model.BroadcastNostrEventRequest, status: String = "processing") + suspend fun broadcastProcessed(broadcastNostrEventRequest: BroadcastNostrEventRequest, status: String = "processing") - suspend fun synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest: at.torch.compose.database.model.SynchronizeNostrEventRequest) + suspend fun synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest: SynchronizeNostrEventRequest) - suspend fun negentropySynchronizeRequestProcessed(negentropySynchronizeRequest: at.torch.compose.database.model.NegentropySynchronizeRequest) + suspend fun negentropySynchronizeRequestProcessed(negentropySynchronizeRequest: NegentropySynchronizeRequest) - suspend fun saveBroadcastReceipt(broadcastNostrEventReceipt: at.torch.compose.database.model.BroadcastNostrEventReceipt) + suspend fun saveBroadcastReceipt(broadcastNostrEventReceipt: BroadcastNostrEventReceipt) /** * Save nostrEvent from relays (and index for local viewing)... */ suspend fun saveNostrEvent( - nostrEvent: at.torch.compose.database.model.NostrEvent, - synchronizeNostrEventRequest: at.torch.compose.database.model.SynchronizeNostrEventRequest, + nostrEvent: NostrEvent, + synchronizeNostrEventRequest: SynchronizeNostrEventRequest, synchronizationRelayURLs: List, activeKeyPair: KeyPair ) suspend fun saveNostrEvent( - nostrEvent: at.torch.compose.database.model.NostrEvent, - negentropySynchronizeRequest: at.torch.compose.database.model.NegentropySynchronizeRequest, + nostrEvent: NostrEvent, + negentropySynchronizeRequest: NegentropySynchronizeRequest, synchronizationRelayURLs: List, activeKeyPair: KeyPair ) suspend fun queueSynchronizeNostrEvent( - synchronizeNostrEventRequests: List, + synchronizeNostrEventRequests: List, ) suspend fun queueNegentropySynchronizeRequest( - negentropySynchronizeRequests: List + negentropySynchronizeRequests: List ) suspend fun observeSynchronizeNostrEventRequestByPurposeAndStatusCount(purpose: String): Flow - suspend fun getNostrEvent(publicKey: HexKey, kind: Kind): at.torch.compose.database.model.NostrEvent? + suspend fun getNostrEvent(publicKey: HexKey, kind: Kind): NostrEvent? - suspend fun observeNostrFeed(): Flow> + suspend fun observeNostrFeed(): Flow> - suspend fun observeNostrFeed(synchronizationFilter: at.torch.compose.database.model.types.SynchronizationFilter): Flow> + suspend fun observeNostrFeed(synchronizationFilter: SynchronizationFilter): Flow> suspend fun getNegentropicNostrFeedIds( - synchronizationFilters: Array, + synchronizationFilters: Array, applyLimits: Boolean = true - ): List + ): List - suspend fun observeLocalNostrEventById(nostrEventId: String): Flow + suspend fun observeLocalNostrEventById(nostrEventId: String): Flow - suspend fun searchableProfiles(): List + suspend fun searchableProfiles(): List - suspend fun filterProfiles(searchTerm: String): List + suspend fun filterProfiles(searchTerm: String): List - suspend fun observeProfileWithFollowers(publicKey: String): Flow + suspend fun observeProfileWithFollowers(publicKey: String): Flow - suspend fun observeProfileWithFollowing(publicKey: String): Flow + suspend fun observeProfileWithFollowing(publicKey: String): Flow - suspend fun getRecentSearches(): List + suspend fun getRecentSearches(): List - suspend fun removeRecentSearch(recentSearch: at.torch.compose.database.model.RecentSearch) + suspend fun removeRecentSearch(recentSearch: RecentSearch) suspend fun saveSearchQuery(query: String, synchronizationFilterArray: at.torch.compose.database.model.typealiases.SynchronizationFilterArray? = null) - suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List) + suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List) companion object { val NO_OP_NOSTR_REPOSITORY = object : NostrRepository { @@ -127,34 +142,34 @@ interface NostrRepository { TODO("Not yet implemented") } - override suspend fun observeProfile(publicKey: HexKey): Flow { + override suspend fun observeProfile(publicKey: HexKey): Flow { TODO("Not yet implemented") } override suspend fun observeRelation( publicKey: HexKey, activePublicKey: HexKey - ): Flow> { + ): Flow> { TODO("Not yet implemented") } - override suspend fun deleteConnection(connection: at.torch.compose.database.model.Connection) { + override suspend fun deleteConnection(connection: Connection) { TODO("Not yet implemented") } - override suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow { + override suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow { TODO("Not yet implemented") } - override suspend fun observePendingBroadcastNostrEventRequests(): Flow> { + override suspend fun observePendingBroadcastNostrEventRequests(): Flow> { TODO("Not yet implemented") } - override suspend fun observePendingSynchronizeNostrEventRequests(): Flow { + override suspend fun observePendingSynchronizeNostrEventRequests(): Flow { TODO("Not yet implemented") } - override suspend fun observePendingNegentropySynchronizeRequests(): Flow { + override suspend fun observePendingNegentropySynchronizeRequests(): Flow { TODO("Not yet implemented") } @@ -174,19 +189,19 @@ interface NostrRepository { publicKey: HexKey, mentionedPublicKeys: List, textInput: String, - inReplyToNostrEvent: at.torch.compose.database.model.intermdiate.LocalNostrEvent?, - quotedNostrEvent: at.torch.compose.database.model.intermdiate.LocalNostrEvent?, + inReplyToNostrEvent: LocalNostrEvent?, + quotedNostrEvent: LocalNostrEvent?, onError: () -> Unit, onSuccess: () -> Unit ) { TODO("Not yet implemented") } - override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: at.torch.compose.database.model.UnsignedNostrEvent): at.torch.compose.database.model.UnsignedNostrEvent? { + override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? { return null } - override suspend fun saveUnsignedNostrEvents(unsignedNostrEvents: List) { + override suspend fun saveUnsignedNostrEvents(unsignedNostrEvents: List) { TODO("Not yet implemented") } @@ -194,8 +209,8 @@ interface NostrRepository { } override suspend fun publishNostrEvent( - unsignedNostrEvent: at.torch.compose.database.model.UnsignedNostrEvent, - nostrEvent: at.torch.compose.database.model.NostrEvent, + unsignedNostrEvent: UnsignedNostrEvent, + nostrEvent: NostrEvent, relayURLs: List, activeKeyPair: KeyPair ) { @@ -203,27 +218,27 @@ interface NostrRepository { } override suspend fun broadcastProcessed( - broadcastNostrEventRequest: at.torch.compose.database.model.BroadcastNostrEventRequest, + broadcastNostrEventRequest: BroadcastNostrEventRequest, status: String ) { TODO("Not yet implemented") } - override suspend fun synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest: at.torch.compose.database.model.SynchronizeNostrEventRequest) { + override suspend fun synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest: SynchronizeNostrEventRequest) { TODO("Not yet implemented") } - override suspend fun negentropySynchronizeRequestProcessed(negentropySynchronizeRequest: at.torch.compose.database.model.NegentropySynchronizeRequest) { + override suspend fun negentropySynchronizeRequestProcessed(negentropySynchronizeRequest: NegentropySynchronizeRequest) { TODO("Not yet implemented") } - override suspend fun saveBroadcastReceipt(broadcastNostrEventReceipt: at.torch.compose.database.model.BroadcastNostrEventReceipt) { + override suspend fun saveBroadcastReceipt(broadcastNostrEventReceipt: BroadcastNostrEventReceipt) { TODO("Not yet implemented") } override suspend fun saveNostrEvent( - nostrEvent: at.torch.compose.database.model.NostrEvent, - synchronizeNostrEventRequest: at.torch.compose.database.model.SynchronizeNostrEventRequest, + nostrEvent: NostrEvent, + synchronizeNostrEventRequest: SynchronizeNostrEventRequest, synchronizationRelayURLs: List, activeKeyPair: KeyPair ) { @@ -231,19 +246,19 @@ interface NostrRepository { } override suspend fun saveNostrEvent( - nostrEvent: at.torch.compose.database.model.NostrEvent, - negentropySynchronizeRequest: at.torch.compose.database.model.NegentropySynchronizeRequest, + nostrEvent: NostrEvent, + negentropySynchronizeRequest: NegentropySynchronizeRequest, synchronizationRelayURLs: List, activeKeyPair: KeyPair ) { TODO("Not yet implemented") } - override suspend fun queueSynchronizeNostrEvent(synchronizeNostrEventRequests: List) { + override suspend fun queueSynchronizeNostrEvent(synchronizeNostrEventRequests: List) { TODO("Not yet implemented") } - override suspend fun queueNegentropySynchronizeRequest(negentropySynchronizeRequests: List) { + override suspend fun queueNegentropySynchronizeRequest(negentropySynchronizeRequests: List) { TODO("Not yet implemented") } @@ -254,50 +269,50 @@ interface NostrRepository { override suspend fun getNostrEvent( publicKey: HexKey, kind: Kind - ): at.torch.compose.database.model.NostrEvent? { + ): NostrEvent? { TODO("Not yet implemented") } - override suspend fun observeNostrFeed(): Flow> { + override suspend fun observeNostrFeed(): Flow> { TODO("Not yet implemented") } - override suspend fun observeNostrFeed(synchronizationFilter: at.torch.compose.database.model.types.SynchronizationFilter): Flow> { + override suspend fun observeNostrFeed(synchronizationFilter: SynchronizationFilter): Flow> { TODO("Not yet implemented") } override suspend fun getNegentropicNostrFeedIds( - synchronizationFilters: Array, + synchronizationFilters: Array, applyLimits: Boolean - ): List { + ): List { TODO("Not yet implemented") } - override suspend fun observeLocalNostrEventById(nostrEventId: String): Flow { + override suspend fun observeLocalNostrEventById(nostrEventId: String): Flow { TODO("Not yet implemented") } - override suspend fun searchableProfiles(): List { + override suspend fun searchableProfiles(): List { TODO("Not yet implemented") } - override suspend fun filterProfiles(searchTerm: String): List { + override suspend fun filterProfiles(searchTerm: String): List { TODO("Not yet implemented") } - override suspend fun observeProfileWithFollowers(publicKey: String): Flow { + override suspend fun observeProfileWithFollowers(publicKey: String): Flow { TODO("Not yet implemented") } - override suspend fun observeProfileWithFollowing(publicKey: String): Flow { + override suspend fun observeProfileWithFollowing(publicKey: String): Flow { TODO("Not yet implemented") } - override suspend fun getRecentSearches(): List { + override suspend fun getRecentSearches(): List { TODO("Not yet implemented") } - override suspend fun removeRecentSearch(recentSearch: at.torch.compose.database.model.RecentSearch) { + override suspend fun removeRecentSearch(recentSearch: RecentSearch) { TODO("Not yet implemented") } @@ -308,7 +323,7 @@ interface NostrRepository { TODO("Not yet implemented") } - override suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List) { + override suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List) { TODO("Not yet implemented") } } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt index ae60f481..4dcd30e4 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt @@ -4,9 +4,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory +import at.torch.compose.database.model.BroadcastNostrEventRequest import at.torch.compose.database.model.SynchronizeNostrEventRequest import at.torch.compose.database.model.types.SynchronizationFilter import at.torch.compose.network.dto.toRelayDTO +import at.torch.compose.network.relays.RelayPool import at.torch.compose.network.relays.RelaysSocketManager import at.torch.compose.network.sockets.NostrIncomingMessage import at.torch.compose.network.sockets.NostrSocketClientFactory @@ -329,7 +331,7 @@ class SynchronizationViewModel( } val eventIds = events.map { it.id } val broadcastNostrEventRequests = result.sendIds.filter { it.toHexString() in eventIds }.map { sendId -> - at.torch.compose.database.model.BroadcastNostrEventRequest( + BroadcastNostrEventRequest( nostrEventId = sendId.toHexString(), relayURL = negentropySynchronizeRequest.relayURL ) @@ -396,7 +398,7 @@ class SynchronizationViewModel( scope.launch(Dispatchers.IO) { relaysSocketManager.publishEvent( localBroadcastNostrEventRequest.nostrEvent - ).timeout(at.torch.compose.network.relays.RelayPool.PUBLISH_TIMEOUT.milliseconds).catch { + ).timeout(RelayPool.PUBLISH_TIMEOUT.milliseconds).catch { // Timeout... }.collect { nostrPublishResult -> if (nostrPublishResult.error != null) { @@ -423,7 +425,7 @@ class SynchronizationViewModel( setOf( localBroadcastNostrEventRequest.broadcastNostrEventRequest.relayURL.toRelayDTO() ) - ).timeout(at.torch.compose.network.relays.RelayPool.PUBLISH_TIMEOUT.milliseconds).catch { + ).timeout(RelayPool.PUBLISH_TIMEOUT.milliseconds).catch { // Timeout... }.collect { nostrPublishResult -> if (nostrPublishResult.error != null) {