Handle retained secrets
This commit is contained in:
@@ -160,8 +160,7 @@ abstract class TorchDatabase: RoomDatabase() {
|
||||
|
||||
abstract fun marmotOutboundDao(): MarmotOutboundDao
|
||||
|
||||
abstract fun marmotRetainedEpochSecret(): MarmotRetainedEpochSecretDao
|
||||
|
||||
abstract fun marmotRetainedEpochSecretDao(): MarmotRetainedEpochSecretDao
|
||||
|
||||
abstract fun mentionDao(): MentionDao
|
||||
|
||||
|
||||
@@ -14,10 +14,13 @@ import at.torch.compose.database.model.MarmotGroupEvent
|
||||
import at.torch.compose.database.model.MarmotInnerEvent
|
||||
import at.torch.compose.database.model.MarmotInnerEvent.Companion.disappearingExpiration
|
||||
import at.torch.compose.database.model.MarmotKeyPackage
|
||||
import at.torch.compose.database.model.MarmotRetainedEpochSecret
|
||||
import at.torch.compose.database.model.NostrEvent
|
||||
import at.torch.compose.database.model.Participant
|
||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||
import at.torch.compose.extensions.exporterSecret
|
||||
import at.torch.compose.extensions.toHex
|
||||
import at.torch.compose.managers.MarmotInboundManager.EPOCH_RETENTION_WINDOW
|
||||
import at.torch.compose.nostr.Relays
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.marmot.mip01Groups.MarmotGroupData
|
||||
@@ -56,12 +59,12 @@ abstract class MarmotOutboundDao(
|
||||
peerKeyPackage: MarmotKeyPackage,
|
||||
relays: List<String> = Relays.DefaultDMRelayList.map { it.url }
|
||||
): String {
|
||||
val groupId = RandomInstance.bytes(32).toHexKey()
|
||||
val nostrGroupId = RandomInstance.bytes(32).toHexKey()
|
||||
val mlsGroup = MlsGroup.create(
|
||||
identity = userPublicKey.hexToByteArray(),
|
||||
initialExtensions = listOf(
|
||||
MarmotGroupData.bootstrap(
|
||||
nostrGroupId = groupId,
|
||||
nostrGroupId = nostrGroupId,
|
||||
creatorPubKey = userPublicKey,
|
||||
outboxRelays = relays,
|
||||
).toExtension()
|
||||
@@ -70,7 +73,7 @@ abstract class MarmotOutboundDao(
|
||||
|
||||
// Save Chat Room
|
||||
val chatRoom = ChatRoom(
|
||||
id = groupId,
|
||||
id = nostrGroupId,
|
||||
userPublicKey = userPublicKey,
|
||||
mlsGroupState = mlsGroup.saveState().encodeTls().toHex(),
|
||||
subject = null,
|
||||
@@ -79,11 +82,25 @@ abstract class MarmotOutboundDao(
|
||||
chatRoom
|
||||
)
|
||||
|
||||
// TODO: Save Participants...
|
||||
// Save Participants...
|
||||
database.participantDao().upsert(
|
||||
listOf(
|
||||
Participant(
|
||||
participantPublicKey = userPublicKey,
|
||||
chatRoomId = chatRoom.id,
|
||||
relayHint = relays.first()
|
||||
),
|
||||
Participant(
|
||||
participantPublicKey = peerPublicKey,
|
||||
chatRoomId = chatRoom.id,
|
||||
relayHint = relays.first()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Invite peer...
|
||||
inviteMember(
|
||||
groupId = groupId,
|
||||
nostrGroupId = nostrGroupId,
|
||||
mlsGroup = mlsGroup,
|
||||
userPublicKey = userPublicKey,
|
||||
peerPublicKey = peerPublicKey,
|
||||
@@ -99,11 +116,11 @@ abstract class MarmotOutboundDao(
|
||||
)
|
||||
)
|
||||
|
||||
return groupId
|
||||
return nostrGroupId
|
||||
}
|
||||
|
||||
private suspend fun inviteMember(
|
||||
groupId: HexKey,
|
||||
nostrGroupId: HexKey,
|
||||
mlsGroup: MlsGroup,
|
||||
userPublicKey: HexKey,
|
||||
peerPublicKey: HexKey,
|
||||
@@ -130,9 +147,25 @@ abstract class MarmotOutboundDao(
|
||||
peerKeyPackage.tlsEncodedMarmotKeyPackage
|
||||
)
|
||||
|
||||
database.marmotRetainedEpochSecretDao().insert(
|
||||
MarmotRetainedEpochSecret(
|
||||
chatRoomId = nostrGroupId,
|
||||
epoch = retainedBefore.epoch,
|
||||
senderDataSecret = retainedBefore.senderDataSecret,
|
||||
encryptionSecret = retainedBefore.encryptionSecret,
|
||||
leafCount = retainedBefore.leafCount,
|
||||
exporterSecret = retainedBefore.exporterSecret,
|
||||
)
|
||||
)
|
||||
|
||||
// pushRetainedEpoch(nostrGroupId, retainedBefore)
|
||||
// persistGroup(nostrGroupId)
|
||||
val defenestratableMarmotRetainedEpochSecrets = database.marmotRetainedEpochSecretDao()
|
||||
.defenestratableMarmotRetainedEpochSecret(
|
||||
epochCutOffPoint = retainedBefore.epoch - EPOCH_RETENTION_WINDOW.toLong()
|
||||
)
|
||||
logger.d("Defenestratable Retained Epochs: ${defenestratableMarmotRetainedEpochSecrets.map { it.epoch }}")
|
||||
database.marmotRetainedEpochSecretDao().defenestrate(
|
||||
defenestratableMarmotRetainedEpochSecrets
|
||||
)
|
||||
|
||||
// CommitEvent...
|
||||
val encryptedContent = encryptedCommitEvent(
|
||||
@@ -143,13 +176,12 @@ abstract class MarmotOutboundDao(
|
||||
|
||||
val template = GroupEvent.build(
|
||||
encryptedContentBase64 = encryptedContent,
|
||||
nostrGroupId = groupId
|
||||
nostrGroupId = nostrGroupId
|
||||
)
|
||||
|
||||
val ephemeralSigner = NostrSignerInternal(KeyPair())
|
||||
val commitEvent: GroupEvent = ephemeralSigner.sign(template)
|
||||
|
||||
|
||||
database.nostrEventDao().upsert(
|
||||
NostrEvent(
|
||||
id = commitEvent.id,
|
||||
@@ -167,7 +199,7 @@ abstract class MarmotOutboundDao(
|
||||
MarmotCommitResult(
|
||||
id = commitEvent.id,
|
||||
isOneMemberInitialGroupCreation = isOneMemberInitialGroupCreation,
|
||||
chatRoomId = groupId,
|
||||
chatRoomId = nostrGroupId,
|
||||
commitBytes = commitResult.commitBytes,
|
||||
preCommitExporterSecret = commitResult.preCommitExporterSecret,
|
||||
welcomeBytes = commitResult.welcomeBytes,
|
||||
@@ -199,7 +231,7 @@ abstract class MarmotOutboundDao(
|
||||
welcomeBase64 = welcomeBase64,
|
||||
keyPackageEventId = peerKeyPackage.id,
|
||||
relays = relays.map { NormalizedRelayUrl(it) },
|
||||
nostrGroupId = groupId,
|
||||
nostrGroupId = nostrGroupId,
|
||||
)
|
||||
// Compute welcomeEventId
|
||||
val welcomeEventId = EventHasher.hashId(
|
||||
@@ -213,7 +245,7 @@ abstract class MarmotOutboundDao(
|
||||
GiftWrapPayload(
|
||||
id = welcomeEventId,
|
||||
publicKey = userPublicKey,
|
||||
chatRoomId = groupId,
|
||||
chatRoomId = nostrGroupId,
|
||||
kind = welcomeEvent.kind,
|
||||
tags = welcomeEvent.tags,
|
||||
content = welcomeEvent.content,
|
||||
@@ -224,7 +256,7 @@ abstract class MarmotOutboundDao(
|
||||
database.chatMessageDao().upsert(
|
||||
ChatMessage(
|
||||
content = "Invited participant to chat", // use profile.humanReadable...
|
||||
chatRoomId = groupId,
|
||||
chatRoomId = nostrGroupId,
|
||||
senderPublicKey = userPublicKey,
|
||||
isUserMessage = true, // TODO: This is information message...
|
||||
giftWrapPayloadId = welcomeEventId,
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
package at.torch.compose.database.dao
|
||||
|
||||
import androidx.room3.Dao
|
||||
import androidx.room3.Delete
|
||||
import androidx.room3.Insert
|
||||
import androidx.room3.OnConflictStrategy.Companion.IGNORE
|
||||
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)
|
||||
@Query("SELECT * FROM MarmotRetainedEpochSecret WHERE epoch < :epochCutOffPoint")
|
||||
suspend fun defenestratableMarmotRetainedEpochSecret(epochCutOffPoint: Long): List<MarmotRetainedEpochSecret>
|
||||
|
||||
@Insert(
|
||||
onConflict = IGNORE
|
||||
)
|
||||
suspend fun insert(marmotRetainedEpochSecret: MarmotRetainedEpochSecret)
|
||||
|
||||
@Delete
|
||||
suspend fun defenestrate(marmotRetainedEpochSecrets: List<MarmotRetainedEpochSecret>)
|
||||
|
||||
}
|
||||
@@ -2,6 +2,12 @@ package at.torch.compose.database.model
|
||||
|
||||
import androidx.room3.Entity
|
||||
import androidx.room3.ForeignKey
|
||||
import at.torch.compose.database.model.traits.LocalStoreEntity
|
||||
import at.torch.compose.database.model.traits.SoftDeletableEntity
|
||||
import at.torch.compose.database.model.traits.TimestampedEntity
|
||||
import at.torch.compose.database.model.traits.UserViewableEntity
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
@@ -21,31 +27,10 @@ data class MarmotRetainedEpochSecret(
|
||||
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
|
||||
}
|
||||
override val createdAt: Instant = Clock.System.now(),
|
||||
override val updatedAt: Instant = createdAt,
|
||||
override val savedAt: Instant = Clock.System.now(),
|
||||
): TimestampedEntity, LocalStoreEntity {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user