Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Semaphore v4 core libraries #468

Merged
merged 5 commits into from
Dec 15, 2023
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
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"scripts": {
"build:libraries": "yarn workspaces foreach -t --no-private run build",
"compile:contracts": "yarn workspace contracts compile",
"download:snark-artifacts": "rimraf snark-artifacts && ts-node scripts/download-snark-artifacts.ts",
"remove:template-files": "ts-node scripts/remove-template-files.ts",
"test": "yarn test:libraries && yarn test:contracts && yarn test:circuits",
"test:libraries": "jest --coverage",
Expand All @@ -24,7 +23,7 @@
"version:release": "changelogithub",
"commit": "cz",
"precommit": "lint-staged",
"postinstall": "yarn download:snark-artifacts && husky install"
"postinstall": "husky install"
},
"keywords": [
"ethereum",
Expand Down
1 change: 1 addition & 0 deletions packages/circuits/circuits.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"semaphore": {
"file": "semaphore",
"template": "Semaphore",
"pubs": ["message", "scope"],
"params": [10]
}
}
72 changes: 53 additions & 19 deletions packages/group/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
</h4>
</div>

| This library is an abstraction of [`@zk-kit/incremental-merkle-tree`](https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/incremental-merkle-tree). The main goal is to make it easier to create offchain groups, which are also used to generate Semaphore proofs. Semaphore groups are actually incremental Merkle trees, and the group members are tree leaves. Since the Merkle tree implementation we are using is a binary tree, the maximum number of members of a group is equal to `2^treeDepth`. |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| This library is an abstraction of the LeanIMT data structure (part of [`@zk-kit/imt`](https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/imt)). The main goal is to make it easier to create offchain groups, which are also used to generate Semaphore proofs. Semaphore groups are actually Merkle trees, and the group members are tree leaves. |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

## 🛠 Install

Expand All @@ -70,54 +70,88 @@ yarn add @semaphore-protocol/group

## 📜 Usage

\# **new Group**(groupId: _Member_, treeDepth = 20): _Group_
\# **new Group**(members: _BigNumberish[]_ = []): _Group_

```typescript
import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"

// Group with max 1048576 members (20^²).
const group1 = new Group(1)
const group1 = new Group()

// Group with max 65536 members (16^²).
const group2 = new Group(1, 16)

// Group with max 16777216 members (24^²).
const group3 = new Group(1, 24)

// Group with a list of predefined members.
const identity1 = new Identity()
const identity2 = new Identity()
const identity3 = new Identity()

const group3 = new Group(1, 16, [identity1.commitment, identity2.commitment, identity3.commitment])
const group2 = new Group([identity1.commitment, identity2.commitment])
```

\# **addMember**(identityCommitment: _Member_)
\# **addMember**(member: _BigNumberish_)

```typescript
import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"

const identity = new Identity()
const commitment = identity.generateCommitment()
const group = new Group()

const { commitment } = new Identity()

group.addMember(commitment)

// "12989101133047504182892154686643420754368236204022364847543591045056549053997"
console.log(group.members[0])
```

\# **updateMember**(index: _number_, member: _BigNumberish_)

```typescript
import { Group } from "@semaphore-protocol/group"

const group = new Group([1, 3])

group.updateMember(0, 2)

console.log(group.members[0]) // "2"
```

\# **removeMember**(index: _number_)

```typescript
import { Group } from "@semaphore-protocol/group"

const group = new Group([1, 3])

group.removeMember(0)

console.log(group.members[0]) // "0"
```

\# **indexOf**(member: _Member_): _number_
\# **indexOf**(member: _BigNumberish_): _number_

```typescript
group.indexOf(commitment) // 0
import { Group } from "@semaphore-protocol/group"

const group = new Group([1])

const index = group.indexOf(1)

console.log(index) // 0
```

\# **generateMerkleProof**(index: _number_): _MerkleProof_

