Send to p2p server first before falling back to storage server.
This commit is contained in:
parent
2694699e4e
commit
bf1c2f4327
2
Pods
2
Pods
|
@ -1 +1 @@
|
|||
Subproject commit e5dc17be2dc588fde41caa91484aa9e6b9c56ebd
|
||||
Subproject commit 04f0c4baf6a53b9e2e2f161d5da3574b2dbd333d
|
|
@ -12,11 +12,42 @@ public extension LokiAPI {
|
|||
/// When the proof of work was calculated, if applicable.
|
||||
///
|
||||
/// - Note: Expressed as milliseconds since 00:00:00 UTC on 1 January 1970.
|
||||
let timestamp: UInt64?
|
||||
var timestamp: UInt64? = nil
|
||||
/// The base 64 encoded proof of work, if applicable.
|
||||
let nonce: String?
|
||||
var nonce: String? = nil
|
||||
|
||||
public init(destination: String, data: LosslessStringConvertible, ttl: UInt64, timestamp: UInt64?, nonce: String?) {
|
||||
/// Construct a `LokiMessage` from a `SignalMessage`.
|
||||
///
|
||||
/// - Note: `timestamp` is the original message timestamp (i.e. `TSOutgoingMessage.timestamp`).
|
||||
public static func from(signalMessage: SignalMessage, timestamp: UInt64) -> Message? {
|
||||
// To match the desktop application, we have to wrap the data in an envelope and then wrap that in a websocket object
|
||||
do {
|
||||
let wrappedMessage = try LokiMessageWrapper.wrap(message: signalMessage, timestamp: timestamp)
|
||||
let data = wrappedMessage.base64EncodedString()
|
||||
let destination = signalMessage["destination"] as! String
|
||||
var ttl = LokiAPI.defaultMessageTTL
|
||||
if let messageTTL = signalMessage["ttl"] as? UInt, messageTTL > 0 { ttl = UInt64(messageTTL) }
|
||||
return Message(destination: destination, data: data, ttl: ttl, timestamp: nil, nonce: nil)
|
||||
} catch let error {
|
||||
Logger.debug("[Loki] Failed to convert Signal message to Loki message: \(signalMessage)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a basic loki message.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - destination: The destination
|
||||
/// - data: The data
|
||||
/// - ttl: The time to live
|
||||
public init(destination: String, data: LosslessStringConvertible, ttl: UInt64) {
|
||||
self.destination = destination
|
||||
self.data = data
|
||||
self.ttl = ttl
|
||||
}
|
||||
|
||||
/// Private init for setting proof of work. Use `calculatePoW` to get a message with these fields
|
||||
private init(destination: String, data: LosslessStringConvertible, ttl: UInt64, timestamp: UInt64?, nonce: String?) {
|
||||
self.destination = destination
|
||||
self.data = data
|
||||
self.ttl = ttl
|
||||
|
@ -24,34 +55,19 @@ public extension LokiAPI {
|
|||
self.nonce = nonce
|
||||
}
|
||||
|
||||
/// Construct a `LokiMessage` from a `SignalMessage`.
|
||||
/// Calculate the proof of work for this message
|
||||
///
|
||||
/// - Note: `timestamp` is the original message timestamp (i.e. `TSOutgoingMessage.timestamp`).
|
||||
public static func from(signalMessage: SignalMessage, timestamp: UInt64, requiringPoW isPoWRequired: Bool) -> Promise<Message> {
|
||||
/// - Returns: This will return a promise with a new message which contains the proof of work
|
||||
public func calculatePoW() -> Promise<Message> {
|
||||
// To match the desktop application, we have to wrap the data in an envelope and then wrap that in a websocket object
|
||||
return Promise<Message> { seal in
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
do {
|
||||
let wrappedMessage = try LokiMessageWrapper.wrap(message: signalMessage, timestamp: timestamp)
|
||||
let data = wrappedMessage.base64EncodedString()
|
||||
let destination = signalMessage["destination"] as! String
|
||||
var ttl = LokiAPI.defaultMessageTTL
|
||||
if let messageTTL = signalMessage["ttl"] as? UInt, messageTTL > 0 { ttl = UInt64(messageTTL) }
|
||||
if isPoWRequired {
|
||||
// The storage server takes a time interval in milliseconds
|
||||
let now = NSDate.ows_millisecondTimeStamp()
|
||||
if let nonce = ProofOfWork.calculate(data: data, pubKey: destination, timestamp: now, ttl: ttl) {
|
||||
let result = Message(destination: destination, data: data, ttl: ttl, timestamp: now, nonce: nonce)
|
||||
seal.fulfill(result)
|
||||
} else {
|
||||
seal.reject(Error.proofOfWorkCalculationFailed)
|
||||
}
|
||||
} else {
|
||||
let result = Message(destination: destination, data: data, ttl: ttl, timestamp: nil, nonce: nil)
|
||||
seal.fulfill(result)
|
||||
}
|
||||
} catch let error {
|
||||
seal.reject(error)
|
||||
let now = NSDate.ows_millisecondTimeStamp()
|
||||
if let nonce = ProofOfWork.calculate(data: self.data as! String, pubKey: self.destination, timestamp: now, ttl: self.ttl) {
|
||||
let result = Message(destination: self.destination, data: self.data, ttl: self.ttl, timestamp: now, nonce: nonce)
|
||||
seal.fulfill(result)
|
||||
} else {
|
||||
seal.reject(Error.proofOfWorkCalculationFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,20 @@ extension LokiAPI {
|
|||
private static let messageSender: MessageSender = SSKEnvironment.shared.messageSender
|
||||
internal static var ourP2PAddress: Target? = nil
|
||||
|
||||
/// This is where we store the p2p details of our contacts
|
||||
internal static var contactP2PDetails = [String: Target]()
|
||||
|
||||
/// Set the Contact p2p details
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - pubKey: The public key of the contact
|
||||
/// - address: The contacts p2p address
|
||||
/// - port: The contacts p2p port
|
||||
@objc public static func setContactP2PDetails(forContact pubKey: String, address: String, port: UInt32) {
|
||||
let target = Target(address: address, port: port)
|
||||
contactP2PDetails[pubKey] = target
|
||||
}
|
||||
|
||||
/// Set our local P2P address
|
||||
///
|
||||
/// - Parameter url: The url to our local server
|
||||
|
|
|
@ -29,9 +29,13 @@ import PromiseKit
|
|||
/// Only applicable to snode targets as proof of work isn't required for P2P messaging.
|
||||
case proofOfWorkCalculationFailed
|
||||
|
||||
// Failed to send the message'
|
||||
case internalError
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .proofOfWorkCalculationFailed: return NSLocalizedString("Failed to calculate proof of work.", comment: "")
|
||||
case .internalError: return "Failed while trying to send message"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,18 +65,39 @@ import PromiseKit
|
|||
}
|
||||
|
||||
public static func sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, timestamp: UInt64) -> Promise<Set<Promise<RawResponse>>> {
|
||||
let isP2PMessagingPossible = false
|
||||
return Message.from(signalMessage: signalMessage, timestamp: timestamp, requiringPoW: !isP2PMessagingPossible).then(sendMessage)
|
||||
guard let message = Message.from(signalMessage: signalMessage, timestamp: timestamp) else {
|
||||
return Promise(error: Error.internalError)
|
||||
}
|
||||
|
||||
// Send message through the storage server
|
||||
// We put this here because `recover` expects `Promise<Set<Promise<RawResponse>>>`
|
||||
let sendThroughStorageServer: () -> Promise<Set<Promise<RawResponse>>> = { () in
|
||||
return message.calculatePoW().then { powMessage -> Promise<Set<Promise<RawResponse>>> in
|
||||
let snodes = getTargetSnodes(for: powMessage.destination)
|
||||
return sendMessage(powMessage, targets: snodes)
|
||||
}
|
||||
}
|
||||
|
||||
// By default our p2p throws and we recover by sending to storage server
|
||||
var p2pPromise: Promise<Set<Promise<RawResponse>>> = Promise(error: Error.internalError)
|
||||
// TODO: probably only send to p2p if user is online or we are pinging them
|
||||
// p2pDetails && (isPing || peerIsOnline)
|
||||
if let p2pDetails = contactP2PDetails[destination] {
|
||||
p2pPromise = sendMessage(message, targets: [p2pDetails])
|
||||
}
|
||||
|
||||
// If we have the p2p details then send message to that
|
||||
// If that failes then fallback to storage server
|
||||
return p2pPromise.recover { _ in return sendThroughStorageServer() }
|
||||
}
|
||||
|
||||
public static func sendMessage(_ lokiMessage: Message) -> Promise<Set<Promise<RawResponse>>> {
|
||||
let isP2PMessagingPossible = false
|
||||
if isP2PMessagingPossible {
|
||||
// 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) }
|
||||
}
|
||||
internal static func sendMessage(_ lokiMessage: Message, targets: [Target]) -> Promise<Set<Promise<RawResponse>>> {
|
||||
return sendMessage(lokiMessage, targets: Promise.wrap(targets))
|
||||
}
|
||||
|
||||
internal static func sendMessage(_ lokiMessage: Message, targets: Promise<[Target]>) -> Promise<Set<Promise<RawResponse>>> {
|
||||
let parameters = lokiMessage.toJSON()
|
||||
return targets.mapValues { invoke(.sendMessage, on: $0, with: parameters).recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) }.map { Set($0) }
|
||||
}
|
||||
|
||||
public static func ping(_ hexEncodedPublicKey: String) -> Promise<Set<Promise<RawResponse>>> {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import PromiseKit
|
||||
|
||||
public extension Promise {
|
||||
static func wrap(_ value: T) -> Promise<T> {
|
||||
return Promise<T> { resolver in
|
||||
resolver.fulfill(value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -432,6 +432,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
[self.primaryStorage setPreKeyBundle:bundle forContact:envelope.source transaction:transaction];
|
||||
}
|
||||
|
||||
// Loki: Check if we got p2p address
|
||||
if (contentProto.lokiAddressMessage) {
|
||||
[LokiAPI setContactP2PDetailsForContact:envelope.source address:contentProto.lokiAddressMessage.ptpAddress port:contentProto.lokiAddressMessage.ptpPort];
|
||||
}
|
||||
|
||||
if (contentProto.syncMessage) {
|
||||
[self throws_handleIncomingEnvelope:envelope
|
||||
|
|
Loading…
Reference in New Issue