diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index a5802c836..be2b06365 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -461,10 +461,17 @@ extension ConversationVC: .filter(id: threadId) .updateAll(db, SessionThread.Columns.shouldBeVisible.set(to: true)) + let authorId: String = { + if let blindedId = self?.viewModel.threadData.currentUserBlindedPublicKey { + return blindedId + } + return self?.viewModel.threadData.currentUserPublicKey ?? getUserHexEncodedPublicKey(db) + }() + // Create the interaction let interaction: Interaction = try Interaction( threadId: threadId, - authorId: getUserHexEncodedPublicKey(db), + authorId: authorId, variant: .standardOutgoing, body: text, timestampMs: sentTimestampMs, diff --git a/Session/Conversations/ConversationViewModel.swift b/Session/Conversations/ConversationViewModel.swift index b37e53b90..d21d932d0 100644 --- a/Session/Conversations/ConversationViewModel.swift +++ b/Session/Conversations/ConversationViewModel.swift @@ -82,7 +82,11 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { // distinct stutter) self.pagedDataObserver = self.setupPagedObserver( for: threadId, - userPublicKey: getUserHexEncodedPublicKey() + userPublicKey: getUserHexEncodedPublicKey(), + blindedPublicKey: SessionThread.getUserHexEncodedBlindedKey( + threadId: threadId, + threadVariant: threadVariant + ) ) // Run the initial query on a background thread so we don't block the push transition @@ -172,7 +176,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { } } - private func setupPagedObserver(for threadId: String, userPublicKey: String) -> PagedDatabaseObserver { + private func setupPagedObserver(for threadId: String, userPublicKey: String, blindedPublicKey: String?) -> PagedDatabaseObserver { return PagedDatabaseObserver( pagedTable: Interaction.self, pageSize: ConversationViewModel.pageSize, @@ -220,6 +224,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { orderSQL: MessageViewModel.orderSQL, dataQuery: MessageViewModel.baseQuery( userPublicKey: userPublicKey, + blindedPublicKey: blindedPublicKey, orderSQL: MessageViewModel.orderSQL, groupSQL: MessageViewModel.groupSQL ), @@ -458,7 +463,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { self.observableThreadData = self.setupObservableThreadData(for: updatedThreadId) self.pagedDataObserver = self.setupPagedObserver( for: updatedThreadId, - userPublicKey: getUserHexEncodedPublicKey() + userPublicKey: getUserHexEncodedPublicKey(), + blindedPublicKey: nil ) // Try load everything up to the initial visible message, fallback to just the initial page of messages diff --git a/SessionMessagingKit/Database/Models/SessionThread.swift b/SessionMessagingKit/Database/Models/SessionThread.swift index ca08aa182..f5d743acf 100644 --- a/SessionMessagingKit/Database/Models/SessionThread.swift +++ b/SessionMessagingKit/Database/Models/SessionThread.swift @@ -322,18 +322,31 @@ public extension SessionThread { ) -> String? { guard threadVariant == .openGroup, - let blindingInfo: (edkeyPair: Box.KeyPair?, publicKey: String?) = Storage.shared.read({ db in + let blindingInfo: (edkeyPair: Box.KeyPair?, publicKey: String?, capabilities: Set) = Storage.shared.read({ db in + struct OpenGroupInfo: Decodable, FetchableRecord { + let publicKey: String? + let server: String? + } + let openGroupInfo: OpenGroupInfo? = try OpenGroup + .filter(id: threadId) + .select(.publicKey, .server) + .asRequest(of: OpenGroupInfo.self) + .fetchOne(db) + return ( Identity.fetchUserEd25519KeyPair(db), - try OpenGroup - .filter(id: threadId) - .select(.publicKey) - .asRequest(of: String.self) - .fetchOne(db) + openGroupInfo?.publicKey, + (try? Capability + .select(.variant) + .filter(Capability.Columns.openGroupServer == openGroupInfo?.server?.lowercased()) + .asRequest(of: Capability.Variant.self) + .fetchSet(db)) + .defaulting(to: []) ) }), let userEdKeyPair: Box.KeyPair = blindingInfo.edkeyPair, - let publicKey: String = blindingInfo.publicKey + let publicKey: String = blindingInfo.publicKey, + blindingInfo.capabilities.isEmpty || blindingInfo.capabilities.contains(.blind) else { return nil } let sodium: Sodium = Sodium() diff --git a/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift b/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift index 980d90b70..2780ae0eb 100644 --- a/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift +++ b/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift @@ -44,6 +44,7 @@ public final class VisibleMessage: Message { // MARK: - Initialization public init( + sender: String? = nil, sentTimestamp: UInt64? = nil, recipient: String? = nil, groupPublicKey: String? = nil, @@ -68,6 +69,7 @@ public final class VisibleMessage: Message { super.init( sentTimestamp: sentTimestamp, recipient: recipient, + sender: sender, groupPublicKey: groupPublicKey ) } @@ -228,6 +230,7 @@ public extension VisibleMessage { let linkPreview: LinkPreview? = try? interaction.linkPreview.fetchOne(db) return VisibleMessage( + sender: interaction.authorId, sentTimestamp: UInt64(interaction.timestampMs), recipient: (try? interaction.recipientStates.fetchOne(db))?.recipientId, groupPublicKey: try? interaction.thread diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index 85746de0b..d48c1d086 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -318,7 +318,6 @@ public final class MessageSender { dependencies: SMKDependencies = SMKDependencies() ) -> Promise { let (promise, seal) = Promise.pending() - let threadId: String // Set the timestamp, sender and recipient if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set @@ -328,7 +327,6 @@ public final class MessageSender { switch destination { case .contact, .closedGroup, .openGroupInbox: preconditionFailure() case .openGroup(let roomToken, let server, let whisperTo, let whisperMods, _): - threadId = OpenGroup.idFor(roomToken: roomToken, server: server) message.recipient = [ server, roomToken, @@ -343,34 +341,12 @@ public final class MessageSender { // which would go into this case, so rather than handling it as an invalid state we just want to // error in a non-retryable way guard - let openGroup: OpenGroup = try? OpenGroup.fetchOne(db, id: threadId), - let userEdKeyPair: Box.KeyPair = Identity.fetchUserEd25519KeyPair(db), case .openGroup(let roomToken, let server, let whisperTo, let whisperMods, let fileIds) = destination else { seal.reject(MessageSenderError.invalidMessage) return promise } - message.sender = { - let capabilities: [Capability.Variant] = (try? Capability - .select(.variant) - .filter(Capability.Columns.openGroupServer == server) - .filter(Capability.Columns.isMissing == false) - .asRequest(of: Capability.Variant.self) - .fetchAll(db)) - .defaulting(to: []) - - // If the server doesn't support blinding then go with an unblinded id - guard capabilities.isEmpty || capabilities.contains(.blind) else { - return SessionId(.unblinded, publicKey: userEdKeyPair.publicKey).hexString - } - guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: openGroup.publicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else { - preconditionFailure() - } - - return SessionId(.blinded, publicKey: blindedKeyPair.publicKey).hexString - }() - // Set the failure handler (need it here already for precondition failure handling) func handleFailure(_ db: Database, with error: MessageSenderError) { MessageSender.handleFailedMessageSend(db, message: message, with: error, interactionId: interactionId) @@ -467,7 +443,6 @@ public final class MessageSender { dependencies: SMKDependencies = SMKDependencies() ) -> Promise { let (promise, seal) = Promise.pending() - let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, dependencies: dependencies) guard case .openGroupInbox(let server, let openGroupPublicKey, let recipientBlindedPublicKey) = destination else { preconditionFailure() @@ -478,7 +453,6 @@ public final class MessageSender { message.sentTimestamp = UInt64(SnodeAPI.currentOffsetTimestampMs()) } - message.sender = currentUserPublicKey message.recipient = recipientBlindedPublicKey // Set the failure handler (need it here already for precondition failure handling) @@ -628,7 +602,6 @@ public final class MessageSender { // real message has no use when we delete a message. It is OK to let it be. try interaction.with( serverHash: message.serverHash, - // Track the open group server message ID and update server timestamp (use server // timestamp for open group messages otherwise the quote messages may not be able // to be found by the timestamp on other devices diff --git a/SessionMessagingKit/Shared Models/MessageViewModel.swift b/SessionMessagingKit/Shared Models/MessageViewModel.swift index ece71bca3..e2a1917cc 100644 --- a/SessionMessagingKit/Shared Models/MessageViewModel.swift +++ b/SessionMessagingKit/Shared Models/MessageViewModel.swift @@ -629,6 +629,7 @@ public extension MessageViewModel { static func baseQuery( userPublicKey: String, + blindedPublicKey: String?, orderSQL: SQL, groupSQL: SQL? ) -> (([Int64]) -> AdaptedFetchRequest>) { @@ -726,7 +727,12 @@ public extension MessageViewModel { \(interactionAttachment[.attachmentId]) AS \(Quote.Columns.attachmentId) FROM \(Quote.self) LEFT JOIN \(Interaction.self) ON ( - \(quote[.authorId]) = \(interaction[.authorId]) AND + ( + \(quote[.authorId]) = \(interaction[.authorId]) OR ( + \(quote[.authorId]) = \(blindedPublicKey ?? "") AND + \(userPublicKey) = \(interaction[.authorId]) + ) + ) AND \(quote[.timestampMs]) = \(interaction[.timestampMs]) ) LEFT JOIN \(InteractionAttachment.self) ON \(interaction[.id]) = \(interactionAttachment[.interactionId])