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

V3 only dms #307

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open

V3 only dms #307

wants to merge 39 commits into from

Conversation

nplasterer
Copy link
Contributor

@nplasterer nplasterer commented Oct 19, 2024

This is the support for just the v3 only dms and no dual sending. This is how someone would get a full working v3 only client.

Puts checks in place to catch if a V2 client is trying to access V3 dms.
Tests to make sure V3 dms are not being exposed anywhere when a V3 only client is not present.

@nplasterer nplasterer self-assigned this Oct 19, 2024
@nplasterer nplasterer changed the base branch from main to np/smart-contract-wallet-support October 19, 2024 19:30
@nplasterer nplasterer marked this pull request as ready for review October 20, 2024 15:43
@nplasterer nplasterer requested a review from a team as a code owner October 20, 2024 15:43
Copy link
Contributor

@richardhuaaa richardhuaaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, mainly left a few questions. Btw, is this summary correct?

Conversation[s] - a wrapper around v2 and v3, including both groups and DMs. Depending on which method you call, it may apply specifically to v2 or v3.

Group and DM - v3 only.

@@ -615,6 +617,33 @@ class Client() {
}
}

fun findConversation(conversationId: String): Conversation? {
val client = v3Client ?: throw XMTPException("Error no V3 client initialized")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just ramping up here, in this version of the SDK are we always automatically creating a v3 client, or do we require implementers to enable it? Is the expectation that this method would only be called if a v3 client exists, otherwise findGroup() would be used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct in the follow up PR where I implement dual sending and clients that potentially have V2 clients associated I'll add in the deprecation warnings for the findGroups method.

is V1 -> throw XMTPException("Only supported for V3")
is V2 -> throw XMTPException("Only supported for V3")
is Group -> group.name
is Dm -> dm.name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we planning to support changeable names for DM's? Same with imageURL/description (in general any group metadata)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great question. I just kept it because we didn't disable it in the backend but I don't have to expose it if people feel strongly about it.

Comment on lines +49 to +52
is V1 -> throw XMTPException("Only supported for V3")
is V2 -> throw XMTPException("Only supported for V3")
is Group -> group.id
is Dm -> dm.id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do implementers need to switch between querying conversations by ID and by topic when they migrate from v2 to v3?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some places where querying by topic is still necessary. For example push notifications still expose only the topic. Since the topic just has the id inside it I have a method that parses the topic into the id to then search for it. In V2 mostly people were looking up conversations by address not topic.

Comment on lines +134 to +137
is V1 -> throw XMTPException("Only supported for V3")
is V2 -> throw XMTPException("Only supported for V3")
is Group -> group.updateConsentState(state)
is Dm -> dm.updateConsentState(state)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just ramping up here, how is consent state updated in V2?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's implemented on the client in a method on contacts. When it was originally written it was just consenting on addresses so that made sense. We could probably update the state here for V1 and V2 but feels unnecessary as we don't want people building new V2 functionality and instead moving to V3. https://github.com/xmtp/xmtp-android/blob/main/library/src/main/java/org/xmtp/android/library/Contacts.kt

}
}

suspend fun send(prepared: PreparedMessage): String {
suspend fun <T> prepareMessageV3(content: T, options: SendOptions? = null): String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do implementers need to call different code when preparing messages when they migrate from v2 to v3? Just curious why we couldn't abstract it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya it's a bit unfortunate. I tried several different hacks to try and make it work for both V3 and V2 but since V2 depends so heavily on the envelope it would be a break change. I'm cool with not exposing this in the interface and just cutting the old one over eventually. Integrators will likely call these methods directly on the group or dm anyways.

Comment on lines +257 to +273
fun decryptV3(
message: MessageV3,
): DecryptedMessage {
return when (this) {
is V1 -> conversationV1.decrypt(envelope)
is V2 -> conversationV2.decrypt(envelope)
is Group -> {
message?.decrypt() ?: throw XMTPException("Groups require message be passed")
}
is V1 -> throw XMTPException("Only supported for V3")
is V2 -> throw XMTPException("Only supported for V3")
is Group -> message.decrypt()
is Dm -> message.decrypt()
}
}

fun decodeV3(message: MessageV3): DecodedMessage {
return when (this) {
is V1 -> throw XMTPException("Only supported for V3")
is V2 -> throw XMTPException("Only supported for V3")
is Group -> message.decode()
is Dm -> message.decode()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we can't merge into the same decrypt() and decode() methods, so implementers don't need to change their code when moving from v2 to v3?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya that would be ideal. Unfortunately the decrypt method in V2 takes an envelope and the decode takes a V3 message. I could make it take a envelope or a message which I think is how it worked before. But it just creates extra null checking for things that will be null half the time in either case. I'm open though if you think it's valuable.

Comment on lines +182 to +197
suspend fun findOrCreateDm(peerAddress: String): Dm {
if (client.hasV2Client) throw XMTPException("Only supported for V3 only clients.")
if (peerAddress.lowercase() == client.address.lowercase()) {
throw XMTPException("Recipient is sender")
}
val contacts = client.contacts
contacts.refreshConsentList()
if (contacts.consentList.state(peerAddress) == ConsentState.UNKNOWN) {
contacts.allow(listOf(peerAddress))
val falseAddresses =
client.canMessageV3(listOf(peerAddress)).filter { !it.value }.map { it.key }
if (falseAddresses.isNotEmpty()) {
throw XMTPException("${falseAddresses.joinToString()} not on network")
}
var dm = client.findDm(peerAddress)
if (dm == null) {
val dmConversation = libXMTPConversations?.createDm(peerAddress.lowercase())
?: throw XMTPException("Client does not support V3 Dms")
dm = Dm(client, dmConversation)
client.contacts.allowGroups(groupIds = listOf(dm.id))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if all of this should happen inside libxmtp? Then we can enforce the same logic across all SDK's, we have fewer calls across the FFI boundaries, and we can perhaps optimize things within libxmtp

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats a great idea!

val signature = consentProof.signature
val timestamp = consentProof.timestamp

if (!KeyUtil.validateConsentSignature(signature, client.address, peerAddress, timestamp)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to log an error/warning, and possibly return it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this code changed at all in this PR. Something to visit whenever we touch consent proofs for V3

ConversationOrder.LAST_MESSAGE -> {
conversations.map { conversation ->
val message =
conversation.findMessages(FfiListMessagesOptions(null, null, null, null))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to limit and order these descending

xmtp/libxmtp#1164

Base automatically changed from np/smart-contract-wallet-support to main October 22, 2024 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants