-
Notifications
You must be signed in to change notification settings - Fork 599
feat(avm): ToRadix gadget #12528
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
Merged
Merged
feat(avm): ToRadix gadget #12528
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
1835e37
first approach
sirasistant 4cc9a50
testing
sirasistant 50c60c4
safe approach
sirasistant 69f17fe
failing test
sirasistant 4153f8b
remove debug stuffs
sirasistant c4caa89
test
sirasistant d81606a
Merge branch 'master' into arv/to_radix_gadget
sirasistant 68083f1
fix
sirasistant 7fd3631
cleanup naming a bit
sirasistant 8f733c3
add negative tests
sirasistant 14c3331
fix
sirasistant 7835ecf
fmt
sirasistant 133fa61
opt
sirasistant 3f61a49
fix: size_t when accessing limbs per radix
sirasistant c266600
size_t fixes
sirasistant 7d13a09
add comment explaining eq trick
sirasistant 0bf1001
compute p_limbs_per_radix in comptime
sirasistant 4a896b0
style
sirasistant abd73b3
Revert "style"
sirasistant c8451fc
refactor: lazy initialization of p limbs per radix
sirasistant 674833c
style
sirasistant 9a394e1
fmt
sirasistant d04df66
snake case
sirasistant File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,221 @@ | ||
| include "range_check.pil"; | ||
|
|
||
| // Decomposes a field in radixes | ||
| // Roughly, what we do is keep track of an accumulator where we reconstruct value using the limbs. We can end when the accumulator is equal to the value (found). | ||
| // For overflow protection, we compare the limbs against the p limbs for the given radix. If we reach the last limb (is_unsafe) we assert that we are under p. | ||
| // Overflow in the accumulator can only happen in the unsafe limb. After the unsafe limb we have padding limbs, that we assert to be zero. This is used | ||
| // because users might want to decompose in more limbs that strictly needed for the radix. | ||
| // The following table is non-exhaustive, we do have some extra columns to compute these values. | ||
| // +-------+-------+------+--------+------------+------+-------------+----------+------------------+-------+------------+-----------+-------+-----+-----+---------+ | ||
| // | value | radix | limb | p_limb | limb_index | acc | acc_under_p | exponent | not_padding_limb | found | safe_limbs | is_unsafe | start | end | sel | not_end | | ||
| // +-------+-------+------+--------+------------+------+-------------+----------+------------------+-------+------------+-----------+-------+-----+-----+---------+ | ||
| // | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | ||
| // | 1337 | 10 | 7 | 7 | 0 | 7 | 0 | 1 | 1 | 0 | 76 | 0 | 1 | 0 | 1 | 1 | | ||
| // | 1337 | 10 | 3 | 1 | 1 | 37 | 0 | 10 | 1 | 0 | 76 | 0 | 0 | 0 | 1 | 1 | | ||
| // | 1337 | 10 | 3 | 6 | 2 | 337 | 1 | 100 | 1 | 0 | 76 | 0 | 0 | 0 | 1 | 1 | | ||
| // | 1337 | 10 | 1 | 5 | 3 | 1337 | 1 | 1000 | 1 | 1 | 76 | 0 | 0 | 0 | 1 | 1 | | ||
| // | 1337 | 10 | 0 | 9 | 4 | 1337 | 1 | 10000 | 1 | 1 | 76 | 0 | 0 | 0 | 1 | 1 | | ||
| // | 1337 | 10 | 0 | 2 | 76 | 1337 | 1 | 10**76 | 1 | 1 | 76 | 1 | 0 | 0 | 1 | 1 | | ||
| // | 1337 | 10 | 0 | 0 | 77 | 1337 | 0 | 0 | 0 | 1 | 76 | 0 | 0 | 1 | 1 | 0 | | ||
| // | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | ||
| // +-------+-------+------+--------+------------+------+-------------+----------+------------------+-------+------------+-----------+-------+-----+-----+---------+ | ||
| namespace to_radix; | ||
|
|
||
| #[skippable_if] | ||
| sel = 0; | ||
|
|
||
| pol commit sel; | ||
| sel * (1 - sel) = 0; | ||
|
|
||
| // Inputs to to_radix | ||
| pol commit value; | ||
| pol commit radix; // Must be 256 >= radix >= 2 | ||
| pol commit limb_index; | ||
|
|
||
| // Outputs of to_radix | ||
| pol commit limb; | ||
|
|
||
|
|
||
| // LIFECYCLE | ||
|
|
||
| pol commit start; | ||
| start * (1 - start) = 0; | ||
|
|
||
| pol commit end; | ||
| end * (1 - end) = 0; | ||
|
|
||
| // end and first_row are NAND | ||
| end * precomputed.first_row = 0; | ||
| pol LATCH_CONDITION = end + precomputed.first_row; | ||
|
|
||
| #[START_AFTER_LATCH] | ||
| sel' * (start' - LATCH_CONDITION) = 0; | ||
|
|
||
| // Selector must be 1 in a start row | ||
| #[SELECTOR_ON_START] | ||
| start * (1 - sel) = 0; | ||
| // Next selector must be current selector unless LATCH_CONDITION | ||
| #[SELECTOR_CONSISTENCY] | ||
| (sel' - sel) * (1 - LATCH_CONDITION) = 0; | ||
| // Selector must be 1 in an end row | ||
| end * (1 - sel) = 0; | ||
|
|
||
| // Commited to reduce the degree of expressions | ||
| pol commit not_end; | ||
| sel * (1 - end) - not_end = 0; | ||
|
|
||
|
|
||
| // EXPONENTIATION | ||
|
|
||
| pol commit exponent; | ||
| // We commit not_padding_limb instead of the more natural padding_limb because it's going to be used in a lookup | ||
| pol commit not_padding_limb; | ||
| not_padding_limb * (1 - not_padding_limb) = 0; | ||
|
|
||
| // exponent starts at 1 | ||
| start * (exponent - 1) = 0; | ||
| // next exponent is current exponent * radix unless it's a padding limb | ||
| not_end * not_padding_limb' * (exponent * radix - exponent') = 0; | ||
| // not_padding_limb starts at 1 | ||
| start * (1 - not_padding_limb) = 0; | ||
| // next not not_padding_limb is current not_padding_limb unless current limb is unsafe, where it changes to 0 | ||
| not_end * ((0 - not_padding_limb) * is_unsafe_limb + not_padding_limb - not_padding_limb') = 0; | ||
|
|
||
| // Padding limbs have zero exponent | ||
| (1 - not_padding_limb) * exponent = 0; | ||
|
|
||
|
|
||
| // ACCUMULATION | ||
|
|
||
| pol commit acc; | ||
| pol commit found; | ||
| found * (1 - found) = 0; | ||
|
|
||
| // Limb index starts at zero | ||
| start * (limb_index - 0) = 0; | ||
|
|
||
| // Limb index must increase | ||
| not_end * (limb_index + 1 - limb_index') = 0; | ||
|
|
||
| // Range check limb so we can safely assert that it's less than radix. | ||
| #[LIMB_RANGE] | ||
| sel { limb } | ||
| in | ||
| precomputed.sel_range_8 | ||
| { precomputed.clk }; | ||
|
|
||
| // Limb should be less than radix | ||
| pol commit limb_radix_diff; | ||
| sel * (radix - 1 - limb - limb_radix_diff) = 0; | ||
|
|
||
| #[LIMB_LESS_THAN_RADIX_RANGE] | ||
| sel { limb_radix_diff } | ||
| in | ||
| precomputed.sel_range_8 | ||
| { precomputed.clk }; | ||
|
|
||
|
|
||
| // On start, current acc must be equal to limb | ||
| start * (acc - limb) = 0; | ||
| // Next acc must be current acc + next_exponent*next_limb | ||
| not_end * (acc + exponent' * limb' - acc') = 0; | ||
|
|
||
| // found is 1 when value - acc = 0 | ||
| pol REM = value - acc; | ||
| pol commit rem_inverse; | ||
| sel * (REM * (found * (1 - rem_inverse) + rem_inverse) - 1 + found) = 0; | ||
|
|
||
| // when found is 1, next limb is 0 | ||
| not_end * found * limb' = 0; | ||
|
|
||
| // We can only enable end when found is one | ||
| (1 - found) * end = 0; | ||
|
|
||
|
|
||
| // OVERFLOW PROTECTION | ||
|
|
||
| pol commit safe_limbs; | ||
|
|
||
| #[FETCH_SAFE_LIMBS] | ||
| start { radix, safe_limbs } | ||
| in | ||
| precomputed.sel_to_radix_safe_limbs | ||
| { precomputed.clk, precomputed.to_radix_safe_limbs }; | ||
|
|
||
| pol commit is_unsafe_limb; | ||
| is_unsafe_limb * (1 - is_unsafe_limb) = 0; | ||
|
|
||
| // If is_padding_limb, limb is zero | ||
| (1 - not_padding_limb) * limb = 0; | ||
| // If is_padding_limb, p_limb is zero | ||
| (1 - not_padding_limb) * p_limb = 0; | ||
|
|
||
| // is_unsafe_limb is on when and only when limb_index == safe_limbs | ||
| pol safety_diff = limb_index - safe_limbs; | ||
| pol commit safety_diff_inverse; | ||
| sel * (safety_diff * (is_unsafe_limb * (1 - safety_diff_inverse) + safety_diff_inverse) - 1 + is_unsafe_limb) = 0; | ||
|
|
||
| // The limb of the modulus p decomposed by radix at this limb_index | ||
| pol commit p_limb; | ||
|
|
||
| #[FETCH_P_LIMB] | ||
| not_padding_limb { radix, limb_index, p_limb } | ||
| in | ||
| precomputed.sel_p_decomposition | ||
| { precomputed.p_decomposition_radix, precomputed.p_decomposition_limb_index, precomputed.p_decomposition_limb }; | ||
|
|
||
| // We carry wether the accumulator is under p. If we reach the unsafe limb, we need to ensure that this is true. | ||
| pol commit acc_under_p; | ||
| acc_under_p * (1 - acc_under_p) = 0; | ||
|
|
||
| // 1 when lt, 0 when gt or equal | ||
| pol commit limb_lt_p; | ||
| limb_lt_p * (1 - limb_lt_p) = 0; | ||
|
|
||
| pol commit limb_eq_p; | ||
| limb_eq_p * (1 - limb_eq_p) = 0; | ||
|
|
||
| // limb_eq_p and limb_lt_p are NAND | ||
| limb_eq_p * limb_lt_p = 0; | ||
|
|
||
| // validate limb_lt_p and limb_eq_p | ||
| pol commit limb_p_diff; | ||
|
|
||
| pol LIMB_LT_P = p_limb - limb - 1; | ||
| pol LIMB_GT_P = limb - p_limb - 1; | ||
| // We already need an 8 bit range check for the LT/GT cases. We can reuse that range check | ||
| // to validate the EQ case, instead of doing the classical inversion mechanism. | ||
| // (limb - p_limb) can go from -255 to +255, so if we multiply by 256 any number in the range | ||
| // will be greater than 8 bits except for 0. | ||
| pol LIMB_EQ_P = (limb - p_limb) * 256; | ||
|
|
||
| // limb_p_diff = if limb_lt_p {LIMB_LT_P} else if limb_eq_p {LIMB_EQ_P} else {LIMB_GT_P} | ||
| limb_lt_p * (LIMB_LT_P - limb_p_diff) = 0; | ||
| sel * (1 - limb_lt_p) * ((LIMB_EQ_P - LIMB_GT_P) * limb_eq_p + LIMB_GT_P - limb_p_diff) = 0; | ||
|
|
||
| #[LIMB_P_DIFF_RANGE] | ||
| not_padding_limb { limb_p_diff } | ||
| in | ||
| precomputed.sel_range_8 | ||
| { precomputed.clk }; | ||
|
|
||
| // acc_under_p is on start is equal to limb_lt_p | ||
| start * (acc_under_p - limb_lt_p) = 0; | ||
| // acc_under_p' = limb_eq_p' ? acc_under_p : limb_lt_p' | ||
| not_end * ((acc_under_p - limb_lt_p') * limb_eq_p' + limb_lt_p' - acc_under_p') = 0; | ||
|
|
||
| // On the the unsafe limb, we must be under p | ||
| #[OVERFLOW_CHECK] | ||
| is_unsafe_limb * (1 - acc_under_p) = 0; | ||
|
|
||
|
|
||
| // CONSTANT CONSISTENCY | ||
|
|
||
| #[CONSTANT_CONSISTENCY_RADIX] | ||
| not_end * (radix - radix') = 0; | ||
|
|
||
| #[CONSTANT_CONSISTENCY_VALUE] | ||
| not_end * (value - value') = 0; | ||
|
|
||
| #[CONSTANT_CONSISTENCY_SAFE_LIMBS] | ||
| not_end * (safe_limbs - safe_limbs') = 0; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| #include "barretenberg/vm2/common/to_radix.hpp" | ||
|
|
||
| #include "barretenberg/numeric/uint256/uint256.hpp" | ||
| #include "barretenberg/vm2/common/field.hpp" | ||
|
|
||
| namespace bb::avm2 { | ||
|
|
||
| namespace { | ||
|
|
||
| // The little endian decompositions of Fr modulus into limbs for each radix. | ||
| // Radix goes up to 256 so we need 257 descompositions. | ||
| std::array<std::vector<uint8_t>, 257> create_p_limbs_per_radix() | ||
| { | ||
| std::array<std::vector<uint8_t>, 257> limbs_per_radix; | ||
|
|
||
| for (size_t radix = 2; radix < 257; ++radix) { | ||
| std::vector<uint8_t> p_limbs{}; | ||
| p_limbs.reserve(31); | ||
| uint256_t p = FF::modulus; | ||
| while (p > 0) { | ||
| p_limbs.push_back(static_cast<uint8_t>(p % radix)); | ||
| p /= radix; | ||
| } | ||
|
|
||
| limbs_per_radix[radix] = p_limbs; | ||
| } | ||
|
|
||
| return limbs_per_radix; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| const std::array<std::vector<uint8_t>, 257>& get_p_limbs_per_radix() | ||
| { | ||
| static const std::array<std::vector<uint8_t>, 257> limbs_per_radix = create_p_limbs_per_radix(); | ||
| return limbs_per_radix; | ||
| } | ||
|
|
||
| } // namespace bb::avm2 | ||
sirasistant marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #pragma once | ||
|
|
||
| #include <array> | ||
| #include <cstdint> | ||
| #include <vector> | ||
|
|
||
| namespace bb::avm2 { | ||
|
|
||
| const std::array<std::vector<uint8_t>, 257>& get_p_limbs_per_radix(); | ||
|
|
||
| } // namespace bb::avm2 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.