Skip to content

Commit

Permalink
ConsentList Concurrency Crash (#315)
Browse files Browse the repository at this point in the history
* make the map a actor

* tweaks to the async code
  • Loading branch information
nplasterer authored Apr 17, 2024
1 parent 076fb0a commit 321c449
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 34 deletions.
124 changes: 92 additions & 32 deletions Sources/XMTPiOS/Contacts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,24 @@ public enum ContactError: Error {
case invalidIdentifier
}

public actor EntriesManager {
public var map: [String: ConsentListEntry] = [:]

func set(_ key: String, _ object: ConsentListEntry) {
map[key] = object
}

func get(_ key: String) -> ConsentListEntry? {
map[key]
}
}

public class ConsentList {
public var entries: [String: ConsentListEntry] = [:]
public let entriesManager = EntriesManager()
var publicKey: Data
var privateKey: Data
var identifier: String?
var lastFetched: Date?
var lastFetched: Date?
var client: Client

init(client: Client) {
Expand Down Expand Up @@ -77,23 +89,23 @@ public class ConsentList {
}
for preference in preferences {
for address in preference.allowAddress.walletAddresses {
_ = allow(address: address)
_ = await allow(address: address)
}

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

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

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

return Array(entries.values)
return await Array(entriesManager.map.values)
}

func publish(entries: [ConsentListEntry]) async throws {
Expand Down Expand Up @@ -148,46 +160,46 @@ public class ConsentList {
try await client.publish(envelopes: [envelope])
}

func allow(address: String) -> ConsentListEntry {
func allow(address: String) async -> ConsentListEntry {
let entry = ConsentListEntry.address(address, type: ConsentState.allowed)
entries[ConsentListEntry.address(address).key] = entry
await entriesManager.set(entry.key, entry)

return entry
}

func deny(address: String) -> ConsentListEntry {
func deny(address: String) async -> ConsentListEntry {
let entry = ConsentListEntry.address(address, type: ConsentState.denied)
entries[ConsentListEntry.address(address).key] = entry
await entriesManager.set(entry.key, entry)

return entry
}

func allowGroup(groupId: Data) -> ConsentListEntry {
func allowGroup(groupId: Data) async -> ConsentListEntry {
let groupIdString = groupId.toHex
let entry = ConsentListEntry.groupId(groupId: groupIdString, type: ConsentState.allowed)
entries[ConsentListEntry.groupId(groupId: groupIdString).key] = entry
await entriesManager.set(entry.key, entry)

return entry
}

func denyGroup(groupId: Data) -> ConsentListEntry {
func denyGroup(groupId: Data) async -> ConsentListEntry {
let groupIdString = groupId.toHex
let entry = ConsentListEntry.groupId(groupId: groupIdString, type: ConsentState.denied)
entries[ConsentListEntry.groupId(groupId: groupIdString).key] = entry
await entriesManager.set(entry.key, entry)

return entry
}

func state(address: String) -> ConsentState {
guard let entry = entries[ConsentListEntry.address(address).key] else {
func state(address: String) async -> ConsentState {
guard let entry = await entriesManager.get(ConsentListEntry.address(address).key) else {
return .unknown
}

return entry.consentType
}

func groupState(groupId: Data) -> ConsentState {
guard let entry = entries[ConsentListEntry.groupId(groupId: groupId.toHex).key] else {
func groupState(groupId: Data) async -> ConsentState {
guard let entry = await entriesManager.get(ConsentListEntry.groupId(groupId: groupId.toHex).key) else {
return .unknown
}

Expand Down Expand Up @@ -217,40 +229,88 @@ public actor Contacts {
return consentList
}

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

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

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

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

public func allow(addresses: [String]) async throws {
let entries = addresses.map { consentList.allow(address: $0) }
var entries: [ConsentListEntry] = []

try await withThrowingTaskGroup(of: ConsentListEntry.self) { group in
for address in addresses {
group.addTask {
return await self.consentList.allow(address: address)
}
}

for try await entry in group {
entries.append(entry)
}
}
try await consentList.publish(entries: entries)
}

public func deny(addresses: [String]) async throws {
let entries = addresses.map { consentList.deny(address: $0) }
var entries: [ConsentListEntry] = []

try await withThrowingTaskGroup(of: ConsentListEntry.self) { group in
for address in addresses {
group.addTask {
return await self.consentList.deny(address: address)
}
}

for try await entry in group {
entries.append(entry)
}
}
try await consentList.publish(entries: entries)
}

public func allowGroup(groupIds: [Data]) async throws {
let entries = groupIds.map { consentList.allowGroup(groupId: $0) }
var entries: [ConsentListEntry] = []

try await withThrowingTaskGroup(of: ConsentListEntry.self) { group in
for groupId in groupIds {
group.addTask {
return await self.consentList.allowGroup(groupId: groupId)
}
}

for try await entry in group {
entries.append(entry)
}
}
try await consentList.publish(entries: entries)
}

public func denyGroup(groupIds: [Data]) async throws {
let entries = groupIds.map { consentList.denyGroup(groupId: $0) }
try await consentList.publish(entries: entries)
var entries: [ConsentListEntry] = []

try await withThrowingTaskGroup(of: ConsentListEntry.self) { group in
for groupId in groupIds {
group.addTask {
return await self.consentList.denyGroup(groupId: groupId)
}
}

for try await entry in group {
entries.append(entry)
}
}
try await consentList.publish(entries: entries)
}

func markIntroduced(_ peerAddress: String, _ isIntroduced: Bool) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/xmtp/libxmtp-swift",
"state" : {
"revision" : "76b7943ca266b0628f58b1b9d5cd311a82a47792",
"version" : "0.4.4-beta1"
"revision" : "79abd63c8850958337f2991d9763ef908223de43",
"version" : "0.4.4-beta2"
}
},
{
Expand Down

0 comments on commit 321c449

Please sign in to comment.