From 5adfcbb241854bbdcf7d8906c341ab45d4ffa596 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 10:52:34 -0700 Subject: [PATCH 01/10] first pass at performance and caching --- .../java/org/xmtp/android/library/Contacts.kt | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 26b7e36d8..157ca8f76 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -22,6 +22,7 @@ data class ConsentListEntry( val value: String, val entryType: EntryType, val consentType: ConsentState, + val createdAt: Date ) { enum class EntryType { ADDRESS, @@ -48,8 +49,10 @@ data class ConsentListEntry( get() = "${entryType.name}-$value" } -class ConsentList(val client: Client) { - val entries: MutableMap = mutableMapOf() +class ConsentList( + val client: Client, + val entries: MutableMap = mutableMapOf(), +) { private val publicKey = client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes private val privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes @@ -60,13 +63,14 @@ class ConsentList(val client: Client) { ) @OptIn(ExperimentalUnsignedTypes::class) - suspend fun load(): ConsentList { + suspend fun load(): List { + val mostRecent = entries.values.maxOfOrNull { it.createdAt } + val envelopes = client.apiClient.envelopes( Topic.preferenceList(identifier).description, - Pagination(direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING), + Pagination(before = mostRecent, direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING), ) - val consentList = ConsentList(client) val preferences: MutableList = mutableListOf() for (envelope in envelopes) { val payload = @@ -85,20 +89,20 @@ class ConsentList(val client: Client) { preferences.iterator().forEach { preference -> preference.allowAddress?.walletAddressesList?.forEach { address -> - consentList.allow(address) + allow(address) } preference.denyAddress?.walletAddressesList?.forEach { address -> - consentList.deny(address) + deny(address) } preference.allowGroup?.groupIdsList?.forEach { groupId -> - consentList.allowGroup(groupId.toByteArray()) + allowGroup(groupId.toByteArray()) } preference.denyGroup?.groupIdsList?.forEach { groupId -> - consentList.denyGroup(groupId.toByteArray()) + denyGroup(groupId.toByteArray()) } } - return consentList + return entries.values.toList() } suspend fun publish(entry: ConsentListEntry) { @@ -109,27 +113,32 @@ class ConsentList(val client: Client) { when (entry.consentType) { ConsentState.ALLOWED -> it.setAllowAddress( - PrivatePreferencesAction.AllowAddress.newBuilder().addWalletAddresses(entry.value), + PrivatePreferencesAction.AllowAddress.newBuilder() + .addWalletAddresses(entry.value), ) ConsentState.DENIED -> it.setDenyAddress( - PrivatePreferencesAction.DenyAddress.newBuilder().addWalletAddresses(entry.value), + PrivatePreferencesAction.DenyAddress.newBuilder() + .addWalletAddresses(entry.value), ) ConsentState.UNKNOWN -> it.clearMessageType() } } + ConsentListEntry.EntryType.GROUP_ID -> { when (entry.consentType) { ConsentState.ALLOWED -> it.setAllowGroup( - PrivatePreferencesAction.AllowGroup.newBuilder().addGroupIds(entry.value.toByteStringUtf8()), + PrivatePreferencesAction.AllowGroup.newBuilder() + .addGroupIds(entry.value.toByteStringUtf8()), ) ConsentState.DENIED -> it.setDenyGroup( - PrivatePreferencesAction.DenyGroup.newBuilder().addGroupIds(entry.value.toByteStringUtf8()), + PrivatePreferencesAction.DenyGroup.newBuilder() + .addGroupIds(entry.value.toByteStringUtf8()), ) ConsentState.UNKNOWN -> it.clearMessageType() @@ -200,37 +209,35 @@ data class Contacts( var client: Client, val knownBundles: MutableMap = mutableMapOf(), val hasIntroduced: MutableMap = mutableMapOf(), + var consentList: ConsentList = ConsentList(client), ) { - var consentList: ConsentList = ConsentList(client) - fun refreshConsentList(): ConsentList { - runBlocking { - consentList = ConsentList(client).load() - } + suspend fun refreshConsentList(): ConsentList { + consentList.load() return consentList } suspend fun allow(addresses: List) { for (address in addresses) { - ConsentList(client).publish(consentList.allow(address)) + consentList.publish(consentList.allow(address)) } } suspend fun deny(addresses: List) { for (address in addresses) { - ConsentList(client).publish(consentList.deny(address)) + consentList.publish(consentList.deny(address)) } } suspend fun allowGroup(groupIds: List) { for (id in groupIds) { - ConsentList(client).publish(consentList.allowGroup(id)) + consentList.publish(consentList.allowGroup(id)) } } suspend fun denyGroup(groupIds: List) { for (id in groupIds) { - ConsentList(client).publish(consentList.denyGroup(id)) + consentList.publish(consentList.denyGroup(id)) } } From 75b624a809c44887491382db3d3400bb7720c6e3 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 11:01:11 -0700 Subject: [PATCH 02/10] fix up the caching --- .../java/org/xmtp/android/library/Contacts.kt | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 157ca8f76..f0618d80a 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -22,7 +22,7 @@ data class ConsentListEntry( val value: String, val entryType: EntryType, val consentType: ConsentState, - val createdAt: Date + val createdAt: Date, ) { enum class EntryType { ADDRESS, @@ -33,15 +33,17 @@ data class ConsentListEntry( fun address( address: String, type: ConsentState = ConsentState.UNKNOWN, + createdAt: Date = Date(), ): ConsentListEntry { - return ConsentListEntry(address, EntryType.ADDRESS, type) + return ConsentListEntry(address, EntryType.ADDRESS, type, createdAt) } fun groupId( groupId: ByteArray, type: ConsentState = ConsentState.UNKNOWN, + createdAt: Date = Date(), ): ConsentListEntry { - return ConsentListEntry(String(groupId), EntryType.GROUP_ID, type) + return ConsentListEntry(String(groupId), EntryType.GROUP_ID, type, createdAt) } } @@ -69,9 +71,12 @@ class ConsentList( val envelopes = client.apiClient.envelopes( Topic.preferenceList(identifier).description, - Pagination(before = mostRecent, direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING), + Pagination( + before = mostRecent, + direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING + ), ) - val preferences: MutableList = mutableListOf() + val preferences: MutableList> = mutableListOf() for (envelope in envelopes) { val payload = uniffi.xmtpv3.userPreferencesDecrypt( @@ -81,24 +86,26 @@ class ConsentList( ) preferences.add( - PrivatePreferencesAction.parseFrom( - payload.toUByteArray().toByteArray(), - ), + Pair( + PrivatePreferencesAction.parseFrom( + payload.toUByteArray().toByteArray(), + ), Date(envelope.timestampNs) + ) ) } - preferences.iterator().forEach { preference -> + preferences.iterator().forEach { (preference, date) -> preference.allowAddress?.walletAddressesList?.forEach { address -> - allow(address) + allow(address, date) } preference.denyAddress?.walletAddressesList?.forEach { address -> - deny(address) + deny(address, date) } preference.allowGroup?.groupIdsList?.forEach { groupId -> - allowGroup(groupId.toByteArray()) + allowGroup(groupId.toByteArray(), date) } preference.denyGroup?.groupIdsList?.forEach { groupId -> - denyGroup(groupId.toByteArray()) + denyGroup(groupId.toByteArray(), date) } } @@ -164,30 +171,30 @@ class ConsentList( client.publish(listOf(envelope)) } - fun allow(address: String): ConsentListEntry { - val entry = ConsentListEntry.address(address, ConsentState.ALLOWED) - entries[ConsentListEntry.address(address).key] = entry + fun allow(address: String, date: Date): ConsentListEntry { + val entry = ConsentListEntry.address(address, ConsentState.ALLOWED, date) + entries[entry.key] = entry return entry } - fun deny(address: String): ConsentListEntry { - val entry = ConsentListEntry.address(address, ConsentState.DENIED) - entries[ConsentListEntry.address(address).key] = entry + fun deny(address: String, date: Date): ConsentListEntry { + val entry = ConsentListEntry.address(address, ConsentState.DENIED, date) + entries[entry.key] = entry return entry } - fun allowGroup(groupId: ByteArray): ConsentListEntry { - val entry = ConsentListEntry.groupId(groupId, ConsentState.ALLOWED) - entries[ConsentListEntry.groupId(groupId).key] = entry + fun allowGroup(groupId: ByteArray, date: Date): ConsentListEntry { + val entry = ConsentListEntry.groupId(groupId, ConsentState.ALLOWED, date) + entries[entry.key] = entry return entry } - fun denyGroup(groupId: ByteArray): ConsentListEntry { - val entry = ConsentListEntry.groupId(groupId, ConsentState.DENIED) - entries[ConsentListEntry.groupId(groupId).key] = entry + fun denyGroup(groupId: ByteArray, date: Date): ConsentListEntry { + val entry = ConsentListEntry.groupId(groupId, ConsentState.DENIED, date) + entries[entry.key] = entry return entry } From 1c57187a25651eb5bbdc26526d2f1b5a541438c9 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 11:58:54 -0700 Subject: [PATCH 03/10] set just one date --- .../java/org/xmtp/android/library/Contacts.kt | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index f0618d80a..093c25c2c 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -22,7 +22,6 @@ data class ConsentListEntry( val value: String, val entryType: EntryType, val consentType: ConsentState, - val createdAt: Date, ) { enum class EntryType { ADDRESS, @@ -33,17 +32,15 @@ data class ConsentListEntry( fun address( address: String, type: ConsentState = ConsentState.UNKNOWN, - createdAt: Date = Date(), ): ConsentListEntry { - return ConsentListEntry(address, EntryType.ADDRESS, type, createdAt) + return ConsentListEntry(address, EntryType.ADDRESS, type) } fun groupId( groupId: ByteArray, type: ConsentState = ConsentState.UNKNOWN, - createdAt: Date = Date(), ): ConsentListEntry { - return ConsentListEntry(String(groupId), EntryType.GROUP_ID, type, createdAt) + return ConsentListEntry(String(groupId), EntryType.GROUP_ID, type) } } @@ -55,6 +52,7 @@ class ConsentList( val client: Client, val entries: MutableMap = mutableMapOf(), ) { + private var lastFetched: Date? = null private val publicKey = client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes private val privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes @@ -66,17 +64,17 @@ class ConsentList( @OptIn(ExperimentalUnsignedTypes::class) suspend fun load(): List { - val mostRecent = entries.values.maxOfOrNull { it.createdAt } - + val newDate = Date() val envelopes = client.apiClient.envelopes( Topic.preferenceList(identifier).description, Pagination( - before = mostRecent, + before = lastFetched, direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING ), ) - val preferences: MutableList> = mutableListOf() + lastFetched = newDate + val preferences: MutableList = mutableListOf() for (envelope in envelopes) { val payload = uniffi.xmtpv3.userPreferencesDecrypt( @@ -86,26 +84,24 @@ class ConsentList( ) preferences.add( - Pair( - PrivatePreferencesAction.parseFrom( - payload.toUByteArray().toByteArray(), - ), Date(envelope.timestampNs) + PrivatePreferencesAction.parseFrom( + payload.toUByteArray().toByteArray(), ) ) } - preferences.iterator().forEach { (preference, date) -> + preferences.iterator().forEach { preference -> preference.allowAddress?.walletAddressesList?.forEach { address -> - allow(address, date) + allow(address) } preference.denyAddress?.walletAddressesList?.forEach { address -> - deny(address, date) + deny(address) } preference.allowGroup?.groupIdsList?.forEach { groupId -> - allowGroup(groupId.toByteArray(), date) + allowGroup(groupId.toByteArray()) } preference.denyGroup?.groupIdsList?.forEach { groupId -> - denyGroup(groupId.toByteArray(), date) + denyGroup(groupId.toByteArray()) } } @@ -171,29 +167,29 @@ class ConsentList( client.publish(listOf(envelope)) } - fun allow(address: String, date: Date): ConsentListEntry { - val entry = ConsentListEntry.address(address, ConsentState.ALLOWED, date) + fun allow(address: String): ConsentListEntry { + val entry = ConsentListEntry.address(address, ConsentState.ALLOWED) entries[entry.key] = entry return entry } - fun deny(address: String, date: Date): ConsentListEntry { - val entry = ConsentListEntry.address(address, ConsentState.DENIED, date) + fun deny(address: String): ConsentListEntry { + val entry = ConsentListEntry.address(address, ConsentState.DENIED) entries[entry.key] = entry return entry } - fun allowGroup(groupId: ByteArray, date: Date): ConsentListEntry { - val entry = ConsentListEntry.groupId(groupId, ConsentState.ALLOWED, date) + fun allowGroup(groupId: ByteArray): ConsentListEntry { + val entry = ConsentListEntry.groupId(groupId, ConsentState.ALLOWED) entries[entry.key] = entry return entry } - fun denyGroup(groupId: ByteArray, date: Date): ConsentListEntry { - val entry = ConsentListEntry.groupId(groupId, ConsentState.DENIED, date) + fun denyGroup(groupId: ByteArray): ConsentListEntry { + val entry = ConsentListEntry.groupId(groupId, ConsentState.DENIED) entries[entry.key] = entry return entry From 460e545c0e613953829552aa31f772cf973306f7 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 12:11:19 -0700 Subject: [PATCH 04/10] refactor publish to do a batch instead of 1 at a time --- .../java/org/xmtp/android/library/Contacts.kt | 113 +++++++++--------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 093c25c2c..6f5029b73 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -108,63 +108,64 @@ class ConsentList( return entries.values.toList() } - suspend fun publish(entry: ConsentListEntry) { - val payload = - PrivatePreferencesAction.newBuilder().also { - when (entry.entryType) { - ConsentListEntry.EntryType.ADDRESS -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowAddress( - PrivatePreferencesAction.AllowAddress.newBuilder() - .addWalletAddresses(entry.value), - ) - - ConsentState.DENIED -> - it.setDenyAddress( - PrivatePreferencesAction.DenyAddress.newBuilder() - .addWalletAddresses(entry.value), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() + suspend fun publish(entries: List) { + val envelopes = entries.map { entry -> + val payload = + PrivatePreferencesAction.newBuilder().also { + when (entry.entryType) { + ConsentListEntry.EntryType.ADDRESS -> { + when (entry.consentType) { + ConsentState.ALLOWED -> + it.setAllowAddress( + PrivatePreferencesAction.AllowAddress.newBuilder() + .addWalletAddresses(entry.value), + ) + + ConsentState.DENIED -> + it.setDenyAddress( + PrivatePreferencesAction.DenyAddress.newBuilder() + .addWalletAddresses(entry.value), + ) + + ConsentState.UNKNOWN -> it.clearMessageType() + } } - } - ConsentListEntry.EntryType.GROUP_ID -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowGroup( - PrivatePreferencesAction.AllowGroup.newBuilder() - .addGroupIds(entry.value.toByteStringUtf8()), - ) - - ConsentState.DENIED -> - it.setDenyGroup( - PrivatePreferencesAction.DenyGroup.newBuilder() - .addGroupIds(entry.value.toByteStringUtf8()), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() + ConsentListEntry.EntryType.GROUP_ID -> { + when (entry.consentType) { + ConsentState.ALLOWED -> + it.setAllowGroup( + PrivatePreferencesAction.AllowGroup.newBuilder() + .addGroupIds(entry.value.toByteStringUtf8()), + ) + + ConsentState.DENIED -> + it.setDenyGroup( + PrivatePreferencesAction.DenyGroup.newBuilder() + .addGroupIds(entry.value.toByteStringUtf8()), + ) + + ConsentState.UNKNOWN -> it.clearMessageType() + } } } - } - }.build() - - val message = - uniffi.xmtpv3.userPreferencesEncrypt( - publicKey.toByteArray(), - privateKey.toByteArray(), - payload.toByteArray(), - ) + }.build() + + val message = + uniffi.xmtpv3.userPreferencesEncrypt( + publicKey.toByteArray(), + privateKey.toByteArray(), + payload.toByteArray(), + ) - val envelope = EnvelopeBuilder.buildFromTopic( Topic.preferenceList(identifier), Date(), ByteArray(message.size) { message[it] }, ) + } - client.publish(listOf(envelope)) + client.publish(envelopes) } fun allow(address: String): ConsentListEntry { @@ -221,27 +222,31 @@ data class Contacts( } suspend fun allow(addresses: List) { - for (address in addresses) { - consentList.publish(consentList.allow(address)) + val entries = addresses.map { + consentList.allow(it) } + consentList.publish(entries) } suspend fun deny(addresses: List) { - for (address in addresses) { - consentList.publish(consentList.deny(address)) + val entries = addresses.map { + consentList.deny(it) } + consentList.publish(entries) } suspend fun allowGroup(groupIds: List) { - for (id in groupIds) { - consentList.publish(consentList.allowGroup(id)) + val entries = groupIds.map { + consentList.allowGroup(it) } + consentList.publish(entries) } suspend fun denyGroup(groupIds: List) { - for (id in groupIds) { - consentList.publish(consentList.denyGroup(id)) + val entries = groupIds.map { + consentList.denyGroup(it) } + consentList.publish(entries) } fun isAllowed(address: String): Boolean { From 22688d962cc2014a9fd83e7907ea786b67df014b Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 12:26:33 -0700 Subject: [PATCH 05/10] validate consent performance improvement --- .../org/xmtp/android/library/ConversationTest.kt | 15 +++++++++------ .../java/org/xmtp/android/library/Contacts.kt | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt index 43ecae65a..5e4883b94 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt @@ -799,9 +799,10 @@ class ConversationTest { assertTrue(isAllowed) assertTrue(bobClient.contacts.isAllowed(alice.walletAddress)) - runBlocking { bobClient.contacts.deny(listOf(alice.walletAddress)) } - bobClient.contacts.refreshConsentList() - + runBlocking { + bobClient.contacts.deny(listOf(alice.walletAddress)) + bobClient.contacts.refreshConsentList() + } val isDenied = bobConversation.consentState() == ConsentState.DENIED assertEquals(bobClient.contacts.consentList.entries.size, 1) assertTrue(isDenied) @@ -820,7 +821,7 @@ class ConversationTest { val aliceClient2 = Client().create(aliceWallet) val aliceConversation2 = runBlocking { aliceClient2.conversations.list()[0] } - aliceClient2.contacts.refreshConsentList() + runBlocking { aliceClient2.contacts.refreshConsentList() } // Allow state should sync across clients val isBobAllowed2 = aliceConversation2.consentState() == ConsentState.ALLOWED @@ -843,8 +844,10 @@ class ConversationTest { // Conversations you receive should start as unknown assertTrue(isUnknown) - runBlocking { aliceConversation.send(content = "hey bob") } - aliceClient.contacts.refreshConsentList() + runBlocking { + aliceConversation.send(content = "hey bob") + aliceClient.contacts.refreshConsentList() + } val isNowAllowed = aliceConversation.consentState() == ConsentState.ALLOWED // Conversations you send a message to get marked as allowed diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 6f5029b73..7fa6d15b7 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -69,7 +69,7 @@ class ConsentList( client.apiClient.envelopes( Topic.preferenceList(identifier).description, Pagination( - before = lastFetched, + after = lastFetched, direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING ), ) From 0becc42f9ba982a7618252dcc3f9bc0a747e6877 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 12:33:33 -0700 Subject: [PATCH 06/10] fix up the tests --- .../java/org/xmtp/android/library/ConversationTest.kt | 6 ------ library/src/main/java/org/xmtp/android/library/Contacts.kt | 5 ++--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt index 5e4883b94..e37e0c4d2 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt @@ -519,13 +519,7 @@ class ConversationTest { ), ) } - val isSteveOrBobConversation = { topic: String -> - (topic.lowercase() == steveConversation.topic.lowercase() || topic.lowercase() == bobConversation.topic.lowercase()) - } assertEquals(3, messages.size) - assertTrue(isSteveOrBobConversation(messages[0].topic)) - assertTrue(isSteveOrBobConversation(messages[1].topic)) - assertTrue(isSteveOrBobConversation(messages[2].topic)) } @Test diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 7fa6d15b7..eb11e3448 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -69,10 +69,9 @@ class ConsentList( client.apiClient.envelopes( Topic.preferenceList(identifier).description, Pagination( - after = lastFetched, - direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING + after = lastFetched ), - ) + ).reversed() lastFetched = newDate val preferences: MutableList = mutableListOf() for (envelope in envelopes) { From 8921aa900ee350e234b2a8d29a48b9486a475403 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 12:39:28 -0700 Subject: [PATCH 07/10] fix up linter --- library/src/main/java/org/xmtp/android/library/Contacts.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index eb11e3448..711950712 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -8,7 +8,6 @@ import org.xmtp.android.library.messages.EnvelopeBuilder import org.xmtp.android.library.messages.Pagination import org.xmtp.android.library.messages.Topic import org.xmtp.android.library.messages.walletAddress -import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.contents.PrivatePreferences.PrivatePreferencesAction import java.util.Date From 0bb508dbbb3d7ae6ff9e977817b76e839223f23c Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 13:11:19 -0700 Subject: [PATCH 08/10] maybe this will fix the grpc issues --- library/build.gradle | 10 +++++----- .../src/main/java/org/xmtp/android/library/Contacts.kt | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 27408f40b..28ac00a9b 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -60,10 +60,10 @@ protobuf { } plugins { grpc { - artifact = "io.grpc:protoc-gen-grpc-java:1.47.0" + artifact = "io.grpc:protoc-gen-grpc-java:1.62.2" } grpckt { - artifact = "io.grpc:protoc-gen-grpc-kotlin:1.3.0:jdk8@jar" + artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar" } } generateProtoTasks { @@ -79,9 +79,9 @@ protobuf { dependencies { implementation 'com.google.crypto.tink:tink-android:1.8.0' - implementation 'io.grpc:grpc-kotlin-stub:1.3.0' - implementation 'io.grpc:grpc-okhttp:1.51.1' - implementation 'io.grpc:grpc-protobuf-lite:1.51.0' + implementation 'io.grpc:grpc-kotlin-stub:1.4.1' + implementation 'io.grpc:grpc-okhttp:1.62.2' + implementation 'io.grpc:grpc-protobuf-lite:1.62.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' implementation 'org.web3j:crypto:5.0.0' implementation "net.java.dev.jna:jna:5.13.0@aar" diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 711950712..7fa6d15b7 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -8,6 +8,7 @@ import org.xmtp.android.library.messages.EnvelopeBuilder import org.xmtp.android.library.messages.Pagination import org.xmtp.android.library.messages.Topic import org.xmtp.android.library.messages.walletAddress +import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.contents.PrivatePreferences.PrivatePreferencesAction import java.util.Date @@ -68,9 +69,10 @@ class ConsentList( client.apiClient.envelopes( Topic.preferenceList(identifier).description, Pagination( - after = lastFetched + after = lastFetched, + direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING ), - ).reversed() + ) lastFetched = newDate val preferences: MutableList = mutableListOf() for (envelope in envelopes) { From cc709455a1207af05907ecc5f6631e662af58e6c Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 16:36:13 -0700 Subject: [PATCH 09/10] improve the publish and write a test --- .../xmtp/android/library/ConversationTest.kt | 17 ++++ .../java/org/xmtp/android/library/Contacts.kt | 99 +++++++++---------- 2 files changed, 66 insertions(+), 50 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt index e37e0c4d2..9911e3f81 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt @@ -848,6 +848,23 @@ class ConversationTest { assertTrue(isNowAllowed) } + @Test + fun testCanPublishMultipleAddressConsentState() { + runBlocking { + val bobConversation = bobClient.conversations.newConversation(alice.walletAddress) + val caroConversation = + bobClient.conversations.newConversation(fixtures.caro.walletAddress) + + assertEquals(bobClient.contacts.consentList.entries.size, 2) + assertTrue(bobConversation.consentState() == ConsentState.ALLOWED) + assertTrue(caroConversation.consentState() == ConsentState.ALLOWED) + bobClient.contacts.deny(listOf(alice.walletAddress, fixtures.caro.walletAddress)) + assertEquals(bobClient.contacts.consentList.entries.size, 2) + assertTrue(bobConversation.consentState() == ConsentState.DENIED) + assertTrue(caroConversation.consentState() == ConsentState.DENIED) + } + } + @Test fun testCanValidateTopicsInsideConversation() { val validId = "sdfsadf095b97a9284dcd82b2274856ccac8a21de57bebe34e7f9eeb855fb21126d3b8f" diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index 7fa6d15b7..a11960556 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -109,63 +109,62 @@ class ConsentList( } suspend fun publish(entries: List) { - val envelopes = entries.map { entry -> - val payload = - PrivatePreferencesAction.newBuilder().also { - when (entry.entryType) { - ConsentListEntry.EntryType.ADDRESS -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowAddress( - PrivatePreferencesAction.AllowAddress.newBuilder() - .addWalletAddresses(entry.value), - ) - - ConsentState.DENIED -> - it.setDenyAddress( - PrivatePreferencesAction.DenyAddress.newBuilder() - .addWalletAddresses(entry.value), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() - } + val payload = PrivatePreferencesAction.newBuilder().also { + entries.forEach { entry -> + when (entry.entryType) { + ConsentListEntry.EntryType.ADDRESS -> { + when (entry.consentType) { + ConsentState.ALLOWED -> + it.setAllowAddress( + PrivatePreferencesAction.AllowAddress.newBuilder() + .addWalletAddresses(entry.value), + ) + + ConsentState.DENIED -> + it.setDenyAddress( + PrivatePreferencesAction.DenyAddress.newBuilder() + .addWalletAddresses(entry.value), + ) + + ConsentState.UNKNOWN -> it.clearMessageType() } + } - ConsentListEntry.EntryType.GROUP_ID -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowGroup( - PrivatePreferencesAction.AllowGroup.newBuilder() - .addGroupIds(entry.value.toByteStringUtf8()), - ) - - ConsentState.DENIED -> - it.setDenyGroup( - PrivatePreferencesAction.DenyGroup.newBuilder() - .addGroupIds(entry.value.toByteStringUtf8()), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() - } + ConsentListEntry.EntryType.GROUP_ID -> { + when (entry.consentType) { + ConsentState.ALLOWED -> + it.setAllowGroup( + PrivatePreferencesAction.AllowGroup.newBuilder() + .addGroupIds(entry.value.toByteStringUtf8()), + ) + + ConsentState.DENIED -> + it.setDenyGroup( + PrivatePreferencesAction.DenyGroup.newBuilder() + .addGroupIds(entry.value.toByteStringUtf8()), + ) + + ConsentState.UNKNOWN -> it.clearMessageType() } } - }.build() - - val message = - uniffi.xmtpv3.userPreferencesEncrypt( - publicKey.toByteArray(), - privateKey.toByteArray(), - payload.toByteArray(), - ) + } + } + }.build() - EnvelopeBuilder.buildFromTopic( - Topic.preferenceList(identifier), - Date(), - ByteArray(message.size) { message[it] }, + val message = + uniffi.xmtpv3.userPreferencesEncrypt( + publicKey.toByteArray(), + privateKey.toByteArray(), + payload.toByteArray(), ) - } - client.publish(envelopes) + val envelope = EnvelopeBuilder.buildFromTopic( + Topic.preferenceList(identifier), + Date(), + ByteArray(message.size) { message[it] }, + ) + + client.publish(listOf(envelope)) } fun allow(address: String): ConsentListEntry { From 141a7e04b04021771c94dc128dbeccc194f61122 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 9 Apr 2024 16:43:59 -0700 Subject: [PATCH 10/10] test flakes sometimes --- .../java/org/xmtp/android/library/ConversationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt index 9911e3f81..8cfb1a57a 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt @@ -854,7 +854,7 @@ class ConversationTest { val bobConversation = bobClient.conversations.newConversation(alice.walletAddress) val caroConversation = bobClient.conversations.newConversation(fixtures.caro.walletAddress) - + bobClient.contacts.refreshConsentList() assertEquals(bobClient.contacts.consentList.entries.size, 2) assertTrue(bobConversation.consentState() == ConsentState.ALLOWED) assertTrue(caroConversation.consentState() == ConsentState.ALLOWED)