Support for GV1 -> GV2 migration
This commit is contained in:
parent
a0baa3e03f
commit
2c69f2c367
32 changed files with 2626 additions and 341 deletions
|
@ -16,7 +16,11 @@ import {
|
|||
GroupV2RecordClass,
|
||||
PinnedConversationClass,
|
||||
} from '../textsecure.d';
|
||||
import { deriveGroupFields, waitThenMaybeUpdateGroup } from '../groups';
|
||||
import {
|
||||
deriveGroupFields,
|
||||
waitThenMaybeUpdateGroup,
|
||||
waitThenRespondToGroupV2Migration,
|
||||
} from '../groups';
|
||||
import { ConversationModel } from '../models/conversations';
|
||||
import { ConversationAttributesTypeType } from '../model-types.d';
|
||||
|
||||
|
@ -414,6 +418,53 @@ export async function mergeGroupV1Record(
|
|||
return hasPendingChanges;
|
||||
}
|
||||
|
||||
async function getGroupV2Conversation(
|
||||
masterKeyBuffer: ArrayBuffer
|
||||
): Promise<ConversationModel> {
|
||||
const groupFields = deriveGroupFields(masterKeyBuffer);
|
||||
|
||||
const groupId = arrayBufferToBase64(groupFields.id);
|
||||
const masterKey = arrayBufferToBase64(masterKeyBuffer);
|
||||
const secretParams = arrayBufferToBase64(groupFields.secretParams);
|
||||
const publicParams = arrayBufferToBase64(groupFields.publicParams);
|
||||
|
||||
// First we check for an existing GroupV2 group
|
||||
const groupV2 = window.ConversationController.get(groupId);
|
||||
if (groupV2) {
|
||||
await groupV2.maybeRepairGroupV2({
|
||||
masterKey,
|
||||
secretParams,
|
||||
publicParams,
|
||||
});
|
||||
|
||||
return groupV2;
|
||||
}
|
||||
|
||||
// Then check for V1 group with matching derived GV2 id
|
||||
const groupV1 = window.ConversationController.getByDerivedGroupV2Id(groupId);
|
||||
if (groupV1) {
|
||||
return groupV1;
|
||||
}
|
||||
|
||||
const conversationId = window.ConversationController.ensureGroup(groupId, {
|
||||
// Note: We don't set active_at, because we don't want the group to show until
|
||||
// we have information about it beyond these initial details.
|
||||
// see maybeUpdateGroup().
|
||||
groupVersion: 2,
|
||||
masterKey,
|
||||
secretParams,
|
||||
publicParams,
|
||||
});
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error(
|
||||
`getGroupV2Conversation: Failed to create conversation for groupv2(${groupId})`
|
||||
);
|
||||
}
|
||||
|
||||
return conversation;
|
||||
}
|
||||
|
||||
export async function mergeGroupV2Record(
|
||||
storageID: string,
|
||||
groupV2Record: GroupV2RecordClass
|
||||
|
@ -423,36 +474,7 @@ export async function mergeGroupV2Record(
|
|||
}
|
||||
|
||||
const masterKeyBuffer = groupV2Record.masterKey.toArrayBuffer();
|
||||
const groupFields = deriveGroupFields(masterKeyBuffer);
|
||||
|
||||
const groupId = arrayBufferToBase64(groupFields.id);
|
||||
const masterKey = arrayBufferToBase64(masterKeyBuffer);
|
||||
const secretParams = arrayBufferToBase64(groupFields.secretParams);
|
||||
const publicParams = arrayBufferToBase64(groupFields.publicParams);
|
||||
|
||||
const now = Date.now();
|
||||
const conversationId = window.ConversationController.ensureGroup(groupId, {
|
||||
// Note: We don't set active_at, because we don't want the group to show until
|
||||
// we have information about it beyond these initial details.
|
||||
// see maybeUpdateGroup().
|
||||
timestamp: now,
|
||||
// Basic GroupV2 data
|
||||
groupVersion: 2,
|
||||
masterKey,
|
||||
secretParams,
|
||||
publicParams,
|
||||
});
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
|
||||
if (!conversation) {
|
||||
throw new Error(`No conversation for groupv2(${groupId})`);
|
||||
}
|
||||
|
||||
conversation.maybeRepairGroupV2({
|
||||
masterKey,
|
||||
secretParams,
|
||||
publicParams,
|
||||
});
|
||||
const conversation = await getGroupV2Conversation(masterKeyBuffer);
|
||||
|
||||
conversation.set({
|
||||
isArchived: Boolean(groupV2Record.archived),
|
||||
|
@ -476,10 +498,22 @@ export async function mergeGroupV2Record(
|
|||
const isFirstSync = !window.storage.get('storageFetchComplete');
|
||||
const dropInitialJoinMessage = isFirstSync;
|
||||
|
||||
// We don't need to update GroupV2 groups all the time. We fetch group state the first
|
||||
// time we hear about these groups, from then on we rely on incoming messages or
|
||||
// the user opening that conversation.
|
||||
if (isGroupNewToUs) {
|
||||
if (conversation.isGroupV1()) {
|
||||
// If we found a GroupV1 conversation from this incoming GroupV2 record, we need to
|
||||
// migrate it!
|
||||
|
||||
// We don't await this because this could take a very long time, waiting for queues to
|
||||
// empty, etc.
|
||||
waitThenRespondToGroupV2Migration({
|
||||
conversation,
|
||||
});
|
||||
} else if (isGroupNewToUs) {
|
||||
// We don't need to update GroupV2 groups all the time. We fetch group state the first
|
||||
// time we hear about these groups, from then on we rely on incoming messages or
|
||||
// the user opening that conversation.
|
||||
|
||||
// We don't await this because this could take a very long time, waiting for queues to
|
||||
// empty, etc.
|
||||
waitThenMaybeUpdateGroup({
|
||||
conversation,
|
||||
dropInitialJoinMessage,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue