use openvm_algebra_circuit::Rv32ModularHybridBuilder;
use openvm_circuit::{
arch::{DEFAULT_BLOCK_SIZE, *},
system::{
cuda::{
extensions::{get_inventory_range_checker, get_or_create_bitwise_op_lookup},
SystemChipInventoryGPU,
},
memory::SharedMemoryHelper,
},
};
use openvm_circuit_primitives::{hybrid_chip::cpu_proving_ctx_to_gpu, Chip};
use openvm_cpu_backend::CpuBackend;
use openvm_cuda_backend::{
base::DeviceMatrix,
prelude::{F, SC},
BabyBearPoseidon2GpuEngine as GpuBabyBearPoseidon2Engine, GpuBackend,
};
use openvm_cuda_common::stream::GpuDeviceCtx;
use openvm_mod_circuit_builder::{ExprBuilderConfig, FieldExpressionMetadata};
use openvm_rv32_adapters::{Rv32VecHeapAdapterCols, Rv32VecHeapAdapterExecutor};
use openvm_stark_backend::{p3_air::BaseAir, prover::AirProvingContext};
use crate::{
get_ec_addne_chip, get_ec_double_chip, EccRecord, Rv32WeierstrassConfig, WeierstrassAir,
WeierstrassChip, WeierstrassExtension, ECC_BLOCKS_32, ECC_BLOCKS_48, NUM_LIMBS_32,
NUM_LIMBS_48,
};
#[derive(derive_new::new)]
pub struct HybridWeierstrassChip<
F,
const NUM_READS: usize,
const BLOCKS: usize,
const BLOCK_SIZE: usize,
> {
cpu: WeierstrassChip<F, NUM_READS, BLOCKS, BLOCK_SIZE>,
device_ctx: GpuDeviceCtx,
}
impl<const NUM_READS: usize, const BLOCKS: usize, const BLOCK_SIZE: usize>
Chip<DenseRecordArena, GpuBackend> for HybridWeierstrassChip<F, NUM_READS, BLOCKS, BLOCK_SIZE>
{
fn generate_proving_ctx(&self, mut arena: DenseRecordArena) -> AirProvingContext<GpuBackend> {
let total_input_limbs =
self.cpu.inner.num_inputs() * self.cpu.inner.expr.canonical_num_limbs();
let layout = AdapterCoreLayout::with_metadata(FieldExpressionMetadata::<
F,
Rv32VecHeapAdapterExecutor<NUM_READS, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
>::new(total_input_limbs));
let record_size = RecordSeeker::<
DenseRecordArena,
EccRecord<NUM_READS, BLOCKS, BLOCK_SIZE>,
_,
>::get_aligned_record_size(&layout);
let records = arena.allocated();
if records.is_empty() {
return AirProvingContext::simple_no_pis(DeviceMatrix::dummy());
}
debug_assert_eq!(records.len() % record_size, 0);
let num_records = records.len() / record_size;
let height = num_records.next_power_of_two();
let mut seeker = arena
.get_record_seeker::<EccRecord<NUM_READS, BLOCKS, BLOCK_SIZE>, AdapterCoreLayout<
FieldExpressionMetadata<
F,
Rv32VecHeapAdapterExecutor<NUM_READS, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
>,
>>();
let adapter_width =
Rv32VecHeapAdapterCols::<F, NUM_READS, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>::width();
let width = adapter_width + BaseAir::<F>::width(&self.cpu.inner.expr);
let mut matrix_arena = MatrixRecordArena::<F>::with_capacity(height, width);
seeker.transfer_to_matrix_arena(&mut matrix_arena, layout);
let cpu_ctx = Chip::<_, CpuBackend<SC>>::generate_proving_ctx(&self.cpu, matrix_arena);
cpu_proving_ctx_to_gpu(cpu_ctx, &self.device_ctx)
}
}
#[derive(Clone, Copy, Default)]
pub struct EccHybridProverExt;
impl VmProverExtension<GpuBabyBearPoseidon2Engine, DenseRecordArena, WeierstrassExtension>
for EccHybridProverExt
{
fn extend_prover(
&self,
extension: &WeierstrassExtension,
inventory: &mut ChipInventory<SC, DenseRecordArena, GpuBackend>,
) -> Result<(), ChipInventoryError> {
let range_checker_gpu = get_inventory_range_checker(inventory);
let timestamp_max_bits = inventory.timestamp_max_bits();
let pointer_max_bits = inventory.airs().pointer_max_bits();
let range_checker = range_checker_gpu.cpu_chip.clone().unwrap();
let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
let bitwise_lu_gpu = get_or_create_bitwise_op_lookup(inventory)?;
let bitwise_lu = bitwise_lu_gpu.cpu_chip.clone().unwrap();
let device_ctx = range_checker_gpu.device_ctx.clone();
for curve in extension.supported_curves.iter() {
let bytes = curve.modulus.bits().div_ceil(8) as usize;
if bytes <= NUM_LIMBS_32 {
let config = ExprBuilderConfig {
modulus: curve.modulus.clone(),
num_limbs: NUM_LIMBS_32,
limb_bits: 8,
};
inventory.next_air::<WeierstrassAir<2, ECC_BLOCKS_32, DEFAULT_BLOCK_SIZE>>()?;
let addne = get_ec_addne_chip::<F, ECC_BLOCKS_32, DEFAULT_BLOCK_SIZE>(
config.clone(),
mem_helper.clone(),
range_checker.clone(),
bitwise_lu.clone(),
pointer_max_bits,
);
inventory.add_executor_chip(HybridWeierstrassChip::new(addne, device_ctx.clone()));
inventory.next_air::<WeierstrassAir<1, ECC_BLOCKS_32, DEFAULT_BLOCK_SIZE>>()?;
let double = get_ec_double_chip::<F, ECC_BLOCKS_32, DEFAULT_BLOCK_SIZE>(
config,
mem_helper.clone(),
range_checker.clone(),
bitwise_lu.clone(),
pointer_max_bits,
curve.a.clone(),
);
inventory.add_executor_chip(HybridWeierstrassChip::new(double, device_ctx.clone()));
} else if bytes <= NUM_LIMBS_48 {
let config = ExprBuilderConfig {
modulus: curve.modulus.clone(),
num_limbs: NUM_LIMBS_48,
limb_bits: 8,
};
inventory.next_air::<WeierstrassAir<2, ECC_BLOCKS_48, DEFAULT_BLOCK_SIZE>>()?;
let addne = get_ec_addne_chip::<F, ECC_BLOCKS_48, DEFAULT_BLOCK_SIZE>(
config.clone(),
mem_helper.clone(),
range_checker.clone(),
bitwise_lu.clone(),
pointer_max_bits,
);
inventory.add_executor_chip(HybridWeierstrassChip::new(addne, device_ctx.clone()));
inventory.next_air::<WeierstrassAir<1, ECC_BLOCKS_48, DEFAULT_BLOCK_SIZE>>()?;
let double = get_ec_double_chip::<F, ECC_BLOCKS_48, DEFAULT_BLOCK_SIZE>(
config,
mem_helper.clone(),
range_checker.clone(),
bitwise_lu.clone(),
pointer_max_bits,
curve.a.clone(),
);
inventory.add_executor_chip(HybridWeierstrassChip::new(double, device_ctx.clone()));
} else {
panic!("Modulus too large");
}
}
Ok(())
}
}
#[derive(Clone)]
pub struct Rv32WeierstrassHybridBuilder;
type E = GpuBabyBearPoseidon2Engine;
impl VmBuilder<E> for Rv32WeierstrassHybridBuilder {
type VmConfig = Rv32WeierstrassConfig;
type SystemChipInventory = SystemChipInventoryGPU;
type RecordArena = DenseRecordArena;
fn create_chip_complex(
&self,
config: &Rv32WeierstrassConfig,
circuit: AirInventory<SC>,
device_ctx: &openvm_stark_backend::EngineDeviceCtx<E>,
) -> Result<
VmChipComplex<SC, Self::RecordArena, GpuBackend, Self::SystemChipInventory>,
ChipInventoryError,
> {
let mut chip_complex = VmBuilder::<E>::create_chip_complex(
&Rv32ModularHybridBuilder,
&config.modular,
circuit,
device_ctx,
)?;
let inventory = &mut chip_complex.inventory;
VmProverExtension::<E, _, _>::extend_prover(
&EccHybridProverExt,
&config.weierstrass,
inventory,
)?;
Ok(chip_complex)
}
}