Skip to content

Commit

Permalink
build: use v3/main edition of libxmtp bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
dmccartney committed Feb 2, 2024
1 parent 634597a commit 33cd739
Show file tree
Hide file tree
Showing 17 changed files with 283 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.13.9'
flutter-version: '3.16.9'
channel: 'stable'
- run: docker-compose -p xmtp -f "tool/local-node/docker-compose.yml" up -d
- run: flutter pub get
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.13.9'
flutter-version: '3.16.9'
channel: 'stable'
- run: docker-compose -p xmtp -f "tool/local-node/docker-compose.yml" up -d
- run: flutter pub get
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ PODS:
- sqlite3/rtree
- url_launcher_ios (0.0.1):
- Flutter
- xmtp_bindings_flutter (0.0.1-development.3)
- xmtp_bindings_flutter (0.1.0-development.1)

DEPENDENCIES:
- cryptography_flutter (from `.symlinks/plugins/cryptography_flutter/ios`)
Expand Down Expand Up @@ -68,16 +68,16 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
cryptography_flutter: 381bdacc984abcfbe3ca45ef7c76566ff061614c
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_app_badger: b87fc231847b03b92ce1412aa351842e7e97932f
integration_test: 13825b8a9334a850581300559b8839134b124670
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
sqlite3: fd89671d969f3e73efe503ce203e28b016b58f68
sqlite3_flutter_libs: 78f93cb854d4680595bc2c63c57209a104b2efb1
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
xmtp_bindings_flutter: 9699cc6008d8fc86ed5dd072b4b920255d01926f
xmtp_bindings_flutter: f153afd7a9fc8afcf86708e76e1dc04f534e6095

PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796

COCOAPODS: 1.12.1
6 changes: 3 additions & 3 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -581,7 +581,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -630,7 +630,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
80 changes: 75 additions & 5 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import 'dart:io';

import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:web3dart/credentials.dart';
import 'package:xmtp_bindings_flutter/xmtp_bindings_flutter.dart' as libxmtp;
import 'package:xmtp_proto/xmtp_proto.dart' as xmtp;

import 'auth.dart';
Expand All @@ -16,6 +21,7 @@ import 'content/decoded.dart';
import 'conversation/conversation.dart';
import 'conversation/conversation_v1.dart';
import 'conversation/conversation_v2.dart';
import 'conversation/conversation_v3.dart';
import 'conversation/manager.dart';

