Skip to content

Commit

Permalink
Merge from main
Browse files Browse the repository at this point in the history
  • Loading branch information
akoshelev committed Oct 30, 2024
2 parents 46b25e6 + 77565ae commit f26de0a
Show file tree
Hide file tree
Showing 48 changed files with 1,696 additions and 706 deletions.
34 changes: 33 additions & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,36 @@ While the computation is happening, Management will call the query_status API un

MPC requires thousands of steps to be executed and coordinated across helpers. Each of these calls is represented as a single HTTP call (this becomes more relevant during MPC computation). The service uses HTTP2 multiplexing capabilities, where multiple requests are being sent and received during the same connection.

**Work in progress**
**Work in progress**

# Testing

## Randomness

Random tests can increase coverage by generating unforeseen corner cases. Even with
detailed error messages, the output of a failed random test may not provide enough
information to see what has gone wrong. When this happens, it is important to be
able to re-run the failing case with additional diagnostic output or with candidate
fixes. To make this possible, all random values used in the test should be derived
from a random seed that is logged in the output of a failed test.

Using a random generator provided by `rand::thread_rng` will not typically achieve
reproducibility. Instead, tests should obtain a random number generator by calling
`TestWorld::rng`. An example of such a test is
`breakdown_reveal::tests::semi_honest_happy_path`. To run a test with a particular seed,
pass the seed to `TestWorld::with_seed` (or `TestWorldConfig::with_seed`).

The [`shuttle`](shuttle.md) concurrency test framework provides its own random number
generator and reproducibility mechanisms. The `ipa_core::rand` module automatically
exports either symbols from the `rand` crate or the `shuttle` equivalents based on the
build configuration. If using `TestWorld::rng`, the switch to the `shuttle` RNG is
handled automatically in `TestWorld`. In tests that do not use `TestWorld`, the
`run_random` helper will automatically use the appropriate RNG, and log a seed if using
the standard RNG. An example of such a test is `ordering_sender::test::shuffle_fp31`.
To run a test with a particular seed, use `run_with_seed`.

The `proptest` framework also has its own random number generator and reproducibility
mechanisms, but the `proptest` RNG is not integrated with `TestWorld`. When using
`proptest`, it is recommended to create a random `u64` seed in the proptest-generated
inputs and pass that seed to `TestWorld::with_seed` (or `TestWorldConfig::with_seed`).
An example of such a test is `aggregate_proptest`.
2 changes: 1 addition & 1 deletion ipa-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ ipa-prf = []
relaxed-dp = []

[dependencies]
ipa-metrics = { path = "../ipa-metrics", features = [] }
ipa-metrics = { path = "../ipa-metrics" }
ipa-metrics-tracing = { optional = true, path = "../ipa-metrics-tracing" }
ipa-step = { version = "*", path = "../ipa-step" }
ipa-step-derive = { version = "*", path = "../ipa-step-derive" }
Expand Down
4 changes: 2 additions & 2 deletions ipa-core/src/bin/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use ipa_core::{
error::BoxError,
executor::IpaRuntime,
helpers::HelperIdentity,
net::{ClientIdentity, MpcHelperClient, MpcHttpTransport, ShardHttpTransport},
net::{ClientIdentity, IpaHttpClient, MpcHttpTransport, ShardHttpTransport},
sharding::ShardIndex,
AppConfig, AppSetup, NonZeroU32PowerOfTwo,
};
Expand Down Expand Up @@ -167,7 +167,7 @@ async fn server(args: ServerArgs, logging_handle: LoggingHandle) -> Result<(), B
// ---

