use std::marker::PhantomData;
use itertools::Itertools;
use openvm_cuda_common::memory_manager::MemTracker;
use openvm_stark_backend::{
poly_common::Squarable,
proof::*,
prover::{
DeviceMultiStarkProvingKey, MultiRapProver, OpeningProver, ProverBackend, ProverDevice,
ProvingContext, TraceCommitter,
},
};
use tracing::instrument;
use crate::{
base::DeviceMatrix,
hash_scheme::{DefaultHashScheme, GpuHashScheme},
logup_zerocheck::prove_zerocheck_and_logup_gpu,
merkle_tree::{MerkleProofQueryDigest, MerkleTreeConstructor},
prelude::{D_EF, EF, F},
sponge::GpuFiatShamirTranscript,
stacked_pcs::{stacked_commit, StackedPcsDataGpu},
stacked_reduction::prove_stacked_opening_reduction_gpu,
whir::prove_whir_opening_gpu,
AirDataGpu, GpuDevice, ProverError,
};
#[derive(Clone, Copy)]
pub struct GenericGpuBackend<HS: GpuHashScheme>(PhantomData<HS>);
impl<HS: GpuHashScheme> Default for GenericGpuBackend<HS> {
fn default() -> Self {
Self(PhantomData)
}
}
pub type GpuBackend = GenericGpuBackend<DefaultHashScheme>;
impl<HS: GpuHashScheme> ProverBackend for GenericGpuBackend<HS> {
const CHALLENGE_EXT_DEGREE: u8 = D_EF as u8;
type Val = F;
type Challenge = EF;
type Commitment = HS::Digest;
type Matrix = DeviceMatrix<F>;
type PcsData = StackedPcsDataGpu<F, HS::Digest>;
type OtherAirData = AirDataGpu;
}
impl<HS: GpuHashScheme> TraceCommitter<GenericGpuBackend<HS>> for GpuDevice
where
HS::MerkleHash: MerkleTreeConstructor,
{
type Error = ProverError;
#[allow(clippy::type_complexity)]
#[instrument(name = "prover.commit", skip_all, fields(phase = "prover"))]
fn commit(
&self,
traces: &[&DeviceMatrix<F>],
) -> Result<(HS::Digest, StackedPcsDataGpu<F, HS::Digest>), Self::Error> {
let cfg = self.params();
stacked_commit::<HS::MerkleHash>(
cfg.l_skip,
cfg.n_stack,
cfg.log_blowup,
cfg.k_whir(),
traces,
*self.prover_config(),
&self.device_ctx,
)
}
}
impl<HS: GpuHashScheme, TS: GpuFiatShamirTranscript<HS::SC>> ProverDevice<GenericGpuBackend<HS>, TS>
for GpuDevice
where
HS::MerkleHash: MerkleTreeConstructor,
HS::Digest: MerkleProofQueryDigest,
{
type Error = ProverError;
type DeviceCtx = openvm_cuda_common::stream::GpuDeviceCtx;
fn device_ctx(&self) -> &openvm_cuda_common::stream::GpuDeviceCtx {
&self.device_ctx
}
}
impl<HS: GpuHashScheme, TS: GpuFiatShamirTranscript<HS::SC>>
MultiRapProver<GenericGpuBackend<HS>, TS> for GpuDevice
{
type PartialProof = (GkrProof<HS::SC>, BatchConstraintProof<HS::SC>);
type Artifacts = Vec<EF>;
type Error = ProverError;
#[allow(clippy::type_complexity)]
#[instrument(name = "prover.rap_constraints", skip_all, fields(phase = "prover"))]
fn prove_rap_constraints(
&self,
transcript: &mut TS,
mpk: &DeviceMultiStarkProvingKey<GenericGpuBackend<HS>>,
ctx: &ProvingContext<GenericGpuBackend<HS>>,
_common_main_pcs_data: &StackedPcsDataGpu<F, HS::Digest>,
) -> Result<((GkrProof<HS::SC>, BatchConstraintProof<HS::SC>), Vec<EF>), Self::Error> {
let mem = MemTracker::start_and_reset_peak("prover.rap_constraints");
let save_memory = self.prover_config().zerocheck_save_memory;
let monomial_num_y_threshold = if self.params().log_blowup == 1 {
512
} else {
64
};
let (gkr_proof, batch_constraint_proof, r) = prove_zerocheck_and_logup_gpu::<HS, TS>(
transcript,
mpk,
ctx,
save_memory,
monomial_num_y_threshold,
self.sm_count(),
&self.device_ctx,
)?;
mem.emit_metrics();
Ok(((gkr_proof, batch_constraint_proof), r))
}
}
impl<HS: GpuHashScheme, TS: GpuFiatShamirTranscript<HS::SC>>
OpeningProver<GenericGpuBackend<HS>, TS> for GpuDevice
where
HS::MerkleHash: MerkleTreeConstructor,
HS::Digest: MerkleProofQueryDigest,
{
type OpeningProof = (StackingProof<HS::SC>, WhirProof<HS::SC>);
type OpeningPoints = Vec<EF>;
type Error = ProverError;
#[instrument(name = "prover.openings", skip_all, fields(phase = "prover"))]
fn prove_openings(
&self,
transcript: &mut TS,
mpk: &DeviceMultiStarkProvingKey<GenericGpuBackend<HS>>,
ctx: ProvingContext<GenericGpuBackend<HS>>,
common_main_pcs_data: StackedPcsDataGpu<F, HS::Digest>,
r: Vec<EF>,
) -> Result<Self::OpeningProof, Self::Error> {
let mut mem = MemTracker::start_and_reset_peak("prover.openings");
let params = self.params();
#[cfg(debug_assertions)]
{
let total_stacked_width: usize = std::iter::once(common_main_pcs_data.layout().width())
.chain(ctx.per_trace.iter().flat_map(|(air_idx, air_ctx)| {
mpk.per_air[*air_idx]
.preprocessed_data
.iter()
.map(|committed| committed.data.layout().width())
.chain(
air_ctx
.cached_mains
.iter()
.map(|committed| committed.data.layout().width()),
)
}))
.sum();
debug_assert!(
total_stacked_width <= mpk.params.w_stack,
"total stacked width across commits ({total_stacked_width}) exceeds w_stack ({})",
mpk.params.w_stack
);
}
let (stacking_proof, u_prisma, stacked_per_commit) =
prove_stacked_opening_reduction_gpu::<HS, TS>(
self,
transcript,
mpk,
ctx,
common_main_pcs_data,
&r,
)?;
let (&u0, u_rest) = u_prisma.split_first().unwrap();
let u_cube = u0
.exp_powers_of_2()
.take(params.l_skip)
.chain(u_rest.iter().copied())
.collect_vec();
let whir_proof = prove_whir_opening_gpu::<HS, TS>(
params,
transcript,
stacked_per_commit,
&u_cube,
&self.device_ctx,
)?;
mem.emit_metrics();
mem.reset_peak();
Ok((stacking_proof, whir_proof))
}
}