store account profile current version as byte array, not hex

This commit is contained in:
Jonathan Klabunde Tomer 2026-05-18 09:45:11 -07:00 committed by Jonathan Klabunde Tomer
parent 022f2b874d
commit 06d96a04df
11 changed files with 104 additions and 68 deletions

View File

@ -216,7 +216,7 @@ public class ProfileController {
.orElseGet(a::getBadges);
a.setBadges(clock, updatedBadges);
a.setCurrentProfileVersion(request.version());
a.setCurrentProfileVersion(HexFormat.of().parseHex(request.version()));
});
if (request.getAvatarChange() == CreateProfileRequest.AvatarChange.UPDATE) {
@ -436,7 +436,7 @@ public class ProfileController {
final ExpiringProfileKeyCredentialResponse expiringProfileKeyCredentialResponse;
if (account.getCurrentProfileVersion().map(version::equals).orElse(false)) {
if (account.getCurrentProfileVersion().map(v -> HexFormat.of().formatHex(v).equals(version)).orElse(false)) {
expiringProfileKeyCredentialResponse = profilesManager.getV1(account.getUuid(), version)
.map(profile -> {
final ExpiringProfileKeyCredentialResponse profileKeyCredentialResponse;
@ -488,7 +488,7 @@ public class ProfileController {
// Allow requests where either the version matches the latest version on Account or the latest version on Account
// is empty to read the payment address.
final byte[] paymentAddress = maybeProfile
.filter(p -> account.getCurrentProfileVersion().map(v -> v.equals(p.version())).orElse(true))
.filter(p -> account.getCurrentProfileVersion().map(v -> HexFormat.of().formatHex(v).equals(p.version())).orElse(true))
.map(VersionedProfileV1::paymentAddress)
.orElse(null);

View File

@ -95,7 +95,7 @@ public class ProfileGrpcHelper {
// Include payment address if the version matches the latest version on Account or the latest version on Account
// is empty
if (account.getCurrentProfileVersion().map(v -> v.equals(HexFormat.of().formatHex(requestVersion))).orElse(true)) {
if (account.getCurrentProfileVersion().map(v -> Arrays.equals(v, requestVersion)).orElse(true)) {
final Optional<byte []> paymentAddress = profile.map(VersionedProfile::paymentAddress).or(() -> v1Profile.map(VersionedProfileV1::paymentAddress));
if (paymentAddress.isPresent()) {

View File

@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.grpc;
import com.google.protobuf.ByteString;
import java.time.Clock;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
@ -107,11 +108,8 @@ public class ProfileGrpcService extends SimpleProfileGrpc.ProfileImplBase {
validateRequest(request);
final String expectedCurrentVersionHex = HexFormat.of().formatHex(request.getExpectedCurrentVersion().toByteArray());
final boolean currentVersionMatchesExpected = account.getCurrentProfileVersion().isEmpty()
? request.getExpectedCurrentVersion().isEmpty()
: account.getCurrentProfileVersion().get().equals(expectedCurrentVersionHex);
final byte[] expectedCurrentVersion = request.getExpectedCurrentVersion().toByteArray();
final boolean currentVersionMatchesExpected = Arrays.equals(account.getCurrentProfileVersion().orElse(new byte[0]), expectedCurrentVersion);
if (!currentVersionMatchesExpected) {
return SetProfileResponse.newBuilder().setExpectedVersionWriteConflict(FailedPrecondition.newBuilder()
@ -177,7 +175,7 @@ public class ProfileGrpcService extends SimpleProfileGrpc.ProfileImplBase {
}
try {
accountsManager.updateCurrentProfileVersion(account.getIdentifier(IdentityType.ACI), version, expectedCurrentVersionHex, a -> {
accountsManager.updateCurrentProfileVersion(account.getIdentifier(IdentityType.ACI), version, expectedCurrentVersion, a -> {
final List<AccountBadge> updatedBadges = Optional.of(request.getBadgeIdsList())
.map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges,

View File

@ -8,12 +8,21 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.time.Clock;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HexFormat;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@ -80,7 +89,9 @@ public class Account {
private IdentityKey phoneNumberIdentityKey;
@JsonProperty("cpv")
private String currentProfileVersion;
@JsonSerialize(using = ProfileKeyAdapter.Serializing.class)
@JsonDeserialize(using = ProfileKeyAdapter.Deserializing.class)
private byte[] currentProfileVersion;
@JsonProperty
private List<AccountBadge> badges = new ArrayList<>();
@ -323,13 +334,13 @@ public class Account {
.orElse(0L);
}
public Optional<String> getCurrentProfileVersion() {
public Optional<byte[]> getCurrentProfileVersion() {
requireNotStale();
return Optional.ofNullable(currentProfileVersion);
}
public void setCurrentProfileVersion(final String currentProfileVersion) {
public void setCurrentProfileVersion(final byte[] currentProfileVersion) {
requireNotStale();
this.currentProfileVersion = currentProfileVersion;
@ -564,4 +575,26 @@ public class Account {
logger.error("Accessor called on stale account", new RuntimeException());
}
}
private static class ProfileKeyAdapter {
private static class Deserializing extends JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
final String val = jsonParser.getValueAsString();
if (val.length() == 64) {
return HexFormat.of().parseHex(val);
} else {
return Base64.getDecoder().decode(val);
}
}
}
// after deploying this once we can get rid of it in favor of the default base64
private static class Serializing extends JsonSerializer<byte[]> {
@Override
public void serialize(byte[] bytes, JsonGenerator jsonGenerator, SerializerProvider serializaterProvider) throws IOException {
jsonGenerator.writeString(HexFormat.of().formatHex(bytes));
}
}
}
}

View File

@ -967,7 +967,7 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
///
/// @throws WriteConflictException if the expected current version does not match the current version
public Account updateCurrentProfileVersion(final UUID accountIdentifier, final byte[] newVersion,
final String expectedCurrentVersion, final Consumer<Account> updater) throws WriteConflictException {
final byte[] expectedCurrentVersion, final Consumer<Account> updater) throws WriteConflictException {
Objects.requireNonNull(expectedCurrentVersion, "expectedCurrentVersion");
@ -975,22 +975,20 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
final Account account = accounts.getByAccountIdentifier(accountIdentifier)
.orElseThrow(() -> new IllegalArgumentException("Account not found: " + accountIdentifier));
final String newVersionHex = HexFormat.of().formatHex(newVersion);
return accountLockManager.withLock(Set.of(account.getPhoneNumberIdentifier()), () -> {
final Account maybeUpdatedAccount = update(accountIdentifier, a -> {
if (!a.getCurrentProfileVersion().orElse("").equals(expectedCurrentVersion)) {
if (!a.getCurrentProfileVersion().orElse(new byte[0]).equals(expectedCurrentVersion)) {
return false;
}
a.setCurrentProfileVersion(newVersionHex);
a.setCurrentProfileVersion(newVersion);
updater.accept(a);
return true;
});
if (!maybeUpdatedAccount.getCurrentProfileVersion().map(v -> v.equals(newVersionHex)).orElse(false)) {
if (!maybeUpdatedAccount.getCurrentProfileVersion().map(v -> v.equals(newVersion)).orElse(false)) {
throw new WriteConflictException();
}

View File

@ -950,13 +950,14 @@ class ProfileControllerTest {
final byte[] name = TestRandomUtil.nextBytes(81);
final byte[] paymentAddress = TestRandomUtil.nextBytes(582);
final String version = versionHex("someversion");
final byte[] version = version("someversion");
final String versionHex = HexFormat.of().formatHex(version);
try (final Response response = resources.getJerseyTest()
.target("/v1/profile")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(
new CreateProfileRequest(commitment, version, name, null, null, paymentAddress, false, false,
new CreateProfileRequest(commitment, versionHex, name, null, null, paymentAddress, false, false,
Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) {
assertThat(response.getStatus()).isEqualTo(200);
@ -969,13 +970,14 @@ class ProfileControllerTest {
@Test
void testGetProfileReturnsNoPaymentAddressIfCurrentVersionMismatch() {
final byte[] paymentAddress = TestRandomUtil.nextBytes(582);
final String version = versionHex("validversion");
when(profilesManager.getV1(AuthHelper.VALID_UUID_TWO, version)).thenReturn(
Optional.of(new VersionedProfileV1(version, null, null, null, null, paymentAddress, null, null)));
final byte[] version = version("validversion");
final String versionHex = HexFormat.of().formatHex(version);
when(profilesManager.getV1(AuthHelper.VALID_UUID_TWO, versionHex)).thenReturn(
Optional.of(new VersionedProfileV1(versionHex, null, null, null, null, paymentAddress, null, null)));
{
final VersionedProfileResponse profile = resources.getJerseyTest()
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + version)
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + versionHex)
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(VersionedProfileResponse.class);
@ -987,7 +989,7 @@ class ProfileControllerTest {
{
final VersionedProfileResponse profile = resources.getJerseyTest()
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + version)
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + versionHex)
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(VersionedProfileResponse.class);
@ -995,11 +997,11 @@ class ProfileControllerTest {
assertThat(profile.paymentAddress()).containsExactly(paymentAddress);
}
when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("someotherversion")));
when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of(version("someotherversion")));
{
final VersionedProfileResponse profile = resources.getJerseyTest()
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + version)
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + versionHex)
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(VersionedProfileResponse.class);
@ -1013,13 +1015,13 @@ class ProfileControllerTest {
final Account account = mock(Account.class);
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
when(account.getIdentifier(IdentityType.ACI)).thenReturn(AuthHelper.VALID_UUID);
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("version")));
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version("version")));
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
when(profilesManager.getV1(any(), any())).thenReturn(Optional.empty());
final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest()
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex("version-that-does-not-exist"), "credential-request"))
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, HexFormat.of().formatHex(version("version-that-does-not-exist")), "credential-request"))
.queryParam("credentialType", "expiringProfileKey")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
@ -1043,12 +1045,13 @@ class ProfileControllerTest {
final byte[] emoji = TestRandomUtil.nextBytes(60);
final byte[] about = TestRandomUtil.nextBytes(156);
final String version = versionHex("anotherversion");
final byte[] version = version("anotherversion");
final String versionHex = HexFormat.of().formatHex(version);
try (final Response response = resources.getJerseyTest()
.target("/v1/profile/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false,
.put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false,
Optional.of(List.of("TEST2")), null), MediaType.APPLICATION_JSON_TYPE))) {
assertThat(response.getStatus()).isEqualTo(200);
@ -1071,7 +1074,7 @@ class ProfileControllerTest {
.target("/v1/profile/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false,
.put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false,
Optional.of(List.of("TEST3", "TEST2")), null), MediaType.APPLICATION_JSON_TYPE))) {
assertThat(response.getStatus()).isEqualTo(200);
@ -1097,7 +1100,7 @@ class ProfileControllerTest {
.target("/v1/profile/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false,
.put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false,
Optional.of(List.of("TEST2", "TEST3")), null), MediaType.APPLICATION_JSON_TYPE))) {
assertThat(response.getStatus()).isEqualTo(200);
@ -1123,7 +1126,7 @@ class ProfileControllerTest {
.target("/v1/profile/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false,
.put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false,
Optional.of(List.of("TEST1")), null), MediaType.APPLICATION_JSON_TYPE))) {
assertThat(response.getStatus()).isEqualTo(200);
@ -1150,7 +1153,8 @@ class ProfileControllerTest {
final byte[] name = TestRandomUtil.nextBytes(81);
final byte[] emoji = TestRandomUtil.nextBytes(60);
final byte[] about = TestRandomUtil.nextBytes(156);
final String version = versionHex("anotherversion");
final byte[] version = version("anotherversion");
final String versionHex = HexFormat.of().formatHex(version);
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
reset(accountsManager);
@ -1173,7 +1177,7 @@ class ProfileControllerTest {
.target("/v1/profile/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false,
.put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false,
Optional.of(List.of("TEST1")), null), MediaType.APPLICATION_JSON_TYPE))) {
assertThat(response.getStatus()).isEqualTo(200);
@ -1198,7 +1202,8 @@ class ProfileControllerTest {
@MethodSource
void testGetProfileWithExpiringProfileKeyCredential(final MultivaluedMap<String, Object> authHeaders)
throws VerificationFailedException, InvalidInputException {
final String version = versionHex("version");
final byte[] version = version("version");
final String versionHex = HexFormat.of().formatHex(version);
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
@ -1233,12 +1238,12 @@ class ProfileControllerTest {
serverZkProfile.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(AuthHelper.VALID_UUID), profileKeyCommitment, expiration);
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(account));
when(profilesManager.getV1(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
when(profilesManager.getV1(AuthHelper.VALID_UUID, versionHex)).thenReturn(Optional.of(versionedProfile));
when(zkProfileOperations.issueExpiringProfileKeyCredential(eq(credentialRequest), eq(new ServiceId.Aci(AuthHelper.VALID_UUID)), eq(profileKeyCommitment), any()))
.thenReturn(credentialResponse);
final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest()
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex,
HexFormat.of().formatHex(credentialRequest.serialize())))
.queryParam("credentialType", "expiringProfileKey")
.request()
@ -1267,7 +1272,8 @@ class ProfileControllerTest {
@Test
void testGetProfileWithExpiringProfileKeyCredentialBadRequest()
throws VerificationFailedException, InvalidInputException {
final String version = versionHex("version");
final byte[] version = version("version");
final String versionHex = HexFormat.of().formatHex(version);
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
@ -1294,12 +1300,12 @@ class ProfileControllerTest {
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version));
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(account));
when(profilesManager.getV1(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
when(profilesManager.getV1(AuthHelper.VALID_UUID, versionHex)).thenReturn(Optional.of(versionedProfile));
when(zkProfileOperations.issueExpiringProfileKeyCredential(any(), any(), any(), any()))
.thenThrow(new VerificationFailedException());
final Response response = resources.getJerseyTest()
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex,
HexFormat.of().formatHex(credentialRequest.serialize())))
.queryParam("credentialType", "expiringProfileKey")
.request()
@ -1312,7 +1318,8 @@ class ProfileControllerTest {
@Test
void testGetProfileWithExpiringProfileKeyCredentialNonCurrentVersion()
throws VerificationFailedException, InvalidInputException {
final String version = versionHex("version");
final byte[] version = version("version");
final String versionHex = HexFormat.of().formatHex(version);
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
@ -1338,13 +1345,13 @@ class ProfileControllerTest {
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(UNIDENTIFIED_ACCESS_KEY));
when(account.isIdentifiedBy(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(true);
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("the-current-version")));
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version("the-current-version")));
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(account));
when(profilesManager.getV1(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
when(profilesManager.getV1(AuthHelper.VALID_UUID, versionHex)).thenReturn(Optional.of(versionedProfile));
final Response response = resources.getJerseyTest()
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex,
HexFormat.of().formatHex(credentialRequest.serialize())))
.queryParam("credentialType", "expiringProfileKey")
.request()
@ -1580,11 +1587,15 @@ class ProfileControllerTest {
}
}
private static String versionHex(final String versionString) {
private static byte[] version(final String versionString) {
try {
return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(versionString.getBytes(StandardCharsets.UTF_8)));
return MessageDigest.getInstance("SHA-256").digest(versionString.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static String versionHex(final String versionString) {
return HexFormat.of().formatHex(version(versionString));
}
}

View File

@ -372,7 +372,7 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
final Optional<VersionedProfileV1> profile = Optional.of(new VersionedProfileV1(HexFormat.of().formatHex(requestVersion), name, avatar, emoji, about, paymentAddress, phoneNumberSharing, new byte[0]));
when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion).map(v -> HexFormat.of().formatHex(v)));
when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion));
when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(false);
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
@ -428,10 +428,9 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
final byte[] paymentAddress = TestRandomUtil.nextBytes(582);
final byte[] commitment = TestRandomUtil.nextBytes(97);
final byte[] version = TestRandomUtil.nextBytes(32);
final String versionHex = HexFormat.of().formatHex(version);
final VersionedProfile v2Profile = new VersionedProfile(version, data, paymentAddress, commitment);
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex));
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version));
when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(false);
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
when(account.hasCapability(DeviceCapability.PROFILES_V2)).thenReturn(true);
@ -472,10 +471,9 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
final byte[] paymentAddress = TestRandomUtil.nextBytes(582);
final byte[] commitment = TestRandomUtil.nextBytes(97);
final byte[] version = TestRandomUtil.nextBytes(32);
final String versionHex = HexFormat.of().formatHex(version);
final VersionedProfile v2Profile = new VersionedProfile(version, data, paymentAddress, commitment);
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex));
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version));
when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(false);
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
when(account.hasCapability(DeviceCapability.PROFILES_V2)).thenReturn(true);

View File

@ -304,7 +304,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
final byte[] validPhoneNumberSharing = new byte[29];
when(account.getCurrentProfileVersion()).thenReturn(
Optional.of(HexFormat.of().formatHex(TestRandomUtil.nextBytes(32))));
Optional.of(TestRandomUtil.nextBytes(32)));
final SetProfileRequest request = SetProfileRequest.newBuilder()
.setVersion(ByteString.copyFrom(VERSION))
@ -715,7 +715,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
.setVersion(ByteString.copyFrom(requestVersion))
.build();
when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion).map(v -> HexFormat.of().formatHex(v)));
when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion));
when(accountsManager.getByServiceIdentifier(any())).thenReturn(Optional.of(account));
when(profilesManager.getV1(any(), any())).thenReturn(profile);
@ -757,7 +757,6 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
final byte[] paymentAddress = TestRandomUtil.nextBytes(582);
final byte[] commitment = TestRandomUtil.nextBytes(97);
final byte[] version = TestRandomUtil.nextBytes(32);
final String versionHex = HexFormat.of().formatHex(version);
final UUID targetAci = UUID.randomUUID();
final VersionedProfile v2Profile = new VersionedProfile(version, data, paymentAddress, commitment);
@ -766,7 +765,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(targetAci)))
.thenReturn(Optional.of(targetAccount));
when(targetAccount.getUuid()).thenReturn(targetAci);
when(targetAccount.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex));
when(targetAccount.getCurrentProfileVersion()).thenReturn(Optional.of(version));
when(targetAccount.hasCapability(DeviceCapability.PROFILES_V2)).thenReturn(true);
when(profilesManager.get(eq(targetAci), aryEq(version))).thenReturn(Optional.of(v2Profile));
@ -801,7 +800,6 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
final byte[] paymentAddress = TestRandomUtil.nextBytes(582);
final byte[] commitment = TestRandomUtil.nextBytes(97);
final byte[] version = TestRandomUtil.nextBytes(32);
final String versionHex = HexFormat.of().formatHex(version);
final VersionedProfile v2Profile = new VersionedProfile(version, data, paymentAddress, commitment);
final UUID targetAci = UUID.randomUUID();
@ -810,7 +808,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
when(targetAccount.hasCapability(DeviceCapability.PROFILES_V2)).thenReturn(true);
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(targetAci))).thenReturn(Optional.of(targetAccount));
when(targetAccount.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex));
when(targetAccount.getCurrentProfileVersion()).thenReturn(Optional.of(version));
when(profilesManager.get(targetAci, version)).thenReturn(Optional.of(v2Profile));
when(profilesManager.getV1(any(), any())).thenReturn(Optional.empty());

