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.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; 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.push.ACI;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
@ -30,19 +30,19 @@ public final class GroupProtoUtil {
private 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 throws GroupNotAMemberException
{ {
ByteString bytes = UuidUtil.toByteString(uuid); ByteString bytes = UuidUtil.toByteString(uuid);
for (DecryptedMember decryptedMember : group.getMembersList()) { for (DecryptedMember decryptedMember : partialDecryptedGroup.getMembersList()) {
if (decryptedMember.getUuid().equals(bytes)) { if (decryptedMember.getUuid().equals(bytes)) {
return decryptedMember.getJoinedAtRevision(); return decryptedMember.getJoinedAtRevision();
} }
} }
for (DecryptedPendingMember decryptedMember : group.getPendingMembersList()) { for (DecryptedPendingMember decryptedMember : partialDecryptedGroup.getPendingMembersList()) {
if (decryptedMember.getUuid().equals(bytes)) { if (decryptedMember.getUuid().equals(bytes)) {
// Assume latest, we don't have any information about when pending members were invited // Assume latest, we don't have any information about when pending members were invited
return group.getRevision(); return partialDecryptedGroup.getRevision();
} }
} }
throw new GroupNotAMemberException(); 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.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException; import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException; 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.push.ACI;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.exceptions.GroupNotFoundException; 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); Log.i(TAG, "Paging from server revision: " + (revision == LATEST ? "latest" : revision) + ", latestOnly: " + latestRevisionOnly);
DecryptedGroup latestServerGroup; PartialDecryptedGroup latestServerGroup;
GlobalGroupState inputGroupState; GlobalGroupState inputGroupState;
try { try {
latestServerGroup = groupsV2Api.getGroup(groupSecretParams, groupsV2Authorization.getAuthorizationForToday(selfAci, groupSecretParams)); latestServerGroup = groupsV2Api.getPartialDecryptedGroup(groupSecretParams, groupsV2Authorization.getAuthorizationForToday(selfAci, groupSecretParams));
} catch (NotInGroupException | GroupNotFoundException e) { } catch (NotInGroupException | GroupNotFoundException e) {
throw new GroupNotAMemberException(e); throw new GroupNotAMemberException(e);
} catch (VerificationFailedException | InvalidGroupStateException e) { } catch (VerificationFailedException | InvalidGroupStateException e) {
@ -315,12 +316,12 @@ public final class GroupsV2StateProcessor {
if (localState != null && localState.getRevision() >= latestServerGroup.getRevision()) { if (localState != null && localState.getRevision() >= latestServerGroup.getRevision()) {
Log.i(TAG, "Local state is at or later than server"); 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())) { if (latestRevisionOnly || !GroupProtoUtil.isMember(selfAci.uuid(), latestServerGroup.getMembersList())) {
Log.i(TAG, "Latest revision or not a member, use latest only"); 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 { } else {
int revisionWeWereAdded = GroupProtoUtil.findRevisionWeWereAdded(latestServerGroup, selfAci.uuid()); int revisionWeWereAdded = GroupProtoUtil.findRevisionWeWereAdded(latestServerGroup, selfAci.uuid());
int logsNeededFrom = localState != null ? Math.max(localState.getRevision(), revisionWeWereAdded) : revisionWeWereAdded; 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.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import org.signal.core.util.logging.Log 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.DecryptedGroupChange
import org.signal.storageservice.protos.groups.local.DecryptedMember import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.signal.storageservice.protos.groups.local.DecryptedTimer 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.thoughtcrime.securesms.util.Hex.fromStringCondensed
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api
import org.whispersystems.signalservice.api.groupsv2.PartialDecryptedGroup
import org.whispersystems.signalservice.api.push.ACI import org.whispersystems.signalservice.api.push.ACI
import java.util.UUID import java.util.UUID
@ -92,8 +94,13 @@ class GroupsV2StateProcessorTest {
doReturn(data.groupRecord).`when`(groupDatabase).getGroup(any(GroupId.V2::class.java)) doReturn(data.groupRecord).`when`(groupDatabase).getGroup(any(GroupId.V2::class.java))
doReturn(!data.groupRecord.isPresent).`when`(groupDatabase).isUnknownGroup(any()) doReturn(!data.groupRecord.isPresent).`when`(groupDatabase).isUnknownGroup(any())
if (data.serverState != null) { data.serverState?.let { serverState ->
doReturn(data.serverState).`when`(groupsV2API).getGroup(any(), any()) 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 -> 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.auth.ClientZkAuthOperations;
import org.signal.zkgroup.groups.ClientZkGroupCipher; import org.signal.zkgroup.groups.ClientZkGroupCipher;
import org.signal.zkgroup.groups.GroupSecretParams; import org.signal.zkgroup.groups.GroupSecretParams;
import org.whispersystems.libsignal.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.internal.push.PushServiceSocket; import org.whispersystems.signalservice.internal.push.PushServiceSocket;
@ -30,7 +29,6 @@ import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
public class GroupsV2Api { public class GroupsV2Api {
@ -85,6 +83,16 @@ public class GroupsV2Api {
socket.putNewGroupsV2Group(group, authorization); 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, public DecryptedGroup getGroup(GroupSecretParams groupSecretParams,
GroupsV2AuthorizationString authorization) GroupsV2AuthorizationString authorization)
throws IOException, InvalidGroupStateException, VerificationFailedException throws IOException, InvalidGroupStateException, VerificationFailedException

Wyświetl plik

@ -369,6 +369,38 @@ public final class GroupsV2Operations {
.setMember(member); .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) public DecryptedGroup decryptGroup(Group group)
throws VerificationFailedException, InvalidGroupStateException 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);
}
}
}