let http_runtime = new_http_runtime(&logging_handle);
let clients = MpcHelperClient::from_conf(
let clients = IpaHttpClient::from_conf(
&IpaRuntime::from_tokio_runtime(&http_runtime),
&network_config,
&identity,
Expand Down
6 changes: 3 additions & 3 deletions ipa-core/src/bin/report_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use ipa_core::{
config::{KeyRegistries, NetworkConfig},
ff::{boolean_array::BA32, FieldType},
helpers::query::{DpMechanism, IpaQueryConfig, QueryConfig, QuerySize, QueryType},
net::{Helper, MpcHelperClient},
net::{Helper, IpaHttpClient},
report::{EncryptedOprfReportStreams, DEFAULT_KEY_ID},
test_fixture::{
ipa::{ipa_in_the_clear, CappingOrder, IpaSecurityModel, TestRawDataRecord},
Expand Down Expand Up @@ -333,7 +333,7 @@ async fn ipa(
args: &Args,
security_model: IpaSecurityModel,
ipa_query_config: IpaQueryConfig,
helper_clients: &[MpcHelperClient; 3],
helper_clients: &[IpaHttpClient<Helper>; 3],
encrypted_inputs: &EncryptedInputs,
) -> Result<(), Box<dyn Error>> {
let query_type = get_query_type(security_model, ipa_query_config);
Expand Down Expand Up @@ -383,7 +383,7 @@ async fn ipa_test(
network: &NetworkConfig<Helper>,
security_model: IpaSecurityModel,
ipa_query_config: IpaQueryConfig,
helper_clients: &[MpcHelperClient; 3],
helper_clients: &[IpaHttpClient<Helper>; 3],
) -> Result<(), Box<dyn Error>> {
let input = InputSource::from(&args.input);
let query_type = get_query_type(security_model, ipa_query_config);
Expand Down
12 changes: 6 additions & 6 deletions ipa-core/src/bin/test_mpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use ipa_core::{
QueryConfig,
QueryType::{TestAddInPrimeField, TestMultiply},
},
net::MpcHelperClient,
net::{Helper, IpaHttpClient},
secret_sharing::{replicated::semi_honest::AdditiveShare, IntoShares},
};

Expand Down Expand Up @@ -113,7 +113,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
Ok(())
}

async fn multiply_in_field<F>(args: &Args, helper_clients: &[MpcHelperClient; 3])
async fn multiply_in_field<F>(args: &Args, helper_clients: &[IpaHttpClient<Helper>; 3])
where
F: Field + U128Conversions + IntoShares<AdditiveShare<F>>,
<F as Serializable>::Size: Add<<F as Serializable>::Size>,
Expand All @@ -130,14 +130,14 @@ where
validate(&expected, &actual);
}

async fn multiply(args: &Args, helper_clients: &[MpcHelperClient; 3]) {
async fn multiply(args: &Args, helper_clients: &[IpaHttpClient<Helper>; 3]) {
match args.input.field {
FieldType::Fp31 => multiply_in_field::<Fp31>(args, helper_clients).await,
FieldType::Fp32BitPrime => multiply_in_field::<Fp32BitPrime>(args, helper_clients).await,
};
}

async fn add_in_field<F>(args: &Args, helper_clients: &[MpcHelperClient; 3])
async fn add_in_field<F>(args: &Args, helper_clients: &[IpaHttpClient<Helper>; 3])
where
F: Field + U128Conversions + IntoShares<AdditiveShare<F>>,
<F as Serializable>::Size: Add<<F as Serializable>::Size>,
Expand All @@ -159,13 +159,13 @@ where
validate(&vec![expected], &vec![actual]);
}

async fn add(args: &Args, helper_clients: &[MpcHelperClient; 3]) {
async fn add(args: &Args, helper_clients: &[IpaHttpClient<Helper>; 3]) {
match args.input.field {
FieldType::Fp31 => add_in_field::<Fp31>(args, helper_clients).await,
FieldType::Fp32BitPrime => add_in_field::<Fp32BitPrime>(args, helper_clients).await,
};
}

async fn sharded_shuffle(_args: &Args, _helper_clients: &[MpcHelperClient; 3]) {
async fn sharded_shuffle(_args: &Args, _helper_clients: &[IpaHttpClient<Helper>; 3]) {
unimplemented!()
}
4 changes: 2 additions & 2 deletions ipa-core/src/cli/playbook/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use typenum::Unsigned;
use crate::{
ff::{Field, Serializable},
helpers::{query::QueryInput, BodyStream},
net::MpcHelperClient,
net::{Helper, IpaHttpClient},
protocol::QueryId,
secret_sharing::{replicated::semi_honest::AdditiveShare as Replicated, IntoShares},
test_fixture::Reconstruct,
Expand All @@ -19,7 +19,7 @@ use crate::{
#[allow(clippy::missing_panics_doc, clippy::disallowed_methods)]
pub async fn secure_add<F>(
input: impl Iterator<Item = F>,
clients: &[MpcHelperClient; 3],
clients: &[IpaHttpClient<Helper>; 3],
query_id: QueryId,
) -> F
where
Expand Down
6 changes: 3 additions & 3 deletions ipa-core/src/cli/playbook/ipa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
BodyStream,
},
hpke::PublicKeyRegistry,
net::MpcHelperClient,
net::{Helper, IpaHttpClient},
protocol::{ipa_prf::OPRFIPAInputRow, QueryId},
query::QueryStatus,
report::{KeyIdentifier, OprfReport},
Expand All @@ -37,7 +37,7 @@ use crate::{
/// If report encryption fails
pub async fn playbook_oprf_ipa<HV, KR>(
records: Vec<TestRawDataRecord>,
clients: &[MpcHelperClient; 3],
clients: &[IpaHttpClient<Helper>; 3],
query_id: QueryId,
query_config: IpaQueryConfig,
encryption: Option<(KeyIdentifier, [&KR; 3])>,
Expand Down Expand Up @@ -101,7 +101,7 @@ where
pub async fn run_query_and_validate<HV>(
inputs: [BodyStream; 3],
query_size: usize,
clients: &[MpcHelperClient; 3],
clients: &[IpaHttpClient<Helper>; 3],
query_id: QueryId,
query_config: IpaQueryConfig,
) -> IpaQueryResult
Expand Down
9 changes: 4 additions & 5 deletions ipa-core/src/cli/playbook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
executor::IpaRuntime,
ff::boolean_array::{BA20, BA3, BA8},
helpers::query::DpMechanism,
net::{ClientIdentity, Helper, MpcHelperClient},
net::{ClientIdentity, Helper, IpaHttpClient},
protocol::{dp::NoiseParams, ipa_prf::oprf_padding::insecure::OPRFPaddingDp},
};

Expand Down Expand Up @@ -194,7 +194,7 @@ pub async fn make_clients(
network_path: Option<&Path>,
scheme: Scheme,
wait: usize,
) -> ([MpcHelperClient; 3], NetworkConfig<Helper>) {
) -> ([IpaHttpClient<Helper>; 3], NetworkConfig<Helper>) {
let mut wait = wait;
let network = if let Some(path) = network_path {
NetworkConfig::from_toml_str(&fs::read_to_string(path).unwrap()).unwrap()
Expand All @@ -212,8 +212,7 @@ pub async fn make_clients(

// Note: This closure is only called when the selected action uses clients.

let clients =
MpcHelperClient::from_conf(&IpaRuntime::current(), &network, &ClientIdentity::None);
let clients = IpaHttpClient::from_conf(&IpaRuntime::current(), &network, &ClientIdentity::None);
while wait > 0 && !clients_ready(&clients).await {
tracing::debug!("waiting for servers to come up");
sleep(Duration::from_secs(1)).await;
Expand All @@ -222,7 +221,7 @@ pub async fn make_clients(
(clients, network)
}

async fn clients_ready(clients: &[MpcHelperClient; 3]) -> bool {
async fn clients_ready(clients: &[IpaHttpClient<Helper>; 3]) -> bool {
clients[0].echo("").await.is_ok()
&& clients[1].echo("").await.is_ok()
&& clients[2].echo("").await.is_ok()
Expand Down
4 changes: 2 additions & 2 deletions ipa-core/src/cli/playbook/multiply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use typenum::Unsigned;
use crate::{
ff::{Field, Serializable},
helpers::{query::QueryInput, BodyStream},
net::MpcHelperClient,
net::{Helper, IpaHttpClient},
protocol::QueryId,
secret_sharing::{replicated::semi_honest::AdditiveShare as Replicated, IntoShares},
test_fixture::Reconstruct,
Expand All @@ -21,7 +21,7 @@ use crate::{
pub async fn secure_mul<F>(
// I couldn't make `share` work with `&[(F, F)]`
input: Vec<(F, F)>,
clients: &[MpcHelperClient; 3],
clients: &[IpaHttpClient<Helper>; 3],
query_id: QueryId,
) -> Vec<F>
where
Expand Down
24 changes: 18 additions & 6 deletions ipa-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,27 +532,39 @@ mod tests {
};

const URI_1: &str = "http://localhost:3000";
const URI_1S: &str = "http://localhost:6000";
const URI_2: &str = "http://localhost:3001";
const URI_2S: &str = "http://localhost:6001";
const URI_3: &str = "http://localhost:3002";
const URI_3S: &str = "http://localhost:6002";

#[test]
fn parse_config() {
let conf = TestConfigBuilder::with_http_and_default_test_ports().build();

let uri1 = URI_1.parse::<Uri>().unwrap();
let id1 = HelperIdentity::try_from(1usize).unwrap();
let value1 = &conf.network.peers()[id1];
assert_eq!(value1.url, uri1);
let ring_value1 = &conf.leaders_ring().network.peers()[id1];
assert_eq!(ring_value1.url, uri1);
let uri1s = URI_1S.parse::<Uri>().unwrap();
let sharding_value1 = conf.get_shards_for_helper(id1).network.get_peer(0).unwrap();
assert_eq!(sharding_value1.url, uri1s);

let uri2 = URI_2.parse::<Uri>().unwrap();
let id2 = HelperIdentity::try_from(2usize).unwrap();
let value2 = &conf.network.peers()[id2];
assert_eq!(value2.url, uri2);
let ring_value2 = &conf.leaders_ring().network.peers()[id2];
assert_eq!(ring_value2.url, uri2);
let uri2s = URI_2S.parse::<Uri>().unwrap();
let sharding_value2 = conf.get_shards_for_helper(id2).network.get_peer(0).unwrap();
assert_eq!(sharding_value2.url, uri2s);

let uri3 = URI_3.parse::<Uri>().unwrap();
let id3 = HelperIdentity::try_from(3usize).unwrap();
let value3 = &conf.network.peers()[id3];
assert_eq!(value3.url, uri3);
let ring_value3 = &conf.leaders_ring().network.peers()[id3];
assert_eq!(ring_value3.url, uri3);
let uri3s = URI_3S.parse::<Uri>().unwrap();
let sharding_value3 = conf.get_shards_for_helper(id3).network.get_peer(0).unwrap();
assert_eq!(sharding_value3.url, uri3s);
}

#[test]
Expand Down
18 changes: 17 additions & 1 deletion ipa-core/src/ff/boolean_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,12 @@ macro_rules! boolean_array_impl {

fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
<[u8; $name::STORE_LEN]>::arbitrary_with(args)
.prop_map(|arr| $name(Store::from(arr)))
.prop_map(|arr| {
let mut v = Store::from(arr);
// make sure the value does not overflow
v[$bits..].fill(false);
$name(v)
})
}
}

Expand Down Expand Up @@ -694,6 +699,17 @@ macro_rules! boolean_array_impl {
assert_eq!(a * c, if bool::from(c) { a } else { $name::ZERO });
assert_eq!(a * &c, if bool::from(c) { a } else { $name::ZERO });
}

#[test]
fn serde_prop(a: $name) {
let mut buf = GenericArray::default();
a.serialize(&mut buf);
assert_eq!(
a,
$name::deserialize(&buf).unwrap(),
"Failed to deserialize a valid value: {a:?}"
);
}
}

#[test]
Expand Down
12 changes: 5 additions & 7 deletions ipa-core/src/helpers/buffers/ordering_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,10 +531,9 @@ mod test {
use crate::{
ff::{Fp31, Fp32BitPrime, Gf9Bit, PrimeField, Serializable, U128Conversions},
helpers::MpcMessage,
rand::thread_rng,
secret_sharing::SharedValue,
sync::Arc,
test_executor::run,
test_executor::{run, run_random},
};

fn sender<F: PrimeField>() -> Arc<OrderingSender> {
Expand Down Expand Up @@ -725,9 +724,9 @@ mod test {
}

/// Shuffle `count` indices.
pub fn shuffle_indices(count: usize) -> Vec<usize> {
pub fn shuffle_indices(count: usize, rng: &mut impl Rng) -> Vec<usize> {
let mut indices = (0..count).collect::<Vec<_>>();
indices.shuffle(&mut thread_rng());
indices.shuffle(rng);
indices
}

Expand All @@ -737,11 +736,10 @@ mod test {
const COUNT: usize = 16;
const SZ: usize = <<Fp31 as Serializable>::Size as Unsigned>::USIZE;

run(|| async {
let mut rng = thread_rng();
run_random(|mut rng| async move {
let mut values = Vec::with_capacity(COUNT);
values.resize_with(COUNT, || rng.gen::<Fp31>());
let indices = shuffle_indices(COUNT);
let indices = shuffle_indices(COUNT, &mut rng);

let sender = sender::<Fp31>();
let (_, (), output) = join3(
Expand Down
Loading

0 comments on commit f26de0a

Please sign in to comment.