View File

@ -171,7 +171,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
}
final boolean discoverableByPhoneNumber = false;
final String currentProfileVersion = "cpv";
final byte[] currentProfileVersion = new byte[32];
final IdentityKey identityKey = new IdentityKey(ECKeyPair.generate().getPublicKey());
final byte[] unidentifiedAccessKey = new byte[]{1};
final String pin = "1234";
@ -214,11 +214,11 @@ class AccountsManagerConcurrentModificationIntegrationTest {
return JsonHelpers.fromJson(redisSetArgumentCapture.getValue(), Account.class);
}
private void verifyAccount(final String name, final Account account, final boolean discoverableByPhoneNumber, final String currentProfileVersion, final IdentityKey identityKey, final byte[] unidentifiedAccessKey, final String pin, final String clientRegistrationLock, final boolean unrestrictedUnidentifiedAccess, final long lastSeen) {
private void verifyAccount(final String name, final Account account, final boolean discoverableByPhoneNumber, final byte[] currentProfileVersion, final IdentityKey identityKey, final byte[] unidentifiedAccessKey, final String pin, final String clientRegistrationLock, final boolean unrestrictedUnidentifiedAccess, final long lastSeen) {
assertAll(name,
() -> assertEquals(discoverableByPhoneNumber, account.isDiscoverableByPhoneNumber()),
() -> assertEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()),
() -> assertArrayEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()),
() -> assertEquals(identityKey, account.getIdentityKey(IdentityType.ACI)),
() -> assertArrayEquals(unidentifiedAccessKey, account.getUnidentifiedAccessKey().orElseThrow()),
() -> assertTrue(account.getRegistrationLock().verify(clientRegistrationLock)),

View File

@ -1460,22 +1460,22 @@ class AccountsManagerTest {
final UUID accountIdentifier = account.getIdentifier(IdentityType.ACI);
addRetrievableAccount(account);
account.setCurrentProfileVersion(HexFormat.of().formatHex(currentVersion));
account.setCurrentProfileVersion(currentVersion);
final AccountBadge badge = new AccountBadge("test", CLOCK.instant().plusSeconds(60), true);
assertTrue(account.getBadges().isEmpty());
if (expectException) {
assertThrows(WriteConflictException.class, () -> accountsManager.updateCurrentProfileVersion(accountIdentifier, newVersion, HexFormat.of().formatHex(expectedVersion), _ -> {}));
assertThrows(WriteConflictException.class, () -> accountsManager.updateCurrentProfileVersion(accountIdentifier, newVersion, expectedVersion, _ -> {}));
} else {
final Account updatedAccount = accountsManager.updateCurrentProfileVersion(accountIdentifier, newVersion,
HexFormat.of().formatHex(expectedVersion), a -> {
expectedVersion, a -> {
a.setBadges(CLOCK, new ArrayList<>(List.of(badge)));
});
assertArrayEquals(newVersion, HexFormat.of().parseHex(updatedAccount.getCurrentProfileVersion().orElseThrow()));
assertArrayEquals(newVersion, updatedAccount.getCurrentProfileVersion().orElseThrow());
assertEquals(List.of(badge), updatedAccount.getBadges());
}
}

View File

@ -1146,7 +1146,7 @@ class AccountsTest {
final Account secondAccountInstance = accounts.getByAccountIdentifier(firstAccountInstance.getUuid()).orElseThrow();
// update via the first instance, which will update the version
firstAccountInstance.setCurrentProfileVersion("1");
firstAccountInstance.setCurrentProfileVersion(new byte[32]);
accounts.update(firstAccountInstance);
assertThrows(ContestedOptimisticLockException.class,