Skip to content

Commit

Permalink
add the allow list implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Oct 24, 2023
1 parent 0fcf65f commit 5b88e4a
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 3 deletions.
6 changes: 6 additions & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ dokkaGfmPartial {
outputDirectory.set(file("build/docs/partial"))
}

ktlint {
filter {
exclude { it.file.path.contains("xmtp_dh") }
}
}

android {
namespace 'org.xmtp.android.library'
compileSdk 33
Expand Down
129 changes: 128 additions & 1 deletion library/src/main/java/org/xmtp/android/library/Contacts.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,142 @@ package org.xmtp.android.library
import kotlinx.coroutines.runBlocking
import org.xmtp.android.library.messages.ContactBundle
import org.xmtp.android.library.messages.ContactBundleBuilder
import org.xmtp.android.library.messages.EnvelopeBuilder
import org.xmtp.android.library.messages.Topic
import org.xmtp.android.library.messages.walletAddress
import java.util.Date

enum class AllowState {
ALLOWED, BLOCKED, UNKNOWN
}

data class AllowListEntry(
val value: String,
val entryType: EntryType,
val permissionType: AllowState,
) {
enum class EntryType {
ADDRESS
}

companion object {
fun address(address: String, type: AllowState = AllowState.UNKNOWN): AllowListEntry {
return AllowListEntry(address, EntryType.ADDRESS, type)
}
}

val key: String
get() = "${entryType.name}-$value"
}

class AllowList {
private val entries: MutableMap<String, AllowState> = mutableMapOf()

companion object {
@OptIn(ExperimentalUnsignedTypes::class)
suspend fun load(client: Client): AllowList {
val publicKey =
client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes
val privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes

val identifier = uniffi.xmtp_dh.generatePrivatePreferencesTopicIdentifier(
privateKey.toByteArray().toUByteArray().toList()
)
val envelopes = client.query(Topic.allowList(identifier))
val allowList = AllowList()

for (envelope in envelopes.envelopesList) {
val payload = uniffi.xmtp_dh.eciesDecryptK256Sha3256(
publicKey.toByteArray().toUByteArray().toList(),
privateKey.toByteArray().toUByteArray().toList(),
envelope.message.toByteArray().toUByteArray().toList()
)

val entry = JSON.decodeFromString(AllowListEntry.serializer(), String(payload))

allowList.entries[entry.key] = entry.permissionType
}

return allowList
}

@OptIn(ExperimentalUnsignedTypes::class)
suspend fun publish(entry: AllowListEntry, client: Client) {
val payload = JSON.encodeToString(entry)

val publicKey =
client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes
val privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes
val identifier = uniffi.xmtp_dh.generatePrivatePreferencesTopicIdentifier(
privateKey.toByteArray().toUByteArray().toList()
)

val message = uniffi.xmtp_dh.eciesDecryptK256Sha3256(
publicKey.toByteArray().toUByteArray().toList(),
privateKey.toByteArray().toUByteArray().toList(),
payload.toByteArray().toUByteArray().toList()
)

val envelope = EnvelopeBuilder.buildFromTopic(
Topic.allowList(identifier),
Date(),
ByteArray(message.size) { message[it].toByte() })

client.publish(listOf(envelope))
}
}

fun allow(address: String): AllowListEntry {
entries[AllowListEntry.address(address).key] = AllowState.ALLOWED

return AllowListEntry.address(address, AllowState.ALLOWED)
}

fun block(address: String): AllowListEntry {
entries[AllowListEntry.address(address).key] = AllowState.BLOCKED

return AllowListEntry.address(address, AllowState.BLOCKED)
}

fun state(address: String): AllowState {
val state = entries[AllowListEntry.address(address).key]

return state ?: AllowState.UNKNOWN
}
}

data class Contacts(
var client: Client,
val knownBundles: MutableMap<String, ContactBundle> = mutableMapOf(),
val hasIntroduced: MutableMap<String, Boolean> = mutableMapOf()
val hasIntroduced: MutableMap<String, Boolean> = mutableMapOf(),
) {

var allowList: AllowList = AllowList()

suspend fun refreshAllowList() {
allowList = AllowList.load(client)
}

fun isAllowed(address: String): Boolean {
return allowList.state(address) == AllowState.ALLOWED
}

fun isBlocked(address: String): Boolean {
return allowList.state(address) == AllowState.BLOCKED
}

suspend fun allow(addresses: List<String>) {
for (address in addresses) {
AllowList.publish(allowList.allow(address), client)
}
}

suspend fun block(addresses: List<String>) {
for (address in addresses) {
AllowList.publish(allowList.block(address), client)
}
}

fun has(peerAddress: String): Boolean =
knownBundles[peerAddress] != null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ sealed class Conversation {
}
}

fun allowState(): AllowState {
val client: Client = when (this) {
is V1 -> conversationV1.client
is V2 -> conversationV2.client
}
return client.contacts.allowList.state(address = peerAddress)
}

fun toTopicData(): TopicData {
val data = TopicData.newBuilder()
.setCreatedNs(createdAt.time * 1_000_000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ sealed class Topic {
data class userInvite(val address: String?) : Topic()
data class directMessageV1(val address1: String?, val address2: String?) : Topic()
data class directMessageV2(val addresses: String?) : Topic()
data class groupInvite(val address: String?) : Topic()

data class allowList(val identifier: String?) : Topic()

val description: String
get() {
Expand All @@ -22,7 +23,7 @@ sealed class Topic {
wrap("dm-${addresses.joinToString(separator = "-")}")
}
is directMessageV2 -> wrap("m-$addresses")
is groupInvite -> wrap("groupInvite-$address")
is allowList -> wrap("privatestore-$identifier/allowlist")
}
}

Expand Down

0 comments on commit 5b88e4a

Please sign in to comment.