Mistakes in calculation of MAX_UNREDUCED_LIMB_SIZE
Description
The maximum unreduced limb size is computed as follows:
// a (currently generous) upper bound on the log of number of fr additions in any of the class operations
static constexpr uint64_t MAX_ADDITION_LOG = 10;
// the rationale of the expression is we should not overflow Fr when applying any bigfield operation (e.g. *) and
// starting with this max limb size
static constexpr uint64_t MAX_UNREDUCED_LIMB_SIZE = (bb::fr::modulus.get_msb() + 1) / 2 - MAX_ADDITION_LOG;
static constexpr uint256_t get_maximum_unreduced_limb_value() { return uint256_t(1) << MAX_UNREDUCED_LIMB_SIZE; }
Let be the native prime modulus of the circuit, the emulated modulus (so bb::fr::modulus
) and b = bb::fr::modulus.get_msb()
.
The intention here seems to be as follows. What is needed is a bound so that as long as and .
Let us first consider the calculation of (as get_maximum_unreduced_limb_value()
) in the code in the case where the bound for were , so we only consider a single summand. This corresponds to MAX_ADDITION_LOG = 0
. Then we would have . From the definition of , it is the most significant bit of that is set, so we have . (The reason we have and not just is that is an odd prime.) If we assume that is odd, then we would have . Thus, the intended property of would not be satisfied. The issue is the addition of one; if we took instead, then we would have .
However, there appears to be another mistake, this time making the bound less sharp, which overcompensates for the just discussed one. Concretely, suppose is a correct bound as above for the one-summand case, so that . Then for the case of up to summands, the code uses . Then we get that , so is smaller than necessary. Instead of reducing the bit width of both factors by 10 bits, only the products need to be reduced in bit width by 10 bits, for which it suffices to reduce the bit width of each factor by five bits, so in MAX_UNREDUCED_LIMB_SIZE
, the contribution of MAX_ADDITION_LOG
could be divided by two as well.
Impact
The mistake reducing sharpness overcorrects for the other mistake, so there is no impact currently. However, there is a risk that making the MAX_ADDITION_LOG
contribution sharp at a later time without correcting the other mistake will cause a bound that violates the required assumption.
Recommendations
We recommend to replace
static constexpr uint64_t MAX_UNREDUCED_LIMB_SIZE = (bb::fr::modulus.get_msb() + 1) / 2 - MAX_ADDITION_LOG;
by
static constexpr uint64_t MAX_UNREDUCED_LIMB_SIZE = (bb::fr::modulus.get_msb() - MAX_ADDITION_LOG) / 2;