openvm_pairing_circuit/fp12_chip/
mul.rs

1use std::{
2    cell::RefCell,
3    rc::Rc,
4    sync::{Arc, Mutex},
5};
6
7use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory};
8use openvm_circuit_derive::InstructionExecutor;
9use openvm_circuit_primitives::var_range::{
10    SharedVariableRangeCheckerChip, VariableRangeCheckerBus,
11};
12use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter};
13use openvm_mod_circuit_builder::{
14    ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreChip,
15};
16use openvm_pairing_transpiler::Fp12Opcode;
17use openvm_rv32_adapters::Rv32VecHeapAdapterChip;
18use openvm_stark_backend::p3_field::PrimeField32;
19
20use crate::Fp12;
21// Input: Fp12 * 2
22// Output: Fp12
23#[derive(Chip, ChipUsageGetter, InstructionExecutor)]
24pub struct Fp12MulChip<F: PrimeField32, const BLOCKS: usize, const BLOCK_SIZE: usize>(
25    pub  VmChipWrapper<
26        F,
27        Rv32VecHeapAdapterChip<F, 2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
28        FieldExpressionCoreChip,
29    >,
30);
31
32impl<F: PrimeField32, const BLOCKS: usize, const BLOCK_SIZE: usize>
33    Fp12MulChip<F, BLOCKS, BLOCK_SIZE>
34{
35    pub fn new(
36        adapter: Rv32VecHeapAdapterChip<F, 2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
37        config: ExprBuilderConfig,
38        xi: [isize; 2],
39        offset: usize,
40        range_checker: SharedVariableRangeCheckerChip,
41        offline_memory: Arc<Mutex<OfflineMemory<F>>>,
42    ) -> Self {
43        let expr = fp12_mul_expr(config, range_checker.bus(), xi);
44        let core = FieldExpressionCoreChip::new(
45            expr,
46            offset,
47            vec![Fp12Opcode::MUL as usize],
48            vec![],
49            range_checker,
50            "Fp12Mul",
51            false,
52        );
53        Self(VmChipWrapper::new(adapter, core, offline_memory))
54    }
55}
56
57pub fn fp12_mul_expr(
58    config: ExprBuilderConfig,
59    range_bus: VariableRangeCheckerBus,
60    xi: [isize; 2],
61) -> FieldExpr {
62    config.check_valid();
63    let builder = ExprBuilder::new(config, range_bus.range_max_bits);
64    let builder = Rc::new(RefCell::new(builder));
65
66    let mut x = Fp12::new(builder.clone());
67    let mut y = Fp12::new(builder.clone());
68    let mut res = x.mul(&mut y, xi);
69    res.save_output();
70
71    let builder = builder.borrow().clone();
72    FieldExpr::new(builder, range_bus, false)
73}
74
75#[cfg(test)]
76mod tests {
77    use halo2curves_axiom::{bn256::Fq12, ff::Field};
78    use itertools::Itertools;
79    use openvm_circuit::arch::testing::{VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS};
80    use openvm_circuit_primitives::bitwise_op_lookup::{
81        BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip,
82    };
83    use openvm_ecc_guest::algebra::field::FieldExtension;
84    use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode};
85    use openvm_mod_circuit_builder::{
86        test_utils::{biguint_to_limbs, bn254_fq12_to_biguint_vec, bn254_fq2_to_biguint_vec},
87        ExprBuilderConfig,
88    };
89    use openvm_pairing_guest::bn254::{BN254_MODULUS, BN254_XI_ISIZE};
90    use openvm_rv32_adapters::rv32_write_heap_default_with_increment;
91    use openvm_stark_backend::p3_field::FieldAlgebra;
92    use openvm_stark_sdk::p3_baby_bear::BabyBear;
93    use rand::{rngs::StdRng, SeedableRng};
94
95    use super::*;
96
97    const LIMB_BITS: usize = 8;
98    type F = BabyBear;
99
100    #[test]
101    fn test_fp12_mul_bn254() {
102        const NUM_LIMBS: usize = 32;
103        const BLOCK_SIZE: usize = 32;
104
105        let mut tester: VmChipTestBuilder<F> = VmChipTestBuilder::default();
106        let config = ExprBuilderConfig {
107            modulus: BN254_MODULUS.clone(),
108            num_limbs: NUM_LIMBS,
109            limb_bits: LIMB_BITS,
110        };
111        let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS);
112        let bitwise_chip = SharedBitwiseOperationLookupChip::<RV32_CELL_BITS>::new(bitwise_bus);
113        let adapter = Rv32VecHeapAdapterChip::<F, 2, 12, 12, BLOCK_SIZE, BLOCK_SIZE>::new(
114            tester.execution_bus(),
115            tester.program_bus(),
116            tester.memory_bridge(),
117            tester.address_bits(),
118            bitwise_chip.clone(),
119        );
120
121        let mut chip = Fp12MulChip::new(
122            adapter,
123            config,
124            BN254_XI_ISIZE,
125            Fp12Opcode::CLASS_OFFSET,
126            tester.range_checker(),
127            tester.offline_memory_mutex_arc(),
128        );
129
130        let mut rng = StdRng::seed_from_u64(64);
131        let x = Fq12::random(&mut rng);
132        let y = Fq12::random(&mut rng);
133        let inputs = [x.to_coeffs(), y.to_coeffs()]
134            .concat()
135            .iter()
136            .flat_map(|&x| bn254_fq2_to_biguint_vec(x))
137            .collect::<Vec<_>>();
138
139        let cmp = bn254_fq12_to_biguint_vec(x * y);
140        let res = chip
141            .0
142            .core
143            .expr()
144            .execute_with_output(inputs.clone(), vec![true]);
145        assert_eq!(res.len(), cmp.len());
146        for i in 0..res.len() {
147            assert_eq!(res[i], cmp[i]);
148        }
149
150        let x_limbs = inputs[..12]
151            .iter()
152            .map(|x| {
153                biguint_to_limbs::<NUM_LIMBS>(x.clone(), LIMB_BITS)
154                    .map(BabyBear::from_canonical_u32)
155            })
156            .collect_vec();
157        let y_limbs = inputs[12..]
158            .iter()
159            .map(|y| {
160                biguint_to_limbs::<NUM_LIMBS>(y.clone(), LIMB_BITS)
161                    .map(BabyBear::from_canonical_u32)
162            })
163            .collect_vec();
164        let instruction = rv32_write_heap_default_with_increment(
165            &mut tester,
166            x_limbs,
167            y_limbs,
168            512,
169            chip.0.core.air.offset + Fp12Opcode::MUL as usize,
170        );
171        tester.execute(&mut chip, &instruction);
172        let tester = tester.build().load(chip).load(bitwise_chip).finalize();
173        tester.simple_test().expect("Verification failed");
174    }
175}