-
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
Use any FieldElement and clean up field API #22
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.
Looks great! While it's a lot of lines changed, I'm pleased at how gracefully F: FieldElement
replaces Field
all over the place. All I have is some obtuse gripes about error handling and interfaces on FieldElement
.
src/client.rs
Outdated
/// Construct a new Prio client | ||
pub fn new(dimension: usize, public_key1: PublicKey, public_key2: PublicKey) -> Option<Self> { | ||
let n = (dimension + 1).next_power_of_two(); | ||
|
||
if 2 * n > Field::generator_order() as usize { | ||
if F::Integer::try_from(2 * n).unwrap() > F::generator_order() { |
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.
Since we're already breaking API compat and now that there's two failure modes in this function (2n
can either be too large for F::Integer
or bigger than generator_order
) I think this should return Result<Self, ClientError>
and provide a friendlier error message that whatever the panic from unwrap()
would be.
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.
Done. (Please take a look at the diff ... I had to refactor things a bit.)
let integer = <Field as FieldElement>::Integer::from_le_bytes(chunk.try_into().unwrap()); | ||
vec.push(Field::from(integer)); | ||
pub fn deserialize<F: FieldElement>(data: &[u8]) -> Result<Vec<F>, FieldError> { | ||
let mut vec = Vec::<F>::with_capacity(data.len() / F::BYTES); |
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.
Should this check that data.len()
is a multiple of F::BYTES
?
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.
Done. (Needed to refactor the code to propagate this error.)
src/server.rs
Outdated
@@ -189,12 +190,13 @@ pub fn generate_verification_message( | |||
mem.points_h[0] = *unpacked.h0; | |||
|
|||
// set points_f and points_g | |||
let one = F::root(0).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.
It seems like we now have quite a few places where we call F::root(0).unwrap()
. That bugs me a bit because at each callsite, the reader has to go "wait, if this method can ever fail, why it is safe and cool to .unwrap()
here?" Your answer is going to be that for any correct implementation of FieldElement
, F::root(0)
cannot fail. So what if we introduce a method fn FieldElement::zeroth_root(&self) -> Self
, which internally does F::root(0).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'll go with F::one()
, since this is always the multiplicative identity.
src/server.rs
Outdated
loop { | ||
let eval_at = Field::from(rand::random::<u32>()); | ||
let eval_at = F::rand(&mut rng); |
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 seems like everywhere F::rand
is used, the caller has to provide a rand::Rng
, which is a bit of a hassle. I see the value in exposing a variant that lets the caller provide an RNG, but could we perhaps have two variants, one that takes no arguments and defaults to rand::thread_rng()
and another that lets the caller provide a rand::Rng
?
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.
Hmm, well already have random_field_from_seed
which maps a seed to a field element. The client and aggregators need to agree on how to do this procedure. This is basically doing the same thing as the current fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self
API, which is a little messy. I think we should just nix fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self;
call and just have fn rand() -> Self
.
fn append_to(&self, bytes: &mut Vec<u8>) { | ||
let int = $fp.from_elem(self.0); | ||
let mut slice = [0; Self::BYTES]; | ||
for i in 0..Self::BYTES { | ||
slice[i] = ((int >> (i << 3)) & 0xff) as u8; | ||
} | ||
bytes.extend_from_slice(&slice); | ||
} | ||
|
||
fn read_from(bytes: &[u8]) -> Result<Self, FieldError> { | ||
if Self::BYTES > bytes.len() { | ||
return Err(FieldError::FromBytesShortRead); | ||
} | ||
|
||
let mut int = 0; | ||
for i in 0..Self::BYTES { | ||
int |= (bytes[i] as u128) << (i << 3); | ||
} | ||
|
||
if int >= $fp.p { | ||
return Err(FieldError::FromBytesModulusOverflow); | ||
} | ||
Ok(Self($fp.elem(int))) | ||
} |
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 bugs me slightly that these methods aren't symmetric. It also seems unfortunate that append_to
is only capable of appending to a Vec<u8>
. It would be more generally useful to provide routines that allow transforming a FieldElement
to and from [u8]
, and then you could do anything you want with the byte array, whether it's appending to a Vec
or writing to an std::io::Write
.
I wonder: what if instead of these two methods on FieldElement
, you had FieldElement
require TryFrom<[u8]>
, and provided From $elem for [u8]
?
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.
As discussed on the design call, @acmiyaguchi will look into replacing these functions with implementations of whatever traits are needed by serde. See #24. I've added TODOs in the code to remind us.
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.
A couple nits about the ?
operator but this is essentially good to go. Please address those, then squash as you see fit, and I'll merge. Thanks!
This change generalizes the code for generating and validating proofs to work with any implementation of the FieldElement trait. It also makes the following API-breaking changes: * Rename Field to Field32 in order to signify that it implements a 32-bit field * Rename FiniteFieldError to FieldError * Move finite_field to field * Simplify the FieldElement API a bit
Squashed and ready to merge. |
Closes #10.
This PR includes the following changes:
FieldElement
trait.Field
toField32
in order to signify that it implements a 32-bit field.finite_field
module to simplyfield
.FiniteFieldError
toFieldError
.