QuorumProof uses on-chain caching for two verification results to reduce gas costs on repeated queries:
- AttestationVerificationCache — Stores whether a credential meets the threshold for a slice (
is_attested) - ThresholdVerificationCache — Stores whether an aggregated BLS signature meets the quorum threshold (
verify_threshold_attestation)
Both caches are invalidated (removed) immediately whenever changes occur that could affect their correctness. This document specifies the invalidation guarantees.
The AttestationVerificationCache entry for (credential_id, slice_id) is removed (invalidated) when:
-
Credential Revocation (
revoke_credential,revoke_consent,approve_revocation)- The credential no longer exists as a valid candidate for attestation
- All cache entries for that credential are removed
-
Credential Amendment (
amend_credential_metadata)- The credential's metadata may affect compliance checks or verification state
- All cache entries for the amended credential are removed
-
Attestation Removal (dispute resolution, challenge upheld)
- When a challenge is upheld, the accused attestor's signature is removed from the credential
- The threshold recalculation may now fail, invalidating any cached result claiming success
- All cache entries for that credential are removed
-
Slice Changes (attestor removal, weight update, threshold change)
- Adding/removing attestors, changing weights, or changing thresholds changes which signatures are required
- All cache entries for all credentials attested in that slice are removed
The ThresholdVerificationCache entry for (credential_id, slice_id) is removed (invalidated) when:
-
Credential Revocation (
revoke_credential,revoke_consent,approve_revocation)- A revoked credential cannot have valid threshold verification
- All cache entries for that credential are removed
-
Credential Amendment (
amend_credential_metadata)- Amendment may affect signature requirements or validation state
- All cache entries for the amended credential are removed
-
Attestation Removal (dispute resolution, challenge upheld)
- When a challenge is upheld, the accused attestor's signature is removed
- The cached signatories list no longer reflects reality
- All cache entries for that credential are removed
-
Slice Delegation Revocation (
revoke_slice_delegation)- A delegation revocation may change the effective signatories for threshold calculation
- All cache entries for all credentials in that slice are removed
For any credential/slice pair, if get_threshold_verification(credential_id, slice_id) returns Some(result), then:
result.is_validaccurately reflects whether the credential meets the quorum threshold as of the time the result was cachedresult.signatoriesaccurately reflects the attestor bitmap that contributed to threshold verification- The result is stale if any subsequent operation (listed above) has modified the credential, attestations, or slice
Cache invalidation occurs in the same transaction that triggers it. Subsequent queries in later transactions will:
- Return
Noneif the cache entry was removed (no stale data) - Recompute and re-cache the result if needed
Both caches use full removal (not explicit staleness markers). When an invalidation trigger occurs:
- The cache entry is removed from contract storage via
env.storage().instance().remove(...) - Subsequent calls to
get_threshold_verification(credential_id, slice_id)returnNone - Callers must recompute by calling
verify_threshold_attestation(...)if they need a fresh result
This approach is simpler and aligns with the existing AttestationVerificationCache convention.
Both caches are indexed by (credential_id, slice_id) pairs:
AttestationVerificationCache(credential_id, slice_id) -> is_attested: bool
ThresholdVerificationCache(credential_id, slice_id) -> ThresholdVerificationResult
Invalidation strategies:
- Per-credential invalidation:
invalidate_*_caches_for_credential(credential_id)removes all cache entries for all slices - Per-slice invalidation:
invalidate_*_caches_for_slice(slice_id)removes all cache entries for all credentials
If your contract or off-chain client needs a verification result:
// Attempt to retrieve cached result
if let Some(result) = client.get_threshold_verification(&credential_id, &slice_id) {
// Use the cached result
if result.is_valid {
grant_credential_benefit(&credential_id);
}
} else {
// Cache miss or invalidated; recompute
let agg_sig = AggregatedSignature { /* ... */ };
let is_valid = client.verify_threshold_attestation(&credential_id, &slice_id, &agg_sig);
if is_valid {
grant_credential_benefit(&credential_id);
}
}Do not assume a cached result persists across multiple transactions:
// ❌ WRONG: Cache may be invalidated between transactions
let result = client.get_threshold_verification(&credential_id, &slice_id);
// ... time passes ...
// ... credential or slice modified ...
// result is now stale but still appears validWhen a credential is revoked, all its cache entries are removed. This is correct because:
- A revoked credential cannot be re-attested or re-verified through normal flows
- Any cached result claiming success is now misleading
When credential metadata is amended (e.g., specialization or degree), all cache entries are removed. This is conservative because:
- Metadata changes might affect compliance checks or policy-driven verification
- Invalidation ensures no stale metadata is reflected in cached results
Cache invalidation has performance costs:
- Revocation: Iterates over all slices (
O(n)slices) and removes each cache entry - Slice changes: Iterates over all credentials (
O(m)credentials) and removes each cache entry
For large numbers of slices/credentials, this cost is amortized across many cache hits during normal operation. If invalidation frequency is high relative to cache hits, consider:
- Batching invalidations with multi-credential revocation flows
- Monitoring cache hit rates via metrics to detect inefficient cache-to-invalidation ratios
docs/weighted-voting.md— Consensus algorithm and slice managementdocs/architecture.md— Contract structure and cross-contract callsdocs/formal-verification.md— Invariant proofs including cache consistency