openvm_ecc_circuit/weierstrass_chip/double/
mod.rs

1use std::{
2    cell::RefCell,
3    ops::{Deref, DerefMut},
4    rc::Rc,
5};
6
7use num_bigint::BigUint;
8use num_traits::One;
9use openvm_circuit::{
10    arch::*,
11    system::memory::{offline_checker::MemoryBridge, SharedMemoryHelper},
12};
13use openvm_circuit_primitives::{
14    bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip},
15    var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerBus},
16};
17use openvm_ecc_transpiler::Rv32WeierstrassOpcode;
18use openvm_instructions::riscv::RV32_CELL_BITS;
19use openvm_mod_circuit_builder::{
20    ExprBuilder, ExprBuilderConfig, FieldExpr, FieldExpressionCoreAir, FieldExpressionExecutor,
21    FieldExpressionFiller, FieldVariable,
22};
23use openvm_rv32_adapters::{
24    Rv32VecHeapAdapterAir, Rv32VecHeapAdapterExecutor, Rv32VecHeapAdapterFiller,
25};
26
27use super::{
28    curves::{get_curve_type, CurveType},
29    WeierstrassAir, WeierstrassChip,
30};
31
32mod execution;
33
34pub fn ec_double_ne_expr(
35    config: ExprBuilderConfig, // The coordinate field.
36    range_bus: VariableRangeCheckerBus,
37    a_biguint: BigUint,
38) -> FieldExpr {
39    config.check_valid();
40    let builder = ExprBuilder::new(config, range_bus.range_max_bits);
41    let builder = Rc::new(RefCell::new(builder));
42
43    let mut x1 = ExprBuilder::new_input(builder.clone());
44    let mut y1 = ExprBuilder::new_input(builder.clone());
45    let a = ExprBuilder::new_const(builder.clone(), a_biguint.clone());
46    let is_double_flag = (*builder).borrow_mut().new_flag();
47    // We need to prevent divide by zero when not double flag
48    // (equivalently, when it is the setup opcode)
49    let lambda_denom = FieldVariable::select(
50        is_double_flag,
51        &y1.int_mul(2),
52        &ExprBuilder::new_const(builder.clone(), BigUint::one()),
53    );
54    let mut lambda = (x1.square().int_mul(3) + a) / lambda_denom;
55    let mut x3 = lambda.square() - x1.int_mul(2);
56    x3.save_output();
57    let mut y3 = lambda * (x1 - x3.clone()) - y1;
58    y3.save_output();
59
60    let builder = (*builder).borrow().clone();
61    FieldExpr::new_with_setup_values(builder, range_bus, true, vec![a_biguint])
62}
63
64/// BLOCK_SIZE: how many cells do we read at a time, must be a power of 2.
65/// BLOCKS: how many blocks do we need to represent one input or output
66/// For example, for bls12_381, BLOCK_SIZE = 16, each element has 3 blocks and with two elements per
67/// input AffinePoint, BLOCKS = 6. For secp256k1, BLOCK_SIZE = 32, BLOCKS = 2.
68// Note: PreflightExecutor is implemented manually in preflight.rs with fast native arithmetic
69#[derive(Clone)]
70pub struct EcDoubleExecutor<const BLOCKS: usize, const BLOCK_SIZE: usize> {
71    pub(crate) inner: FieldExpressionExecutor<
72        Rv32VecHeapAdapterExecutor<1, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
73    >,
74    pub(crate) cached_curve_type: Option<CurveType>,
75}
76
77impl<const BLOCKS: usize, const BLOCK_SIZE: usize> EcDoubleExecutor<BLOCKS, BLOCK_SIZE> {
78    pub fn new(
79        inner: FieldExpressionExecutor<
80            Rv32VecHeapAdapterExecutor<1, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
81        >,
82    ) -> Self {
83        let cached_curve_type = inner
84            .expr
85            .setup_values
86            .first()
87            .and_then(|a| get_curve_type(&inner.expr.prime, a));
88        Self {
89            inner,
90            cached_curve_type,
91        }
92    }
93}
94
95impl<const BLOCKS: usize, const BLOCK_SIZE: usize> Deref for EcDoubleExecutor<BLOCKS, BLOCK_SIZE> {
96    type Target = FieldExpressionExecutor<
97        Rv32VecHeapAdapterExecutor<1, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
98    >;
99
100    fn deref(&self) -> &Self::Target {
101        &self.inner
102    }
103}
104
105impl<const BLOCKS: usize, const BLOCK_SIZE: usize> DerefMut
106    for EcDoubleExecutor<BLOCKS, BLOCK_SIZE>
107{
108    fn deref_mut(&mut self) -> &mut Self::Target {
109        &mut self.inner
110    }
111}
112
113fn gen_base_expr(
114    config: ExprBuilderConfig,
115    range_checker_bus: VariableRangeCheckerBus,
116    a_biguint: BigUint,
117) -> (FieldExpr, Vec<usize>) {
118    let expr = ec_double_ne_expr(config, range_checker_bus, a_biguint);
119
120    let local_opcode_idx = vec![
121        Rv32WeierstrassOpcode::EC_DOUBLE as usize,
122        Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize,
123    ];
124
125    (expr, local_opcode_idx)
126}
127
128#[allow(clippy::too_many_arguments)]
129pub fn get_ec_double_air<const BLOCKS: usize, const BLOCK_SIZE: usize>(
130    exec_bridge: ExecutionBridge,
131    mem_bridge: MemoryBridge,
132    config: ExprBuilderConfig,
133    range_checker_bus: VariableRangeCheckerBus,
134    bitwise_lookup_bus: BitwiseOperationLookupBus,
135    pointer_max_bits: usize,
136    offset: usize,
137    a_biguint: BigUint,
138) -> WeierstrassAir<1, BLOCKS, BLOCK_SIZE> {
139    let (expr, local_opcode_idx) = gen_base_expr(config, range_checker_bus, a_biguint);
140    WeierstrassAir::new(
141        Rv32VecHeapAdapterAir::new(
142            exec_bridge,
143            mem_bridge,
144            bitwise_lookup_bus,
145            pointer_max_bits,
146        ),
147        FieldExpressionCoreAir::new(expr.clone(), offset, local_opcode_idx.clone(), vec![]),
148    )
149}
150
151pub fn get_ec_double_executor<const BLOCKS: usize, const BLOCK_SIZE: usize>(
152    config: ExprBuilderConfig,
153    range_checker_bus: VariableRangeCheckerBus,
154    pointer_max_bits: usize,
155    offset: usize,
156    a_biguint: BigUint,
157) -> EcDoubleExecutor<BLOCKS, BLOCK_SIZE> {
158    let (expr, local_opcode_idx) = gen_base_expr(config, range_checker_bus, a_biguint);
159    EcDoubleExecutor::new(FieldExpressionExecutor::new(
160        Rv32VecHeapAdapterExecutor::new(pointer_max_bits),
161        expr,
162        offset,
163        local_opcode_idx,
164        vec![],
165        "EcDouble",
166    ))
167}
168
169pub fn get_ec_double_chip<F, const BLOCKS: usize, const BLOCK_SIZE: usize>(
170    config: ExprBuilderConfig,
171    mem_helper: SharedMemoryHelper<F>,
172    range_checker: SharedVariableRangeCheckerChip,
173    bitwise_lookup_chip: SharedBitwiseOperationLookupChip<RV32_CELL_BITS>,
174    pointer_max_bits: usize,
175    a_biguint: BigUint,
176) -> WeierstrassChip<F, 1, BLOCKS, BLOCK_SIZE> {
177    let (expr, local_opcode_idx) = gen_base_expr(config, range_checker.bus(), a_biguint);
178    WeierstrassChip::new(
179        FieldExpressionFiller::new(
180            Rv32VecHeapAdapterFiller::new(pointer_max_bits, bitwise_lookup_chip),
181            expr,
182            local_opcode_idx,
183            vec![],
184            range_checker,
185            true,
186        ),
187        mem_helper,
188    )
189}