Skip to content

Commit c9f21d5

Browse files
authored
Always read from new and old PQ prekey stores, add experiment to start writing to new prekey store
1 parent 80c11e7 commit c9f21d5

10 files changed

+182
-34
lines changed

service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
368368

369369
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
370370

371+
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager);
372+
371373
if (config.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) {
372374
defaultServerFactory.getApplicationConnectors()
373375
.forEach(connectorFactory -> {
@@ -444,7 +446,8 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
444446
config.getDynamoDbTables().getPagedKemKeys().getTableName(),
445447
config.getPagedSingleUseKEMPreKeyStore().bucket()),
446448
new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient, config.getDynamoDbTables().getEcSignedPreKeys().getTableName()),
447-
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient, config.getDynamoDbTables().getKemLastResortKeys().getTableName()));
449+
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient, config.getDynamoDbTables().getKemLastResortKeys().getTableName()),
450+
experimentEnrollmentManager);
448451
MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbClient, dynamoDbAsyncClient,
449452
config.getDynamoDbTables().getMessages().getTableName(),
450453
config.getDynamoDbTables().getMessages().getExpiration(),
@@ -604,8 +607,6 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
604607
ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
605608
config.getSvr2Configuration());
606609

607-
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(
608-
dynamicConfigurationManager);
609610
RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager =
610611
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
611612
UsernameHashZkProofVerifier usernameHashZkProofVerifier = new UsernameHashZkProofVerifier();

service/src/main/java/org/whispersystems/textsecuregcm/storage/KeysManager.java

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55

66
package org.whispersystems.textsecuregcm.storage;
77

8-
import java.time.Instant;
8+
import io.micrometer.core.instrument.Metrics;
99
import java.util.List;
1010
import java.util.Optional;
1111
import java.util.UUID;
1212
import java.util.concurrent.CompletableFuture;
1313
import org.whispersystems.textsecuregcm.entities.ECPreKey;
1414
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
1515
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
16+
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
17+
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
1618
import reactor.core.publisher.Flux;
1719
import software.amazon.awssdk.services.dynamodb.model.TransactWriteItem;
1820

