zkvc 0.1.0

A Rust crate for building zero-knowledge proof-based volunteer computing systems
Documentation
use actix_web::{web, App, HttpResponse, HttpServer};
use ark_bls12_381::{Bls12_381, Fr};
use ark_groth16::{Groth16, Proof, VerifyingKey};
use ark_serialize::CanonicalDeserialize;
use ark_snark::SNARK;
use base64::{engine::general_purpose::STANDARD, Engine as _};
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::{path::PathBuf, sync::Arc};

use crate::{
    circuit::ProofRequest,
    response::VerificationResponse,
    utils::{field_from_string, VERIFY_PATH},
};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServerConfig {
    pub listen_address: String,
    pub verification_key_path: PathBuf,
}

pub struct ServerApp<VP, IP, EP>
where
    VP: Fn(&str, &[Fr]) -> Result<(), anyhow::Error> + Send + Sync + 'static,
    IP: Fn(&str, &str) -> Result<(), anyhow::Error> + Send + Sync + 'static,
    EP: Fn(&str, &anyhow::Error) -> Result<(), anyhow::Error> + Send + Sync + 'static,
{
    config: ServerConfig,
    verification_key: Arc<VerifyingKey<Bls12_381>>,
    valid_proof_handler: Option<VP>,
    invalid_proof_handler: Option<IP>,
    error_handler: Option<EP>,
}

impl<VP, IP, EP> ServerApp<VP, IP, EP>
where
    VP: Fn(&str, &[Fr]) -> Result<(), anyhow::Error> + Send + Sync + 'static,
    IP: Fn(&str, &str) -> Result<(), anyhow::Error> + Send + Sync + 'static,
    EP: Fn(&str, &anyhow::Error) -> Result<(), anyhow::Error> + Send + Sync + 'static,
{
    pub fn new(config: ServerConfig) -> Result<Self, anyhow::Error> {
        debug!("Creating new ServerApp instance");
        let vk_bytes = std::fs::read(&config.verification_key_path)?;
        let vk = VerifyingKey::deserialize_uncompressed(&*vk_bytes)?;
        info!("Verification key loaded successfully");

        Ok(Self {
            config,
            verification_key: Arc::new(vk),
            valid_proof_handler: None,
            invalid_proof_handler: None,
            error_handler: None,
        })
    }

    pub fn with_valid_proof_handler(mut self, handler: VP) -> Self {
        self.valid_proof_handler = Some(handler);
        self
    }

    pub fn with_invalid_proof_handler(mut self, handler: IP) -> Self {
        self.invalid_proof_handler = Some(handler);
        self
    }

    pub fn with_error_handler(mut self, handler: EP) -> Self {
        self.error_handler = Some(handler);
        self
    }

    fn verify(&self, request: &ProofRequest) -> Result<bool, anyhow::Error> {
        debug!("Verifying proof");
        let proof_bytes = STANDARD.decode(&request.proof.0)?;
        let proof: Proof<Bls12_381> = Proof::deserialize_uncompressed(&*proof_bytes)?;
        let inputs: Vec<Fr> = request
            .public_inputs
            .iter()
            .map(|s| field_from_string(s))
            .collect::<Result<Vec<_>, _>>()?;
        debug!("Inputs: {:?}", inputs);

        let result = Groth16::<Bls12_381>::verify(&self.verification_key, &inputs, &proof)?;
        info!("Proof verification result: {}", result);
        Ok(result)
    }

    async fn verify_handler(
        request: web::Json<ProofRequest>,
        app: web::Data<Arc<Self>>,
    ) -> HttpResponse {
        debug!(
            "Received verification request from client {}",
            request.client_id
        );
        let inputs: Vec<Fr> = match request
            .public_inputs
            .iter()
            .map(|s| field_from_string(s))
            .collect::<Result<Vec<_>, _>>()
        {
            Ok(inputs) => inputs,
            Err(e) => {
                error!("Failed to parse inputs: {}", e);
                if let Some(handler) = &app.error_handler {
                    if let Err(handler_err) = handler(&request.client_id, &e) {
                        error!("Error handler failed: {}", handler_err);
                    }
                }
                return HttpResponse::InternalServerError().json(VerificationResponse::Error {
                    error: e.to_string(),
                });
            }
        };

        match app.verify(&request) {
            Ok(true) => {
                info!(
                    "Proof verified successfully for client {}",
                    request.client_id
                );
                if let Some(handler) = &app.valid_proof_handler {
                    if let Err(e) = handler(&request.client_id, &inputs) {
                        error!("Valid proof handler failed: {}", e);
                        return HttpResponse::InternalServerError().json(
                            VerificationResponse::Error {
                                error: e.to_string(),
                            },
                        );
                    }
                }
                HttpResponse::Ok().json(VerificationResponse::Valid {
                    result: Some(request.public_inputs.clone()),
                })
            }
            Ok(false) => {
                warn!("Invalid proof received from client {}", request.client_id);
                let reason = "Proof verification failed".to_string();
                if let Some(handler) = &app.invalid_proof_handler {
                    if let Err(e) = handler(&request.client_id, &reason) {
                        error!("Invalid proof handler failed: {}", e);
                        return HttpResponse::InternalServerError().json(
                            VerificationResponse::Error {
                                error: e.to_string(),
                            },
                        );
                    }
                }
                HttpResponse::BadRequest().json(VerificationResponse::Invalid { reason })
            }
            Err(e) => {
                error!("Verification error for client {}: {}", request.client_id, e);
                if let Some(handler) = &app.error_handler {
                    if let Err(handler_err) = handler(&request.client_id, &e) {
                        error!("Error handler failed: {}", handler_err);
                    }
                }
                HttpResponse::InternalServerError().json(VerificationResponse::Error {
                    error: e.to_string(),
                })
            }
        }
    }

    pub async fn run(self) -> std::io::Result<()> {
        let address = self.config.listen_address.clone();
        info!("Starting server on {}", address);

        let app = Arc::new(self);
        HttpServer::new(move || {
            let app = web::Data::new(Arc::clone(&app));
            App::new()
                .app_data(app)
                .route(VERIFY_PATH, web::post().to(Self::verify_handler))
        })
        .bind(address)?
        .run()
        .await
    }

    pub fn get_listen_address(&self) -> &str {
        &self.config.listen_address
    }
}