Mastic appears in Proceedings on Privacy Enhancing Technologies (PoPETS), 2025. The preprint can be accessed here; you can cite this work as follows:
@Article{PoPETS:MPDST25,
author = "Dimitris Mouris and
Christopher Patton and
Hannah Davis and
Pratik Sarkar and
Nektarios Georgios Tsoutsos",
title = "{Mastic: Private Weighted Heavy-Hitters and Attribute-Based Metrics}",
year = 2025,
volume = 2025,
month = July,
journal = "{Proceedings on Privacy Enhancing Technologies}",
number = 1,
pages = "290--319",
doi = "10.56553/popets-2025-0017"
}
The following runs two aggregators and the leader each in a different container for weighted heavy hitters:
❯❯ CONFIG=weighted-heavy-hitters.toml docker compose up
Similarly, for the two other modes that Mastic supports:
❯❯ CONFIG=attribute-based-metrics.toml docker compose up
❯❯ CONFIG=plain-metrics.toml docker compose up
First, make sure that you have a working Rust installation:
❯❯ rustc --version
rustc 1.82.0
❯❯ cargo --version
cargo 1.82.0
Next, build from sources using:
❯❯ cargo build --release
...
Finished `release` profile [optimized] target(s) in ...s
The client and servers use a common configuration file, which contains the parameters for the system. The config file is also used to choose between the different modes of operation. Here, we show the basic structure of the config file. Each mode (Weighted Heavy Hitters, Attribute-Based Metrics, and Plain Metrics with Prio) uses a different config. The contents that are shared between all the config files are shown below:
data_bits = 8 # Number of bits of each string.
hist_buckets = 2 # Number of each histogram buckets
# [mode] # Mode of operation, one of:
# mode.weighted_heavy_hitters.threshold = 0.01
# mode.attribute_based_metrics.num_attributes = 10
# mode = "plain_metrics"
server_0 = "0.0.0.0:8000" # The `IP:port` for server 0.
server_1 = "0.0.0.0:8001" # The `IP:port` for server 1.
add_report_share_batch_size = 1000 # Size of RPC requests for transmitting keys.
query_flp_batch_size = 100000 # Size of RPC requests for transmitting FLPs.
zipf_unique_buckets = 1000 # Zipf parameter
zipf_exponent = 1.03 # Zipf exponent
# ...
mode.weighted_heavy_hitters.threshold = 0.01
# ...
Run the aggregators in two separate shells. They will wait and be ready to process client requests.
cargo run --release --bin server -- --config src/configs/weighted-heavy-hitters.toml --server_id 0
cargo run --release --bin server -- --config src/configs/weighted-heavy-hitters.toml --server_id 1
In another shell, send 100 client requests to the Aggregators:
cargo run --release --bin driver -- --config src/configs/weighted-heavy-hitters.toml -n 100
To run with the presence of malicious clients include the --malicious
flag followed by the
percentage of malicious clients to generate ([0.0, 0.9]). For instance, to run with 5% malicious
clients use:
cargo run --release --bin driver -- --config src/configs/weighted-heavy-hitters.toml -n 100 --malicious 0.05
# ...
mode.attribute_based_metrics.num_attributes = 10
# ...
Run the aggregators in two separate shells. They will wait and be ready to process client requests.
cargo run --release --bin server -- --config src/configs/attribute-based-metrics.toml --server_id 0
cargo run --release --bin server -- --config src/configs/attribute-based-metrics.toml --server_id 1
In another shell, send 100 client requests to the Aggregators:
cargo run --release --bin driver -- --config src/configs/attribute-based-metrics.toml -n 100
To run with the presence of malicious clients include the --malicious
flag followed by the
percentage of malicious clients to generate ([0.0, 0.9]). For instance, to run with 5% malicious
clients use:
cargo run --release --bin driver -- --config src/configs/attribute-based-metrics.toml -n 100 --malicious 0.05
# ...
mode = "plain_metrics"
# ...
Run the aggregators in two separate shells. They will wait and be ready to process client requests.
cargo run --release --bin server -- --config src/configs/plain-metrics.toml --server_id 0
cargo run --release --bin server -- --config src/configs/plain-metrics.toml --server_id 1
In another shell, send 100 client requests to the servers:
cargo run --release --bin driver -- --config src/configs/plain-metrics.toml -n 100
To run with the presence of malicious clients include the --malicious
flag followed by the
percentage of malicious clients to generate ([0.0, 0.9]). For instance, to run with 5% malicious
clients use:
cargo run --release --bin driver -- --config src/configs/plain-metrics.toml -n 100 --malicious 0.05
This branch can do Plain Heavy Hitters by setting the histogram size to 1, but a
more efficient implementation uses the Count
circuit and is in the Count
branch.
Mastic relies on the tarpc library which has a limit on the size of the RPC messages. As such, you might see an error similar to the following:
thread 'main' panicked at src/bin/driver.rs:335:
called `Result::unwrap()` on an `Err` value: Disconnected
which is caused by the RPC batch sizes.
To fix this, reduce the batch sizes of either the reports or the FLPs (or both).
add_report_share_batch_size = 1000
query_flp_batch_size = 100000
Note: this does not affect the online running time, but it affects the
upload time from the driver
to the Mastic servers.
This is software for a research prototype and not production-ready code. This repository builds upon plasma, heavy-hitters, and libprio-rs.
This is a Rust implementation of the ideas presented in Mastic Verifiable Distributed Aggregation Function (VDAF) individual Internet-Draft. You can read the draft on the Datatracker Page.
This work was partially supported by the National Science Foundation (Award #2239334).