-
Notifications
You must be signed in to change notification settings - Fork 30
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
Add derivation of precomputed values for fields #769
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.
Just some naive questions, nothing actionable.
self.modulus, | ||
) | ||
) | ||
for i in range(min(self.num_roots, 20) + 1) |
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.
Where does the min(self.num_roots, 20)
come from, i.e. why do we need at least 20 roots?
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.
This is mainly here to match the Rust-side data structure, which uses [u128; 21]
. If a field had a smaller power-of-two-sized multiplicative subgroup, such that we couldn't provide as many roots, then that would impact utility by putting a ceiling on how large of an FFT we could do. 20 should be plenty for most cases, as that lets us call gadgets about a million times (~2^20). We haven't had a need for more roots, and it seems unlikely for now that we will in practice.
Field( | ||
"Field64", | ||
2 ^ 32 * 4294967295 + 1, | ||
pow(7, 4294967295, 2 ^ 32 * 4294967295 + 1), |
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 were the generators originally chosen (I get that they're defined in the spec)? Just some arbitrary value that fit this criteria?
To be FFT-friendly, the order of this subgroup MUST be a power of 2. In addition, the size of the subgroup dictates how large interpolated polynomials can be. It is RECOMMENDED that a generator is chosen with order at least 2^20.
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 wasn't able to figure out how the generator used with FieldPrio2
was selected, but it has the right order, so it works. For the other two, the definitions use a neat trick wherein they start with an arbitrary group member with an order that's too high, and then knock off the unneeded cofactors through exponentiation until they get an element with the desired order. (If
sage: Field64 = GF(2^32 * 4294967295 + 1)
sage: factor(Field64.unit_group().order())
2^32 * 3 * 5 * 17 * 257 * 65537
sage: factor(Field64(7).multiplicative_order())
2^32 * 3 * 5 * 17 * 257 * 65537
sage: factor((Field64(7) ^ 4294967295).multiplicative_order())
2^32
sage: Field128 = GF(2^66 * 4611686018427387897 + 1)
sage: factor(Field128.unit_group().order())
2^66 * 3 * 3491 * 440340496364689
sage: factor(Field128(7).multiplicative_order())
2^66 * 3 * 3491 * 440340496364689
sage: factor((Field128(7) ^ 4611686018427387897).multiplicative_order())
2^66
In #758 , I added documentation about the generation of the constants for field128:
This |
1eed2ff
to
4240c94
Compare
This adds a Sage script that demonstrates how to compute all the values in
src/fp.rs
for each supported FFT-friendly field. It starts from the choices of prime modulus and generator element (two coming fromdraft-irtf-cfrg-vdaf
, and one coming from Prio2 code), reproduces all the values we have in that file currently, and documents what each value means and how they are defined. Note that some fields are pre-transformed into Montgomery representation.