Skip to content

Commit 35d5427

Browse files
committed
Merge branch 'main' of https://github.com/xmtp/xmtp-android into np/bump-latest-rust
2 parents bc2738e + 037b85b commit 35d5427

21 files changed

+373
-105
lines changed

library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt

-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import androidx.test.platform.app.InstrumentationRegistry
55
import kotlinx.coroutines.runBlocking
66
import org.junit.Assert.assertEquals
77
import org.junit.Assert.fail
8-
import org.junit.Ignore
98
import org.junit.Test
109
import org.junit.runner.RunWith
1110
import org.xmtp.android.library.messages.PrivateKeyBuilder
@@ -202,7 +201,6 @@ class ClientTest {
202201
}
203202

204203
@Test
205-
@Ignore("CI Issues")
206204
fun testPublicCanMessage() {
207205
val aliceWallet = PrivateKeyBuilder()
208206
val notOnNetwork = PrivateKeyBuilder()
@@ -218,7 +216,6 @@ class ClientTest {
218216
}
219217

220218
@Test
221-
@Ignore("CI Issues")
222219
fun testPreEnableIdentityCallback() {
223220
val fakeWallet = PrivateKeyBuilder()
224221
val expectation = CompletableFuture<Unit>()
@@ -241,7 +238,6 @@ class ClientTest {
241238
}
242239

243240
@Test
244-
@Ignore("CI Issues")
245241
fun testPreCreateIdentityCallback() {
246242
val fakeWallet = PrivateKeyBuilder()
247243
val expectation = CompletableFuture<Unit>()

library/src/androidTest/java/org/xmtp/android/library/CodecTest.kt

+106-8
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,38 @@ package org.xmtp.android.library
33
import androidx.test.ext.junit.runners.AndroidJUnit4
44
import com.google.protobuf.kotlin.toByteStringUtf8
55
import org.junit.Assert.assertEquals
6+
import org.junit.Assert.assertTrue
67
import org.junit.Test
78
import org.junit.runner.RunWith
9+
import org.xmtp.android.library.Crypto.Companion.verifyHmacSignature
810
import org.xmtp.android.library.codecs.CompositeCodec
911
import org.xmtp.android.library.codecs.ContentCodec
1012
import org.xmtp.android.library.codecs.ContentTypeId
1113
import org.xmtp.android.library.codecs.ContentTypeIdBuilder
1214
import org.xmtp.android.library.codecs.DecodedComposite
1315
import org.xmtp.android.library.codecs.EncodedContent
1416
import org.xmtp.android.library.codecs.TextCodec
17+
import org.xmtp.android.library.messages.InvitationV1ContextBuilder
18+
import org.xmtp.android.library.messages.MessageV2Builder
19+
import org.xmtp.android.library.messages.PrivateKeyBuilder
1520
import org.xmtp.android.library.messages.walletAddress
21+
import java.time.Instant
1622

1723
data class NumberCodec(
1824
override var contentType: ContentTypeId = ContentTypeIdBuilder.builderFromAuthorityId(
1925
authorityId = "example.com",
2026
typeId = "number",
2127
versionMajor = 1,
22-
versionMinor = 1
23-
)
28+
versionMinor = 1,
29+
),
2430
) : ContentCodec<Double> {
2531
override fun encode(content: Double): EncodedContent {
2632
return EncodedContent.newBuilder().also {
2733
it.type = ContentTypeIdBuilder.builderFromAuthorityId(
2834
authorityId = "example.com",
2935
typeId = "number",
3036
versionMajor = 1,
31-
versionMinor = 1
37+
versionMinor = 1,
3238
)
3339
it.content = mapOf(Pair("number", content)).toString().toByteStringUtf8()
3440
}.build()
@@ -37,10 +43,13 @@ data class NumberCodec(
3743
override fun decode(content: EncodedContent): Double =
3844
content.content.toStringUtf8().filter { it.isDigit() || it == '.' }.toDouble()
3945

46+
override fun shouldPush(content: Double): Boolean = false
47+
4048
override fun fallback(content: Double): String? {
4149
return "Error: This app does not support numbers."
4250
}
4351
}
52+
4453
@RunWith(AndroidJUnit4::class)
4554
class CodecTest {
4655

@@ -53,7 +62,7 @@ class CodecTest {
5362
aliceClient.conversations.newConversation(fixtures.bob.walletAddress)
5463
aliceConversation.send(
5564
content = 3.14,
56-
options = SendOptions(contentType = NumberCodec().contentType)
65+
options = SendOptions(contentType = NumberCodec().contentType),
5766
)
5867
val messages = aliceConversation.messages()
5968
assertEquals(messages.size, 1)
@@ -75,7 +84,7 @@ class CodecTest {
7584
val source = DecodedComposite(encodedContent = textContent)
7685
aliceConversation.send(
7786
content = source,
78-
options = SendOptions(contentType = CompositeCodec().contentType)
87+
options = SendOptions(contentType = CompositeCodec().contentType),
7988
)
8089
val messages = aliceConversation.messages()
8190
val decoded: DecodedComposite? = messages[0].content()
@@ -95,12 +104,12 @@ class CodecTest {
95104
val source = DecodedComposite(
96105
parts = listOf(
97106
DecodedComposite(encodedContent = textContent),
98-
DecodedComposite(parts = listOf(DecodedComposite(encodedContent = numberContent)))
99-
)
107+
DecodedComposite(parts = listOf(DecodedComposite(encodedContent = numberContent))),
108+
),
100109
)
101110
aliceConversation.send(
102111
content = source,
103-
options = SendOptions(contentType = CompositeCodec().contentType)
112+
options = SendOptions(contentType = CompositeCodec().contentType),
104113
)
105114
val messages = aliceConversation.messages()
106115
val decoded: DecodedComposite? = messages[0].content()
@@ -109,4 +118,93 @@ class CodecTest {
109118
assertEquals("sup", part1.content())
110119
assertEquals(3.14, part2.content())
111120
}
121+
122+
@Test
123+
fun testCanGetPushInfoBeforeDecoded() {
124+
val codec = NumberCodec()
125+
Client.register(codec = codec)
126+
val fixtures = fixtures()
127+
val aliceClient = fixtures.aliceClient!!
128+
val aliceConversation =
129+
aliceClient.conversations.newConversation(fixtures.bob.walletAddress)
130+
aliceConversation.send(
131+
content = 3.14,
132+
options = SendOptions(contentType = codec.contentType),
133+
)
134+
val messages = aliceConversation.messages()
135+
assert(messages.isNotEmpty())
136+
137+
val message = MessageV2Builder.buildEncode(
138+
client = aliceClient,
139+
encodedContent = messages[0].encodedContent,
140+
topic = aliceConversation.topic,
141+
keyMaterial = aliceConversation.keyMaterial!!,
142+
codec = codec,
143+
)
144+
145+
assertEquals(false, message.shouldPush)
146+
assertEquals(true, message.senderHmac?.isNotEmpty())
147+
}
148+
149+
@Test
150+
fun testReturnsAllHMACKeys() {
151+
val alix = PrivateKeyBuilder()
152+
val clientOptions =
153+
ClientOptions(api = ClientOptions.Api(env = XMTPEnvironment.LOCAL, isSecure = false))
154+
val alixClient = Client().create(alix, clientOptions)
155+
val conversations = mutableListOf<Conversation>()
156+
repeat(5) {
157+
val account = PrivateKeyBuilder()
158+
val client = Client().create(account, clientOptions)
159+
conversations.add(
160+
alixClient.conversations.newConversation(
161+
client.address,
162+
context = InvitationV1ContextBuilder.buildFromConversation(conversationId = "hi")
163+
)
164+
)
165+
}
166+
167+
val thirtyDayPeriodsSinceEpoch = Instant.now().epochSecond / 60 / 60 / 24 / 30
168+
169+
val hmacKeys = alixClient.conversations.getHmacKeys()
170+
171+
val topics = hmacKeys.hmacKeysMap.keys
172+
conversations.forEach { convo ->
173+
assertTrue(topics.contains(convo.topic))
174+
}
175+
176+
val topicHmacs = mutableMapOf<String, ByteArray>()
177+
val headerBytes = ByteArray(10)
178+
179+
conversations.forEach { conversation ->
180+
val topic = conversation.topic
181+
val payload = TextCodec().encode(content = "Hello, world!")
182+
183+
val message = MessageV2Builder.buildEncode(
184+
client = alixClient,
185+
encodedContent = payload,
186+
topic = topic,
187+
keyMaterial = headerBytes,
188+
codec = TextCodec()
189+
)
190+
191+
val keyMaterial = conversation.keyMaterial
192+
val info = "$thirtyDayPeriodsSinceEpoch-${alixClient.address}"
193+
val key = Crypto.deriveKey(keyMaterial!!, ByteArray(0), info.toByteArray())
194+
val hmac = Crypto.calculateMac(key, headerBytes)
195+
196+
topicHmacs[topic] = hmac
197+
}
198+
199+
hmacKeys.hmacKeysMap.forEach { (topic, hmacData) ->
200+
hmacData.valuesList.forEachIndexed { idx, hmacKeyThirtyDayPeriod ->
201+
val valid = verifyHmacSignature(
202+
hmacKeyThirtyDayPeriod.hmacKey.toByteArray(),
203+
topicHmacs[topic]!!,
204+
headerBytes
205+
)
206+
assertTrue(valid == (idx == 1))
207+
}
208+
}
209+
}
112210
}

library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt

+9-4
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,16 @@ class ConversationTest {
229229
additionalData = headerBytes,
230230
)
231231
val tamperedMessage =
232-
MessageV2Builder.buildFromCipherText(headerBytes = headerBytes, ciphertext = ciphertext)
232+
MessageV2Builder.buildFromCipherText(
233+
headerBytes = headerBytes,
234+
ciphertext = ciphertext,
235+
senderHmac = null,
236+
shouldPush = true,
237+
)
233238
val tamperedEnvelope = EnvelopeBuilder.buildFromString(
234239
topic = aliceConversation.topic,
235240
timestamp = Date(),
236-
message = MessageBuilder.buildFromMessageV2(v2 = tamperedMessage).toByteArray(),
241+
message = MessageBuilder.buildFromMessageV2(v2 = tamperedMessage.messageV2).toByteArray(),
237242
)
238243
aliceClient.publish(envelopes = listOf(tamperedEnvelope))
239244
val bobConversation = bobClient.conversations.newConversation(
@@ -585,7 +590,8 @@ class ConversationTest {
585590
encodedContent,
586591
topic = conversation.topic,
587592
keyMaterial = conversation.keyMaterial!!,
588-
),
593+
codec = encoder,
594+
).messageV2,
589595
).toByteArray(),
590596
),
591597
)
@@ -848,7 +854,6 @@ class ConversationTest {
848854
val directMessageV1 = Topic.directMessageV1(invalidId, "sd").description
849855
val directMessageV2 = Topic.directMessageV2(invalidId).description
850856
val preferenceList = Topic.preferenceList(invalidId).description
851-
val conversations = bobClient.conversations
852857

853858
// check if validation of topics no accept all types with invalid topic
854859
assertFalse(Topic.isValidTopic(privateStore))

library/src/androidTest/java/org/xmtp/android/library/GroupMembershipChangeTest.kt

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package org.xmtp.android.library
22

3+
import android.content.Context
4+
import androidx.test.core.app.ApplicationProvider
35
import androidx.test.ext.junit.runners.AndroidJUnit4
4-
import androidx.test.platform.app.InstrumentationRegistry
56
import org.junit.Assert.assertEquals
67
import org.junit.Before
78
import org.junit.Test
@@ -25,18 +26,17 @@ class GroupMembershipChangeTest {
2526
lateinit var caro: PrivateKey
2627
lateinit var caroClient: Client
2728
lateinit var fixtures: Fixtures
29+
val context = ApplicationProvider.getApplicationContext<Context>()
2830

2931
@Before
3032
fun setUp() {
31-
val context = InstrumentationRegistry.getInstrumentation().targetContext
32-
fixtures =
33-
fixtures(
34-
clientOptions = ClientOptions(
35-
ClientOptions.Api(XMTPEnvironment.LOCAL, false),
36-
enableAlphaMls = true,
37-
appContext = context
38-
)
33+
fixtures = fixtures(
34+
clientOptions = ClientOptions(
35+
ClientOptions.Api(XMTPEnvironment.LOCAL, false),
36+
enableAlphaMls = true,
37+
appContext = context,
3938
)
39+
)
4040
alixWallet = fixtures.aliceAccount
4141
alix = fixtures.alice
4242
boWallet = fixtures.bobAccount

0 commit comments

Comments
 (0)