Skip to content

Commit

Permalink
Merge pull request #379 from xmtp/ar/consent-proof
Browse files Browse the repository at this point in the history
feat: Consent Proofs
  • Loading branch information
alexrisch authored May 7, 2024
2 parents 4c62517 + df430d5 commit ea2ef84
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 65 deletions.
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:0.10.4"
implementation "org.xmtp:android:0.11.2"
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
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import org.xmtp.android.library.messages.getPublicKeyBundle
import org.xmtp.android.library.push.XMTPPush
import org.xmtp.proto.keystore.api.v1.Keystore.TopicMap.TopicData
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
import org.xmtp.proto.message.contents.Invitation.ConsentProofPayload
import org.xmtp.proto.message.contents.PrivateKeyOuterClass
import java.io.File
import java.util.Date
Expand Down Expand Up @@ -574,11 +575,25 @@ class XMTPModule : Module() {
}
}

AsyncFunction("createConversation") Coroutine { clientAddress: String, peerAddress: String, contextJson: String ->
AsyncFunction("createConversation") Coroutine { clientAddress: String, peerAddress: String, contextJson: String, consentProofPayload: List<Int> ->
withContext(Dispatchers.IO) {
logV("createConversation: $contextJson")
val client = clients[clientAddress] ?: throw XMTPException("No client")
val context = JsonParser.parseString(contextJson).asJsonObject

var consentProof: ConsentProofPayload? = null
if (consentProofPayload.isNotEmpty()) {
val consentProofDataBytes = consentProofPayload.foldIndexed(ByteArray(consentProofPayload.size)) { i, a, v ->
a.apply {
set(
i,
v.toByte()
)
}
}
consentProof = ConsentProofPayload.parseFrom(consentProofDataBytes)
}

val conversation = client.conversations.newConversation(
peerAddress,
context = InvitationV1ContextBuilder.buildFromConversation(
Expand All @@ -595,11 +610,15 @@ class XMTPModule : Module() {

else -> mapOf()
},
)
),
consentProof
)
if (conversation.keyMaterial == null) {
logV("Null key material before encode conversation")
}
if (conversation.consentProof == null) {
logV("Null consent before encode conversation")
}
ConversationWrapper.encode(client, conversation)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.xmtp.android.library.Conversation
class ConversationWrapper {

companion object {
fun encodeToObj(client: Client, conversation: Conversation): Map<String, Any> {
fun encodeToObj(client: Client, conversation: Conversation): Map<String, Any?> {
val context = when (conversation.version) {
Conversation.Version.V2 -> mapOf<String, Any>(
"conversationID" to (conversation.conversationId ?: ""),
Expand All @@ -26,7 +26,8 @@ class ConversationWrapper {
"peerAddress" to conversation.peerAddress,
"version" to if (conversation.version == Conversation.Version.V1) "v1" else "v2",
"conversationID" to (conversation.conversationId ?: ""),
"keyMaterial" to (conversation.keyMaterial?.let { Base64.encodeToString(it, Base64.NO_WRAP) } ?: "")
"keyMaterial" to (conversation.keyMaterial?.let { Base64.encodeToString(it, Base64.NO_WRAP) } ?: ""),
"consentProof" to if (conversation.consentProof != null) Base64.encodeToString(conversation.consentProof?.toByteArray(), Base64.NO_WRAP) else null
)
}

Expand Down
30 changes: 15 additions & 15 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ PODS:
- hermes-engine/Pre-built (= 0.71.14)
- hermes-engine/Pre-built (0.71.14)
- libevent (2.1.12)
- LibXMTP (0.4.4-beta2)
- LibXMTP (0.4.4-beta5)
- Logging (1.0.0)
- MessagePacker (0.4.7)
- MMKV (1.3.4):
- MMKVCore (~> 1.3.4)
- MMKVCore (1.3.4)
- MMKV (1.3.5):
- MMKVCore (~> 1.3.5)
- MMKVCore (1.3.5)
- OpenSSL-Universal (1.1.2200)
- RCT-Folly (2021.07.22.00):
- boost
Expand Down Expand Up @@ -322,7 +322,7 @@ PODS:
- React-Core
- react-native-encrypted-storage (4.0.3):
- React-Core
- react-native-get-random-values (1.11.0):
- react-native-get-random-values (1.10.0):
- React-Core
- react-native-mmkv (2.11.0):
- MMKV (>= 1.2.13)
Expand Down Expand Up @@ -445,16 +445,16 @@ PODS:
- GenericJSON (~> 2.0)
- Logging (~> 1.0.0)
- secp256k1.swift (~> 0.1)
- XMTP (0.10.3):
- XMTP (0.10.9):
- Connect-Swift (= 0.12.0)
- GzipSwift
- LibXMTP (= 0.4.4-beta2)
- LibXMTP (= 0.4.4-beta5)
- web3.swift
- XMTPReactNative (0.1.0):
- ExpoModulesCore
- MessagePacker
- secp256k1.swift
- XMTP (= 0.10.3)
- XMTP (= 0.10.9)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -701,11 +701,11 @@ SPEC CHECKSUMS:
GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
hermes-engine: d7cc127932c89c53374452d6f93473f1970d8e88
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
LibXMTP: 0c073613451e3850bfcaaab5438b481fe887cd97
LibXMTP: e2fb601691981900099551ff3e05621bd73dccf1
Logging: 9ef4ecb546ad3169398d5a723bc9bea1c46bef26
MessagePacker: ab2fe250e86ea7aedd1a9ee47a37083edd41fd02
MMKV: ed58ad794b3f88c24d604a5b74f3fba17fcbaf74
MMKVCore: a67a1cede26175c413176f404a7cedec43f96a0b
MMKV: 506311d0494023c2f7e0b62cc1f31b7370fa3cfb
MMKVCore: 9e2e5fd529b64a9fe15f1a7afb3d73b2e27b4db9
OpenSSL-Universal: 6e1ae0555546e604dbc632a2b9a24a9c46c41ef6
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: e9df143e880d0e879e7a498dc06923d728809c79
Expand All @@ -724,7 +724,7 @@ SPEC CHECKSUMS:
react-native-blob-util: d8fa1a7f726867907a8e43163fdd8b441d4489ea
react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8
react-native-encrypted-storage: db300a3f2f0aba1e818417c1c0a6be549038deb7
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-get-random-values: 384787fd76976f5aec9465aff6fa9e9129af1e74
react-native-mmkv: e97c0c79403fb94577e5d902ab1ebd42b0715b43
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
react-native-quick-base64: 777057ea4286f806b00259ede65dc79c7c706320
Expand All @@ -751,10 +751,10 @@ SPEC CHECKSUMS:
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
XMTP: bf00ef58d4fbcc8ab740145a6303591adf3fb355
XMTPReactNative: fae44562e5e457c66fde8e2613e1dfd3c1a71113
XMTP: 5efac5f69327cc6a4ad5b1f1fb9624b9e8355efe
XMTPReactNative: 5c4baec31d5ef83e5e5a35c33c31e70524db620e
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 95d6ace79946933ecf80684613842ee553dd76a2

COCOAPODS: 1.14.2
COCOAPODS: 1.15.2
1 change: 1 addition & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@thirdweb-dev/react-native": "^0.6.2",
"@thirdweb-dev/react-native-compat": "^0.6.2",
"@xmtp/frames-client": "^0.3.2",
"@xmtp/proto": "3.54.0",
"ethers": "^5.7.2",
"expo": "~48.0.18",
"expo-document-picker": "^11.5.4",
Expand Down
70 changes: 65 additions & 5 deletions example/src/tests.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { sha256 } from '@noble/hashes/sha256'
import { FramesClient } from '@xmtp/frames-client'
import { content } from '@xmtp/proto'
import { content, invitation, signature as signatureProto } from '@xmtp/proto'
import { createHmac } from 'crypto'
import { ethers } from 'ethers'
import ReactNativeBlobUtil from 'react-native-blob-util'
import Config from 'react-native-config'
import { TextEncoder, TextDecoder } from 'text-encoding'
import { PrivateKeyAccount } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { createWalletClient, custom, PrivateKeyAccount, toHex } from 'viem'
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
import { DecodedMessage } from 'xmtp-react-native-sdk/lib/DecodedMessage'

import {
Expand Down Expand Up @@ -259,9 +261,9 @@ test('can load 1995 conversations from dev network "2k lens convos" account', as
xmtpClient.address === '0x209fAEc92D9B072f3E03d6115002d6652ef563cd',
'Address: ' + xmtpClient.address
)
let start = Date.now()
const start = Date.now()
const conversations = await xmtpClient.conversations.list()
let end = Date.now()
const end = Date.now()
console.log(
`Loaded ${conversations.length} conversations in ${end - start}ms`
)
Expand Down Expand Up @@ -1675,3 +1677,61 @@ test('can handle complex streaming setup with messages from self', async () => {

return true
})

test('can send and receive consent proofs', async () => {
const alixWallet = await ethers.Wallet.createRandom()
const boWallet = await ethers.Wallet.createRandom()
const bo = await Client.create(boWallet, { env: 'local' })
await delayToPropogate()
const alix = await Client.create(alixWallet, { env: 'local' })
await delayToPropogate()

const timestamp = Date.now()
const consentMessage =
'XMTP : Grant inbox consent to sender\n' +
'\n' +
`Current Time: ${new Date(timestamp).toUTCString()}\n` +
`From Address: ${bo.address}\n` +
'\n' +
'For more info: https://xmtp.org/signatures/'
const sig = await alixWallet.signMessage(consentMessage)
const consentProof = invitation.ConsentProofPayload.fromPartial({
payloadVersion:
invitation.ConsentProofPayloadVersion.CONSENT_PROOF_PAYLOAD_VERSION_1,
signature: sig,
timestamp,
})

const boConvo = await bo.conversations.newConversation(
alix.address,
undefined,
consentProof
)
await delayToPropogate()
assert(!!boConvo?.consentProof, 'bo consentProof should exist')
const convos = await alix.conversations.list()
const alixConvo = convos.find((convo) => convo.topic === boConvo.topic)
await delayToPropogate()
assert(!!alixConvo?.consentProof, ' alix consentProof should not exist')
await delayToPropogate()
await alix.contacts.refreshConsentList()
const isAllowed = await alix.contacts.isAllowed(bo.address)
assert(isAllowed, 'bo should be allowed')
return true
})

test('can start conversations without consent proofs', async () => {
const bo = await Client.createRandom({ env: 'local' })
await delayToPropogate()
const alix = await Client.createRandom({ env: 'local' })
await delayToPropogate()

const boConvo = await bo.conversations.newConversation(alix.address)
await delayToPropogate()
assert(!boConvo.consentProof, 'consentProof should not exist')
const alixConvo = (await alix.conversations.list())[0]
await delayToPropogate()
assert(!alixConvo.consentProof, 'consentProof should not exist')
await delayToPropogate()
return true
})
43 changes: 39 additions & 4 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4215,7 +4215,7 @@

"@ethersproject/shims@^5.7.0":
version "5.7.0"
resolved "https://registry.npmjs.org/@ethersproject/shims/-/shims-5.7.0.tgz"
resolved "https://registry.yarnpkg.com/@ethersproject/shims/-/shims-5.7.0.tgz#ee32e543418595774029c5ea6123ea8995e7e154"
integrity sha512-WeDptc6oAprov5CCN2LJ/6/+dC9gTonnkdAtLepm/7P5Z+3PRxS5NpfVWmOMs1yE4Vitl2cU8bOPWC0GvGSbVg==

"@ethersproject/[email protected]", "@ethersproject/signing-key@^5.7.0":
Expand Down Expand Up @@ -6843,6 +6843,16 @@
rxjs "^7.8.0"
undici "^5.8.1"

"@xmtp/[email protected]":
version "3.54.0"
resolved "https://registry.yarnpkg.com/@xmtp/proto/-/proto-3.54.0.tgz#1b6be9fa2268a51b730bf484af3bf0ebc2621505"
integrity sha512-X0jDRR19/tH0qRB8mM/H/vBueQAK22VZF4QUnDN7TgnbNaOYL5DvSmPfXFH+xzeGKQ5S0zgwc+qFJbI4xoKNHw==
dependencies:
long "^5.2.0"
protobufjs "^7.0.0"
rxjs "^7.8.0"
undici "^5.8.1"

JSONStream@^1.3.5:
version "1.3.5"
resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz"
Expand Down Expand Up @@ -14471,7 +14481,16 @@ strict-uri-encode@^2.0.0:
resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -14545,7 +14564,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand All @@ -14559,6 +14578,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz"
Expand Down Expand Up @@ -15829,7 +15855,7 @@ word-wrap@~1.2.3:
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -15847,6 +15873,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz"
Expand Down
7 changes: 6 additions & 1 deletion ios/Wrappers/ConversationWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ struct ConversationWrapper {
"metadata": cv2.context.metadata,
]
}
var consentProof: String? = nil
if (conversation.consentProof != nil) {
consentProof = try conversation.consentProof?.serializedData().base64EncodedString()
}
return [
"clientAddress": client.address,
"topic": conversation.topic,
Expand All @@ -25,7 +29,8 @@ struct ConversationWrapper {
"peerAddress": conversation.peerAddress,
"version": conversation.version == .v1 ? "v1" : "v2",
"conversationID": conversation.conversationID ?? "",
"keyMaterial": conversation.keyMaterial?.base64EncodedString() ?? ""
"keyMaterial": conversation.keyMaterial?.base64EncodedString() ?? "",
"consentProof": consentProof ?? ""
]
}

Expand Down
Loading

0 comments on commit ea2ef84

Please sign in to comment.