From 80367143cc8deb61aa65d8f80abd3a0f0e8c570e Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:31:09 -0500 Subject: [PATCH] Fix GSE for delivery receipts in inactive groups Co-authored-by: Jamie Kyle <113370520+jamiebuilds-signal@users.noreply.github.com> --- ts/services/profiles.ts | 4 ++- ts/util/groupSendEndorsements.ts | 20 +++++++++----- ts/util/sendToGroup.ts | 45 ++++++++++++-------------------- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/ts/services/profiles.ts b/ts/services/profiles.ts index 11dcfc405d84..8cdbd263b67f 100644 --- a/ts/services/profiles.ts +++ b/ts/services/profiles.ts @@ -458,7 +458,9 @@ async function doGetProfile( c: ConversationModel, groupId: string | null ): Promise { - const logId = `getProfile(${c.idForLogging()})`; + const logId = groupId + ? `getProfile(${c.idForLogging()} in groupv2(${groupId}))` + : `getProfile(${c.idForLogging()})`; const { messaging } = window.textsecure; strictAssert( messaging, diff --git a/ts/util/groupSendEndorsements.ts b/ts/util/groupSendEndorsements.ts index ef0574dccba5..3157a989011a 100644 --- a/ts/util/groupSendEndorsements.ts +++ b/ts/util/groupSendEndorsements.ts @@ -30,7 +30,7 @@ import { isTestOrMockEnvironment } from '../environment'; import { isAlpha } from './version'; import { parseStrict } from './schemas'; import { DataReader } from '../sql/Client'; -import { maybeUpdateGroup } from '../groups'; +import { waitThenMaybeUpdateGroup } from '../groups'; import { isGroupV2 } from './whatTypeOfConversation'; export function decodeGroupSendEndorsementResponse({ @@ -139,6 +139,7 @@ export function isValidGroupSendEndorsementsExpiration( } export class GroupSendEndorsementState { + #logId: string; #combinedEndorsement: GroupSendCombinedEndorsementRecord; #memberEndorsements = new Map< ServiceIdString, @@ -153,6 +154,7 @@ export class GroupSendEndorsementState { data: GroupSendEndorsementsData, groupSecretParamsBase64: string ) { + this.#logId = `GroupSendEndorsementState/groupv2(${data.combinedEndorsement.groupId})`; this.#combinedEndorsement = data.combinedEndorsement; this.#groupSecretParamsBase64 = groupSecretParamsBase64; this.#ourAci = window.textsecure.storage.user.getCheckedAci(); @@ -194,7 +196,7 @@ export class GroupSendEndorsementState { strictAssert( isValidGroupSendEndorsementsExpiration(expiration.getTime()), - 'Cannot build token with invalid expiration' + `${this.#logId}: toToken: Cannot build token with invalid expiration` ); const fullToken = endorsement.toFullToken(groupSecretParams, expiration); @@ -218,7 +220,7 @@ export class GroupSendEndorsementState { const memberEndorsement = this.#memberEndorsements.get(serviceId); strictAssert( memberEndorsement, - 'subtractMemberEndorsements: Missing endorsement' + `${this.#logId}: getMemberEndorsement: Missing endorsement for ${serviceId}` ); return this.#toEndorsement(memberEndorsement.endorsement); } @@ -231,7 +233,7 @@ export class GroupSendEndorsementState { ): GroupSendEndorsement { strictAssert( !otherMembersServiceIds.has(this.#ourAci), - 'subtractMemberEndorsements: Cannot subtract our own aci from the combined endorsement' + `${this.#logId}: subtractMemberEndorsements: Cannot subtract our own aci from the combined endorsement` ); return this.#getCombinedEndorsement(includesOurs).byRemoving( this.#combineMemberEndorsements(otherMembersServiceIds) @@ -253,7 +255,7 @@ export class GroupSendEndorsementState { #buildToken(serviceIds: Set): GroupSendEndorsement { const sendCount = serviceIds.size; const memberCount = this.#memberEndorsements.size; - const logId = `GroupSendEndorsementState.buildToken(${sendCount} of ${memberCount})`; + const logId = `${this.#logId}: buildToken(${sendCount} of ${memberCount})`; // Fast path sending to one person if (serviceIds.size === 1) { @@ -351,6 +353,12 @@ export async function maybeCreateGroupSendEndorsementState( if (data == null) { const ourAci = window.textsecure.storage.user.getCheckedAci(); if (conversation.isMember(ourAci)) { + if (!alreadyRefreshedGroupState) { + log.info(`${logId}: Missing endorsements for group, refreshing group`); + await waitThenMaybeUpdateGroup({ conversation, force: true }); + return { state: null, didRefreshGroupState: true }; + } + onFailedToSendWithEndorsements( new Error(`${logId}: Missing all endorsements for group`) ); @@ -374,7 +382,7 @@ export async function maybeCreateGroupSendEndorsementState( log.info( `${logId}: Endorsements close to expiration (${groupSendEndorsementState.getExpiration().getTime()}, ${Date.now()}), refreshing group` ); - await maybeUpdateGroup({ conversation }); + await waitThenMaybeUpdateGroup({ conversation, force: true }); return { state: null, didRefreshGroupState: true }; } diff --git a/ts/util/sendToGroup.ts b/ts/util/sendToGroup.ts index 28f8aa72fc61..f1daf0398005 100644 --- a/ts/util/sendToGroup.ts +++ b/ts/util/sendToGroup.ts @@ -296,22 +296,20 @@ export async function sendToGroupViaSenderKey( } = options; const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; - const logId = sendTarget.idForLogging(); + const logId = `sendToGroupViaSenderKey/${sendTarget.idForLogging()}`; log.info( - `sendToGroupViaSenderKey/${logId}: Starting ${timestamp}, recursion count ${recursion.count}, reason: ${recursion.reason}...` + `${logId}: Starting ${timestamp}, recursion count ${recursion.count}, reason: ${recursion.reason}...` ); if (recursion.count > MAX_RECURSION) { throw new Error( - `sendToGroupViaSenderKey/${logId}: Too much recursion! Count is at ${recursion.count}` + `${logId}: Too much recursion! Count is at ${recursion.count}` ); } const groupId = sendTarget.getGroupId(); if (!sendTarget.isValid()) { - throw new Error( - `sendToGroupViaSenderKey/${logId}: sendTarget is not valid!` - ); + throw new Error(`${logId}: sendTarget is not valid!`); } if ( @@ -319,9 +317,7 @@ export async function sendToGroupViaSenderKey( contentHint !== ContentHint.RESENDABLE && contentHint !== ContentHint.IMPLICIT ) { - throw new Error( - `sendToGroupViaSenderKey/${logId}: Invalid contentHint ${contentHint}` - ); + throw new Error(`${logId}: Invalid contentHint ${contentHint}`); } strictAssert( @@ -334,9 +330,7 @@ export async function sendToGroupViaSenderKey( const senderKeyInfo = sendTarget.getSenderKeyInfo(); if (!senderKeyInfo) { - log.info( - `sendToGroupViaSenderKey/${logId}: Adding initial sender key info` - ); + log.info(`${logId}: Adding initial sender key info`); await sendTarget.saveSenderKeyInfo({ createdAtDate: Date.now(), distributionId: generateUuid(), @@ -350,9 +344,7 @@ export async function sendToGroupViaSenderKey( const EXPIRE_DURATION = getSenderKeyExpireDuration(); if (isOlderThan(senderKeyInfo.createdAtDate, EXPIRE_DURATION)) { const { createdAtDate } = senderKeyInfo; - log.info( - `sendToGroupViaSenderKey/${logId}: Resetting sender key; ${createdAtDate} is too old` - ); + log.info(`${logId}: Resetting sender key; ${createdAtDate} is too old`); await resetSenderKey(sendTarget); // Restart here because we updated senderKeyInfo @@ -419,7 +411,7 @@ export async function sendToGroupViaSenderKey( const senderKeyRecipients = getServiceIdsFromDevices(devicesForSenderKey); const normalSendRecipients = getServiceIdsFromDevices(devicesForNormalSend); log.info( - `sendToGroupViaSenderKey/${logId}:` + + `${logId}:` + ` ${senderKeyRecipients.length} accounts for sender key (${devicesForSenderKey.length} devices),` + ` ${normalSendRecipients.length} accounts for normal send (${devicesForNormalSend.length} devices)` ); @@ -427,7 +419,7 @@ export async function sendToGroupViaSenderKey( // 5. Ensure we have enough recipients if (senderKeyRecipients.length < 2) { throw new Error( - `sendToGroupViaSenderKey/${logId}: Not enough recipients for Sender Key message. Failing over.` + `${logId}: Not enough recipients for Sender Key message. Failing over.` ); } @@ -460,7 +452,7 @@ export async function sendToGroupViaSenderKey( // have our sender key before we send sender key messages to them. if (newToMemberServiceIds.length > 0) { log.info( - `sendToGroupViaSenderKey/${logId}: Sending sender key to ${ + `${logId}: Sending sender key to ${ newToMemberServiceIds.length } members: ${JSON.stringify(newToMemberServiceIds)}` ); @@ -583,7 +575,7 @@ export async function sendToGroupViaSenderKey( ); } else { log.error( - `sendToGroupViaSenderKey/${logId}: Server returned unexpected 200 response ${JSON.stringify( + `${logId}: Server returned unexpected 200 response ${JSON.stringify( parsed.error.flatten() )}` ); @@ -637,7 +629,7 @@ export async function sendToGroupViaSenderKey( const brokenAccount = window.ConversationController.get(name); if (brokenAccount) { log.warn( - `sendToGroupViaSenderKey/${logId}: Disabling sealed sender for ${brokenAccount.idForLogging()}` + `${logId}: Disabling sealed sender for ${brokenAccount.idForLogging()}` ); brokenAccount.set({ sealedSender: SEALED_SENDER.DISABLED }); await DataWriter.updateConversation(brokenAccount.attributes); @@ -652,7 +644,7 @@ export async function sendToGroupViaSenderKey( } log.error( - `sendToGroupViaSenderKey/${logId}: Returned unexpected error code: ${ + `${logId}: Returned unexpected error code: ${ error.code }, error class: ${typeof error}` ); @@ -1381,11 +1373,8 @@ async function fetchKeysForServiceId( devices: Array | null, groupSendEndorsementState: GroupSendEndorsementState | null ): Promise { - log.info( - `fetchKeysForServiceId: Fetching ${ - devices || 'all' - } devices for ${serviceId}` - ); + const logId = `fetchKeysForServiceId/${serviceId}`; + log.info(`${logId}: Fetching ${devices || 'all'} devices`); if (!window.textsecure?.messaging?.server) { throw new Error('fetchKeysForServiceId: No server available!'); @@ -1442,9 +1431,7 @@ async function fetchKeysForServiceId( onFailedToSendWithEndorsements(error as Error); } log.error( - `fetchKeysForServiceId: Error fetching ${ - devices || 'all' - } devices for ${serviceId}`, + `${logId}: Error fetching ${devices || 'all'} devices`, Errors.toLogFormat(error) ); throw error;