docs: add page about spartan implementation (#16)

* docs: spartan

* chore: requested changes

* chore: enhance some parts in spartan docs inlcuding requested changes

---------

Co-authored-by: sripwoud <me@sripwoud.xyz>
This commit is contained in:
Iskander
2024-09-12 10:28:56 +02:00
committed by GitHub
parent 7775afaf02
commit 9ef1df0b0e

View File

@@ -1 +1,196 @@
TODO
> Refer to the [Spartan circuit in `anonklub/anonklub` repository](https://github.com/anonklub/anonklub/tree/main/pkgs/spartan-ecdsa-wasm)
> This work here is mainly based on [spartan-ecdsa](https://github.com/personaelabs/spartan-ecdsa) from Personae labs.
> Requirements
>
> - [ ] Install [@anonklub/merkle-tree-worker](https://www.npmjs.com/package/@anonklub/merkle-tree-worker), a web worker that includes the WebAssembly compilation of a [binary merkle tree rust implementation](https://github.com/anonklub/anonklub/tree/main/pkgs/merkle-tree-wasm).
> - [ ] Install [@anonklub/spartan-ecdsa-worker](https://www.npmjs.com/package/@anonklub/spartan-ecdsa-worker), a web worker that contains the WebAssembly compilation of the Spartan circuit, implemented with the [`sapir`](https://github.com/personaelabs/sapir) rust crate.
## TLDR
###
```js
// React.js / Next.js example
import { type ProveMembershipFn, SpartanEcdsaWorker, type VerifyMembershipFn } from '@anonklub/spartan-ecdsa-worker'
import { type GenerateMerkleProofFn, MerkleTreeWorker } from '@anonklub/merkle-tree-worker'
import { useEffect, useState } from 'react'
// A React hook example for dealing with web-workers; i.e. https://github.com/anonklub/anonklub/blob/main/ui/src/hooks/useWorker.ts
export const useWorker = (
worker: typeof SpartanEcdsaWorker | typeof MerkleTreeWorker
) => {
const [isWorkerReady, setIsWorkerReady] = useState(false)
useEffect(() => {
void (async () => {
await worker.prepare()
setIsWorkerReady(true)
})()
}, [])
return isWorkerReady
}
// A React hook example for integrating with `@anonklub/merkle-tree-worker`; https://github.com/anonklub/anonklub/blob/main/ui/src/hooks/useMerkleTreeWorker.ts
export const useMerkleTreeWasmWorker = () => {
const isWorkerReady = useWorker(MerkleTreeWorker)
const generateMerkleProof: GenerateMerkleProofFn = async (
leaves,
leaf,
depth,
): Promise<Uint8Array> => {
process.env.NODE_ENV === 'development' && console.time('==>merkle')
const proof = await MerkleTreeWorker.generateMerkleProof(
leaves,
leaf,
depth,
)
process.env.NODE_ENV === 'development' && console.timeEnd('==>merkle')
return proof
}
return {
generateMerkleProof,
isWorkerReady,
}
}
// A React hook example for integrating with `@anonklub/spartan-ecdsa-worker` circuit web-worker; https://github.com/anonklub/anonklub/blob/main/ui/src/hooks/useSpartanEcdsaWorker.ts
export const useSpartanEcdsaWorker = () => {
const isWorkerReady = useWorker(SpartanEcdsaWorker)
const proveMembership: ProveMembershipFn = async ({
merkleProofBytesSerialized,
message,
sig,
}): Promise<Uint8Array> => {
process.env.NODE_ENV === 'development' && console.time('==> Prove')
const proof = await SpartanEcdsaWorker.proveMembership({
merkleProofBytesSerialized,
message,
sig,
})
process.env.NODE_ENV === 'development' && console.timeEnd('==> Prove')
return proof
}
const verifyMembership: VerifyMembershipFn = async (
anonklubProof: Uint8Array,
): Promise<boolean> => {
process.env.NODE_ENV === 'development' && console.time('==> Verify')
const isVerified = await SpartanEcdsaWorker.verifyMembership(anonklubProof)
process.env.NODE_ENV === 'development' && console.timeEnd('==> Verify')
return isVerified
}
return {
isWorkerReady,
proveMembership,
verifyMembership,
}
}
```
## Step By Step
### Prepare
[@anonklub/merkle-tree-worker](https://www.npmjs.com/package/@anonklub/merkle-tree-worker) and [@anonklub/spartan-ecdsa-worker](https://www.npmjs.com/package/@anonklub/spartan-ecdsa-worker) are designed to operate on the client side. In the example above, ensure that you prepare each worker using `await worker.prepare()`. Please check [`Wasm & Web-Workers`](https://anonklub.github.io/#/prove/wasm) doc for more details.
## Merkle Proof
Generating a Merkle proof to verify the inclusion of an Ethereum address within a Merkle tree is crucial for the circuit's functionality. We use a binary Merkle tree structure `@anonklub/merkle-tree-worker` library in case of Spartan circuits. Follow these steps to generate a Merkle proof:
1. Call the `prepare()` function as previously described.
2. Prepare the list of Ethereum addresses. The Anonklub project can assist in scanning the blockchain to create this list, check out [query docs](https://anonklub.github.io/#/apis?id=query).
3. Define the parameters needed to generate the Merkle proof, including the number of `leaves` in the tree, the tree's `depth` (e.g., default: 15), and the specific `leaf` (address) for which you want to prove membership.
4. The generated proof will be serialized, making it immediately usable as a parameter for the proof function in the circuit.
```js
export type GenerateMerkleProofFn = (
leaves: string[],
leaf: string,
depth: number,
) => Promise<Uint8Array>
```
## Membership Proof
Once you have generated the Merkle proof for an Ethereum address (leaf), you can proceed to create a Spartan proof for the membership of that address in the Merkle tree. Follow these steps to generate a Spartan membership proof:
1. Call the `prepare()` function as outlined earlier.
2. Sign a `message`and obtain the `signature` in hexadecimal format.
3. The generated Spartan membership proof `membershipProofSerialized` is in a serialized form and it will be ready for immediate use in the `verifyMembership()` function.
```js
export interface ProveInputs {
sig: Hex
message: string
merkleProofBytesSerialized: Uint8Array
}
```
### Example of use
```js
import { useAsync } from 'react-use'
import type { Hex } from 'viem'
import { useSpartanEcdsaWorker } from './useSpartanEcdsaWorker'
import { useStore } from './useStore'
export const useProofResult = () => {
const { proofRequest } = useStore()
const { isWorkerReady, proveMembership } = useSpartanEcdsaWorker()
return useAsync(async () => {
if (proofRequest === null || !isWorkerReady) return
return await proveMembership({
merkleProofBytesSerialized: proofRequest.merkleProof,
message: proofRequest.message,
sig: proofRequest.rawSignature as Hex,
})
}, [isWorkerReady, proofRequest])
}
```
## Membership Verification
After successfully generating the Spartan proof, you can proceed with verifying that proof.
- Ensure you have the `membershipProofSerialized` output from the proof of membership step.
```js
export interface VerifyInputs {
anonklubProof: Uint8Array
}
```
### Example of use
```js
import { useAsync } from 'react-use';
import { useSpartanEcdsaWorker } from './useSpartanEcdsaWorker';
import { useStore } from './useStore';
export const useVerifyProof = () => {
const { proof } = useStore();
const { verifyMembership } = useSpartanEcdsaWorker();
return useAsync(async () => {
if (proof === null) return;
return await verifyMembership(proof);
}, [proof]);
};
```