Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Oct 24, 2024
2 parents e499905 + 3b31d0f commit c18f087
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 59 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ let package = Package(
.package(url: "https://github.com/1024jp/GzipSwift", from: "5.2.0"),
.package(url: "https://github.com/bufbuild/connect-swift", exact: "0.12.0"),
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"),
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "0.5.9-beta1"),
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "0.5.10"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
35 changes: 35 additions & 0 deletions Sources/XMTPTestHelpers/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#if canImport(XCTest)
import Combine
import CryptoKit
import XCTest
@testable import XMTPiOS
import LibXMTP
Expand Down Expand Up @@ -67,6 +68,40 @@ public struct FakeWallet: SigningKey {
}
}

public struct FakeSCWWallet: SigningKey {
public var walletAddress: String
private var internalSignature: String

public init() throws {
// Simulate a wallet address (could be derived from a hash of some internal data)
self.walletAddress = UUID().uuidString // Using UUID for uniqueness in this fake example
self.internalSignature = Data(repeating: 0x01, count: 64).toHex // Fake internal signature
}

public var address: String {
walletAddress
}

public var type: WalletType {
WalletType.SCW
}

public var chainId: Int64? {
1
}

public static func generate() throws -> FakeSCWWallet {
return try FakeSCWWallet()
}

public func signSCW(message: String) async throws -> Data {
// swiftlint:disable force_unwrapping
let digest = SHA256.hash(data: message.data(using: .utf8)!)
// swiftlint:enable force_unwrapping
return Data(digest)
}
}

@available(iOS 15, *)
public struct Fixtures {
public var alice: PrivateKey!
Expand Down
67 changes: 52 additions & 15 deletions Sources/XMTPiOS/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,39 +161,64 @@ public final class Client {
}
}

