diff --git a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift index d6897e1ff..c61a64cc4 100644 --- a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift +++ b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift @@ -3,6 +3,7 @@ import Foundation import GRDB import SessionUtilitiesKit +import SessionSnodeKit public enum DisappearingMessagesJob: JobExecutor { public static let maxFailureCount: Int = -1 @@ -67,6 +68,16 @@ public extension DisappearingMessagesJob { } @discardableResult static func updateNextRunIfNeeded(_ db: Database, interactionIds: [Int64], startedAtMs: Double) -> Job? { + let interactionsByExpiresInSeconds: [TimeInterval?: [Interaction]]? = try? Interaction + .filter(interactionIds.contains(Interaction.Columns.id)) + .filter( + Interaction.Columns.expiresInSeconds != nil && + Interaction.Columns.expiresStartedAtMs == nil + ) + .fetchAll(db) + .grouped(by: \.expiresInSeconds) + + // Update the expiring messages expiresStartedAtMs value let changeCount: Int? = try? Interaction .filter(interactionIds.contains(Interaction.Columns.id)) @@ -79,6 +90,18 @@ public extension DisappearingMessagesJob { // If there were no changes then none of the provided `interactionIds` are expiring messages guard (changeCount ?? 0) > 0 else { return nil } + interactionsByExpiresInSeconds?.forEach { expiresInSeconds, interactions in + let serverHashes = interactions.compactMap { $0.serverHash } + guard let expiresInSeconds = expiresInSeconds, !serverHashes.isEmpty else { return } + + SnodeAPI.updateExpiry( + publicKey: getUserHexEncodedPublicKey(db), + updatedExpiryMs: Int64(ceil(startedAtMs + expiresInSeconds)), + serverHashes: serverHashes + ) + .retainUntilComplete() + } + return updateNextRunIfNeeded(db) } diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index db0d2903a..6e523b294 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -728,25 +728,30 @@ public final class SnodeAPI { public static func updateExpiry( publicKey: String, - edKeyPair: Box.KeyPair, - updatedExpiryMs: UInt64, + updatedExpiryMs: Int64, serverHashes: [String] ) -> Promise<[String: (hashes: [String], expiry: UInt64)]> { + guard let userED25519KeyPair = Identity.fetchUserEd25519KeyPair() else { + return Promise(error: SnodeAPIError.noKeyPair) + } + let publicKey = (Features.useTestnet ? publicKey.removingIdPrefixIfNeeded() : publicKey) + let updatedExpiryMsWithNetworkOffset: UInt64 = UInt64(updatedExpiryMs + SnodeAPI.clockOffset.wrappedValue) + return attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { getSwarm(for: publicKey) .then2 { swarm -> Promise<[String: (hashes: [String], expiry: UInt64)]> in // "expire" || expiry || messages[0] || ... || messages[N] let verificationBytes = SnodeAPIEndpoint.expire.rawValue.bytes - .appending(contentsOf: "\(updatedExpiryMs)".data(using: .ascii)?.bytes) + .appending(contentsOf: "\(updatedExpiryMsWithNetworkOffset)".data(using: .ascii)?.bytes) .appending(contentsOf: serverHashes.joined().bytes) guard let snode = swarm.randomElement(), let signature = sodium.sign.signature( message: verificationBytes, - secretKey: edKeyPair.secretKey + secretKey: userED25519KeyPair.secretKey ) else { throw SnodeAPIError.signingFailed @@ -754,8 +759,8 @@ public final class SnodeAPI { let parameters: JSON = [ "pubkey" : publicKey, - "pubkey_ed25519" : edKeyPair.publicKey.toHexString(), - "expiry": updatedExpiryMs, + "pubkey_ed25519" : userED25519KeyPair.publicKey.toHexString(), + "expiry": updatedExpiryMsWithNetworkOffset, "messages": serverHashes, "signature": signature.toBase64() ]