```typescript
import { Group } from "@semaphore-protocol/group"

const group = new Group([1, 3])

const proof = group.generateMerkleProof(0)

console.log(proof)
/*
{
index: 0,
leaf: '1',
root: '21106761926285267690763443010820487107972411248208546226053195422384279971821',
siblings: [ '3' ]
}
*/
```
5 changes: 1 addition & 4 deletions packages/group/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@
"rollup-plugin-typescript2": "^0.31.2"
},
"dependencies": {
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/keccak256": "^5.7.0",
"@zk-kit/incremental-merkle-tree": "1.1.0"
"@zk-kit/imt": "^2.0.0-beta"
}
}
82 changes: 33 additions & 49 deletions packages/group/src/group.test.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,96 @@
import Group from "./group"
import hash from "./hash"

describe("Group", () => {
describe("# Group", () => {
it("Should create a group", () => {
const group = new Group(1)
const group = new Group()

expect(group.id).toBe(1)
expect(group.root.toString()).toContain("103543")
expect(group.depth).toBe(20)
expect(group.zeroValue).toBe(hash(1))
expect(group.members).toHaveLength(0)
})

it("Should not create a group with a wrong tree depth", () => {
const fun = () => new Group(1, 33)

expect(fun).toThrow("The tree depth must be between 16 and 32")
})

it("Should create a group with a different tree depth", () => {
const group = new Group(1, 32)

expect(group.root.toString()).toContain("460373")
expect(group.depth).toBe(32)
expect(group.zeroValue).toBe(hash(1))
expect(group.members).toHaveLength(0)
expect(group.root).toBeUndefined()
expect(group.depth).toBe(0)
expect(group.size).toBe(0)
})

it("Should create a group with a list of members", () => {
const group = new Group(2, 20, [1, 2, 3])
const group = new Group([1, 2, 3])

const group2 = new Group(2, 20)
const group2 = new Group()

group2.addMember(1)
group2.addMember(2)
group2.addMember(3)

expect(group.root.toString()).toContain(group2.root.toString())
expect(group.depth).toBe(20)
expect(group.zeroValue).toBe(hash(2))
expect(group.members).toHaveLength(3)
expect(group.root).toContain(group2.root)
expect(group.depth).toBe(2)
expect(group.size).toBe(3)
})
})

describe("# addMember", () => {
it("Should add a member to a group", () => {
const group = new Group(1)
const group = new Group()

group.addMember(BigInt(3))
group.addMember(3)

expect(group.members).toHaveLength(1)
expect(group.size).toBe(1)
})
})

describe("# addMembers", () => {
it("Should add many members to a group", () => {
const group = new Group(1)
const group = new Group()

group.addMembers([BigInt(1), BigInt(3)])
group.addMembers([1, 3])

expect(group.members).toHaveLength(2)
expect(group.size).toBe(2)
})
})

describe("# indexOf", () => {
it("Should return the index of a member in a group", () => {
const group = new Group(1)
group.addMembers([BigInt(1), BigInt(3)])
const group = new Group()
group.addMembers([1, 3])

const index = group.indexOf(BigInt(3))
const index = group.indexOf(3)

expect(index).toBe(1)
})
})

describe("# updateMember", () => {
it("Should update a member in a group", () => {
const group = new Group(1)
group.addMembers([BigInt(1), BigInt(3)])
const group = new Group()
group.addMembers([1, 3])

group.updateMember(0, BigInt(1))
group.updateMember(0, 1)

expect(group.members).toHaveLength(2)
expect(group.members[0]).toBe(BigInt(1))
expect(group.size).toBe(2)
expect(group.members[0]).toBe("1")
})
})

describe("# removeMember", () => {
it("Should remove a member from a group", () => {
const group = new Group(1)
group.addMembers([BigInt(1), BigInt(3)])
const group = new Group()
group.addMembers([1, 3])

group.removeMember(0)

expect(group.members).toHaveLength(2)
expect(group.members[0]).toBe(group.zeroValue)
expect(group.size).toBe(2)
expect(group.members[0]).toBe("0")
})
})

describe("# generateMerkleProof", () => {
it("Should generate a proof of membership", () => {
const group = new Group(1)
group.addMembers([BigInt(1), BigInt(3)])
const group = new Group()

group.addMembers([1, 3])

const proof = group.generateMerkleProof(0)

expect(proof.leaf).toBe(BigInt(1))
console.log(proof)

Check warning on line 91 in packages/group/src/group.test.ts

View workflow job for this annotation

GitHub Actions / style

Unexpected console statement

expect(proof.leaf).toBe("1")
})
})
})
Loading
Loading