From 8a9da51943c5d0a6f3f5fc5d17341da22a0c652d Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 22 May 2019 16:54:57 +1000 Subject: [PATCH 1/7] Implement swarm specific error handling --- .../src/Loki/API/LokiAPI+SwarmAPI.swift | 28 +++++++++++++++++-- SignalServiceKit/src/Loki/API/LokiAPI.swift | 27 +++++------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift index 4b4018172..0e1df6849 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift @@ -7,7 +7,7 @@ extension LokiAPI { private static let defaultSnodePort: UInt16 = 8080 // MARK: Caching - private static var swarmCache: [String:[Target]] = [:] + fileprivate static var swarmCache: [String:[Target]] = [:] // TODO: Persist on disk // MARK: Internal API private static func getRandomSnode() -> Promise { @@ -21,7 +21,7 @@ extension LokiAPI { return Promise<[Target]> { $0.fulfill(cachedSwarm) } } else { let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey ] - return getRandomSnode().then { invoke(.getSwarm, on: $0, with: parameters) }.map { parseTargets(from: $0) }.get { swarmCache[hexEncodedPublicKey] = $0 } + return getRandomSnode().then { invoke(.getSwarm, on: $0, associatedWith: hexEncodedPublicKey, parameters: parameters) }.map { parseTargets(from: $0) }.get { swarmCache[hexEncodedPublicKey] = $0 } } } @@ -44,3 +44,27 @@ extension LokiAPI { // return addresses.map { Target(address: $0, port: defaultSnodePort) } } } + +internal extension Promise { + + func handlingSwarmSpecificErrorsIfNeeded(for target: LokiAPI.Target, associatedWith hexEncodedPublicKey: String) -> Promise { + return recover { error -> Promise in + if let error = error as? NetworkManagerError { + switch error.statusCode { + case 0: + // The snode is unreachable; usually a problem with LokiNet + Logger.warn("[Loki] There appears to be a problem with LokiNet.") + case 421: + // The snode isn't associated with the given public key anymore + let swarm = LokiAPI.swarmCache[hexEncodedPublicKey] + if var swarm = swarm, let index = swarm.firstIndex(of: target) { + swarm.remove(at: index) + LokiAPI.swarmCache[hexEncodedPublicKey] = swarm + } + default: break + } + } + throw error + } + } +} diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 6d0769096..04dd160b9 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -13,9 +13,9 @@ import PromiseKit let port: UInt16 enum Method : String { - /// Only applicable to snode targets. + /// Only supported by snode targets. case getSwarm = "get_snodes_for_pubkey" - /// Only applicable to snode targets. + /// Only supported by snode targets. case getMessages = "retrieve" case sendMessage = "store" } @@ -38,10 +38,10 @@ import PromiseKit override private init() { } // MARK: Internal API - internal static func invoke(_ method: Target.Method, on target: Target, with parameters: [String:Any] = [:]) -> Promise { + internal static func invoke(_ method: Target.Method, on target: Target, associatedWith hexEncodedPublicKey: String, parameters: [String:Any] = [:]) -> Promise { let url = URL(string: "\(target.address):\(target.port)/\(version)/storage_rpc")! let request = TSRequest(url: url, method: "POST", parameters: [ "method" : method.rawValue, "params" : parameters ]) - return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject } + return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.handlingSwarmSpecificErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey) } // MARK: Public API @@ -50,7 +50,7 @@ import PromiseKit return getTargetSnodes(for: hexEncodedPublicKey).mapValues { targetSnode in let lastHash = getLastMessageHashValue(for: targetSnode) ?? "" let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey, "lastHash" : lastHash ] - return invoke(.getMessages, on: targetSnode, with: parameters).map { rawResponse in + return invoke(.getMessages, on: targetSnode, associatedWith: hexEncodedPublicKey, parameters: parameters).map { rawResponse in guard let json = rawResponse as? JSON, let rawMessages = json["messages"] as? [JSON] else { return [] } updateLastMessageHashValueIfPossible(for: targetSnode, from: rawMessages) let newRawMessages = removeDuplicates(from: rawMessages) @@ -70,7 +70,7 @@ import PromiseKit // TODO: Send using P2P protocol } else { let parameters = lokiMessage.toJSON() - return getTargetSnodes(for: lokiMessage.destination).mapValues { invoke(.sendMessage, on: $0, with: parameters).recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) }.map { Set($0) } + return getTargetSnodes(for: lokiMessage.destination).mapValues { invoke(.sendMessage, on: $0, associatedWith: lokiMessage.destination, parameters: parameters) }.map { Set($0) } } } @@ -80,7 +80,7 @@ import PromiseKit // TODO: Send using P2P protocol } else { let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey ] // TODO: Figure out correct parameters - return getTargetSnodes(for: hexEncodedPublicKey).mapValues { invoke(.sendMessage, on: $0, with: parameters).recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) }.map { Set($0) } + return getTargetSnodes(for: hexEncodedPublicKey).mapValues { invoke(.sendMessage, on: $0, associatedWith: hexEncodedPublicKey, parameters: parameters) }.map { Set($0) } } } @@ -169,16 +169,3 @@ private extension AnyPromise { return result } } - -// MARK: Error Handling -private extension Promise { - - func recoverNetworkErrorIfNeeded(on queue: DispatchQueue) -> Promise { - return recover(on: queue) { error -> Promise in - switch error { - case NetworkManagerError.taskError(_, let underlyingError): throw underlyingError - default: throw error - } - } - } -} From cef940547369fc89c6cee9ac7d44605c9131ebd5 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 23 May 2019 11:35:56 +1000 Subject: [PATCH 2/7] Distinguish between minimum snode count and target snode count --- SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift index 0e1df6849..31bbb70a4 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift @@ -3,7 +3,8 @@ import PromiseKit extension LokiAPI { // MARK: Settings - private static let targetSnodeCount = 2 + private static let minimumSnodeCount = 2 + private static let targetSnodeCount = 3 private static let defaultSnodePort: UInt16 = 8080 // MARK: Caching @@ -17,7 +18,7 @@ extension LokiAPI { } private static func getSwarm(for hexEncodedPublicKey: String) -> Promise<[Target]> { - if let cachedSwarm = swarmCache[hexEncodedPublicKey], cachedSwarm.count >= targetSnodeCount { + if let cachedSwarm = swarmCache[hexEncodedPublicKey], cachedSwarm.count >= minimumSnodeCount { return Promise<[Target]> { $0.fulfill(cachedSwarm) } } else { let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey ] From 5af0ad03b90884d40d65ae701817dd9b610baee7 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 23 May 2019 12:28:35 +1000 Subject: [PATCH 3/7] Persist swarm cache & make access modifiers explicit --- Signal/src/AppDelegate.m | 2 ++ .../src/Loki/API/LokiAPI+SwarmAPI.swift | 31 ++++++++++++++++--- .../src/Loki/API/LokiAPI+Target.swift | 26 ++++++++++++++++ .../src/Loki/API/LokiAPI+TargetWrapper.swift | 22 +++++++++++++ SignalServiceKit/src/Loki/API/LokiAPI.swift | 23 ++++---------- .../src/Loki/Crypto/ECKeyPair+Loki.swift | 4 +-- .../Loki/Crypto/FallbackSessionCipher.swift | 2 +- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.h | 3 -- .../src/Loki/Crypto/OWSPrimaryStorage+Loki.m | 8 ----- .../src/Loki/Crypto/ProofOfWork.swift | 6 ++-- .../Messages/FriendRequestExpirationJob.swift | 6 ++-- .../src/Loki/Utilities/Promise+Hashable.swift | 2 +- 12 files changed, 93 insertions(+), 42 deletions(-) create mode 100644 SignalServiceKit/src/Loki/API/LokiAPI+Target.swift create mode 100644 SignalServiceKit/src/Loki/API/LokiAPI+TargetWrapper.swift diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 5f9be61a3..1801d86cd 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1204,6 +1204,8 @@ static NSTimeInterval launchStartedAt; { OWSAssertIsOnMainThread(); OWSLogInfo(@"storageIsReady"); + + [LokiAPI loadSwarmCache]; [self checkIfAppIsReady]; } diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift index 31bbb70a4..6748384e9 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift @@ -1,6 +1,6 @@ import PromiseKit -extension LokiAPI { +public extension LokiAPI { // MARK: Settings private static let minimumSnodeCount = 2 @@ -8,7 +8,26 @@ extension LokiAPI { private static let defaultSnodePort: UInt16 = 8080 // MARK: Caching - fileprivate static var swarmCache: [String:[Target]] = [:] // TODO: Persist on disk + private static let swarmCacheKey = "swarmCacheKey" + private static let swarmCacheCollection = "swarmCacheCollection" + + fileprivate static var swarmCache: [String:[Target]] = [:] + + @objc public static func loadSwarmCache() { + var result: [String:[Target]]? = nil + storage.dbReadConnection.read { transaction in + let intermediate = transaction.object(forKey: swarmCacheKey, inCollection: swarmCacheCollection) as! [String:[TargetWrapper]] + result = intermediate.mapValues { $0.map { Target(from: $0) } } + } + swarmCache = result ?? [:] + } + + private static func saveSwarmCache() { + let intermediate = swarmCache.mapValues { $0.map { TargetWrapper(from: $0) } } + storage.dbReadWriteConnection.readWrite { transaction in + transaction.setObject(intermediate, forKey: swarmCacheKey, inCollection: swarmCacheCollection) + } + } // MARK: Internal API private static func getRandomSnode() -> Promise { @@ -22,7 +41,10 @@ extension LokiAPI { return Promise<[Target]> { $0.fulfill(cachedSwarm) } } else { let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey ] - return getRandomSnode().then { invoke(.getSwarm, on: $0, associatedWith: hexEncodedPublicKey, parameters: parameters) }.map { parseTargets(from: $0) }.get { swarmCache[hexEncodedPublicKey] = $0 } + return getRandomSnode().then { invoke(.getSwarm, on: $0, associatedWith: hexEncodedPublicKey, parameters: parameters) }.map { parseTargets(from: $0) }.get { swarm in + swarmCache[hexEncodedPublicKey] = swarm + saveSwarmCache() + } } } @@ -46,9 +68,10 @@ extension LokiAPI { } } +// MARK: Error Handling internal extension Promise { - func handlingSwarmSpecificErrorsIfNeeded(for target: LokiAPI.Target, associatedWith hexEncodedPublicKey: String) -> Promise { + internal func handlingSwarmSpecificErrorsIfNeeded(for target: LokiAPI.Target, associatedWith hexEncodedPublicKey: String) -> Promise { return recover { error -> Promise in if let error = error as? NetworkManagerError { switch error.statusCode { diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Target.swift b/SignalServiceKit/src/Loki/API/LokiAPI+Target.swift new file mode 100644 index 000000000..cd15175f6 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/LokiAPI+Target.swift @@ -0,0 +1,26 @@ + +internal extension LokiAPI { + + internal struct Target : Hashable { + internal let address: String + internal let port: UInt16 + + internal init(address: String, port: UInt16) { + self.address = address + self.port = port + } + + internal init(from targetWrapper: TargetWrapper) { + self.address = targetWrapper.address + self.port = targetWrapper.port + } + + internal enum Method : String { + /// Only supported by snode targets. + case getSwarm = "get_snodes_for_pubkey" + /// Only supported by snode targets. + case getMessages = "retrieve" + case sendMessage = "store" + } + } +} diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+TargetWrapper.swift b/SignalServiceKit/src/Loki/API/LokiAPI+TargetWrapper.swift new file mode 100644 index 000000000..f8f14fba1 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/LokiAPI+TargetWrapper.swift @@ -0,0 +1,22 @@ + +@objc internal final class TargetWrapper : NSObject, NSCoding { + internal let address: String + internal let port: UInt16 + + internal init(from target: LokiAPI.Target) { + address = target.address + port = target.port + super.init() + } + + internal init?(coder: NSCoder) { + address = coder.decodeObject(forKey: "address") as! String + port = coder.decodeObject(forKey: "port") as! UInt16 + super.init() + } + + internal func encode(with coder: NSCoder) { + coder.encode(address, forKey: "address") + coder.encode(port, forKey: "port") + } +} diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 04dd160b9..efc0ea18a 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -1,26 +1,15 @@ import PromiseKit @objc public final class LokiAPI : NSObject { - private static let storage = OWSPrimaryStorage.shared() + internal static let storage = OWSPrimaryStorage.shared() // MARK: Settings private static let version = "v1" public static let defaultMessageTTL: UInt64 = 1 * 24 * 60 * 60 + private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey" + private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection" // MARK: Types - internal struct Target : Hashable { - let address: String - let port: UInt16 - - enum Method : String { - /// Only supported by snode targets. - case getSwarm = "get_snodes_for_pubkey" - /// Only supported by snode targets. - case getMessages = "retrieve" - case sendMessage = "store" - } - } - public typealias RawResponse = Any public enum Error : LocalizedError { @@ -149,21 +138,21 @@ import PromiseKit private static func getReceivedMessageHashValues() -> Set? { var result: Set? = nil storage.dbReadConnection.read { transaction in - result = storage.getReceivedMessageHashes(with: transaction) + result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set? } return result } private static func setReceivedMessageHashValues(to receivedMessageHashValues: Set) { storage.dbReadWriteConnection.readWrite { transaction in - storage.setReceivedMessageHashes(receivedMessageHashValues, with: transaction) + transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) } } } private extension AnyPromise { - static func from(_ promise: Promise) -> AnyPromise { + fileprivate static func from(_ promise: Promise) -> AnyPromise { let result = AnyPromise(promise) result.retainUntilComplete() return result diff --git a/SignalServiceKit/src/Loki/Crypto/ECKeyPair+Loki.swift b/SignalServiceKit/src/Loki/Crypto/ECKeyPair+Loki.swift index d96aa0b06..57fa63440 100644 --- a/SignalServiceKit/src/Loki/Crypto/ECKeyPair+Loki.swift +++ b/SignalServiceKit/src/Loki/Crypto/ECKeyPair+Loki.swift @@ -1,11 +1,11 @@ public extension ECKeyPair { - @objc var hexEncodedPrivateKey: String { + @objc public var hexEncodedPrivateKey: String { return privateKey.map { String(format: "%02hhx", $0) }.joined() } - @objc var hexEncodedPublicKey: String { + @objc public var hexEncodedPublicKey: String { // Prefixing with "05" is necessary for what seems to be a sort of Signal public key versioning system // Ref: [NSData prependKeyType] in AxolotKit return "05" + publicKey.map { String(format: "%02hhx", $0) }.joined() diff --git a/SignalServiceKit/src/Loki/Crypto/FallbackSessionCipher.swift b/SignalServiceKit/src/Loki/Crypto/FallbackSessionCipher.swift index 8a4ae5039..336bdc4a6 100644 --- a/SignalServiceKit/src/Loki/Crypto/FallbackSessionCipher.swift +++ b/SignalServiceKit/src/Loki/Crypto/FallbackSessionCipher.swift @@ -4,7 +4,7 @@ import Curve25519Kit private extension String { // Convert hex string to Data - var hexData: Data { + fileprivate var hexData: Data { var hex = self var data = Data() while(hex.count > 0) { diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h index beb789e9e..d74f63696 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.h @@ -95,9 +95,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setLastMessageHashForServiceNode:(NSString *)serviceNode hash:(NSString *)hash expiresAt:(u_int64_t)expiresAt transaction:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setLastMessageHash(forServiceNode:hash:expiresAt:transaction:)); -- (NSSet *_Nullable)getReceivedMessageHashesWithTransaction:(YapDatabaseReadTransaction *)transaction; -- (void)setReceivedMessageHashes:(NSSet *)receivedMessageHashes withTransaction:(YapDatabaseReadWriteTransaction *)transaction; - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m index 26744fbec..b737ef437 100644 --- a/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/OWSPrimaryStorage+Loki.m @@ -153,12 +153,4 @@ [transaction removeObjectForKey:serviceNode inCollection:LKLastMessageHashCollection]; } -- (NSSet *_Nullable)getReceivedMessageHashesWithTransaction:(YapDatabaseReadTransaction *)transaction { - return (NSSet *)[[transaction objectForKey:LKReceivedMessageHashesKey inCollection:LKReceivedMessageHashesCollection] as:NSSet.class]; -} - -- (void)setReceivedMessageHashes:(NSSet *)receivedMessageHashes withTransaction:(YapDatabaseReadWriteTransaction *)transaction { - [transaction setObject:receivedMessageHashes forKey:LKReceivedMessageHashesKey inCollection:LKReceivedMessageHashesCollection]; -} - @end diff --git a/SignalServiceKit/src/Loki/Crypto/ProofOfWork.swift b/SignalServiceKit/src/Loki/Crypto/ProofOfWork.swift index 1073318fe..7d2afd2a2 100644 --- a/SignalServiceKit/src/Loki/Crypto/ProofOfWork.swift +++ b/SignalServiceKit/src/Loki/Crypto/ProofOfWork.swift @@ -2,12 +2,12 @@ import CryptoSwift private extension UInt64 { - init(_ decimal: Decimal) { + fileprivate init(_ decimal: Decimal) { self.init(truncating: decimal as NSDecimalNumber) } // Convert a UInt8 array to a UInt64 - init(_ bytes: [UInt8]) { + fileprivate init(_ bytes: [UInt8]) { precondition(bytes.count <= MemoryLayout.size) var value: UInt64 = 0 for byte in bytes { @@ -24,7 +24,7 @@ private extension MutableCollection where Element == UInt8, Index == Int { /// /// - Parameter amount: The amount to increment by /// - Returns: The incremented collection - func increment(by amount: Int) -> Self { + fileprivate func increment(by amount: Int) -> Self { var result = self var increment = amount for i in (0.. Date: Thu, 23 May 2019 12:29:16 +1000 Subject: [PATCH 4/7] Update Pods --- Pods | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pods b/Pods index 6174a578a..5abfb1bbc 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 6174a578a93622fc02184ba8983fc809ef7bc0ec +Subproject commit 5abfb1bbce6b21f96ff73f8c1ad47ce70a671977 From 9927fdb620efe2bcd5c1373e6c3a453116677115 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 23 May 2019 14:38:59 +1000 Subject: [PATCH 5/7] Fix build --- Signal/Signal-Info.plist | 2 +- .../src/Loki/API/LokiAPI+Convenience.swift | 45 +++++++++++++++++++ .../src/Loki/API/LokiAPI+SwarmAPI.swift | 4 +- SignalServiceKit/src/Loki/API/LokiAPI.swift | 41 ----------------- ...argetWrapper.swift => TargetWrapper.swift} | 0 .../src/Loki/Utilities/Promise+Hashable.swift | 2 +- 6 files changed, 49 insertions(+), 45 deletions(-) create mode 100644 SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift rename SignalServiceKit/src/Loki/API/{LokiAPI+TargetWrapper.swift => TargetWrapper.swift} (100%) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 258a90a89..7396df8e5 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -7,7 +7,7 @@ CarthageVersion 0.33.0 OSXVersion - 10.14.5 + 10.14.4 WebRTCCommit 1445d719bf05280270e9f77576f80f973fd847f8 M73 diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift b/SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift new file mode 100644 index 000000000..16e489b24 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift @@ -0,0 +1,45 @@ +import PromiseKit + +internal extension LokiAPI { + + private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey" + private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection" + + internal static func getLastMessageHashValue(for target: Target) -> String? { + var result: String? = nil + // Uses a read/write connection because getting the last message hash value also removes expired messages as needed + storage.dbReadWriteConnection.readWrite { transaction in + result = storage.getLastMessageHash(forServiceNode: target.address, transaction: transaction) + } + return result + } + + internal static func setLastMessageHashValue(for target: Target, hashValue: String, expiresAt: UInt64) { + storage.dbReadWriteConnection.readWrite { transaction in + storage.setLastMessageHash(forServiceNode: target.address, hash: hashValue, expiresAt: expiresAt, transaction: transaction) + } + } + + internal static func getReceivedMessageHashValues() -> Set? { + var result: Set? = nil + storage.dbReadConnection.read { transaction in + result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set? + } + return result + } + + internal static func setReceivedMessageHashValues(to receivedMessageHashValues: Set) { + storage.dbReadWriteConnection.readWrite { transaction in + transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) + } + } +} + +internal extension AnyPromise { + + internal static func from(_ promise: Promise) -> AnyPromise { + let result = AnyPromise(promise) + result.retainUntilComplete() + return result + } +} diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift index 6748384e9..d9b2d77ed 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift @@ -3,8 +3,8 @@ import PromiseKit public extension LokiAPI { // MARK: Settings - private static let minimumSnodeCount = 2 - private static let targetSnodeCount = 3 + private static let minimumSnodeCount = 2 // TODO: For debugging purposes + private static let targetSnodeCount = 3 // TODO: For debugging purposes private static let defaultSnodePort: UInt16 = 8080 // MARK: Caching diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index efc0ea18a..0edd31621 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -6,8 +6,6 @@ import PromiseKit // MARK: Settings private static let version = "v1" public static let defaultMessageTTL: UInt64 = 1 * 24 * 60 * 60 - private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey" - private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection" // MARK: Types public typealias RawResponse = Any @@ -118,43 +116,4 @@ import PromiseKit return envelope } } - - // MARK: Convenience - private static func getLastMessageHashValue(for target: Target) -> String? { - var result: String? = nil - // Uses a read/write connection because getting the last message hash value also removes expired messages as needed - storage.dbReadWriteConnection.readWrite { transaction in - result = storage.getLastMessageHash(forServiceNode: target.address, transaction: transaction) - } - return result - } - - private static func setLastMessageHashValue(for target: Target, hashValue: String, expiresAt: UInt64) { - storage.dbReadWriteConnection.readWrite { transaction in - storage.setLastMessageHash(forServiceNode: target.address, hash: hashValue, expiresAt: expiresAt, transaction: transaction) - } - } - - private static func getReceivedMessageHashValues() -> Set? { - var result: Set? = nil - storage.dbReadConnection.read { transaction in - result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set? - } - return result - } - - private static func setReceivedMessageHashValues(to receivedMessageHashValues: Set) { - storage.dbReadWriteConnection.readWrite { transaction in - transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) - } - } -} - -private extension AnyPromise { - - fileprivate static func from(_ promise: Promise) -> AnyPromise { - let result = AnyPromise(promise) - result.retainUntilComplete() - return result - } } diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+TargetWrapper.swift b/SignalServiceKit/src/Loki/API/TargetWrapper.swift similarity index 100% rename from SignalServiceKit/src/Loki/API/LokiAPI+TargetWrapper.swift rename to SignalServiceKit/src/Loki/API/TargetWrapper.swift diff --git a/SignalServiceKit/src/Loki/Utilities/Promise+Hashable.swift b/SignalServiceKit/src/Loki/Utilities/Promise+Hashable.swift index 10c995bb2..0d2ec3bd7 100644 --- a/SignalServiceKit/src/Loki/Utilities/Promise+Hashable.swift +++ b/SignalServiceKit/src/Loki/Utilities/Promise+Hashable.swift @@ -1,6 +1,6 @@ import PromiseKit -public extension Promise : Hashable { +extension Promise : Hashable { public func hash(into hasher: inout Hasher) { let reference = ObjectIdentifier(self).hashValue From 0054489972b68ea6b0e45d78b49bfce14d16b5c8 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 23 May 2019 14:40:55 +1000 Subject: [PATCH 6/7] Update Pods --- Pods | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pods b/Pods index 5abfb1bbc..a3aec3701 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 5abfb1bbce6b21f96ff73f8c1ad47ce70a671977 +Subproject commit a3aec37016db3844567b4274c5a3c07d76382407 From 07fefc168fcadb5e96aa3a076a27b6756cf91b6c Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 23 May 2019 15:09:29 +1000 Subject: [PATCH 7/7] Fix crash --- Signal/src/Loki/LokiP2PServer.swift | 6 ++++-- SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Signal/src/Loki/LokiP2PServer.swift b/Signal/src/Loki/LokiP2PServer.swift index 759b28079..19002e7b8 100644 --- a/Signal/src/Loki/LokiP2PServer.swift +++ b/Signal/src/Loki/LokiP2PServer.swift @@ -8,6 +8,7 @@ private extension GCDWebServerResponse { } private extension GCDWebServerDataRequest { + var truncatedContentType: String? { guard let contentType = contentType else { return nil } guard let substring = contentType.split(separator: ";").first else { return contentType } @@ -89,8 +90,9 @@ final class LokiP2PServer : NSObject { @objc public var serverURL: URL? { return webServer.serverURL } @objc public var isRunning: Bool { return webServer.isRunning } - - @objc @discardableResult func start(onPort port: UInt) -> Bool { + + @discardableResult + @objc func start(onPort port: UInt) -> Bool { guard !webServer.isRunning else { return false } webServer.start(withPort: port, bonjourName: nil) return webServer.isRunning diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift index d9b2d77ed..e99b26862 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift @@ -16,8 +16,8 @@ public extension LokiAPI { @objc public static func loadSwarmCache() { var result: [String:[Target]]? = nil storage.dbReadConnection.read { transaction in - let intermediate = transaction.object(forKey: swarmCacheKey, inCollection: swarmCacheCollection) as! [String:[TargetWrapper]] - result = intermediate.mapValues { $0.map { Target(from: $0) } } + let intermediate = transaction.object(forKey: swarmCacheKey, inCollection: swarmCacheCollection) as! [String:[TargetWrapper]]? + result = intermediate?.mapValues { $0.map { Target(from: $0) } } } swarmCache = result ?? [:] }