/// This is the top-level entrypoint to the XMTP flutter SDK.
Expand Down Expand Up @@ -68,6 +74,7 @@ class Client implements Codec<DecodedContent> {
xmtp.PrivateKeyBundle get keys => _auth.keys;

final Api _api;
final libxmtp.Client? _v3Client;
final ConversationManager _conversations;
final AuthManager _auth;
final ContactManager _contacts;
Expand All @@ -76,6 +83,7 @@ class Client implements Codec<DecodedContent> {
Client._(
this.address,
this._api,
this._v3Client,
this._conversations,
this._auth,
this._contacts,
Expand All @@ -88,8 +96,15 @@ class Client implements Codec<DecodedContent> {
Api api,
Signer wallet, {
List<Codec> customCodecs = const [],
bool enableGroups = false,
}) async {
var client = await _createUninitialized(api, wallet.address, customCodecs);
var client = await _createUninitialized(
wallet,
api,
wallet.address,
customCodecs,
enableV3: enableGroups,
);
await client._auth.authenticateWithCredentials(wallet);
await client._contacts.ensureSavedContact(client._auth.keys);
return client;
Expand All @@ -101,9 +116,10 @@ class Client implements Codec<DecodedContent> {
Api api,
xmtp.PrivateKeyBundle keys, {
List<Codec> customCodecs = const [],
Signer? signer,
}) async {
var address = keys.wallet;
var client = await _createUninitialized(api, address, customCodecs);
var client = await _createUninitialized(signer, api, address, customCodecs);
await client._auth.authenticateWithKeys(keys);
await client._contacts.ensureSavedContact(client._auth.keys);
return client;
Expand All @@ -113,10 +129,16 @@ class Client implements Codec<DecodedContent> {
/// It assembles the graph of dependencies needed by the [Client].
/// It does not perform authentication nor does it ensure the contact is saved.
static Future<Client> _createUninitialized(
Signer? signer,
Api api,
EthereumAddress address,
List<Codec> customCodecs,
) async {
List<Codec> customCodecs, {
bool enableV3 = false,
// TODO: expose a way for the app to specify these (instead of ephemeral defaults)
String? dbPath,
libxmtp.U8Array32? encryptionKey,
}) async {
await libxmtp.libxmtpInit();
var auth = AuthManager(address, api);
var codecs = CodecRegistry();
var commonCodecs = <Codec>[
Expand All @@ -135,10 +157,39 @@ class Client implements Codec<DecodedContent> {
var contacts = ContactManager(api, auth);
var v1 = ConversationManagerV1(address, api, auth, codecs, contacts);
var v2 = ConversationManagerV2(address, api, auth, codecs, contacts);
var conversations = ConversationManager(address, contacts, v1, v2);
ConversationManagerV3? v3;
libxmtp.Client? v3Client;
if (enableV3) {
// TODO: default to path_provider app folder instead of tmp
dbPath ??= p.join(
Directory.systemTemp.createTempSync().path,
"${address.hex}.${api.config.env}.xmtp.db",
);
// TODO: default to a consistent/stored key
encryptionKey ??= libxmtp.U8Array32.init();
var created = await libxmtp.createClient(
host: "https://${api.config.host}:${api.config.port}",
isSecure: api.config.isSecure,
dbPath: dbPath,
encryptionKey: encryptionKey,
accountAddress: address.hex,
);
v3Client = switch (created) {
libxmtp.CreatedClient_Ready(field0: var v3Client) => v3Client,
libxmtp.CreatedClient_RequiresSignature(field0: var req) =>
signer == null
? throw StateError("signer required to initialize client")
: await req.sign(
signature: await signer.signPersonalMessage(req.textToSign),
),
};
v3 = ConversationManagerV3(address, v3Client);
}
var conversations = ConversationManager(address, contacts, v1, v2, v3);
return Client._(
address,
api,
v3Client,
conversations,
auth,
contacts,
Expand Down Expand Up @@ -170,6 +221,25 @@ class Client implements Codec<DecodedContent> {
}) =>
_conversations.listConversations(start, end, limit, sort);

@experimental
Future<List<Group>> listGroups({
DateTime? start,
DateTime? end,
int? limit,
xmtp.SortDirection? sort = xmtp.SortDirection.SORT_DIRECTION_DESCENDING,
}) =>
_v3Client == null
? throw UnsupportedError("groups are not enabled")
: _conversations.listGroups(start, end, limit, sort);

@experimental
Future<Group> createGroup({
List<String> addresses = const [],
}) =>
_v3Client == null
? throw UnsupportedError("groups are not enabled")
: _conversations.createGroup(addresses);

/// This exposes a stream of new [Conversation]s for the user.
Stream<Conversation> streamConversations() =>
_conversations.streamConversations();
Expand Down
63 changes: 61 additions & 2 deletions lib/src/common/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:grpc/grpc.dart' as grpc;
import 'package:quiver/collection.dart';
import 'package:xmtp_proto/xmtp_proto.dart' as xmtp;

const sdkVersion = '1.3.1';
Expand All @@ -12,22 +13,78 @@ const clientVersion = "xmtp-flutter/$sdkVersion";
/// The conversation managers use this to automatically partition calls.
const maxQueryRequestsPerBatch = 50;

enum ApiEnv {
dev,
production,
custom,
}

class ApiConfig {
final ApiEnv env;
final String host;
final int port;
final bool isSecure;
final bool debugLogRequests;
final String appVersion;

ApiConfig({
required this.env,
required this.host,
required this.port,
required this.isSecure,
required this.debugLogRequests,
required this.appVersion,
});
}

final _hostByEnv = BiMap<ApiEnv, String>()
..addAll({
ApiEnv.dev: 'dev.xmtp.network',
ApiEnv.production: 'production.xmtp.network',
ApiEnv.custom: '127.0.0.1',
});

/// This is an instance of the [xmtp.MessageApiClient] with some
/// metadata helpers (e.g. for setting the authorization token).
class Api {
final ApiConfig config;
final xmtp.MessageApiClient client;
final grpc.ClientChannel _channel;
final _MetadataManager _metadata;

Api._(this._channel, this.client, this._metadata);
Api._(this.config, this._channel, this.client, this._metadata);

factory Api.createEnv(
ApiEnv env, {
bool debugLogRequests = kDebugMode,
String appVersion = "dev/0.0.0-development",
}) =>
Api.create(
env: env,
host: _hostByEnv[env]!,
port: 5556,
isSecure: env != ApiEnv.custom,
debugLogRequests: debugLogRequests,
appVersion: appVersion,
);

factory Api.create({
ApiEnv? env,
String host = 'dev.xmtp.network',
int port = 5556,
bool isSecure = true,
bool debugLogRequests = kDebugMode,
String appVersion = "dev/0.0.0-development",
}) {
var config = ApiConfig(
env: env ?? _hostByEnv.inverse[host] ?? ApiEnv.custom,
// ^ explicit else guess from host else custom
host: host,
port: port,
isSecure: isSecure,
debugLogRequests: debugLogRequests,
appVersion: appVersion,
);
var channel = grpc.ClientChannel(
host,
port: port,
Expand All @@ -40,6 +97,7 @@ class Api {
);

return Api.createAdvanced(
config,
channel,
options: grpc.CallOptions(
timeout: const Duration(minutes: 5),
Expand All @@ -52,6 +110,7 @@ class Api {
}

factory Api.createAdvanced(
ApiConfig config,
grpc.ClientChannel channel, {
grpc.CallOptions? options,
Iterable<grpc.ClientInterceptor>? interceptors,
Expand All @@ -67,7 +126,7 @@ class Api {
interceptors: interceptors,
);
metadata.appVersion = appVersion;
return Api._(channel, client, metadata);
return Api._(config, channel, client, metadata);
}

void clearAuthTokenProvider() {
Expand Down
10 changes: 6 additions & 4 deletions lib/src/common/topic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ class Topic {
EthereumAddress.fromHex(walletAddress).hexEip55;
}

Future<String> generateUserPreferencesIdentifier(List<int> privateKey) async =>
libxmtp.generatePrivatePreferencesTopicIdentifier(
privateKeyBytes: Uint8List.fromList(privateKey),
);
Future<String> generateUserPreferencesIdentifier(List<int> privateKey) async {
await libxmtpInit(); // typically no-op because it's already initialized
return generatePrivatePreferencesTopicIdentifier(
privateKeyBytes: Uint8List.fromList(privateKey),
);
}
6 changes: 4 additions & 2 deletions lib/src/contact.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ class ContactManager {
List<int> payload,
) async {
try {
var decrypted = await libxmtp.userPreferencesDecrypt(
await libxmtpInit(); // typically no-op because it's already initialized
var decrypted = await userPreferencesDecrypt(
publicKey: keys.identity.publicKey.getEncoded(false),
privateKey: keys.identity.privateKey,
encryptedMessage: Uint8List.fromList(payload),
Expand Down Expand Up @@ -226,7 +227,8 @@ class ContactManager {
xmtp.PrivatePreferencesAction action,
) async {
var topic = await Topic.userPreferences(keys.identity.privateKey);
var payload = await libxmtp.userPreferencesEncrypt(
await libxmtpInit(); // typically no-op because it's already initialized
var payload = await userPreferencesEncrypt(
publicKey: keys.identity.publicKey.getEncoded(false),
privateKey: keys.identity.privateKey,
message: action.writeToBuffer(),
Expand Down
Loading

0 comments on commit 33cd739

Please sign in to comment.