Merge pull request #761 from mpretty-cyro/fix/use-offset-time

Use service node offset time where possible
This commit is contained in:
RyanZhao 2023-01-10 11:41:54 +11:00 committed by GitHub
commit 1f9e58956c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 113 additions and 66 deletions

View File

@ -206,7 +206,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
let thread: SessionThread = try? SessionThread.fetchOne(db, id: sessionId) let thread: SessionThread = try? SessionThread.fetchOne(db, id: sessionId)
else { return } else { return }
let timestampMs: Int64 = Int64(floor(Date().timeIntervalSince1970 * 1000)) let timestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs()
let message: CallMessage = CallMessage( let message: CallMessage = CallMessage(
uuid: self.uuid, uuid: self.uuid,
kind: .preOffer, kind: .preOffer,

View File

@ -409,7 +409,7 @@ extension ConversationVC:
// flags appropriately // flags appropriately
let threadId: String = self.viewModel.threadData.threadId let threadId: String = self.viewModel.threadData.threadId
let oldThreadShouldBeVisible: Bool = (self.viewModel.threadData.threadShouldBeVisible == true) 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 linkPreviewDraft: LinkPreviewDraft? = snInputView.linkPreviewInfo?.draft
let quoteModel: QuotedReplyModel? = snInputView.quoteDraftInfo?.model let quoteModel: QuotedReplyModel? = snInputView.quoteDraftInfo?.model
@ -534,7 +534,7 @@ extension ConversationVC:
// flags appropriately // flags appropriately
let threadId: String = self.viewModel.threadData.threadId let threadId: String = self.viewModel.threadData.threadId
let oldThreadShouldBeVisible: Bool = (self.viewModel.threadData.threadShouldBeVisible == true) 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 // If this was a message request then approve it
approveMessageRequestIfNeeded( approveMessageRequestIfNeeded(
@ -640,7 +640,7 @@ extension ConversationVC:
threadVariant: threadVariant, threadVariant: threadVariant,
threadIsMessageRequest: threadIsMessageRequest, threadIsMessageRequest: threadIsMessageRequest,
direction: .outgoing, direction: .outgoing,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
) )
if needsToStartTypingIndicator { if needsToStartTypingIndicator {
@ -1219,7 +1219,7 @@ extension ConversationVC:
guard !threadIsMessageRequest else { return } guard !threadIsMessageRequest else { return }
// Perform local rate limiting (don't allow more than 20 reactions within 60 seconds) // 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 let recentReactionTimestamps: [Int64] = General.cache.wrappedValue.recentReactionTimestamps
guard guard
@ -2044,7 +2044,7 @@ extension ConversationVC:
// Create URL // Create URL
let directory: String = OWSTemporaryDirectory() 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) let url: URL = URL(fileURLWithPath: directory).appendingPathComponent(fileName)
// Set up audio session // Set up audio session
@ -2285,7 +2285,7 @@ extension ConversationVC {
for: self.viewModel.threadData.threadId, for: self.viewModel.threadData.threadId,
threadVariant: self.viewModel.threadData.threadVariant, threadVariant: self.viewModel.threadData.threadVariant,
isNewThread: false, isNewThread: false,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
) )
} }

View File

@ -8,6 +8,7 @@
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
#import <SignalCoreKit/NSDate+OWS.h> #import <SignalCoreKit/NSDate+OWS.h>
#import <SessionUtilitiesKit/NSTimer+Proxying.h> #import <SessionUtilitiesKit/NSTimer+Proxying.h>
#import <SessionSnodeKit/SessionSnodeKit.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -75,7 +76,7 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
return; return;
} }
uint64_t nowTimestamp = [NSDate ows_millisecondTimeStamp]; uint64_t nowTimestamp = [SNSnodeAPI currentOffsetTimestampMs];
CGFloat secondsLeft CGFloat secondsLeft
= (self.expirationTimestamp > nowTimestamp ? (self.expirationTimestamp - nowTimestamp) / 1000.f : 0.f); = (self.expirationTimestamp > nowTimestamp ? (self.expirationTimestamp - nowTimestamp) / 1000.f : 0.f);
CGFloat progress = 0.f; CGFloat progress = 0.f;

View File

@ -168,7 +168,7 @@ class ThreadDisappearingMessagesViewModel: SessionTableViewModel<ThreadDisappear
authorId: getUserHexEncodedPublicKey(db), authorId: getUserHexEncodedPublicKey(db),
variant: .infoDisappearingMessagesUpdate, variant: .infoDisappearingMessagesUpdate,
body: config.messageInfoString(with: nil), body: config.messageInfoString(with: nil),
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
) )
.inserted(db) .inserted(db)

