kopia lustrzana https://github.com/ryukoposting/Signal-Android
Improve GV2 state change processing speed.
rodzic
5080567ca9
commit
6f788ee3df
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue