Made ProofOfWork work in objective c.

Added proof of work calculation when sending message.
pull/6/head
Mikunj 6 years ago
parent de01731316
commit ad0fc7944e

@ -43,7 +43,8 @@ private extension MutableCollection where Element == UInt8, Index == Int {
* This was copied from the desktop messenger. * This was copied from the desktop messenger.
* Ref: libloki/proof-of-work.js * Ref: libloki/proof-of-work.js
*/ */
public enum ProofOfWork { @objc public class ProofOfWork: NSObject {
private override init() {}
// If this changes then we also have to use something other than UInt64 to support the new length // If this changes then we also have to use something other than UInt64 to support the new length
private static let nonceLength = 8 private static let nonceLength = 8
@ -55,36 +56,19 @@ public enum ProofOfWork {
} }
}() }()
public struct Configuration {
var pubKey: String
var data: String
var timestamp: Date
var ttl: Int
var payload: [UInt8] {
let timestampString = String(Int(timestamp.timeIntervalSince1970))
let ttlString = String(ttl)
let payloadString = timestampString + ttlString + pubKey + data
return payloadString.bytes
}
public init(pubKey: String, data: String, timestamp: Date, ttl: Int) {
self.pubKey = pubKey
self.data = data
self.timestamp = timestamp
self.ttl = ttl
}
}
/// Calculate a proof of work with the given configuration /// Calculate a proof of work with the given configuration
/// ///
/// Ref: https://bitmessage.org/wiki/Proof_of_work /// Ref: https://bitmessage.org/wiki/Proof_of_work
/// ///
/// - Parameter config: The configuration /// - Parameters:
/// - data: The message data
/// - pubKey: The message recipient
/// - timestamp: The timestamp
/// - ttl: The message time to live
/// - Returns: A nonce string or nil if it failed /// - Returns: A nonce string or nil if it failed
public static func calculate(with config: Configuration) -> String? { @objc public class func calculate(forData data: String, pubKey: String, timestamp: UInt64, ttl: Int) -> String? {
let payload = config.payload let payload = getPayload(pubKey: pubKey, data: data, timestamp: timestamp, ttl: ttl)
let target = calcTarget(ttl: config.ttl, payloadLength: payload.count, nonceTrials: nonceTrialCount) let target = calcTarget(ttl: ttl, payloadLength: payload.count, nonceTrials: nonceTrialCount)
// Start with the max value // Start with the max value
var trialValue = UInt64.max var trialValue = UInt64.max
@ -105,8 +89,16 @@ public enum ProofOfWork {
return nonce.toBase64() return nonce.toBase64()
} }
/// Get the proof of work payload
private class func getPayload(pubKey: String, data: String, timestamp: UInt64, ttl: Int) -> [UInt8] {
let timestampString = String(timestamp)
let ttlString = String(ttl)
let payloadString = timestampString + ttlString + pubKey + data
return payloadString.bytes
}
/// Calculate the target we need to reach /// Calculate the target we need to reach
private static func calcTarget(ttl: Int, payloadLength: Int, nonceTrials: Int) -> UInt64 { private class func calcTarget(ttl: Int, payloadLength: Int, nonceTrials: Int) -> UInt64 {
let two16 = UInt64(pow(2, 16) - 1) let two16 = UInt64(pow(2, 16) - 1)
let two64 = UInt64(pow(2, 64) - 1) let two64 = UInt64(pow(2, 64) - 1)

@ -915,6 +915,38 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return deviceMessages; return deviceMessages;
} }
- (AnyPromise *)calculateProofOfWorkForDeviceMessages:(NSArray<NSDictionary *> *)deviceMessages
ttl:(NSNumber *)ttl
{
// LOKI: Calculate the proof of work for each device message
NSMutableArray *promises = [[NSMutableArray alloc] init];
for (NSDictionary<NSString *, id> *deviceMessage in deviceMessages) {
AnyPromise *promise = [AnyPromise promiseWithValue:deviceMessage]
.thenOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(NSDictionary<NSString *, id> *message) {
NSTimeInterval timestampInterval = [[NSDate date] timeIntervalSince1970];
NSNumber *timestamp = [NSNumber numberWithDouble:timestampInterval];
NSString *destination = message[@"destination"];
NSString *data = message[@"content"];
NSString *_Nullable nonce = [ProofOfWork calculateForData:data pubKey:destination timestamp:timestamp.unsignedIntegerValue ttl:ttl.integerValue];
// Return our timestamp along with the nonce
// These will help us identify which nonce belongs to which message
return @{
@"destination": destination,
@"deviceId": message[@"destinationDeviceId"],
@"timestamp": timestamp,
@"nonce": nonce
};
});
[promises addObject:promise];
}
// Wait for all the PoW Calculations to finish
return PMKWhen(promises);
}
- (void)sendMessageToRecipient:(OWSMessageSend *)messageSend - (void)sendMessageToRecipient:(OWSMessageSend *)messageSend
{ {
OWSAssertDebug(messageSend); OWSAssertDebug(messageSend);
@ -1087,65 +1119,82 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// linked devices that we don't know about. // linked devices that we don't know about.
OWSLogWarn(@"Sending a message with no device messages."); OWSLogWarn(@"Sending a message with no device messages.");
} }
OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc] initWithLabel:@"Message Send" // TODO: Update message here to show the pow cog icon
requestFactoryBlock:^(SMKUDAccessKey *_Nullable udAccessKey) {
return [OWSRequestFactory submitMessageRequestWithRecipient:recipient.recipientId // LOKI: Calculate the proof of work for each device message
messages:deviceMessages NSNumber *ttl = [NSNumber numberWithInteger:@(4 * 24 * 60 * 60)];
timeStamp:message.timestamp AnyPromise *PoWPromise = [self calculateProofOfWorkForDeviceMessages:deviceMessages ttl:ttl];
udAccessKey:udAccessKey]; [PoWPromise
} .thenOn([OWSDispatch sendingQueue], ^(NSArray *nonceArray) {
udAuthFailureBlock:^{ OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc] initWithLabel:@"Message Send"
// Note the UD auth failure so subsequent retries requestFactoryBlock:^(SMKUDAccessKey *_Nullable udAccessKey) {
// to this recipient also use basic auth. // Loki Changes:
[messageSend setHasUDAuthFailed]; return [OWSRequestFactory submitLokiMessageRequestWithRecipient:recipient.recipientId
} messages:deviceMessages
websocketFailureBlock:^{ nonceArray:nonceArray
// Note the websocket failure so subsequent retries ttl:ttl];
// to this recipient also use REST. /* Original Code:
messageSend.hasWebsocketSendFailed = YES; return [OWSRequestFactory submitMessageRequestWithRecipient:recipient.recipientId
} messages:deviceMessages
recipientId:recipient.recipientId timeStamp:message.timestamp
udAccess:messageSend.udAccess udAccessKey:udAccessKey];
canFailoverUDAuth:NO]; */
[[requestMaker makeRequestObjc] }
.then(^(OWSRequestMakerResult *result) { udAuthFailureBlock:^{
dispatch_async([OWSDispatch sendingQueue], ^{ // Note the UD auth failure so subsequent retries
[self messageSendDidSucceed:messageSend // to this recipient also use basic auth.
deviceMessages:deviceMessages [messageSend setHasUDAuthFailed];
wasSentByUD:result.wasSentByUD }
wasSentByWebsocket:result.wasSentByWebsocket]; websocketFailureBlock:^{
}); // Note the websocket failure so subsequent retries
}) // to this recipient also use REST.
.catch(^(NSError *error) { messageSend.hasWebsocketSendFailed = YES;
dispatch_async([OWSDispatch sendingQueue], ^{ }
NSUInteger statusCode = 0; recipientId:recipient.recipientId
NSData *_Nullable responseData = nil; udAccess:messageSend.udAccess
if ([error.domain isEqualToString:@"SignalServiceKit.RequestMakerUDAuthError"]) { canFailoverUDAuth:NO];
// Try again. return requestMaker;
OWSLogInfo(@"UD request auth failed; failing over to non-UD request."); })
[error setIsRetryable:YES]; .thenOn([OWSDispatch sendingQueue], ^(OWSRequestMaker *requestMaker) {
} else if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) { return [requestMaker makeRequestObjc];
statusCode = error.code; }).then(^(OWSRequestMakerResult *result) {
dispatch_async([OWSDispatch sendingQueue], ^{
NSError *_Nullable underlyingError = error.userInfo[NSUnderlyingErrorKey]; [self messageSendDidSucceed:messageSend
if (underlyingError) { deviceMessages:deviceMessages
responseData wasSentByUD:result.wasSentByUD
= underlyingError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; wasSentByWebsocket:result.wasSentByWebsocket];
} else { });
OWSFailDebug(@"Missing underlying error: %@", error); })
} .catch(^(NSError *error) {
dispatch_async([OWSDispatch sendingQueue], ^{
NSUInteger statusCode = 0;
NSData *_Nullable responseData = nil;
if ([error.domain isEqualToString:@"SignalServiceKit.RequestMakerUDAuthError"]) {
// Try again.
OWSLogInfo(@"UD request auth failed; failing over to non-UD request.");
[error setIsRetryable:YES];
} else if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) {
statusCode = error.code;
NSError *_Nullable underlyingError = error.userInfo[NSUnderlyingErrorKey];
if (underlyingError) {
responseData
= underlyingError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
} else { } else {
OWSFailDebug(@"Unexpected error: %@", error); OWSFailDebug(@"Missing underlying error: %@", error);
} }
} else {
[self messageSendDidFail:messageSend OWSFailDebug(@"Unexpected error: %@", error);
deviceMessages:deviceMessages }
statusCode:statusCode
error:error [self messageSendDidFail:messageSend
responseData:responseData]; deviceMessages:deviceMessages
}); statusCode:statusCode
}) retainUntilComplete]; error:error
responseData:responseData];
});
}) retainUntilComplete];
} }
- (void)messageSendDidSucceed:(OWSMessageSend *)messageSend - (void)messageSendDidSucceed:(OWSMessageSend *)messageSend

