1 /*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "fcp/secagg/client/secagg_client_r3_unmasking_state.h"
18
19 #include <memory>
20 #include <string>
21 #include <unordered_map>
22 #include <vector>
23
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "fcp/base/monitoring.h"
27 #include "fcp/secagg/client/secagg_client_aborted_state.h"
28 #include "fcp/secagg/client/secagg_client_completed_state.h"
29 #include "fcp/secagg/shared/secagg_messages.pb.h"
30 #include "fcp/secagg/shared/secagg_vector.h"
31 #include "fcp/secagg/shared/shamir_secret_sharing.h"
32 #include "fcp/secagg/testing/mock_send_to_server_interface.h"
33 #include "fcp/secagg/testing/mock_state_transition_listener.h"
34 #include "fcp/testing/testing.h"
35
36 namespace fcp {
37 namespace secagg {
38 namespace {
39
40 using ::testing::Eq;
41 using ::testing::Pointee;
42 using ::testing::StrEq;
43
44 static const ShamirShare test_pairwise_share = {"test pairwise share"};
45 static const ShamirShare test_self_share = {"test self share"};
46
TEST(SecAggClientR3UnmaskingStateTest,IsAbortedReturnsFalse)47 TEST(SecAggClientR3UnmaskingStateTest, IsAbortedReturnsFalse) {
48 MockSendToServerInterface* sender = new MockSendToServerInterface();
49 MockStateTransitionListener* transition_listener =
50 new MockStateTransitionListener();
51 SecAggClientR3UnmaskingState r3_state(
52 0, 4, 4, 4, std::make_unique<std::vector<OtherClientState> >(4),
53 std::make_unique<std::vector<ShamirShare> >(4),
54 std::make_unique<std::vector<ShamirShare> >(4),
55 std::unique_ptr<SendToServerInterface>(sender),
56 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
57 EXPECT_THAT(r3_state.IsAborted(), Eq(false));
58 }
59
TEST(SecAggClientR3UnmaskingStateTest,IsCompletedSuccessfullyReturnsFalse)60 TEST(SecAggClientR3UnmaskingStateTest, IsCompletedSuccessfullyReturnsFalse) {
61 MockSendToServerInterface* sender = new MockSendToServerInterface();
62 MockStateTransitionListener* transition_listener =
63 new MockStateTransitionListener();
64 SecAggClientR3UnmaskingState r3_state(
65 0, 4, 4, 4, std::make_unique<std::vector<OtherClientState> >(4),
66 std::make_unique<std::vector<ShamirShare> >(4),
67 std::make_unique<std::vector<ShamirShare> >(4),
68 std::unique_ptr<SendToServerInterface>(sender),
69 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
70 EXPECT_THAT(r3_state.IsCompletedSuccessfully(), Eq(false));
71 }
72
TEST(SecAggClientR3UnmaskingStateTest,StartRaisesErrorStatus)73 TEST(SecAggClientR3UnmaskingStateTest, StartRaisesErrorStatus) {
74 MockSendToServerInterface* sender = new MockSendToServerInterface();
75 MockStateTransitionListener* transition_listener =
76 new MockStateTransitionListener();
77 SecAggClientR3UnmaskingState r3_state(
78 0, 4, 4, 4, std::make_unique<std::vector<OtherClientState> >(4),
79 std::make_unique<std::vector<ShamirShare> >(4),
80 std::make_unique<std::vector<ShamirShare> >(4),
81 std::unique_ptr<SendToServerInterface>(sender),
82 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
83 EXPECT_THAT(r3_state.Start().ok(), Eq(false));
84 }
85
TEST(SecAggClientR3UnmaskingStateTest,SetInputRaisesErrorStatus)86 TEST(SecAggClientR3UnmaskingStateTest, SetInputRaisesErrorStatus) {
87 MockSendToServerInterface* sender = new MockSendToServerInterface();
88 MockStateTransitionListener* transition_listener =
89 new MockStateTransitionListener();
90 SecAggClientR3UnmaskingState r3_state(
91 0, 4, 4, 4, std::make_unique<std::vector<OtherClientState> >(4),
92 std::make_unique<std::vector<ShamirShare> >(4),
93 std::make_unique<std::vector<ShamirShare> >(4),
94 std::unique_ptr<SendToServerInterface>(sender),
95 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
96 EXPECT_THAT(r3_state.SetInput(std::make_unique<SecAggVectorMap>()).ok(),
97 Eq(false));
98 }
99
TEST(SecAggClientR3UnmaskingStateTest,ErrorMessageRaisesErrorStatus)100 TEST(SecAggClientR3UnmaskingStateTest, ErrorMessageRaisesErrorStatus) {
101 MockSendToServerInterface* sender = new MockSendToServerInterface();
102 MockStateTransitionListener* transition_listener =
103 new MockStateTransitionListener();
104 SecAggClientR3UnmaskingState r3_state(
105 0, 4, 4, 4, std::make_unique<std::vector<OtherClientState> >(4),
106 std::make_unique<std::vector<ShamirShare> >(4),
107 std::make_unique<std::vector<ShamirShare> >(4),
108 std::unique_ptr<SendToServerInterface>(sender),
109 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
110 EXPECT_THAT(r3_state.ErrorMessage().ok(), Eq(false));
111 }
112
TEST(SecAggClientR3UnmaskingStateTest,AbortReturnsValidAbortStateAndNotifiesServer)113 TEST(SecAggClientR3UnmaskingStateTest,
114 AbortReturnsValidAbortStateAndNotifiesServer) {
115 MockSendToServerInterface* sender = new MockSendToServerInterface();
116 MockStateTransitionListener* transition_listener =
117 new MockStateTransitionListener();
118
119 std::string error_string =
120 "Abort upon external request for reason <Abort reason>.";
121
122 ClientToServerWrapperMessage expected_message;
123 expected_message.mutable_abort()->set_diagnostic_info(error_string);
124 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
125
126 SecAggClientR3UnmaskingState r3_state(
127 0, 4, 4, 4, std::make_unique<std::vector<OtherClientState> >(4),
128 std::make_unique<std::vector<ShamirShare> >(4),
129 std::make_unique<std::vector<ShamirShare> >(4),
130 std::unique_ptr<SendToServerInterface>(sender),
131 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
132 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
133 r3_state.Abort("Abort reason");
134 ASSERT_THAT(new_state.ok(), Eq(true));
135 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
136 EXPECT_THAT(new_state.value()->ErrorMessage().value(), StrEq(error_string));
137 }
138
TEST(SecAggClientR3UnmaskingStateTest,AbortFailureMessageCausesAbortWithoutNotifyingServer)139 TEST(SecAggClientR3UnmaskingStateTest,
140 AbortFailureMessageCausesAbortWithoutNotifyingServer) {
141 MockSendToServerInterface* sender = new MockSendToServerInterface();
142 MockStateTransitionListener* transition_listener =
143 new MockStateTransitionListener();
144 SecAggClientR3UnmaskingState r3_state(
145 1, // client_id
146 6, // number_of_alive_neighbors
147 4, // minimum_surviving_neighbors_for_reconstruction
148 6, // number_of_neighbors
149 std::make_unique<std::vector<OtherClientState> >(
150 6, OtherClientState::kAlive),
151 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
152 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
153 std::unique_ptr<SendToServerInterface>(sender),
154 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
155
156 EXPECT_CALL(*sender, Send(::testing::_)).Times(0);
157 ServerToClientWrapperMessage abort_message;
158 abort_message.mutable_abort()->set_early_success(false);
159
160 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
161 r3_state.HandleMessage(abort_message);
162 ASSERT_TRUE(new_state.ok());
163 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
164 EXPECT_THAT(new_state.value()->ErrorMessage().value(),
165 StrEq("Aborting because of abort message from the server."));
166 }
167
TEST(SecAggClientR3UnmaskingStateTest,EarlySuccessMessageCausesTransitionToCompletedState)168 TEST(SecAggClientR3UnmaskingStateTest,
169 EarlySuccessMessageCausesTransitionToCompletedState) {
170 MockSendToServerInterface* sender = new MockSendToServerInterface();
171 MockStateTransitionListener* transition_listener =
172 new MockStateTransitionListener();
173 SecAggClientR3UnmaskingState r3_state(
174 1, // client_id
175 6, // number_of_alive_neighbors
176 4, // minimum_surviving_neighbors_for_reconstruction
177 6, // number_of_neighbors
178 std::make_unique<std::vector<OtherClientState> >(
179 6, OtherClientState::kAlive),
180 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
181 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
182 std::unique_ptr<SendToServerInterface>(sender),
183 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
184
185 EXPECT_CALL(*sender, Send(::testing::_)).Times(0);
186 ServerToClientWrapperMessage abort_message;
187 abort_message.mutable_abort()->set_early_success(true);
188
189 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
190 r3_state.HandleMessage(abort_message);
191 ASSERT_TRUE(new_state.ok());
192 EXPECT_THAT(new_state.value()->StateName(), StrEq("COMPLETED"));
193 }
194
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestIsCorrectlyHandledWhenNoClientsDie)195 TEST(SecAggClientR3UnmaskingStateTest,
196 UnmaskingRequestIsCorrectlyHandledWhenNoClientsDie) {
197 // In this test, this is client id 1. There are 6 clients, and none of them
198 // drop out.
199 MockSendToServerInterface* sender = new MockSendToServerInterface();
200 MockStateTransitionListener* transition_listener =
201 new MockStateTransitionListener();
202 SecAggClientR3UnmaskingState r3_state(
203 1, // client_id
204 6, // number_of_alive_neighbors
205 4, // minimum_surviving_neighbors_for_reconstruction
206 6, // number_of_neighbors
207 std::make_unique<std::vector<OtherClientState> >(
208 6, OtherClientState::kAlive),
209 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
210 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
211 std::unique_ptr<SendToServerInterface>(sender),
212 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
213
214 ClientToServerWrapperMessage expected_message;
215 for (int i = 0; i < 6; i++) {
216 expected_message.mutable_unmasking_response()
217 ->add_noise_or_prf_key_shares()
218 ->set_prf_sk_share("test self share");
219 }
220 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
221
222 ServerToClientWrapperMessage request;
223 request.mutable_unmasking_request();
224 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
225 r3_state.HandleMessage(request);
226 ASSERT_TRUE(new_state.ok());
227 EXPECT_THAT(new_state.value()->StateName(), StrEq("COMPLETED"));
228 }
229
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestIsCorrectlyHandledWhenFewClientsDie)230 TEST(SecAggClientR3UnmaskingStateTest,
231 UnmaskingRequestIsCorrectlyHandledWhenFewClientsDie) {
232 // In this test, this is client id 1. Client 3 already died at round 2, and
233 // client 5 dies in round 3.
234 MockSendToServerInterface* sender = new MockSendToServerInterface();
235 MockStateTransitionListener* transition_listener =
236 new MockStateTransitionListener();
237 std::vector<OtherClientState> other_clients_states{
238 OtherClientState::kAlive, OtherClientState::kAlive,
239 OtherClientState::kAlive, OtherClientState::kDeadAtRound2,
240 OtherClientState::kAlive, OtherClientState::kAlive};
241
242 SecAggClientR3UnmaskingState r3_state(
243 1, // client_id
244 5, // number_of_alive_neighbors
245 4, // minimum_surviving_neighbors_for_reconstruction
246 6, // number_of_neighbors
247 std::make_unique<std::vector<OtherClientState> >(other_clients_states),
248 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
249 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
250 std::unique_ptr<SendToServerInterface>(sender),
251 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
252
253 ClientToServerWrapperMessage expected_message;
254 for (int i = 0; i < 6; i++) {
255 if (i == 3) {
256 expected_message.mutable_unmasking_response()
257 ->add_noise_or_prf_key_shares();
258 } else if (i == 5) {
259 expected_message.mutable_unmasking_response()
260 ->add_noise_or_prf_key_shares()
261 ->set_noise_sk_share("test pairwise share");
262 } else {
263 expected_message.mutable_unmasking_response()
264 ->add_noise_or_prf_key_shares()
265 ->set_prf_sk_share("test self share");
266 }
267 }
268 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
269
270 ServerToClientWrapperMessage request;
271 // TODO(team): 6 -> 5 below, once backwards compatibility not needed.
272 request.mutable_unmasking_request()->add_dead_3_client_ids(6);
273 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
274 r3_state.HandleMessage(request);
275 ASSERT_TRUE(new_state.ok());
276 EXPECT_THAT(new_state.value()->StateName(), StrEq("COMPLETED"));
277 }
278
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestCausesAbortWhenTooManyClientsDie)279 TEST(SecAggClientR3UnmaskingStateTest,
280 UnmaskingRequestCausesAbortWhenTooManyClientsDie) {
281 // In this test, this is client id 1. Client 3 already died at round 2, and
282 // clients 4 and 5 die in round 3. This should cause a transition to an abort
283 // state and an abort message to be sent to the server.
284 MockSendToServerInterface* sender = new MockSendToServerInterface();
285 MockStateTransitionListener* transition_listener =
286 new MockStateTransitionListener();
287 std::vector<OtherClientState> other_clients_states{
288 OtherClientState::kAlive, OtherClientState::kAlive,
289 OtherClientState::kAlive, OtherClientState::kDeadAtRound2,
290 OtherClientState::kAlive, OtherClientState::kAlive};
291
292 SecAggClientR3UnmaskingState r3_state(
293 1, // client_id
294 5, // number_of_alive_neighbors
295 4, // minimum_surviving_neighbors_for_reconstruction
296 6, // number_of_neighbors
297 std::make_unique<std::vector<OtherClientState> >(other_clients_states),
298 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
299 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
300 std::unique_ptr<SendToServerInterface>(sender),
301 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
302
303 std::string error_string =
304 "Not enough clients survived. The server should not have sent this "
305 "UnmaskingRequest.";
306 ClientToServerWrapperMessage expected_message;
307 expected_message.mutable_abort()->set_diagnostic_info(error_string);
308 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
309
310 ServerToClientWrapperMessage request;
311 // TODO(team): 5 -> 4 below, once backwards compatibility not needed.
312 request.mutable_unmasking_request()->add_dead_3_client_ids(5);
313 // TODO(team): 6 -> 5 below, once backwards compatibility not needed.
314 request.mutable_unmasking_request()->add_dead_3_client_ids(6);
315 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
316 r3_state.HandleMessage(request);
317 ASSERT_TRUE(new_state.ok());
318 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
319 EXPECT_THAT(new_state.value()->ErrorMessage().value(), StrEq(error_string));
320 }
321
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestCausesAbortIfServerListsThisClientAsDead)322 TEST(SecAggClientR3UnmaskingStateTest,
323 UnmaskingRequestCausesAbortIfServerListsThisClientAsDead) {
324 // In this test, this is client id 1, but the server lists client 1 as dead.
325 // This should cause a transition to an abort state and an abort message to be
326 // sent to the server.
327 MockSendToServerInterface* sender = new MockSendToServerInterface();
328 MockStateTransitionListener* transition_listener =
329 new MockStateTransitionListener();
330
331 SecAggClientR3UnmaskingState r3_state(
332 1, // client_id
333 6, // number_of_alive_neighbors
334 4, // minimum_surviving_neighbors_for_reconstruction
335 6, // number_of_neighbors
336 std::make_unique<std::vector<OtherClientState> >(
337 6, OtherClientState::kAlive),
338 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
339 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
340 std::unique_ptr<SendToServerInterface>(sender),
341 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
342
343 std::string error_string =
344 "The received UnmaskingRequest states this client has aborted, but this "
345 "client had not yet aborted.";
346 ClientToServerWrapperMessage expected_message;
347 expected_message.mutable_abort()->set_diagnostic_info(error_string);
348 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
349
350 ServerToClientWrapperMessage request;
351 // TODO(team): 2 -> 1 below, once backwards compatibility not needed.
352 request.mutable_unmasking_request()->add_dead_3_client_ids(2);
353 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
354 r3_state.HandleMessage(request);
355 ASSERT_TRUE(new_state.ok());
356 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
357 EXPECT_THAT(new_state.value()->ErrorMessage().value(), StrEq(error_string));
358 }
359
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestCausesAbortIfServerListsNonexistentClientAsDead)360 TEST(SecAggClientR3UnmaskingStateTest,
361 UnmaskingRequestCausesAbortIfServerListsNonexistentClientAsDead) {
362 // In this test, there are 6 clients (labeled 0-5), but the server lists
363 // client 6 as dead. This should cause a transition to an abort state and an
364 // abort message to be sent to the server.
365 MockSendToServerInterface* sender = new MockSendToServerInterface();
366 MockStateTransitionListener* transition_listener =
367 new MockStateTransitionListener();
368
369 SecAggClientR3UnmaskingState r3_state(
370 1, // client_id
371 6, // number_of_alive_neighbors
372 4, // minimum_surviving_neighbors_for_reconstruction
373 6, // number_of_neighbors
374 std::make_unique<std::vector<OtherClientState> >(
375 6, OtherClientState::kAlive),
376 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
377 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
378 std::unique_ptr<SendToServerInterface>(sender),
379 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
380
381 std::string error_string =
382 "The received UnmaskingRequest contains a client id that does not "
383 "correspond to any client.";
384 ClientToServerWrapperMessage expected_message;
385 expected_message.mutable_abort()->set_diagnostic_info(error_string);
386 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
387
388 ServerToClientWrapperMessage request;
389 // TODO(team): 7 -> 6 below, once backwards compatibility not needed.
390 request.mutable_unmasking_request()->add_dead_3_client_ids(7);
391 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
392 r3_state.HandleMessage(request);
393 ASSERT_TRUE(new_state.ok());
394 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
395 EXPECT_THAT(new_state.value()->ErrorMessage().value(), StrEq(error_string));
396 }
397
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestCausesAbortIfServerListsClientThatAlreadyDied)398 TEST(SecAggClientR3UnmaskingStateTest,
399 UnmaskingRequestCausesAbortIfServerListsClientThatAlreadyDied) {
400 // In this test, client 3 died at round 1, but the server lists client 3 as
401 // dead at round 3. This should cause a transition to an abort state and an
402 // abort message to be sent to the server.
403 MockSendToServerInterface* sender = new MockSendToServerInterface();
404 MockStateTransitionListener* transition_listener =
405 new MockStateTransitionListener();
406 std::vector<OtherClientState> other_clients_states{
407 OtherClientState::kAlive, OtherClientState::kAlive,
408 OtherClientState::kAlive, OtherClientState::kDeadAtRound1,
409 OtherClientState::kAlive, OtherClientState::kAlive};
410
411 SecAggClientR3UnmaskingState r3_state(
412 1, // client_id
413 5, // number_of_alive_neighbors
414 4, // minimum_surviving_neighbors_for_reconstruction
415 6, // number_of_neighbors
416 std::make_unique<std::vector<OtherClientState> >(other_clients_states),
417 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
418 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
419 std::unique_ptr<SendToServerInterface>(sender),
420 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
421
422 std::string error_string =
423 "The received UnmaskingRequest considers a client dead in round 3 "
424 "that was already considered dead.";
425 ClientToServerWrapperMessage expected_message;
426 expected_message.mutable_abort()->set_diagnostic_info(error_string);
427 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
428
429 ServerToClientWrapperMessage request;
430 // TODO(team): 4 -> 3 below, once backwards compatibility not needed.
431 request.mutable_unmasking_request()->add_dead_3_client_ids(4);
432 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
433 r3_state.HandleMessage(request);
434 ASSERT_TRUE(new_state.ok());
435 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
436 EXPECT_THAT(new_state.value()->ErrorMessage().value(), StrEq(error_string));
437 }
438
TEST(SecAggClientR3UnmaskingStateTest,UnmaskingRequestCausesAbortIfServerListsSameClientTwice)439 TEST(SecAggClientR3UnmaskingStateTest,
440 UnmaskingRequestCausesAbortIfServerListsSameClientTwice) {
441 // In this test, the server lists client 5 as dead at round 3 twice. This
442 // should cause a transition to an abort state and an abort message to be sent
443 // to the server.
444 MockSendToServerInterface* sender = new MockSendToServerInterface();
445 MockStateTransitionListener* transition_listener =
446 new MockStateTransitionListener();
447
448 SecAggClientR3UnmaskingState r3_state(
449 1, // client_id
450 6, // number_of_alive_neighbors
451 4, // minimum_surviving_neighbors_for_reconstruction
452 6, // number_of_neighbors
453 std::make_unique<std::vector<OtherClientState> >(
454 6, OtherClientState::kAlive),
455 std::make_unique<std::vector<ShamirShare> >(6, test_pairwise_share),
456 std::make_unique<std::vector<ShamirShare> >(6, test_self_share),
457 std::unique_ptr<SendToServerInterface>(sender),
458 std::unique_ptr<StateTransitionListenerInterface>(transition_listener));
459
460 std::string error_string =
461 "The received UnmaskingRequest repeated a client more than once as a "
462 "dead client.";
463 ClientToServerWrapperMessage expected_message;
464 expected_message.mutable_abort()->set_diagnostic_info(error_string);
465 EXPECT_CALL(*sender, Send(Pointee(EqualsProto(expected_message))));
466
467 ServerToClientWrapperMessage request;
468 // TODO(team): 6 -> 5 below, once backwards compatibility not needed.
469 request.mutable_unmasking_request()->add_dead_3_client_ids(6);
470 request.mutable_unmasking_request()->add_dead_3_client_ids(6);
471 StatusOr<std::unique_ptr<SecAggClientState> > new_state =
472 r3_state.HandleMessage(request);
473 ASSERT_TRUE(new_state.ok());
474 EXPECT_THAT(new_state.value()->StateName(), StrEq("ABORTED"));
475 EXPECT_THAT(new_state.value()->ErrorMessage().value(), StrEq(error_string));
476 }
477
478 } // namespace
479 } // namespace secagg
480 } // namespace fcp
481