From 9d7cec90e1012a5273f33cd2710f5f5b3169f783 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 21 May 2019 13:26:51 +1000 Subject: [PATCH] WIP --- SignalServiceKit/src/Loki/API/LokiAPI.swift | 82 ++++++++++++--------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 748fcf45d..84da0a506 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -3,18 +3,19 @@ import PromiseKit @objc public final class LokiAPI : NSObject { private static let version = "v1" + private static let targetSnodeCount = 2 public static let defaultMessageTTL: UInt64 = 4 * 24 * 60 * 60 // MARK: Types - private enum Method : String { - case getMessages = "retrieve" - case sendMessage = "store" - case getSwarm = "get_snodes_for_pubkey" - } - - public struct Target : Hashable { + private struct Target : Hashable { let address: String let port: UInt16 + + enum Method : String { + case getSwarm = "get_snodes_for_pubkey" + case getMessages = "retrieve" + case sendMessage = "store" + } } public typealias RawResponse = Any @@ -32,37 +33,36 @@ import PromiseKit // MARK: Lifecycle override private init() { } - // MARK: API - private static func invoke(_ method: Method, on target: Target, with parameters: [String:Any] = [:]) -> Promise { + // MARK: Internal API + private static func invoke(_ method: Target.Method, on target: Target, with 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 } } - public static func getRandomSnode() -> Promise { + private static func getRandomSnode() -> Promise { return Promise { seal in seal.fulfill(Target(address: "http://13.238.53.205", port: 8080)) // TODO: Temporary } } - public static func getMessages() -> Promise<[SSKProtoEnvelope]> { - let parameters = [ - "pubKey" : OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey, - "lastHash" : "" // TODO: Implement - ] - return getRandomSnode().then { invoke(.getMessages, on: $0, with: parameters) }.map { rawResponse in // TODO: Use getSwarm() - guard let json = rawResponse as? JSON, let messages = json["messages"] as? [JSON] else { return [] } - return messages.compactMap { message in - guard let base64EncodedData = message["data"] as? String, let data = Data(base64Encoded: base64EncodedData) else { - Logger.warn("[Loki] Failed to decode data for message: \(message).") - return nil - } - guard let envelope = try? unwrap(data: data) else { - Logger.warn("[Loki] Failed to unwrap data for message: \(message).") - return nil - } - return envelope - } + private static func getSwarm(for hexEncodedPublicKey: String) -> Promise> { + return getRandomSnode().then { invoke(.getSwarm, on: $0, with: [ "pubKey" : hexEncodedPublicKey ]) }.map { rawResponse in return [] } // TODO: Parse targets from raw response + } + + private static func getTargetSnodes(for hexEncodedPublicKey: String) -> Promise> { + return getSwarm(for: hexEncodedPublicKey).map { Set(Array($0).shuffled().prefix(targetSnodeCount)) } + } + + // MARK: Public API + public typealias MessagesPromise = Promise<[SSKProtoEnvelope]> + + public static func getMessages() -> Promise<[MessagesPromise]> { + let hexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + let lastHash = "" // TODO: Implement + let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey, "lastHash" : "" ] + return getTargetSnodes(for: hexEncodedPublicKey).mapValues { targetSnode in + return invoke(.getMessages, on: targetSnode, with: parameters).map { parseProtoEnvelopes(from: $0) } } } @@ -74,19 +74,29 @@ import PromiseKit return getRandomSnode().then { invoke(.sendMessage, on: $0, with: [ "pubKey" : hexEncodedPublicKey ]) } // TODO: Use getSwarm() and figure out correct parameters } - public static func getSwarm(for hexEncodedPublicKey: String) -> Promise> { - return getRandomSnode().then { invoke(.getSwarm, on: $0, with: [ "pubKey" : hexEncodedPublicKey ]) }.map { rawResponse in return [] } // TODO: Parse targets from raw response - } - - // MARK: Obj-C API + // MARK: Public API (Obj-C) @objc public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, timestamp: UInt64, requiringPoW isPoWRequired: Bool) -> AnyPromise { - let promise = Message.from(signalMessage: signalMessage, timestamp: timestamp, requiringPoW: isPoWRequired) - .then(sendMessage) - .recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) + let promise = Message.from(signalMessage: signalMessage, timestamp: timestamp, requiringPoW: isPoWRequired).then(sendMessage).recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) let anyPromise = AnyPromise(promise) anyPromise.retainUntilComplete() return anyPromise } + + // MARK: Convenience + private static func parseProtoEnvelopes(from rawResponse: Any) -> [SSKProtoEnvelope] { + guard let json = rawResponse as? JSON, let messages = json["messages"] as? [JSON] else { return [] } + return messages.compactMap { message in + guard let base64EncodedData = message["data"] as? String, let data = Data(base64Encoded: base64EncodedData) else { + Logger.warn("[Loki] Failed to decode data for message: \(message).") + return nil + } + guard let envelope = try? unwrap(data: data) else { + Logger.warn("[Loki] Failed to unwrap data for message: \(message).") + return nil + } + return envelope + } + } } // MARK: - Convenience