Skip to content

Commit

Permalink
feat: Consent Proofs
Browse files Browse the repository at this point in the history
Added handling for consent proofs
Bumped protos
Bumped native packages
  • Loading branch information
Alex Risch authored and Alex Risch committed May 4, 2024
1 parent 4c62517 commit e315881
Show file tree
Hide file tree
Showing 15 changed files with 253 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.1"
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
36 changes: 19 additions & 17 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.8):
- 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.8)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -522,6 +522,7 @@ DEPENDENCIES:
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
- XMTP (from `/Users/alexrisch/ios/xmtp-ios`)
- XMTPReactNative (from `../../ios`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

Expand All @@ -543,7 +544,6 @@ SPEC REPOS:
- secp256k1.swift
- SwiftProtobuf
- web3.swift
- XMTP

EXTERNAL SOURCES:
boost:
Expand Down Expand Up @@ -668,6 +668,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-screens"
RNSVG:
:path: "../node_modules/react-native-svg"
XMTP:
:path: "/Users/alexrisch/ios/xmtp-ios"
XMTPReactNative:
:path: "../../ios"
Yoga:
Expand Down Expand Up @@ -701,11 +703,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 +726,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 +753,10 @@ SPEC CHECKSUMS:
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
XMTP: bf00ef58d4fbcc8ab740145a6303591adf3fb355
XMTPReactNative: fae44562e5e457c66fde8e2613e1dfd3c1a71113
XMTP: 1bee03b6a541f811f8199b254ef3a8ce09b85bdb
XMTPReactNative: 655fdf39d8481ce474bbbfc7d63af0c55cee0e54
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 95d6ace79946933ecf80684613842ee553dd76a2
PODFILE CHECKSUM: b1a17a26fef9b207a68bf254156a4e8e034432c8

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
73 changes: 69 additions & 4 deletions example/src/tests.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
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 ReactNativeBlobUtil from 'react-native-blob-util'
import Config from 'react-native-config'
import { TextEncoder, TextDecoder } from 'text-encoding'
import { PrivateKeyAccount } from 'viem'
import { PrivateKeyAccount, toHex } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { DecodedMessage } from 'xmtp-react-native-sdk/lib/DecodedMessage'

Expand Down Expand Up @@ -259,9 +260,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 +1676,67 @@ test('can handle complex streaming setup with messages from self', async () => {

return true
})

test('can send and receive consent proofs', async () => {
const bo = await Client.createRandom({ env: 'local' })
await delayToPropogate()
const alix = await Client.createRandom({ env: 'local' })
await delayToPropogate()
const timestamp = Date.now()
const consentMessage =
'XMTP : Grant inbox consent to sender\n' +
'\n' +
`Current Time: ${timestamp}\n` +
`From Address: ${bo.address}\n` +
'\n' +
'For more info: https://xmtp.org/signatures/'
const data = sha256(consentMessage)
const signature = await alix.sign(data, {
kind: 'identity',
})
const sig = signatureProto.Signature.decode(signature)
console.log('sig', sig)
if (!sig.ecdsaCompact) {
throw new Error('signature should have ecdsaCompact')
}
const r = toHex(sig.ecdsaCompact?.bytes.slice(0, 32))
const s = toHex(sig.ecdsaCompact?.bytes.slice(32, 64))
const v =
sig.ecdsaCompact?.recovery < 27
? sig.ecdsaCompact?.recovery + 27
: sig.ecdsaCompact?.recovery
const test = r + s.substring(2) + v.toString(16)

const consentProof = invitation.ConsentProofPayload.fromPartial({
payloadVersion:
invitation.ConsentProofPayloadVersion.CONSENT_PROOF_PAYLOAD_VERSION_1,
signature: test,
timestamp,
})

const boConvo = await bo.conversations.newConversation(
alix.address,
undefined,
consentProof
)
assert(!!boConvo.consentProof, 'consentProof should exist')
const alixConvo = (await alix.conversations.list())[0]
assert(!!alixConvo.consentProof, 'consentProof should exist')
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
})
41 changes: 38 additions & 3 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6843,6 +6843,16 @@
rxjs "^7.8.0"
undici "^5.8.1"

"@xmtp/proto@3.54.0":
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 e315881

Please sign in to comment.