Proof Verification
Verify the integrity of any action on the DUAL platform. Two types of cryptographic proofs—Merkle inclusion and zero-knowledge validity—enable investors, developers, auditors, and regulators to independently confirm that actions were processed correctly and state is tamper-proof.
Why Verification Matters
Trust in the DUAL platform is not blind. Every action can be independently verified using cryptographic proofs. Instead of relying on DUAL's assertions, you can perform mathematical verification that proves:
- •Don't trust, verify: Anyone can independently confirm the platform state without relying on DUAL's word.
- •No reliance on DUAL: Cryptographic proof is mathematical certainty. The proofs are valid even if DUAL goes offline or acts maliciously.
- •Regulatory value: Auditors and regulators can verify platform state without accessing sensitive data. Proofs are non-interactive and tamper-proof.
- •Investor confidence: Proof of correctness is embedded in the platform. Institutional participants can rely on cryptographic guarantees, not trust.
Key Metrics
Proof Types
2
Merkle inclusion + ZK validity
Verification Cost
~300K gas
On-chain ZK verification
Proof Size
~200 bytes
Merkle proof (log n depth)
Trustless
Yes
Fully independent verification
Two Types of Proof
DUAL uses two complementary proof systems to enable trustless verification:
Merkle Inclusion Proofs
Prove that a specific action is part of a checkpoint Merkle tree.
How it works:
Path from leaf (action hash) to root (checkpoint hash)
When to use:
"Was my transaction included?"
Proof size:
O(log n) - typically 200-300 bytes
ZK Validity Proofs
Prove that all state transitions in a checkpoint are valid.
How it works:
ZK-SNARK verifies computation without replaying it
When to use:
"Were all rules followed?"
Verification:
Single elliptic curve pairing check
Merkle Proof Deep Dive
Merkle proofs allow you to prove that a specific action was included in a checkpoint without downloading the entire batch. They are small, fast to verify, and work off-chain or on-chain.
Leaf Hash Construction
Each leaf is a SHA-256 hash of action metadata:
Proof Size & Complexity
Logarithmic space—grows slowly with batch size:
Verification Algorithm
To verify a Merkle proof:
- 1.Compute the leaf hash from action data (orgId || actionId || timestamp || payloadHash)
- 2.Obtain the Merkle proof (array of sibling hashes from leaf to root)
- 3.Hash upward along the proof path: hash(leaf, sibling₁) → hash(result, sibling₂) → ...
- 4.Verify the computed root matches the on-chain checkpoint root
This computation is O(log N) and requires only the Merkle proof and the checkpoint root—no need to download the entire batch.
Code: Verifying a Merkle Proof
Using the DUAL SDK:
import { DualClient } from "dual-sdk";
import crypto from "crypto";
const client = new DualClient({
token: process.env.DUAL_API_KEY,
authMode: "api_key",
});
// Get the Merkle proof for an action
const proofData = await client.sequencer.getProof(
"action-id-12345"
);
// Manually verify (optional—SDK does this automatically)
function verifyMerkleProof(
leaf,
proof,
root
) {
let hash = leaf;
for (const sibling of proof) {
hash = crypto
.createHash("sha256")
.update(Buffer.concat([hash, sibling]))
.digest();
}
return hash.equals(root);
}
const isIncluded = verifyMerkleProof(
proofData.leafHash,
proofData.proof,
proofData.checkpointRoot
);
console.log(isIncluded ? "✓ Included" : "✗ Not found");ZK Validity Proof Deep Dive
Zero-knowledge proofs prove that all state transitions in a checkpoint are valid without replaying the computation. This enables regulators and auditors to verify correctness independently.
What the Circuit Proves
- ✓Signature validity: All action signatures are cryptographically valid and signed by authorized parties
- ✓State transitions: All state transitions follow template rules and ownership constraints
- ✓No double-spends: No token is transferred or burned twice in the same checkpoint
- ✓State root: Final state root matches the claimed state root hash
Public Inputs
Known to both prover and verifier:
Verification
Performed via DUALVerifier contract:
Code: Verifying On-Chain
Call the DUALVerifier contract directly using ethers.js:
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const verifier = new ethers.Contract(
DUAL_VERIFIER_ADDRESS,
VERIFIER_ABI,
provider
);
const publicInputs = [
checkpointRoot,
previousStateRoot,
finalStateRoot,
actionCount
];
const isValid = await verifier.verify(
zkProof,
publicInputs
);
if (isValid) {
console.log("✓ ZK proof verified on-chain");
} else {
console.log("✗ Proof verification failed");
}Verification Workflows
Different workflows for different use cases:
Developers
Verify via API
GET /sequencer/proof/:actionIdFetch Merkle proof for a single action and verify off-chain using SDK methods.
Auditors
Bulk verification
GET /sequencer/checkpoints/:idDownload a checkpoint and verify all proofs in batch. Stream actions and verify state transitions.
Regulators
On-chain verification
DUALVerifier.verify()Call the verifier contract directly. No API access needed—verification is independent of DUAL servers.
Users
End-user flow
App webhookApplication automatically verifies proof when checkpoint is confirmed. Display verified badge in UI.
Building Verification Into Your App
Integrate proof verification into your application to show users that their actions are cryptographically verified.
SDK Methods
client.sequencer.getProof(actionId)Fetch the Merkle proof for a given action
client.sequencer.verifyProof(proof, root)Verify a Merkle proof against a checkpoint root (off-chain)
client.sequencer.getCheckpoint(checkpointId)Fetch full checkpoint metadata and actions
Full Example: Fetch and Verify
End-to-end verification flow:
async function verifyAction(actionId) {
// Step 1: Get the proof from the sequencer
const { proof, checkpointRoot } =
await client.sequencer.getProof(actionId);
// Step 2: Verify the proof
const isValid = await client.sequencer.verifyProof({
actionId,
proof,
checkpointRoot,
});
// Step 3: Show result to user
if (isValid) {
showVerifiedBadge(actionId);
console.log("✓ Cryptographically verified");
} else {
showErrorBadge(actionId);
console.log("✗ Verification failed");
}
return isValid;
}
// Call it after action confirmation
await client.actions.submit(actionData);
await verifyAction("action-id-12345");Webhook Integration
Auto-verify when checkpoints confirm:
// Listen for checkpoint.confirmed webhook
app.post("/webhooks/checkpoint", async (req) => {
const { checkpointId, actions } = req.body;
// Verify all actions in the checkpoint
const results = await Promise.all(
actions.map(async (action) => {
const isValid = await client.sequencer
.verifyProof({
actionId: action.id,
proof: action.proof,
checkpointRoot: action.checkpointRoot,
});
return { actionId: action.id, verified: isValid };
})
);
// Mark actions as verified in your database
await markActionsVerified(results);
});UI Pattern: Verification Status Badge
Display verification status in your UI:
function ActionRow({ action, verified }) {
return (
<div className="flex items-center gap-3">
<span>{action.description}</span>
{verified ? (
<span className="badge verified">
✓ Verified
</span>
) : verified === false ? (
<span className="badge error">
✗ Failed
</span>
) : (
<span className="badge pending">
⏳ Verifying...
</span>
)}
</div>
);
}Trust Model
DUAL offers multiple trust levels. Choose the one appropriate for your risk model:
Level 1: API Trust
Trust DUAL's API response directly.
Level 2: Merkle Trust
Trust cryptographic math, not DUAL.
Level 3: On-Chain Trust
Trust the blockchain, not DUAL.
When to Use Each Level
API Trust:
Early development, internal testing, low-stakes transactions
Merkle Trust:
Production apps, user-facing transactions, external audits
On-Chain Trust:
Institutional settlement, regulatory compliance, high-value assets