AgentSkillsCN

midnight-proofs:proof-verification

在服务端验证 ZK 证明、在交易提交前对证明进行校验、构建验证网关、执行批量验证,或排查证明生成过程中的各类问题时使用。

SKILL.md
--- frontmatter
name: midnight-proofs:proof-verification
description: Use when verifying ZK proofs server-side, validating proofs before transaction submission, building verification gateways, implementing batch verification, or debugging proof generation issues.

Proof Verification

Verify ZK proofs server-side before transaction submission to catch invalid proofs early and reduce failed transactions.

When to Use

  • Validating proofs before submitting transactions to the network
  • Building a verification gateway for incoming proofs
  • Implementing batch verification for high-throughput systems
  • Checking proof validity as part of an API request
  • Debugging proof generation issues

Key Concepts

Why Verify Server-Side?

BenefitDescription
Fail fastCatch invalid proofs before network submission
Save gasAvoid transaction fees for invalid proofs
Better UXProvide immediate feedback to users
SecurityValidate proofs from untrusted clients

Verification vs Validation

  • Verification - Cryptographic check that proof is valid for the circuit
  • Validation - Business logic checks (e.g., correct public inputs)
typescript
// Both verification and validation
async function verifyAndValidate(proof: Proof, publicInputs: PublicInputs): Promise<boolean> {
  // Step 1: Cryptographic verification
  const isValidProof = await verifier.verify(proof, publicInputs);
  if (!isValidProof) return false;

  // Step 2: Business validation
  const isValidBusiness = validateBusinessRules(publicInputs);
  return isValidBusiness;
}

Verification Performance

Verification is much faster than proof generation:

OperationTypical Duration
Proof Generation5-30 seconds
Proof Verification10-100 milliseconds

References

DocumentDescription
verification-api.mdVerifier SDK setup and API reference

Examples

ExampleDescription
pre-submit-validation/Verify before transaction submit
batch-verification/Verify multiple proofs in parallel

Quick Start

1. Set Up Verifier

typescript
import { createVerifier } from '@midnight-ntwrk/midnight-js-verifier';

const verifier = await createVerifier({
  verificationKeysPath: './circuit-keys',
});

2. Verify a Single Proof

typescript
async function verifyProof(
  circuitId: string,
  proof: Uint8Array,
  publicInputs: Record<string, unknown>
): Promise<boolean> {
  try {
    const result = await verifier.verify(circuitId, proof, publicInputs);
    return result.valid;
  } catch (error) {
    console.error('Verification failed:', error);
    return false;
  }
}

3. Verify Before Submit

typescript
async function submitWithVerification(
  transaction: Transaction
): Promise<string> {
  // Extract proof and public inputs from transaction
  const { proof, publicInputs, circuitId } = transaction;

  // Verify first
  const isValid = await verifyProof(circuitId, proof, publicInputs);
  if (!isValid) {
    throw new Error('Proof verification failed');
  }

  // Submit to network
  return await submitTransaction(transaction);
}

Common Patterns

Verification Gateway

typescript
import express from 'express';

const app = express();

app.post('/api/verify', async (req, res) => {
  const { circuitId, proof, publicInputs } = req.body;

  try {
    const result = await verifier.verify(
      circuitId,
      Buffer.from(proof, 'base64'),
      publicInputs
    );

    res.json({
      valid: result.valid,
      circuitId,
      verificationTime: result.duration,
    });
  } catch (error) {
    res.status(400).json({
      valid: false,
      error: error.message,
    });
  }
});

Batch Verification

typescript
interface ProofBatch {
  circuitId: string;
  proof: Uint8Array;
  publicInputs: Record<string, unknown>;
}

async function verifyBatch(
  proofs: ProofBatch[]
): Promise<Map<number, boolean>> {
  const results = new Map<number, boolean>();

  // Verify in parallel
  const verifications = proofs.map(async (p, index) => {
    try {
      const result = await verifier.verify(
        p.circuitId,
        p.proof,
        p.publicInputs
      );
      results.set(index, result.valid);
    } catch {
      results.set(index, false);
    }
  });

  await Promise.all(verifications);
  return results;
}

Caching Verified Proofs

typescript
import { createHash } from 'crypto';

const verifiedProofs = new Map<string, boolean>();

function proofHash(proof: Uint8Array, publicInputs: Record<string, unknown>): string {
  const data = JSON.stringify({ proof: proof.toString(), publicInputs });
  return createHash('sha256').update(data).digest('hex');
}

async function verifyWithCache(
  circuitId: string,
  proof: Uint8Array,
  publicInputs: Record<string, unknown>
): Promise<boolean> {
  const hash = proofHash(proof, publicInputs);

  // Check cache
  if (verifiedProofs.has(hash)) {
    return verifiedProofs.get(hash)!;
  }

  // Verify
  const result = await verifier.verify(circuitId, proof, publicInputs);
  verifiedProofs.set(hash, result.valid);

  return result.valid;
}

Verification with Timeout

typescript
async function verifyWithTimeout(
  circuitId: string,
  proof: Uint8Array,
  publicInputs: Record<string, unknown>,
  timeoutMs = 5000
): Promise<boolean> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const result = await verifier.verify(circuitId, proof, publicInputs, {
      signal: controller.signal,
    });
    return result.valid;
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Verification timeout');
    }
    throw error;
  } finally {
    clearTimeout(timeout);
  }
}

Error Classification

typescript
enum VerificationError {
  INVALID_PROOF = 'INVALID_PROOF',
  INVALID_PUBLIC_INPUTS = 'INVALID_PUBLIC_INPUTS',
  CIRCUIT_NOT_FOUND = 'CIRCUIT_NOT_FOUND',
  MALFORMED_PROOF = 'MALFORMED_PROOF',
  TIMEOUT = 'TIMEOUT',
}

function classifyVerificationError(error: Error): VerificationError {
  if (error.message.includes('circuit')) {
    return VerificationError.CIRCUIT_NOT_FOUND;
  }
  if (error.message.includes('public input')) {
    return VerificationError.INVALID_PUBLIC_INPUTS;
  }
  if (error.message.includes('malformed')) {
    return VerificationError.MALFORMED_PROOF;
  }
  if (error.message.includes('timeout')) {
    return VerificationError.TIMEOUT;
  }
  return VerificationError.INVALID_PROOF;
}

Performance Considerations

ConcernMitigation
Verification key loadingPreload keys at startup
High request volumeUse verification caching
Large batchesLimit concurrent verifications
Memory usageStream large proof batches

Related Skills

  • proof-generation - Generate proofs server-side
  • proof-caching - Cache verification results
  • prover-optimization - Optimize prover that creates proofs

Related Commands

None currently defined.