@ -59,6 +59,11 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
captchaToken:(nullable NSString *)captchaToken captchaToken:(nullable NSString *)captchaToken
transport:(TSVerificationTransport)transport; transport:(TSVerificationTransport)transport;
+ (TSRequest *)submitLokiMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
nonceArray:(NSArray *)nonceArray
ttl: (NSNumber *)ttl;
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId + (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages messages:(NSArray *)messages
timeStamp:(uint64_t)timeStamp timeStamp:(uint64_t)timeStamp

@ -354,6 +354,58 @@ NS_ASSUME_NONNULL_BEGIN
return [accountAttributes copy]; return [accountAttributes copy];
} }
// LOKI: Convert Signal JSON messages to Loki messages
+ (NSDictionary *)lokiMessagesFromMessages:(NSArray *)messages
nonceArray:(NSArray *)nonceArray
ttl:(NSNumber *)ttl {
NSMutableArray *modifiedMessages = [[NSMutableArray alloc] init];
for (NSDictionary *message in messages) {
NSMutableDictionary *lokiMessage = [[NSMutableDictionary alloc] init];
// Params for our message server
lokiMessage[@"pubKey"] = message[@"destination"];
lokiMessage[@"data"] = message[@"content"];
lokiMessage[@"ttl"] = ttl;
NSDictionary *_Nullable nonce = [self getNonceFromArray:nonceArray forMessage:message];
if (nonce) {
lokiMessage[@"timestamp"] = nonce[@"timestmap"];
lokiMessage[@"nonce"] = nonce[@"nonce"];
}
[modifiedMessages addObject:lokiMessage];
}
return modifiedMessages;
}
+ (NSDictionary *_Nullable)getNonceFromArray:(NSArray *)nonceArray forMessage:(NSDictionary *)message {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"destination == %@ AND deviceId == %d", message[@"destination"], message[@"destinationDeviceId"]];
NSArray *filtered = [nonceArray filteredArrayUsingPredicate:predicate];
return filtered.count > 0 ? [filtered objectAtIndex:0] : nil;
}
// LOKI: This is the function below with our changes
+ (TSRequest *)submitLokiMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
nonceArray:(NSArray *)nonceArray
ttl: (NSNumber *)ttl
{
// NOTE: messages may be empty; See comments in OWSDeviceManager.
OWSAssertDebug(recipientId.length > 0);
NSDictionary *lokiMessages = [self lokiMessagesFromMessages:messages nonceArray:nonceArray ttl:ttl];
NSString *path = [textSecureMessagesAPI stringByAppendingString:recipientId];
NSDictionary *parameters = @{
@"messages" : lokiMessages,
};
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
return request;
}
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId + (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages messages:(NSArray *)messages
timeStamp:(uint64_t)timeStamp timeStamp:(uint64_t)timeStamp

Loading…
Cancel
Save