From 06c726c543641dce7abf3c92dcf83ee806647210 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Mon, 6 Jul 2026 17:03:48 +0200 Subject: [PATCH] Rename to nostrGroupId and add MarmotCommitResult --- .../1.json | 182 +++++++++++++++++- .../at/torch/compose/database/dao/NostrDao.kt | 9 - .../compose/managers/MarmotInboundManager.kt | 13 +- 3 files changed, 186 insertions(+), 18 deletions(-) diff --git a/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json b/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json index e0cb0b45..00406868 100644 --- a/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json +++ b/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "d2e739e05b7ba033c4f8b240af7e386b", + "identityHash": "305e0b745bddf4352f8e34b9cbf11242", "entities": [ { "tableName": "BroadcastNostrEventReceipt", @@ -1152,6 +1152,104 @@ } ] }, + { + "tableName": "MarmotCommitResult", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `peerKeyPackageEventId` TEXT NOT NULL, `chatRoomId` TEXT NOT NULL, `isOneMemberInitialGroupCreation` INTEGER NOT NULL, `commitBytes` BLOB NOT NULL, `welcomeBytes` BLOB, `groupInfoBytes` BLOB, `framedCommitBytes` BLOB NOT NULL, `preCommitExporterSecret` BLOB NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userPublicKey", + "columnName": "userPublicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "peerKeyPackageEventId", + "columnName": "peerKeyPackageEventId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chatRoomId", + "columnName": "chatRoomId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isOneMemberInitialGroupCreation", + "columnName": "isOneMemberInitialGroupCreation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "commitBytes", + "columnName": "commitBytes", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "welcomeBytes", + "columnName": "welcomeBytes", + "affinity": "BLOB" + }, + { + "fieldPath": "groupInfoBytes", + "columnName": "groupInfoBytes", + "affinity": "BLOB" + }, + { + "fieldPath": "framedCommitBytes", + "columnName": "framedCommitBytes", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "preCommitExporterSecret", + "columnName": "preCommitExporterSecret", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewedAt", + "columnName": "viewedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, { "tableName": "MarmotGroupEvent", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `chatRoomId` TEXT NOT NULL, `encryptedContent` TEXT NOT NULL, `expiresAt` INTEGER, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))", @@ -1502,6 +1600,86 @@ } ] }, + { + "tableName": "MarmotRetainedEpochSecret", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`chatRoomId` TEXT NOT NULL, `epoch` INTEGER NOT NULL, `senderDataSecret` BLOB NOT NULL, `encryptionSecret` BLOB NOT NULL, `leafCount` INTEGER NOT NULL, `exporterSecret` BLOB NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, PRIMARY KEY(`chatRoomId`, `epoch`), FOREIGN KEY(`chatRoomId`) REFERENCES `ChatRoom`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "chatRoomId", + "columnName": "chatRoomId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "epoch", + "columnName": "epoch", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderDataSecret", + "columnName": "senderDataSecret", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "encryptionSecret", + "columnName": "encryptionSecret", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "leafCount", + "columnName": "leafCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exporterSecret", + "columnName": "exporterSecret", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "chatRoomId", + "epoch" + ] + }, + "foreignKeys": [ + { + "table": "ChatRoom", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "chatRoomId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, { "tableName": "Mention", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mentionedPublicKey` TEXT NOT NULL, `mentioningNostrEventId` TEXT, `relayUrl` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, FOREIGN KEY(`mentionedPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`mentioningNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", @@ -3261,7 +3439,7 @@ ], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd2e739e05b7ba033c4f8b240af7e386b')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '305e0b745bddf4352f8e34b9cbf11242')" ] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt index d9e36461..98d1ee0c 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt @@ -23,28 +23,19 @@ import at.torch.compose.exceptions.MarmotMissingChatGroupException import at.torch.compose.exceptions.MarmotMissingKeyPackageBundleException import at.torch.compose.exceptions.MarmotMissingNostrGroupDataExtension import at.torch.compose.exceptions.MarmotNotMemberOfChatGroupException -import at.torch.compose.exceptions.MarmotUnsupportedWireFormatException import at.torch.compose.exceptions.MarmotWelcomeEventMissingKeyPackageEventIdException -import at.torch.compose.extensions.exporterSecret import at.torch.compose.extensions.toHex import at.torch.compose.managers.MarmotInboundManager import co.touchlab.kermit.Logger -import com.vitorpamplona.quartz.marmot.GroupEventResult import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageEvent import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent import com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeEvent -import com.vitorpamplona.quartz.marmot.mip03GroupMessages.GroupEventEncryption import com.vitorpamplona.quartz.marmot.mls.codec.TlsReader -import com.vitorpamplona.quartz.marmot.mls.framing.ContentType -import com.vitorpamplona.quartz.marmot.mls.framing.MlsMessage -import com.vitorpamplona.quartz.marmot.mls.framing.PublicMessage -import com.vitorpamplona.quartz.marmot.mls.framing.WireFormat import com.vitorpamplona.quartz.marmot.mls.group.MlsGroup import com.vitorpamplona.quartz.marmot.mls.group.MlsGroupState import com.vitorpamplona.quartz.marmot.mls.messages.KeyPackageBundle import com.vitorpamplona.quartz.marmot.mls.messages.MlsKeyPackage import com.vitorpamplona.quartz.marmot.mls.tree.Credential -import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent 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 80287635..0d3c615f 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/managers/MarmotInboundManager.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/managers/MarmotInboundManager.kt @@ -1,7 +1,6 @@ package at.torch.compose.managers import at.torch.compose.database.TorchDatabase -import at.torch.compose.database.model.ChatMessage import at.torch.compose.database.model.intermdiate.LocalChatRoom import at.torch.compose.exceptions.MarmotMissingChatGroupException import at.torch.compose.exceptions.MarmotUnsupportedWireFormatException @@ -27,7 +26,6 @@ import com.vitorpamplona.quartz.marmot.mls.schedule.KeySchedule import com.vitorpamplona.quartz.marmot.mls.schedule.SecretTree import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair -import kotlin.time.Instant object MarmotInboundManager { private const val TAG = "MarmotInboundManager" @@ -268,23 +266,24 @@ object MarmotInboundManager { mlsGroup: MlsGroup, groupEvent: GroupEvent ): GroupEventResult { - val groupId = mlsGroup.currentMarmotData()?.nostrGroupId ?: mlsGroup.groupId.toHex() + logger.d("handleCommitEvent: ${groupEvent.id}") + val nostrGroupId = mlsGroup.currentMarmotData()?.nostrGroupId ?: mlsGroup.groupId.toHex() val currentEpoch = mlsGroup.epoch - commitTracker.addCommit(groupId, currentEpoch, groupEvent) + commitTracker.addCommit(nostrGroupId, currentEpoch, groupEvent) // If this is the only commit for this epoch, apply immediately - val pending = commitTracker.pendingForEpoch(groupId, currentEpoch) + val pending = commitTracker.pendingForEpoch(nostrGroupId, currentEpoch) return if (pending.size == 1) { val result = applyCommit( database, mlsGroup, groupEvent ) - commitTracker.clearEpoch(groupId, currentEpoch) + commitTracker.clearEpoch(nostrGroupId, currentEpoch) result } else { - GroupEventResult.CommitPending(groupId, currentEpoch) + GroupEventResult.CommitPending(nostrGroupId, currentEpoch) } }