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#[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}