@@ -23,18 +25,25 @@ public class KeysManager {
2325
private final PagedSingleUseKEMPreKeyStore pagedPqPreKeys;
2426
private final RepeatedUseECSignedPreKeyStore ecSignedPreKeys;
2527
private final RepeatedUseKEMSignedPreKeyStore pqLastResortKeys;
28+
private final ExperimentEnrollmentManager experimentEnrollmentManager;
29+
30+
public static String PAGED_KEYS_EXPERIMENT_NAME = "pagedPreKeys";
31+
32+
private static final String TAKE_PQ_NAME = MetricsUtil.name(KeysManager.class, "takePq");
2633

2734
public KeysManager(
2835
final SingleUseECPreKeyStore ecPreKeys,
2936
final SingleUseKEMPreKeyStore pqPreKeys,
3037
final PagedSingleUseKEMPreKeyStore pagedPqPreKeys,
3138
final RepeatedUseECSignedPreKeyStore ecSignedPreKeys,
32-
final RepeatedUseKEMSignedPreKeyStore pqLastResortKeys) {
39+
final RepeatedUseKEMSignedPreKeyStore pqLastResortKeys,
40+
final ExperimentEnrollmentManager experimentEnrollmentManager) {
3341
this.ecPreKeys = ecPreKeys;
3442
this.pqPreKeys = pqPreKeys;
3543
this.pagedPqPreKeys = pagedPqPreKeys;
3644
this.ecSignedPreKeys = ecSignedPreKeys;
3745
this.pqLastResortKeys = pqLastResortKeys;
46+
this.experimentEnrollmentManager = experimentEnrollmentManager;
3847
}
3948

4049
public TransactWriteItem buildWriteItemForEcSignedPreKey(final UUID identifier,
@@ -79,33 +88,68 @@ public List<TransactWriteItem> buildWriteItemsForRemovedDevice(final UUID accoun
7988
);
8089
}
8190

82-
public CompletableFuture<Void> storeEcSignedPreKeys(final UUID identifier, final byte deviceId, final ECSignedPreKey ecSignedPreKey) {
91+
public CompletableFuture<Void> storeEcSignedPreKeys(final UUID identifier, final byte deviceId,
92+
final ECSignedPreKey ecSignedPreKey) {
8393
return ecSignedPreKeys.store(identifier, deviceId, ecSignedPreKey);
8494
}
8595

86-
public CompletableFuture<Void> storePqLastResort(final UUID identifier, final byte deviceId, final KEMSignedPreKey lastResortKey) {
96+
public CompletableFuture<Void> storePqLastResort(final UUID identifier, final byte deviceId,
97+
final KEMSignedPreKey lastResortKey) {
8798
return pqLastResortKeys.store(identifier, deviceId, lastResortKey);
8899
}
89100

90101
public CompletableFuture<Void> storeEcOneTimePreKeys(final UUID identifier, final byte deviceId,
91-
final List<ECPreKey> preKeys) {
102+
final List<ECPreKey> preKeys) {
92103
return ecPreKeys.store(identifier, deviceId, preKeys);
93104
}
94105

95106
public CompletableFuture<Void> storeKemOneTimePreKeys(final UUID identifier, final byte deviceId,
96-
final List<KEMSignedPreKey> preKeys) {
97-
return pqPreKeys.store(identifier, deviceId, preKeys);
107+
final List<KEMSignedPreKey> preKeys) {
108+
final boolean enrolledInPagedKeys = experimentEnrollmentManager.isEnrolled(identifier, PAGED_KEYS_EXPERIMENT_NAME);
109+
final CompletableFuture<Void> deleteOtherKeys = enrolledInPagedKeys
110+
? pqPreKeys.delete(identifier, deviceId)
111+
: pagedPqPreKeys.delete(identifier, deviceId);
112+
return deleteOtherKeys.thenCompose(ignored -> enrolledInPagedKeys
113+
? pagedPqPreKeys.store(identifier, deviceId, preKeys)
114+
: pqPreKeys.store(identifier, deviceId, preKeys));
115+
98116
}
99117

100118
public CompletableFuture<Optional<ECPreKey>> takeEC(final UUID identifier, final byte deviceId) {
101119
return ecPreKeys.take(identifier, deviceId);
102120
}
103121

104122
public CompletableFuture<Optional<KEMSignedPreKey>> takePQ(final UUID identifier, final byte deviceId) {
105-
return pqPreKeys.take(identifier, deviceId)
123+
final boolean enrolledInPagedKeys = experimentEnrollmentManager.isEnrolled(identifier, PAGED_KEYS_EXPERIMENT_NAME);
124+
return tagTakePQ(pagedPqPreKeys.take(identifier, deviceId), PQSource.PAGE, enrolledInPagedKeys)
125+
.thenCompose(maybeSingleUsePreKey -> maybeSingleUsePreKey
126+
.map(ignored -> CompletableFuture.completedFuture(maybeSingleUsePreKey))
127+
.orElseGet(() -> tagTakePQ(pqPreKeys.take(identifier, deviceId), PQSource.ROW, enrolledInPagedKeys)))
106128
.thenCompose(maybeSingleUsePreKey -> maybeSingleUsePreKey
107129
.map(singleUsePreKey -> CompletableFuture.completedFuture(maybeSingleUsePreKey))
108-
.orElseGet(() -> pqLastResortKeys.find(identifier, deviceId)));
130+
.orElseGet(() -> tagTakePQ(pqLastResortKeys.find(identifier, deviceId), PQSource.LAST_RESORT, enrolledInPagedKeys)));
131+
}
132+
133+
private enum PQSource {
134+
PAGE,
135+
ROW,
136+
LAST_RESORT
137+
}
138+
private CompletableFuture<Optional<KEMSignedPreKey>> tagTakePQ(CompletableFuture<Optional<KEMSignedPreKey>> prekey, final PQSource source, final boolean enrolledInPagedKeys) {
139+
return prekey.thenApply(maybeSingleUsePreKey -> {
140+
final Optional<String> maybeSourceTag = maybeSingleUsePreKey
141+
// If we found a PK, use this source tag
142+
.map(ignore -> source.name())
143+
// If we didn't and this is our last resort, we didn't find a PK
144+
.or(() -> source == PQSource.LAST_RESORT ? Optional.of("absent") : Optional.empty());
145+
maybeSourceTag.ifPresent(sourceTag -> {
146+
Metrics.counter(TAKE_PQ_NAME,
147+
"source", sourceTag,
148+
"enrolled", Boolean.toString(enrolledInPagedKeys))
149+
.increment();
150+
});
151+
return maybeSingleUsePreKey;
152+
});
109153
}
110154

111155
public CompletableFuture<Optional<KEMSignedPreKey>> getLastResort(final UUID identifier, final byte deviceId) {
@@ -121,20 +165,24 @@ public CompletableFuture<Integer> getEcCount(final UUID identifier, final byte d
121165
}
122166

123167
public CompletableFuture<Integer> getPqCount(final UUID identifier, final byte deviceId) {
124-
return pqPreKeys.getCount(identifier, deviceId);
168+
return pagedPqPreKeys.getCount(identifier, deviceId).thenCompose(count -> count == 0
169+
? pqPreKeys.getCount(identifier, deviceId)
170+
: CompletableFuture.completedFuture(count));
125171
}
126172

127173
public CompletableFuture<Void> deleteSingleUsePreKeys(final UUID identifier) {
128174
return CompletableFuture.allOf(
129175
ecPreKeys.delete(identifier),
130-
pqPreKeys.delete(identifier)
176+
pqPreKeys.delete(identifier),
177+
pagedPqPreKeys.delete(identifier)
131178
);
132179
}
133180

134181
public CompletableFuture<Void> deleteSingleUsePreKeys(final UUID accountUuid, final byte deviceId) {
135182
return CompletableFuture.allOf(
136183
ecPreKeys.delete(accountUuid, deviceId),
137-
pqPreKeys.delete(accountUuid, deviceId)
184+
pqPreKeys.delete(accountUuid, deviceId),
185+
pagedPqPreKeys.delete(accountUuid, deviceId)
138186
);
139187
}
140188

service/src/main/java/org/whispersystems/textsecuregcm/storage/PagedSingleUseKEMPreKeyStore.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.util.concurrent.CompletionException;
2323
import java.util.function.Function;
2424
import java.util.stream.Collectors;
25-
2625
import org.signal.libsignal.protocol.InvalidKeyException;
2726
import org.slf4j.Logger;
2827
import org.slf4j.LoggerFactory;
@@ -44,7 +43,12 @@
4443
import software.amazon.awssdk.services.dynamodb.model.ReturnValue;
4544
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
4645
import software.amazon.awssdk.services.s3.S3AsyncClient;
47-
import software.amazon.awssdk.services.s3.model.*;
46+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
47+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
48+
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
49+
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
50+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
51+
import software.amazon.awssdk.services.s3.model.S3Object;
4852

4953
/**
5054
* @implNote This version of a {@link SingleUsePreKeyStore} store bundles prekeys into "pages", which are stored in on

service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
3434
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
3535
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
36+
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
3637
import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSamples;
3738
import org.whispersystems.textsecuregcm.limits.RateLimiters;
3839
import org.whispersystems.textsecuregcm.metrics.MicrometerAwsSdkMetricPublisher;
@@ -122,6 +123,9 @@ static CommandDependencies build(
122123
new DynamicConfigurationManager<>(
123124
configuration.getDynamicConfig().build(awsCredentialsProvider, dynamicConfigurationExecutor), DynamicConfiguration.class);
124125
dynamicConfigurationManager.start();
126+
ExperimentEnrollmentManager experimentEnrollmentManager =
127+
new ExperimentEnrollmentManager(dynamicConfigurationManager);
128+
125129
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
126130

127131
FaultTolerantRedisClusterClient cacheCluster = configuration.getCacheClusterConfiguration()
@@ -224,7 +228,8 @@ static CommandDependencies build(
224228
new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient,
225229
configuration.getDynamoDbTables().getEcSignedPreKeys().getTableName()),
226230
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient,
227-
configuration.getDynamoDbTables().getKemLastResortKeys().getTableName()));
231+
configuration.getDynamoDbTables().getKemLastResortKeys().getTableName()),
232+
experimentEnrollmentManager);
228233
MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbClient, dynamoDbAsyncClient,
229234
configuration.getDynamoDbTables().getMessages().getTableName(),
230235
configuration.getDynamoDbTables().getMessages().getExpiration(),

service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountCreationDeletionIntegrationTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
4545
import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
4646
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
47+
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
4748
import org.whispersystems.textsecuregcm.identity.IdentityType;
4849
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
4950
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
@@ -66,6 +67,7 @@ public class AccountCreationDeletionIntegrationTest {
6667
DynamoDbExtensionSchema.Tables.USERNAMES,
6768
DynamoDbExtensionSchema.Tables.EC_KEYS,
6869
DynamoDbExtensionSchema.Tables.PQ_KEYS,
70+
DynamoDbExtensionSchema.Tables.PAGED_PQ_KEYS,
6971
DynamoDbExtensionSchema.Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS,
7072
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS);
7173

@@ -105,7 +107,8 @@ void setUp() {
105107
new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient,
106108
DynamoDbExtensionSchema.Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS.tableName()),
107109
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient,
108-
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()));
110+
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()),
111+
mock(ExperimentEnrollmentManager.class));
109112

110113
final ClientPublicKeys clientPublicKeys = new ClientPublicKeys(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
111114
DynamoDbExtensionSchema.Tables.CLIENT_PUBLIC_KEYS.tableName());

service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
3737
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
3838
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
39+
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
3940
import org.whispersystems.textsecuregcm.identity.IdentityType;
4041
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
4142
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
@@ -60,6 +61,7 @@ class AccountsManagerChangeNumberIntegrationTest {
6061
Tables.USERNAMES,
6162
Tables.EC_KEYS,
6263
Tables.PQ_KEYS,
64+
Tables.PAGED_PQ_KEYS,
6365
Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS,
6466
Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS);
6567

@@ -96,7 +98,8 @@ void setup() throws InterruptedException {
9698
new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient,
9799
DynamoDbExtensionSchema.Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS.tableName()),
98100
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient,
99-
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()));
101+
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()),
102+
mock(ExperimentEnrollmentManager.class));
100103

101104
final ClientPublicKeys clientPublicKeys = new ClientPublicKeys(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
102105
DynamoDbExtensionSchema.Tables.CLIENT_PUBLIC_KEYS.tableName());

service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
7373
Tables.DELETED_ACCOUNTS,
7474
Tables.EC_KEYS,
7575
Tables.PQ_KEYS,
76+
Tables.PAGED_PQ_KEYS,
7677
Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS,
7778
Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS);
7879

service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerUsernameIntegrationTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.mockito.Mockito;
3939
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
4040
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
41+
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
4142
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
4243
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
4344
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
@@ -73,6 +74,7 @@ class AccountsManagerUsernameIntegrationTest {
7374
Tables.PNI_ASSIGNMENTS,
7475
Tables.EC_KEYS,
7576
Tables.PQ_KEYS,
77+
Tables.PAGED_PQ_KEYS,
7678
Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS,
7779
Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS);
7880

@@ -109,7 +111,8 @@ private void buildAccountsManager(final int initialWidth, int discriminatorMaxWi
109111
new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient,
110112
DynamoDbExtensionSchema.Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS.tableName()),
111113
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient,
112-
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()));
114+
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()),
115+
mock(ExperimentEnrollmentManager.class));
113116

114117
accounts = Mockito.spy(new Accounts(
115118
Clock.systemUTC(),

service/src/test/java/org/whispersystems/textsecuregcm/storage/AddRemoveDeviceIntegrationTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
3737
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
3838
import org.whispersystems.textsecuregcm.entities.DeviceInfo;
39+
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
3940
import org.whispersystems.textsecuregcm.identity.IdentityType;
4041
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
4142
import org.whispersystems.textsecuregcm.redis.RedisServerExtension;
@@ -62,6 +63,7 @@ public class AddRemoveDeviceIntegrationTest {
6263
DynamoDbExtensionSchema.Tables.USERNAMES,
6364
DynamoDbExtensionSchema.Tables.EC_KEYS,
6465
DynamoDbExtensionSchema.Tables.PQ_KEYS,
66+
DynamoDbExtensionSchema.Tables.PAGED_PQ_KEYS,
6567
DynamoDbExtensionSchema.Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS,
6668
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS);
6769

@@ -104,7 +106,8 @@ void setUp() {
104106
new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient,
105107
DynamoDbExtensionSchema.Tables.REPEATED_USE_EC_SIGNED_PRE_KEYS.tableName()),
106108
new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient,
107-
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()));
109+
DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()),
110+
mock(ExperimentEnrollmentManager.class));
108111

109112
final ClientPublicKeys clientPublicKeys = new ClientPublicKeys(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
110113
DynamoDbExtensionSchema.Tables.CLIENT_PUBLIC_KEYS.tableName());

0 commit comments

Comments
 (0)