arnaucube
arnaucube/plonky2-ecdsaPublic Self-hosted repository
plonky2-ecdsa/src/gadgets/split_nonnative.rs
src/gadgets/split_nonnative.rsRaw
  1use alloc::vec::Vec;
  2use core::marker::PhantomData;
  3
  4use itertools::Itertools;
  5use plonky2::field::extension::Extendable;
  6use plonky2::field::types::Field;
  7use plonky2::hash::hash_types::RichField;
  8use plonky2::iop::target::Target;
  9use plonky2::plonk::circuit_builder::CircuitBuilder;
 10use plonky2_u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target};
 11
 12use crate::gadgets::biguint::BigUintTarget;
 13use crate::gadgets::nonnative::NonNativeTarget;
 14
 15pub trait CircuitBuilderSplit<F: RichField + Extendable<D>, const D: usize> {
 16    fn split_u32_to_4_bit_limbs(&mut self, val: U32Target) -> Vec<Target>;
 17
 18    fn split_nonnative_to_4_bit_limbs<FF: Field>(
 19        &mut self,
 20        val: &NonNativeTarget<FF>,
 21    ) -> Vec<Target>;
 22
 23    fn split_nonnative_to_2_bit_limbs<FF: Field>(
 24        &mut self,
 25        val: &NonNativeTarget<FF>,
 26    ) -> Vec<Target>;
 27
 28    // Note: assumes its inputs are 4-bit limbs, and does not range-check.
 29    fn recombine_nonnative_4_bit_limbs<FF: Field>(
 30        &mut self,
 31        limbs: Vec<Target>,
 32    ) -> NonNativeTarget<FF>;
 33}
 34
 35impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderSplit<F, D>
 36    for CircuitBuilder<F, D>
 37{
 38    fn split_u32_to_4_bit_limbs(&mut self, val: U32Target) -> Vec<Target> {
 39        let two_bit_limbs = self.split_le_base::<4>(val.0, 16);
 40        let four = self.constant(F::from_canonical_usize(4));
 41        let combined_limbs = two_bit_limbs
 42            .iter()
 43            .tuples()
 44            .map(|(&a, &b)| self.mul_add(b, four, a))
 45            .collect();
 46
 47        combined_limbs
 48    }
 49
 50    fn split_nonnative_to_4_bit_limbs<FF: Field>(
 51        &mut self,
 52        val: &NonNativeTarget<FF>,
 53    ) -> Vec<Target> {
 54        val.value
 55            .limbs
 56            .iter()
 57            .flat_map(|&l| self.split_u32_to_4_bit_limbs(l))
 58            .collect()
 59    }
 60
 61    fn split_nonnative_to_2_bit_limbs<FF: Field>(
 62        &mut self,
 63        val: &NonNativeTarget<FF>,
 64    ) -> Vec<Target> {
 65        val.value
 66            .limbs
 67            .iter()
 68            .flat_map(|&l| self.split_le_base::<4>(l.0, 16))
 69            .collect()
 70    }
 71
 72    // Note: assumes its inputs are 4-bit limbs, and does not range-check.
 73    fn recombine_nonnative_4_bit_limbs<FF: Field>(
 74        &mut self,
 75        limbs: Vec<Target>,
 76    ) -> NonNativeTarget<FF> {
 77        let base = self.constant_u32(1 << 4);
 78        let u32_limbs = limbs
 79            .chunks(8)
 80            .map(|chunk| {
 81                let mut combined_chunk = self.zero_u32();
 82                for i in (0..8).rev() {
 83                    let (low, _high) = self.mul_add_u32(combined_chunk, base, U32Target(chunk[i]));
 84                    combined_chunk = low;
 85                }
 86                combined_chunk
 87            })
 88            .collect();
 89
 90        NonNativeTarget {
 91            value: BigUintTarget { limbs: u32_limbs },
 92            _phantom: PhantomData,
 93        }
 94    }
 95}
 96
 97#[cfg(test)]
 98mod tests {
 99    use anyhow::Result;
100    use plonky2::field::secp256k1_scalar::Secp256K1Scalar;
101    use plonky2::field::types::Sample;
102    use plonky2::iop::witness::PartialWitness;
103    use plonky2::plonk::circuit_data::CircuitConfig;
104    use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
105
106    use super::*;
107    use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget};
108
109    #[test]
110    fn test_split_nonnative() -> Result<()> {
111        type FF = Secp256K1Scalar;
112        const D: usize = 2;
113        type C = PoseidonGoldilocksConfig;
114        type F = <C as GenericConfig<D>>::F;
115
116        let config = CircuitConfig::standard_ecc_config();
117        let pw = PartialWitness::new();
118        let mut builder = CircuitBuilder::<F, D>::new(config);
119
120        let x = FF::rand();
121        let x_target = builder.constant_nonnative(x);
122        let split = builder.split_nonnative_to_4_bit_limbs(&x_target);
123        let combined: NonNativeTarget<Secp256K1Scalar> =
124            builder.recombine_nonnative_4_bit_limbs(split);
125        builder.connect_nonnative(&x_target, &combined);
126
127        let data = builder.build::<C>();
128        let proof = data.prove(pw).unwrap();
129        data.verify(proof)
130    }
131}