diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 4d133dbf0..b79f4ad52 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -209,7 +209,7 @@ class XMTPModule : Module() { // // Auth functions // - AsyncFunction("auth") { address: String, environment: String, appVersion: String?, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, enableAlphaMls: Boolean?, dbEncryptionKey: List? -> + AsyncFunction("auth") { address: String, environment: String, appVersion: String?, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, enableAlphaMls: Boolean?, dbEncryptionKey: List?, dbDirectory: String? -> logV("auth") requireNotProductionEnvForAlphaMLS(enableAlphaMls, environment) val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address) @@ -236,6 +236,7 @@ class XMTPModule : Module() { enableAlphaMls = enableAlphaMls == true, appContext = context, dbEncryptionKey = encryptionKeyBytes, + dbDirectory = dbDirectory ) val client = Client().create(account = reactSigner, options = options) clients[address] = client @@ -250,7 +251,7 @@ class XMTPModule : Module() { } // Generate a random wallet and set the client to that - AsyncFunction("createRandom") { environment: String, appVersion: String?, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, enableAlphaMls: Boolean?, dbEncryptionKey: List? -> + AsyncFunction("createRandom") { environment: String, appVersion: String?, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, enableAlphaMls: Boolean?, dbEncryptionKey: List?, dbDirectory: String? -> logV("createRandom") requireNotProductionEnvForAlphaMLS(enableAlphaMls, environment) val privateKey = PrivateKeyBuilder() @@ -276,6 +277,7 @@ class XMTPModule : Module() { enableAlphaMls = enableAlphaMls == true, appContext = context, dbEncryptionKey = encryptionKeyBytes, + dbDirectory = dbDirectory ) val randomClient = Client().create(account = privateKey, options = options) ContentJson.Companion @@ -286,7 +288,7 @@ class XMTPModule : Module() { ) } - AsyncFunction("createFromKeyBundle") { keyBundle: String, environment: String, appVersion: String?, enableAlphaMls: Boolean?, dbEncryptionKey: List? -> + AsyncFunction("createFromKeyBundle") { keyBundle: String, environment: String, appVersion: String?, enableAlphaMls: Boolean?, dbEncryptionKey: List?, dbDirectory: String? -> logV("createFromKeyBundle") requireNotProductionEnvForAlphaMLS(enableAlphaMls, environment) @@ -301,6 +303,7 @@ class XMTPModule : Module() { enableAlphaMls = enableAlphaMls == true, appContext = context, dbEncryptionKey = encryptionKeyBytes, + dbDirectory = dbDirectory ) val bundle = PrivateKeyOuterClass.PrivateKeyBundle.parseFrom( diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f77f8d46e..981b99906 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -449,7 +449,7 @@ PODS: - GenericJSON (~> 2.0) - Logging (~> 1.0.0) - secp256k1.swift (~> 0.1) - - XMTP (0.11.2): + - XMTP (0.11.3): - Connect-Swift (= 0.12.0) - GzipSwift - LibXMTP (= 0.5.1-beta0) @@ -458,7 +458,7 @@ PODS: - ExpoModulesCore - MessagePacker - secp256k1.swift - - XMTP (= 0.11.2) + - XMTP (= 0.11.3) - Yoga (1.14.0) DEPENDENCIES: @@ -763,8 +763,8 @@ SPEC CHECKSUMS: secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634 SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1 web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959 - XMTP: 9c447c0e2df1e7aab5e4ebded23e972259c6e930 - XMTPReactNative: 44c04dd6954e4ba370002387b3bbc37ebeaa728f + XMTP: 3e8e649cc8a6cebe4a1a16f6c396a273fefb2662 + XMTPReactNative: 602e98fa5dfa0fe17c1bda5a94c7efbaa693c38c Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9 PODFILE CHECKSUM: 95d6ace79946933ecf80684613842ee553dd76a2 diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 548a13c37..4dce7acc8 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -67,6 +67,60 @@ test('can delete a local database', async () => { return true }) +test('can make a MLS V3 client with encryption key and database directory', async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const dbDir = 'xmtp_db' + + const key = new Uint8Array([ + 233, 120, 198, 96, 154, 65, 132, 17, 132, 96, 250, 40, 103, 35, 125, 64, + 166, 83, 208, 224, 254, 44, 205, 227, 175, 49, 234, 129, 74, 252, 135, 145, + ]) + const client = await Client.createRandom({ + env: 'local', + appVersion: 'Testing/0.0.0', + enableAlphaMls: true, + dbEncryptionKey: key, + dbDirectory: dbDir, + }) + + const anotherClient = await Client.createRandom({ + env: 'local', + appVersion: 'Testing/0.0.0', + enableAlphaMls: true, + dbEncryptionKey: key, + }) + + await client.conversations.newGroup([anotherClient.address]) + assert( + (await client.conversations.listGroups()).length === 1, + `should have a group size of 1 but was ${ + (await client.conversations.listGroups()).length + }` + ) + + const bundle = await client.exportKeyBundle() + const clientFromBundle = await Client.createFromKeyBundle(bundle, { + env: 'local', + appVersion: 'Testing/0.0.0', + enableAlphaMls: true, + dbEncryptionKey: key, + dbDirectory: dbDir, + }) + + assert( + clientFromBundle.address === client.address, + `clients dont match ${client.address} and ${clientFromBundle.address}` + ) + + assert( + (await clientFromBundle.conversations.listGroups()).length === 1, + `should have a group size of 1 but was ${ + (await clientFromBundle.conversations.listGroups()).length + }` + ) + return true +}) + test('can drop a local database', async () => { const [client, anotherClient] = await createClients(2) diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index 210dae2e6..8b96a825b 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -123,7 +123,7 @@ public class XMTPModule: Module { // // Auth functions // - AsyncFunction("auth") { (address: String, environment: String, appVersion: String?, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, enableAlphaMls: Bool?, dbEncryptionKey: [UInt8]?) in + AsyncFunction("auth") { (address: String, environment: String, appVersion: String?, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, enableAlphaMls: Bool?, dbEncryptionKey: [UInt8]?, dbDirectory: String?) in try requireNotProductionEnvForAlphaMLS(enableAlphaMls: enableAlphaMls, environment: environment) let signer = ReactNativeSigner(module: self, address: address) @@ -138,7 +138,7 @@ public class XMTPModule: Module { let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) - let options = createClientConfig(env: environment, appVersion: appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData) + let options = createClientConfig(env: environment, appVersion: appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData, dbDirectory: dbDirectory) let client = try await XMTP.Client.create(account: signer, options: options) await clientsManager.updateClient(key: address, client: client) self.signer = nil @@ -150,7 +150,7 @@ public class XMTPModule: Module { } // Generate a random wallet and set the client to that - AsyncFunction("createRandom") { (environment: String, appVersion: String?, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, enableAlphaMls: Bool?, dbEncryptionKey: [UInt8]?) -> [String: String] in + AsyncFunction("createRandom") { (environment: String, appVersion: String?, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, enableAlphaMls: Bool?, dbEncryptionKey: [UInt8]?, dbDirectory: String?) -> [String: String] in try requireNotProductionEnvForAlphaMLS(enableAlphaMls: enableAlphaMls, environment: environment) let privateKey = try PrivateKey.generate() @@ -164,7 +164,7 @@ public class XMTPModule: Module { let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) - let options = createClientConfig(env: environment, appVersion: appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData) + let options = createClientConfig(env: environment, appVersion: appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData, dbDirectory: dbDirectory) let client = try await Client.create(account: privateKey, options: options) await clientsManager.updateClient(key: client.address, client: client) @@ -175,7 +175,7 @@ public class XMTPModule: Module { } // Create a client using its serialized key bundle. - AsyncFunction("createFromKeyBundle") { (keyBundle: String, environment: String, appVersion: String?, enableAlphaMls: Bool?, dbEncryptionKey: [UInt8]?) -> [String: String] in + AsyncFunction("createFromKeyBundle") { (keyBundle: String, environment: String, appVersion: String?, enableAlphaMls: Bool?, dbEncryptionKey: [UInt8]?, dbDirectory: String?) -> [String: String] in try requireNotProductionEnvForAlphaMLS(enableAlphaMls: enableAlphaMls, environment: environment) do { @@ -185,7 +185,7 @@ public class XMTPModule: Module { throw Error.invalidKeyBundle } let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) - let options = createClientConfig(env: environment, appVersion: appVersion, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData) + let options = createClientConfig(env: environment, appVersion: appVersion, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData, dbDirectory: dbDirectory) let client = try await Client.from(bundle: bundle, options: options) await clientsManager.updateClient(key: client.address, client: client) return [ @@ -1198,7 +1198,7 @@ public class XMTPModule: Module { // Helpers // - func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, mlsAlpha: Bool = false, encryptionKey: Data? = nil) -> XMTP.ClientOptions { + func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, mlsAlpha: Bool = false, encryptionKey: Data? = nil, dbDirectory: String? = nil) -> XMTP.ClientOptions { // Ensure that all codecs have been registered. switch env { case "local": @@ -1206,19 +1206,19 @@ public class XMTPModule: Module { env: XMTP.XMTPEnvironment.local, isSecure: false, appVersion: appVersion - ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: mlsAlpha, mlsEncryptionKey: encryptionKey) + ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: mlsAlpha, mlsEncryptionKey: encryptionKey, mlsDbDirectory: dbDirectory) case "production": return XMTP.ClientOptions(api: XMTP.ClientOptions.Api( env: XMTP.XMTPEnvironment.production, isSecure: true, appVersion: appVersion - ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: false, mlsEncryptionKey: encryptionKey) + ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: false, mlsEncryptionKey: encryptionKey, mlsDbDirectory: dbDirectory) default: return XMTP.ClientOptions(api: XMTP.ClientOptions.Api( env: XMTP.XMTPEnvironment.dev, isSecure: true, appVersion: appVersion - ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: mlsAlpha, mlsEncryptionKey: encryptionKey) + ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, mlsAlpha: mlsAlpha, mlsEncryptionKey: encryptionKey, mlsDbDirectory: dbDirectory) } } diff --git a/ios/XMTPReactNative.podspec b/ios/XMTPReactNative.podspec index 4f6750618..22e140c5e 100644 --- a/ios/XMTPReactNative.podspec +++ b/ios/XMTPReactNative.podspec @@ -26,5 +26,5 @@ Pod::Spec.new do |s| s.source_files = "**/*.{h,m,swift}" s.dependency 'secp256k1.swift' s.dependency "MessagePacker" - s.dependency "XMTP", "= 0.11.2" + s.dependency "XMTP", "= 0.11.3" end diff --git a/src/index.ts b/src/index.ts index b0b6d10d2..734909119 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,7 +64,8 @@ export async function auth( hasCreateIdentityCallback?: boolean | undefined, hasEnableIdentityCallback?: boolean | undefined, enableAlphaMls?: boolean | undefined, - dbEncryptionKey?: Uint8Array | undefined + dbEncryptionKey?: Uint8Array | undefined, + dbDirectory?: string | undefined ) { return await XMTPModule.auth( address, @@ -73,7 +74,8 @@ export async function auth( hasCreateIdentityCallback, hasEnableIdentityCallback, enableAlphaMls, - dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined + dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined, + dbDirectory ) } @@ -87,7 +89,8 @@ export async function createRandom( hasCreateIdentityCallback?: boolean | undefined, hasEnableIdentityCallback?: boolean | undefined, enableAlphaMls?: boolean | undefined, - dbEncryptionKey?: Uint8Array | undefined + dbEncryptionKey?: Uint8Array | undefined, + dbDirectory?: string | undefined ): Promise { return await XMTPModule.createRandom( environment, @@ -95,7 +98,8 @@ export async function createRandom( hasCreateIdentityCallback, hasEnableIdentityCallback, enableAlphaMls, - dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined + dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined, + dbDirectory ) } @@ -104,14 +108,16 @@ export async function createFromKeyBundle( environment: 'local' | 'dev' | 'production', appVersion?: string | undefined, enableAlphaMls?: boolean | undefined, - dbEncryptionKey?: Uint8Array | undefined + dbEncryptionKey?: Uint8Array | undefined, + dbDirectory?: string | undefined ): Promise { return await XMTPModule.createFromKeyBundle( keyBundle, environment, appVersion, enableAlphaMls, - dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined + dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined, + dbDirectory ) } diff --git a/src/lib/Client.ts b/src/lib/Client.ts index e2675d6c2..7993781a0 100644 --- a/src/lib/Client.ts +++ b/src/lib/Client.ts @@ -108,7 +108,8 @@ export class Client< Boolean(createSubscription), Boolean(enableSubscription), Boolean(options.enableAlphaMls), - options.dbEncryptionKey + options.dbEncryptionKey, + options.dbDirectory ) })().catch((error) => { console.error('ERROR in create: ', error) @@ -148,7 +149,8 @@ export class Client< Boolean(createSubscription), Boolean(enableSubscription), Boolean(options.enableAlphaMls), - options.dbEncryptionKey + options.dbEncryptionKey, + options.dbDirectory ) this.removeSubscription(enableSubscription) this.removeSubscription(createSubscription) @@ -182,7 +184,8 @@ export class Client< options.env, options.appVersion, Boolean(options.enableAlphaMls), - options.dbEncryptionKey + options.dbEncryptionKey, + options.dbDirectory ) return new Client( @@ -468,6 +471,10 @@ export type ClientOptions = { * OPTIONAL specify the encryption key for the database. The encryption key must be exactly 32 bytes. */ dbEncryptionKey?: Uint8Array + /** + * OPTIONAL specify the XMTP managed database directory + */ + dbDirectory?: string } export type KeyType = { @@ -485,6 +492,7 @@ export function defaultOptions(opts?: Partial): ClientOptions { env: 'dev', enableAlphaMls: false, dbEncryptionKey: undefined, + dbDirectory: undefined, } return { ..._defaultOptions, ...opts } as ClientOptions