Rename to nostrGroupId and add MarmotCommitResult

This commit is contained in:
Kgothatso Ngako
2026-07-06 17:03:48 +02:00
parent bac2b783ee
commit 06c726c543
3 changed files with 186 additions and 18 deletions

View File

@@ -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')"
]
}
}

View File

@@ -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

View File

@@ -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)
}
}