-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hybrid shuffle #1387
base: main
Are you sure you want to change the base?
Hybrid shuffle #1387
Changes from 3 commits
cd90468
d8f31f8
7f61f8e
fba348b
9677ec9
3b1587f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
use crate::{ | ||
error::Error, | ||
ff::boolean_array::{BooleanArray, BA112, BA144, BA64}, | ||
protocol::{ | ||
context::Context, | ||
ipa_prf::{ | ||
boolean_ops::{expand_shared_array_in_place, extract_from_shared_array}, | ||
shuffle::Shuffle, | ||
}, | ||
}, | ||
report::hybrid::IndistinguishableHybridReport, | ||
secret_sharing::{ | ||
replicated::{semi_honest::AdditiveShare, ReplicatedSecretSharing}, | ||
SharedValue, | ||
}, | ||
}; | ||
|
||
/// Shuffles a Vec of IndistinguishableHybridReport | ||
/// # Errors | ||
/// Propogates errors from ctx.shuffle | ||
#[tracing::instrument(name = "shuffle_inputs", skip_all)] | ||
pub async fn shuffle_hybrid_inputs<C, BK, V>( | ||
ctx: C, | ||
input: Vec<IndistinguishableHybridReport<BK, V>>, | ||
) -> Result<Vec<IndistinguishableHybridReport<BK, V>>, Error> | ||
where | ||
C: Context + Shuffle, | ||
BK: BooleanArray, | ||
V: BooleanArray, | ||
{ | ||
let shuffle_input: Vec<AdditiveShare<BA112>> = input | ||
.into_iter() | ||
.map(|item| hybrid_report_to_shuffle_input::<BA112, BK, V>(&item)) | ||
.collect::<Vec<_>>(); | ||
|
||
let shuffled = ctx.shuffle::<BA112, BA144, _>(shuffle_input).await?; | ||
|
||
Ok(shuffled | ||
.into_iter() | ||
.map(|item| shuffled_to_hybrid_report(&item)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: |
||
.collect::<Vec<_>>()) | ||
} | ||
|
||
/// Converts `IndistinguishableHybridReport` into | ||
/// an `AdditiveShare` needed for shuffle protocol. | ||
pub fn hybrid_report_to_shuffle_input<YS, BK, V>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it make more sense to implement these as Right now,
but you could also consider updating that so that Updating this now will either mean updating existing IPA to work the same way (yuck!), so maybe we wait for now. |
||
input: &IndistinguishableHybridReport<BK, V>, | ||
) -> AdditiveShare<YS> | ||
where | ||
YS: BooleanArray, | ||
BK: BooleanArray, | ||
V: BooleanArray, | ||
{ | ||
let mut y = AdditiveShare::new(YS::ZERO, YS::ZERO); | ||
expand_shared_array_in_place(&mut y, &input.match_key, 0); | ||
|
||
let mut offset = BA64::BITS as usize; | ||
|
||
expand_shared_array_in_place(&mut y, &input.breakdown_key, offset); | ||
|
||
offset += BK::BITS as usize; | ||
expand_shared_array_in_place(&mut y, &input.value, offset); | ||
|
||
y | ||
} | ||
|
||
/// Converts an `AdditiveShare` obtained from shuffle protocol | ||
/// into an `IndistinguishableHybridReport`. | ||
pub fn shuffled_to_hybrid_report<YS, BK, V>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these go away with the new sharded shuffle and its There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, they do. |
||
input: &AdditiveShare<YS>, | ||
) -> IndistinguishableHybridReport<BK, V> | ||
where | ||
YS: BooleanArray, | ||
BK: BooleanArray, | ||
V: BooleanArray, | ||
{ | ||
let match_key = extract_from_shared_array::<YS, BA64>(input, 0); | ||
|
||
let mut offset = BA64::BITS as usize; | ||
|
||
let breakdown_key = extract_from_shared_array::<YS, BK>(input, offset); | ||
|
||
offset += BK::BITS as usize; | ||
let value = extract_from_shared_array::<YS, V>(input, offset); | ||
|
||
IndistinguishableHybridReport { | ||
match_key, | ||
value, | ||
breakdown_key, | ||
} | ||
} | ||
|
||
#[cfg(all(test, unit_test))] | ||
pub mod tests { | ||
use rand::Rng; | ||
|
||
use super::{hybrid_report_to_shuffle_input, shuffle_hybrid_inputs, shuffled_to_hybrid_report}; | ||
use crate::{ | ||
ff::boolean_array::{BA112, BA3, BA8}, | ||
report::hybrid::IndistinguishableHybridReport, | ||
secret_sharing::{ | ||
replicated::{semi_honest::AdditiveShare, ReplicatedSecretSharing}, | ||
SharedValue, | ||
}, | ||
test_executor::run, | ||
test_fixture::{hybrid::TestIndistinguishableHybridReport, Reconstruct, Runner, TestWorld}, | ||
}; | ||
|
||
#[test] | ||
fn hybrid_shuffle_conversion() { | ||
let mut rng = rand::thread_rng(); | ||
let report = IndistinguishableHybridReport::<BA8, BA3> { | ||
match_key: AdditiveShare::new(rng.gen(), rng.gen()), | ||
breakdown_key: AdditiveShare::new(rng.gen(), rng.gen()), | ||
value: AdditiveShare::new(rng.gen(), rng.gen()), | ||
}; | ||
|
||
let additive_share = hybrid_report_to_shuffle_input::<BA112, BA8, BA3>(&report); | ||
assert_ne!(additive_share.left(), BA112::ZERO); | ||
assert_ne!(additive_share.right(), BA112::ZERO); | ||
|
||
let report_copy = shuffled_to_hybrid_report::<BA112, BA8, BA3>(&additive_share); | ||
assert_eq!(report, report_copy); | ||
} | ||
|
||
#[test] | ||
fn test_shuffle_hybrid_inputs() { | ||
const BATCHSIZE: usize = 50; | ||
run(|| async { | ||
let world = TestWorld::default(); | ||
|
||
let mut rng = rand::thread_rng(); | ||
let mut records = Vec::new(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Can you do something like?
|
||
|
||
for _ in 0..BATCHSIZE { | ||
records.push({ | ||
TestIndistinguishableHybridReport { | ||
match_key: rng.gen::<u64>(), | ||
breakdown_key: rng.gen_range(0u32..1 << 8), | ||
value: rng.gen_range(0u32..1 << 3), | ||
} | ||
}); | ||
} | ||
|
||
let mut result: Vec<TestIndistinguishableHybridReport> = world | ||
.semi_honest(records.clone().into_iter(), |ctx, input_rows| async move { | ||
shuffle_hybrid_inputs::<_, BA8, BA3>(ctx, input_rows) | ||
.await | ||
.unwrap() | ||
}) | ||
.await | ||
.reconstruct(); | ||
assert_ne!(result, records); | ||
records.sort(); | ||
result.sort(); | ||
assert_eq!(result, records); | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Don't think you need the owning iterator. See comment bellow.