From 49969e8221f6b5e839cc196f5dc8ed01dca64d70 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Thu, 19 Dec 2024 16:50:32 -0800 Subject: [PATCH] hmac key updates --- .../xmtp/android/library/ConversationsTest.kt | 22 +++++ .../xmtp/android/library/HistorySyncTest.kt | 5 +- .../java/org/xmtp/android/library/Client.kt | 4 - .../org/xmtp/android/library/Conversations.kt | 39 +++++++- .../android/library/PrivatePreferences.kt | 4 - library/src/main/java/xmtpv3.kt | 90 +++++++++++++------ 6 files changed, 124 insertions(+), 40 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ConversationsTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ConversationsTest.kt index 5e7c7b56..c7fde19a 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ConversationsTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ConversationsTest.kt @@ -6,12 +6,14 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.xmtp.android.library.messages.PrivateKey import org.xmtp.android.library.messages.PrivateKeyBuilder import org.xmtp.android.library.messages.walletAddress +import java.time.Instant @RunWith(AndroidJUnit4::class) class ConversationsTest { @@ -186,4 +188,24 @@ class ConversationsTest { assertEquals(2, allMessages.size) job.cancel() } + + @Test + fun testReturnsAllHMACKeys() { + val conversations = mutableListOf() + repeat(5) { + val account = PrivateKeyBuilder() + val client = runBlocking { Client().create(account, fixtures.clientOptions) } + runBlocking { + conversations.add( + alixClient.conversations.newConversation(client.address) + ) + } + } + val hmacKeys = alixClient.conversations.getHmacKeys() + + val topics = hmacKeys.hmacKeysMap.keys + conversations.forEach { convo -> + assertTrue(topics.contains(convo.topic)) + } + } } diff --git a/library/src/androidTest/java/org/xmtp/android/library/HistorySyncTest.kt b/library/src/androidTest/java/org/xmtp/android/library/HistorySyncTest.kt index 6505727a..b0f4d893 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/HistorySyncTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/HistorySyncTest.kt @@ -74,8 +74,6 @@ class HistorySyncTest { assertEquals(state.installations.size, 2) runBlocking { - alixClient2.preferences.syncConsent() - Thread.sleep(2000) alixClient.conversations.syncAllConversations() Thread.sleep(2000) alixClient2.conversations.syncAllConversations() @@ -125,8 +123,7 @@ class HistorySyncTest { runBlocking { alix2Group.send("A message") alix2Group.send("A second message") - alixClient3.requestMessageHistorySync() - Thread.sleep(1000) + Thread.sleep(2000) alixClient.conversations.syncAllConversations() Thread.sleep(2000) alixClient2.conversations.syncAllConversations() diff --git a/library/src/main/java/org/xmtp/android/library/Client.kt b/library/src/main/java/org/xmtp/android/library/Client.kt index f194f046..2dad6105 100644 --- a/library/src/main/java/org/xmtp/android/library/Client.kt +++ b/library/src/main/java/org/xmtp/android/library/Client.kt @@ -386,10 +386,6 @@ class Client() { ffiClient.dbReconnect() } - suspend fun requestMessageHistorySync() { - ffiClient.sendSyncRequest(FfiDeviceSyncKind.MESSAGES) - } - suspend fun inboxStatesForInboxIds( refreshFromNetwork: Boolean, inboxIds: List, diff --git a/library/src/main/java/org/xmtp/android/library/Conversations.kt b/library/src/main/java/org/xmtp/android/library/Conversations.kt index e9f26948..b0281575 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversations.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversations.kt @@ -1,12 +1,15 @@ package org.xmtp.android.library import android.util.Log +import com.google.protobuf.kotlin.toByteString import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch import org.xmtp.android.library.libxmtp.Message +import org.xmtp.android.library.messages.Topic +import org.xmtp.proto.keystore.api.v1.Keystore import uniffi.xmtpv3.FfiConversation import uniffi.xmtpv3.FfiConversationCallback import uniffi.xmtpv3.FfiConversationType @@ -20,6 +23,7 @@ import uniffi.xmtpv3.FfiMessage import uniffi.xmtpv3.FfiMessageCallback import uniffi.xmtpv3.FfiPermissionPolicySet import uniffi.xmtpv3.FfiSubscribeException +import uniffi.xmtpv3.FfiXmtpClient import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionPolicySet import java.util.Date @@ -131,7 +135,11 @@ data class Conversations( // Sync all new and existing conversations data from the network suspend fun syncAllConversations(consentState: ConsentState? = null): UInt { - return ffiConversations.syncAllConversations(consentState?.let { ConsentState.toFfiConsentState(it) }) + return ffiConversations.syncAllConversations(consentState?.let { + ConsentState.toFfiConsentState( + it + ) + }) } suspend fun newConversation(peerAddress: String): Conversation { @@ -264,7 +272,15 @@ data class Conversations( override fun onConversation(conversation: FfiConversation) { launch(Dispatchers.IO) { when (conversation.conversationType()) { - FfiConversationType.DM -> trySend(Conversation.Dm(Dm(client, conversation))) + FfiConversationType.DM -> trySend( + Conversation.Dm( + Dm( + client, + conversation + ) + ) + ) + else -> trySend(Conversation.Group(Group(client, conversation))) } } @@ -305,4 +321,23 @@ data class Conversations( awaitClose { stream.end() } } + + fun getHmacKeys(): Keystore.GetConversationHmacKeysResponse { + val hmacKeysResponse = Keystore.GetConversationHmacKeysResponse.newBuilder() + val conversations = ffiConversations.getHmacKeys() + conversations.iterator().forEach { + val hmacKeys = Keystore.GetConversationHmacKeysResponse.HmacKeys.newBuilder() + it.value.forEach { key -> + val hmacKeyData = Keystore.GetConversationHmacKeysResponse.HmacKeyData.newBuilder() + hmacKeyData.hmacKey = key.key.toByteString() + hmacKeyData.thirtyDayPeriodsSinceEpoch = key.epoch.toInt() + hmacKeys.addValues(hmacKeyData) + } + hmacKeysResponse.putHmacKeys( + Topic.groupMessage(it.key.toHex()).description, + hmacKeys.build() + ) + } + return hmacKeysResponse.build() + } } diff --git a/library/src/main/java/org/xmtp/android/library/PrivatePreferences.kt b/library/src/main/java/org/xmtp/android/library/PrivatePreferences.kt index ec39b334..cefd859a 100644 --- a/library/src/main/java/org/xmtp/android/library/PrivatePreferences.kt +++ b/library/src/main/java/org/xmtp/android/library/PrivatePreferences.kt @@ -96,10 +96,6 @@ data class PrivatePreferences( var client: Client, private val ffiClient: FfiXmtpClient, ) { - suspend fun syncConsent() { - ffiClient.sendSyncRequest(FfiDeviceSyncKind.CONSENT) - } - suspend fun streamConsent(): Flow = callbackFlow { val consentCallback = object : FfiConsentCallback { override fun onConsentUpdate(consent: List) { diff --git a/library/src/main/java/xmtpv3.kt b/library/src/main/java/xmtpv3.kt index 29d59d38..f5a52c2c 100644 --- a/library/src/main/java/xmtpv3.kt +++ b/library/src/main/java/xmtpv3.kt @@ -1219,6 +1219,10 @@ internal interface UniffiLib : Library { `ptr`: Pointer, `accountAddresses`: RustBuffer.ByValue, `opts`: RustBuffer.ByValue, ): Long + fun uniffi_xmtpv3_fn_method_fficonversations_get_hmac_keys( + `ptr`: Pointer, uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun uniffi_xmtpv3_fn_method_fficonversations_get_sync_group( `ptr`: Pointer, uniffi_out_err: UniffiRustCallStatus, ): Pointer @@ -1511,10 +1515,6 @@ internal interface UniffiLib : Library { `ptr`: Pointer, `entityType`: RustBuffer.ByValue, `entity`: RustBuffer.ByValue, ): Long - fun uniffi_xmtpv3_fn_method_ffixmtpclient_get_hmac_keys( - `ptr`: Pointer, uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - fun uniffi_xmtpv3_fn_method_ffixmtpclient_get_latest_inbox_state( `ptr`: Pointer, `inboxId`: RustBuffer.ByValue, ): Long @@ -2103,6 +2103,9 @@ internal interface UniffiLib : Library { fun uniffi_xmtpv3_checksum_method_fficonversations_create_group( ): Short + fun uniffi_xmtpv3_checksum_method_fficonversations_get_hmac_keys( + ): Short + fun uniffi_xmtpv3_checksum_method_fficonversations_get_sync_group( ): Short @@ -2256,9 +2259,6 @@ internal interface UniffiLib : Library { fun uniffi_xmtpv3_checksum_method_ffixmtpclient_get_consent_state( ): Short - fun uniffi_xmtpv3_checksum_method_ffixmtpclient_get_hmac_keys( - ): Short - fun uniffi_xmtpv3_checksum_method_ffixmtpclient_get_latest_inbox_state( ): Short @@ -2519,6 +2519,9 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) { if (lib.uniffi_xmtpv3_checksum_method_fficonversations_create_group() != 7282.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_xmtpv3_checksum_method_fficonversations_get_hmac_keys() != 44064.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_xmtpv3_checksum_method_fficonversations_get_sync_group() != 42973.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -2672,9 +2675,6 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) { if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_get_consent_state() != 58208.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_get_hmac_keys() != 36015.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_get_latest_inbox_state() != 3165.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -5330,6 +5330,8 @@ public interface FfiConversationsInterface { `opts`: FfiCreateGroupOptions, ): FfiConversation + fun `getHmacKeys`(): Map> + fun `getSyncGroup`(): FfiConversation suspend fun `list`(`opts`: FfiListConversationsOptions): List @@ -5526,6 +5528,20 @@ open class FfiConversations : Disposable, AutoCloseable, FfiConversationsInterfa } + @Throws(GenericException::class) + override fun `getHmacKeys`(): Map> { + return FfiConverterMapByteArraySequenceTypeFfiHmacKey.lift( + callWithPointer { + uniffiRustCallWithError(GenericException) { _status -> + UniffiLib.INSTANCE.uniffi_xmtpv3_fn_method_fficonversations_get_hmac_keys( + it, _status + ) + } + } + ) + } + + @Throws(GenericException::class) override fun `getSyncGroup`(): FfiConversation { return FfiConverterTypeFfiConversation.lift( @@ -8783,8 +8799,6 @@ public interface FfiXmtpClientInterface { `entity`: kotlin.String, ): FfiConsentState - fun `getHmacKeys`(): List - suspend fun `getLatestInboxState`(`inboxId`: kotlin.String): FfiInboxState fun `inboxId`(): kotlin.String @@ -9203,20 +9217,6 @@ open class FfiXmtpClient : Disposable, AutoCloseable, FfiXmtpClientInterface { } - @Throws(GenericException::class) - override fun `getHmacKeys`(): List { - return FfiConverterSequenceTypeFfiHmacKey.lift( - callWithPointer { - uniffiRustCallWithError(GenericException) { _status -> - UniffiLib.INSTANCE.uniffi_xmtpv3_fn_method_ffixmtpclient_get_hmac_keys( - it, _status - ) - } - } - ) - } - - @Throws(GenericException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") override suspend fun `getLatestInboxState`(`inboxId`: kotlin.String): FfiInboxState { @@ -12078,6 +12078,44 @@ public object FfiConverterMapStringBoolean : } +/** + * @suppress + */ +public object FfiConverterMapByteArraySequenceTypeFfiHmacKey : + FfiConverterRustBuffer>> { + override fun read(buf: ByteBuffer): Map> { + val len = buf.getInt() + return buildMap>(len) { + repeat(len) { + val k = FfiConverterByteArray.read(buf) + val v = FfiConverterSequenceTypeFfiHmacKey.read(buf) + this[k] = v + } + } + } + + override fun allocationSize(value: Map>): ULong { + val spaceForMapSize = 4UL + val spaceForChildren = value.map { (k, v) -> + FfiConverterByteArray.allocationSize(k) + + FfiConverterSequenceTypeFfiHmacKey.allocationSize(v) + }.sum() + return spaceForMapSize + spaceForChildren + } + + override fun write(value: Map>, buf: ByteBuffer) { + buf.putInt(value.size) + // The parens on `(k, v)` here ensure we're calling the right method, + // which is important for compatibility with older android devices. + // Ref https://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/ + value.forEach { (k, v) -> + FfiConverterByteArray.write(k, buf) + FfiConverterSequenceTypeFfiHmacKey.write(v, buf) + } + } +} + + @Throws(GenericException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") suspend fun `connectToBackend`(`host`: kotlin.String, `isSecure`: kotlin.Boolean): XmtpApiClient {