snarkVM icon indicating copy to clipboard operation
snarkVM copied to clipboard

[Proposal] `to_field_element` bit packing instead of byte packing

Open howardwu opened this issue 4 years ago • 1 comments

💥 Proposal

Currently, converting a [u8] into Vec<F> uses byte-aligned indices to convert to field elements.

    #[inline]
    fn to_field_elements(&self) -> Result<Vec<F>, ConstraintFieldError> {
        let max_size = <F as PrimeField>::Parameters::CAPACITY / 8;
        let max_size = max_size as usize;
        let fes = self
            .chunks(max_size)
            .map(|chunk| {
                let mut chunk = chunk.to_vec();
                chunk.resize(max_size + 1, 0u8);
                F::read_le(chunk.as_slice())
            })
            .collect::<Result<Vec<_>, _>>()?;
        Ok(fes)
    }

So if a field modulus is 383 bits, the current logic would occupy up to 376 bits and leave 6 bits unused. (We cannot use the 383rd-bit given the value could lie outside the modulus)

Instead, one could bit-align it up to the penultimate bit, to better use the space available (hacky pseudocode):

    #[inline]
    fn to_field_elements(&self) -> Result<Vec<F>, ConstraintFieldError> {
        let max_size = <F as PrimeField>::Parameters::CAPACITY;
        let fes = BitIteratorLE::new(self)
            .chunks(max_size)
            .map(|chunk| {
                F::from_repr(BigInteger::from_bititerator(chunk))
            })
            .collect::<Result<Vec<_>, _>>()?;
        Ok(fes)
    }

howardwu avatar Jul 14 '21 00:07 howardwu

It seems that the from_bititerator can be done using BigInteger's from_bits_le. https://github.com/AleoHQ/snarkVM/blob/cb0552a8077a3dd3fcf496bbb3447ed99401e121/utilities/src/bits.rs#L50

The implementation above looks correct. Note that we also need to implement the gadget version (ToConstraintFieldGadget) for [UInt8].

weikengchen avatar Jul 14 '21 00:07 weikengchen