View File

@ -622,7 +622,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
threadId: thread.id, threadId: thread.id,
authorId: userId, authorId: userId,
variant: .standardOutgoing, variant: .standardOutgoing,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)), timestampMs: SnodeAPI.currentOffsetTimestampMs(),
expiresInSeconds: try? DisappearingMessagesConfiguration expiresInSeconds: try? DisappearingMessagesConfiguration
.select(.durationSeconds) .select(.durationSeconds)
.filter(id: userId) .filter(id: userId)

View File

@ -532,7 +532,7 @@ class NotificationActionHandler {
authorId: getUserHexEncodedPublicKey(db), authorId: getUserHexEncodedPublicKey(db),
variant: .standardOutgoing, variant: .standardOutgoing,
body: replyText, body: replyText,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)), timestampMs: SnodeAPI.currentOffsetTimestampMs(),
hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: replyText), hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: replyText),
expiresInSeconds: try? DisappearingMessagesConfiguration expiresInSeconds: try? DisappearingMessagesConfiguration
.select(.durationSeconds) .select(.durationSeconds)

View File

@ -5,6 +5,7 @@ import GRDB
import PromiseKit import PromiseKit
import WebRTC import WebRTC
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public protocol WebRTCSessionDelegate: AnyObject { public protocol WebRTCSessionDelegate: AnyObject {
var videoCapturer: RTCVideoCapturer { get } var videoCapturer: RTCVideoCapturer { get }
@ -179,7 +180,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
uuid: uuid, uuid: uuid,
kind: .offer, kind: .offer,
sdps: [ sdp.sdp ], sdps: [ sdp.sdp ],
sentTimestampMs: UInt64(floor(Date().timeIntervalSince1970 * 1000)) sentTimestampMs: UInt64(SnodeAPI.currentOffsetTimestampMs())
), ),
interactionId: nil, interactionId: nil,
in: thread in: thread

View File

