Midnight Proofsproof Caching | Skills Pool
Midnight Proofsproof Caching Use when caching ZK proofs for performance, implementing proof cache invalidation, storing verification results, caching intermediate proof components, or building distributed proof caches with Redis.
aaronbassett 0 estrellas 5 feb 2026 Ocupación Categorías Bases de Datos NoSQL Contenido de la habilidad
Proof Caching
Cache proofs and proof components to improve performance and reduce redundant computation in proof generation services.
When to Use
Caching verification results to avoid re-verifying the same proofs
Storing generated proofs for potential reuse
Caching intermediate proof components
Implementing TTL-based proof expiration
Building distributed proof caches with Redis
Key Concepts
What Can Be Cached?
Component Cacheable? TTL Considerations Verification results Yes Long (proofs are immutable) Generated proofs Depends Short (state may change)
Instalación rápida
Midnight Proofsproof Caching npx skillvault add aaronbassett/aaronbassett-midnight-knowledgebase-plugins-midnight-proofs-skills-proof-caching-skill-md
estrellas 0
Actualizado 5 feb 2026
Ocupación Circuit keys
Witness templates Sometimes Depends on use case
Cache Key Design A good proof cache key uniquely identifies the proof:
// For verification results
function verificationCacheKey(
circuitId: string,
proofHash: string,
publicInputsHash: string
): string {
return `verify:${circuitId}:${proofHash}:${publicInputsHash}`;
}
// For generated proofs
function proofCacheKey(
circuitId: string,
witnessHash: string
): string {
return `proof:${circuitId}:${witnessHash}`;
}
Cache Invalidation Scenario Invalidation Strategy Contract upgrade Clear all proofs for circuit State change Clear proofs depending on changed state TTL expiration Automatic removal Manual purge Admin-triggered clear
References
Examples
Quick Start
1. Simple In-Memory Cache import { LRUCache } from 'lru-cache';
import { createHash } from 'crypto';
const verificationCache = new LRUCache<string, boolean>({
max: 10000,
ttl: 1000 * 60 * 60, // 1 hour
});
function hashData(data: unknown): string {
return createHash('sha256')
.update(JSON.stringify(data))
.digest('hex');
}
async function verifyWithCache(
circuitId: string,
proof: Uint8Array,
publicInputs: Record<string, unknown>
): Promise<boolean> {
const key = `${circuitId}:${hashData(proof)}:${hashData(publicInputs)}`;
// Check cache
const cached = verificationCache.get(key);
if (cached !== undefined) {
return cached;
}
// Verify
const result = await verifier.verify(circuitId, proof, publicInputs);
// Cache result
verificationCache.set(key, result.valid);
return result.valid;
}
2. Redis-Based Distributed Cache import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
async function verifyWithRedisCache(
circuitId: string,
proof: Uint8Array,
publicInputs: Record<string, unknown>
): Promise<boolean> {
const key = `verify:${circuitId}:${hashData(proof)}:${hashData(publicInputs)}`;
// Check cache
const cached = await redis.get(key);
if (cached !== null) {
return cached === 'true';
}
// Verify
const result = await verifier.verify(circuitId, proof, publicInputs);
// Cache with 1 hour TTL
await redis.setex(key, 3600, result.valid ? 'true' : 'false');
return result.valid;
}
Common Patterns
Multi-Level Caching class MultiLevelProofCache {
private l1: LRUCache<string, boolean>; // In-memory
private l2: Redis; // Redis
constructor(redis: Redis) {
this.l1 = new LRUCache({ max: 1000, ttl: 60000 });
this.l2 = redis;
}
async get(key: string): Promise<boolean | undefined> {
// Check L1
const l1Result = this.l1.get(key);
if (l1Result !== undefined) {
return l1Result;
}
// Check L2
const l2Result = await this.l2.get(key);
if (l2Result !== null) {
const value = l2Result === 'true';
this.l1.set(key, value); // Populate L1
return value;
}
return undefined;
}
async set(key: string, value: boolean, ttlSeconds: number): Promise<void> {
this.l1.set(key, value);
await this.l2.setex(key, ttlSeconds, value ? 'true' : 'false');
}
}
Cache-Aside Pattern async function verifyProofCacheAside(
circuitId: string,
proof: Uint8Array,
publicInputs: Record<string, unknown>
): Promise<boolean> {
const key = buildCacheKey(circuitId, proof, publicInputs);
// 1. Try cache
const cached = await cache.get(key);
if (cached !== undefined) {
metrics.cacheHit();
return cached;
}
metrics.cacheMiss();
// 2. Compute
const result = await verifier.verify(circuitId, proof, publicInputs);
// 3. Store in cache
await cache.set(key, result.valid, 3600);
return result.valid;
}
Batch Cache Lookup async function verifyBatchWithCache(
proofs: ProofItem[]
): Promise<Map<string, boolean>> {
const results = new Map<string, boolean>();
const uncached: ProofItem[] = [];
// Build keys
const keys = proofs.map((p) => buildCacheKey(p.circuitId, p.proof, p.publicInputs));
// Batch cache lookup
const cachedValues = await redis.mget(keys);
// Separate cached and uncached
proofs.forEach((proof, i) => {
if (cachedValues[i] !== null) {
results.set(proof.id, cachedValues[i] === 'true');
} else {
uncached.push(proof);
}
});
// Verify uncached
if (uncached.length > 0) {
const verifyResults = await verifyBatch(uncached);
// Cache new results
const pipeline = redis.pipeline();
uncached.forEach((proof, i) => {
const key = buildCacheKey(proof.circuitId, proof.proof, proof.publicInputs);
pipeline.setex(key, 3600, verifyResults[i] ? 'true' : 'false');
results.set(proof.id, verifyResults[i]);
});
await pipeline.exec();
}
return results;
}
Cache Warming async function warmCache(circuitIds: string[]): Promise<void> {
console.log('Warming cache for circuits:', circuitIds);
for (const circuitId of circuitIds) {
// Pre-generate common proofs
const commonWitnesses = await getCommonWitnesses(circuitId);
for (const witness of commonWitnesses) {
try {
const proof = await prover.prove(circuitId, witness);
const key = buildProofCacheKey(circuitId, witness);
await cache.set(key, proof, 3600);
} catch (error) {
console.warn(`Failed to warm cache for ${circuitId}:`, error);
}
}
}
console.log('Cache warming complete');
}
Cache Metrics class CacheMetrics {
private hits = 0;
private misses = 0;
private evictions = 0;
hit(): void {
this.hits++;
}
miss(): void {
this.misses++;
}
evict(): void {
this.evictions++;
}
getStats(): {
hits: number;
misses: number;
hitRate: number;
evictions: number;
} {
const total = this.hits + this.misses;
return {
hits: this.hits,
misses: this.misses,
hitRate: total > 0 ? this.hits / total : 0,
evictions: this.evictions,
};
}
}
Concern Mitigation Cache stampede Use locks or probabilistic early expiration Memory pressure Set appropriate max size, use LRU eviction Stale data Set appropriate TTL, implement invalidation Network latency (Redis) Use connection pooling, multi-level cache
proof-generation - Generate proofs to cache
proof-verification - Verify proofs with caching
prover-optimization - Optimize proof generation
02
When to Use