Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.xmtp.android.library
import com.google.protobuf.kotlin.toByteStringUtf8
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Ignore
import org.junit.Before
import org.junit.Test
import org.xmtp.android.library.codecs.Attachment
import org.xmtp.android.library.codecs.AttachmentCodec
Expand All @@ -18,6 +18,18 @@ import java.net.URL

class RemoteAttachmentTest {

private lateinit var fixtures: Fixtures
private lateinit var filesDir: File

private lateinit var testFetcher: TestFetcher

@Before
fun setUp() {
fixtures = fixtures()
filesDir = fixtures.context.filesDir
testFetcher = TestFetcher(filesDir)
}

@Test
fun testEncryptedContentShouldBeDecryptable() {
Client.register(codec = AttachmentCodec())
Expand All @@ -27,9 +39,9 @@ class RemoteAttachmentTest {
data = "hello world".toByteStringUtf8(),
)

val encrypted = RemoteAttachment.encodeEncrypted(attachment, AttachmentCodec())
val encrypted = RemoteAttachment.Companion.encodeEncrypted(attachment, AttachmentCodec())

val decrypted = RemoteAttachment.decryptEncoded(encrypted)
val decrypted = RemoteAttachment.Companion.decryptEncoded(encrypted)
Assert.assertEquals(ContentTypeAttachment.id, decrypted.type.id)

val decoded = decrypted.decoded<Attachment>()
Expand All @@ -39,7 +51,6 @@ class RemoteAttachmentTest {
}

@Test
@Ignore("Flaky")
fun testCanUseRemoteAttachmentCodec() {
val attachment = Attachment(
filename = "test.txt",
Expand All @@ -50,25 +61,24 @@ class RemoteAttachmentTest {
Client.register(codec = AttachmentCodec())
Client.register(codec = RemoteAttachmentCodec())

val encodedEncryptedContent = RemoteAttachment.encodeEncrypted(
val encodedEncryptedContent = RemoteAttachment.Companion.encodeEncrypted(
content = attachment,
codec = AttachmentCodec(),
)

File("abcdefg").writeBytes(encodedEncryptedContent.payload.toByteArray())
File(filesDir, "abcdefg").writeBytes(encodedEncryptedContent.payload.toByteArray())

val remoteAttachment = RemoteAttachment.from(
val remoteAttachment = RemoteAttachment.Companion.from(
url = URL("https://abcdefg"),
encryptedEncodedContent = encodedEncryptedContent,
)

remoteAttachment.contentLength = attachment.data.size()
remoteAttachment.filename = attachment.filename

val fixtures = fixtures()
val aliceClient = fixtures.aliceClient
val aliceClient = fixtures.alixClient
val aliceConversation = runBlocking {
aliceClient.conversations.newConversation(fixtures.bobClient.inboxId)
aliceClient.conversations.newConversation(fixtures.boClient.inboxId)
}

runBlocking {
Expand All @@ -79,11 +89,12 @@ class RemoteAttachmentTest {
}

val messages = runBlocking { aliceConversation.messages() }
Assert.assertEquals(messages.size, 1)
// membership-change and the remote attachment message
Assert.assertEquals(messages.size, 2)

if (messages.size == 1) {
if (messages.size == 2) {
val loadedRemoteAttachment: RemoteAttachment = messages[0].content()!!
loadedRemoteAttachment.fetcher = TestFetcher()
loadedRemoteAttachment.fetcher = TestFetcher(filesDir)
runBlocking {
val attachment2: Attachment =
loadedRemoteAttachment.load() ?: throw XMTPException("did not get attachment")
Expand All @@ -105,23 +116,22 @@ class RemoteAttachmentTest {
Client.register(codec = AttachmentCodec())
Client.register(codec = RemoteAttachmentCodec())

val encodedEncryptedContent = RemoteAttachment.encodeEncrypted(
val encodedEncryptedContent = RemoteAttachment.Companion.encodeEncrypted(
content = attachment,
codec = AttachmentCodec(),
)

File("abcdefg").writeBytes(encodedEncryptedContent.payload.toByteArray())
File(filesDir, "abcdefg").writeBytes(encodedEncryptedContent.payload.toByteArray())

Assert.assertThrows(XMTPException::class.java) {
RemoteAttachment.from(
RemoteAttachment.Companion.from(
url = URL("http://abcdefg"),
encryptedEncodedContent = encodedEncryptedContent,
)
}
}

@Test
@Ignore("Flaky")
fun testEnsuresContentDigestMatches() {
val attachment = Attachment(
filename = "test.txt",
Expand All @@ -132,25 +142,21 @@ class RemoteAttachmentTest {
Client.register(codec = AttachmentCodec())
Client.register(codec = RemoteAttachmentCodec())

val encodedEncryptedContent = RemoteAttachment.encodeEncrypted(
val encodedEncryptedContent = RemoteAttachment.Companion.encodeEncrypted(
content = attachment,
codec = AttachmentCodec(),
)

File("abcdefg").writeBytes(encodedEncryptedContent.payload.toByteArray())
File(filesDir, "abcdefg").writeBytes(encodedEncryptedContent.payload.toByteArray())

val remoteAttachment = RemoteAttachment.from(
val remoteAttachment = RemoteAttachment.Companion.from(
url = URL("https://abcdefg"),
encryptedEncodedContent = encodedEncryptedContent,
)

remoteAttachment.contentLength = attachment.data.size()
remoteAttachment.filename = attachment.filename

val fixtures = fixtures()
val aliceClient = fixtures.aliceClient
val aliceClient = fixtures.alixClient
val aliceConversation = runBlocking {
aliceClient.conversations.newConversation(fixtures.bobClient.inboxId)
aliceClient.conversations.newConversation(fixtures.boClient.inboxId)
}

runBlocking {
Expand All @@ -161,14 +167,15 @@ class RemoteAttachmentTest {
}

val messages = runBlocking { aliceConversation.messages() }
Assert.assertEquals(messages.size, 1)
// membership-change and the remote attachment message
Assert.assertEquals(messages.size, 2)

// Tamper with the payload
File("abcdefg").writeBytes("sup".toByteArray())
File(filesDir, "abcdefg").writeBytes("sup".toByteArray())

if (messages.size == 1) {
if (messages.size == 2) {
val loadedRemoteAttachment: RemoteAttachment = messages[0].content()!!
loadedRemoteAttachment.fetcher = TestFetcher()
loadedRemoteAttachment.fetcher = TestFetcher(filesDir)
Assert.assertThrows(XMTPException::class.java) {
runBlocking {
val attachment: Attachment? = loadedRemoteAttachment.load()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import org.web3j.tx.gas.DefaultGasProvider
import org.web3j.utils.Numeric
import org.xmtp.android.library.artifact.CoinbaseSmartWallet
import org.xmtp.android.library.artifact.CoinbaseSmartWalletFactory
import org.xmtp.android.library.codecs.Fetcher
import org.xmtp.android.library.libxmtp.IdentityKind
import org.xmtp.android.library.libxmtp.PublicIdentity
import org.xmtp.android.library.messages.PrivateKey
import org.xmtp.android.library.messages.PrivateKeyBuilder
import java.io.File
import java.math.BigInteger
import java.net.URL
import java.security.SecureRandom

const val ANVIL_TEST_PRIVATE_KEY_1 =
Expand Down Expand Up @@ -111,6 +114,12 @@ class FakeSCWWallet : SigningKey {
}
}

class TestFetcher(val filesDir: File) : Fetcher {
override fun fetch(url: URL): ByteArray {
return File(filesDir, url.toString().replace("https://", "")).readBytes()
}
}

class Fixtures(
api: ClientOptions.Api = ClientOptions.Api(
XMTPEnvironment.LOCAL,
Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/org/xmtp/android/library/Dm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Dm(
}
return encoded
} catch (e: Exception) {
throw XMTPException("Codec type is not registered")
throw XMTPException("Failed to encode content: ${e.message}", e)
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/org/xmtp/android/library/Group.kt
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class Group(
}
return encoded
} catch (e: Exception) {
throw XMTPException("Codec type is not registered")
throw XMTPException("Failed to encode content: ${e.message}", e)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,26 @@ class HTTPFetcher : Fetcher {
data class RemoteAttachmentCodec(override var contentType: ContentTypeId = ContentTypeRemoteAttachment) :
ContentCodec<RemoteAttachment> {
override fun encode(content: RemoteAttachment): EncodedContent {
return EncodedContent.newBuilder().also {
return EncodedContent.newBuilder().also { it ->
it.type = ContentTypeRemoteAttachment
it.putAllParameters(
mapOf(
"contentDigest" to content.contentDigest,
"secret" to content.secret.toByteArray().toHex(),
"salt" to content.salt.toByteArray().toHex(),
"nonce" to content.nonce.toByteArray().toHex(),
"scheme" to content.scheme,
"contentLength" to content.contentLength.toString(),
"filename" to content.filename,
),
val parametersMap = mutableMapOf(
"contentDigest" to content.contentDigest,
"secret" to content.secret.toByteArray().toHex(),
"salt" to content.salt.toByteArray().toHex(),
"nonce" to content.nonce.toByteArray().toHex(),
"scheme" to content.scheme,
)

content.contentLength?.let {
parametersMap["contentLength"] = it.toString()
}

content.filename?.let {
parametersMap["filename"] = it
}

it.putAllParameters(parametersMap)

it.content = content.url.toString().toByteStringUtf8()
}.build()
}
Expand All @@ -176,9 +183,8 @@ data class RemoteAttachmentCodec(override var contentType: ContentTypeId = Conte
val salt = content.parametersMap["salt"] ?: throw XMTPException("missing salt")
val nonce = content.parametersMap["nonce"] ?: throw XMTPException("missing nonce")
val scheme = content.parametersMap["scheme"] ?: throw XMTPException("missing scheme")
val contentLength =
content.parametersMap["contentLength"] ?: throw XMTPException("missing contentLength")
val filename = content.parametersMap["filename"] ?: throw XMTPException("missing filename")
val contentLength = content.parametersMap["contentLength"]?.toIntOrNull()
val filename = content.parametersMap["filename"]
val encodedContent = content.content ?: throw XMTPException("missing content")

return RemoteAttachment(
Expand All @@ -188,7 +194,7 @@ data class RemoteAttachmentCodec(override var contentType: ContentTypeId = Conte
salt = Numeric.hexStringToByteArray(salt).toByteString(),
nonce = Numeric.hexStringToByteArray(nonce).toByteString(),
scheme = scheme,
contentLength = contentLength.toInt(),
contentLength = contentLength,
filename = filename,
)
}
Expand Down
Loading