Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebase from main #476

Merged
merged 16 commits into from
Oct 26, 2023
Merged
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
237 changes: 149 additions & 88 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"updateSnapVersion": "npm view @xmtp/snap --json | jq '{\"version\": .version, \"package\": .name}' > ./src/snapInfo.json",
"test:setup": "./dev/up",
"test:teardown": "./dev/down",
"test": "npm run test:node",
"test": "npm run test:node --",
"test:node": "jest --no-cache --env='node' --testTimeout=30000",
"test:jsdom": "jest --no-cache --env='./jest.jsdom.env.cjs' --testTimeout=30000",
"test:cov": "jest --coverage --no-cache --runInBand",
Expand Down
4 changes: 2 additions & 2 deletions src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ async function getUserContactFromNetwork(
address = undefined
}

if (address === peerAddress) {
if (address?.toLowerCase() === peerAddress.toLowerCase()) {
return keyBundle
}
}
Expand Down Expand Up @@ -815,7 +815,7 @@ async function getUserContactsFromNetwork(
try {
const keyBundle = decodeContactBundle(env.message)
const signingAddress = await keyBundle?.walletSignatureAddress()
if (address === signingAddress) {
if (address.toLowerCase() === signingAddress.toLowerCase()) {
return keyBundle
} else {
console.info('Received contact bundle with incorrect address')
Expand Down
10 changes: 7 additions & 3 deletions src/PreparedMessage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Envelope } from '@xmtp/proto/ts/dist/types/message_api/v1/message_api.pb'
import { bytesToHex } from './crypto/utils'
import { sha256 } from './crypto/encryption'
import { DecodedMessage } from './Message'

export class PreparedMessage {
messageEnvelope: Envelope
onSend: () => Promise<void>
onSend: () => Promise<DecodedMessage>

constructor(messageEnvelope: Envelope, onSend: () => Promise<void>) {
constructor(
messageEnvelope: Envelope,
onSend: () => Promise<DecodedMessage>
) {
this.messageEnvelope = messageEnvelope
this.onSend = onSend
}
Expand All @@ -20,6 +24,6 @@ export class PreparedMessage {
}

async send() {
await this.onSend()
return this.onSend()
}
}
52 changes: 35 additions & 17 deletions src/conversations/Conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,33 +235,45 @@ export class ConversationV1<ContentTypes>
recipient = recipient.toLegacyBundle()
}

const topic = options?.ephemeral ? this.ephemeralTopic : this.topic

if (!this.client.contacts.has(this.peerAddress)) {
topics = [
buildUserIntroTopic(this.peerAddress),
buildUserIntroTopic(this.client.address),
this.topic,
topic,
]
this.client.contacts.add(this.peerAddress)
} else {
topics = [this.topic]
topics = [topic]
}
const payload = await this.client.encodeContent(content, options)
const msg = await this.createMessage(payload, recipient, options?.timestamp)
const msgBytes = msg.toBytes()

const env: messageApi.Envelope = {
contentTopic: this.topic,
message: msg.toBytes(),
contentTopic: topic,
message: msgBytes,
timestampNs: toNanoString(msg.sent),
}

return new PreparedMessage(env, async () => {
await this.client.publishEnvelopes(
topics.map((topic) => ({
contentTopic: topic,
message: msg.toBytes(),
message: msgBytes,
timestamp: msg.sent,
}))
)

return DecodedMessage.fromV1Message(
msg,
content,
options?.contentType || ContentTypeText,
payload,
topic,
this
)
})
}

Expand Down Expand Up @@ -341,7 +353,7 @@ export class ConversationV1<ContentTypes>
]
this.client.contacts.add(this.peerAddress)
} else {
topics = [this.topic]
topics = [topic]
}
const contentType = options?.contentType || ContentTypeText
const payload = await this.client.encodeContent(content, options)
Expand All @@ -360,7 +372,7 @@ export class ConversationV1<ContentTypes>
content,
contentType,
payload,
topics[0], // Just use the first topic for the returned value
topic,
this
)
}
Expand Down Expand Up @@ -529,12 +541,7 @@ export class ConversationV2<ContentTypes>
const payload = await this.client.encodeContent(content, options)
const msg = await this.createMessage(payload, options?.timestamp)

let topic: string
if (options?.ephemeral) {
topic = this.ephemeralTopic
} else {
topic = this.topic
}
const topic = options?.ephemeral ? this.ephemeralTopic : this.topic