@ -1286,7 +1286,7 @@ enum _003_YDBToGRDBMigration: Migration {
// so we can reverse-engineer an approximate timestamp by extracting it from // so we can reverse-engineer an approximate timestamp by extracting it from
// the id (this value is unlikely to match exactly though) // the id (this value is unlikely to match exactly though)
let fallbackTimestamp: UInt64 = legacyJob.id let fallbackTimestamp: UInt64 = legacyJob.id
.map { UInt64($0.prefix("\(Int(Date().timeIntervalSince1970 * 1000))".count)) } .map { UInt64($0.prefix("\(SnodeAPI.currentOffsetTimestampMs())".count)) }
.defaulting(to: 0) .defaulting(to: 0)
let legacyIdentifier: String = identifier( let legacyIdentifier: String = identifier(
for: threadId, for: threadId,
@ -1657,7 +1657,7 @@ enum _003_YDBToGRDBMigration: Migration {
state: .invalid, state: .invalid,
contentType: "", contentType: "",
byteCount: 0, byteCount: 0,
creationTimestamp: Date().timeIntervalSince1970, creationTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000),
sourceFilename: nil, sourceFilename: nil,
downloadUrl: nil, downloadUrl: nil,
localRelativeFilePath: nil, localRelativeFilePath: nil,

View File

@ -1,12 +1,13 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation import Foundation
import AVFAudio
import AVFoundation
import GRDB import GRDB
import PromiseKit import PromiseKit
import SignalCoreKit import SignalCoreKit
import SessionUtilitiesKit import SessionUtilitiesKit
import AVFAudio import SessionSnodeKit
import AVFoundation
public struct Attachment: Codable, Identifiable, Equatable, Hashable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible { public struct Attachment: Codable, Identifiable, Equatable, Hashable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "attachment" } public static var databaseTableName: String { "attachment" }
@ -1114,7 +1115,7 @@ extension Attachment {
state: .uploaded, state: .uploaded,
creationTimestamp: ( creationTimestamp: (
updatedAttachment?.creationTimestamp ?? updatedAttachment?.creationTimestamp ??
Date().timeIntervalSince1970 (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000)
), ),
downloadUrl: "\(FileServerAPI.server)/files/\(fileId)" downloadUrl: "\(FileServerAPI.server)/files/\(fileId)"
) )

View File

@ -3,6 +3,7 @@
import Foundation import Foundation
import GRDB import GRDB
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
/// We can rely on the unique constraints within the `Interaction` table to prevent duplicate `VisibleMessage` /// We can rely on the unique constraints within the `Interaction` table to prevent duplicate `VisibleMessage`
/// values from being processed, but some control messages dont have an associated interaction - this table provides /// values from being processed, but some control messages dont have an associated interaction - this table provides
@ -168,7 +169,10 @@ internal extension ControlMessageProcessRecord {
self.threadId = threadId self.threadId = threadId
self.timestampMs = timestampMs self.timestampMs = timestampMs
self.serverExpirationTimestamp = (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds) self.serverExpirationTimestamp = (
(TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) +
ControlMessageProcessRecord.defaultExpirationSeconds
)
} }
/// This method should only be used for records created during migration from the legacy /// This method should only be used for records created during migration from the legacy
@ -179,7 +183,8 @@ internal extension ControlMessageProcessRecord {
/// clean out these excessive entries after `defaultExpirationSeconds`) /// clean out these excessive entries after `defaultExpirationSeconds`)
static func generateLegacyProcessRecords(_ db: Database, receivedMessageTimestamps: [Int64]) throws { static func generateLegacyProcessRecords(_ db: Database, receivedMessageTimestamps: [Int64]) throws {
let defaultExpirationTimestamp: TimeInterval = ( let defaultExpirationTimestamp: TimeInterval = (
Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) +
ControlMessageProcessRecord.defaultExpirationSeconds
) )
try receivedMessageTimestamps.forEach { timestampMs in try receivedMessageTimestamps.forEach { timestampMs in

View File

@ -3,6 +3,7 @@
import Foundation import Foundation
import GRDB import GRDB
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public struct DisappearingMessagesConfiguration: Codable, Identifiable, Equatable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible { public struct DisappearingMessagesConfiguration: Codable, Identifiable, Equatable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "disappearingMessagesConfiguration" } public static var databaseTableName: String { "disappearingMessagesConfiguration" }
@ -206,7 +207,7 @@ public class SMKDisappearingMessagesConfiguration: NSObject {
authorId: getUserHexEncodedPublicKey(db), authorId: getUserHexEncodedPublicKey(db),
variant: .infoDisappearingMessagesUpdate, variant: .infoDisappearingMessagesUpdate,
body: config.messageInfoString(with: nil), body: config.messageInfoString(with: nil),
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
) )
.inserted(db) .inserted(db)

View File

@ -4,6 +4,7 @@ import Foundation
import GRDB import GRDB
import Sodium import Sodium
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public struct Interaction: Codable, Identifiable, Equatable, FetchableRecord, MutablePersistableRecord, TableRecord, ColumnExpressible { public struct Interaction: Codable, Identifiable, Equatable, FetchableRecord, MutablePersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "interaction" } public static var databaseTableName: String { "interaction" }
@ -298,7 +299,7 @@ public struct Interaction: Codable, Identifiable, Equatable, FetchableRecord, Mu
self.timestampMs = timestampMs self.timestampMs = timestampMs
self.receivedAtTimestampMs = { self.receivedAtTimestampMs = {
switch variant { switch variant {
case .standardIncoming, .standardOutgoing: return Int64(Date().timeIntervalSince1970 * 1000) case .standardIncoming, .standardOutgoing: return SnodeAPI.currentOffsetTimestampMs()
/// For TSInteractions which are not `standardIncoming` and `standardOutgoing` use the `timestampMs` value /// For TSInteractions which are not `standardIncoming` and `standardOutgoing` use the `timestampMs` value
default: return timestampMs default: return timestampMs
@ -458,7 +459,7 @@ public extension Interaction {
job: DisappearingMessagesJob.updateNextRunIfNeeded( job: DisappearingMessagesJob.updateNextRunIfNeeded(
db, db,
interactionIds: interactionIds, interactionIds: interactionIds,
startedAtMs: (Date().timeIntervalSince1970 * 1000) startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs())
) )
) )

View File

@ -6,6 +6,7 @@ import PromiseKit
import AFNetworking import AFNetworking
import SignalCoreKit import SignalCoreKit
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public struct LinkPreview: Codable, Equatable, Hashable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible { public struct LinkPreview: Codable, Equatable, Hashable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "linkPreview" } public static var databaseTableName: String { "linkPreview" }
@ -60,7 +61,7 @@ public struct LinkPreview: Codable, Equatable, Hashable, FetchableRecord, Persis
public init( public init(
url: String, url: String,
timestamp: TimeInterval = LinkPreview.timestampFor( timestamp: TimeInterval = LinkPreview.timestampFor(
sentTimestampMs: (Date().timeIntervalSince1970 * 1000) // Default to now sentTimestampMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs()) // Default to now
), ),
variant: Variant = .standard, variant: Variant = .standard,
title: String?, title: String?,

View File

@ -4,6 +4,7 @@ import Foundation
import GRDB import GRDB
import Sodium import Sodium
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible { public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "thread" } public static var databaseTableName: String { "thread" }
@ -104,7 +105,7 @@ public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord,
public init( public init(
id: String, id: String,
variant: Variant, variant: Variant,
creationDateTimestamp: TimeInterval = Date().timeIntervalSince1970, creationDateTimestamp: TimeInterval = (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000),
shouldBeVisible: Bool = false, shouldBeVisible: Bool = false,
isPinned: Bool = false, isPinned: Bool = false,
messageDraft: String? = nil, messageDraft: String? = nil,

View File

@ -145,7 +145,7 @@ public enum AttachmentDownloadJob: JobExecutor {
_ = try attachment _ = try attachment
.with( .with(
state: .downloaded, state: .downloaded,
creationTimestamp: Date().timeIntervalSince1970, creationTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000),
localRelativeFilePath: ( localRelativeFilePath: (
attachment.localRelativeFilePath ?? attachment.localRelativeFilePath ??
Attachment.localRelativeFilePath(from: attachment.originalFilePath) Attachment.localRelativeFilePath(from: attachment.originalFilePath)

View File

@ -3,6 +3,7 @@
import Foundation import Foundation
import GRDB import GRDB
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public enum DisappearingMessagesJob: JobExecutor { public enum DisappearingMessagesJob: JobExecutor {
public static let maxFailureCount: Int = -1 public static let maxFailureCount: Int = -1
@ -17,7 +18,7 @@ public enum DisappearingMessagesJob: JobExecutor {
deferred: @escaping (Job) -> () deferred: @escaping (Job) -> ()
) { ) {
// The 'backgroundTask' gets captured and cleared within the 'completion' block // 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) var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function)
let updatedJob: Job? = Storage.shared.write { db in let updatedJob: Job? = Storage.shared.write { db in
@ -59,10 +60,14 @@ public extension DisappearingMessagesJob {
guard let nextExpirationTimestampMs: Double = nextExpirationTimestampMs else { return nil } guard let nextExpirationTimestampMs: Double = nextExpirationTimestampMs else { return nil }
/// The `expiresStartedAtMs` timestamp is now based on the `SnodeAPI.currentOffsetTimestampMs()` value
/// so we need to make sure offset the `nextRunTimestamp` accordingly to ensure it runs at the correct local time
let clockOffsetMs: Int64 = SnodeAPI.clockOffsetMs.wrappedValue
return try? Job return try? Job
.filter(Job.Columns.variant == Job.Variant.disappearingMessages) .filter(Job.Columns.variant == Job.Variant.disappearingMessages)
.fetchOne(db)? .fetchOne(db)?
.with(nextRunTimestamp: ceil(nextExpirationTimestampMs / 1000)) .with(nextRunTimestamp: ceil((nextExpirationTimestampMs - Double(clockOffsetMs)) / 1000))
.saved(db) .saved(db)
} }

View File

@ -259,7 +259,10 @@ public extension Message {
return try processRawReceivedMessage( return try processRawReceivedMessage(
db, db,
envelope: envelope, envelope: envelope,
serverExpirationTimestamp: (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds), serverExpirationTimestamp: (
(TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) +
ControlMessageProcessRecord.defaultExpirationSeconds
),
serverHash: serverHash, serverHash: serverHash,
handleClosedGroupKeyUpdateMessages: true handleClosedGroupKeyUpdateMessages: true
) )
@ -275,7 +278,10 @@ public extension Message {
let processedMessage: ProcessedMessage? = try processRawReceivedMessage( let processedMessage: ProcessedMessage? = try processRawReceivedMessage(
db, db,
envelope: envelope, envelope: envelope,
serverExpirationTimestamp: (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds), serverExpirationTimestamp: (
(TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) +
ControlMessageProcessRecord.defaultExpirationSeconds
),
serverHash: nil, serverHash: nil,
handleClosedGroupKeyUpdateMessages: false handleClosedGroupKeyUpdateMessages: false
) )
@ -407,7 +413,7 @@ public extension Message {
let count: Int64 = rawReaction.you ? rawReaction.count - 1 : rawReaction.count 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 maxLength: Int = shouldAddSelfReaction ? 4 : 5
let desiredReactorIds: [String] = reactors let desiredReactorIds: [String] = reactors
.filter { $0 != blindedUserPublicKey && $0 != userPublicKey } // Remove current user for now, will add back if needed .filter { $0 != blindedUserPublicKey && $0 != userPublicKey } // Remove current user for now, will add back if needed

View File

@ -4,6 +4,7 @@ import Foundation
import GRDB import GRDB
import WebRTC import WebRTC
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
extension MessageReceiver { extension MessageReceiver {
public static func handleCallMessage(_ db: Database, message: CallMessage) throws { public static func handleCallMessage(_ db: Database, message: CallMessage) throws {
@ -189,7 +190,7 @@ extension MessageReceiver {
body: String(data: messageInfoData, encoding: .utf8), body: String(data: messageInfoData, encoding: .utf8),
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
) )
.inserted(db) .inserted(db)
@ -235,7 +236,7 @@ extension MessageReceiver {
) )
let timestampMs: Int64 = ( let timestampMs: Int64 = (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
guard let messageInfoData: Data = try? JSONEncoder().encode(messageInfo) else { return nil } guard let messageInfoData: Data = try? JSONEncoder().encode(messageInfo) else { return nil }

View File

@ -4,6 +4,7 @@ import Foundation
import GRDB import GRDB
import Sodium import Sodium
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
extension MessageReceiver { extension MessageReceiver {
public static func handleClosedGroupControlMessage(_ db: Database, _ message: ClosedGroupControlMessage) throws { public static func handleClosedGroupControlMessage(_ db: Database, _ message: ClosedGroupControlMessage) throws {
@ -135,7 +136,7 @@ extension MessageReceiver {
threadId: groupPublicKey, threadId: groupPublicKey,
publicKey: Data(encryptionKeyPair.publicKey), publicKey: Data(encryptionKeyPair.publicKey),
secretKey: Data(encryptionKeyPair.secretKey), secretKey: Data(encryptionKeyPair.secretKey),
receivedTimestamp: Date().timeIntervalSince1970 receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000)
).insert(db) ).insert(db)
// Start polling // Start polling
@ -196,7 +197,7 @@ extension MessageReceiver {
threadId: groupPublicKey, threadId: groupPublicKey,
publicKey: proto.publicKey.removingIdPrefixIfNeeded(), publicKey: proto.publicKey.removingIdPrefixIfNeeded(),
secretKey: proto.privateKey, secretKey: proto.privateKey,
receivedTimestamp: Date().timeIntervalSince1970 receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000)
).insert(db) ).insert(db)
} }
catch { catch {
@ -231,7 +232,7 @@ extension MessageReceiver {
.infoMessage(db, sender: sender), .infoMessage(db, sender: sender),
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
).inserted(db) ).inserted(db)
} }
@ -307,7 +308,7 @@ extension MessageReceiver {
.infoMessage(db, sender: sender), .infoMessage(db, sender: sender),
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
).inserted(db) ).inserted(db)
} }
@ -383,7 +384,7 @@ extension MessageReceiver {
.infoMessage(db, sender: sender), .infoMessage(db, sender: sender),
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
).inserted(db) ).inserted(db)
} }
@ -461,7 +462,7 @@ extension MessageReceiver {
.infoMessage(db, sender: sender), .infoMessage(db, sender: sender),
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
).inserted(db) ).inserted(db)
} }

View File

@ -2,6 +2,7 @@
import Foundation import Foundation
import GRDB import GRDB
import SessionSnodeKit
extension MessageReceiver { extension MessageReceiver {
internal static func handleDataExtractionNotification(_ db: Database, message: DataExtractionNotification) throws { internal static func handleDataExtractionNotification(_ db: Database, message: DataExtractionNotification) throws {
@ -24,7 +25,7 @@ extension MessageReceiver {
}(), }(),
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
).inserted(db) ).inserted(db)
} }

View File

@ -4,6 +4,7 @@ import Foundation
import GRDB import GRDB
import SignalCoreKit import SignalCoreKit
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
extension MessageReceiver { extension MessageReceiver {
internal static func handleMessageRequestResponse( internal static func handleMessageRequestResponse(
@ -123,7 +124,7 @@ extension MessageReceiver {
variant: .infoMessageRequestAccepted, variant: .infoMessageRequestAccepted,
timestampMs: ( timestampMs: (
message.sentTimestamp.map { Int64($0) } ?? message.sentTimestamp.map { Int64($0) } ??
Int64(floor(Date().timeIntervalSince1970 * 1000)) SnodeAPI.currentOffsetTimestampMs()
) )
).inserted(db) ).inserted(db)
} }

View File

@ -6,6 +6,7 @@ import Sodium
import Curve25519Kit import Curve25519Kit
import PromiseKit import PromiseKit
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
extension MessageSender { extension MessageSender {
public static var distributingKeyPairs: Atomic<[String: [ClosedGroupKeyPair]]> = Atomic([:]) public static var distributingKeyPairs: Atomic<[String: [ClosedGroupKeyPair]]> = Atomic([:])
@ -24,7 +25,7 @@ extension MessageSender {
let membersAsData = members.map { Data(hex: $0) } let membersAsData = members.map { Data(hex: $0) }
let admins = [ userPublicKey ] let admins = [ userPublicKey ]
let adminsAsData = admins.map { Data(hex: $0) } let adminsAsData = admins.map { Data(hex: $0) }
let formationTimestamp: TimeInterval = Date().timeIntervalSince1970 let formationTimestamp: TimeInterval = (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000)
let thread: SessionThread = try SessionThread let thread: SessionThread = try SessionThread
.fetchOrCreate(db, id: groupPublicKey, variant: .closedGroup) .fetchOrCreate(db, id: groupPublicKey, variant: .closedGroup)
try ClosedGroup( try ClosedGroup(
@ -91,7 +92,7 @@ extension MessageSender {
threadId: groupPublicKey, threadId: groupPublicKey,
publicKey: encryptionKeyPair.publicKey, publicKey: encryptionKeyPair.publicKey,
secretKey: encryptionKeyPair.privateKey, secretKey: encryptionKeyPair.privateKey,
receivedTimestamp: Date().timeIntervalSince1970 receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000)
).insert(db) ).insert(db)
// Notify the PN server // Notify the PN server
@ -110,7 +111,7 @@ extension MessageSender {
threadId: thread.id, threadId: thread.id,
authorId: userPublicKey, authorId: userPublicKey,
variant: .infoClosedGroupCreated, variant: .infoClosedGroupCreated,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
).inserted(db) ).inserted(db)
// Start polling // Start polling
@ -142,7 +143,7 @@ extension MessageSender {
threadId: closedGroup.threadId, threadId: closedGroup.threadId,
publicKey: legacyNewKeyPair.publicKey, publicKey: legacyNewKeyPair.publicKey,
secretKey: legacyNewKeyPair.privateKey, secretKey: legacyNewKeyPair.privateKey,
receivedTimestamp: Date().timeIntervalSince1970 receivedTimestamp: (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000)
) )
// Distribute it // Distribute it
@ -230,7 +231,7 @@ extension MessageSender {
body: ClosedGroupControlMessage.Kind body: ClosedGroupControlMessage.Kind
.nameChange(name: name) .nameChange(name: name)
.infoMessage(db, sender: userPublicKey), .infoMessage(db, sender: userPublicKey),
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
).inserted(db) ).inserted(db)
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved } guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
@ -330,7 +331,7 @@ extension MessageSender {
body: ClosedGroupControlMessage.Kind body: ClosedGroupControlMessage.Kind
.membersAdded(members: addedMembers.map { Data(hex: $0) }) .membersAdded(members: addedMembers.map { Data(hex: $0) })
.infoMessage(db, sender: userPublicKey), .infoMessage(db, sender: userPublicKey),
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
).inserted(db) ).inserted(db)
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved } guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
@ -431,7 +432,7 @@ extension MessageSender {
body: ClosedGroupControlMessage.Kind body: ClosedGroupControlMessage.Kind
.membersRemoved(members: removedMembers.map { Data(hex: $0) }) .membersRemoved(members: removedMembers.map { Data(hex: $0) })
.infoMessage(db, sender: userPublicKey), .infoMessage(db, sender: userPublicKey),
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
).inserted(db) ).inserted(db)
guard let newInteractionId: Int64 = interaction.id else { throw StorageError.objectNotSaved } guard let newInteractionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
@ -496,7 +497,7 @@ extension MessageSender {
body: ClosedGroupControlMessage.Kind body: ClosedGroupControlMessage.Kind
.memberLeft .memberLeft
.infoMessage(db, sender: userPublicKey), .infoMessage(db, sender: userPublicKey),
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) timestampMs: SnodeAPI.currentOffsetTimestampMs()
).inserted(db) ).inserted(db)
guard let interactionId: Int64 = interaction.id else { guard let interactionId: Int64 = interaction.id else {

View File

@ -5,6 +5,7 @@ import GRDB
import Sodium import Sodium
import SignalCoreKit import SignalCoreKit
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public enum MessageReceiver { public enum MessageReceiver {
private static var lastEncryptionKeyPairRequest: [String: Date] = [:] private static var lastEncryptionKeyPairRequest: [String: Date] = [:]
@ -144,7 +145,7 @@ public enum MessageReceiver {
message.sender = sender message.sender = sender
message.recipient = userPublicKey message.recipient = userPublicKey
message.sentTimestamp = envelope.timestamp message.sentTimestamp = envelope.timestamp
message.receivedTimestamp = UInt64((Date().timeIntervalSince1970) * 1000) message.receivedTimestamp = UInt64(SnodeAPI.currentOffsetTimestampMs())
message.groupPublicKey = groupPublicKey message.groupPublicKey = groupPublicKey
message.openGroupServerMessageId = openGroupMessageServerId.map { UInt64($0) } message.openGroupServerMessageId = openGroupMessageServerId.map { UInt64($0) }

View File

@ -67,7 +67,7 @@ public final class MessageSender {
let (promise, seal) = Promise<Void>.pending() let (promise, seal) = Promise<Void>.pending()
let userPublicKey: String = getUserHexEncodedPublicKey(db) let userPublicKey: String = getUserHexEncodedPublicKey(db)
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) 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 // Set the timestamp, sender and recipient
message.sentTimestamp = ( message.sentTimestamp = (
@ -202,7 +202,7 @@ public final class MessageSender {
recipient: message.recipient!, recipient: message.recipient!,
data: base64EncodedData, data: base64EncodedData,
ttl: message.ttl, ttl: message.ttl,
timestampMs: UInt64(messageSendTimestamp + SnodeAPI.clockOffset.wrappedValue) timestampMs: UInt64(messageSendTimestamp)
) )
SnodeAPI SnodeAPI
@ -322,7 +322,7 @@ public final class MessageSender {
// Set the timestamp, sender and recipient // Set the timestamp, sender and recipient
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
message.sentTimestamp = UInt64(floor(Date().timeIntervalSince1970 * 1000)) message.sentTimestamp = UInt64(SnodeAPI.currentOffsetTimestampMs())
} }
switch destination { switch destination {
@ -472,7 +472,7 @@ public final class MessageSender {
// Set the timestamp, sender and recipient // Set the timestamp, sender and recipient
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
message.sentTimestamp = UInt64(floor(Date().timeIntervalSince1970 * 1000)) message.sentTimestamp = UInt64(SnodeAPI.currentOffsetTimestampMs())
} }
message.sender = userPublicKey message.sender = userPublicKey
@ -617,7 +617,7 @@ public final class MessageSender {
job: DisappearingMessagesJob.updateNextRunIfNeeded( job: DisappearingMessagesJob.updateNextRunIfNeeded(
db, db,
interaction: interaction, interaction: interaction,
startedAtMs: (Date().timeIntervalSince1970 * 1000) startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs())
) )
) )
} }
@ -636,7 +636,10 @@ public final class MessageSender {
} }
}(), }(),
message: message, message: message,
serverExpirationTimestamp: (Date().timeIntervalSince1970 + ControlMessageProcessRecord.defaultExpirationSeconds) serverExpirationTimestamp: (
(TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000) +
ControlMessageProcessRecord.defaultExpirationSeconds
)
)?.insert(db) )?.insert(db)
// Sync the message if: // Sync the message if:

View File

@ -3,6 +3,7 @@
import Foundation import Foundation
import GRDB import GRDB
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
public class TypingIndicators { public class TypingIndicators {
// MARK: - Direction // MARK: - Direction
@ -41,7 +42,7 @@ public class TypingIndicators {
self.threadId = threadId self.threadId = threadId
self.direction = direction self.direction = direction
self.timestampMs = (timestampMs ?? Int64(floor(Date().timeIntervalSince1970 * 1000))) self.timestampMs = (timestampMs ?? SnodeAPI.currentOffsetTimestampMs())
} }
fileprivate func start(_ db: Database) { fileprivate func start(_ db: Database) {

View File

@ -196,7 +196,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
authorId: getUserHexEncodedPublicKey(db), authorId: getUserHexEncodedPublicKey(db),
variant: .standardOutgoing, variant: .standardOutgoing,
body: body, body: body,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)), timestampMs: SnodeAPI.currentOffsetTimestampMs(),
hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: body), hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: body),
expiresInSeconds: try? DisappearingMessagesConfiguration expiresInSeconds: try? DisappearingMessagesConfiguration
.select(.durationSeconds) .select(.durationSeconds)

View File

@ -93,7 +93,7 @@ public extension SnodeReceivedMessageInfo {
return try SnodeReceivedMessageInfo return try SnodeReceivedMessageInfo
.select(Column.rowID) .select(Column.rowID)
.filter(SnodeReceivedMessageInfo.Columns.key == key(for: snode, publicKey: publicKey, namespace: namespace)) .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) .asRequest(of: Int64.self)
.fetchAll(db) .fetchAll(db)
} }
@ -122,7 +122,7 @@ public extension SnodeReceivedMessageInfo {
SnodeReceivedMessageInfo.Columns.wasDeletedOrInvalid == false SnodeReceivedMessageInfo.Columns.wasDeletedOrInvalid == false
) )
.filter(SnodeReceivedMessageInfo.Columns.key == key(for: snode, publicKey: publicKey, namespace: namespace)) .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) .order(SnodeReceivedMessageInfo.Columns.id.desc)
.fetchOne(db) .fetchOne(db)

