Skip to content

Commit

Permalink
feat(group): update group with lean-imt
Browse files Browse the repository at this point in the history
  • Loading branch information
cedoor committed Jan 12, 2024
1 parent 202e4be commit 1c8e218
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 131 deletions.
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)

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

0 comments on commit 1c8e218

Please sign in to comment.