Skip to content

Commit

Permalink
add tests reproducing the SCW issues
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Oct 22, 2024
1 parent 492c797 commit 2f94fe1
Show file tree
Hide file tree
Showing 2 changed files with 253 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,77 @@ package org.xmtp.android.library

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.xmtp.android.library.messages.MessageDeliveryStatus
import org.xmtp.android.library.messages.PrivateKey
import org.xmtp.android.library.messages.PrivateKeyBuilder
import org.xmtp.android.library.messages.walletAddress

@RunWith(AndroidJUnit4::class)
class SmartContractWalletTest {
@Test
fun testCanCreateASCW() {
private lateinit var davonSCW: FakeSCWWallet
private lateinit var davonSCWClient: Client
private lateinit var eriSCW: FakeSCWWallet
private lateinit var eriSCWClient: Client
private lateinit var options: ClientOptions
private lateinit var boV3Wallet: PrivateKeyBuilder
private lateinit var boV3: PrivateKey
private lateinit var boV3Client: Client

@Before
fun setUp() {
val key = byteArrayOf(
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
)
val context = InstrumentationRegistry.getInstrumentation().targetContext
val davonSCW = FakeSCWWallet.generate()
val options = ClientOptions(
options = ClientOptions(
ClientOptions.Api(XMTPEnvironment.LOCAL, false),
enableV3 = true,
appContext = context,
dbEncryptionKey = key
)
val davonSCWClient = runBlocking {

// EOA
boV3Wallet = PrivateKeyBuilder()
boV3 = boV3Wallet.getPrivateKey()
boV3Client = runBlocking {
Client().createV3(
account = boV3Wallet,
options = options
)
}

// SCW
davonSCW = FakeSCWWallet.generate(ANVIL_TEST_PRIVATE_KEY_1)
davonSCWClient = runBlocking {
Client().createV3(
account = davonSCW,
options = options
)
}

// SCW
eriSCW = FakeSCWWallet.generate(ANVIL_TEST_PRIVATE_KEY_2)
eriSCWClient = runBlocking {
Client().createV3(
account = davonSCW,
options = options
)
}
}

@Test
fun testCanBuildASCW() {
val davonSCWClient2 = runBlocking {
Client().buildV3(
address = davonSCW.address,
Expand All @@ -41,4 +83,195 @@ class SmartContractWalletTest {

assertEquals(davonSCWClient.inboxId, davonSCWClient2.inboxId)
}

@Test
fun testsCanCreateGroup() {
val group1 = runBlocking {
boV3Client.conversations.newGroup(
listOf(
davonSCW.walletAddress,
eriSCW.walletAddress
)
)
}
val group2 = runBlocking {
davonSCWClient.conversations.newGroup(
listOf(
boV3.walletAddress,
eriSCW.walletAddress
)
)
}

assertEquals(
runBlocking { group1.members().map { it.inboxId }.sorted() },
listOf(davonSCWClient.inboxId, boV3Client.inboxId, eriSCWClient.inboxId).sorted()
)
assertEquals(
runBlocking { group2.members().map { it.addresses.first() }.sorted() },
listOf(davonSCWClient.address, boV3Client.address, eriSCWClient.address).sorted()
)
}

@Test
fun testsCanSendMessages() {
val boGroup = runBlocking {
boV3Client.conversations.newGroup(
listOf(
davonSCW.walletAddress,
eriSCW.walletAddress
)
)
}
runBlocking { boGroup.send("howdy") }
val messageId = runBlocking { boGroup.send("gm") }
runBlocking { boGroup.sync() }
assertEquals(boGroup.messages().first().body, "gm")
assertEquals(boGroup.messages().first().id, messageId)
assertEquals(boGroup.messages().first().deliveryStatus, MessageDeliveryStatus.PUBLISHED)
assertEquals(boGroup.messages().size, 3)

runBlocking { davonSCWClient.conversations.syncGroups() }
val davonGroup = runBlocking { davonSCWClient.conversations.listGroups().last() }
runBlocking { davonGroup.sync() }
assertEquals(davonGroup.messages().size, 2)
assertEquals(davonGroup.messages().first().body, "gm")
runBlocking { davonGroup.send("from davon") }

runBlocking { eriSCWClient.conversations.syncGroups() }
val eriGroup = runBlocking { davonSCWClient.findGroup(davonGroup.id) }
runBlocking { eriGroup?.sync() }
assertEquals(eriGroup?.messages()?.size, 3)
assertEquals(eriGroup?.messages()?.first()?.body, "from davon")
runBlocking { eriGroup?.send("from eri") }
}

@Test
fun testGroupConsent() {
runBlocking {
val davonGroup = runBlocking {
davonSCWClient.conversations.newGroup(
listOf(
boV3.walletAddress,
eriSCW.walletAddress
)
)
}
assert(davonSCWClient.contacts.isGroupAllowed(davonGroup.id))
assertEquals(davonGroup.consentState(), ConsentState.ALLOWED)

davonSCWClient.contacts.denyGroups(listOf(davonGroup.id))
assert(davonSCWClient.contacts.isGroupDenied(davonGroup.id))
assertEquals(davonGroup.consentState(), ConsentState.DENIED)

davonGroup.updateConsentState(ConsentState.ALLOWED)
assert(davonSCWClient.contacts.isGroupAllowed(davonGroup.id))
assertEquals(davonGroup.consentState(), ConsentState.ALLOWED)
}
}

@Test
fun testCanAllowAndDenyInboxId() {
runBlocking {
val davonGroup = runBlocking {
davonSCWClient.conversations.newGroup(
listOf(
boV3.walletAddress,
eriSCW.walletAddress
)
)
}
assert(!davonSCWClient.contacts.isInboxAllowed(boV3Client.inboxId))
assert(!davonSCWClient.contacts.isInboxDenied(boV3Client.inboxId))

davonSCWClient.contacts.allowInboxes(listOf(boV3Client.inboxId))
var caroMember = davonGroup.members().firstOrNull { it.inboxId == boV3Client.inboxId }
assertEquals(caroMember!!.consentState, ConsentState.ALLOWED)

assert(davonSCWClient.contacts.isInboxAllowed(boV3Client.inboxId))
assert(!davonSCWClient.contacts.isInboxDenied(boV3Client.inboxId))
assert(davonSCWClient.contacts.isAllowed(boV3Client.address))
assert(!davonSCWClient.contacts.isDenied(boV3Client.address))

davonSCWClient.contacts.denyInboxes(listOf(boV3Client.inboxId))
caroMember = davonGroup.members().firstOrNull { it.inboxId == boV3Client.inboxId }
assertEquals(caroMember!!.consentState, ConsentState.DENIED)

assert(!davonSCWClient.contacts.isInboxAllowed(boV3Client.inboxId))
assert(davonSCWClient.contacts.isInboxDenied(boV3Client.inboxId))

davonSCWClient.contacts.allow(listOf(eriSCWClient.address))
assert(davonSCWClient.contacts.isAllowed(eriSCWClient.address))
assert(!davonSCWClient.contacts.isDenied(eriSCWClient.address))
assert(davonSCWClient.contacts.isInboxAllowed(eriSCWClient.inboxId))
assert(!davonSCWClient.contacts.isInboxDenied(eriSCWClient.inboxId))
}
}

@Test
fun testCanStreamAllMessages() {
val group = runBlocking {
davonSCWClient.conversations.newGroup(
listOf(
boV3.walletAddress,
eriSCW.walletAddress
)
)
}
val group2 = runBlocking {
boV3Client.conversations.newGroup(
listOf(
davonSCW.walletAddress,
eriSCW.walletAddress
)
)
}
runBlocking { davonSCWClient.conversations.syncGroups() }

val allMessages = mutableListOf<DecodedMessage>()

val job = CoroutineScope(Dispatchers.IO).launch {
try {
davonSCWClient.conversations.streamAllGroupMessages()
.collect { message ->
allMessages.add(message)
}
} catch (e: Exception) {
}
}
Thread.sleep(1000)
runBlocking {
group.send("hi")
group2.send("hi")
}
Thread.sleep(1000)
assertEquals(2, allMessages.size)
job.cancel()
}

@Test
fun testCanStreamGroups() {
val allMessages = mutableListOf<String>()

val job = CoroutineScope(Dispatchers.IO).launch {
try {
davonSCWClient.conversations.streamGroups()
.collect { message ->
allMessages.add(message.topic)
}
} catch (e: Exception) {
}
}
Thread.sleep(1000)

runBlocking {
eriSCWClient.conversations.newGroup(listOf(boV3.walletAddress, davonSCW.walletAddress))
boV3Client.conversations.newGroup(listOf(eriSCW.walletAddress, davonSCW.walletAddress))
}

Thread.sleep(1000)
assertEquals(2, allMessages.size)
job.cancel()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ class FakeWallet : SigningKey {
get() = privateKey.walletAddress
}

private const val ANVIL_TEST_PRIVATE_KEY =
const val ANVIL_TEST_PRIVATE_KEY_1 =
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
const val ANVIL_TEST_PRIVATE_KEY_2 =
"59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
private const val ANVIL_TEST_PORT = "http://10.0.2.2:8545"

class FakeSCWWallet : SigningKey {
private var web3j: Web3j = Web3j.build(HttpService(ANVIL_TEST_PORT))
private val contractDeployerCredentials: Credentials =
Credentials.create(ANVIL_TEST_PRIVATE_KEY)
private val web3j: Web3j = Web3j.build(HttpService(ANVIL_TEST_PORT))
private var contractDeployerCredentials: Credentials? = null
var walletAddress: String = ""

override val address: String
Expand All @@ -73,8 +74,9 @@ class FakeSCWWallet : SigningKey {
override var chainId: Long? = 31337L

companion object {
fun generate(): FakeSCWWallet {
fun generate(privateKey: String): FakeSCWWallet {
return FakeSCWWallet().apply {
contractDeployerCredentials = Credentials.create(privateKey)
createSmartContractWallet()
}
}
Expand All @@ -91,7 +93,7 @@ class FakeSCWWallet : SigningKey {
val replaySafeHash = smartWallet.replaySafeHash(digest).send()

val signature =
Sign.signMessage(replaySafeHash, contractDeployerCredentials.ecKeyPair, false)
Sign.signMessage(replaySafeHash, contractDeployerCredentials!!.ecKeyPair, false)
val signatureBytes = signature.r + signature.s + signature.v
val tokens = listOf(
Uint(BigInteger.ZERO),
Expand Down Expand Up @@ -119,7 +121,13 @@ class FakeSCWWallet : SigningKey {
).send()

val ownerAddress = ByteArray(32) { 0 }.apply {
System.arraycopy(contractDeployerCredentials.address.hexToByteArray(), 0, this, 12, 20)
System.arraycopy(
contractDeployerCredentials!!.address.hexToByteArray(),
0,
this,
12,
20
)
}
val owners = listOf(ownerAddress)
val nonce = BigInteger.ZERO
Expand Down

0 comments on commit 2f94fe1

Please sign in to comment.