Container_BottledPeers.swift [plain text]
import CoreData
import Foundation
extension Container {
func onMOCQueueFindBottle(bottleID: String) throws -> (BottleMO) {
guard let containerBottles = self.containerMO.bottles as? Set<BottleMO> else {
throw ContainerError.noBottlesPresent
}
let bottles = containerBottles.filter { $0.bottleID == bottleID }
guard let bottle = bottles.first else {
throw ContainerError.noBottlesForEscrowRecordID
}
return bottle
}
func preflightVouchWithBottle(bottleID: String,
reply: @escaping (String?, TPSyncingPolicy?, Bool, Error?) -> Void) {
self.semaphore.wait()
let reply: (String?, TPSyncingPolicy?, Bool, Error?) -> Void = {
os_log("preflightVouchWithBottle complete: %{public}@",
log: tplogTrace, type: .info, traceError($3))
self.semaphore.signal()
reply($0, $1, $2, $3)
}
self.moc.performAndWait {
do {
let (_, peerID, syncingPolicy) = try self.onMOCQueuePerformPreflight(bottleID: bottleID)
reply(peerID, syncingPolicy, false, nil)
} catch {
os_log("preflightVouchWithBottle failed; forcing refetch and retrying: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
self.fetchAndPersistChanges { fetchError in
guard fetchError == nil else {
os_log("preflightVouchWithBottle unable to fetch current peers: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "")
reply(nil, nil, true, fetchError)
return
}
// Ensure we have all policy versions claimed by peers, including our sponsor
let allPolicyVersions = self.model.allPolicyVersions()
self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, fetchPolicyDocumentsError in
guard fetchPolicyDocumentsError == nil else {
os_log("preflightVouchWithBottle unable to fetch policy documents: %{public}@", log: tplogDebug, type: .default, (fetchPolicyDocumentsError as CVarArg?) ?? "no error")
reply(nil, nil, true, fetchPolicyDocumentsError)
return
}
self.fetchViableBottlesWithSemaphore { _, _, fetchBottlesError in
guard fetchBottlesError == nil else {
os_log("preflightVouchWithBottle unable to fetch viable bottles: %{public}@", log: tplogDebug, type: .default, (fetchPolicyDocumentsError as CVarArg?) ?? "no error")
reply(nil, nil, true, fetchBottlesError)
return
}
// and try again:
self.moc.performAndWait {
do {
let (_, peerID, syncingPolicy) = try self.onMOCQueuePerformPreflight(bottleID: bottleID)
reply(peerID, syncingPolicy, true, nil)
} catch {
os_log("preflightVouchWithBottle failed after refetches; failing: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
reply(nil, nil, true, error)
}
}
}
}
}
}
}
}
func onMOCQueuePerformPreflight(bottleID: String) throws -> (BottleMO, String, TPSyncingPolicy) {
guard let egoPeerID = self.containerMO.egoPeerID,
let egoPermData = self.containerMO.egoPeerPermanentInfo,
let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
throw ContainerError.noPreparedIdentity
}
let keyFactory = TPECPublicKeyFactory()
guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
throw ContainerError.invalidPermanentInfoOrSig
}
let bottleMO = try self.onMOCQueueFindBottle(bottleID: bottleID)
guard let sponsorPeer = self.model.peer(withID: bottleMO.peerID ?? "") else {
os_log("preflightVouchWithBottle found no peer to match bottle", log: tplogDebug, type: .default)
throw ContainerError.sponsorNotRegistered(bottleMO.peerID ?? "no peer ID given")
}
guard let sponsorPeerStableInfo = sponsorPeer.stableInfo else {
os_log("preflightVouchWithBottle sponsor peer has no stable info", log: tplogDebug, type: .default)
throw ContainerError.sponsorNotRegistered(bottleMO.peerID ?? "no peer ID given")
}
// We need to extract the syncing policy that the remote peer would have used (if they were the type of device that we are)
let policy = try self.syncingPolicyFor(modelID: egoPermanentInfo.modelID, stableInfo: sponsorPeerStableInfo)
return (bottleMO, sponsorPeer.peerID, policy)
}
}