Save profile keypackage bundle

This commit is contained in:
Kgothatso Ngako committed 2026-06-19 10:51:39 +03:00
1 parent 24cb4db9a9
commit 3b9be3a9dd
13 files changed
+281 -51

No files matched your search

@@ -5,45 +5,50 @@ import androidx.room3.RoomDatabase
import androidx.room3.TypeConverters import androidx.room3.TypeConverters
import androidx.room3.immediateTransaction import androidx.room3.immediateTransaction
import androidx.room3.useWriterConnection import androidx.room3.useWriterConnection
import at.torch.compose.database.dao.MarmotKeyPackageBundleDao
import at.torch.compose.database.dao.MarmotKeyPackageDao
import at.torch.compose.database.model.MarmotKeyPackageBundle
import kotlin.time.Instant import kotlin.time.Instant
val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
@Database( @Database(
entities = [ entities = [
_root_ide_package_.at.torch.compose.database.model.BroadcastNostrEventReceipt::class, at.torch.compose.database.model.BroadcastNostrEventReceipt::class,
_root_ide_package_.at.torch.compose.database.model.BroadcastNostrEventRequest::class, at.torch.compose.database.model.BroadcastNostrEventRequest::class,
_root_ide_package_.at.torch.compose.database.model.Connection::class, at.torch.compose.database.model.Connection::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessage::class, at.torch.compose.database.model.ChatMessage::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation::class, at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation::class, at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessageNostrEventRelation::class, at.torch.compose.database.model.ChatMessageNostrEventRelation::class,
_root_ide_package_.at.torch.compose.database.model.ChatRoom::class, at.torch.compose.database.model.ChatRoom::class,
_root_ide_package_.at.torch.compose.database.model.InReplyToRelation::class, at.torch.compose.database.model.GiftWrapMessage::class,
_root_ide_package_.at.torch.compose.database.model.GiftWrapMessage::class, at.torch.compose.database.model.GiftWrapSeal::class,
_root_ide_package_.at.torch.compose.database.model.GiftWrapSeal::class, at.torch.compose.database.model.GiftWrapPayload::class,
_root_ide_package_.at.torch.compose.database.model.GiftWrapPayload::class, at.torch.compose.database.model.InReplyToRelation::class,
_root_ide_package_.at.torch.compose.database.model.Mention::class, at.torch.compose.database.model.MarmotKeyPackage::class,
_root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeRequest::class, at.torch.compose.database.model.MarmotKeyPackageBundle::class,
_root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeResult::class, at.torch.compose.database.model.Mention::class,
_root_ide_package_.at.torch.compose.database.model.NostrEvent::class, at.torch.compose.database.model.NegentropySynchronizeRequest::class,
_root_ide_package_.at.torch.compose.database.model.NostrEventRelay::class, at.torch.compose.database.model.NegentropySynchronizeResult::class,
_root_ide_package_.at.torch.compose.database.model.Participant::class, at.torch.compose.database.model.NostrEvent::class,
_root_ide_package_.at.torch.compose.database.model.Post::class, at.torch.compose.database.model.NostrEventRelay::class,
_root_ide_package_.at.torch.compose.database.model.Profile::class, at.torch.compose.database.model.Participant::class,
_root_ide_package_.at.torch.compose.database.model.QuotedRelation::class, at.torch.compose.database.model.Post::class,
_root_ide_package_.at.torch.compose.database.model.Reaction::class, at.torch.compose.database.model.Profile::class,
_root_ide_package_.at.torch.compose.database.model.RecentSearch::class, at.torch.compose.database.model.QuotedRelation::class,
_root_ide_package_.at.torch.compose.database.model.Relay::class, at.torch.compose.database.model.Reaction::class,
_root_ide_package_.at.torch.compose.database.model.RepostedRelation::class, at.torch.compose.database.model.RecentSearch::class,
_root_ide_package_.at.torch.compose.database.model.SynchronizeNostrEventRequest::class, at.torch.compose.database.model.Relay::class,
_root_ide_package_.at.torch.compose.database.model.SynchronizeNostrEventResult::class, at.torch.compose.database.model.RepostedRelation::class,
_root_ide_package_.at.torch.compose.database.model.UnsignedNostrEvent::class, at.torch.compose.database.model.SynchronizeNostrEventRequest::class,
_root_ide_package_.at.torch.compose.database.model.Zap::class at.torch.compose.database.model.SynchronizeNostrEventResult::class,
at.torch.compose.database.model.UnsignedNostrEvent::class,
at.torch.compose.database.model.Zap::class
], ],
version = 1 version = 1
) )
@TypeConverters(_root_ide_package_.at.torch.compose.database.converters.AuxConverters::class) @TypeConverters(at.torch.compose.database.converters.AuxConverters::class)
abstract class TorchDatabase: RoomDatabase() { abstract class TorchDatabase: RoomDatabase() {
abstract fun broadcastNostrEventReceiptDao(): at.torch.compose.database.dao.BroadcastNostrEventReceiptDao abstract fun broadcastNostrEventReceiptDao(): at.torch.compose.database.dao.BroadcastNostrEventReceiptDao
abstract fun broadcastNostrEventRequestDao(): at.torch.compose.database.dao.BroadcastNostrEventRequestDao abstract fun broadcastNostrEventRequestDao(): at.torch.compose.database.dao.BroadcastNostrEventRequestDao
@@ -68,6 +73,10 @@ abstract class TorchDatabase: RoomDatabase() {
abstract fun inReplyToRelationDao(): at.torch.compose.database.dao.InReplyToRelationDao abstract fun inReplyToRelationDao(): at.torch.compose.database.dao.InReplyToRelationDao
abstract fun marmotKeyPackageDao(): MarmotKeyPackageDao
abstract fun marmotKeyPackageBundleDao(): MarmotKeyPackageBundleDao
abstract fun mentionDao(): at.torch.compose.database.dao.MentionDao abstract fun mentionDao(): at.torch.compose.database.dao.MentionDao
abstract fun negentropySynchronizeRequestDao(): at.torch.compose.database.dao.NegentropySynchronizeRequestDao abstract fun negentropySynchronizeRequestDao(): at.torch.compose.database.dao.NegentropySynchronizeRequestDao
@@ -0,0 +1,15 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Upsert
import at.torch.compose.database.model.MarmotKeyPackage
import at.torch.compose.database.model.MarmotKeyPackageBundle
import at.torch.compose.database.model.Mention
@Dao
interface MarmotKeyPackageBundleDao {
@Upsert
suspend fun upsert(marmotKeyPackageBundle: MarmotKeyPackageBundle)
}
@@ -0,0 +1,15 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Upsert
import at.torch.compose.database.model.MarmotKeyPackage
import at.torch.compose.database.model.Mention
@Dao
interface MarmotKeyPackageDao {
@Upsert
suspend fun upsert(marmotKeyPackage: MarmotKeyPackage)
}
@@ -0,0 +1,68 @@
package at.torch.compose.database.model
import androidx.room3.Entity
import androidx.room3.ForeignKey
import androidx.room3.PrimaryKey
import at.torch.compose.database.model.traits.BroadcastableEntity
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 com.vitorpamplona.quartz.nip01Core.core.HexKey
import kotlin.time.Clock
import kotlin.time.Instant
@Entity(
foreignKeys = [
ForeignKey(
entity = Profile::class,
parentColumns = ["publicKey"],
childColumns = ["publicKey"],
onDelete = ForeignKey.CASCADE,
),
]
)
data class MarmotKeyPackage(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val publicKey: HexKey,
val tlsEncodedMarmotKeyPackage: ByteArray,
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = Clock.System.now(),
override val savedAt: Instant = Clock.System.now(),
override val deletedAt: Instant? = null,
override val broadcastedAt: Instant? = null,
): TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as MarmotKeyPackage
if (id != other.id) return false
if (publicKey != other.publicKey) return false
if (!tlsEncodedMarmotKeyPackage.contentEquals(other.tlsEncodedMarmotKeyPackage)) return false
if (createdAt != other.createdAt) return false
if (updatedAt != other.updatedAt) return false
if (savedAt != other.savedAt) return false
if (deletedAt != other.deletedAt) return false
if (broadcastedAt != other.broadcastedAt) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + publicKey.hashCode()
result = 31 * result + tlsEncodedMarmotKeyPackage.contentHashCode()
result = 31 * result + createdAt.hashCode()
result = 31 * result + updatedAt.hashCode()
result = 31 * result + savedAt.hashCode()
result = 31 * result + (deletedAt?.hashCode() ?: 0)
result = 31 * result + (broadcastedAt?.hashCode() ?: 0)
return result
}
}
@@ -0,0 +1,42 @@
package at.torch.compose.database.model
import androidx.room3.Entity
import androidx.room3.ForeignKey
import androidx.room3.PrimaryKey
import at.torch.compose.database.model.traits.BroadcastableEntity
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 com.vitorpamplona.quartz.nip01Core.core.HexKey
import kotlin.time.Clock
import kotlin.time.Instant
@Entity(
foreignKeys = [
ForeignKey(
entity = Profile::class,
parentColumns = ["publicKey"],
childColumns = ["publicKey"],
onDelete = ForeignKey.CASCADE,
),
]
)
data class MarmotKeyPackageBundle(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val publicKey: HexKey,
val tlsEncodedMarmotKeyPackage: String,
val ncryptsecInitPrivateKey: String,
val ncryptsecEncryptionPrivateKey: String,
val ncryptsecSignaturePrivateKey: String,
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = Clock.System.now(),
override val savedAt: Instant = Clock.System.now(),
override val deletedAt: Instant? = null,
override val broadcastedAt: Instant? = null,
): TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity {
}
@@ -3,6 +3,10 @@ package at.torch.compose.database.model
import androidx.room3.Entity import androidx.room3.Entity
import androidx.room3.ForeignKey import androidx.room3.ForeignKey
import androidx.room3.PrimaryKey import androidx.room3.PrimaryKey
import at.torch.compose.database.model.traits.BroadcastableEntity
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 com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import kotlin.time.Clock import kotlin.time.Clock
import kotlin.time.Instant import kotlin.time.Instant
@@ -37,7 +41,7 @@ data class Mention(
override val savedAt: Instant = Clock.System.now(), override val savedAt: Instant = Clock.System.now(),
override val deletedAt: Instant? = null, override val deletedAt: Instant? = null,
override val broadcastedAt: Instant? = null, override val broadcastedAt: Instant? = null,
): at.torch.compose.database.model.traits.TimestampedEntity, ): TimestampedEntity,
at.torch.compose.database.model.traits.LocalStoreEntity, LocalStoreEntity,
at.torch.compose.database.model.traits.BroadcastableEntity, BroadcastableEntity,
at.torch.compose.database.model.traits.SoftDeletableEntity SoftDeletableEntity
@@ -0,0 +1,37 @@
package at.torch.compose.database.repository
import at.torch.compose.database.TorchDatabase
import at.torch.compose.database.model.MarmotKeyPackageBundle
import at.torch.compose.extensions.toHex
import at.torch.compose.repository.MarmotRepository
import com.vitorpamplona.quartz.marmot.mls.codec.TlsWriter
import com.vitorpamplona.quartz.marmot.mls.messages.KeyPackageBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip49PrivKeyEnc.Nip49
class DatabaseMarmotRepository(
val database: TorchDatabase
): MarmotRepository {
override suspend fun saveMarmotKeyPackageBundle(
publicKey: HexKey,
keyPackageBundle: KeyPackageBundle,
nsecPassword: String
) {
val tlsWriter = TlsWriter()
keyPackageBundle.keyPackage.encodeTls(tlsWriter)
val nip49 = Nip49()
database.marmotKeyPackageBundleDao().upsert(
MarmotKeyPackageBundle(
publicKey = publicKey,
tlsEncodedMarmotKeyPackage = tlsWriter.toByteArray().toHex(),
ncryptsecInitPrivateKey = nip49.encrypt(keyPackageBundle.initPrivateKey.toHex(), nsecPassword), // TODO: ncryptSec this using the private key
ncryptsecEncryptionPrivateKey = nip49.encrypt(keyPackageBundle.encryptionPrivateKey.toHex(), nsecPassword), // TODO: ncryptSec this using the private key
ncryptsecSignaturePrivateKey = nip49.encrypt(keyPackageBundle.signaturePrivateKey.toHex(), nsecPassword) // TODO: ncryptSec this using the private key
)
)
}
}
@@ -1,9 +1,6 @@
package at.torch.compose.database.repository package at.torch.compose.database.repository
import at.torch.compose.database.GENESIS_AT import at.torch.compose.database.model.MarmotKeyPackageBundle
import at.torch.compose.database.model.BroadcastNostrEventReceipt
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
import at.torch.compose.database.model.RecentSearch
import at.torch.compose.database.model.UnsignedNostrEvent import at.torch.compose.database.model.UnsignedNostrEvent
import at.torch.compose.nostr.Relays import at.torch.compose.nostr.Relays
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
@@ -11,6 +8,7 @@ import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageEvent
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRotationManager.Companion.KEY_PACKAGE_LIFETIME_SECONDS import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRotationManager.Companion.KEY_PACKAGE_LIFETIME_SECONDS
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageUtils import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageUtils
import com.vitorpamplona.quartz.marmot.mls.codec.TlsWriter
import com.vitorpamplona.quartz.marmot.mls.crypto.Ed25519 import com.vitorpamplona.quartz.marmot.mls.crypto.Ed25519
import com.vitorpamplona.quartz.marmot.mls.crypto.MlsCryptoProvider import com.vitorpamplona.quartz.marmot.mls.crypto.MlsCryptoProvider
import com.vitorpamplona.quartz.marmot.mls.crypto.X25519 import com.vitorpamplona.quartz.marmot.mls.crypto.X25519
@@ -48,14 +46,11 @@ import com.vitorpamplona.quartz.nip18Reposts.quotes.QEventTag
import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.tags.RelayTag import com.vitorpamplona.quartz.nip51Lists.tags.RelayTag
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -118,7 +113,7 @@ class DatabaseNostrRepository(
publicKey: HexKey, publicKey: HexKey,
name: String?, name: String?,
biography: String? biography: String?
) { ): KeyPackageBundle {
val profileEventTemplate = MetadataEvent.createNew( val profileEventTemplate = MetadataEvent.createNew(
name = name, name = name,
about = biography, about = biography,
@@ -213,8 +208,6 @@ class DatabaseNostrRepository(
val identity = publicKey.hexToByteArray() val identity = publicKey.hexToByteArray()
val bundle = generateKeyPackage(identity, dTag) val bundle = generateKeyPackage(identity, dTag)
// TODO: Save the bundle?
val keyPackageBytes = bundle.keyPackage.toTlsBytes() val keyPackageBytes = bundle.keyPackage.toTlsBytes()
val keyPackageBase64 = Base64.encode(keyPackageBytes) val keyPackageBase64 = Base64.encode(keyPackageBytes)
val keyPackageRef = bundle.keyPackage.reference().toHexKey() val keyPackageRef = bundle.keyPackage.reference().toHexKey()
@@ -236,6 +229,8 @@ class DatabaseNostrRepository(
) )
saveUnsignedNostrEvents(unsignedNostrEvents) saveUnsignedNostrEvents(unsignedNostrEvents)
return bundle
} }
private fun generateKeyPackage( private fun generateKeyPackage(
@@ -0,0 +1,26 @@
package at.torch.compose.repository
import com.vitorpamplona.quartz.marmot.mls.messages.KeyPackageBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
interface MarmotRepository {
suspend fun saveMarmotKeyPackageBundle(
publicKey: HexKey,
keyPackageBundle: KeyPackageBundle,
nsecPassword: String
)
companion object {
private val NO_OP_MARMOT_KEY_PACKAGE_BUNDLE = object: MarmotRepository {
override suspend fun saveMarmotKeyPackageBundle(
publicKey: HexKey,
keyPackageBundle: KeyPackageBundle,
nsecPassword: String
) {
TODO("Not yet implemented")
}
}
}
}
@@ -1,5 +1,6 @@
package at.torch.compose.repository package at.torch.compose.repository
import com.vitorpamplona.quartz.marmot.mls.messages.KeyPackageBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.core.Kind
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
@@ -24,7 +25,7 @@ interface NostrRepository {
publicKey: HexKey, publicKey: HexKey,
name: String?, name: String?,
biography: String? biography: String?
) ): KeyPackageBundle
suspend fun signInToProfile( suspend fun signInToProfile(
publicKey: HexKey publicKey: HexKey
@@ -155,7 +156,8 @@ interface NostrRepository {
publicKey: HexKey, publicKey: HexKey,
name: String?, name: String?,
biography: String? biography: String?
) { ): KeyPackageBundle {
TODO("")
} }
override suspend fun signInToProfile(publicKey: HexKey) { override suspend fun signInToProfile(publicKey: HexKey) {
@@ -90,6 +90,7 @@ class ChatRoomCreationViewModel(
viewModelScope.launch(Dispatchers.Main) { viewModelScope.launch(Dispatchers.Main) {
if (localChatRoom != null) { if (localChatRoom != null) {
// TODO: Broadcast the thing... // TODO: Broadcast the thing...
// TODO: Navigate to invitation screen... // TODO: Navigate to invitation screen...
onNavigateToChatRoom.invoke( onNavigateToChatRoom.invoke(
ChatRoomDetailRoute( ChatRoomDetailRoute(
@@ -10,6 +10,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import at.torch.compose.ui.view.state.form.CreateProfileFormState import at.torch.compose.ui.view.state.form.CreateProfileFormState
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import at.torch.compose.repository.MarmotRepository
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.Chain import fr.acinq.bitcoin.Chain
import fr.acinq.bitcoin.MnemonicCode import fr.acinq.bitcoin.MnemonicCode
@@ -18,6 +19,7 @@ import fr.acinq.lightning.Lightning
import fr.acinq.lightning.crypto.LocalKeyManager import fr.acinq.lightning.crypto.LocalKeyManager
import fr.acinq.phoenix.managers.NodeParamsManager import fr.acinq.phoenix.managers.NodeParamsManager
import fr.acinq.phoenix.managers.nostrPublicKey import fr.acinq.phoenix.managers.nostrPublicKey
import fr.acinq.phoenix.managers.nsecPassword
import fr.acinq.phoenix.utils.MnemonicLanguage import fr.acinq.phoenix.utils.MnemonicLanguage
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -27,19 +29,22 @@ import kotlinx.coroutines.launch
class CreateProfileViewModel( class CreateProfileViewModel(
val initialCreateProfileUIState: CreateProfileUIState, val initialCreateProfileUIState: CreateProfileUIState,
val createProfileFormState: CreateProfileFormState = CreateProfileFormState(), val createProfileFormState: CreateProfileFormState = CreateProfileFormState(),
val nostrRepository: NostrRepository val nostrRepository: NostrRepository,
val marmotRepository: MarmotRepository
): ViewModel() { ): ViewModel() {
companion object { companion object {
private const val TAG = "CreateAccountViewModel" private const val TAG = "CreateAccountViewModel"
fun factory( fun factory(
initialCreateProfileUIState: CreateProfileUIState, initialCreateProfileUIState: CreateProfileUIState,
nostrRepository: NostrRepository nostrRepository: NostrRepository,
marmotRepository: MarmotRepository
): ViewModelProvider.Factory = viewModelFactory { ): ViewModelProvider.Factory = viewModelFactory {
initializer { initializer {
CreateProfileViewModel( CreateProfileViewModel(
initialCreateProfileUIState, initialCreateProfileUIState,
nostrRepository = nostrRepository nostrRepository = nostrRepository,
marmotRepository = marmotRepository
) )
} }
} }
@@ -100,12 +105,17 @@ class CreateProfileViewModel(
) )
val pubkey = localKeyManager.nostrPublicKey() val pubkey = localKeyManager.nostrPublicKey()
logger.d("NostrKey: $pubkey") val keyPackageBundle = nostrRepository.createNewProfile(
nostrRepository.createNewProfile(
pubkey, pubkey,
name = createProfileFormState.nameField.textFieldState.text.toString(), name = createProfileFormState.nameField.textFieldState.text.toString(),
biography = createProfileFormState.biographyField.textFieldState.text.toString() biography = createProfileFormState.biographyField.textFieldState.text.toString()
) )
marmotRepository.saveMarmotKeyPackageBundle(
publicKey = pubkey,
keyPackageBundle,
nsecPassword = localKeyManager.nsecPassword()
)
} }
} }
@@ -102,6 +102,12 @@ fun LocalKeyManager.nostrPrivateKey(): PrivateKey {
return derivePrivateKey(path).privateKey return derivePrivateKey(path).privateKey
} }
fun LocalKeyManager.nsecPassword(): String {
return Crypto.hash160(
nostrPrivateKey().value
).byteVector().toHex()
}
fun LocalKeyManager.nostrPublicKey(): HexKey { fun LocalKeyManager.nostrPublicKey(): HexKey {
return KeyPair( return KeyPair(
privKey = nostrPrivateKey().value.toByteArray() privKey = nostrPrivateKey().value.toByteArray()