await this.client.publishEnvelopes([
{
Expand All @@ -549,7 +556,7 @@ export class ConversationV2<ContentTypes>
msg,
content,
contentType,
this.topic,
topic,
payload,
this,
this.client.address
Expand Down Expand Up @@ -703,23 +710,34 @@ export class ConversationV2<ContentTypes>
): Promise<PreparedMessage> {
const payload = await this.client.encodeContent(content, options)
const msg = await this.createMessage(payload, options?.timestamp)
const msgBytes = msg.toBytes()

const topic = options?.ephemeral ? this.ephemeralTopic : this.topic

const env: messageApi.Envelope = {
contentTopic: topic,
message: msg.toBytes(),
message: msgBytes,
timestampNs: toNanoString(msg.sent),
}

return new PreparedMessage(env, async () => {
await this.client.publishEnvelopes([
{
contentTopic: this.topic,
message: msg.toBytes(),
contentTopic: topic,
message: msgBytes,
timestamp: msg.sent,
},
])

return DecodedMessage.fromV2Message(
msg,
content,
options?.contentType || ContentTypeText,
topic,
payload,
this,
this.client.address
)
})
}

Expand Down
12 changes: 7 additions & 5 deletions src/conversations/Conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ export default class Conversations<ContentTypes = any> {
if (msg instanceof DecodedMessage && msg.contentTopic === introTopic) {
const convo = new ConversationV1(
this.client,
msg.recipientAddress === this.client.address
msg.recipientAddress?.toLowerCase() ===
this.client.address.toLowerCase()
? (msg.senderAddress as string)
: (msg.recipientAddress as string),
msg.sent
Expand Down Expand Up @@ -474,7 +475,7 @@ export default class Conversations<ContentTypes = any> {
throw new Error(`Recipient ${peerAddress} is not on the XMTP network`)
}

if (peerAddress === this.client.address) {
if (peerAddress.toLowerCase() === this.client.address.toLowerCase()) {
throw new Error('self messaging not supported')
}

Expand All @@ -487,7 +488,7 @@ export default class Conversations<ContentTypes = any> {
if (!context?.conversationId) {
const v1Convos = await this.listV1Conversations()
const matchingConvo = v1Convos.find(
(convo) => convo.peerAddress === peerAddress
(convo) => convo.peerAddress.toLowerCase() === peerAddress.toLowerCase()
)
// If intro already exists, return V1 conversation
// if both peers have V1 compatible key bundles
Expand Down Expand Up @@ -516,7 +517,7 @@ export default class Conversations<ContentTypes = any> {

// Define a function for matching V2 conversations
const matcherFn = (convo: Conversation<ContentTypes>) =>
convo.peerAddress === peerAddress &&
convo.peerAddress.toLowerCase() === peerAddress.toLowerCase() &&
isMatchingContext(context, convo.context ?? undefined)

const existing = await this.getV2ConversationsFromKeystore()
Expand Down Expand Up @@ -571,7 +572,8 @@ export default class Conversations<ContentTypes = any> {

private getPeerAddress(message: MessageV1): string {
const peerAddress =
message.recipientAddress === this.client.address
message.recipientAddress?.toLowerCase() ===
this.client.address.toLowerCase()
? message.senderAddress
: message.recipientAddress

Expand Down
11 changes: 9 additions & 2 deletions test/conversations/Conversation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ describe('conversation', () => {
expect(
alice.conversations.newConversation(alice.address)
).rejects.toThrow('self messaging not supported')
expect(
alice.conversations.newConversation(alice.address.toLowerCase())
).rejects.toThrow('self messaging not supported')
})

it('can send a prepared message v1', async () => {
Expand All @@ -138,11 +141,13 @@ describe('conversation', () => {
const preparedMessage = await aliceConversation.prepareMessage('1')
const messageID = await preparedMessage.messageID()

await preparedMessage.send()
const sentMessage = await preparedMessage.send()

const messages = await aliceConversation.messages()
const message = messages[0]
expect(message.id).toBe(messageID)
expect(sentMessage.id).toBe(messageID)
expect(sentMessage.messageVersion).toBe('v1')
})

it('can send a prepared message v2', async () => {
Expand All @@ -157,12 +162,14 @@ describe('conversation', () => {
const preparedMessage = await aliceConversation.prepareMessage('sup')
const messageID = await preparedMessage.messageID()

await preparedMessage.send()
const sentMessage = await preparedMessage.send()

const messages = await aliceConversation.messages()
const message = messages[0]
expect(message.id).toBe(messageID)
expect(message.content).toBe('sup')
expect(sentMessage.id).toBe(messageID)
expect(sentMessage.messageVersion).toBe('v2')
})

it('can send and stream ephemeral topic v1', async () => {
Expand Down
13 changes: 13 additions & 0 deletions test/conversations/Conversations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,19 @@ describe('conversations', () => {
expect(bobConvo instanceof ConversationV1).toBeTruthy()
})

it('does not create a duplicate conversation with an address case mismatch', async () => {
const convo1 = await alice.conversations.newConversation(bob.address)
await convo1.send('gm')
const convos = await alice.conversations.list()
expect(convos).toHaveLength(1)
const convo2 = await alice.conversations.newConversation(
bob.address.toLowerCase()
)
await convo2.send('gm')
const convos2 = await alice.conversations.list()
expect(convos2).toHaveLength(1)
})

it('continues to use v1 conversation even after upgrading bundle', async () => {
const aliceConvo = await alice.conversations.newConversation(bob.address)
await aliceConvo.send('gm')
Expand Down
Loading