From 7e697037d5f8b2a172ae2b22a91cd921e101ae57 Mon Sep 17 00:00:00 2001 From: Pat Nakajima Date: Wed, 25 Oct 2023 18:05:10 -0700 Subject: [PATCH] Implement Persistent Preferences (#175) * Start on v2 allow state stuff * encrypt * Derive topic identifier instead of using wallet address * generate the latest proto code * update contacts to use proto code * pull the identifier and keys out to the init * remove proto code that breaks the build * bump the pod specs for RN * update the topic * name it preference list and confirm tests still pass * fix the linter issue --------- Co-authored-by: Naomi Plasterer --- Package.resolved | 4 +- Package.swift | 2 +- Sources/XMTP/Contacts.swift | 160 ++++- Sources/XMTP/Conversation.swift | 39 +- Sources/XMTP/Conversations.swift | 2 + Sources/XMTP/Messages/Topic.swift | 5 +- .../Proto/keystore_api/v1/keystore.pb.swift | 653 +++++++++++++++++- .../XMTP/Proto/message_api/v1/authn.pb.swift | 2 +- .../Proto/message_api/v1/message_api.pb.swift | 2 +- .../Proto/message_contents/content.pb.swift | 2 +- .../Proto/message_contents/ecies.pb.swift | 116 ++++ .../message_contents/private_key.pb.swift | 2 +- .../private_preferences.pb.swift | 252 +++++++ .../message_contents/public_key.pb.swift | 2 +- .../message_contents/signed_payload.pb.swift | 97 +++ Tests/XMTPTests/ContactsTests.swift | 28 + Tests/XMTPTests/ConversationTests.swift | 183 ++--- XMTP.podspec | 4 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- 19 files changed, 1453 insertions(+), 106 deletions(-) create mode 100644 Sources/XMTP/Proto/message_contents/ecies.pb.swift create mode 100644 Sources/XMTP/Proto/message_contents/private_preferences.pb.swift create mode 100644 Sources/XMTP/Proto/message_contents/signed_payload.pb.swift diff --git a/Package.resolved b/Package.resolved index e5bfa207..ffdbb7f1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/xmtp/xmtp-rust-swift", "state" : { - "revision" : "4a76e5401fa780c40610e2f0d248f695261d08dd", - "version" : "0.3.1-beta0" + "branch" : "main", + "revision" : "e857176b7e368c51e1dadcbbcce648bb20432f26" } } ], diff --git a/Package.swift b/Package.swift index 8ee196d6..82d8b97a 100644 --- a/Package.swift +++ b/Package.swift @@ -26,7 +26,7 @@ let package = Package( .package(url: "https://github.com/1024jp/GzipSwift", from: "5.2.0"), .package(url: "https://github.com/bufbuild/connect-swift", from: "0.3.0"), .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"), - .package(url: "https://github.com/xmtp/xmtp-rust-swift", from: "0.3.5-beta0"), + .package(url: "https://github.com/xmtp/xmtp-rust-swift", branch: "main"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/Sources/XMTP/Contacts.swift b/Sources/XMTP/Contacts.swift index 1e750628..c3704471 100644 --- a/Sources/XMTP/Contacts.swift +++ b/Sources/XMTP/Contacts.swift @@ -6,6 +6,137 @@ // import Foundation +import XMTPRust + + +public typealias PrivatePreferencesAction = Xmtp_MessageContents_PrivatePreferencesAction + +public enum AllowState: String, Codable { + case allowed, blocked, unknown +} + +struct AllowListEntry: Codable, Hashable { + enum EntryType: String, Codable { + case address + } + + static func address(_ address: String, type: AllowState = .unknown) -> AllowListEntry { + AllowListEntry(value: address, entryType: .address, permissionType: type) + } + + var value: String + var entryType: EntryType + var permissionType: AllowState + + var key: String { + "\(entryType)-\(value)" + } +} + +public enum ContactError: Error { + case invalidIdentifier +} + +class AllowList { + var entries: [String: AllowState] = [:] + var publicKey: Data + var privateKey: Data + var identifier: String? + + var client: Client + + init(client: Client) { + self.client = client + self.privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes + self.publicKey = client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes + // swiftlint:disable no_optional_try + self.identifier = try? XMTPRust.generate_private_preferences_topic_identifier(RustVec(privateKey)).toString() + // swiftlint:enable no_optional_try + } + + func load() async throws -> AllowList { + guard let identifier = identifier else { + throw ContactError.invalidIdentifier + } + + let envelopes = try await client.query(topic: .preferenceList(identifier)) + + let allowList = AllowList(client: client) + + var preferences: [PrivatePreferencesAction] = [] + + for envelope in envelopes.envelopes { + + + let payload = try XMTPRust.ecies_decrypt_k256_sha3_256( + RustVec(publicKey), + RustVec(privateKey), + RustVec(envelope.message) + ) + + preferences.append(try PrivatePreferencesAction(contiguousBytes: Data(payload).bytes)) + } + + preferences.forEach { preference in + preference.allow.walletAddresses.forEach { address in + allowList.allow(address: address) + } + preference.block.walletAddresses.forEach { address in + allowList.block(address: address) + } + } + + return allowList + } + + func publish(entry: AllowListEntry) async throws { + guard let identifier = identifier else { + throw ContactError.invalidIdentifier + } + + var payload = PrivatePreferencesAction() + switch entry.permissionType { + case .allowed: + payload.allow.walletAddresses = [entry.value] + case .blocked: + payload.block.walletAddresses = [entry.value] + case .unknown: + payload.unknownFields + } + + let message = try XMTPRust.ecies_encrypt_k256_sha3_256( + RustVec(publicKey), + RustVec(privateKey), + RustVec(payload.serializedData()) + ) + + let envelope = Envelope( + topic: Topic.preferenceList(identifier), + timestamp: Date(), + message: Data(message) + ) + + try await client.publish(envelopes: [envelope]) + } + + func allow(address: String) -> AllowListEntry { + entries[AllowListEntry.address(address).key] = .allowed + + return .address(address, type: .allowed) + } + + func block(address: String) -> AllowListEntry { + entries[AllowListEntry.address(address).key] = .blocked + + return .address(address, type: .blocked) + } + + func state(address: String) -> AllowState { + let state = entries[AllowListEntry.address(address).key] + + return state ?? .unknown + } +} /// Provides access to contact bundles. public actor Contacts { @@ -17,8 +148,35 @@ public actor Contacts { // Whether or not we have sent invite/intro to this contact var hasIntroduced: [String: Bool] = [:] - init(client: Client) { + var allowList: AllowList + + init(client: Client) { self.client = client + self.allowList = AllowList(client: client) + } + + public func refreshAllowList() async throws { + self.allowList = try await AllowList(client: client).load() + } + + public func isAllowed(_ address: String) -> Bool { + return allowList.state(address: address) == .allowed + } + + public func isBlocked(_ address: String) -> Bool { + return allowList.state(address: address) == .blocked + } + + public func allow(addresses: [String]) async throws { + for address in addresses { + try await AllowList(client: client).publish(entry: allowList.allow(address: address)) + } + } + + public func block(addresses: [String]) async throws { + for address in addresses { + try await AllowList(client: client).publish(entry: allowList.block(address: address)) + } } func markIntroduced(_ peerAddress: String, _ isIntroduced: Bool) { diff --git a/Sources/XMTP/Conversation.swift b/Sources/XMTP/Conversation.swift index 5d922af7..2b0c0c2f 100644 --- a/Sources/XMTP/Conversation.swift +++ b/Sources/XMTP/Conversation.swift @@ -29,6 +29,19 @@ public enum Conversation: Sendable { case v1, v2 } + public func allowState() async -> AllowState { + let client: Client + + switch self { + case .v1(let conversationV1): + client = conversationV1.client + case .v2(let conversationV2): + client = conversationV2.client + } + + return await client.contacts.allowList.state(address: peerAddress) + } + public var version: Version { switch self { case .v1: @@ -82,7 +95,7 @@ public enum Conversation: Sendable { /// See Conversations.importTopicData() public func toTopicData() -> Xmtp_KeystoreApi_V1_TopicMap.TopicData { Xmtp_KeystoreApi_V1_TopicMap.TopicData.with { - $0.createdNs = UInt64(createdAt.timeIntervalSince1970 * 1_000) * 1_000_000 + $0.createdNs = UInt64(createdAt.timeIntervalSince1970 * 1000) * 1_000_000 $0.peerAddress = peerAddress if case let .v2(cv2) = self { $0.invitation = Xmtp_MessageContents_InvitationV1.with { @@ -123,16 +136,16 @@ public enum Conversation: Sendable { } } - // This is a convenience for invoking the underlying `client.publish(prepared.envelopes)` - // If a caller has a `Client` handy, they may opt to do that directly instead. - @discardableResult public func send(prepared: PreparedMessage) async throws -> String { - switch self { - case let .v1(conversationV1): - return try await conversationV1.send(prepared: prepared) - case let .v2(conversationV2): - return try await conversationV2.send(prepared: prepared) - } - } + // This is a convenience for invoking the underlying `client.publish(prepared.envelopes)` + // If a caller has a `Client` handy, they may opt to do that directly instead. + @discardableResult public func send(prepared: PreparedMessage) async throws -> String { + switch self { + case let .v1(conversationV1): + return try await conversationV1.send(prepared: prepared) + case let .v2(conversationV2): + return try await conversationV2.send(prepared: prepared) + } + } @discardableResult public func send(content: T, options: SendOptions? = nil, fallback _: String? = nil) async throws -> String { switch self { @@ -199,10 +212,10 @@ public enum Conversation: Sendable { } /// List messages in the conversation - public func messages(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecodedMessage] { + public func messages(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecodedMessage] { switch self { case let .v1(conversationV1): - return try await conversationV1.messages(limit: limit, before: before, after: after, direction: direction) + return try await conversationV1.messages(limit: limit, before: before, after: after, direction: direction) case let .v2(conversationV2): return try await conversationV2.messages(limit: limit, before: before, after: after, direction: direction) } diff --git a/Sources/XMTP/Conversations.swift b/Sources/XMTP/Conversations.swift index 109e67ca..9b9bd3ea 100644 --- a/Sources/XMTP/Conversations.swift +++ b/Sources/XMTP/Conversations.swift @@ -155,6 +155,8 @@ public actor Conversations { let sealedInvitation = try await sendInvitation(recipient: recipient, invitation: invitation, created: Date()) let conversationV2 = try ConversationV2.create(client: client, invitation: invitation, header: sealedInvitation.v1.header) + try await client.contacts.allow(addresses: [peerAddress]) + let conversation: Conversation = .v2(conversationV2) conversationsByTopic[conversation.topic] = conversation return conversation diff --git a/Sources/XMTP/Messages/Topic.swift b/Sources/XMTP/Messages/Topic.swift index e0b9c0b6..fde788f7 100644 --- a/Sources/XMTP/Messages/Topic.swift +++ b/Sources/XMTP/Messages/Topic.swift @@ -13,7 +13,8 @@ public enum Topic { userIntro(String), userInvite(String), directMessageV1(String, String), - directMessageV2(String) + directMessageV2(String), + preferenceList(String) var description: String { switch self { @@ -30,6 +31,8 @@ public enum Topic { return wrap("dm-\(addresses)") case let .directMessageV2(randomString): return wrap("m-\(randomString)") + case let .preferenceList(identifier): + return wrap("pppp-\(identifier)") } } diff --git a/Sources/XMTP/Proto/keystore_api/v1/keystore.pb.swift b/Sources/XMTP/Proto/keystore_api/v1/keystore.pb.swift index 586462ad..865874ae 100644 --- a/Sources/XMTP/Proto/keystore_api/v1/keystore.pb.swift +++ b/Sources/XMTP/Proto/keystore_api/v1/keystore.pb.swift @@ -58,7 +58,7 @@ public enum Xmtp_KeystoreApi_V1_ErrorCode: SwiftProtobuf.Enum { extension Xmtp_KeystoreApi_V1_ErrorCode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - public static var allCases: [Xmtp_KeystoreApi_V1_ErrorCode] = [ + public static let allCases: [Xmtp_KeystoreApi_V1_ErrorCode] = [ .unspecified, .invalidInput, .noMatchingPrekey, @@ -67,6 +67,51 @@ extension Xmtp_KeystoreApi_V1_ErrorCode: CaseIterable { #endif // swift(>=4.2) +/// JobType is used to specify the type of job the caller would like info on +public enum Xmtp_KeystoreApi_V1_JobType: SwiftProtobuf.Enum { + public typealias RawValue = Int + case unspecified // = 0 + case refreshV1 // = 1 + case refreshV2 // = 2 + case UNRECOGNIZED(Int) + + public init() { + self = .unspecified + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .unspecified + case 1: self = .refreshV1 + case 2: self = .refreshV2 + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .unspecified: return 0 + case .refreshV1: return 1 + case .refreshV2: return 2 + case .UNRECOGNIZED(let i): return i + } + } + +} + +#if swift(>=4.2) + +extension Xmtp_KeystoreApi_V1_JobType: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Xmtp_KeystoreApi_V1_JobType] = [ + .unspecified, + .refreshV1, + .refreshV2, + ] +} + +#endif // swift(>=4.2) + /// Wrapper class for errors from the Keystore API public struct Xmtp_KeystoreApi_V1_KeystoreError { // SwiftProtobuf.Message conformance is added in an extension below. See the @@ -604,6 +649,178 @@ public struct Xmtp_KeystoreApi_V1_CreateAuthTokenRequest { fileprivate var _timestampNs: UInt64? = nil } +/// SaveV1ConversationsRequest is used to save a batch of conversations to the +/// built in persistence +public struct Xmtp_KeystoreApi_V1_SaveV1ConversationsRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var conversations: [Xmtp_MessageContents_ConversationReference] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Placeholder response type for SaveV1Conversations +public struct Xmtp_KeystoreApi_V1_SaveV1ConversationsResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Response for GetV2Conversations +public struct Xmtp_KeystoreApi_V1_GetConversationsResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var conversations: [Xmtp_MessageContents_ConversationReference] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Used to check if the Keystore implementation has been setup for the given +/// wallet address Only used for MM Snap Keystore currently +public struct Xmtp_KeystoreApi_V1_GetKeystoreStatusRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var walletAddress: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Response to GetKeystoreStatusRequest +public struct Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var status: Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse.KeystoreStatus = .unspecified + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// Status of the Keystore for the specified wallet address + public enum KeystoreStatus: SwiftProtobuf.Enum { + public typealias RawValue = Int + case unspecified // = 0 + case uninitialized // = 1 + case initialized // = 2 + case UNRECOGNIZED(Int) + + public init() { + self = .unspecified + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .unspecified + case 1: self = .uninitialized + case 2: self = .initialized + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .unspecified: return 0 + case .uninitialized: return 1 + case .initialized: return 2 + case .UNRECOGNIZED(let i): return i + } + } + + } + + public init() {} +} + +#if swift(>=4.2) + +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse.KeystoreStatus: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse.KeystoreStatus] = [ + .unspecified, + .uninitialized, + .initialized, + ] +} + +#endif // swift(>=4.2) + +/// Used to initialize the Keystore with a private key bundle retrieved from the +/// client +public struct Xmtp_KeystoreApi_V1_InitKeystoreRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var bundle: Xmtp_KeystoreApi_V1_InitKeystoreRequest.OneOf_Bundle? = nil + + public var v1: Xmtp_MessageContents_PrivateKeyBundleV1 { + get { + if case .v1(let v)? = bundle {return v} + return Xmtp_MessageContents_PrivateKeyBundleV1() + } + set {bundle = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Bundle: Equatable { + case v1(Xmtp_MessageContents_PrivateKeyBundleV1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_KeystoreApi_V1_InitKeystoreRequest.OneOf_Bundle, rhs: Xmtp_KeystoreApi_V1_InitKeystoreRequest.OneOf_Bundle) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + public init() {} +} + +/// Response to the request to initialize the Keystore +public struct Xmtp_KeystoreApi_V1_InitKeystoreResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var error: Xmtp_KeystoreApi_V1_KeystoreError { + get {return _error ?? Xmtp_KeystoreApi_V1_KeystoreError()} + set {_error = newValue} + } + /// Returns true if `error` has been explicitly set. + public var hasError: Bool {return self._error != nil} + /// Clears the value of `error`. Subsequent reads from it will return its default value. + public mutating func clearError() {self._error = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _error: Xmtp_KeystoreApi_V1_KeystoreError? = nil +} + /// SignDigestRequest is used to sign a digest with either the identity key /// or a prekey public struct Xmtp_KeystoreApi_V1_SignDigestRequest { @@ -660,6 +877,58 @@ public struct Xmtp_KeystoreApi_V1_SignDigestRequest { public init() {} } +/// GetRefreshJobRequest is used to get the last run time of a refresh job +public struct Xmtp_KeystoreApi_V1_GetRefreshJobRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var jobType: Xmtp_KeystoreApi_V1_JobType = .unspecified + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// GetRefreshJobResponse is used to return the last run time of a refresh job +public struct Xmtp_KeystoreApi_V1_GetRefreshJobResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var lastRunNs: Int64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// SetRefreshJobRequest is used to set the last run time of a refresh job +public struct Xmtp_KeystoreApi_V1_SetRefeshJobRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var jobType: Xmtp_KeystoreApi_V1_JobType = .unspecified + + public var lastRunNs: Int64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// SetRefreshJobResponse is an empty response type +public struct Xmtp_KeystoreApi_V1_SetRefreshJobResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + /// A mapping of topics to their decrypted invitations public struct Xmtp_KeystoreApi_V1_TopicMap { // SwiftProtobuf.Message conformance is added in an extension below. See the @@ -701,6 +970,7 @@ public struct Xmtp_KeystoreApi_V1_TopicMap { #if swift(>=5.5) && canImport(_Concurrency) extension Xmtp_KeystoreApi_V1_ErrorCode: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_JobType: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_KeystoreError: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_DecryptV1Request: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_DecryptV1Request.Request: @unchecked Sendable {} @@ -727,8 +997,21 @@ extension Xmtp_KeystoreApi_V1_SaveInvitesResponse.Response: @unchecked Sendable extension Xmtp_KeystoreApi_V1_SaveInvitesResponse.Response.OneOf_Response: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_SaveInvitesResponse.Response.Success: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_CreateAuthTokenRequest: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_SaveV1ConversationsRequest: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_SaveV1ConversationsResponse: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_GetConversationsResponse: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusRequest: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse.KeystoreStatus: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_InitKeystoreRequest: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_InitKeystoreRequest.OneOf_Bundle: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_InitKeystoreResponse: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_SignDigestRequest: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_SignDigestRequest.OneOf_Signer: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_GetRefreshJobRequest: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_GetRefreshJobResponse: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_SetRefeshJobRequest: @unchecked Sendable {} +extension Xmtp_KeystoreApi_V1_SetRefreshJobResponse: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_TopicMap: @unchecked Sendable {} extension Xmtp_KeystoreApi_V1_TopicMap.TopicData: @unchecked Sendable {} #endif // swift(>=5.5) && canImport(_Concurrency) @@ -745,6 +1028,14 @@ extension Xmtp_KeystoreApi_V1_ErrorCode: SwiftProtobuf._ProtoNameProviding { ] } +extension Xmtp_KeystoreApi_V1_JobType: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "JOB_TYPE_UNSPECIFIED"), + 1: .same(proto: "JOB_TYPE_REFRESH_V1"), + 2: .same(proto: "JOB_TYPE_REFRESH_V2"), + ] +} + extension Xmtp_KeystoreApi_V1_KeystoreError: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeystoreError" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1717,6 +2008,245 @@ extension Xmtp_KeystoreApi_V1_CreateAuthTokenRequest: SwiftProtobuf.Message, Swi } } +extension Xmtp_KeystoreApi_V1_SaveV1ConversationsRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SaveV1ConversationsRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "conversations"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.conversations) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.conversations.isEmpty { + try visitor.visitRepeatedMessageField(value: self.conversations, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_SaveV1ConversationsRequest, rhs: Xmtp_KeystoreApi_V1_SaveV1ConversationsRequest) -> Bool { + if lhs.conversations != rhs.conversations {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_SaveV1ConversationsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SaveV1ConversationsResponse" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { + } + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_SaveV1ConversationsResponse, rhs: Xmtp_KeystoreApi_V1_SaveV1ConversationsResponse) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_GetConversationsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetConversationsResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "conversations"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.conversations) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.conversations.isEmpty { + try visitor.visitRepeatedMessageField(value: self.conversations, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_GetConversationsResponse, rhs: Xmtp_KeystoreApi_V1_GetConversationsResponse) -> Bool { + if lhs.conversations != rhs.conversations {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetKeystoreStatusRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "wallet_address"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.walletAddress) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.walletAddress.isEmpty { + try visitor.visitSingularStringField(value: self.walletAddress, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_GetKeystoreStatusRequest, rhs: Xmtp_KeystoreApi_V1_GetKeystoreStatusRequest) -> Bool { + if lhs.walletAddress != rhs.walletAddress {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetKeystoreStatusResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "status"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.status) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.status != .unspecified { + try visitor.visitSingularEnumField(value: self.status, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse, rhs: Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse) -> Bool { + if lhs.status != rhs.status {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_GetKeystoreStatusResponse.KeystoreStatus: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "KEYSTORE_STATUS_UNSPECIFIED"), + 1: .same(proto: "KEYSTORE_STATUS_UNINITIALIZED"), + 2: .same(proto: "KEYSTORE_STATUS_INITIALIZED"), + ] +} + +extension Xmtp_KeystoreApi_V1_InitKeystoreRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".InitKeystoreRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_MessageContents_PrivateKeyBundleV1? + var hadOneofValue = false + if let current = self.bundle { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.bundle = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.bundle { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_InitKeystoreRequest, rhs: Xmtp_KeystoreApi_V1_InitKeystoreRequest) -> Bool { + if lhs.bundle != rhs.bundle {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_InitKeystoreResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".InitKeystoreResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "error"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._error) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._error { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_InitKeystoreResponse, rhs: Xmtp_KeystoreApi_V1_InitKeystoreResponse) -> Bool { + if lhs._error != rhs._error {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Xmtp_KeystoreApi_V1_SignDigestRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".SignDigestRequest" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1783,6 +2313,127 @@ extension Xmtp_KeystoreApi_V1_SignDigestRequest: SwiftProtobuf.Message, SwiftPro } } +extension Xmtp_KeystoreApi_V1_GetRefreshJobRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetRefreshJobRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "job_type"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.jobType) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.jobType != .unspecified { + try visitor.visitSingularEnumField(value: self.jobType, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_GetRefreshJobRequest, rhs: Xmtp_KeystoreApi_V1_GetRefreshJobRequest) -> Bool { + if lhs.jobType != rhs.jobType {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_GetRefreshJobResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetRefreshJobResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "last_run_ns"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularInt64Field(value: &self.lastRunNs) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.lastRunNs != 0 { + try visitor.visitSingularInt64Field(value: self.lastRunNs, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_GetRefreshJobResponse, rhs: Xmtp_KeystoreApi_V1_GetRefreshJobResponse) -> Bool { + if lhs.lastRunNs != rhs.lastRunNs {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_SetRefeshJobRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SetRefeshJobRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "job_type"), + 2: .standard(proto: "last_run_ns"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.jobType) }() + case 2: try { try decoder.decodeSingularInt64Field(value: &self.lastRunNs) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.jobType != .unspecified { + try visitor.visitSingularEnumField(value: self.jobType, fieldNumber: 1) + } + if self.lastRunNs != 0 { + try visitor.visitSingularInt64Field(value: self.lastRunNs, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_SetRefeshJobRequest, rhs: Xmtp_KeystoreApi_V1_SetRefeshJobRequest) -> Bool { + if lhs.jobType != rhs.jobType {return false} + if lhs.lastRunNs != rhs.lastRunNs {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_KeystoreApi_V1_SetRefreshJobResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SetRefreshJobResponse" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { + } + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_KeystoreApi_V1_SetRefreshJobResponse, rhs: Xmtp_KeystoreApi_V1_SetRefreshJobResponse) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Xmtp_KeystoreApi_V1_TopicMap: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".TopicMap" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ diff --git a/Sources/XMTP/Proto/message_api/v1/authn.pb.swift b/Sources/XMTP/Proto/message_api/v1/authn.pb.swift index 22361f7c..238944c2 100644 --- a/Sources/XMTP/Proto/message_api/v1/authn.pb.swift +++ b/Sources/XMTP/Proto/message_api/v1/authn.pb.swift @@ -73,7 +73,7 @@ public struct Xmtp_MessageApi_V1_AuthData { /// address of the wallet public var walletAddr: String = String() - /// time when the token was generated/signed + /// time when the token was generated/signed public var createdNs: UInt64 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() diff --git a/Sources/XMTP/Proto/message_api/v1/message_api.pb.swift b/Sources/XMTP/Proto/message_api/v1/message_api.pb.swift index 03ae0a3a..c3ac86e3 100644 --- a/Sources/XMTP/Proto/message_api/v1/message_api.pb.swift +++ b/Sources/XMTP/Proto/message_api/v1/message_api.pb.swift @@ -58,7 +58,7 @@ public enum Xmtp_MessageApi_V1_SortDirection: SwiftProtobuf.Enum { extension Xmtp_MessageApi_V1_SortDirection: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - public static var allCases: [Xmtp_MessageApi_V1_SortDirection] = [ + public static let allCases: [Xmtp_MessageApi_V1_SortDirection] = [ .unspecified, .ascending, .descending, diff --git a/Sources/XMTP/Proto/message_contents/content.pb.swift b/Sources/XMTP/Proto/message_contents/content.pb.swift index cf73da0d..6f588dd0 100644 --- a/Sources/XMTP/Proto/message_contents/content.pb.swift +++ b/Sources/XMTP/Proto/message_contents/content.pb.swift @@ -56,7 +56,7 @@ public enum Xmtp_MessageContents_Compression: SwiftProtobuf.Enum { extension Xmtp_MessageContents_Compression: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - public static var allCases: [Xmtp_MessageContents_Compression] = [ + public static let allCases: [Xmtp_MessageContents_Compression] = [ .deflate, .gzip, ] diff --git a/Sources/XMTP/Proto/message_contents/ecies.pb.swift b/Sources/XMTP/Proto/message_contents/ecies.pb.swift new file mode 100644 index 00000000..21f3b6f1 --- /dev/null +++ b/Sources/XMTP/Proto/message_contents/ecies.pb.swift @@ -0,0 +1,116 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: message_contents/ecies.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// ECIES is a wrapper for ECIES payloads + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// EciesMessage is a wrapper for ECIES encrypted payloads +public struct Xmtp_MessageContents_EciesMessage { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_MessageContents_EciesMessage.OneOf_Version? = nil + + /// Expected to be an ECIES encrypted SignedPayload + public var v1: Data { + get { + if case .v1(let v)? = version {return v} + return Data() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + /// Expected to be an ECIES encrypted SignedPayload + case v1(Data) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_MessageContents_EciesMessage.OneOf_Version, rhs: Xmtp_MessageContents_EciesMessage.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_MessageContents_EciesMessage: @unchecked Sendable {} +extension Xmtp_MessageContents_EciesMessage.OneOf_Version: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.message_contents" + +extension Xmtp_MessageContents_EciesMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EciesMessage" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Data? + try decoder.decodeSingularBytesField(value: &v) + if let v = v { + if self.version != nil {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularBytesField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_MessageContents_EciesMessage, rhs: Xmtp_MessageContents_EciesMessage) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTP/Proto/message_contents/private_key.pb.swift b/Sources/XMTP/Proto/message_contents/private_key.pb.swift index 4e916b5e..2015222a 100644 --- a/Sources/XMTP/Proto/message_contents/private_key.pb.swift +++ b/Sources/XMTP/Proto/message_contents/private_key.pb.swift @@ -279,7 +279,7 @@ public struct Xmtp_MessageContents_EncryptedPrivateKeyBundleV1 { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - /// randomly generated pre-key + /// randomly generated pre-key public var walletPreKey: Data = Data() /// MUST contain encrypted PrivateKeyBundle diff --git a/Sources/XMTP/Proto/message_contents/private_preferences.pb.swift b/Sources/XMTP/Proto/message_contents/private_preferences.pb.swift new file mode 100644 index 00000000..0e969de9 --- /dev/null +++ b/Sources/XMTP/Proto/message_contents/private_preferences.pb.swift @@ -0,0 +1,252 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: message_contents/private_preferences.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Private Key Storage +/// +/// Following definitions are not used in the protocol, instead +/// they provide a way for encoding private keys for storage. + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// PrivatePreferencesAction is a message used to update the client's +/// preference store. The only current actions are allow and block. +/// Other actions may be added later +public struct Xmtp_MessageContents_PrivatePreferencesAction { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var messageType: Xmtp_MessageContents_PrivatePreferencesAction.OneOf_MessageType? = nil + + public var allow: Xmtp_MessageContents_PrivatePreferencesAction.Allow { + get { + if case .allow(let v)? = messageType {return v} + return Xmtp_MessageContents_PrivatePreferencesAction.Allow() + } + set {messageType = .allow(newValue)} + } + + public var block: Xmtp_MessageContents_PrivatePreferencesAction.Block { + get { + if case .block(let v)? = messageType {return v} + return Xmtp_MessageContents_PrivatePreferencesAction.Block() + } + set {messageType = .block(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_MessageType: Equatable { + case allow(Xmtp_MessageContents_PrivatePreferencesAction.Allow) + case block(Xmtp_MessageContents_PrivatePreferencesAction.Block) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_MessageContents_PrivatePreferencesAction.OneOf_MessageType, rhs: Xmtp_MessageContents_PrivatePreferencesAction.OneOf_MessageType) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.allow, .allow): return { + guard case .allow(let l) = lhs, case .allow(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.block, .block): return { + guard case .block(let l) = lhs, case .block(let r) = rhs else { preconditionFailure() } + return l == r + }() + default: return false + } + } + #endif + } + + /// Add the given wallet addresses to the allow list + public struct Allow { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var walletAddresses: [String] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + /// Add the given wallet addresses to the block list + public struct Block { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var walletAddresses: [String] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_MessageContents_PrivatePreferencesAction: @unchecked Sendable {} +extension Xmtp_MessageContents_PrivatePreferencesAction.OneOf_MessageType: @unchecked Sendable {} +extension Xmtp_MessageContents_PrivatePreferencesAction.Allow: @unchecked Sendable {} +extension Xmtp_MessageContents_PrivatePreferencesAction.Block: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.message_contents" + +extension Xmtp_MessageContents_PrivatePreferencesAction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".PrivatePreferencesAction" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "allow"), + 2: .same(proto: "block"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_MessageContents_PrivatePreferencesAction.Allow? + var hadOneofValue = false + if let current = self.messageType { + hadOneofValue = true + if case .allow(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.messageType = .allow(v) + } + }() + case 2: try { + var v: Xmtp_MessageContents_PrivatePreferencesAction.Block? + var hadOneofValue = false + if let current = self.messageType { + hadOneofValue = true + if case .block(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.messageType = .block(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + switch self.messageType { + case .allow?: try { + guard case .allow(let v)? = self.messageType else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + }() + case .block?: try { + guard case .block(let v)? = self.messageType else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + }() + case nil: break + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_MessageContents_PrivatePreferencesAction, rhs: Xmtp_MessageContents_PrivatePreferencesAction) -> Bool { + if lhs.messageType != rhs.messageType {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_MessageContents_PrivatePreferencesAction.Allow: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_MessageContents_PrivatePreferencesAction.protoMessageName + ".Allow" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "wallet_addresses"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedStringField(value: &self.walletAddresses) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.walletAddresses.isEmpty { + try visitor.visitRepeatedStringField(value: self.walletAddresses, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_MessageContents_PrivatePreferencesAction.Allow, rhs: Xmtp_MessageContents_PrivatePreferencesAction.Allow) -> Bool { + if lhs.walletAddresses != rhs.walletAddresses {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_MessageContents_PrivatePreferencesAction.Block: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_MessageContents_PrivatePreferencesAction.protoMessageName + ".Block" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "wallet_addresses"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedStringField(value: &self.walletAddresses) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.walletAddresses.isEmpty { + try visitor.visitRepeatedStringField(value: self.walletAddresses, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_MessageContents_PrivatePreferencesAction.Block, rhs: Xmtp_MessageContents_PrivatePreferencesAction.Block) -> Bool { + if lhs.walletAddresses != rhs.walletAddresses {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTP/Proto/message_contents/public_key.pb.swift b/Sources/XMTP/Proto/message_contents/public_key.pb.swift index a1accdaf..534e9075 100644 --- a/Sources/XMTP/Proto/message_contents/public_key.pb.swift +++ b/Sources/XMTP/Proto/message_contents/public_key.pb.swift @@ -79,7 +79,7 @@ public struct Xmtp_MessageContents_UnsignedPublicKey { public init() {} } -/// SignedPublicKey +/// SignedPublicKey public struct Xmtp_MessageContents_SignedPublicKey { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for diff --git a/Sources/XMTP/Proto/message_contents/signed_payload.pb.swift b/Sources/XMTP/Proto/message_contents/signed_payload.pb.swift new file mode 100644 index 00000000..3d8550cf --- /dev/null +++ b/Sources/XMTP/Proto/message_contents/signed_payload.pb.swift @@ -0,0 +1,97 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: message_contents/signed_payload.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Signature is a generic structure for signed byte arrays + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// SignedPayload is a wrapper for a signature and a payload +public struct Xmtp_MessageContents_SignedPayload { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var payload: Data = Data() + + public var signature: Xmtp_MessageContents_Signature { + get {return _signature ?? Xmtp_MessageContents_Signature()} + set {_signature = newValue} + } + /// Returns true if `signature` has been explicitly set. + public var hasSignature: Bool {return self._signature != nil} + /// Clears the value of `signature`. Subsequent reads from it will return its default value. + public mutating func clearSignature() {self._signature = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _signature: Xmtp_MessageContents_Signature? = nil +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_MessageContents_SignedPayload: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.message_contents" + +extension Xmtp_MessageContents_SignedPayload: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SignedPayload" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "payload"), + 2: .same(proto: "signature"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.payload) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._signature) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.payload.isEmpty { + try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 1) + } + try { if let v = self._signature { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_MessageContents_SignedPayload, rhs: Xmtp_MessageContents_SignedPayload) -> Bool { + if lhs.payload != rhs.payload {return false} + if lhs._signature != rhs._signature {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Tests/XMTPTests/ContactsTests.swift b/Tests/XMTPTests/ContactsTests.swift index 072bb415..d17423c8 100644 --- a/Tests/XMTPTests/ContactsTests.swift +++ b/Tests/XMTPTests/ContactsTests.swift @@ -53,4 +53,32 @@ class ContactsTests: XCTestCase { let hasContact = await fixtures.aliceClient.contacts.has(fixtures.bob.walletAddress) XCTAssert(hasContact) } + + func testAllowAddress() async throws { + let fixtures = await fixtures() + + let contacts = fixtures.bobClient.contacts + var result = await contacts.isAllowed(fixtures.alice.address) + + XCTAssertFalse(result) + + try await contacts.allow(addresses: [fixtures.alice.address]) + + result = await contacts.isAllowed(fixtures.alice.address) + XCTAssertTrue(result) + } + + func testBlockAddress() async throws { + let fixtures = await fixtures() + + let contacts = fixtures.bobClient.contacts + var result = await contacts.isAllowed(fixtures.alice.address) + + XCTAssertFalse(result) + + try await contacts.block(addresses: [fixtures.alice.address]) + + result = await contacts.isBlocked(fixtures.alice.address) + XCTAssertTrue(result) + } } diff --git a/Tests/XMTPTests/ConversationTests.swift b/Tests/XMTPTests/ConversationTests.swift index b6ede00a..6e56e966 100644 --- a/Tests/XMTPTests/ConversationTests.swift +++ b/Tests/XMTPTests/ConversationTests.swift @@ -39,7 +39,7 @@ class ConversationTests: XCTestCase { let preparedMessage = try await conversation.prepareMessage(content: "hi") let messageID = preparedMessage.messageID - try await conversation.send(prepared: preparedMessage) + try await conversation.send(prepared: preparedMessage) let messages = try await conversation.messages() let message = messages[0] @@ -48,27 +48,27 @@ class ConversationTests: XCTestCase { XCTAssertEqual(message.id, messageID) } - func testCanSendPreparedMessagesWithoutAConversation() async throws { - let conversation = try await aliceClient.conversations.newConversation(with: bob.address) - let preparedMessage = try await conversation.prepareMessage(content: "hi") - let messageID = preparedMessage.messageID + func testCanSendPreparedMessagesWithoutAConversation() async throws { + let conversation = try await aliceClient.conversations.newConversation(with: bob.address) + let preparedMessage = try await conversation.prepareMessage(content: "hi") + let messageID = preparedMessage.messageID - // This does not need the `conversation` to `.publish` the message. - // This simulates a background task publishes all pending messages upon connection. - try await aliceClient.publish(envelopes: preparedMessage.envelopes) + // This does not need the `conversation` to `.publish` the message. + // This simulates a background task publishes all pending messages upon connection. + try await aliceClient.publish(envelopes: preparedMessage.envelopes) - let messages = try await conversation.messages() - let message = messages[0] + let messages = try await conversation.messages() + let message = messages[0] - XCTAssertEqual("hi", message.body) - XCTAssertEqual(message.id, messageID) - } + XCTAssertEqual("hi", message.body) + XCTAssertEqual(message.id, messageID) + } func testV2RejectsSpoofedContactBundles() async throws { let topic = "/xmtp/0/m-Gdb7oj5nNdfZ3MJFLAcS4WTABgr6al1hePy6JV1-QUE/proto" guard let envelopeMessage = Data(base64String: "Er0ECkcIwNruhKLgkKUXEjsveG10cC8wL20tR2RiN29qNW5OZGZaM01KRkxBY1M0V1RBQmdyNmFsMWhlUHk2SlYxLVFVRS9wcm90bxLxAwruAwognstLoG6LWgiBRsWuBOt+tYNJz+CqCj9zq6hYymLoak8SDFsVSy+cVAII0/r3sxq7A/GCOrVtKH6J+4ggfUuI5lDkFPJ8G5DHlysCfRyFMcQDIG/2SFUqSILAlpTNbeTC9eSI2hUjcnlpH9+ncFcBu8StGfmilVGfiADru2fGdThiQ+VYturqLIJQXCHO2DkvbbUOg9xI66E4Hj41R9vE8yRGeZ/eRGRLRm06HftwSQgzAYf2AukbvjNx/k+xCMqti49Qtv9AjzxVnwttLiA/9O+GDcOsiB1RQzbZZzaDjQ/nLDTF6K4vKI4rS9QwzTJqnoCdp0SbMZFf+KVZpq3VWnMGkMxLW5Fr6gMvKny1e1LAtUJSIclI/1xPXu5nsKd4IyzGb2ZQFXFQ/BVL9Z4CeOZTsjZLGTOGS75xzzGHDtKohcl79+0lgIhAuSWSLDa2+o2OYT0fAjChp+qqxXcisAyrD5FB6c9spXKfoDZsqMV/bnCg3+udIuNtk7zBk7jdTDMkofEtE3hyIm8d3ycmxKYOakDPqeo+Nk1hQ0ogxI8Z7cEoS2ovi9+rGBMwREzltUkTVR3BKvgV2EOADxxTWo7y8WRwWxQ+O6mYPACsiFNqjX5Nvah5lRjihphQldJfyVOG8Rgf4UwkFxmI"), - let keyMaterial = Data(base64String: "R0BBM5OPftNEuavH/991IKyJ1UqsgdEG4SrdxlIG2ZY=") + let keyMaterial = Data(base64String: "R0BBM5OPftNEuavH/991IKyJ1UqsgdEG4SrdxlIG2ZY=") else { XCTFail("did not have correct setup data") return @@ -88,7 +88,7 @@ class ConversationTests: XCTestCase { } func testDoesNotAllowConversationWithSelf() async throws { - try TestConfig.skipIfNotRunningLocalNodeTests() + try TestConfig.skipIfNotRunningLocalNodeTests() let expectation = expectation(description: "convo with self throws") let client = aliceClient! @@ -221,12 +221,12 @@ class ConversationTests: XCTestCase { let encodedContent = try encoder.encode(content: "hi alice", client: aliceClient) // Stream a message - fakeApiClient.send( + try fakeApiClient.send( envelope: Envelope( topic: conversation.topic, timestamp: Date(), - message: try Message( - v2: try await MessageV2.encode( + message: Message( + v2: await MessageV2.encode( client: bobClient, content: encodedContent, topic: conversation.topic, @@ -292,7 +292,7 @@ class ConversationTests: XCTestCase { ) try await aliceClient.publish(envelopes: [ - Envelope(topic: aliceConversation.topic, timestamp: Date(), message: try Message(v2: tamperedMessage).serializedData()), + Envelope(topic: aliceConversation.topic, timestamp: Date(), message: Message(v2: tamperedMessage).serializedData()), ]) guard case let .v2(bobConversation) = try await bobClient.conversations.newConversation(with: alice.address, context: InvitationV1.Context(conversationID: "hi")) else { @@ -343,7 +343,7 @@ class ConversationTests: XCTestCase { let messages = try await aliceConversation.messages(limit: 1) XCTAssertEqual(1, messages.count) XCTAssertEqual("hey alice 3", messages[0].body) - XCTAssertEqual(aliceConversation.topic.description, messages[0].topic) + XCTAssertEqual(aliceConversation.topic.description, messages[0].topic) let messages2 = try await aliceConversation.messages(limit: 1, before: messages[0].sent) XCTAssertEqual(1, messages2.count) @@ -385,7 +385,7 @@ class ConversationTests: XCTestCase { let messages = try await aliceConversation.messages(limit: 1) XCTAssertEqual(1, messages.count) XCTAssertEqual("hey alice 3", messages[0].body) - XCTAssertEqual(aliceConversation.topic, messages[0].topic) + XCTAssertEqual(aliceConversation.topic, messages[0].topic) let messages2 = try await aliceConversation.messages(limit: 1, before: messages[0].sent) XCTAssertEqual(1, messages2.count) @@ -393,7 +393,6 @@ class ConversationTests: XCTestCase { } func testCanRetrieveAllMessages() async throws { - guard case let .v2(bobConversation) = try await bobClient.conversations.newConversation(with: alice.address, context: InvitationV1.Context(conversationID: "hi")) else { XCTFail("did not get a v2 conversation for bob") return @@ -404,7 +403,7 @@ class ConversationTests: XCTestCase { return } - for i in 0..<110 { + for i in 0 ..< 110 { do { let content = "hey alice \(i)" let sentAt = Date().addingTimeInterval(-1000) @@ -423,61 +422,60 @@ class ConversationTests: XCTestCase { let messages = try await aliceConversation.messages() XCTAssertEqual(110, messages.count) } - - func testCanRetrieveBatchMessages() async throws { - - guard case let .v2(bobConversation) = try await aliceClient.conversations.newConversation(with: bob.address, context: InvitationV1.Context(conversationID: "hi")) else { - XCTFail("did not get a v2 conversation for bob") - return - } - - for i in 0..<3 { - do { - let content = "hey alice \(i)" - let sentAt = Date().addingTimeInterval(-1000) - try await bobConversation.send(content: content, sentAt: sentAt) - } catch { - print("Error sending message:", error) - } - } - - let messages = try await aliceClient.conversations.listBatchMessages( - topics: [bobConversation.topic : Pagination(limit:3)] - ) - XCTAssertEqual(3, messages.count) - XCTAssertEqual(bobConversation.topic, messages[0].topic) - XCTAssertEqual(bobConversation.topic, messages[1].topic) - XCTAssertEqual(bobConversation.topic, messages[2].topic) - } - - func testProperlyDiscardBadBatchMessages() async throws { - - guard case let .v2(bobConversation) = try await aliceClient.conversations - .newConversation(with: bob.address) else { - XCTFail("did not get a v2 conversation for bob") - return - } - - try await bobConversation.send(content: "Hello") - - // Now we send some garbage and expect it to be properly ignored. - try await bobClient.apiClient.publish(envelopes: [ - Envelope( - topic: bobConversation.topic, - timestamp: Date(), - message: Data([1, 2, 3]) // garbage, malformed message - ) - ]) - - try await bobConversation.send(content: "Goodbye") - - let messages = try await aliceClient.conversations.listBatchMessages( - topics: [bobConversation.topic : nil] - ) - XCTAssertEqual(2, messages.count) - XCTAssertEqual("Goodbye", try messages[0].content()) - XCTAssertEqual("Hello", try messages[1].content()) - } + + func testCanRetrieveBatchMessages() async throws { + guard case let .v2(bobConversation) = try await aliceClient.conversations.newConversation(with: bob.address, context: InvitationV1.Context(conversationID: "hi")) else { + XCTFail("did not get a v2 conversation for bob") + return + } + + for i in 0 ..< 3 { + do { + let content = "hey alice \(i)" + let sentAt = Date().addingTimeInterval(-1000) + try await bobConversation.send(content: content, sentAt: sentAt) + } catch { + print("Error sending message:", error) + } + } + + let messages = try await aliceClient.conversations.listBatchMessages( + topics: [bobConversation.topic: Pagination(limit: 3)] + ) + XCTAssertEqual(3, messages.count) + XCTAssertEqual(bobConversation.topic, messages[0].topic) + XCTAssertEqual(bobConversation.topic, messages[1].topic) + XCTAssertEqual(bobConversation.topic, messages[2].topic) + } + + func testProperlyDiscardBadBatchMessages() async throws { + guard case let .v2(bobConversation) = try await aliceClient.conversations + .newConversation(with: bob.address) + else { + XCTFail("did not get a v2 conversation for bob") + return + } + + try await bobConversation.send(content: "Hello") + + // Now we send some garbage and expect it to be properly ignored. + try await bobClient.apiClient.publish(envelopes: [ + Envelope( + topic: bobConversation.topic, + timestamp: Date(), + message: Data([1, 2, 3]) // garbage, malformed message + ), + ]) + + try await bobConversation.send(content: "Goodbye") + + let messages = try await aliceClient.conversations.listBatchMessages( + topics: [bobConversation.topic: nil] + ) + XCTAssertEqual(2, messages.count) + XCTAssertEqual("Goodbye", try messages[0].content()) + XCTAssertEqual("Hello", try messages[1].content()) + } func testImportV1ConversationFromJS() async throws { let jsExportJSONData = Data(""" @@ -607,4 +605,33 @@ class ConversationTests: XCTestCase { XCTAssertEqual(Array(repeating: "A", count: 1000).joined(), messages[0].body) XCTAssertEqual(bob.address, messages[0].senderAddress) } + + func testCanHaveAllowState() async throws { + let bobConversation = try await bobClient.conversations.newConversation(with: alice.address, context: InvitationV1.Context(conversationID: "hi")) + let isAllowed = (await bobConversation.allowState()) == .allowed + + // Conversations you start should start as allowed + XCTAssertTrue(isAllowed) + + let aliceConversation = (try await aliceClient.conversations.list())[0] + let isUnknown = (await aliceConversation.allowState()) == .unknown + + // Conversations started with you should start as unknown + XCTAssertTrue(isUnknown) + + try await aliceClient.contacts.allow(addresses: [bob.address]) + + let isBobAllowed = (await aliceConversation.allowState()) == .allowed + XCTAssertTrue(isBobAllowed) + + let aliceClient2 = try await Client.create(account: alice, apiClient: fakeApiClient) + let aliceConversation2 = (try await aliceClient2.conversations.list())[0] + + try await aliceClient2.contacts.refreshAllowList() + + // Allow state should sync across clients + let isBobAllowed2 = (await aliceConversation2.allowState()) == .allowed + + XCTAssertTrue(isBobAllowed2) + } } diff --git a/XMTP.podspec b/XMTP.podspec index 94de8558..8be822db 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "XMTP" - spec.version = "0.6.0-alpha0" + spec.version = "0.6.1-alpha0" spec.summary = "XMTP SDK Cocoapod" # This description is used to generate tags and improve search results. @@ -44,7 +44,7 @@ Pod::Spec.new do |spec| spec.dependency "web3.swift" spec.dependency "GzipSwift" spec.dependency "Connect-Swift" - spec.dependency 'XMTPRust', '= 0.3.5-beta0' + spec.dependency 'XMTPRust', '= 0.3.6-beta0' spec.xcconfig = {'VALID_ARCHS' => 'arm64' } end diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 65e196f6..38a4515b 100644 --- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/xmtp/xmtp-rust-swift", "state" : { - "revision" : "d1aaac47fc7c57645a6fe9e06972b957b3efa33c", - "version" : "0.3.5-beta0" + "branch" : "main", + "revision" : "e857176b7e368c51e1dadcbbcce648bb20432f26" } } ],