Skip to content

Verifier Functions

RunTimeAdmin edited this page Nov 3, 2026 · 31 revisions

Verifier Functions

**Referenced Files in This Document** - [verifier.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/verifier.ts) - [did.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/did.ts) - [challenge.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/challenge.ts) - [keys.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/keys.ts) - [agent.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/agent.ts) - [types.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/types.ts) - [README.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/README.md) - [verifier.test.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/test/verifier.test.ts) - [reputation-model.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/docs/reputation-model.md)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion

Introduction

This document explains the verifier functions in the Sigvara SDK, focusing on how agents authenticate themselves and how callers validate agent credentials, reputation, and identity. The SDK provides:

  • Signature verification utilities that bind a challenge to an agent DID, enforce freshness, bind the response to a specific verifier audience, and verify the Ed25519 signature against the public key stored on-chain.
  • DID parsing, formatting, and hash computation used to derive on-chain identifiers from an agent address and chain ID.
  • Reputation reading and threshold checking for matured scores, including score breakdowns and total score access.
  • Stake checks for minimum bonding requirements.
  • W3C-compatible DID document construction from on-chain state.
  • Agent-side helpers for generating keys, issuing challenges, and signing challenges.

The verifier is designed so that unauthenticated input is rejected before any expensive or network-bound operation whenever possible, while still allowing callers to perform full reads when needed.

This page describes the SDK in 1.0.0-alpha.15, published to npm on 3 November 2026. New since 1.0.0-alpha.14: reads on SigvaraVerifier for a trust check, getEarnedScore, getPendingScore, getMinimumStake, getPendingWithdrawal, getSlashProposal, getStakeTokenDecimals, getEvidenceRoot and verifyEvidence, which sigvara-mcp is built on. Neither 1.0.0-alpha.13 nor 1.0.0-alpha.14 changed SigvaraVerifier: they added atomicNonceStore, the gate's instances and profile settings, and signPaymentOutcome and verifyPaymentOutcome beside it. New since 1.0.0-alpha.11: the agent's signChallenge is removed, so a call does not compile in TypeScript and throws in JavaScript, and signChallengeFor is the way to sign. New since 1.0.0-alpha.10: verifySignature requires the expected audience (see the changelog). These items are new since 1.0.0-alpha.9 (listed in the changelog): signChallengeFor on the agent, the 30 second read timeout, the refusal of a challenge dated in the future, the parallel status and score read in the gate, the gate's bond check and optional maxScoreAgeSeconds limit, and getScoreUpdatedAt. getTotalScore was already in alpha.9.

Project Structure

The verifier-related functionality lives primarily in the TypeScript SDK package under packages/sdk/src. The most relevant files are:

File Responsibility
verifier.ts SigvaraVerifier class, registration helper, and high-level verification logic
did.ts DID parsing, formatting, and deterministic hash derivation
challenge.ts Challenge generation, parsing, signing, verification, and expiration checks
keys.ts Base58 encoding/decoding, hex conversion, Ed25519 keypair handling, and multibase encoding
agent.ts Agent-side key management, DID exposure, challenge issuance, and signing
types.ts Shared interfaces for configuration, identity, reputation, challenges, and DID documents
README.md SDK usage examples, security notes, and gate guidance
verifier.test.ts Pre-chain guard tests for malformed payloads, mismatched DIDs, expired challenges, and signature width validation
reputation-model.md On-chain reputation model, maturity, thresholds, and oracle behavior
graph TB
subgraph "SDK Package"
V["verifier.ts"]
D["did.ts"]
C["challenge.ts"]
K["keys.ts"]
A["agent.ts"]
T["types.ts"]
end
V --> D
V --> C
V --> K
V --> T
A --> D
A --> C
A --> K
A --> T
Loading

Diagram sources

Section sources

Core Components