View File

@ -676,7 +676,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
if let timestamp = body["t"] as? Int64 { if let timestamp = body["t"] as? Int64 {
let offset = timestamp - Int64(floor(Date().timeIntervalSince1970 * 1000)) let offset = timestamp - Int64(floor(Date().timeIntervalSince1970 * 1000))
SnodeAPI.clockOffset.mutate { $0 = offset } SnodeAPI.clockOffsetMs.mutate { $0 = offset }
} }
guard 200...299 ~= statusCode else { guard 200...299 ~= statusCode else {

View File

@ -19,10 +19,16 @@ public final class SnodeAPI {
internal static var snodePool: Atomic<Set<Snode>> = Atomic([]) internal static var snodePool: Atomic<Set<Snode>> = Atomic([])
/// The offset between the user's clock and the Service Node's clock. Used in cases where the /// The offset between the user's clock and the Service Node's clock. Used in cases where the
/// user's clock is incorrect. /// user's clock is incorrect
/// public static var clockOffsetMs: Atomic<Int64> = Atomic(0)
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
public static var clockOffset: Atomic<Int64> = Atomic(0) public static func currentOffsetTimestampMs() -> Int64 {
return (
Int64(floor(Date().timeIntervalSince1970 * 1000)) +
SnodeAPI.clockOffsetMs.wrappedValue
)
}
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions. /// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
public static var swarmCache: Atomic<[String: Set<Snode>]> = Atomic([:]) public static var swarmCache: Atomic<[String: Set<Snode>]> = Atomic([:])
@ -546,7 +552,7 @@ public final class SnodeAPI {
let lastHash = SnodeReceivedMessageInfo.fetchLastNotExpired(for: snode, namespace: namespace, associatedWith: publicKey)?.hash ?? "" let lastHash = SnodeReceivedMessageInfo.fetchLastNotExpired(for: snode, namespace: namespace, associatedWith: publicKey)?.hash ?? ""
// Construct signature // Construct signature
let timestamp = UInt64(Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.clockOffset.wrappedValue) let timestamp = UInt64(SnodeAPI.currentOffsetTimestampMs())
let ed25519PublicKey = userED25519KeyPair.publicKey.toHexString() let ed25519PublicKey = userED25519KeyPair.publicKey.toHexString()
let namespaceVerificationString = (namespace == defaultNamespace ? "" : String(namespace)) let namespaceVerificationString = (namespace == defaultNamespace ? "" : String(namespace))
@ -647,7 +653,7 @@ public final class SnodeAPI {
} }
// Construct signature // Construct signature
let timestamp = UInt64(Int64(floor(Date().timeIntervalSince1970 * 1000)) + SnodeAPI.clockOffset.wrappedValue) let timestamp = UInt64(SnodeAPI.currentOffsetTimestampMs())
let ed25519PublicKey = userED25519KeyPair.publicKey.toHexString() let ed25519PublicKey = userED25519KeyPair.publicKey.toHexString()
guard guard
@ -1102,3 +1108,11 @@ public final class SnodeAPI {
return nil return nil
} }
} }
@objc(SNSnodeAPI)
public final class SNSnodeAPI: NSObject {
@objc(currentOffsetTimestampMs)
public static func currentOffsetTimestampMs() -> UInt64 {
return UInt64(SnodeAPI.currentOffsetTimestampMs())
}
}