@@ -3,15 +3,161 @@ package org.xmtp.android.library
3
3
import kotlinx.coroutines.runBlocking
4
4
import org.xmtp.android.library.messages.ContactBundle
5
5
import org.xmtp.android.library.messages.ContactBundleBuilder
6
+ import org.xmtp.android.library.messages.EnvelopeBuilder
6
7
import org.xmtp.android.library.messages.Topic
7
8
import org.xmtp.android.library.messages.walletAddress
9
+ import org.xmtp.proto.message.contents.PrivatePreferences.PrivatePreferencesAction
10
+ import java.util.Date
11
+
12
+ typealias MessageType = PrivatePreferencesAction .MessageTypeCase
13
+
14
+ enum class AllowState {
15
+ ALLOW ,
16
+ BLOCK ,
17
+ UNKNOWN
18
+ }
19
+ data class AllowListEntry (
20
+ val value : String ,
21
+ val entryType : EntryType ,
22
+ val permissionType : AllowState ,
23
+ ) {
24
+ enum class EntryType {
25
+ ADDRESS
26
+ }
27
+
28
+ companion object {
29
+ fun address (
30
+ address : String ,
31
+ type : AllowState = AllowState .UNKNOWN ,
32
+ ): AllowListEntry {
33
+ return AllowListEntry (address, EntryType .ADDRESS , type)
34
+ }
35
+ }
36
+
37
+ val key: String
38
+ get() = " ${entryType.name} -$value "
39
+ }
40
+
41
+ class AllowList (val client : Client ) {
42
+ private val entries: MutableMap <String , AllowState > = mutableMapOf ()
43
+ private val publicKey =
44
+ client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes
45
+ private val privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes
46
+
47
+ @OptIn(ExperimentalUnsignedTypes ::class )
48
+ private val identifier: String = uniffi.xmtp_dh.generatePrivatePreferencesTopicIdentifier(
49
+ privateKey.toByteArray().toUByteArray().toList()
50
+ )
51
+
52
+ @OptIn(ExperimentalUnsignedTypes ::class )
53
+ suspend fun load (): AllowList {
54
+ val envelopes = client.query(Topic .preferenceList(identifier))
55
+ val allowList = AllowList (client)
56
+ val preferences: MutableList <PrivatePreferencesAction > = mutableListOf ()
57
+
58
+ for (envelope in envelopes.envelopesList) {
59
+ val payload = uniffi.xmtp_dh.eciesDecryptK256Sha3256(
60
+ publicKey.toByteArray().toUByteArray().toList(),
61
+ privateKey.toByteArray().toUByteArray().toList(),
62
+ envelope.message.toByteArray().toUByteArray().toList()
63
+ )
64
+
65
+ preferences.add(
66
+ PrivatePreferencesAction .parseFrom(
67
+ payload.toUByteArray().toByteArray()
68
+ )
69
+ )
70
+ }
71
+
72
+ preferences.iterator().forEach { preference ->
73
+ preference.allow?.walletAddressesList?.forEach { address ->
74
+ allowList.allow(address)
75
+ }
76
+ preference.block?.walletAddressesList?.forEach { address ->
77
+ allowList.block(address)
78
+ }
79
+ }
80
+ return allowList
81
+ }
82
+
83
+ @OptIn(ExperimentalUnsignedTypes ::class )
84
+ fun publish (entry : AllowListEntry ) {
85
+ val payload = PrivatePreferencesAction .newBuilder().also {
86
+ when (entry.permissionType) {
87
+ AllowState .ALLOW -> it.setAllow(PrivatePreferencesAction .Allow .newBuilder().addWalletAddresses(entry.value))
88
+ AllowState .BLOCK -> it.setBlock(PrivatePreferencesAction .Block .newBuilder().addWalletAddresses(entry.value))
89
+ AllowState .UNKNOWN -> it.clearMessageType()
90
+ }
91
+ }.build()
92
+
93
+ val message = uniffi.xmtp_dh.eciesEncryptK256Sha3256(
94
+ publicKey.toByteArray().toUByteArray().toList(),
95
+ privateKey.toByteArray().toUByteArray().toList(),
96
+ payload.toByteArray().toUByteArray().toList()
97
+ )
98
+
99
+ val envelope = EnvelopeBuilder .buildFromTopic(
100
+ Topic .preferenceList(identifier),
101
+ Date (),
102
+ ByteArray (message.size) { message[it].toByte() }
103
+ )
104
+
105
+ client.publish(listOf (envelope))
106
+ }
107
+
108
+ fun allow (address : String ): AllowListEntry {
109
+ entries[AllowListEntry .address(address).key] = AllowState .ALLOW
110
+
111
+ return AllowListEntry .address(address, AllowState .ALLOW )
112
+ }
113
+
114
+ fun block (address : String ): AllowListEntry {
115
+ entries[AllowListEntry .address(address).key] = AllowState .BLOCK
116
+
117
+ return AllowListEntry .address(address, AllowState .BLOCK )
118
+ }
119
+
120
+ fun state (address : String ): AllowState {
121
+ val state = entries[AllowListEntry .address(address).key]
122
+
123
+ return state ? : AllowState .UNKNOWN
124
+ }
125
+ }
8
126
9
127
data class Contacts (
10
128
var client : Client ,
11
129
val knownBundles : MutableMap <String , ContactBundle > = mutableMapOf(),
12
- val hasIntroduced : MutableMap <String , Boolean > = mutableMapOf()
130
+ val hasIntroduced : MutableMap <String , Boolean > = mutableMapOf(),
13
131
) {
14
132
133
+ var allowList: AllowList = AllowList (client)
134
+
135
+ fun refreshAllowList () {
136
+ runBlocking {
137
+ allowList = AllowList (client).load()
138
+ }
139
+ }
140
+
141
+ fun isAllowed (address : String ): Boolean {
142
+ return allowList.state(address) == AllowState .ALLOW
143
+ }
144
+
145
+ fun isBlocked (address : String ): Boolean {
146
+ return allowList.state(address) == AllowState .BLOCK
147
+ }
148
+
149
+ fun allow (addresses : List <String >) {
150
+ for (address in addresses) {
151
+ AllowList (client).publish(allowList.allow(address))
152
+ }
153
+ }
154
+
155
+ fun block (addresses : List <String >) {
156
+ for (address in addresses) {
157
+ AllowList (client).publish(allowList.block(address))
158
+ }
159
+ }
160
+
15
161
fun has (peerAddress : String ): Boolean =
16
162
knownBundles[peerAddress] != null
17
163
0 commit comments