The core verifier component is SigvaraVerifier. It encapsulates:

  • An Ethereum JSON-RPC provider. Each read gives up after 30 seconds (DEFAULT_RPC_TIMEOUT_MS) unless timeoutMs in the config says otherwise, and the constructor throws if timeoutMs is not a positive number (alpha.9 uses ethers' own five minute default).
  • Contract addresses for identity, reputation, and staking.
  • Lazily initialized contract instances.
  • Chain ID caching.
  • DID-to-hash conversion.
  • Identity, reputation, stake, and stake-minimum queries.
  • Full signature verification with pre-checks, audience binding, TTL enforcement, and cryptographic verification.
  • W3C DID document building.

There is also a standalone registration helper that lets an operator register an agent on-chain using either an agent signer or a precomputed signature.

Key responsibilities by method:

Method / Function Purpose
getIdentity(did) Reads operator, agent address, Ed25519 public key, status, and registration timestamp
isActive(did) Checks whether the agent is active on-chain
getReputation(did) Reads six-factor reputation plus last updated time and total matured score
getTotalScore(did) Reads only the matured total score without factor breakdown
getScoreUpdatedAt(did) Reads when the score was last written, in unix seconds, 0 if never. The gate's maxScoreAgeSeconds limit uses it
meetsThreshold(did, threshold) Delegates threshold check to the reputation contract
getStake(did) Reads current stake amount
hasMinimumStake(did) Delegates minimum-stake check to the staking contract
getEarnedScore(did) Reads the earned score, the factors summed before maturity (from 1.0.0-alpha.15)
getPendingScore(did) Reads a proposal waiting out its challenge window: factors, times, proposer and evidence root (from 1.0.0-alpha.15)
getMinimumStake() Reads the minimum stake the staking contract asks now (from 1.0.0-alpha.15)
getPendingWithdrawal(did) Reads stake queued for withdrawal and when it can be claimed (from 1.0.0-alpha.15)
getSlashProposal(did) Reads the agent's most recent slash proposal and its state (from 1.0.0-alpha.15)
getStakeTokenDecimals() Reads the staking token's decimals, once (from 1.0.0-alpha.15)
getEvidenceRoot(did) Reads the evidence root of the finalized score, zero if none (from 1.0.0-alpha.15)
verifyEvidence(did, leaf, proof) Asks the reputation contract whether one leaf is in the finalized root (from 1.0.0-alpha.15)
verifySignature(...) Validates challenge payload, audience, freshness, signature width, on-chain identity, and Ed25519 signature
buildDidDocument(did) Builds a W3C DID document with controller, verification method, authentication, and assertion methods
registerAgent(...) Registers an agent on-chain and returns the resulting DID hash and transaction hash

Section sources

Architecture Overview

At a high level, verification flows through these layers:

  1. Caller input: DID, challenge payload, base58-encoded signature, an optional maximum age, and the expected audience, which is required.
  2. Payload validation: Parse the challenge and ensure it names the expected prover DID.
  3. Audience binding: Reject legacy v1 payloads and require the signed audience to match the expected audience.
  4. Freshness check: Reject challenges older than the configured maximum age, and challenges dated more than 60 seconds ahead of the verifier's clock.
  5. Signature shape check: Reject signatures outside the expected base58 length range before decoding or calling the chain.
  6. Identity read: Fetch the agent’s registered public key and registration timestamp from the identity contract.
  7. Public key conversion: Convert the bytes32 public key into raw Ed25519 bytes.
  8. Cryptographic verification: Verify the Ed25519 signature over the UTF-8 challenge payload.
  9. Result: Return true only if all checks pass; otherwise return false without leaking internal errors to the caller.
sequenceDiagram
participant Caller as "Caller"
participant Verifier as "SigvaraVerifier"
participant Challenge as "Challenge Utilities"
participant Identity as "Identity Contract"
participant Crypto as "Ed25519 Verification"
Caller->>Verifier : "verifySignature(did, payload, signature, maxAge, audience)"
Verifier->>Challenge : "parseChallengePayload(payload)"
Challenge-->>Verifier : "parsed fields"
Verifier->>Verifier : "check prover DID matches"
Verifier->>Verifier : "check audience binding"
Verifier->>Challenge : "isChallengeExpired(payload, maxAge)"
Challenge-->>Verifier : "expired or not"
Verifier->>Verifier : "validate signature base58 length"
Verifier->>Identity : "getIdentity(did)"
Identity-->>Verifier : "operator, agentAddress, ed25519PubKey, status, registeredAt"
Verifier->>Verifier : "convert bytes32 public key to Ed25519 bytes"
Verifier->>Crypto : "verifyChallenge(payload, signature, publicKey)"
Crypto-->>Verifier : "true or false"
Verifier-->>Caller : "boolean result"
Loading

Diagram sources

Detailed Component Analysis

Signature Verification with SigvaraVerifier.verifySignature

verifySignature is the central entry point for validating agent credentials. It performs layered checks to prevent replay attacks, relay attacks, malformed input abuse, and unnecessary chain calls.

Before step 1, a missing or empty expected audience throws expectedAudience is required. That is new in 1.0.0-alpha.11: earlier versions skipped step 3 when the audience was left out, so a response given to one verifier was accepted at another.

Verification steps:

  1. Parse the challenge payload. If parsing fails, the function returns false rather than throwing.
  2. Bind the prover DID. The payload must name the same DID passed to the function.
  3. Bind the audience. The expected audience is required:
    • Legacy v1 payloads are rejected because they do not carry an audience.
    • The signed audience must equal the expected audience.
  4. Check freshness. Expired challenges are rejected based on the payload timestamp and maximum age. A payload dated more than 60 seconds (MAX_FUTURE_SKEW_SECONDS) ahead of the verifier's clock is rejected too. It used to count as fresh for as long as its date stayed in the future, so a signature over a payload dated ten years out would have worked for ten years (on alpha.9 it still would).
  5. Validate signature width. Base58-encoded Ed25519 signatures have a bounded length. Out-of-range values are rejected before decoding.
  6. Read on-chain identity. The function fetches the agent’s registered public key and registration timestamp.
  7. Reject invalid identity state. Unregistered identities or missing public keys cause rejection.
  8. Verify the signature. The final cryptographic check uses the parsed payload, base58-decoded signature, and converted public key.
  9. Normalize errors. Cryptographic or decoding failures are treated as false, not exceptions, to avoid turning client input errors into server errors.
flowchart TD
Start(["verifySignature Entry"]) --> AudienceGiven{"Expected audience given?"}
AudienceGiven --> |No| ThrowMissing["Throw: expectedAudience is required"]
AudienceGiven --> |Yes| Parse["Parse challenge payload"]
Parse --> ParseOk{"Parse successful?"}
ParseOk --> |No| ReturnFalse["Return false"]
ParseOk --> |Yes| CheckDID["Check payload DID equals requested DID"]
CheckDID --> DIDMatch{"DID matches?"}
DIDMatch --> |No| ReturnFalse
DIDMatch --> |Yes| AudienceCheck{"v2 payload and<br/>audience matches?"}
AudienceCheck --> |No| ReturnFalse
AudienceCheck --> |Yes| Freshness["Check challenge expiration"]
Freshness --> Expired{"Expired?"}
Expired --> |Yes| ReturnFalse
Expired --> |No| WidthCheck["Check base58 signature length"]
WidthCheck --> WidthOk{"Length in valid range?"}
WidthOk --> |No| ReturnFalse
WidthOk --> |Yes| ReadIdentity["Read identity from chain"]
ReadIdentity --> ValidIdentity{"Registered and has public key?"}
ValidIdentity --> |No| ReturnFalse
ValidIdentity --> |Yes| VerifyCrypto["Verify Ed25519 signature"]
VerifyCrypto --> Result(["Return boolean"])
Loading

Diagram sources

Practical example pattern:

  • Generate a challenge with the agent’s DID and your service origin as the audience.
  • Have the agent sign the challenge payload. SigvaraAgent.signChallengeFor(payload, audience) signs only a v2 challenge that names this agent as the prover and names exactly the audience the agent passes, and otherwise throws and signs nothing. The agent's signChallenge, which signed whatever it was handed, is removed in 1.0.0-alpha.12 (alpha.9 has only signChallenge).
  • Call verifySignature with the same DID, payload, signature, TTL, and audience.
  • Combine the result with isActive, hasMinimumStake and meetsThreshold to decide whether to admit the request.

Section sources

DID Validation and Resolution

The SDK implements a compact DID scheme for Sigvara agents:

  • Parsing: parseDid validates the format did:sigvara:<chainId>:<address> and extracts the chain ID and agent address.
  • Formatting: formatDid reconstructs the canonical DID string from an address and chain ID.
  • Hashing: computeDidHash reproduces the on-chain keccak256 derivation used to identify agents deterministically.

Resolution is indirect: instead of fetching a remote DID document, the verifier builds one from on-chain state. This keeps identity authoritative and avoids external resolver failures during verification.

classDiagram
class DidHelpers {
+parseDid(did) ParsedDid
+formatDid(agentAddress, chainId) string
+computeDidHash(agentAddress, chainId) string
}
class SigvaraVerifier {
-provider
-addresses
-_chainId
+getIdentity(did) AgentIdentity
+buildDidDocument(did) DidDocument
-didHashFromDid(did) string
}
SigvaraVerifier --> DidHelpers : "uses"
Loading

Diagram sources

Practical example pattern:

  • Use SigvaraAgent.did and SigvaraAgent.didHash to obtain the formatted DID and its on-chain hash.
  • Pass the DID to SigvaraVerifier.getIdentity, getReputation, getTotalScore, or buildDidDocument.
  • For off-chain hashing or cross-system matching, use computeDidHash directly.

Section sources

Reputation Checking

Reputation is modeled as a six-factor score computed off-chain by the oracle and stored on-chain. The SDK exposes both detailed and aggregate views:

API Behavior
getReputation(did) Returns fee, success, age, external, community, propagation scores, last updated timestamp, and total matured score
getTotalScore(did) Returns only the matured total score, avoiding the extra tuple decode when only the threshold matters
meetsThreshold(did, threshold) Calls the reputation contract to check whether the matured score meets the threshold

Important concepts:

  • Earned vs matured score: The earned score is finalized by the oracle; the matured score climbs toward it over time. Threshold checks use the matured score.
  • New agents start low: A newly bonded agent begins with minimal score and must build activity, tenure, external trust, and counterparty standing.
  • Slashing resets reputation: Slashed agents have their reputation zeroed and cannot be reactivated under the same DID.
flowchart TD
Input(["Call getReputation or getTotalScore"]) --> DidHash["Convert DID to didHash"]
DidHash --> ReputationContract["Call reputation contract"]
ReputationContract --> Detail{"Need factor breakdown?"}
Detail --> |Yes| ReadTuple["Read six-factor tuple"]
Detail --> |No| ReadTotal["Read total score only"]
ReadTuple --> Assemble["Assemble ReputationData"]
ReadTotal --> ReturnTotal["Return number"]
Assemble --> ReturnData["Return ReputationData"]
Loading

Diagram sources

Practical example pattern:

  • For admission gates, prefer meetsThreshold or getTotalScore when you only need a single score comparison. Neither looks at status or bond, so pair them with isActive and hasMinimumStake.
  • For diagnostics, logging, or UI display, use getReputation to show the factor breakdown.
  • Always combine reputation with signature validity, active status and the bond (hasMinimumStake).

Section sources

Evidence Verification and Merkle Proofs

Evidence roots are proposed by the oracle with each score and stored by the reputation contract when the score is finalized; the contract's verifyEvidence checks one payment's leaf and proof against the stored root. From 1.0.0-alpha.15 the SDK can check them too: evidenceLeaf, evidenceRoot and matchEvidenceRoot rebuild an oracle's root from the payments its /evidence lists, held to the contract test's fixture, and SigvaraVerifier.getEvidenceRoot and verifyEvidence read the finalized root and ask the contract about one payment. sigvara-mcp's sigvara_verify_evidence puts these together.

Key points:

  • The oracle recomputes factors, proposes scores, and commits evidence roots.
  • Finalization remains permissionless, and divergence between operators is recorded for governance or checker modes.
  • A reproduced root shows the oracle scored on the payments it lists. It does not show each transaction happened as listed; read a payment's txHash on chain for that.

For consumers who need to validate evidence independently:

  • Use the SDK's evidence functions, or the oracle's Merkle utilities, which share one encoding.
  • Validate settlement transactions, payment attestations, and decay calculations according to the oracle’s rules.
  • Treat the SDK’s reputation APIs as a convenient read layer over on-chain state.

Section sources

Agent Credential Verification Workflow

A typical agent credential verification flow involves three parties:

  1. Agent: Generates an Ed25519 keypair, registers the agent on-chain, bonds stake, and signs challenges.
  2. Counterparty or service: Issues a challenge with the agent’s DID and a stable audience.
  3. Verifier: Validates the signature, checks active status, and checks reputation threshold.
sequenceDiagram
participant Service as "Service"
participant Gate as "SigvaraGate or Caller"
participant Agent as "SigvaraAgent"
participant Verifier as "SigvaraVerifier"
participant Chain as "On-chain Contracts"
Service->>Gate : "Request requiring trusted agent"
Gate->>Agent : "Issue challenge with prover DID and audience"
Agent->>Agent : "signChallengeFor(payload, audience)"
Agent-->>Gate : "Return base58 signature"
Gate->>Verifier : "verifySignature(did, payload, signature, ttl, audience)"
Verifier->>Chain : "Read identity and verify signature"
Chain-->>Verifier : "Public key and identity state"
Verifier-->>Gate : "Signature valid or not"
Gate->>Verifier : "isActive(did), hasMinimumStake(did), getTotalScore(did)"
Verifier->>Chain : "Read status, stake and reputation"
Chain-->>Verifier : "Status, bond and score"
Verifier-->>Gate : "Active and threshold met"
Gate-->>Service : "Admit or refuse with reason"
Loading

Diagram sources

Section sources

Registration Helper

The registerAgent helper allows an operator to register an agent on-chain. It supports two proof modes:

  • Agent signer mode: The operator passes a signer that can sign the registration digest.
  • Precomputed signature mode: The operator passes a signature string, useful when the agent is a contract or its key is managed externally.

After registration, the helper parses transaction logs to extract the emitted AgentRegistered event and returns the DID hash and transaction hash.

flowchart TD
Start(["registerAgent Entry"]) --> Contract["Create contract instance"]
Contract --> ProofMode{"Proof mode"}
ProofMode --> |Signer| Digest["Read registrationDigest from contract"]
ProofMode --> |Signature| UseSig["Use provided signature"]
Digest --> Sign["Sign digest with agent signer"]
UseSig --> Tx["Call registerAgent on-chain"]
Sign --> Tx
Tx --> Wait["Wait for receipt"]
Wait --> ParseLogs["Parse AgentRegistered event"]
ParseLogs --> Result(["Return didHash and txHash"])
Loading

Diagram sources

Section sources

Dependency Analysis

The verifier depends on several lower-level modules:

  • ethers: Provider, contract interaction, and cryptographic utilities.
  • did module: DID parsing, formatting, and hash computation.
  • challenge module: Challenge generation, parsing, signing, verification, and expiration.
  • keys module: Base58 encoding/decoding, hex conversion, and Ed25519 key transformations.
  • types module: Shared interfaces for configuration, identity, reputation, challenges, and DID documents.
graph LR
Ethers["ethers"] --> Verifier["verifier.ts"]
Did["did.ts"] --> Verifier
Challenge["challenge.ts"] --> Verifier
Keys["keys.ts"] --> Verifier
Types["types.ts"] --> Verifier
Loading

Diagram sources

Section sources

Performance Considerations

Several design choices in the verifier improve performance and reduce attack surface:

  • Lazy contract initialization: Contract instances are created once per verifier instance and reused.
  • Chain ID caching: The chain ID is fetched once and cached.
  • Pre-chain guards: Malformed payloads, mismatched DIDs, expired challenges, and out-of-range signatures are rejected before calling the chain. A well-sized signature for a registered agent still costs one identity read, because the key it is checked against lives on chain, so rate-limit callers where a verifier is exposed.
  • Read timeout: A chain read that gets no answer is abandoned after 30 seconds by default instead of ethers' five minutes.
  • Targeted reputation reads: getTotalScore avoids decoding the full reputation tuple when only the total is needed.
  • Parallel reputation reads: getReputation reads the reputation tuple and total score concurrently where supported.
  • Error normalization: Invalid input is returned as false rather than thrown, preventing client errors from becoming server errors.

Batch verification recommendations:

  • Reuse a single SigvaraVerifier instance across multiple verification calls to reuse provider and contract instances.
  • Batch independent reputation reads using Promise.all at the application layer.
  • Prefer getTotalScore or meetsThreshold when you do not need the full factor breakdown.
  • Cache nonces and seen challenges at the application layer if you call verifySignature directly; the SDK does not maintain nonce state.
  • Use a shared nonce store backed by Redis or a database when running multiple processes behind SigvaraGate. Set instances: 'multiple' on the gate as well; it then refuses to start unless the store has a consume(), which must be atomic, and atomicNonceStore builds such a store.

Troubleshooting Guide

Invalid or Malformed Signatures

Symptoms:

  • verifySignature returns false.
  • Base58 decoding throws internally but is caught and normalized to false.

Likely causes:

  • The signature is not a valid base58 string.
  • The signature length is outside the expected range.
  • The signature does not correspond to the Ed25519 public key stored on-chain.

Actions:

  • Ensure the agent signs the exact challenge payload returned by generateChallenge.
  • Confirm the signature is base58-encoded.
  • Verify that the agent’s public key was registered correctly.

Section sources

Expired Challenges

Symptoms:

  • verifySignature rejects the challenge even though the signature is valid.

Likely causes:

  • The challenge timestamp is older than maxAgeSeconds.
  • The challenge timestamp is more than 60 seconds ahead of the verifier's clock.
  • The system clock is skewed.
  • A stale challenge is being replayed.

Actions:

  • Issue fresh challenges with a reasonable TTL.
  • Compare the challenge timestamp with the verifier’s current time.
  • Implement nonce tracking to prevent replay within the freshness window.

Section sources

Audience Mismatch or Relay Attack Prevention

Symptoms:

  • verifySignature returns false for the payload, or throws expectedAudience is required because the call left the audience out.
  • A valid response works for one verifier but not another.

Likely causes:

  • The payload is version 1 and does not include an audience.
  • The signed audience does not match the expected audience.
  • A response intended for one service is presented to another.

Actions:

  • Always generate challenges with a stable audience identifier.
  • Pass the same audience to verifySignature.
  • A v1 payload names no audience, so verifySignature refuses it; ask the agent for a v2 challenge.

Section sources

Network Failures and RPC Errors

Symptoms:

  • Verification throws after passing pre-chain checks.
  • Identity or reputation reads fail.

Likely causes:

  • The RPC endpoint is unreachable, or has gone quiet. A read is abandoned after 30 seconds; alpha.9 waits the five minutes ethers allows.
  • The contract address is incorrect.
  • The chain ID does not match the provider network.

Actions:

  • Validate rpcUrl, addresses, and chainId in configuration.
  • Set timeoutMs if 30 seconds is the wrong limit for your node.
  • Retry failed RPC calls with backoff at the application layer.
  • Log the failing method and parameters for debugging.

Section sources

Expired or Invalid Credentials

Symptoms:

  • Agent is not active.
  • Reputation is below threshold.
  • Stake is insufficient.

Likely causes:

  • The agent is suspended, slashed, or pending bond.
  • The agent has not yet built sufficient matured reputation.
  • The agent has not posted the minimum stake.

Actions:

  • Check isActive separately from signature verification.
  • Use getReputation to diagnose which factors are low.
  • Confirm the agent has bonded at least the minimum stake.

Section sources

Conclusion

The Sigvara SDK verifier provides a secure, layered approach to agent authentication and reputation validation. Its design emphasizes:

  • Strong binding of challenges to both prover and verifier.
  • Early rejection of malformed or malicious input.
  • Clear separation between cryptographic verification, identity resolution, and reputation checks.
  • Practical APIs for both simple threshold checks and detailed reputation inspection.
  • Support for W3C-compatible DID documents built from on-chain state.

For production systems, combine verifySignature with active-status checks, reputation thresholds, stake validation, and robust nonce and audience handling. When scaling, reuse verifier instances, batch independent reads, and cache nonces in a shared store.

Overview

Clone this wiki locally