Skip to content

Commit

Permalink
feat: Consent Caching
Browse files Browse the repository at this point in the history
Added Caching for Consent List
Updated allow/deny calls to only call network once
  • Loading branch information
Alex Risch authored and Alex Risch committed Apr 9, 2024
1 parent bf1219f commit a1133f0
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 83 deletions.
144 changes: 71 additions & 73 deletions Sources/XMTPiOS/Contacts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class ConsentList {
var publicKey: Data
var privateKey: Data
var identifier: String?

var lastFetched: Date?
var client: Client

init(client: Client) {
Expand All @@ -55,17 +55,18 @@ public class ConsentList {
identifier = try? LibXMTP.generatePrivatePreferencesTopicIdentifier(privateKey: privateKey)
}

func load(afterDate: Date? = nil) async throws -> ConsentList {
func load() async throws -> [ConsentListEntry] {
guard let identifier = identifier else {
throw ContactError.invalidIdentifier
}
let newDate = Date()

let pagination = Pagination(
after: afterDate,
direction: .ascending
)
after: lastFetched,
direction: .ascending
)
let envelopes = try await client.apiClient.envelopes(topic: Topic.preferenceList(identifier).description, pagination: pagination)
let consentList = ConsentList(client: client)
lastFetched = newDate

var preferences: [PrivatePreferencesAction] = []

Expand All @@ -74,75 +75,78 @@ public class ConsentList {

try preferences.append(PrivatePreferencesAction(serializedData: Data(payload)))
}

for preference in preferences {
for address in preference.allowAddress.walletAddresses {
_ = consentList.allow(address: address)
_ = allow(address: address)
}

for address in preference.denyAddress.walletAddresses {
_ = consentList.deny(address: address)
_ = deny(address: address)
}

for groupId in preference.allowGroup.groupIds {
_ = consentList.allowGroup(groupId: groupId)
_ = allowGroup(groupId: groupId)
}

for groupId in preference.denyGroup.groupIds {
_ = consentList.denyGroup(groupId: groupId)
_ = denyGroup(groupId: groupId)
}
}

return consentList
return Array(entries.values)
}

func publish(entry: ConsentListEntry) async throws {
guard let identifier = identifier else {
throw ContactError.invalidIdentifier
}

var payload = PrivatePreferencesAction()
switch entry.entryType {

case .address:
switch entry.consentType {
case .allowed:
payload.allowAddress.walletAddresses = [entry.value]
case .denied:
payload.denyAddress.walletAddresses = [entry.value]
case .unknown:
payload.messageType = nil
}

case .groupId:
switch entry.consentType {
case .allowed:
if let valueData = entry.value.data(using: .utf8) {
payload.allowGroup.groupIds = [valueData]
}
case .denied:
if let valueData = entry.value.data(using: .utf8) {
payload.denyGroup.groupIds = [valueData]
}
case .unknown:
payload.messageType = nil
}
}

let message = try LibXMTP.userPreferencesEncrypt(
publicKey: publicKey,
privateKey: privateKey,
message: payload.serializedData()
)
func publish(entries: [ConsentListEntry]) async throws {
guard let identifier = identifier else {
throw ContactError.invalidIdentifier
}
var payload = PrivatePreferencesAction()

for entry in entries {
switch entry.entryType {

case .address:
switch entry.consentType {
case .allowed:
payload.allowAddress.walletAddresses.append(entry.value)
case .denied:
payload.denyAddress.walletAddresses.append(entry.value)
case .unknown:
payload.messageType = nil
}

case .groupId:
switch entry.consentType {
case .allowed:
if let valueData = entry.value.data(using: .utf8) {
payload.allowGroup.groupIds.append(valueData)
}
case .denied:
if let valueData = entry.value.data(using: .utf8) {
payload.denyGroup.groupIds.append(valueData)
}
case .unknown:
payload.messageType = nil
}
}

}


let message = try LibXMTP.userPreferencesEncrypt(
publicKey: publicKey,
privateKey: privateKey,
message: payload.serializedData()
)

let envelope = Envelope(
topic: Topic.preferenceList(identifier),
timestamp: Date(),
message: Data(message)
)
let envelope = Envelope(
topic: Topic.preferenceList(identifier),
timestamp: Date(),
message: Data(message)
)

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

func allow(address: String) -> ConsentListEntry {
let entry = ConsentListEntry.address(address, type: ConsentState.allowed)
Expand Down Expand Up @@ -208,8 +212,8 @@ public actor Contacts {
consentList = ConsentList(client: client)
}

public func refreshConsentList(afterDate: Date? = nil) async throws -> ConsentList {
consentList = try await ConsentList(client: client).load(afterDate: afterDate)
public func refreshConsentList() async throws -> ConsentList {
_ = try await consentList.load()
return consentList
}

Expand All @@ -230,29 +234,23 @@ public actor Contacts {
}

public func allow(addresses: [String]) async throws {
for address in addresses {
try await ConsentList(client: client).publish(entry: consentList.allow(address: address))
}
let entries = addresses.map { consentList.allow(address: $0) }
try await consentList.publish(entries: entries)
}

public func deny(addresses: [String]) async throws {
for address in addresses {
try await ConsentList(client: client).publish(entry: consentList.deny(address: address))
}
let entries = addresses.map { consentList.deny(address: $0) }
try await consentList.publish(entries: entries)
}

public func allowGroup(groupIds: [Data]) async throws {
for groupId in groupIds {
let entry = consentList.allowGroup(groupId: groupId)
try await ConsentList(client: client).publish(entry: entry)
}
let entries = groupIds.map { consentList.allowGroup(groupId: $0) }
try await consentList.publish(entries: entries)
}

public func denyGroup(groupIds: [Data]) async throws {
for groupId in groupIds {
let entry = consentList.denyGroup(groupId: groupId)
try await ConsentList(client: client).publish(entry: entry)
}
let entries = groupIds.map { consentList.denyGroup(groupId: $0) }
try await consentList.publish(entries: entries)
}

func markIntroduced(_ peerAddress: String, _ isIntroduced: Bool) {
Expand Down
30 changes: 21 additions & 9 deletions Tests/XMTPTests/ContactsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,28 @@ class ContactsTests: XCTestCase {
XCTAssertTrue(result)
}

func testRefreshConsentListPagination() async throws {
func testHandleMultipleAddresses() async throws {
let fixtures = await fixtures()
let caro = try PrivateKey.generate()
let fakeApiClient = FakeApiClient()
let caroClient = try await Client.create(account: caro, apiClient: fakeApiClient)

let contacts = fixtures.bobClient.contacts
let aliceAddress = fixtures.alice.address
try await contacts.deny(addresses: [aliceAddress])
let date = Date()

let result = try await contacts.consentList.load(afterDate: date)
XCTAssertNil(result.entries[ConsentListEntry.address(aliceAddress).key]?.consentType)
let allResult = try await contacts.consentList.load()
XCTAssertNotNil(allResult.entries[ConsentListEntry.address(aliceAddress).key]?.consentType)
var result = await contacts.isAllowed(fixtures.alice.address)
XCTAssertFalse(result)
result = 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)
XCTAssertTrue(aliceResult)
var caroResult = 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)
XCTAssertTrue(aliceResult)
caroResult = await contacts.isAllowed(fixtures.alice.address)
XCTAssertTrue(caroResult)
}
}
2 changes: 1 addition & 1 deletion XMTP.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Pod::Spec.new do |spec|
#

spec.name = "XMTP"
spec.version = "0.9.5"
spec.version = "0.9.6"
spec.summary = "XMTP SDK Cocoapod"

# This description is used to generate tags and improve search results.
Expand Down

0 comments on commit a1133f0

Please sign in to comment.