TypeScript Verifier
Note
The TypeScript verifier supports CSMT proofs only. MPF proof verification is not yet available in TypeScript.
A TypeScript library for verifying CSMT inclusion proofs client-side. This enables browser and Node.js applications to verify proofs without a Haskell runtime.
Installation
npm install @paolino/csmt-verify
Or with yarn:
yarn add @paolino/csmt-verify
Quick Start
import { parseProof, verifyInclusionProof } from '@paolino/csmt-verify';
// Parse CBOR-encoded proof bytes
const proof = parseProof(proofBytes);
// Verify the proof against a trusted root hash
if (verifyInclusionProof(trustedRootHash, proof)) {
console.log('Proof verified against trusted root!');
}
API Reference
Types
type Direction = 0 | 1; // L=0, R=1
type Key = Direction[];
type Hash = Uint8Array; // 32 bytes (Blake2b-256)
interface Indirect {
jump: Key;
value: Hash;
}
interface ProofStep {
stepConsumed: number;
stepSibling: Indirect;
}
interface InclusionProof {
proofKey: Key;
proofValue: Hash;
proofSteps: ProofStep[];
proofRootJump: Key;
}
Functions
parseProof(bytes: Uint8Array): InclusionProof
Parse CBOR-encoded proof bytes into an InclusionProof object.
const proof = parseProof(cborBytes);
console.log('Key length:', proof.proofKey.length);
console.log('Steps:', proof.proofSteps.length);
Throws an error if the bytes are not valid CBOR or don't match the expected format.
verifyInclusionProof(trustedRoot: Hash, proof: InclusionProof): boolean
Verify a proof by recomputing the root hash and comparing it to the supplied trusted root hash.
const isValid = verifyInclusionProof(trustedRoot, proof);
Returns true if the computed root matches the trusted root.
computeRootHash(proof: InclusionProof): Hash
Compute the Merkle root hash from the proof data. Useful when you need the computed hash for comparison with multiple trusted roots.
const computed = computeRootHash(proof);
verifyProofBytes(trustedRoot: Hash, bytes: Uint8Array): boolean
Convenience function that parses and verifies against a trusted root in one call.
const isValid = verifyProofBytes(trustedRoot, cborBytes);
Advanced Functions
For custom implementations or debugging:
import {
blake2b256, // Compute Blake2b-256 hash
rootHash, // Hash an Indirect value
combineHash, // Combine two Indirect values
serializeKey, // Serialize Key to bytes
serializeIndirect // Serialize Indirect to bytes
} from '@paolino/csmt-verify';
Usage Examples
Verify Against Trusted Root
import { parseProof, verifyInclusionProof } from '@paolino/csmt-verify';
async function verifyMembership(
proofBytes: Uint8Array,
trustedRoot: Uint8Array
): Promise<boolean> {
const proof = parseProof(proofBytes);
return verifyInclusionProof(trustedRoot, proof);
}
Browser Usage
The library works in browsers with no additional configuration:
<script type="module">
import { parseProof, verifyInclusionProof } from '@paolino/csmt-verify';
// Fetch proof from your API
const response = await fetch('/api/proof/mykey');
const proofBytes = new Uint8Array(await response.arrayBuffer());
const proof = parseProof(proofBytes);
const isValid = verifyInclusionProof(trustedRoot, proof);
document.getElementById('result').textContent =
isValid ? 'Valid' : 'Invalid';
</script>
Extracting Proof Data
import { parseProof, L, R } from '@paolino/csmt-verify';
const proof = parseProof(proofBytes);
// Convert key to hex string
const keyHex = proof.proofKey
.map(d => d === L ? '0' : '1')
.join('');
// Get value hash as hex
const valueHex = Buffer.from(proof.proofValue).toString('hex');
console.log(`Key: ${keyHex}`);
console.log(`Value hash: ${valueHex}`);
console.log(`Proof steps: ${proof.proofSteps.length}`);
Integration with Haskell Backend
The TypeScript library verifies proofs generated by the Haskell CSMT library.
Workflow
sequenceDiagram
participant Client as Browser/Node.js
participant Server as Haskell Backend
participant Chain as Blockchain
Client->>Server: Request proof for key
Server->>Server: generateInclusionProof
Server->>Client: CBOR proof bytes
Client->>Client: parseProof(bytes)
Client->>Chain: Get trusted root
Chain->>Client: root hash
Client->>Client: verifyInclusionProof(trusted, proof)
Generating Proofs (Server)
-- Haskell backend
import CSMT.Hashes (generateInclusionProof, fromKVHashes)
handleProofRequest :: ByteString -> Transaction m cf d ops (Maybe ByteString)
handleProofRequest key = do
result <- generateInclusionProof fromKVHashes kvCol csmtCol key
pure $ fmap snd result -- Return just the proof bytes
Verifying Proofs (Client)
// TypeScript client
const response = await fetch(`/proof/${key}`);
const proofBytes = new Uint8Array(await response.arrayBuffer());
const proof = parseProof(proofBytes);
if (verifyInclusionProof(trustedRoot, proof)) {
// Proof is valid against trusted root
}
Development
The TypeScript package has its own Nix flake for development:
cd verifiers/typescript
# Enter development shell
nix develop
# Install dependencies
npm install
# Run tests
npm test
# Or run tests via Nix
nix run .#test
# Build
npm run build
Dependencies
Both work in Node.js and browsers.