diff --git a/Session/Calls/Call Management/SessionCall.swift b/Session/Calls/Call Management/SessionCall.swift index 1fc8620a5..a9ccbcdf0 100644 --- a/Session/Calls/Call Management/SessionCall.swift +++ b/Session/Calls/Call Management/SessionCall.swift @@ -206,7 +206,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate { let thread: SessionThread = try? SessionThread.fetchOne(db, id: sessionId) else { return } - let timestampMs: Int64 = Int64(floor(Date().timeIntervalSince1970 * 1000)) + let timestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs() let message: CallMessage = CallMessage( uuid: self.uuid, kind: .preOffer, diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 83cbbb45a..7758c398e 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -409,7 +409,7 @@ extension ConversationVC: // flags appropriately let threadId: String = self.viewModel.threadData.threadId let oldThreadShouldBeVisible: Bool = (self.viewModel.threadData.threadShouldBeVisible == true) - let sentTimestampMs: Int64 = Int64(floor((Date().timeIntervalSince1970 * 1000))) + let sentTimestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs() let linkPreviewDraft: LinkPreviewDraft? = snInputView.linkPreviewInfo?.draft let quoteModel: QuotedReplyModel? = snInputView.quoteDraftInfo?.model @@ -534,7 +534,7 @@ extension ConversationVC: // flags appropriately let threadId: String = self.viewModel.threadData.threadId let oldThreadShouldBeVisible: Bool = (self.viewModel.threadData.threadShouldBeVisible == true) - let sentTimestampMs: Int64 = Int64(floor((Date().timeIntervalSince1970 * 1000))) + let sentTimestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs() // If this was a message request then approve it approveMessageRequestIfNeeded( @@ -640,7 +640,7 @@ extension ConversationVC: threadVariant: threadVariant, threadIsMessageRequest: threadIsMessageRequest, direction: .outgoing, - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ) if needsToStartTypingIndicator { @@ -1219,7 +1219,7 @@ extension ConversationVC: guard !threadIsMessageRequest else { return } // Perform local rate limiting (don't allow more than 20 reactions within 60 seconds) - let sentTimestamp: Int64 = Int64(floor(Date().timeIntervalSince1970 * 1000)) + let sentTimestamp: Int64 = SnodeAPI.currentOffsetTimestampMs() let recentReactionTimestamps: [Int64] = General.cache.wrappedValue.recentReactionTimestamps guard @@ -2044,7 +2044,7 @@ extension ConversationVC: // Create URL let directory: String = OWSTemporaryDirectory() - let fileName: String = "\(Int64(floor(Date().timeIntervalSince1970 * 1000))).m4a" + let fileName: String = "\(SnodeAPI.currentOffsetTimestampMs()).m4a" let url: URL = URL(fileURLWithPath: directory).appendingPathComponent(fileName) // Set up audio session @@ -2285,7 +2285,7 @@ extension ConversationVC { for: self.viewModel.threadData.threadId, threadVariant: self.viewModel.threadData.threadVariant, isNewThread: false, - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ) } diff --git a/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift b/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift index aace471aa..886cc326a 100644 --- a/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift +++ b/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift @@ -168,7 +168,7 @@ class ThreadDisappearingMessagesViewModel: SessionTableViewModel () ) { // The 'backgroundTask' gets captured and cleared within the 'completion' block - let timestampNowMs: TimeInterval = ceil(Date().timeIntervalSince1970 * 1000) + let timestampNowMs: TimeInterval = TimeInterval(SnodeAPI.currentOffsetTimestampMs()) var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function) let updatedJob: Job? = Storage.shared.write { db in diff --git a/SessionMessagingKit/Messages/Message.swift b/SessionMessagingKit/Messages/Message.swift index bac6dbb5e..1f239920c 100644 --- a/SessionMessagingKit/Messages/Message.swift +++ b/SessionMessagingKit/Messages/Message.swift @@ -259,7 +259,10 @@ public extension Message { return try processRawReceivedMessage( db, envelope: envelope, - serverExpirationTimestamp: (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds), + serverExpirationTimestamp: ( + (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) + + ControlMessageProcessRecord.defaultExpirationSeconds + ), serverHash: serverHash, handleClosedGroupKeyUpdateMessages: true ) @@ -275,7 +278,10 @@ public extension Message { let processedMessage: ProcessedMessage? = try processRawReceivedMessage( db, envelope: envelope, - serverExpirationTimestamp: (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds), + serverExpirationTimestamp: ( + (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) + + ControlMessageProcessRecord.defaultExpirationSeconds + ), serverHash: nil, handleClosedGroupKeyUpdateMessages: false ) @@ -407,7 +413,7 @@ public extension Message { let count: Int64 = rawReaction.you ? rawReaction.count - 1 : rawReaction.count - let timestampMs: Int64 = Int64(floor((Date().timeIntervalSince1970 * 1000))) + let timestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs() let maxLength: Int = shouldAddSelfReaction ? 4 : 5 let desiredReactorIds: [String] = reactors .filter { $0 != blindedUserPublicKey && $0 != userPublicKey } // Remove current user for now, will add back if needed diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift index 8dc60c126..9f8f652db 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift @@ -4,6 +4,7 @@ import Foundation import GRDB import WebRTC import SessionUtilitiesKit +import SessionSnodeKit extension MessageReceiver { public static func handleCallMessage(_ db: Database, message: CallMessage) throws { @@ -189,7 +190,7 @@ extension MessageReceiver { body: String(data: messageInfoData, encoding: .utf8), timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ) .inserted(db) @@ -235,7 +236,7 @@ extension MessageReceiver { ) let timestampMs: Int64 = ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) guard let messageInfoData: Data = try? JSONEncoder().encode(messageInfo) else { return nil } diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ClosedGroups.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ClosedGroups.swift index 5c5620a81..669e46de0 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ClosedGroups.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ClosedGroups.swift @@ -4,6 +4,7 @@ import Foundation import GRDB import Sodium import SessionUtilitiesKit +import SessionSnodeKit extension MessageReceiver { public static func handleClosedGroupControlMessage(_ db: Database, _ message: ClosedGroupControlMessage) throws { @@ -135,7 +136,7 @@ extension MessageReceiver { threadId: groupPublicKey, publicKey: Data(encryptionKeyPair.publicKey), secretKey: Data(encryptionKeyPair.secretKey), - receivedTimestamp: Date().timeIntervalSince1970 + receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) ).insert(db) // Start polling @@ -196,7 +197,7 @@ extension MessageReceiver { threadId: groupPublicKey, publicKey: proto.publicKey.removingIdPrefixIfNeeded(), secretKey: proto.privateKey, - receivedTimestamp: Date().timeIntervalSince1970 + receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) ).insert(db) } catch { @@ -231,7 +232,7 @@ extension MessageReceiver { .infoMessage(db, sender: sender), timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ).inserted(db) } @@ -307,7 +308,7 @@ extension MessageReceiver { .infoMessage(db, sender: sender), timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ).inserted(db) } @@ -383,7 +384,7 @@ extension MessageReceiver { .infoMessage(db, sender: sender), timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ).inserted(db) } @@ -461,7 +462,7 @@ extension MessageReceiver { .infoMessage(db, sender: sender), timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ).inserted(db) } diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+DataExtractionNotification.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+DataExtractionNotification.swift index fd74915a9..a28346f6d 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+DataExtractionNotification.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+DataExtractionNotification.swift @@ -2,6 +2,7 @@ import Foundation import GRDB +import SessionSnodeKit extension MessageReceiver { internal static func handleDataExtractionNotification(_ db: Database, message: DataExtractionNotification) throws { @@ -24,7 +25,7 @@ extension MessageReceiver { }(), timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ).inserted(db) } diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift index d16adbe95..06ee54eb3 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift @@ -4,6 +4,7 @@ import Foundation import GRDB import SignalCoreKit import SessionUtilitiesKit +import SessionSnodeKit extension MessageReceiver { internal static func handleMessageRequestResponse( @@ -123,7 +124,7 @@ extension MessageReceiver { variant: .infoMessageRequestAccepted, timestampMs: ( message.sentTimestamp.map { Int64($0) } ?? - Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.currentOffsetTimestampMs() ) ).inserted(db) } diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift index bc8d52a0b..0d219b5b2 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift @@ -6,6 +6,7 @@ import Sodium import Curve25519Kit import PromiseKit import SessionUtilitiesKit +import SessionSnodeKit extension MessageSender { public static var distributingKeyPairs: Atomic<[String: [ClosedGroupKeyPair]]> = Atomic([:]) @@ -24,7 +25,7 @@ extension MessageSender { let membersAsData = members.map { Data(hex: $0) } let admins = [ userPublicKey ] let adminsAsData = admins.map { Data(hex: $0) } - let formationTimestamp: TimeInterval = Date().timeIntervalSince1970 + let formationTimestamp: TimeInterval = (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) let thread: SessionThread = try SessionThread .fetchOrCreate(db, id: groupPublicKey, variant: .closedGroup) try ClosedGroup( @@ -91,7 +92,7 @@ extension MessageSender { threadId: groupPublicKey, publicKey: encryptionKeyPair.publicKey, secretKey: encryptionKeyPair.privateKey, - receivedTimestamp: Date().timeIntervalSince1970 + receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) ).insert(db) // Notify the PN server @@ -110,7 +111,7 @@ extension MessageSender { threadId: thread.id, authorId: userPublicKey, variant: .infoClosedGroupCreated, - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ).inserted(db) // Start polling @@ -142,7 +143,7 @@ extension MessageSender { threadId: closedGroup.threadId, publicKey: legacyNewKeyPair.publicKey, secretKey: legacyNewKeyPair.privateKey, - receivedTimestamp: Date().timeIntervalSince1970 + receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) ) // Distribute it @@ -230,7 +231,7 @@ extension MessageSender { body: ClosedGroupControlMessage.Kind .nameChange(name: name) .infoMessage(db, sender: userPublicKey), - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ).inserted(db) guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved } @@ -330,7 +331,7 @@ extension MessageSender { body: ClosedGroupControlMessage.Kind .membersAdded(members: addedMembers.map { Data(hex: $0) }) .infoMessage(db, sender: userPublicKey), - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ).inserted(db) guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved } @@ -431,7 +432,7 @@ extension MessageSender { body: ClosedGroupControlMessage.Kind .membersRemoved(members: removedMembers.map { Data(hex: $0) }) .infoMessage(db, sender: userPublicKey), - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ).inserted(db) guard let newInteractionId: Int64 = interaction.id else { throw StorageError.objectNotSaved } @@ -496,7 +497,7 @@ extension MessageSender { body: ClosedGroupControlMessage.Kind .memberLeft .infoMessage(db, sender: userPublicKey), - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) + timestampMs: SnodeAPI.currentOffsetTimestampMs() ).inserted(db) guard let interactionId: Int64 = interaction.id else { diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift index f3b6f674c..88b9d4ea2 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift @@ -5,6 +5,7 @@ import GRDB import Sodium import SignalCoreKit import SessionUtilitiesKit +import SessionSnodeKit public enum MessageReceiver { private static var lastEncryptionKeyPairRequest: [String: Date] = [:] @@ -144,7 +145,7 @@ public enum MessageReceiver { message.sender = sender message.recipient = userPublicKey message.sentTimestamp = envelope.timestamp - message.receivedTimestamp = UInt64((Date().timeIntervalSince1970) * 1000) + message.receivedTimestamp = SnodeAPI.currentTimestampMs() message.groupPublicKey = groupPublicKey message.openGroupServerMessageId = openGroupMessageServerId.map { UInt64($0) } diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index 547ab9521..297e70732 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -67,7 +67,7 @@ public final class MessageSender { let (promise, seal) = Promise.pending() let userPublicKey: String = getUserHexEncodedPublicKey(db) let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) - let messageSendTimestamp: Int64 = Int64(floor(Date().timeIntervalSince1970 * 1000)) + let messageSendTimestamp: Int64 = SnodeAPI.currentOffsetTimestampMs() // Set the timestamp, sender and recipient message.sentTimestamp = ( @@ -322,7 +322,7 @@ public final class MessageSender { // Set the timestamp, sender and recipient if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set - message.sentTimestamp = UInt64(floor(Date().timeIntervalSince1970 * 1000)) + message.sentTimestamp = SnodeAPI.currentTimestampMs() } switch destination { @@ -472,7 +472,7 @@ public final class MessageSender { // Set the timestamp, sender and recipient if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set - message.sentTimestamp = UInt64(floor(Date().timeIntervalSince1970 * 1000)) + message.sentTimestamp = SnodeAPI.currentTimestampMs() } message.sender = userPublicKey @@ -617,7 +617,7 @@ public final class MessageSender { job: DisappearingMessagesJob.updateNextRunIfNeeded( db, interaction: interaction, - startedAtMs: (Date().timeIntervalSince1970 * 1000) + startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs()) ) ) } @@ -636,7 +636,10 @@ public final class MessageSender { } }(), message: message, - serverExpirationTimestamp: (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds) + serverExpirationTimestamp: ( + (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) + + ControlMessageProcessRecord.defaultExpirationSeconds + ) )?.insert(db) // Sync the message if: diff --git a/SessionMessagingKit/Sending & Receiving/Typing Indicators/TypingIndicators.swift b/SessionMessagingKit/Sending & Receiving/Typing Indicators/TypingIndicators.swift index 9e50e80b8..afebe2c10 100644 --- a/SessionMessagingKit/Sending & Receiving/Typing Indicators/TypingIndicators.swift +++ b/SessionMessagingKit/Sending & Receiving/Typing Indicators/TypingIndicators.swift @@ -3,6 +3,7 @@ import Foundation import GRDB import SessionUtilitiesKit +import SessionSnodeKit public class TypingIndicators { // MARK: - Direction @@ -41,7 +42,7 @@ public class TypingIndicators { self.threadId = threadId self.direction = direction - self.timestampMs = (timestampMs ?? Int64(floor(Date().timeIntervalSince1970 * 1000))) + self.timestampMs = (timestampMs ?? SnodeAPI.currentOffsetTimestampMs()) } fileprivate func start(_ db: Database) { diff --git a/SessionShareExtension/ThreadPickerVC.swift b/SessionShareExtension/ThreadPickerVC.swift index 7db42407d..5fb7aaf2a 100644 --- a/SessionShareExtension/ThreadPickerVC.swift +++ b/SessionShareExtension/ThreadPickerVC.swift @@ -196,7 +196,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView authorId: getUserHexEncodedPublicKey(db), variant: .standardOutgoing, body: body, - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)), + timestampMs: SnodeAPI.currentOffsetTimestampMs(), hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: body), expiresInSeconds: try? DisappearingMessagesConfiguration .select(.durationSeconds) diff --git a/SessionSnodeKit/Database/Models/SnodeReceivedMessageInfo.swift b/SessionSnodeKit/Database/Models/SnodeReceivedMessageInfo.swift index 7addb56e5..336a19de8 100644 --- a/SessionSnodeKit/Database/Models/SnodeReceivedMessageInfo.swift +++ b/SessionSnodeKit/Database/Models/SnodeReceivedMessageInfo.swift @@ -93,7 +93,7 @@ public extension SnodeReceivedMessageInfo { return try SnodeReceivedMessageInfo .select(Column.rowID) .filter(SnodeReceivedMessageInfo.Columns.key == key(for: snode, publicKey: publicKey, namespace: namespace)) - .filter(SnodeReceivedMessageInfo.Columns.expirationDateMs <= (Date().timeIntervalSince1970 * 1000)) + .filter(SnodeReceivedMessageInfo.Columns.expirationDateMs <= SnodeAPI.currentOffsetTimestampMs()) .asRequest(of: Int64.self) .fetchAll(db) } @@ -122,7 +122,7 @@ public extension SnodeReceivedMessageInfo { SnodeReceivedMessageInfo.Columns.wasDeletedOrInvalid == false ) .filter(SnodeReceivedMessageInfo.Columns.key == key(for: snode, publicKey: publicKey, namespace: namespace)) - .filter(SnodeReceivedMessageInfo.Columns.expirationDateMs > (Date().timeIntervalSince1970 * 1000)) + .filter(SnodeReceivedMessageInfo.Columns.expirationDateMs > SnodeAPI.currentOffsetTimestampMs()) .order(SnodeReceivedMessageInfo.Columns.id.desc) .fetchOne(db) diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index db0d2903a..4f0aff7f0 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -23,6 +23,14 @@ public final class SnodeAPI { /// /// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions. public static var clockOffset: Atomic = Atomic(0) + + public static func currentOffsetTimestampMs() -> Int64 { + return ( + Int64(floor(Date().timeIntervalSince1970 * 1000)) + + SnodeAPI.clockOffset.wrappedValue + ) + } + /// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions. public static var swarmCache: Atomic<[String: Set]> = Atomic([:]) @@ -546,7 +554,7 @@ public final class SnodeAPI { let lastHash = SnodeReceivedMessageInfo.fetchLastNotExpired(for: snode, namespace: namespace, associatedWith: publicKey)?.hash ?? "" // Construct signature - let timestamp = UInt64(Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.clockOffset.wrappedValue) + let timestamp = UInt64(SnodeAPI.currentOffsetTimestampMs()) let ed25519PublicKey = userED25519KeyPair.publicKey.toHexString() let namespaceVerificationString = (namespace == defaultNamespace ? "" : String(namespace)) @@ -647,7 +655,7 @@ public final class SnodeAPI { } // Construct signature - let timestamp = UInt64(Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.clockOffset.wrappedValue) + let timestamp = UInt64(SnodeAPI.currentOffsetTimestampMs()) let ed25519PublicKey = userED25519KeyPair.publicKey.toHexString() guard