// This is a V3 only feature
public static func createOrBuild(account: SigningKey, options: ClientOptions) async throws -> Client {
let inboxId = try await getOrCreateInboxId(options: options, address: account.address)

static func initializeClient(
accountAddress: String,
options: ClientOptions,
signingKey: SigningKey?,
inboxId: String
) async throws -> Client {
let (libxmtpClient, dbPath) = try await initV3Client(
accountAddress: account.address,
accountAddress: accountAddress,
options: options,
privateKeyBundleV1: nil,
signingKey: account,
signingKey: signingKey,
inboxId: inboxId
)

guard let v3Client = libxmtpClient else {
throw ClientError.noV3Client("Error no V3 client initialized")
}

let client = try Client(
address: account.address,
address: accountAddress,
v3Client: v3Client,
dbPath: dbPath,
installationID: v3Client.installationId().toHex,
inboxID: v3Client.inboxId(),
environment: options.api.env
)

let conversations = client.conversations
let contacts = client.contacts

for codec in (options.codecs) {
// Register codecs
for codec in options.codecs {
client.register(codec: codec)
}

return client
}

public static func createV3(account: SigningKey, options: ClientOptions) async throws -> Client {
let accountAddress = account.address
let inboxId = try await getOrCreateInboxId(options: options, address: accountAddress)

return try await initializeClient(
accountAddress: accountAddress,
options: options,
signingKey: account,
inboxId: inboxId
)
}

public static func buildV3(address: String, options: ClientOptions) async throws -> Client {
let inboxId = try await getOrCreateInboxId(options: options, address: address)

return try await initializeClient(
accountAddress: address,
options: options,
signingKey: nil,
inboxId: inboxId
)
}

static func initV3Client(
accountAddress: String,
options: ClientOptions?,
Expand Down Expand Up @@ -224,7 +249,7 @@ public final class Client {
let alias = "xmtp-\(options?.api.env.rawValue ?? "")-\(inboxId).db3"
let dbURL = directoryURL.appendingPathComponent(alias).path

var encryptionKey = options?.dbEncryptionKey
let encryptionKey = options?.dbEncryptionKey
if (encryptionKey == nil) {
throw ClientError.creationError("No encryption key passed for the database. Please store and provide a secure encryption key.")
}
Expand All @@ -246,8 +271,20 @@ public final class Client {
if let signatureRequest = v3Client.signatureRequest() {
if let signingKey = signingKey {
do {
let signedData = try await signingKey.sign(message: signatureRequest.signatureText())
try await signatureRequest.addEcdsaSignature(signatureBytes: signedData.rawData)
if signingKey.type == WalletType.SCW {
guard let chainId = signingKey.chainId else {
throw ClientError.creationError("Chain id must be present to sign Smart Contract Wallet")
}
let signedData = try await signingKey.signSCW(message: signatureRequest.signatureText())
try await signatureRequest.addScwSignature(signatureBytes: signedData,
address: signingKey.address,
chainId: UInt64(chainId),
blockNumber: signingKey.blockNumber.flatMap { $0 >= 0 ? UInt64($0) : nil })

} else {
let signedData = try await signingKey.sign(message: signatureRequest.signatureText())
try await signatureRequest.addEcdsaSignature(signatureBytes: signedData.rawData)
}
try await v3Client.registerIdentity(signatureRequest: signatureRequest)
} catch {
throw ClientError.creationError("Failed to sign the message: \(error.localizedDescription)")
Expand Down Expand Up @@ -651,7 +688,7 @@ public final class Client {
throw ClientError.noV3Client("Error no V3 client initialized")
}
do {
return Group(ffiGroup: try client.group(groupId: groupId.hexToData), client: self)
return Group(ffiGroup: try client.conversation(conversationId: groupId.hexToData), client: self)
} catch {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTPiOS/Contacts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public class ConsentList {
func groupState(groupId: String) async throws -> ConsentState {
if let client = client.v3Client {
return try await client.getConsentState(
entityType: .groupId,
entityType: .conversationId,
entity: groupId
).fromFFI
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/XMTPiOS/Conversations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ final class GroupStreamCallback: FfiConversationCallback {
self.callback = callback
}

func onConversation(conversation: FfiGroup) {
func onConversation(conversation: FfiConversation) {
self.callback(conversation.fromFFI(client: client))
}
}
Expand Down Expand Up @@ -119,7 +119,7 @@ public actor Conversations {
guard let v3Client = client.v3Client else {
return 0
}
return try await v3Client.conversations().syncAllGroups()
return try await v3Client.conversations().syncAllConversations()
}

public func groups(createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil) async throws -> [Group] {
Expand All @@ -136,7 +136,7 @@ public actor Conversations {
if let limit {
options.limit = Int64(limit)
}
return try await v3Client.conversations().list(opts: options).map { $0.fromFFI(client: client) }
return try await v3Client.conversations().listGroups(opts: options).map { $0.fromFFI(client: client) }
}

public func streamGroups() async throws -> AsyncThrowingStream<Group, Error> {
Expand All @@ -150,7 +150,7 @@ public actor Conversations {
}
continuation.yield(group)
}
guard let stream = await self.client.v3Client?.conversations().stream(callback: groupCallback) else {
guard let stream = await self.client.v3Client?.conversations().streamGroups(callback: groupCallback) else {
continuation.finish(throwing: GroupError.streamingFailure)
return
}
Expand All @@ -175,7 +175,7 @@ public actor Conversations {
AsyncThrowingStream { continuation in
let ffiStreamActor = FfiStreamActor()
let task = Task {
let stream = await self.client.v3Client?.conversations().stream(
let stream = await self.client.v3Client?.conversations().streamGroups(
callback: GroupStreamCallback(client: self.client) { group in
guard !Task.isCancelled else {
continuation.finish()
Expand Down Expand Up @@ -435,7 +435,7 @@ public actor Conversations {
AsyncThrowingStream { continuation in
let ffiStreamActor = FfiStreamActor()
let task = Task {
let stream = await self.client.v3Client?.conversations().streamAllMessages(
let stream = await self.client.v3Client?.conversations().streamAllGroupMessages(
messageCallback: MessageCallback(client: self.client) { message in
guard !Task.isCancelled else {
continuation.finish()
Expand Down Expand Up @@ -500,7 +500,7 @@ public actor Conversations {
AsyncThrowingStream { continuation in
let ffiStreamActor = FfiStreamActor()
let task = Task {
let stream = await self.client.v3Client?.conversations().streamAllMessages(
let stream = await self.client.v3Client?.conversations().streamAllGroupMessages(
messageCallback: MessageCallback(client: self.client) { message in
guard !Task.isCancelled else {
continuation.finish()
Expand Down
6 changes: 3 additions & 3 deletions Sources/XMTPiOS/Extensions/Ffi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,13 @@ extension FfiV2SubscribeRequest {

// MARK: Group

extension FfiGroup {
extension FfiConversation {
func fromFFI(client: Client) -> Group {
Group(ffiGroup: self, client: client)
}
}

extension FfiGroupMember {
extension FfiConversationMember {
var fromFFI: Member {
Member(ffiGroupMember: self)
}
Expand Down Expand Up @@ -230,7 +230,7 @@ extension FfiConsentState {
extension EntryType {
var toFFI: FfiConsentEntityType{
switch (self) {
case .group_id: return FfiConsentEntityType.groupId
case .group_id: return FfiConsentEntityType.conversationId
case .inbox_id: return FfiConsentEntityType.inboxId
case .address: return FfiConsentEntityType.address
}
Expand Down
54 changes: 32 additions & 22 deletions Sources/XMTPiOS/Group.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class StreamHolder {
}

public struct Group: Identifiable, Equatable, Hashable {
var ffiGroup: FfiGroup
var ffiGroup: FfiConversation
var client: Client
let streamHolder = StreamHolder()

Expand All @@ -39,7 +39,7 @@ public struct Group: Identifiable, Equatable, Hashable {
Topic.groupMessage(id).description
}

func metadata() throws -> FfiGroupMetadata {
func metadata() throws -> FfiConversationMetadata {
return try ffiGroup.groupMetadata()
}

Expand Down Expand Up @@ -230,12 +230,12 @@ public struct Group: Identifiable, Equatable, Hashable {
}

public func processMessage(envelopeBytes: Data) async throws -> DecodedMessage {
let message = try await ffiGroup.processStreamedGroupMessage(envelopeBytes: envelopeBytes)
let message = try await ffiGroup.processStreamedConversationMessage(envelopeBytes: envelopeBytes)
return try MessageV3(client: client, ffiMessage: message).decode()
}

public func processMessageDecrypted(envelopeBytes: Data) async throws -> DecryptedMessage {
let message = try await ffiGroup.processStreamedGroupMessage(envelopeBytes: envelopeBytes)
let message = try await ffiGroup.processStreamedConversationMessage(envelopeBytes: envelopeBytes)
return try MessageV3(client: client, ffiMessage: message).decrypt()
}

Expand Down Expand Up @@ -373,7 +373,8 @@ public struct Group: Identifiable, Equatable, Hashable {
sentBeforeNs: nil,
sentAfterNs: nil,
limit: nil,
deliveryStatus: nil
deliveryStatus: nil,
direction: nil
)

if let before {
Expand Down Expand Up @@ -402,16 +403,20 @@ public struct Group: Identifiable, Equatable, Hashable {
}()

options.deliveryStatus = status

let direction: FfiDirection? = {
switch direction {
case .ascending:
return FfiDirection.ascending
default:
return FfiDirection.descending
}
}()

let messages = try ffiGroup.findMessages(opts: options).compactMap { ffiMessage in
return MessageV3(client: self.client, ffiMessage: ffiMessage).decodeOrNull()
}
options.direction = direction

switch direction {
case .ascending:
return messages
default:
return messages.reversed()
return try ffiGroup.findMessages(opts: options).compactMap { ffiMessage in
return MessageV3(client: self.client, ffiMessage: ffiMessage).decodeOrNull()
}
}

Expand All @@ -426,7 +431,8 @@ public struct Group: Identifiable, Equatable, Hashable {
sentBeforeNs: nil,
sentAfterNs: nil,
limit: nil,
deliveryStatus: nil
deliveryStatus: nil,
direction: nil
)

if let before {
Expand Down Expand Up @@ -455,16 +461,20 @@ public struct Group: Identifiable, Equatable, Hashable {
}()

options.deliveryStatus = status

let direction: FfiDirection? = {
switch direction {
case .ascending:
return FfiDirection.ascending
default:
return FfiDirection.descending
}
}()

let messages = try ffiGroup.findMessages(opts: options).compactMap { ffiMessage in
options.direction = direction

return try ffiGroup.findMessages(opts: options).compactMap { ffiMessage in
return MessageV3(client: self.client, ffiMessage: ffiMessage).decryptOrNull()
}

switch direction {
case .ascending:
return messages
default:
return messages.reversed()
}
}
}
4 changes: 2 additions & 2 deletions Sources/XMTPiOS/Mls/Member.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public enum PermissionLevel {
}

public struct Member {
var ffiGroupMember: FfiGroupMember
var ffiGroupMember: FfiConversationMember

init(ffiGroupMember: FfiGroupMember) {
init(ffiGroupMember: FfiConversationMember) {
self.ffiGroupMember = ffiGroupMember
}

Expand Down
Loading

0 comments on commit c18f087

Please sign in to comment.