Skip to content

Commit f3cc1f0

Browse files
authored
Handle intersection when client is larger than server (#151)
* use the max of client and server inputs when computing the bits of the bloomfilter * fix gcs * typo * tests * cleanup * Update WASM tests * package lock * use min * fix * fix rollup * revert fpr * revert js tests * use better fpr
1 parent 32d6116 commit f3cc1f0

File tree

13 files changed

+88
-64
lines changed

13 files changed

+88
-64
lines changed

CHANGES.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# Version 1.1.0
2+
3+
Bugfix:
4+
5+
- Fixed an issue when computing an intersection with the client's set **larger**
6+
than the server's set. In this case, the intersection reported more values
7+
than possible. e.g. computing an intersection with a client set of 100 values
8+
and server with 10 values sometimes resulted in 12.
9+
110
# Version 1.0.3
211

312
Bugfix:

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openmined/psi.js",
3-
"version": "1.0.3",
3+
"version": "1.1.0",
44
"description": "Private Set Intersection for JavaScript",
55
"repository": {
66
"type": "git",

private_set_intersection/cpp/bloom_filter.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ BloomFilter::BloomFilter(
3333
context_(std::move(context)) {}
3434

3535
StatusOr<std::unique_ptr<BloomFilter>> BloomFilter::Create(
36-
double fpr, absl::Span<const std::string> elements) {
37-
ASSIGN_OR_RETURN(auto filter, CreateEmpty(fpr, elements.size()));
36+
double fpr, int64_t num_client_inputs,
37+
absl::Span<const std::string> elements) {
38+
auto num_server_inputs = static_cast<int64_t>(elements.size());
39+
ASSIGN_OR_RETURN(auto filter, CreateEmpty(fpr, std::max(num_client_inputs,
40+
num_server_inputs)));
41+
3842
filter->Add(elements);
3943
// This move seems to be needed for some versions of GCC. See for example this
4044
// failing build:

private_set_intersection/cpp/bloom_filter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ class BloomFilter {
4545
BloomFilter() = delete;
4646

4747
static StatusOr<std::unique_ptr<BloomFilter>> Create(
48-
double fpr, absl::Span<const std::string> elements);
48+
double fpr, int64_t num_client_inputs,
49+
absl::Span<const std::string> elements);
4950

5051
// Creates a new Bloom filter. As long as less than `max_elements` are
5152
// inserted, the probability of false positives when performing checks

private_set_intersection/cpp/gcs.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ GCS::GCS(std::string golomb, int64_t div, int64_t hash_range,
3535
context_(std::move(context)) {}
3636

3737
StatusOr<std::unique_ptr<GCS>> GCS::Create(
38-
double fpr, absl::Span<const std::string> elements) {
38+
double fpr, int64_t num_client_inputs,
39+
absl::Span<const std::string> elements) {
3940
if (fpr <= 0 || fpr >= 1) {
4041
return absl::InvalidArgumentError("`fpr` must be in (0,1)");
4142
}
42-
43-
auto hash_range = static_cast<int64_t>(elements.size() / fpr);
43+
auto num_server_inputs = static_cast<int64_t>(elements.size());
44+
auto hash_range = static_cast<int64_t>(
45+
std::max(num_client_inputs, num_server_inputs) / fpr);
4446
std::vector<int64_t> hashes;
4547
hashes.reserve(elements.size());
4648
auto context = absl::make_unique<::private_join_and_compute::Context>();

private_set_intersection/cpp/gcs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class GCS {
3333
GCS() = delete;
3434

3535
static StatusOr<std::unique_ptr<GCS>> Create(
36-
double fpr, absl::Span<const std::string> elements);
36+
double fpr, int64_t num_client_inputs,
37+
absl::Span<const std::string> elements);
3738

3839
static StatusOr<std::unique_ptr<GCS>> CreateFromProtobuf(
3940
const psi_proto::ServerSetup& encoded_set);

private_set_intersection/cpp/gcs_test.cpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ namespace {
3030

3131
TEST(GCSTest, TestIntersect) {
3232
std::vector<std::string> elements = {"a", "b", "c", "d"};
33+
std::vector<std::string> elements2 = {"a", "b", "d", "e"};
34+
auto max_inputs =
35+
static_cast<int64_t>(std::max(elements.size(), elements2.size()));
3336

3437
std::unique_ptr<GCS> gcs;
3538
PSI_ASSERT_OK_AND_ASSIGN(
36-
gcs,
37-
GCS::Create(0.001, absl::MakeConstSpan(&elements[0], elements.size())));
39+
gcs, GCS::Create(0.001, max_inputs,
40+
absl::MakeConstSpan(&elements[0], elements.size())));
3841

39-
std::vector<std::string> elements2 = {"a", "b", "d", "e"};
4042
std::vector<int64_t> intersect = {0, 1, 2};
4143

4244
auto res =
@@ -63,7 +65,7 @@ TEST(GCSTest, TestFPR) {
6365

6466
std::unique_ptr<GCS> gcs;
6567
PSI_ASSERT_OK_AND_ASSIGN(
66-
gcs, GCS::Create(target_fpr,
68+
gcs, GCS::Create(target_fpr, (int64_t)elements.size(),
6769
absl::MakeConstSpan(&elements[0], elements.size())));
6870

6971
// Test 10k elements to measure FPR.
@@ -98,8 +100,8 @@ TEST(GCSTest, TestToProtobuf) {
98100

99101
std::unique_ptr<GCS> gcs;
100102
PSI_ASSERT_OK_AND_ASSIGN(
101-
gcs,
102-
GCS::Create(fpr, absl::MakeConstSpan(&elements[0], elements.size())));
103+
gcs, GCS::Create(fpr, (int64_t)elements.size(),
104+
absl::MakeConstSpan(&elements[0], elements.size())));
103105

104106
// Create the protobuf from the GCS and check if it matches.
105107
psi_proto::ServerSetup encoded_gcs = gcs->ToProtobuf();
@@ -110,17 +112,19 @@ TEST(GCSTest, TestToProtobuf) {
110112

111113
TEST(GCSTest, TestCreateFromProtobuf) {
112114
std::vector<std::string> elements = {"a", "b", "c", "d"};
115+
std::vector<std::string> elements2 = {"a", "b", "c", "d", "not present"};
116+
auto max_inputs =
117+
static_cast<int64_t>(std::max(elements.size(), elements2.size()));
113118

114119
std::unique_ptr<GCS> gcs;
115120
PSI_ASSERT_OK_AND_ASSIGN(
116-
gcs,
117-
GCS::Create(0.001, absl::MakeConstSpan(&elements[0], elements.size())));
121+
gcs, GCS::Create(0.001, max_inputs,
122+
absl::MakeConstSpan(&elements[0], elements.size())));
118123

119124
psi_proto::ServerSetup encoded_gcs = gcs->ToProtobuf();
120125
std::unique_ptr<GCS> gcs2;
121126
PSI_ASSERT_OK_AND_ASSIGN(gcs2, GCS::CreateFromProtobuf(encoded_gcs));
122127

123-
std::vector<std::string> elements2 = {"a", "b", "c", "d", "not present"};
124128
auto res =
125129
gcs2->Intersect(absl::MakeConstSpan(&elements2[0], elements2.size()));
126130
std::vector<int64_t> intersect = {0, 1, 2, 3};
@@ -149,7 +153,7 @@ TEST(GCSTest, TestGolombSize) {
149153
for (size_t i = 0; i < sizeof(fpr) / sizeof(double); i++) {
150154
std::unique_ptr<GCS> gcs;
151155
PSI_ASSERT_OK_AND_ASSIGN(
152-
gcs, GCS::Create(fpr[i],
156+
gcs, GCS::Create(fpr[i], (int64_t)elements.size(),
153157
absl::MakeConstSpan(&elements[0], elements.size())));
154158
auto res =
155159
gcs->Intersect(absl::MakeConstSpan(&elements[0], elements.size()));

private_set_intersection/cpp/psi_client_test.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class PsiClientTest : public ::testing::Test {
5555
// Insert server elements into GCS.
5656
PSI_ASSERT_OK_AND_ASSIGN(
5757
auto gcs,
58-
GCS::Create(fpr, absl::MakeConstSpan(&elements[0], elements.size())));
58+
GCS::Create(fpr, (int64_t)elements.size(),
59+
absl::MakeConstSpan(&elements[0], elements.size())));
5960
*server_setup = gcs->ToProtobuf();
6061
}
6162

private_set_intersection/cpp/psi_server.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ StatusOr<psi_proto::ServerSetup> PsiServer::CreateSetupMessage(
7777
// Create a GCS and insert elements into it.
7878
ASSIGN_OR_RETURN(
7979
auto gcs,
80-
GCS::Create(corrected_fpr,
80+
GCS::Create(corrected_fpr, num_client_inputs,
8181
absl::MakeConstSpan(&encrypted[0], encrypted.size())));
8282

8383
// Return the GCS as a Protobuf
@@ -86,7 +86,7 @@ StatusOr<psi_proto::ServerSetup> PsiServer::CreateSetupMessage(
8686
// Create a Bloom Filter and insert elements into it.
8787
ASSIGN_OR_RETURN(auto filter,
8888
BloomFilter::Create(
89-
corrected_fpr,
89+
corrected_fpr, num_client_inputs,
9090
absl::MakeConstSpan(&encrypted[0], encrypted.size())));
9191

9292
// Return the Bloom Filter as a Protobuf

0 commit comments

Comments
 (0)