From ac769a2945f23dea42e1249b5503e2aaa3c3867d Mon Sep 17 00:00:00 2001 From: tuddman Date: Thu, 15 Feb 2024 17:15:30 +0100 Subject: [PATCH 01/11] fix: change block to denyDM --- .../java/org/xmtp/android/library/ContactsTest.kt | 2 +- library/src/main/java/org/xmtp/android/library/Contacts.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt index 7cb2f6104..d6dd89c03 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt @@ -54,7 +54,7 @@ class ContactsTest { } @Test - fun testBlockAddress() { + fun testDenyAddress() { val fixtures = fixtures() val contacts = fixtures.bobClient.contacts 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 f433e6e1d..e9a42a7fe 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -87,11 +87,11 @@ class ConsentList(val client: Client) { val payload = PrivatePreferencesAction.newBuilder().also { when (entry.consentType) { ConsentState.ALLOWED -> it.setAllow( - PrivatePreferencesAction.Allow.newBuilder().addWalletAddresses(entry.value) + PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value) ) - ConsentState.DENIED -> it.setBlock( - PrivatePreferencesAction.Block.newBuilder().addWalletAddresses(entry.value) + ConsentState.DENIED -> it.setDeny( + PrivatePreferencesAction.DenyDM.newBuilder().addWalletAddresses(entry.value) ) ConsentState.UNKNOWN -> it.clearMessageType() From da52f8a3d45b660065d20c979ee000a3856f639a Mon Sep 17 00:00:00 2001 From: tuddman Date: Thu, 15 Feb 2024 19:50:09 +0100 Subject: [PATCH 02/11] feat: WIP on adding group consent support --- .../org/xmtp/android/library/ContactsTest.kt | 30 ++++++++ .../java/org/xmtp/android/library/Contacts.kt | 74 ++++++++++++++++++- .../org/xmtp/android/library/TestHelpers.kt | 20 +++++ 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt index d6dd89c03..5ec0f58fc 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt @@ -67,4 +67,34 @@ class ContactsTest { result = contacts.isDenied(fixtures.alice.walletAddress) assert(result) } + + @Test + fun testAllowGroup() { + val groupFixtures = groupFixtures() + + val groups = groupFixtures.bobClient.groups + var result = contacts.isGroupAllowed(groupFixtures.) + + assert(!result) + + contacts.allowGroup(listOf(groupFixtures.)) + + result = contacts.isGroupAllowed(groupFixtures.) + assert(result) + } + + @Test + fun testDenyGroup() { + val groupFixtures = groupFixtures() + + val groups = groupFixtures.bobClient.groups + var result = contacts.isGroupAllowed(groupFixtures.) + + assert(!result) + + contacts.denyGroup(listOf(groupFixtures.)) + + result = contacts.isGroupDenied(groupFixtures.) + assert(result) + } } 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 e9a42a7fe..1addaf453 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -23,7 +23,8 @@ data class ConsentListEntry( val consentType: ConsentState, ) { enum class EntryType { - ADDRESS + ADDRESS, + GROUP_ID } companion object { @@ -33,6 +34,13 @@ data class ConsentListEntry( ): ConsentListEntry { return ConsentListEntry(address, EntryType.ADDRESS, type) } + + fun groupId( + groupId: ByteArray, + type: ConsentState = ConsentState.UNKNOWN, + ): ConsentListEntry { + return ConsentListEntry(groupId, EntryType.GROUP_ID, type) + } } val key: String @@ -72,12 +80,18 @@ class ConsentList(val client: Client) { } preferences.iterator().forEach { preference -> - preference.allow?.walletAddressesList?.forEach { address -> + preference.allowDM?.walletAddressesList?.forEach { address -> consentList.allow(address) } - preference.block?.walletAddressesList?.forEach { address -> + preference.denyDM?.walletAddressesList?.forEach { address -> consentList.deny(address) } + preference.allowGroup?.groupIdsList?.forEach { groupId -> + consentList.allowGroup(groupId) + } + preference.denyGroup?.groupIdsList?.forEach { groupId -> + consentList.denyGroup(groupId) + } } return consentList @@ -85,7 +99,7 @@ class ConsentList(val client: Client) { fun publish(entry: ConsentListEntry) { val payload = PrivatePreferencesAction.newBuilder().also { - when (entry.consentType) { + when (entry.consentType && entry.entryType == ADDRESS) { ConsentState.ALLOWED -> it.setAllow( PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value) ) @@ -94,6 +108,18 @@ class ConsentList(val client: Client) { PrivatePreferencesAction.DenyDM.newBuilder().addWalletAddresses(entry.value) ) + ConsentState.UNKNOWN -> it.clearMessageType() + } + + when (entry.consentType && entry.entryType == GROUP_ID) { + ConsentState.ALLOWED -> it.setAllow( + PrivatePreferencesAction.AllowGroup.newBuilder().addGroupIds(entry.value) + ) + + ConsentState.DENIED -> it.setDeny( + PrivatePreferencesAction.DenyGroup.newBuilder().addGroupIds(entry.value) + ) + ConsentState.UNKNOWN -> it.clearMessageType() } }.build() @@ -127,11 +153,31 @@ class ConsentList(val client: Client) { return entry } + fun allowGroup(groupId: ByteArray): ConsentListEntry { + val entry = ConsentListEntry.groupId(groupId, ConsentState.ALLOWED) + entries[ConsentListEntry.groupId(groupId).key] = entry + + return entry + } + + fun denyGroup(groupId: ByteArray): ConsentListEntry { + val entry = ConsentListEntry.groupId(groupId, ConsentState.DENIED) + entries[ConsentListEntry.groupId(groupId).key] = entry + + return entry + } + fun state(address: String): ConsentState { val entry = entries[ConsentListEntry.address(address).key] return entry?.consentType ?: ConsentState.UNKNOWN } + + fun groupState(groupId: ByteArray): ConsentState { + val entry = entries[ConsentListEntry.groupId(groupId).key] + + return entry?.consentType ?: ConsentState.UNKNOWN + } } data class Contacts( @@ -169,6 +215,26 @@ data class Contacts( } } + fun isGroupAllowed(groupId: ByteArray): Boolean { + return consentList.groupState(groupId) == ConsentState.ALLOWED + } + + fun isGroupDenied(groupId: ByteArray): Boolean { + return consentList.groupState(groupId) == ConsentState.DENIED + } + + fun allowGroup(groupIds: List) { + for (id in groupIds) { + ConsentList(client).publish(consentList.allowGroup(id)) + } + } + + fun denyGroup(groupIds: List) { + for (id in groupIds) { + ConsentList(client).publish(consentList.denyGroup(id)) + } + } + fun has(peerAddress: String): Boolean = knownBundles[peerAddress] != null diff --git a/library/src/test/java/org/xmtp/android/library/TestHelpers.kt b/library/src/test/java/org/xmtp/android/library/TestHelpers.kt index 8ea449f9b..9ab23e97c 100644 --- a/library/src/test/java/org/xmtp/android/library/TestHelpers.kt +++ b/library/src/test/java/org/xmtp/android/library/TestHelpers.kt @@ -234,3 +234,23 @@ data class Fixtures(val aliceAccount: PrivateKeyBuilder, val bobAccount: Private fun fixtures(): Fixtures = Fixtures() + +data class GroupFixtures() { + var fakeApiClient: FakeApiClient = FakeApiClient() + var alice: PrivateKey = aliceAccount.getPrivateKey() + var aliceClient: Client = Client().create(account = aliceAccount, apiClient = fakeApiClient) + var bob: PrivateKey = bobAccount.getPrivateKey() + var bobClient: Client = Client().create(account = bobAccount, apiClient = fakeApiClient) + var charlie: PrivateKey = bobAccount.getPrivateKey() + var charlieClient: Client = Client().create(account = charlieAccount, apiClient = fakeApiClient) + + constructor() : this(aliceAccount = PrivateKeyBuilder(), bobAccount = PrivateKeyBuilder(), charlieAccount = PrivateKeyBuilder()) + + // TODO: Insert necessary group creation/publish functions + +} + +fun groupFixtures(): GroupFixtures = + GroupFixtures() + + From 7f85026f857650f99b38e88bb5c3a9062634a233 Mon Sep 17 00:00:00 2001 From: tuddman Date: Fri, 16 Feb 2024 16:22:10 +0100 Subject: [PATCH 03/11] fix: updates Proto to 3.43.0 and fixes syntax errors --- library/build.gradle | 2 +- .../java/org/xmtp/android/library/Contacts.kt | 62 +++++++++++-------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index aee6d99ff..ff2692cec 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -86,7 +86,7 @@ dependencies { implementation 'org.web3j:crypto:5.0.0' implementation "net.java.dev.jna:jna:5.13.0@aar" api 'com.google.protobuf:protobuf-kotlin-lite:3.22.3' - api 'org.xmtp:proto-kotlin:3.40.1' + api 'org.xmtp:proto-kotlin:3.43.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'app.cash.turbine:turbine:0.12.1' 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 1addaf453..ef681f569 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -1,5 +1,7 @@ package org.xmtp.android.library +import com.google.protobuf.ByteString +import com.google.protobuf.kotlin.toByteStringUtf8 import kotlinx.coroutines.runBlocking import org.xmtp.android.library.messages.ContactBundle import org.xmtp.android.library.messages.ContactBundleBuilder @@ -9,6 +11,7 @@ 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.security.KeyStore.Entry import java.util.Date enum class ConsentState { @@ -39,7 +42,7 @@ data class ConsentListEntry( groupId: ByteArray, type: ConsentState = ConsentState.UNKNOWN, ): ConsentListEntry { - return ConsentListEntry(groupId, EntryType.GROUP_ID, type) + return ConsentListEntry(String(groupId), EntryType.GROUP_ID, type) } } @@ -80,17 +83,17 @@ class ConsentList(val client: Client) { } preferences.iterator().forEach { preference -> - preference.allowDM?.walletAddressesList?.forEach { address -> + preference.allowDm?.walletAddressesList?.forEach { address -> consentList.allow(address) } - preference.denyDM?.walletAddressesList?.forEach { address -> + preference.denyDm?.walletAddressesList?.forEach { address -> consentList.deny(address) } preference.allowGroup?.groupIdsList?.forEach { groupId -> - consentList.allowGroup(groupId) + consentList.allowGroup(groupId.toByteArray()) } preference.denyGroup?.groupIdsList?.forEach { groupId -> - consentList.denyGroup(groupId) + consentList.denyGroup(groupId.toByteArray()) } } @@ -99,28 +102,33 @@ class ConsentList(val client: Client) { fun publish(entry: ConsentListEntry) { val payload = PrivatePreferencesAction.newBuilder().also { - when (entry.consentType && entry.entryType == ADDRESS) { - ConsentState.ALLOWED -> it.setAllow( - PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value) - ) - - ConsentState.DENIED -> it.setDeny( - PrivatePreferencesAction.DenyDM.newBuilder().addWalletAddresses(entry.value) - ) - - ConsentState.UNKNOWN -> it.clearMessageType() - } - - when (entry.consentType && entry.entryType == GROUP_ID) { - ConsentState.ALLOWED -> it.setAllow( - PrivatePreferencesAction.AllowGroup.newBuilder().addGroupIds(entry.value) - ) - - ConsentState.DENIED -> it.setDeny( - PrivatePreferencesAction.DenyGroup.newBuilder().addGroupIds(entry.value) - ) - - ConsentState.UNKNOWN -> it.clearMessageType() + when (entry.entryType) { + ConsentListEntry.EntryType.ADDRESS -> { + when (entry.consentType) { + ConsentState.ALLOWED -> it.setAllowDm( + PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value) + ) + + ConsentState.DENIED -> it.setDenyDm( + PrivatePreferencesAction.DenyDM.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() + } + } } }.build() From 92e50acc3694c18ae6bd1e22460360970fd48e20 Mon Sep 17 00:00:00 2001 From: tuddman Date: Fri, 16 Feb 2024 19:27:21 +0100 Subject: [PATCH 04/11] fix: fixes group consent tests, typo and ordering --- .../org/xmtp/android/library/ContactsTest.kt | 34 +---------- .../org/xmtp/android/library/GroupTest.kt | 61 +++++++++++++++---- .../java/org/xmtp/android/library/Contacts.kt | 34 +++++------ .../org/xmtp/android/library/Conversation.kt | 2 +- .../org/xmtp/android/library/TestHelpers.kt | 32 ++-------- 5 files changed, 73 insertions(+), 90 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt index 5ec0f58fc..6cccc9b99 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt @@ -12,8 +12,8 @@ class ContactsTest { fun testNormalizesAddresses() { val fixtures = fixtures() fixtures.bobClient.ensureUserContactPublished() - val bobAddressLowercased = fixtures.bobClient.address?.lowercase() - val bobContact = fixtures.aliceClient.getUserContact(peerAddress = bobAddressLowercased!!) + val bobAddressLowerCased = fixtures.bobClient.address.lowercase() + val bobContact = fixtures.aliceClient.getUserContact(peerAddress = bobAddressLowerCased) assert(bobContact != null) } @@ -67,34 +67,4 @@ class ContactsTest { result = contacts.isDenied(fixtures.alice.walletAddress) assert(result) } - - @Test - fun testAllowGroup() { - val groupFixtures = groupFixtures() - - val groups = groupFixtures.bobClient.groups - var result = contacts.isGroupAllowed(groupFixtures.) - - assert(!result) - - contacts.allowGroup(listOf(groupFixtures.)) - - result = contacts.isGroupAllowed(groupFixtures.) - assert(result) - } - - @Test - fun testDenyGroup() { - val groupFixtures = groupFixtures() - - val groups = groupFixtures.bobClient.groups - var result = contacts.isGroupAllowed(groupFixtures.) - - assert(!result) - - contacts.denyGroup(listOf(groupFixtures.)) - - result = contacts.isGroupDenied(groupFixtures.) - assert(result) - } } diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt index 7c5167363..f0d2982f7 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -20,17 +20,17 @@ import org.xmtp.android.library.messages.walletAddress @RunWith(AndroidJUnit4::class) class GroupTest { - lateinit var fakeApiClient: FakeApiClient - lateinit var alixWallet: PrivateKeyBuilder - lateinit var boWallet: PrivateKeyBuilder - lateinit var alix: PrivateKey - lateinit var alixClient: Client - lateinit var bo: PrivateKey - lateinit var boClient: Client - lateinit var caroWallet: PrivateKeyBuilder - lateinit var caro: PrivateKey - lateinit var caroClient: Client - lateinit var fixtures: Fixtures + private lateinit var fakeApiClient: FakeApiClient + private lateinit var alixWallet: PrivateKeyBuilder + private lateinit var boWallet: PrivateKeyBuilder + private lateinit var alix: PrivateKey + private lateinit var alixClient: Client + private lateinit var bo: PrivateKey + private lateinit var boClient: Client + private lateinit var caroWallet: PrivateKeyBuilder + private lateinit var caro: PrivateKey + private lateinit var caroClient: Client + private lateinit var fixtures: Fixtures @Before fun setUp() { @@ -150,7 +150,7 @@ class GroupTest { @Test fun testCannotSendMessageToGroupMemberNotOnV3() { - var fakeApiClient = FakeApiClient() + val fakeApiClient = FakeApiClient() val chuxAccount = PrivateKeyBuilder() val chux: PrivateKey = chuxAccount.getPrivateKey() val chuxClient: Client = Client().create(account = chuxAccount, apiClient = fakeApiClient) @@ -264,4 +264,41 @@ class GroupTest { assertEquals(conversation.topic, awaitItem().topic) } } + + @Test + fun testCanAllowGroup() { + val group = boClient.conversations.newGroup( + listOf( + alix.walletAddress, + caro.walletAddress + ) + ) + + var result = boClient.contacts.isGroupAllowed(group.id) + + assert(!result) + + boClient.contacts.allowGroup(listOf(group.id)) + + result = boClient.contacts.isGroupAllowed(group.id) + assert(result) + } + + @Test + fun testCanDenyGroup() { + val group = boClient.conversations.newGroup( + listOf( + alix.walletAddress, + caro.walletAddress + ) + ) + var result = boClient.contacts.isGroupAllowed(group.id) + + assert(!result) + + boClient.contacts.denyGroup(listOf(group.id)) + + result = boClient.contacts.isGroupDenied(group.id) + assert(result) + } } 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 ef681f569..36ad1734d 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -1,6 +1,5 @@ package org.xmtp.android.library -import com.google.protobuf.ByteString import com.google.protobuf.kotlin.toByteStringUtf8 import kotlinx.coroutines.runBlocking import org.xmtp.android.library.messages.ContactBundle @@ -11,7 +10,6 @@ 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.security.KeyStore.Entry import java.util.Date enum class ConsentState { @@ -203,14 +201,6 @@ data class Contacts( return consentList } - fun isAllowed(address: String): Boolean { - return consentList.state(address) == ConsentState.ALLOWED - } - - fun isDenied(address: String): Boolean { - return consentList.state(address) == ConsentState.DENIED - } - fun allow(addresses: List) { for (address in addresses) { ConsentList(client).publish(consentList.allow(address)) @@ -223,14 +213,6 @@ data class Contacts( } } - fun isGroupAllowed(groupId: ByteArray): Boolean { - return consentList.groupState(groupId) == ConsentState.ALLOWED - } - - fun isGroupDenied(groupId: ByteArray): Boolean { - return consentList.groupState(groupId) == ConsentState.DENIED - } - fun allowGroup(groupIds: List) { for (id in groupIds) { ConsentList(client).publish(consentList.allowGroup(id)) @@ -243,6 +225,22 @@ data class Contacts( } } + fun isAllowed(address: String): Boolean { + return consentList.state(address) == ConsentState.ALLOWED + } + + fun isDenied(address: String): Boolean { + return consentList.state(address) == ConsentState.DENIED + } + + fun isGroupAllowed(groupId: ByteArray): Boolean { + return consentList.groupState(groupId) == ConsentState.ALLOWED + } + + fun isGroupDenied(groupId: ByteArray): Boolean { + return consentList.groupState(groupId) == ConsentState.DENIED + } + fun has(peerAddress: String): Boolean = knownBundles[peerAddress] != null diff --git a/library/src/main/java/org/xmtp/android/library/Conversation.kt b/library/src/main/java/org/xmtp/android/library/Conversation.kt index 534e42a0f..676ad4eb2 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -214,7 +214,7 @@ sealed class Conversation { return client.address } - // Is the topic of the conversation depending of the version + // Is the topic of the conversation depending on the version val topic: String get() { return when (this) { diff --git a/library/src/test/java/org/xmtp/android/library/TestHelpers.kt b/library/src/test/java/org/xmtp/android/library/TestHelpers.kt index 9ab23e97c..ada882eb5 100644 --- a/library/src/test/java/org/xmtp/android/library/TestHelpers.kt +++ b/library/src/test/java/org/xmtp/android/library/TestHelpers.kt @@ -43,13 +43,11 @@ class FakeWallet : SigningKey { } override suspend fun sign(data: ByteArray): Signature { - val signature = privateKeyBuilder.sign(data) - return signature + return privateKeyBuilder.sign(data) } override suspend fun sign(message: String): Signature { - val signature = privateKeyBuilder.sign(message) - return signature + return privateKeyBuilder.sign(message) } override val address: String @@ -66,8 +64,8 @@ class FakeApiClient : ApiClient { override val environment: XMTPEnvironment = XMTPEnvironment.LOCAL private var authToken: String? = null private val responses: MutableMap> = mutableMapOf() - val published: MutableList = mutableListOf() - var forbiddingQueries = false + private val published: MutableList = mutableListOf() + private var forbiddingQueries = false private var stream = FakeStreamHolder() fun assertNoPublish(callback: () -> Unit) { @@ -233,24 +231,4 @@ data class Fixtures(val aliceAccount: PrivateKeyBuilder, val bobAccount: Private } fun fixtures(): Fixtures = - Fixtures() - -data class GroupFixtures() { - var fakeApiClient: FakeApiClient = FakeApiClient() - var alice: PrivateKey = aliceAccount.getPrivateKey() - var aliceClient: Client = Client().create(account = aliceAccount, apiClient = fakeApiClient) - var bob: PrivateKey = bobAccount.getPrivateKey() - var bobClient: Client = Client().create(account = bobAccount, apiClient = fakeApiClient) - var charlie: PrivateKey = bobAccount.getPrivateKey() - var charlieClient: Client = Client().create(account = charlieAccount, apiClient = fakeApiClient) - - constructor() : this(aliceAccount = PrivateKeyBuilder(), bobAccount = PrivateKeyBuilder(), charlieAccount = PrivateKeyBuilder()) - - // TODO: Insert necessary group creation/publish functions - -} - -fun groupFixtures(): GroupFixtures = - GroupFixtures() - - + Fixtures() \ No newline at end of file From 0b1585f7c80d7b3f23101536c651d1b8bd7ed8a2 Mon Sep 17 00:00:00 2001 From: tuddman Date: Fri, 16 Feb 2024 19:46:09 +0100 Subject: [PATCH 05/11] fix: kotlin likes newlines at the end --- library/src/test/java/org/xmtp/android/library/TestHelpers.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/test/java/org/xmtp/android/library/TestHelpers.kt b/library/src/test/java/org/xmtp/android/library/TestHelpers.kt index ada882eb5..8ac997b1f 100644 --- a/library/src/test/java/org/xmtp/android/library/TestHelpers.kt +++ b/library/src/test/java/org/xmtp/android/library/TestHelpers.kt @@ -231,4 +231,4 @@ data class Fixtures(val aliceAccount: PrivateKeyBuilder, val bobAccount: Private } fun fixtures(): Fixtures = - Fixtures() \ No newline at end of file + Fixtures() From b231c00a4aeea37466f798f24812b4d442bc43c5 Mon Sep 17 00:00:00 2001 From: tuddman Date: Fri, 16 Feb 2024 19:53:11 +0100 Subject: [PATCH 06/11] fix: whitespace --- library/src/main/java/org/xmtp/android/library/Contacts.kt | 2 +- 1 file changed, 1 insertion(+), 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 36ad1734d..62805d0a4 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -178,7 +178,7 @@ class ConsentList(val client: Client) { return entry?.consentType ?: ConsentState.UNKNOWN } - + fun groupState(groupId: ByteArray): ConsentState { val entry = entries[ConsentListEntry.groupId(groupId).key] From 13777755c8eddeb9b000d5515698115202e65c37 Mon Sep 17 00:00:00 2001 From: tuddman Date: Fri, 16 Feb 2024 19:56:20 +0100 Subject: [PATCH 07/11] fix: runs ktlint on Contacts.kt --- .../java/org/xmtp/android/library/Contacts.kt | 125 +++++++++--------- 1 file changed, 66 insertions(+), 59 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 62805d0a4..ac3f0106e 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -15,7 +15,7 @@ import java.util.Date enum class ConsentState { ALLOWED, DENIED, - UNKNOWN + UNKNOWN, } data class ConsentListEntry( @@ -25,7 +25,7 @@ data class ConsentListEntry( ) { enum class EntryType { ADDRESS, - GROUP_ID + GROUP_ID, } companion object { @@ -54,29 +54,32 @@ class ConsentList(val client: Client) { client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes private val privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes - private val identifier: String = uniffi.xmtpv3.generatePrivatePreferencesTopicIdentifier( - privateKey.toByteArray() - ) + private val identifier: String = + uniffi.xmtpv3.generatePrivatePreferencesTopicIdentifier( + privateKey.toByteArray(), + ) @OptIn(ExperimentalUnsignedTypes::class) suspend fun load(): ConsentList { - val envelopes = client.apiClient.envelopes( - Topic.preferenceList(identifier).description, - Pagination(direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING) - ) + val envelopes = + client.apiClient.envelopes( + Topic.preferenceList(identifier).description, + Pagination(direction = MessageApiOuterClass.SortDirection.SORT_DIRECTION_ASCENDING), + ) val consentList = ConsentList(client) val preferences: MutableList = mutableListOf() for (envelope in envelopes) { - val payload = uniffi.xmtpv3.userPreferencesDecrypt( - publicKey.toByteArray(), - privateKey.toByteArray(), - envelope.message.toByteArray() - ) + val payload = + uniffi.xmtpv3.userPreferencesDecrypt( + publicKey.toByteArray(), + privateKey.toByteArray(), + envelope.message.toByteArray(), + ) preferences.add( PrivatePreferencesAction.parseFrom( - payload.toUByteArray().toByteArray() - ) + payload.toUByteArray().toByteArray(), + ), ) } @@ -99,48 +102,55 @@ class ConsentList(val client: Client) { } fun publish(entry: ConsentListEntry) { - val payload = PrivatePreferencesAction.newBuilder().also { - when (entry.entryType) { - ConsentListEntry.EntryType.ADDRESS -> { - when (entry.consentType) { - ConsentState.ALLOWED -> it.setAllowDm( - PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value) - ) - - ConsentState.DENIED -> it.setDenyDm( - PrivatePreferencesAction.DenyDM.newBuilder().addWalletAddresses(entry.value) - ) - - ConsentState.UNKNOWN -> it.clearMessageType() + val payload = + PrivatePreferencesAction.newBuilder().also { + when (entry.entryType) { + ConsentListEntry.EntryType.ADDRESS -> { + when (entry.consentType) { + ConsentState.ALLOWED -> + it.setAllowDm( + PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value), + ) + + ConsentState.DENIED -> + it.setDenyDm( + PrivatePreferencesAction.DenyDM.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() + }.build() - val message = uniffi.xmtpv3.userPreferencesEncrypt( - publicKey.toByteArray(), - privateKey.toByteArray(), - payload.toByteArray() - ) + val message = + uniffi.xmtpv3.userPreferencesEncrypt( + publicKey.toByteArray(), + privateKey.toByteArray(), + payload.toByteArray(), + ) - val envelope = EnvelopeBuilder.buildFromTopic( - Topic.preferenceList(identifier), - Date(), - ByteArray(message.size) { message[it].toByte() } - ) + val envelope = + EnvelopeBuilder.buildFromTopic( + Topic.preferenceList(identifier), + Date(), + ByteArray(message.size) { message[it].toByte() }, + ) client.publish(listOf(envelope)) } @@ -178,7 +188,7 @@ class ConsentList(val client: Client) { return entry?.consentType ?: ConsentState.UNKNOWN } - + fun groupState(groupId: ByteArray): ConsentState { val entry = entries[ConsentListEntry.groupId(groupId).key] @@ -191,7 +201,6 @@ data class Contacts( val knownBundles: MutableMap = mutableMapOf(), val hasIntroduced: MutableMap = mutableMapOf(), ) { - var consentList: ConsentList = ConsentList(client) fun refreshConsentList(): ConsentList { @@ -241,11 +250,9 @@ data class Contacts( return consentList.groupState(groupId) == ConsentState.DENIED } - fun has(peerAddress: String): Boolean = - knownBundles[peerAddress] != null + fun has(peerAddress: String): Boolean = knownBundles[peerAddress] != null - fun needsIntroduction(peerAddress: String): Boolean = - hasIntroduced[peerAddress] != true + fun needsIntroduction(peerAddress: String): Boolean = hasIntroduced[peerAddress] != true fun find(peerAddress: String): ContactBundle? { val knownBundle = knownBundles[peerAddress] From d79bac4b10668779a8c1b5cf8f5a3a35f8958c91 Mon Sep 17 00:00:00 2001 From: tuddman Date: Tue, 20 Feb 2024 22:59:16 +0100 Subject: [PATCH 08/11] updates proto to 3.43.2 and makes corresponding changes --- library/build.gradle | 2 +- .../main/java/org/xmtp/android/library/Contacts.kt | 12 ++++++------ .../java/org/xmtp/android/library/Conversation.kt | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index ff2692cec..126525620 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -86,7 +86,7 @@ dependencies { implementation 'org.web3j:crypto:5.0.0' implementation "net.java.dev.jna:jna:5.13.0@aar" api 'com.google.protobuf:protobuf-kotlin-lite:3.22.3' - api 'org.xmtp:proto-kotlin:3.43.0' + api 'org.xmtp:proto-kotlin:3.43.2' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'app.cash.turbine:turbine:0.12.1' 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 ac3f0106e..b47988a4a 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -84,10 +84,10 @@ class ConsentList(val client: Client) { } preferences.iterator().forEach { preference -> - preference.allowDm?.walletAddressesList?.forEach { address -> + preference.allowAddress?.walletAddressesList?.forEach { address -> consentList.allow(address) } - preference.denyDm?.walletAddressesList?.forEach { address -> + preference.denyAddress?.walletAddressesList?.forEach { address -> consentList.deny(address) } preference.allowGroup?.groupIdsList?.forEach { groupId -> @@ -108,13 +108,13 @@ class ConsentList(val client: Client) { ConsentListEntry.EntryType.ADDRESS -> { when (entry.consentType) { ConsentState.ALLOWED -> - it.setAllowDm( - PrivatePreferencesAction.AllowDM.newBuilder().addWalletAddresses(entry.value), + it.setAllowAddress( + PrivatePreferencesAction.AllowAddress.newBuilder().addWalletAddresses(entry.value), ) ConsentState.DENIED -> - it.setDenyDm( - PrivatePreferencesAction.DenyDM.newBuilder().addWalletAddresses(entry.value), + it.setDenyAddress( + PrivatePreferencesAction.DenyAddress.newBuilder().addWalletAddresses(entry.value), ) ConsentState.UNKNOWN -> it.clearMessageType() diff --git a/library/src/main/java/org/xmtp/android/library/Conversation.kt b/library/src/main/java/org/xmtp/android/library/Conversation.kt index 676ad4eb2..6e2685b58 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -100,7 +100,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.client.contacts.consentList.state(address = peerAddress) is V2 -> conversationV2.client.contacts.consentList.state(address = peerAddress) - is Group -> ConsentState.UNKNOWN // No such thing as consent for a group + is Group -> group.client.contacts.consentList.groupState(groupId = group.id) } } From 459920b44cc69501c4a18da2e478d7897f81c888 Mon Sep 17 00:00:00 2001 From: tuddman Date: Tue, 20 Feb 2024 23:15:08 +0100 Subject: [PATCH 09/11] fix: per PR feedback, adds additional implicit consent --- .../src/main/java/org/xmtp/android/library/Conversations.kt | 2 ++ library/src/main/java/org/xmtp/android/library/Group.kt | 3 +++ 2 files changed, 5 insertions(+) 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 ef8605027..73334ec3d 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversations.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversations.kt @@ -108,6 +108,8 @@ data class Conversations( libXMTPConversations?.createGroup(accountAddresses, permissions = null) ?: throw XMTPException("Client does not support Groups") } + client.contacts.allowGroup(groupIds = listOf(group.id())) + return Group(client, group) } diff --git a/library/src/main/java/org/xmtp/android/library/Group.kt b/library/src/main/java/org/xmtp/android/library/Group.kt index a425da20d..5ebd09baf 100644 --- a/library/src/main/java/org/xmtp/android/library/Group.kt +++ b/library/src/main/java/org/xmtp/android/library/Group.kt @@ -36,6 +36,9 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { } fun send(encodedContent: EncodedContent): String { + if (client.contacts.consentList.groupState(groupId = id) == ConsentState.UNKNOWN) { + client.contacts.allowGroup(groupIds = listOf(id)) + } runBlocking { libXMTPGroup.send(contentBytes = encodedContent.toByteArray()) } From 12c7a746d37ff05fc5c20661427376a1565eb9bf Mon Sep 17 00:00:00 2001 From: tuddman Date: Tue, 20 Feb 2024 23:16:25 +0100 Subject: [PATCH 10/11] fix: from warning - removes duplicative conversion --- library/src/main/java/org/xmtp/android/library/Contacts.kt | 2 +- 1 file changed, 1 insertion(+), 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 b47988a4a..09bfa8f6e 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -149,7 +149,7 @@ class ConsentList(val client: Client) { EnvelopeBuilder.buildFromTopic( Topic.preferenceList(identifier), Date(), - ByteArray(message.size) { message[it].toByte() }, + ByteArray(message.size) { message[it] }, ) client.publish(listOf(envelope)) From 50b9ad34d87e7102c68961591bc7940e6833d7e5 Mon Sep 17 00:00:00 2001 From: tuddman Date: Wed, 21 Feb 2024 16:53:40 +0100 Subject: [PATCH 11/11] fix: adds tests for group allowance and consent --- .../java/org/xmtp/android/library/GroupTest.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt index dda3b5e9e..0402a8754 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -99,6 +99,9 @@ class GroupTest { assert(boGroup.id.isNotEmpty()) assert(alixGroup.id.isNotEmpty()) + assertEquals(boClient.contacts.consentList.groupState(boGroup.id), ConsentState.ALLOWED) + assertEquals(alixClient.contacts.consentList.groupState(alixGroup.id), ConsentState.UNKNOWN) + boGroup.addMembers(listOf(caro.walletAddress)) runBlocking { alixGroup.sync() } assertEquals(alixGroup.memberAddresses().size, 3) @@ -263,6 +266,16 @@ class GroupTest { } } + @Test + fun testGroupStartsWithAllowedState() { + val group = boClient.conversations.newGroup(listOf(alix.walletAddress)) + group.send("howdy") + group.send("gm") + runBlocking { group.sync() } + assert(boClient.contacts.isGroupAllowed(group.id)) + assertEquals(boClient.contacts.consentList.groupState(group.id), ConsentState.ALLOWED) + } + @Test fun testCanSendMessageToGroup() { val group = boClient.conversations.newGroup(listOf(alix.walletAddress))