Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions examples/CRISP/client/libs/crispSDKWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ self.onmessage = async function (event) {

if (isMasking) {
proof = await sdk.generateMaskVoteProof({
serverUrl: crispServer,
e3Id,
publicKey,
balance,
Expand All @@ -35,7 +34,6 @@ self.onmessage = async function (event) {
})
} else {
proof = await sdk.generateVoteProof({
serverUrl: crispServer,
vote,
e3Id,
publicKey,
Expand Down
2 changes: 1 addition & 1 deletion examples/CRISP/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"deploy": "gh-pages -d dist"
},
"dependencies": {
"@crisp-e3/sdk": "0.5.3",
"@crisp-e3/sdk": "0.5.4",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/react": "^11.11.4",
"@phosphor-icons/react": "^2.1.4",
Expand Down
2 changes: 1 addition & 1 deletion examples/CRISP/packages/crisp-contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@crisp-e3/contracts",
"version": "0.5.3",
"version": "0.5.4",
"type": "module",
"files": [
"contracts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ import {
encodeSolidityProof,
generateMerkleTree,
SIGNATURE_MESSAGE_HASH,
encryptVote,
} from '@crisp-e3/sdk'
import { expect } from 'chai'
import { deployCRISPProgram, deployHonkVerifier, deployMockEnclave, ethers } from './utils'

let publicKey = generatePublicKey()
const previousCiphertext = encryptVote({ yes: 0n, no: 0n }, publicKey)

describe('CRISP Contracts', function () {
describe('decode tally', () => {
Expand Down Expand Up @@ -61,8 +59,6 @@ describe('CRISP Contracts', function () {
merkleLeaves: leaves,
balance,
messageHash: SIGNATURE_MESSAGE_HASH,
isFirstVote: true,
previousCiphertext,
slotAddress: address,
})

Expand Down Expand Up @@ -94,8 +90,6 @@ describe('CRISP Contracts', function () {
merkleLeaves: leaves,
balance,
messageHash: SIGNATURE_MESSAGE_HASH,
isFirstVote: true,
previousCiphertext,
slotAddress: address,
})

Expand Down
165 changes: 154 additions & 11 deletions examples/CRISP/packages/crisp-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,60 @@ npm install @crisp-e3/sdk

## Usage

### Get Round Details
### CrispSDK Class (Recommended)

The `CrispSDK` class provides a convenient interface that automatically handles server communication
for fetching previous ciphertexts and checking slot status.

```typescript
import { CrispSDK } from '@crisp-e3/sdk'

const sdk = new CrispSDK(serverUrl)

// Generate a vote proof (automatically fetches previous ciphertext if needed)
const voteProof = await sdk.generateVoteProof({
e3Id: 1,
vote: { yes: 100n, no: 0n },
publicKey: publicKeyBytes,
signature: '0x...',
messageHash: '0x...',
balance: 1000n,
slotAddress: '0x...',
merkleLeaves: [...],
})

// Generate a mask vote proof (automatically fetches previous ciphertext if needed)
const maskProof = await sdk.generateMaskVoteProof({
e3Id: 1,
balance: 1000n,
slotAddress: '0x...',
publicKey: publicKeyBytes,
merkleLeaves: [...],
})
```

### Standalone Functions

#### Get Round Details

```typescript
import { getRoundDetails } from '@crisp-e3/sdk'
import { getRoundDetails, getRoundTokenDetails } from '@crisp-e3/sdk'

const roundDetails = await getRoundDetails(serverUrl, e3Id)
const tokenDetails = await getRoundTokenDetails(serverUrl, e3Id)
```

### Get Token Balance
#### Get Token Balance and Supply

```typescript
import { getBalanceAt } from '@crisp-e3/sdk'
import { getBalanceAt, getTotalSupplyAt, getTreeData } from '@crisp-e3/sdk'

const balance = await getBalanceAt(voterAddress, tokenAddress, snapshotBlock, chainId)
const totalSupply = await getTotalSupplyAt(tokenAddress, snapshotBlock, chainId)
const merkleLeaves = await getTreeData(serverUrl, e3Id)
```

### Generate Vote Proof
#### Generate Vote Proof (Low-level)

```typescript
import { generateVoteProof } from '@crisp-e3/sdk'
Expand All @@ -44,12 +81,15 @@ const proof = await generateVoteProof({
vote: { yes: 100n, no: 0n },
publicKey: publicKeyBytes,
signature: '0x...',
messageHash: '0x...',
balance: 1000n,
slotAddress: '0x...',
merkleLeaves: [...],
previousCiphertext: previousCiphertextBytes, // optional
})
```

### Generate Mask Vote Proof
#### Generate Mask Vote Proof (Low-level)

```typescript
import { generateMaskVoteProof } from '@crisp-e3/sdk'
Expand All @@ -63,17 +103,120 @@ const maskProof = await generateMaskVoteProof({
})
```

### Verify Proof
#### Verify Proof

```typescript
import { verifyProof } from '@crisp-e3/sdk'

const isValid = await verifyProof(proof)
```

#### Decode Tally

```typescript
import { decodeTally } from '@crisp-e3/sdk'

const tally = decodeTally(tallyBytes)
// Returns: { yes: bigint, no: bigint }
```

#### Cryptographic Utilities

```typescript
import { generatePublicKey, encryptVote, encodeSolidityProof } from '@crisp-e3/sdk'

const publicKey = generatePublicKey()
const encryptedVote = encryptVote(vote, publicKey)
const encodedProof = encodeSolidityProof(proof)
```

#### Merkle Tree Utilities

```typescript
import {
generateMerkleProof,
generateMerkleTree,
hashLeaf,
getAddressFromSignature,
} from '@crisp-e3/sdk'

const leaf = hashLeaf(address, balance)
const tree = generateMerkleTree(leaves)
const proof = generateMerkleProof(balance, address, merkleLeaves)
const address = await getAddressFromSignature(signature, messageHash)
```

#### State Utilities

```typescript
import { getPreviousCiphertext, getIsSlotEmpty } from '@crisp-e3/sdk'

const previousCiphertext = await getPreviousCiphertext(serverUrl, e3Id, slotAddress)
const isEmpty = await getIsSlotEmpty(serverUrl, e3Id, slotAddress)
```

## API

- **State**: `getRoundDetails`, `getRoundTokenDetails`
- **Token**: `getBalanceAt`, `getTotalSupplyAt`, `getTreeData`
- **Vote**: `generateVoteProof`, `generateMaskVoteProof`, `verifyProof`, `decodeTally`
- **Utils**: `generateMerkleProof`, `generateMerkleTree`, `hashLeaf`, `getAddressFromSignature`
### CrispSDK Class

- `constructor(serverUrl: string)` - Create a new SDK instance
- `generateVoteProof(voteProofRequest: VoteProofRequest): Promise<ProofData>` - Generate a vote
proof (automatically handles previous ciphertext)
- `generateMaskVoteProof(maskVoteProofRequest: MaskVoteProofRequest): Promise<ProofData>` - Generate
a mask vote proof (automatically handles previous ciphertext)

### State Functions

- `getRoundDetails(serverUrl: string, e3Id: number): Promise<RoundDetails>` - Get round details
- `getRoundTokenDetails(serverUrl: string, e3Id: number): Promise<TokenDetails>` - Get token details
for a round
- `getPreviousCiphertext(serverUrl: string, e3Id: number, address: string): Promise<Uint8Array>` -
Get previous ciphertext for a slot
- `getIsSlotEmpty(serverUrl: string, e3Id: number, address: string): Promise<boolean>` - Check if a
slot is empty

### Token Functions

- `getBalanceAt(voterAddress: string, tokenAddress: string, snapshotBlock: number, chainId: number): Promise<bigint>` -
Get token balance at a specific block
- `getTotalSupplyAt(tokenAddress: string, snapshotBlock: number, chainId: number): Promise<bigint>` -
Get total supply at a specific block
- `getTreeData(serverUrl: string, e3Id: number): Promise<bigint[]>` - Get merkle tree leaves from
server

### Vote Functions

- `generateVoteProof(voteProofInputs: VoteProofInputs): Promise<ProofData>` - Generate a vote proof
(low-level)
- `generateMaskVoteProof(maskVoteProofInputs: MaskVoteProofInputs): Promise<ProofData>` - Generate a
mask vote proof (low-level)
- `verifyProof(proof: ProofData): Promise<boolean>` - Verify a proof locally
- `decodeTally(tallyBytes: string): Vote` - Decode an encoded tally
- `generatePublicKey(): Uint8Array` - Generate a random public key
- `encryptVote(vote: Vote, publicKey: Uint8Array): Uint8Array` - Encrypt a vote
- `encodeSolidityProof(proof: ProofData): Hex` - Encode proof for Solidity contract

### Utility Functions

- `generateMerkleProof(balance: bigint, address: string, leaves: bigint[] | string[]): MerkleProof` -
Generate merkle proof
- `generateMerkleTree(leaves: bigint[]): LeanIMT` - Generate merkle tree
- `hashLeaf(address: string, balance: bigint): bigint` - Hash a leaf node
- `getAddressFromSignature(signature: \`0x${string}\`, messageHash?: \`0x${string}\`):
Promise<string>` - Extract address from signature

### Constants

- `MERKLE_TREE_MAX_DEPTH` - Maximum depth of the merkle tree
- `SIGNATURE_MESSAGE` - Message used for signature verification
- `MAXIMUM_VOTE_VALUE` - Maximum allowed vote value
- `SIGNATURE_MESSAGE_HASH` - Hash of the signature message

### Types

- `RoundDetails` - Round details type
- `RoundDetailsResponse` - Server response type for round details
- `TokenDetails` - Token details type
- `Vote` - Vote type with `yes` and `no` bigint fields
- `MaskVoteProofInputs` - Inputs for mask vote proof generation
- `VoteProofInputs` - Inputs for vote proof generation
2 changes: 1 addition & 1 deletion examples/CRISP/packages/crisp-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@crisp-e3/sdk",
"version": "0.5.3",
"version": "0.5.4",
"type": "module",
"author": {
"name": "gnosisguild",
Expand Down
27 changes: 11 additions & 16 deletions examples/CRISP/packages/crisp-sdk/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

import { getIsSlotEmpty, getPreviousCiphertext } from './state'
import { encryptVote, generateMaskVoteProof, generateVoteProof } from './vote'
import { ZERO_VOTE } from './constants'
import { generateMaskVoteProof, generateVoteProof } from './vote'

import type { ProofData } from '@aztec/bb.js'
import type { MaskVoteProofRequest, VoteProofRequest } from './types'

/**
* A class representing the Crisp SDK.
* A class representing the CRISP SDK.
*/
export class CrispSDK {
/**
* The server URL for the Crisp SDK.
* It's used by methods that communicate directly with the Crisp server.
* The server URL for the CRISP SDK.
* It's used by methods that communicate directly with the CRISP server.
*/
private serverUrl: string

/**
* Create a new instance
* Create a new instance.
* @param serverUrl
*/
constructor(serverUrl: string) {
Expand All @@ -31,22 +30,21 @@ export class CrispSDK {

/**
* Generate a proof for a vote masking.
* @param maskProofInputs - The inputs required to generate the mask vote proof.
* @returns A promise that resolves to the generated proof data.
*/
async generateMaskVoteProof(maskProofInputs: MaskVoteProofRequest): Promise<ProofData> {
// check if the slot is empty first
const isSlotEmpty = await getIsSlotEmpty(this.serverUrl, maskProofInputs.e3Id, maskProofInputs.slotAddress)

let previousCiphertext: Uint8Array
let previousCiphertext

if (!isSlotEmpty) {
previousCiphertext = await getPreviousCiphertext(this.serverUrl, maskProofInputs.e3Id, maskProofInputs.slotAddress)
} else {
previousCiphertext = encryptVote(ZERO_VOTE, maskProofInputs.publicKey)
}

return generateMaskVoteProof({
...maskProofInputs,
previousCiphertext,
isFirstVote: isSlotEmpty,
})
}

Expand All @@ -56,20 +54,17 @@ export class CrispSDK {
* @returns A promise that resolves to the generated proof data.
*/
async generateVoteProof(voteProofInputs: VoteProofRequest): Promise<ProofData> {
// check if the slot is empty first
const isSlotEmpty = await getIsSlotEmpty(this.serverUrl, voteProofInputs.e3Id, voteProofInputs.slotAddress)

let previousCiphertext: Uint8Array
let previousCiphertext

if (!isSlotEmpty) {
previousCiphertext = await getPreviousCiphertext(this.serverUrl, voteProofInputs.e3Id, voteProofInputs.slotAddress)
} else {
previousCiphertext = encryptVote(ZERO_VOTE, voteProofInputs.publicKey)
}

return generateVoteProof({
...voteProofInputs,
previousCiphertext,
isFirstVote: isSlotEmpty,
})
}
}
Loading
Loading