Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consent V3 #402

Merged
merged 16 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.8-beta5"),
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "0.5.8-beta6"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
72 changes: 50 additions & 22 deletions Sources/XMTPiOS/Contacts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ public typealias PrivatePreferencesAction = Xmtp_MessageContents_PrivatePreferen
public enum ConsentState: String, Codable {
case allowed, denied, unknown
}
public enum EntryType: String, Codable {
case address, group_id, inbox_id
}

public struct ConsentListEntry: Codable, Hashable {
public enum EntryType: String, Codable {
case address, group_id, inbox_id
}

static func address(_ address: String, type: ConsentState = .unknown) -> ConsentListEntry {
ConsentListEntry(value: address, entryType: .address, consentType: type)
}
Expand Down Expand Up @@ -125,6 +124,9 @@ public class ConsentList {
}

func publish(entries: [ConsentListEntry]) async throws {
if (client.v3Client != nil) {
try await setV3ConsentState(entries: entries)
}
if (client.hasV2Client) {
let privateKey = try client.v1keys.identityKey.secp256K1.bytes
let publicKey = try client.v1keys.identityKey.publicKey.secp256K1Uncompressed.bytes
Expand Down Expand Up @@ -180,7 +182,11 @@ public class ConsentList {

try await client.publish(envelopes: [envelope])
}
}
}

func setV3ConsentState(entries: [ConsentListEntry]) async throws {
try await client.v3Client?.setConsentStates(records: entries.map(\.toFFI))
}

func allow(address: String) async -> ConsentListEntry {
let entry = ConsentListEntry.address(address, type: ConsentState.allowed)
Expand Down Expand Up @@ -224,23 +230,44 @@ public class ConsentList {
return entry
}

func state(address: String) async -> ConsentState {
func state(address: String) async throws -> ConsentState {
if let client = client.v3Client {
return try await client.getConsentState(
entityType: .address,
entity: address
).fromFFI
}

guard let entry = await entriesManager.get(ConsentListEntry.address(address).key) else {
return .unknown
}

return entry.consentType
}

func groupState(groupId: String) async -> ConsentState {
func groupState(groupId: String) async throws -> ConsentState {
if let client = client.v3Client {
return try await client.getConsentState(
entityType: .groupId,
entity: groupId
).fromFFI
}

guard let entry = await entriesManager.get(ConsentListEntry.groupId(groupId: groupId).key) else {
return .unknown
}

return entry.consentType
}

func inboxIdState(inboxId: String) async -> ConsentState {
func inboxIdState(inboxId: String) async throws-> ConsentState {
if let client = client.v3Client {
return try await client.getConsentState(
entityType: .inboxId,
entity: inboxId
).fromFFI
}

guard let entry = await entriesManager.get(ConsentListEntry.inboxId(inboxId).key) else {
return .unknown
}
Expand All @@ -266,33 +293,34 @@ public actor Contacts {
consentList = ConsentList(client: client)
}

public func refreshConsentList() async throws -> ConsentList {
_ = try await consentList.load()
public func refreshConsentList() async throws -> ConsentList {
let entries = try await consentList.load()
try await consentList.setV3ConsentState(entries: entries)
return consentList
}

public func isAllowed(_ address: String) async -> Bool {
return await consentList.state(address: address) == .allowed
public func isAllowed(_ address: String) async throws -> Bool {
return try await consentList.state(address: address) == .allowed
}

public func isDenied(_ address: String) async -> Bool {
return await consentList.state(address: address) == .denied
public func isDenied(_ address: String) async throws -> Bool {
return try await consentList.state(address: address) == .denied
}

public func isGroupAllowed(groupId: String) async -> Bool {
return await consentList.groupState(groupId: groupId) == .allowed
public func isGroupAllowed(groupId: String) async throws -> Bool {
return try await consentList.groupState(groupId: groupId) == .allowed
}

public func isGroupDenied(groupId: String) async -> Bool {
return await consentList.groupState(groupId: groupId) == .denied
public func isGroupDenied(groupId: String) async throws -> Bool {
return try await consentList.groupState(groupId: groupId) == .denied
}

public func isInboxAllowed(inboxId: String) async -> Bool {
return await consentList.inboxIdState(inboxId: inboxId) == .allowed
public func isInboxAllowed(inboxId: String) async throws -> Bool {
return try await consentList.inboxIdState(inboxId: inboxId) == .allowed
}

public func isInboxDenied(inboxId: String) async -> Bool {
return await consentList.inboxIdState(inboxId: inboxId) == .denied
public func isInboxDenied(inboxId: String) async throws -> Bool {
return try await consentList.inboxIdState(inboxId: inboxId) == .denied
}

public func allow(addresses: [String]) async throws {
Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTPiOS/Conversation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public enum Conversation: Sendable {
case .v2(let conversationV2):
return try await conversationV2.client.contacts.consentList.state(address: peerAddress)
case let .group(group):
return await group.client.contacts.consentList.groupState(groupId: group.id)
return try group.consentState()
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTPiOS/ConversationV1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public struct ConversationV1 {

@discardableResult func send(prepared: PreparedMessage) async throws -> String {
try await client.publish(envelopes: prepared.envelopes)
if((await client.contacts.consentList.state(address: peerAddress)) == .unknown) {
if((try await client.contacts.consentList.state(address: peerAddress)) == .unknown) {
try await client.contacts.allow(addresses: [peerAddress])
}
return prepared.messageID
Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTPiOS/ConversationV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public struct ConversationV2 {

@discardableResult func send(prepared: PreparedMessage) async throws -> String {
try await client.publish(envelopes: prepared.envelopes)
if((await client.contacts.consentList.state(address: peerAddress)) == .unknown) {
if((try await client.contacts.consentList.state(address: peerAddress)) == .unknown) {
try await client.contacts.allow(addresses: [peerAddress])
}
return prepared.messageID
Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTPiOS/Conversations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ public actor Conversations {
}
let contacts = client.contacts
_ = try await contacts.refreshConsentList()
if await (contacts.consentList.state(address: peerAddress) == .unknown) {
if try await (contacts.consentList.state(address: peerAddress) == .unknown) {
try await contacts.allow(addresses: [peerAddress])
}
}
Expand Down
36 changes: 36 additions & 0 deletions Sources/XMTPiOS/Extensions/Ffi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,39 @@ extension FfiGroupMember {
Member(ffiGroupMember: self)
}
}

extension ConsentState {
var toFFI: FfiConsentState{
switch (self) {
case .allowed: return FfiConsentState.allowed
case .denied: return FfiConsentState.denied
default: return FfiConsentState.unknown
}
}
}

extension FfiConsentState {
var fromFFI: ConsentState{
switch (self) {
case .allowed: return ConsentState.allowed
case .denied: return ConsentState.denied
default: return ConsentState.unknown
}
}
}

extension EntryType {
var toFFI: FfiConsentEntityType{
switch (self) {
case .group_id: return FfiConsentEntityType.groupId
case .inbox_id: return FfiConsentEntityType.inboxId
case .address: return FfiConsentEntityType.address
}
}
}

extension ConsentListEntry {
var toFFI: FfiConsent {
FfiConsent(entityType: entryType.toFFI, state: consentType.toFFI, entity: value)
}
}
27 changes: 19 additions & 8 deletions Sources/XMTPiOS/Group.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,21 @@ public struct Group: Identifiable, Equatable, Hashable {
try await ffiGroup.updatePermissionPolicy(permissionUpdateType: FfiPermissionUpdateType.updateMetadata, permissionPolicyOption: PermissionOption.toFfiPermissionPolicy(option: newPermissionOption), metadataField: FfiMetadataField.pinnedFrameUrl)
}

public func updateConsentState(state: ConsentState) async throws {
if (client.hasV2Client) {
switch (state) {
case .allowed: try await client.contacts.allowGroups(groupIds: [id])
case .denied: try await client.contacts.denyGroups(groupIds: [id])
case .unknown: ()
}
}

try ffiGroup.updateConsentState(state: state.toFFI)
}

public func consentState() throws -> ConsentState{
return try ffiGroup.consentState().fromFFI
}

public func processMessage(envelopeBytes: Data) async throws -> DecodedMessage {
let message = try await ffiGroup.processStreamedGroupMessage(envelopeBytes: envelopeBytes)
Expand All @@ -230,10 +245,8 @@ public struct Group: Identifiable, Equatable, Hashable {
}

public func send(encodedContent: EncodedContent) async throws -> String {
let groupState = await client.contacts.consentList.groupState(groupId: id)

if groupState == ConsentState.unknown {
try await client.contacts.allowGroups(groupIds: [id])
if (try consentState() == .unknown) {
try await updateConsentState(state: .allowed)
}

let messageId = try await ffiGroup.send(contentBytes: encodedContent.serializedData())
Expand Down Expand Up @@ -273,10 +286,8 @@ public struct Group: Identifiable, Equatable, Hashable {
}

public func prepareMessage<T>(content: T, options: SendOptions? = nil) async throws -> String {
let groupState = await client.contacts.consentList.groupState(groupId: id)

if groupState == ConsentState.unknown {
try await client.contacts.allowGroups(groupIds: [id])
if (try consentState() == .unknown) {
try await updateConsentState(state: .allowed)
}

let encodeContent = try await encodeContent(content: content, options: options)
Expand Down
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Mls/Member.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,9 @@ public struct Member {
return PermissionLevel.SuperAdmin
}
}

public var consentState: ConsentState {
ffiGroupMember.consentState.fromFFI
}
}

20 changes: 10 additions & 10 deletions Tests/XMTPTests/ContactsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,27 @@ class ContactsTests: XCTestCase {
let fixtures = await fixtures()

let contacts = fixtures.bobClient.contacts
var result = await contacts.isAllowed(fixtures.alice.address)
var result = try await contacts.isAllowed(fixtures.alice.address)

XCTAssertFalse(result)

try await contacts.allow(addresses: [fixtures.alice.address])

result = await contacts.isAllowed(fixtures.alice.address)
result = try await contacts.isAllowed(fixtures.alice.address)
XCTAssertTrue(result)
}

func testDenyAddress() async throws {
let fixtures = await fixtures()

let contacts = fixtures.bobClient.contacts
var result = await contacts.isAllowed(fixtures.alice.address)
var result = try await contacts.isAllowed(fixtures.alice.address)

XCTAssertFalse(result)

try await contacts.deny(addresses: [fixtures.alice.address])

result = await contacts.isDenied(fixtures.alice.address)
result = try await contacts.isDenied(fixtures.alice.address)
XCTAssertTrue(result)
}

Expand All @@ -67,21 +67,21 @@ class ContactsTests: XCTestCase {
let caroClient = try await Client.create(account: caro, options: fixtures.clientOptions)

let contacts = fixtures.bobClient.contacts
var result = await contacts.isAllowed(fixtures.alice.address)
var result = try await contacts.isAllowed(fixtures.alice.address)
XCTAssertFalse(result)
result = await contacts.isAllowed(caroClient.address)
result = try await contacts.isAllowed(caroClient.address)
XCTAssertFalse(result)

try await contacts.deny(addresses: [fixtures.alice.address, caroClient.address])

var aliceResult = await contacts.isDenied(fixtures.alice.address)
var aliceResult = try await contacts.isDenied(fixtures.alice.address)
XCTAssertTrue(aliceResult)
var caroResult = await contacts.isDenied(fixtures.alice.address)
var caroResult = try await contacts.isDenied(fixtures.alice.address)
XCTAssertTrue(caroResult)
try await contacts.allow(addresses: [fixtures.alice.address, caroClient.address])
aliceResult = await contacts.isAllowed(fixtures.alice.address)
aliceResult = try await contacts.isAllowed(fixtures.alice.address)
XCTAssertTrue(aliceResult)
caroResult = await contacts.isAllowed(fixtures.alice.address)
caroResult = try await contacts.isAllowed(fixtures.alice.address)
XCTAssertTrue(caroResult)
}
}
6 changes: 3 additions & 3 deletions Tests/XMTPTests/ConversationsTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class ConversationsTests: XCTestCase {
fixtures.aliceClient.conversations.list()
let alixConversation = alixConversations.first(where: { $0.topic == boConversation.topic })
XCTAssertNotNil(alixConversation)
let consentStatus = await fixtures.aliceClient.contacts.isAllowed(fixtures.bobClient.address)
let consentStatus = try await fixtures.aliceClient.contacts.isAllowed(fixtures.bobClient.address)
XCTAssertTrue(consentStatus)
}

Expand All @@ -228,7 +228,7 @@ class ConversationsTests: XCTestCase {
fixtures.aliceClient.conversations.list()
let alixConversation = alixConversations.first(where: { $0.topic == boConversation.topic })
XCTAssertNotNil(alixConversation)
let isDenied = await fixtures.aliceClient.contacts.isDenied(fixtures.bobClient.address)
let isDenied = try await fixtures.aliceClient.contacts.isDenied(fixtures.bobClient.address)
XCTAssertTrue(isDenied)
}

Expand All @@ -250,7 +250,7 @@ class ConversationsTests: XCTestCase {
fixtures.aliceClient.conversations.list()
let alixConversation = alixConversations.first(where: { $0.topic == boConversation.topic })
XCTAssertNotNil(alixConversation)
let isAllowed = await fixtures.aliceClient.contacts.isAllowed(fixtures.bobClient.address)
let isAllowed = try await fixtures.aliceClient.contacts.isAllowed(fixtures.bobClient.address)
XCTAssertFalse(isAllowed)
}
}
Loading
Loading