Skip to content

Commit

Permalink
Merge pull request #569 from xmtp/cv/update-libxmtp-fork-fix-intent-f…
Browse files Browse the repository at this point in the history
…ilter

Fork fixes via intent filter fix
  • Loading branch information
cameronvoell authored Dec 17, 2024
2 parents 7fbe833 + 57ce753 commit 5badf1d
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/fork-fix-filter-adjustment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@xmtp/react-native-sdk": patch
---

Fix for forked groups via intent filter adjustment
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ repositories {
dependencies {
implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation "org.xmtp:android:3.0.15"
implementation "org.xmtp:android:3.0.16"
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.facebook.react:react-native:0.71.3'
implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1"
Expand Down
14 changes: 7 additions & 7 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ PODS:
- hermes-engine/Pre-built (= 0.71.14)
- hermes-engine/Pre-built (0.71.14)
- libevent (2.1.12)
- LibXMTP (3.0.10)
- LibXMTP (3.0.12)
- MessagePacker (0.4.7)
- MMKV (2.0.0):
- MMKVCore (~> 2.0.0)
Expand Down Expand Up @@ -448,18 +448,18 @@ PODS:
- SQLCipher/standard (4.5.7):
- SQLCipher/common
- SwiftProtobuf (1.28.2)
- XMTP (3.0.16):
- XMTP (3.0.17):
- Connect-Swift (= 1.0.0)
- CryptoSwift (= 1.8.3)
- CSecp256k1 (~> 0.2)
- LibXMTP (= 3.0.10)
- LibXMTP (= 3.0.12)
- SQLCipher (= 4.5.7)
- XMTPReactNative (3.1.2):
- CSecp256k1 (~> 0.2)
- ExpoModulesCore
- MessagePacker
- SQLCipher (= 4.5.7)
- XMTP (= 3.0.16)
- XMTP (= 3.0.17)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -711,7 +711,7 @@ SPEC CHECKSUMS:
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: d7cc127932c89c53374452d6f93473f1970d8e88
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
LibXMTP: e550ccb4565023eb77f5a2eddd9d5a71cfb9d2b3
LibXMTP: 7e7607786ccc82c0230960964aa00843a5d47094
MessagePacker: ab2fe250e86ea7aedd1a9ee47a37083edd41fd02
MMKV: f7d1d5945c8765f97f39c3d121f353d46735d801
MMKVCore: c04b296010fcb1d1638f2c69405096aac12f6390
Expand Down Expand Up @@ -762,8 +762,8 @@ SPEC CHECKSUMS:
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
SQLCipher: 5e6bfb47323635c8b657b1b27d25c5f1baf63bf5
SwiftProtobuf: 4dbaffec76a39a8dc5da23b40af1a5dc01a4c02d
XMTP: ce70e4a8e71db02af15bf4a0c230f5990c619281
XMTPReactNative: 00f79e4244439587ade2f7d65900e0dc9bd2634f
XMTP: 5be6a8212c789e828b6eeef9edae84a227c61f22
XMTPReactNative: 72e92330c1d9883a93c4a1be62ac4342e95dd80d
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 0e6fe50018f34e575d38dc6a1fdf1f99c9596cdd
Expand Down
272 changes: 272 additions & 0 deletions example/src/tests/groupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,285 @@ import {
DecodedMessage,
ConsentRecord,
} from '../../../src/index'
import { DefaultContentTypes } from 'xmtp-react-native-sdk/lib/types/DefaultContentType'

export const groupTests: Test[] = []
let counter = 1
function test(name: string, perform: () => Promise<boolean>) {
groupTests.push({ name: String(counter++) + '. ' + name, run: perform })
}

test('groups cannot fork', async () => {
const [alix, bo, caro] = await createClients(3)
// Create group with 3 users
const { id: groupId } = await alix.conversations.newGroup([
bo.address,
caro.address,
])

const getGroupForClient = async (client: Client) => {
// Always sync the client before getting the group
await client.conversations.sync()
const group = await client.conversations.findGroup(groupId)
assert(group !== undefined, `Group not found for ${client.address}`)
return group as Group<DefaultContentTypes>
}

const syncClientAndGroup = async (client: Client) => {
const group = await getGroupForClient(client)
await group.sync()
}

const addMemberToGroup = async (fromClient: Client, addresses: string[]) => {
await syncClientAndGroup(fromClient)
const group = await getGroupForClient(fromClient)
await group.addMembers(addresses)
await delayToPropogate(500)
}

const removeMemberFromGroup = async (
fromClient: Client,
addresses: string[]
) => {
await syncClientAndGroup(fromClient)
const group = await getGroupForClient(fromClient)
await group.removeMembers(addresses)
await delayToPropogate(500)
}

// Helper to send a message from a bunch of senders and make sure it is received by all receivers
const testMessageSending = async (senderClient: Client, receiver: Client) => {
// for (const senderClient of senders) {
const messageContent = Math.random().toString(36)
await syncClientAndGroup(senderClient)
const senderGroup = await getGroupForClient(senderClient)
await senderGroup.send(messageContent)

await delayToPropogate(500)
await senderGroup.sync()

await syncClientAndGroup(receiver)

const receiverGroupToCheck = await getGroupForClient(receiver)
await receiverGroupToCheck.sync()

const messages = await receiverGroupToCheck.messages({
direction: 'DESCENDING',
})
const lastMessage = messages[0]
// console.log(lastMessage);
console.log(
`${receiverGroupToCheck.client.address} sees ${messages.length} messages in group`
)
assert(
lastMessage !== undefined &&
lastMessage.nativeContent.text === messageContent,
`${receiverGroupToCheck.client.address} should have received the message, FORK? ${lastMessage?.nativeContent.text} !== ${messageContent}`
)
// }
}

console.log('Testing that messages sent by alix are received by bo')
await testMessageSending(alix, bo)
console.log('Alix & Bo are not forked at the beginning')

// Test adding members one by one
// console.log('Testing adding members one by one...')
const newClients = await createClients(2)

// Add back several members
console.log('Adding new members to the group...')
for (const client of newClients) {
console.log(`Adding member ${client.address}...`)
await addMemberToGroup(alix, [client.address])
}
await delayToPropogate()

await alix.conversations.sync()
await syncClientAndGroup(alix)

// NB => if we don't use Promise.all but a loop, we don't get a fork
const REMOVE_MEMBERS_IN_PARALLEL = true
if (REMOVE_MEMBERS_IN_PARALLEL) {
console.log('Removing members in parallel')

await Promise.all(
newClients.map((client) => {
console.log(`Removing member ${client.address}...`)
return removeMemberFromGroup(alix, [client.address])
})
)
} else {
console.log('Removing members one by one')

for (const client of newClients) {
console.log(`Removing member ${client.address}...`)
await removeMemberFromGroup(alix, [client.address])
}
}

await delayToPropogate(1000)

// When forked, it stays forked even if we try 5 times
// but sometimes it is not forked and works 5/5 times
let forkCount = 0
const tryCount = 5
for (let i = 0; i < tryCount; i++) {
console.log(`Checking fork status ${i+1}/${tryCount}`)
try {
await syncClientAndGroup(alix)
await syncClientAndGroup(bo)
await delayToPropogate(500)
await testMessageSending(alix, bo)
console.log('Not forked!')
} catch (e: any) {
console.log('Forked!')
console.log(e)
forkCount++
}
}

assert(forkCount === 0, `Forked ${forkCount}/${tryCount} times`)

return true
})

test('groups cannot fork short version', async () => {
const [alix, bo, new_one, new_two] = await createClients(4)
// Create group with 2 users
const alixGroup = await alix.conversations.newGroup([
bo.address,
new_one.address,
new_two.address,
])

// sync clients
await alix.conversations.sync()
await bo.conversations.sync()
const boGroup: Group<DefaultContentTypes> = (await bo.conversations.findGroup(alixGroup.id))!

// Remove two members in parallel
// NB => if we don't use Promise.all but a loop, we don't get a fork
console.log('*************libxmtp*********************: Removing members in parallel')
await Promise.all([
alixGroup.removeMembers([new_one.address]),
alixGroup.removeMembers([new_two.address])
])

// Helper to send a message from a bunch of senders and make sure it is received by all receivers
const testMessageSending = async (senderGroup: Group<DefaultContentTypes>, receiverGroup: Group<DefaultContentTypes>) => {
const messageContent = Math.random().toString(36)
await senderGroup.sync()
await alixGroup.send(messageContent)

await delayToPropogate(500)
await alixGroup.sync()
await receiverGroup.sync()

const messages = await receiverGroup.messages({
direction: 'DESCENDING',
})
const lastMessage = messages[0]
console.log(
`${receiverGroup.client.address} sees ${messages.length} messages in group`
)
assert(
lastMessage !== undefined &&
lastMessage.nativeContent.text === messageContent,
`${receiverGroup.client.address} should have received the message, FORK? ${lastMessage?.nativeContent.text} !== ${messageContent}`
)
}
// When forked, it stays forked even if we try 5 times
// but sometimes it is not forked and works 5/5 times
let forkCount = 0
const tryCount = 5
for (let i = 0; i < tryCount; i++) {
console.log(`Checking fork status ${i+1}/${tryCount}`)
try {
await alixGroup.sync()
await boGroup.sync()
await delayToPropogate(500)
await testMessageSending(alixGroup, boGroup)
console.log('Not forked!')
} catch (e: any) {
console.log('Forked!')
console.log(e)
forkCount++
}
}
assert(forkCount === 0, `Forked ${forkCount}/${tryCount} times`)

return true
})

test('groups cannot fork short version - update metadata', async () => {
const [alix, bo, new_one, new_two, new_three, new_four] = await createClients(6)
// Create group with 2 users
const alixGroup = await alix.conversations.newGroup([
bo.address,
new_one.address,
new_two.address,
])

// sync clients
await alix.conversations.sync()
await bo.conversations.sync()
const boGroup: Group<DefaultContentTypes> = (await bo.conversations.findGroup(alixGroup.id))!

// Remove two members in parallel
// NB => if we don't use Promise.all but a loop, we don't get a fork
console.log('*************libxmtp*********************: Updating metadata in parallel')
await Promise.all([
alixGroup.updateGroupName('new name'),
alixGroup.updateGroupName('new name 2')
])

// Helper to send a message from a bunch of senders and make sure it is received by all receivers
const testMessageSending = async (senderGroup: Group<DefaultContentTypes>, receiverGroup: Group<DefaultContentTypes>) => {
const messageContent = Math.random().toString(36)
await senderGroup.sync()
await alixGroup.send(messageContent)

await delayToPropogate(500)
await alixGroup.sync()
await receiverGroup.sync()

const messages = await receiverGroup.messages({
direction: 'DESCENDING',
})
const lastMessage = messages[0]
console.log(
`${receiverGroup.client.address} sees ${messages.length} messages in group`
)
assert(
lastMessage !== undefined &&
lastMessage.nativeContent.text === messageContent,
`${receiverGroup.client.address} should have received the message, FORK? ${lastMessage?.nativeContent.text} !== ${messageContent}`
)
}
// When forked, it stays forked even if we try 5 times
// but sometimes it is not forked and works 5/5 times
let forkCount = 0
const tryCount = 5
for (let i = 0; i < tryCount; i++) {
console.log(`Checking fork status ${i+1}/${tryCount}`)
try {
await alixGroup.sync()
await boGroup.sync()
await delayToPropogate(500)
await testMessageSending(alixGroup, boGroup)
console.log('Not forked!')
} catch (e: any) {
console.log('Forked!')
console.log(e)
forkCount++
}
}
assert(forkCount === 0, `Forked ${forkCount}/${tryCount} times`)

return true
})

test('can cancel streams', async () => {
const [alix, bo] = await createClients(2)
let messageCallbacks = 0
Expand Down
2 changes: 1 addition & 1 deletion example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6283,7 +6283,7 @@
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz"
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==

"@types/node@^20.14.9":
"@types/node@^20.15.0":
version "20.17.10"
resolved "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz#3f7166190aece19a0d1d364d75c8b0b5778c1e18"
integrity sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==
Expand Down
2 changes: 1 addition & 1 deletion ios/XMTPReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Pod::Spec.new do |s|
s.source_files = "**/*.{h,m,swift}"

s.dependency "MessagePacker"
s.dependency "XMTP", "= 3.0.16"
s.dependency "XMTP", "= 3.0.17"
s.dependency 'CSecp256k1', '~> 0.2'
s.dependency "SQLCipher", "= 4.5.7"
end

0 comments on commit 5badf1d

Please sign in to comment.