Improve GV2 state change processing speed.

fork-5.53.8
Cody Henthorne 2022-02-16 16:30:45 -05:00 zatwierdzone przez Greyson Parrelli
rodzic 5080567ca9
commit 6f788ee3df
6 zmienionych plików z 120 dodań i 14 usunięć

Wyświetl plik

@ -17,7 +17,7 @@ import org.signal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.groupsv2.PartialDecryptedGroup;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
@ -30,19 +30,19 @@ public final class GroupProtoUtil {
private GroupProtoUtil() {
}
public static int findRevisionWeWereAdded(@NonNull DecryptedGroup group, @NonNull UUID uuid)
public static int findRevisionWeWereAdded(@NonNull PartialDecryptedGroup partialDecryptedGroup, @NonNull UUID uuid)
throws GroupNotAMemberException
{
ByteString bytes = UuidUtil.toByteString(uuid);
for (DecryptedMember decryptedMember : group.getMembersList()) {
for (DecryptedMember decryptedMember : partialDecryptedGroup.getMembersList()) {
if (decryptedMember.getUuid().equals(bytes)) {
return decryptedMember.getJoinedAtRevision();
}
}
for (DecryptedPendingMember decryptedMember : group.getPendingMembersList()) {
for (DecryptedPendingMember decryptedMember : partialDecryptedGroup.getPendingMembersList()) {
if (decryptedMember.getUuid().equals(bytes)) {
// Assume latest, we don't have any information about when pending members were invited
return group.getRevision();
return partialDecryptedGroup.getRevision();
}
}
throw new GroupNotAMemberException();

Wyświetl plik

@ -51,6 +51,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
import org.whispersystems.signalservice.api.groupsv2.PartialDecryptedGroup;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.exceptions.GroupNotFoundException;
@ -302,11 +303,11 @@ public final class GroupsV2StateProcessor {
Log.i(TAG, "Paging from server revision: " + (revision == LATEST ? "latest" : revision) + ", latestOnly: " + latestRevisionOnly);
DecryptedGroup latestServerGroup;
GlobalGroupState inputGroupState;
PartialDecryptedGroup latestServerGroup;
GlobalGroupState inputGroupState;
try {
latestServerGroup = groupsV2Api.getGroup(groupSecretParams, groupsV2Authorization.getAuthorizationForToday(selfAci, groupSecretParams));
latestServerGroup = groupsV2Api.getPartialDecryptedGroup(groupSecretParams, groupsV2Authorization.getAuthorizationForToday(selfAci, groupSecretParams));
} catch (NotInGroupException | GroupNotFoundException e) {
throw new GroupNotAMemberException(e);
} catch (VerificationFailedException | InvalidGroupStateException e) {
@ -315,12 +316,12 @@ public final class GroupsV2StateProcessor {
if (localState != null && localState.getRevision() >= latestServerGroup.getRevision()) {
Log.i(TAG, "Local state is at or later than server");
return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, latestServerGroup);
return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, null);
}
if (latestRevisionOnly || !GroupProtoUtil.isMember(selfAci.uuid(), latestServerGroup.getMembersList())) {
Log.i(TAG, "Latest revision or not a member, use latest only");
inputGroupState = new GlobalGroupState(localState, Collections.singletonList(new ServerGroupLogEntry(latestServerGroup, null)));
inputGroupState = new GlobalGroupState(localState, Collections.singletonList(new ServerGroupLogEntry(latestServerGroup.getFullyDecryptedGroup(), null)));
} else {
int revisionWeWereAdded = GroupProtoUtil.findRevisionWeWereAdded(latestServerGroup, selfAci.uuid());
int logsNeededFrom = localState != null ? Math.max(localState.getRevision(), revisionWeWereAdded) : revisionWeWereAdded;

Wyświetl plik

@ -19,6 +19,7 @@ import org.mockito.Mockito.verify
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.signal.core.util.logging.Log
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange
import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.signal.storageservice.protos.groups.local.DecryptedTimer
@ -40,6 +41,7 @@ import org.thoughtcrime.securesms.testutil.SystemOutLogger
import org.thoughtcrime.securesms.util.Hex.fromStringCondensed
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api
import org.whispersystems.signalservice.api.groupsv2.PartialDecryptedGroup
import org.whispersystems.signalservice.api.push.ACI
import java.util.UUID
@ -92,8 +94,13 @@ class GroupsV2StateProcessorTest {
doReturn(data.groupRecord).`when`(groupDatabase).getGroup(any(GroupId.V2::class.java))
doReturn(!data.groupRecord.isPresent).`when`(groupDatabase).isUnknownGroup(any())
if (data.serverState != null) {
doReturn(data.serverState).`when`(groupsV2API).getGroup(any(), any())
data.serverState?.let { serverState ->
val testPartial = object : PartialDecryptedGroup(null, serverState, null, null) {
override fun getFullyDecryptedGroup(): DecryptedGroup {
return serverState
}
}
doReturn(testPartial).`when`(groupsV2API).getPartialDecryptedGroup(any(), any())
}
data.changeSet?.let { changeSet ->

Wyświetl plik

@ -20,7 +20,6 @@ import org.signal.zkgroup.auth.AuthCredentialResponse;
import org.signal.zkgroup.auth.ClientZkAuthOperations;
import org.signal.zkgroup.groups.ClientZkGroupCipher;
import org.signal.zkgroup.groups.GroupSecretParams;
import org.whispersystems.libsignal.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
@ -30,7 +29,6 @@ import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
public class GroupsV2Api {
@ -85,6 +83,16 @@ public class GroupsV2Api {
socket.putNewGroupsV2Group(group, authorization);
}
public PartialDecryptedGroup getPartialDecryptedGroup(GroupSecretParams groupSecretParams,
GroupsV2AuthorizationString authorization)
throws IOException, InvalidGroupStateException, VerificationFailedException
{
Group group = socket.getGroupsV2Group(authorization);
return groupsOperations.forGroup(groupSecretParams)
.partialDecryptGroup(group);
}
public DecryptedGroup getGroup(GroupSecretParams groupSecretParams,
GroupsV2AuthorizationString authorization)
throws IOException, InvalidGroupStateException, VerificationFailedException

Wyświetl plik

@ -369,6 +369,38 @@ public final class GroupsV2Operations {
.setMember(member);
}
public PartialDecryptedGroup partialDecryptGroup(Group group)
throws VerificationFailedException, InvalidGroupStateException
{
List<Member> membersList = group.getMembersList();
List<PendingMember> pendingMembersList = group.getPendingMembersList();
List<DecryptedMember> decryptedMembers = new ArrayList<>(membersList.size());
List<DecryptedPendingMember> decryptedPendingMembers = new ArrayList<>(pendingMembersList.size());
for (Member member : membersList) {
UUID memberUuid = decryptUuid(member.getUserId());
decryptedMembers.add(DecryptedMember.newBuilder()
.setUuid(UuidUtil.toByteString(memberUuid))
.setJoinedAtRevision(member.getJoinedAtRevision())
.build());
}
for (PendingMember member : pendingMembersList) {
UUID pendingMemberUuid = decryptUuidOrUnknown(member.getMember().getUserId());
decryptedMembers.add(DecryptedMember.newBuilder()
.setUuid(UuidUtil.toByteString(pendingMemberUuid))
.build());
}
DecryptedGroup decryptedGroup = DecryptedGroup.newBuilder()
.setRevision(group.getRevision())
.addAllMembers(decryptedMembers)
.addAllPendingMembers(decryptedPendingMembers)
.build();
return new PartialDecryptedGroup(group, decryptedGroup, GroupsV2Operations.this, groupSecretParams);
}
public DecryptedGroup decryptGroup(Group group)
throws VerificationFailedException, InvalidGroupStateException
{

Wyświetl plik

@ -0,0 +1,58 @@
package org.whispersystems.signalservice.api.groupsv2;
import org.signal.storageservice.protos.groups.Group;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.groups.GroupSecretParams;
import java.io.IOException;
import java.util.List;
/**
* Decrypting an entire group can be expensive for large groups. Since not every
* operation requires all data to be decrypted, this class can be populated with only
* the minimalist about of information need to perform an operation. Currently, only
* updating from the server utilizes it.
*/
public class PartialDecryptedGroup {
private final Group group;
private final DecryptedGroup decryptedGroup;
private final GroupsV2Operations groupsOperations;
private final GroupSecretParams groupSecretParams;
public PartialDecryptedGroup(Group group,
DecryptedGroup decryptedGroup,
GroupsV2Operations groupsOperations,
GroupSecretParams groupSecretParams)
{
this.group = group;
this.decryptedGroup = decryptedGroup;
this.groupsOperations = groupsOperations;
this.groupSecretParams = groupSecretParams;
}
public int getRevision() {
return decryptedGroup.getRevision();
}
public List<DecryptedMember> getMembersList() {
return decryptedGroup.getMembersList();
}
public List<DecryptedPendingMember> getPendingMembersList() {
return decryptedGroup.getPendingMembersList();
}
public DecryptedGroup getFullyDecryptedGroup()
throws IOException
{
try {
return groupsOperations.forGroup(groupSecretParams)
.decryptGroup(group);
} catch (VerificationFailedException | InvalidGroupStateException e) {
throw new IOException(e);
}
}
}