-
Notifications
You must be signed in to change notification settings - Fork 31
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
make vdaf::verify_finish
take a slice
#85
Conversation
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.
Dope! While at it, do you think we should do the same thing for dist_input()
and dist_init()
, i.e., make these output a vector of constant size?
src/vdaf.rs
Outdated
M: TryInto<[VerifierMessage<V::Field>; N], Error = E>, | ||
E: std::fmt::Debug, |
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.
It would be nice to reduce the number of type parameters. Is this possible?
M: TryInto<[VerifierMessage<V::Field>; N], Error = E>, | |
E: std::fmt::Debug, | |
M: TryInto<[VerifierMessage<V::Field>; N], Error: std::fmt::Debug>, |
It would avoid the additional type parameter E
. (Also, I think Debug
is imported by default these days.)
Alternatively, I would be comfortable with a panic!()
here instead of propagating the error. This is less of an input validation issue than it is a programmer error.
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.
I tried doing that but you can't do it yet. There's a Rust RFC, it's implemented, but it's not yet stabilized.
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.
In that case my preference would be to panic instead of propagating the error.
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.
Sure, I struggle to imagine this failing for any reason beyond whatever collection msg
is having the wrong number of elements, so I think we can just panic!
and get rid of the Debug
trait bound.
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.
cjpatton and I chatted about this and got extremely galaxy brained about it. I'm going to try changing this so that verify_finish
takes IntoIterator<Item = VerifierMessage<V::Field>>
, which ✅ allows callers to provide any kind of slice or a vec or whatever and ✅ is infallible so we don't have to deal with any type bounds on errors.
src/vdaf.rs
Outdated
{ | ||
let msgs: [VerifierMessage<V::Field>; N] = msgs.try_into().map_err(|e| { | ||
VdafError::Uncategorized(format!( | ||
"input could not be converted to VerifierMessage slice: {:?}", |
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.
"input could not be converted to VerifierMessage slice: {:?}", | |
"verify_finish(): input could not be converted to VerifierMessage slice: {:?}", |
For consistency
src/vdaf.rs
Outdated
@@ -468,7 +480,8 @@ mod tests { | |||
// Aggregators decide whether the input is valid based on the verifier messages. | |||
let mut output = vec![Field64::zero(); input.as_slice().len()]; | |||
for state in states { | |||
let output_share = verify_finish(state, verifiers.clone()).unwrap(); | |||
let output_share = | |||
verify_finish::<_, _, _, NUM_SHARES>(state, verifiers.clone()).unwrap(); |
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.
I'm curious if all of the type parameters, including NUM_SHARES
, could be inferred if verifiers
had type [VerifierMessage<Boolean<Field64>>; NUM_SHARES]
.
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.
Yes, you could instead do:
let verifiers_slice: [VerifierMessage<Field64>; NUM_SHARES] = verifiers.try_into().unwrap();
for state in states {
let output_share = verify_finish(state, verifiers_slice.clone()).unwrap();
<...>
I wanted to prove that passing in a Vec<VerifierMessage<F>>
works, though. What would be cool is if Rust had named trait bounds, so that instead of specifying NUM_SHARES
positionally, I could do let output_share = verify_finish::<N = NUM_SHARES>(...);
, since the _, _, _,
is rather ugly.
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.
I'm not concerened about the ugliness here, I was just curious.
src/vdaf.rs
Outdated
@@ -374,10 +374,22 @@ pub fn verify_start<V: Value>( | |||
/// [`VerifierMessage`] messages broadcast by all of the aggregators and produces the aggregator's | |||
/// input share. | |||
// TODO(cjpatton) Check for ciphersuite mismatch between `state` and `msgs` and among `msgs`. | |||
pub fn verify_finish<V: Value>( | |||
pub fn verify_finish<M, E, V, const N: usize>( |
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.
How about SHARES
instead of N
? This would align nicely with the spec.
Yeah, that is a good idea, if we're prepared to commit to making the number of shares a parameter that's known at compile time. I think that's fine because |
If we're going to constrain the number of inputs to verify_finish, then it makes sense to constrain the other functions the same way, since they would be effectively constrained at the protocol level anyway. I think |
I tried using const generics to make
The equivalent construction with an array would be something like:
Because you can't have an uninitialized array in Rust. The problem is that we need to provide an |
Tagging @branlwyd for visibility -- don't worry if none of this makes sense, I just want you to be in the loop on libprio-rs changes. |
`verify_finish` wants to consume a collection of `VerifierMessage`. Accomplishing this with `Vec<VerifierMessage>` forces allocation and copying to the heap. We instead take `IntoIterator<Item = VerifierMessage>`, which allows callers to pass either a slice of `VerifierMessage` on the stack or a `Vec` on the heap, or anything else that implements `IntoIterator`. Besides the flexibility, `IntoIterator.into_iter` is infallible, so this doesn't introduce any new error cases to handle.
534d9cf
to
dd4c7b8
Compare
verify_finish
wants to consume a collection ofVerifierMessage
.Accomplishing this with
Vec<VerifierMessage>
forces allocation andcopying to the heap. We now instead use const generics and take
M: TryInto<[VerifierMessage; const N: usize]>
, so that callers that holdeither a
[VerifierMessage]
on the stack or aVec<VerifierMessage>
onthe heap, or anything else that can be converted to a slice of
VerifierMessage
, can useverify_finish
.