From 6b3cf4b44a03ac807a1bbdeb12ab2e9effa22d56 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Mon, 6 Jul 2026 13:52:30 +0200 Subject: [PATCH] Add MarmotRetainedEpochSecret --- .../torch/compose/database/TorchDatabase.kt | 114 ++++++++++++------ .../compose/database/dao/MarmotOutboundDao.kt | 31 ++--- .../dao/MarmotRetainedEpochSecretDao.kt | 16 +++ .../model/MarmotRetainedEpochSecret.kt | 51 ++++++++ 4 files changed, 162 insertions(+), 50 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotRetainedEpochSecretDao.kt create mode 100644 composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotRetainedEpochSecret.kt diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/TorchDatabase.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/TorchDatabase.kt index 45fd1127..1194e8a5 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/TorchDatabase.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/TorchDatabase.kt @@ -5,11 +5,45 @@ import androidx.room3.RoomDatabase import androidx.room3.TypeConverters import androidx.room3.immediateTransaction import androidx.room3.useWriterConnection +import at.torch.compose.database.converters.TorchConverters +import at.torch.compose.database.dao.BroadcastNostrEventReceiptDao +import at.torch.compose.database.dao.BroadcastNostrEventRequestDao +import at.torch.compose.database.dao.ChatMessageBroadcastNostrEventReceiptRelationDao +import at.torch.compose.database.dao.ChatMessageBroadcastNostrEventRequestRelationDao +import at.torch.compose.database.dao.ChatMessageDao +import at.torch.compose.database.dao.ChatMessageNostrEventRelationDao +import at.torch.compose.database.dao.ChatRoomDao +import at.torch.compose.database.dao.ConnectionDao +import at.torch.compose.database.dao.GiftWrapMessageDao +import at.torch.compose.database.dao.GiftWrapPayloadDao +import at.torch.compose.database.dao.GiftWrapSealDao +import at.torch.compose.database.dao.InReplyToRelationDao +import at.torch.compose.database.dao.MarmotCommitResultDao import at.torch.compose.database.dao.MarmotOutboundDao import at.torch.compose.database.dao.MarmotGroupEventDao import at.torch.compose.database.dao.MarmotInnerEventDao import at.torch.compose.database.dao.MarmotKeyPackageBundleDao import at.torch.compose.database.dao.MarmotKeyPackageDao +import at.torch.compose.database.dao.MarmotRetainedEpochSecretDao +import at.torch.compose.database.dao.MentionDao +import at.torch.compose.database.dao.NegentropySynchronizeRequestDao +import at.torch.compose.database.dao.NegentropySynchronizeResultDao +import at.torch.compose.database.dao.NostrDao +import at.torch.compose.database.dao.NostrEventDao +import at.torch.compose.database.dao.NostrEventRelayDao +import at.torch.compose.database.dao.NostrNip17Dao +import at.torch.compose.database.dao.ParticipantDao +import at.torch.compose.database.dao.PostDao +import at.torch.compose.database.dao.ProfileDao +import at.torch.compose.database.dao.QuotedRelationDao +import at.torch.compose.database.dao.ReactionDao +import at.torch.compose.database.dao.RecentSearchDao +import at.torch.compose.database.dao.RelayDao +import at.torch.compose.database.dao.RepostedRelationDao +import at.torch.compose.database.dao.SynchronizeNostrEventRequestDao +import at.torch.compose.database.dao.SynchronizeNostrEventResultDao +import at.torch.compose.database.dao.UnsignedNostrEventDao +import at.torch.compose.database.dao.ZapDao import at.torch.compose.database.model.BroadcastNostrEventReceipt import at.torch.compose.database.model.BroadcastNostrEventRequest import at.torch.compose.database.model.ChatMessage @@ -22,10 +56,12 @@ import at.torch.compose.database.model.GiftWrapMessage import at.torch.compose.database.model.GiftWrapPayload import at.torch.compose.database.model.GiftWrapSeal import at.torch.compose.database.model.InReplyToRelation +import at.torch.compose.database.model.MarmotCommitResult import at.torch.compose.database.model.MarmotGroupEvent import at.torch.compose.database.model.MarmotInnerEvent import at.torch.compose.database.model.MarmotKeyPackage import at.torch.compose.database.model.MarmotKeyPackageBundle +import at.torch.compose.database.model.MarmotRetainedEpochSecret import at.torch.compose.database.model.Mention import at.torch.compose.database.model.NegentropySynchronizeRequest import at.torch.compose.database.model.NegentropySynchronizeResult @@ -61,10 +97,12 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) GiftWrapSeal::class, GiftWrapPayload::class, InReplyToRelation::class, + MarmotCommitResult::class, MarmotGroupEvent::class, MarmotInnerEvent::class, MarmotKeyPackage::class, MarmotKeyPackageBundle::class, + MarmotRetainedEpochSecret::class, Mention::class, NegentropySynchronizeRequest::class, NegentropySynchronizeResult::class, @@ -85,76 +123,82 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) ], version = 1 ) -@TypeConverters(at.torch.compose.database.converters.TorchConverters::class) +@TypeConverters(TorchConverters::class) abstract class TorchDatabase: RoomDatabase() { - abstract fun broadcastNostrEventReceiptDao(): at.torch.compose.database.dao.BroadcastNostrEventReceiptDao - abstract fun broadcastNostrEventRequestDao(): at.torch.compose.database.dao.BroadcastNostrEventRequestDao + abstract fun broadcastNostrEventReceiptDao(): BroadcastNostrEventReceiptDao + abstract fun broadcastNostrEventRequestDao(): BroadcastNostrEventRequestDao - abstract fun chatMessageDao(): at.torch.compose.database.dao.ChatMessageDao + abstract fun chatMessageDao(): ChatMessageDao - abstract fun chatMessageBroadcastNostrEventRequestRelationDao(): at.torch.compose.database.dao.ChatMessageBroadcastNostrEventRequestRelationDao + abstract fun chatMessageBroadcastNostrEventRequestRelationDao(): ChatMessageBroadcastNostrEventRequestRelationDao - abstract fun chatMessageBroadcastNostrEventReceiptRelationDao(): at.torch.compose.database.dao.ChatMessageBroadcastNostrEventReceiptRelationDao + abstract fun chatMessageBroadcastNostrEventReceiptRelationDao(): ChatMessageBroadcastNostrEventReceiptRelationDao - abstract fun chatMessageNostrEventRelationDao(): at.torch.compose.database.dao.ChatMessageNostrEventRelationDao + abstract fun chatMessageNostrEventRelationDao(): ChatMessageNostrEventRelationDao - abstract fun chatRoomDao(): at.torch.compose.database.dao.ChatRoomDao + abstract fun chatRoomDao(): ChatRoomDao - abstract fun connectionDao(): at.torch.compose.database.dao.ConnectionDao + abstract fun connectionDao(): ConnectionDao - abstract fun giftWrapMessageDao(): at.torch.compose.database.dao.GiftWrapMessageDao + abstract fun giftWrapMessageDao(): GiftWrapMessageDao - abstract fun giftWrapSealDao(): at.torch.compose.database.dao.GiftWrapSealDao + abstract fun giftWrapSealDao(): GiftWrapSealDao - abstract fun giftWrapPayloadDao(): at.torch.compose.database.dao.GiftWrapPayloadDao + abstract fun giftWrapPayloadDao(): GiftWrapPayloadDao - abstract fun inReplyToRelationDao(): at.torch.compose.database.dao.InReplyToRelationDao + abstract fun inReplyToRelationDao(): InReplyToRelationDao - abstract fun marmotOutboundDao(): MarmotOutboundDao + abstract fun marmotCommitResultDao(): MarmotCommitResultDao abstract fun marmotGroupEventDao(): MarmotGroupEventDao - abstract fun marmotInnerEventDao(): MarmotInnerEventDao - abstract fun marmotKeyPackageDao(): MarmotKeyPackageDao + abstract fun marmotInnerEventDao(): MarmotInnerEventDao abstract fun marmotKeyPackageBundleDao(): MarmotKeyPackageBundleDao - abstract fun mentionDao(): at.torch.compose.database.dao.MentionDao + abstract fun marmotKeyPackageDao(): MarmotKeyPackageDao - abstract fun negentropySynchronizeRequestDao(): at.torch.compose.database.dao.NegentropySynchronizeRequestDao + abstract fun marmotOutboundDao(): MarmotOutboundDao - abstract fun negentropySynchronizeResultDao(): at.torch.compose.database.dao.NegentropySynchronizeResultDao + abstract fun marmotRetainedEpochSecret(): MarmotRetainedEpochSecretDao - abstract fun nostrDao(): at.torch.compose.database.dao.NostrDao - abstract fun nostrEventDao(): at.torch.compose.database.dao.NostrEventDao + abstract fun mentionDao(): MentionDao - abstract fun nostrEventRelayDao(): at.torch.compose.database.dao.NostrEventRelayDao + abstract fun negentropySynchronizeRequestDao(): NegentropySynchronizeRequestDao - abstract fun nostrNip17Dao(): at.torch.compose.database.dao.NostrNip17Dao + abstract fun negentropySynchronizeResultDao(): NegentropySynchronizeResultDao - abstract fun participantDao(): at.torch.compose.database.dao.ParticipantDao + abstract fun nostrDao(): NostrDao - abstract fun postDao(): at.torch.compose.database.dao.PostDao + abstract fun nostrEventDao(): NostrEventDao - abstract fun profileDao(): at.torch.compose.database.dao.ProfileDao + abstract fun nostrEventRelayDao(): NostrEventRelayDao - abstract fun quotedRelationDao(): at.torch.compose.database.dao.QuotedRelationDao + abstract fun nostrNip17Dao(): NostrNip17Dao - abstract fun reactionDao(): at.torch.compose.database.dao.ReactionDao + abstract fun participantDao(): ParticipantDao - abstract fun recentSearchDao(): at.torch.compose.database.dao.RecentSearchDao + abstract fun postDao(): PostDao - abstract fun relayDao(): at.torch.compose.database.dao.RelayDao + abstract fun profileDao(): ProfileDao - abstract fun repostedRelationDao(): at.torch.compose.database.dao.RepostedRelationDao + abstract fun quotedRelationDao(): QuotedRelationDao - abstract fun synchronizeNostrEventRequestDao(): at.torch.compose.database.dao.SynchronizeNostrEventRequestDao + abstract fun reactionDao(): ReactionDao - abstract fun synchronizeNostrEventResultDao(): at.torch.compose.database.dao.SynchronizeNostrEventResultDao + abstract fun recentSearchDao(): RecentSearchDao - abstract fun unsignedNostrEventDao(): at.torch.compose.database.dao.UnsignedNostrEventDao - abstract fun zapDao(): at.torch.compose.database.dao.ZapDao + abstract fun relayDao(): RelayDao + + abstract fun repostedRelationDao(): RepostedRelationDao + + abstract fun synchronizeNostrEventRequestDao(): SynchronizeNostrEventRequestDao + + abstract fun synchronizeNostrEventResultDao(): SynchronizeNostrEventResultDao + + abstract fun unsignedNostrEventDao(): UnsignedNostrEventDao + abstract fun zapDao(): ZapDao suspend fun wipeData() { useWriterConnection { transactor -> 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 630f7b84..25d61813 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 @@ -130,6 +130,7 @@ abstract class MarmotOutboundDao( peerKeyPackage.tlsEncodedMarmotKeyPackage ) + // pushRetainedEpoch(nostrGroupId, retainedBefore) // persistGroup(nostrGroupId) @@ -161,21 +162,21 @@ abstract class MarmotOutboundDao( ) ) - // TODO: Save commitResult... in case we need to broadcast welcomeEvent after relay acknowledgement... -// database.marmotCommitResultDao().upsert( -// MarmotCommitResult( -// id = commitEvent.id, -// isOneMemberInitialGroupCreation = isOneMemberInitialGroupCreation, -// chatRoomId = groupId, -// commitBytes = commitResult.commitBytes, -// preCommitExporterSecret = commitResult.preCommitExporterSecret, -// welcomeBytes = commitResult.welcomeBytes, -// framedCommitBytes = commitResult.preCommitExporterSecret, -// groupInfoBytes = commitResult.groupInfoBytes, -// userPublicKey = userPublicKey, -// createdAt = Instant.fromEpochSeconds(commitEvent.createdAt) -// ) -// ) + // Save commitResult... in case we need to broadcast welcomeEvent after relay acknowledgement... + database.marmotCommitResultDao().upsert( + MarmotCommitResult( + id = commitEvent.id, + isOneMemberInitialGroupCreation = isOneMemberInitialGroupCreation, + chatRoomId = groupId, + commitBytes = commitResult.commitBytes, + preCommitExporterSecret = commitResult.preCommitExporterSecret, + welcomeBytes = commitResult.welcomeBytes, + framedCommitBytes = commitResult.preCommitExporterSecret, + groupInfoBytes = commitResult.groupInfoBytes, + userPublicKey = userPublicKey, + createdAt = Instant.fromEpochSeconds(commitEvent.createdAt) + ) + ) database.broadcastNostrEventRequestDao().insert( relays.map { diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotRetainedEpochSecretDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotRetainedEpochSecretDao.kt new file mode 100644 index 00000000..342b888b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotRetainedEpochSecretDao.kt @@ -0,0 +1,16 @@ +package at.torch.compose.database.dao + +import androidx.room3.Dao +import androidx.room3.Query +import androidx.room3.Upsert +import at.torch.compose.database.model.MarmotCommitResult +import at.torch.compose.database.model.MarmotKeyPackage +import at.torch.compose.database.model.MarmotRetainedEpochSecret +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import fr.acinq.bitcoin.PublicKey + +@Dao +interface MarmotRetainedEpochSecretDao { + @Upsert + suspend fun upsert(marmotRetainedEpochSecret: MarmotRetainedEpochSecret) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotRetainedEpochSecret.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotRetainedEpochSecret.kt new file mode 100644 index 00000000..a757227d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotRetainedEpochSecret.kt @@ -0,0 +1,51 @@ +package at.torch.compose.database.model + +import androidx.room3.Entity +import androidx.room3.ForeignKey + +@Entity( + foreignKeys = [ + ForeignKey( + entity = ChatRoom::class, + parentColumns = ["id"], + childColumns = ["chatRoomId"], + onDelete = ForeignKey.CASCADE, + ), + ], + primaryKeys = ["chatRoomId", "epoch"] +) +data class MarmotRetainedEpochSecret( + val chatRoomId: String, + val epoch: Long, + val senderDataSecret: ByteArray, // TODO: Encrypt this... + val encryptionSecret: ByteArray, // TODO: Encrypt this... + val leafCount: Int, + val exporterSecret: ByteArray = ByteArray(0), // TODO: Encrypt this... +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as MarmotRetainedEpochSecret + + if (epoch != other.epoch) return false + if (leafCount != other.leafCount) return false + if (chatRoomId != other.chatRoomId) return false + if (!senderDataSecret.contentEquals(other.senderDataSecret)) return false + if (!encryptionSecret.contentEquals(other.encryptionSecret)) return false + if (!exporterSecret.contentEquals(other.exporterSecret)) return false + + return true + } + + override fun hashCode(): Int { + var result = epoch.hashCode() + result = 31 * result + leafCount + result = 31 * result + chatRoomId.hashCode() + result = 31 * result + senderDataSecret.contentHashCode() + result = 31 * result + encryptionSecret.contentHashCode() + result = 31 * result + exporterSecret.contentHashCode() + return result + } + +} \ No newline at end of file