From 5f6da0739e39936249706d52648918b481c29f8b Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Tue, 3 Aug 2021 10:26:18 +1000 Subject: [PATCH] delete message on storage server --- Session/Conversations/ConversationViewItem.m | 10 +++- SessionSnodeKit/SnodeAPI.swift | 54 ++++++++++---------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/Session/Conversations/ConversationViewItem.m b/Session/Conversations/ConversationViewItem.m index a569caf6e..e9db68a4e 100644 --- a/Session/Conversations/ConversationViewItem.m +++ b/Session/Conversations/ConversationViewItem.m @@ -1010,11 +1010,17 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) }) retainUntilComplete]; } else { NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:groupThread.groupModel.groupId]; - [SNSnodeAPI deleteMessageForPublickKey:groupPublicKey serverHash:message.serverHash]; + [[SNSnodeAPI deleteMessageForPublickKey:groupPublicKey serverHashes:@[message.serverHash]].catch(^(NSError *error) { + // Roll back + [self.interaction save]; + }) retainUntilComplete]; } } else { TSContactThread *contactThread = (TSContactThread *)self.interaction.thread; - [SNSnodeAPI deleteMessageForPublickKey:contactThread.contactSessionID serverHash:message.serverHash]; + [[SNSnodeAPI deleteMessageForPublickKey:contactThread.contactSessionID serverHashes:@[message.serverHash]].catch(^(NSError *error) { + // Roll back + [self.interaction save]; + }) retainUntilComplete]; } } diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index 10cadd7ab..6512ffbfc 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -454,52 +454,54 @@ public final class SnodeAPI : NSObject { return promise } - @objc(deleteMessageForPublickKey:serverHash:) - public static func objc_deleteMessage(publicKey: String, serverHash: String) -> AnyPromise { - AnyPromise.from(deleteMessage(publicKey: publicKey, serverHash: serverHash)) + @objc(deleteMessageForPublickKey:serverHashes:) + public static func objc_deleteMessage(publicKey: String, serverHashes: [String]) -> AnyPromise { + AnyPromise.from(deleteMessage(publicKey: publicKey, serverHashes: serverHashes)) } - public static func deleteMessage(publicKey: String, serverHash: String) -> Promise { + public static func deleteMessage(publicKey: String, serverHashes: [String]) -> Promise<[String:Bool]> { let storage = SNSnodeKitConfiguration.shared.storage guard let userX25519PublicKey = storage.getUserPublicKey(), let userED25519KeyPair = storage.getUserED25519KeyPair() else { return Promise(error: Error.noKeyPair) } let publicKey = Features.useTestnet ? publicKey.removing05PrefixIfNeeded() : publicKey - let (promise, seal) = Promise.pending() - Threading.workQueue.async { - getTargetSnodes(for: publicKey).map2 { targetSnodes in - let snode = targetSnodes.first! - let verificationData = (Snode.Method.deleteMessage.rawValue + serverHash).data(using: String.Encoding.utf8)! + return attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { + getSwarm(for: publicKey).then2 { swarm -> Promise<[String:Bool]> in + let snode = swarm.randomElement()! + let verificationData = (Snode.Method.deleteMessage.rawValue + serverHashes.joined(separator: "")).data(using: String.Encoding.utf8)! guard let signature = sodium.sign.signature(message: Bytes(verificationData), secretKey: userED25519KeyPair.secretKey) else { throw Error.signingFailed } let parameters: JSON = [ "pubkey" : userX25519PublicKey, "pubkey_ed25519" : userED25519KeyPair.publicKey.toHexString(), - "messages": [serverHash], + "messages": serverHashes, "signature": signature.toBase64()! ] return attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { - invoke(.deleteMessage, on: snode, associatedWith: publicKey, parameters: parameters).map2{ rawResponse -> RawResponse in - guard let json = rawResponse as? JSON else { throw HTTP.Error.invalidJSON } - var result = false - let isFailed = json["failed"] as? Bool ?? false - if !isFailed { - guard let hashes = json["deleted"] as? [String], let signature = json["signature"] as? String else { throw HTTP.Error.invalidJSON } - // The signature format is ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) - let verificationData = (publicKey + serverHash + hashes.joined(separator: "")).data(using: String.Encoding.utf8)! - let isValid = sodium.sign.verify(message: Bytes(verificationData), publicKey: Bytes(Data(hex: snode.publicKeySet.ed25519Key)), signature: Bytes(Data(base64Encoded: signature)!)) - result = isValid - } else { - if let reason = json["reason"] as? String, let statusCode = json["code"] as? String { - SNLog("Couldn't delete message with hash: \(serverHash) due to error: \(reason) (\(statusCode)).") + invoke(.deleteMessage, on: snode, associatedWith: publicKey, parameters: parameters).map2{ rawResponse -> [String:Bool] in + guard let json = rawResponse as? JSON, let swarm = json["swarm"] as? JSON else { throw HTTP.Error.invalidJSON } + var result: [String:Bool] = [:] + for (snodePublicKey, rawJSON) in swarm { + guard let json = rawJSON as? JSON else { throw HTTP.Error.invalidJSON } + let isFailed = json["failed"] as? Bool ?? false + if !isFailed { + guard let hashes = json["deleted"] as? [String], let signature = json["signature"] as? String else { throw HTTP.Error.invalidJSON } + // The signature format is ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) + let verificationData = (userX25519PublicKey + serverHashes.joined(separator: "") + hashes.joined(separator: "")).data(using: String.Encoding.utf8)! + let isValid = sodium.sign.verify(message: Bytes(verificationData), publicKey: Bytes(Data(hex: snodePublicKey)), signature: Bytes(Data(base64Encoded: signature)!)) + result[snodePublicKey] = isValid } else { - SNLog("Couldn't delete message with hash: \(serverHash).") + if let reason = json["reason"] as? String, let statusCode = json["code"] as? String { + SNLog("Couldn't delete data from: \(snodePublicKey) due to error: \(reason) (\(statusCode)).") + } else { + SNLog("Couldn't delete data from: \(snodePublicKey).") + } + result[snodePublicKey] = false } } return result } } - }.done2 { seal.fulfill($0) }.catch2 { seal.reject($0) } + } } - return promise } /// Clears all the user's data from their swarm. Returns a dictionary of snode public key to deletion confirmation.