Skip to content

Commit

Permalink
Merge pull request #1031 from privacy-scaling-explorations/deps/optim…
Browse files Browse the repository at this point in the history
…isedmt

refactor(optimisedmt): remove dependency and implement locally
  • Loading branch information
ctrlc03 authored Jan 17, 2024
2 parents 0ca81f5 + c7d81e2 commit 45896ab
Show file tree
Hide file tree
Showing 23 changed files with 931 additions and 1,420 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ module.exports = {
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: ["**/*.test.ts"],
devDependencies: ["**/*.test.ts", "**/__benchmarks__/**"],
},
],
"no-debugger": isProduction ? "error" : "off",
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Benchmarks

on:
push:
branches: [dev]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest

- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- name: Install
run: |
pnpm install --frozen-lockfile --prefer-offline
- name: Build
run: |
pnpm build
- name: Benchmarks
run: pnpm benchmarks
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,6 @@ circuits/circom/test
publish

# typedoc
docs/typedoc/*
docs/typedoc/*

**/ts/__benchmarks__/results/**
4 changes: 2 additions & 2 deletions circuits/ts/__tests__/IncrementalQuinTree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ describe("IncrementalQuinTree circuit", function test() {
tree.insert(leaf);
});

const proof = tree.genMerklePath(2);
const proof = tree.genProof(2);

const circuitInputs = {
root: tree.root,
leaf: 3n,
path_elements: proof.pathElements,
path_index: proof.indices,
path_index: proof.pathIndices,
};

const witness = await circuitLeafExists.calculateWitness(circuitInputs);
Expand Down
1 change: 0 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"dependencies": {
"@commander-js/extra-typings": "^11.1.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"big-integer": "^1.6.52",
"commander": "^11.1.0",
"dotenv": "^16.3.1",
"ethers": "^6.10.0",
Expand Down
8 changes: 6 additions & 2 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"scripts": {
"watch": "tsc --watch",
"build": "tsc -p tsconfig.build.json",
"benchmarks": "ts-node ts/__benchmarks__/index.ts",
"types": "tsc -p tsconfig.json --noEmit",
"test:processMessage": "ts-mocha --exit ts/__tests__/ProcessMessage.test.ts",
"test:maciState": "ts-mocha --exit ts/__tests__/MaciState.test.ts",
"test:e2e": "ts-mocha --exit ts/__tests__/e2e.test.ts",
"test:utils": "ts-mocha --exit ts/__tests__/utils.test.ts",
Expand All @@ -23,10 +23,12 @@
"@types/chai": "^4.3.11",
"@types/mocha": "^10.0.6",
"@types/node": "^20.11.2",
"benny": "^3.7.1",
"chai": "^4.3.10",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"ts-mocha": "^10.0.0"
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1"
},
"nyc": {
"reporter": [
Expand All @@ -38,6 +40,8 @@
],
"all": true,
"exclude": [
"ts/__benchmarks__/**",
"**/__tests__/**",
"**/__tests__/*.ts",
"**/*.js",
"**/*.d.ts"
Expand Down
39 changes: 17 additions & 22 deletions core/ts/Poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,6 @@ export class Poll implements IPoll {
*/
copyStateFromMaci = (): void => {
// Copy the state tree, ballot tree, state leaves, and ballot leaves
assert(this.maciStateRef.stateLeaves.length === this.maciStateRef.stateTree.nextIndex);

this.stateLeaves = this.maciStateRef.stateLeaves.map((x) => x.copy());
this.stateTree = this.maciStateRef.stateTree.copy();

Expand Down Expand Up @@ -277,18 +275,18 @@ export class Poll implements IPoll {
// calculate the path elements for the state tree given the original state tree (before any changes)
// changes could effectively be made by this new vote - either a key change or vote change
// would result in a different state leaf
const originalStateLeafPathElements = this.stateTree?.genMerklePath(Number(stateLeafIndex)).pathElements;
const originalStateLeafPathElements = this.stateTree?.genProof(Number(stateLeafIndex)).pathElements;
// calculate the path elements for the ballot tree given the original ballot tree (before any changes)
// changes could effectively be made by this new ballot
const originalBallotPathElements = this.ballotTree?.genMerklePath(Number(stateLeafIndex)).pathElements;
const originalBallotPathElements = this.ballotTree?.genProof(Number(stateLeafIndex)).pathElements;

// create a new quinary tree where we insert the votes of the origin (up until this message is processed) ballot
const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, STATE_TREE_ARITY, hash5);
for (let i = 0; i < this.ballots[0].votes.length; i += 1) {
vt.insert(ballot.votes[i]);
}
// calculate the path elements for the vote option tree given the original vote option tree (before any changes)
const originalVoteWeightsPathElements = vt.genMerklePath(voteOptionIndex).pathElements;
const originalVoteWeightsPathElements = vt.genProof(voteOptionIndex).pathElements;
// we return the data which is then to be used in the processMessage circuit
// to generate a proof of processing
return {
Expand Down Expand Up @@ -524,10 +522,10 @@ export class Poll implements IPoll {
if (e instanceof ProcessMessageError) {
// Since the command is invalid, use a blank state leaf
currentStateLeaves.unshift(this.stateLeaves[0].copy());
currentStateLeavesPathElements.unshift(this.stateTree!.genMerklePath(0).pathElements);
currentStateLeavesPathElements.unshift(this.stateTree!.genProof(0).pathElements);
// since the command is invliad we use the blank ballot
currentBallots.unshift(this.ballots[0].copy());
currentBallotsPathElements.unshift(this.ballotTree!.genMerklePath(0).pathElements);
currentBallotsPathElements.unshift(this.ballotTree!.genProof(0).pathElements);

// Since the command is invalid, we use a zero vote weight
currentVoteWeights.unshift(this.ballots[0].votes[0]);
Expand All @@ -536,7 +534,7 @@ export class Poll implements IPoll {
const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, STATE_TREE_ARITY, hash5);
vt.insert(this.ballots[0].votes[0]);
// get the path elements for this empty vote weight leaf
currentVoteWeightsPathElements.unshift(vt.genMerklePath(0).pathElements);
currentVoteWeightsPathElements.unshift(vt.genProof(0).pathElements);
} else {
throw e;
}
Expand All @@ -550,7 +548,7 @@ export class Poll implements IPoll {
const amount = message.data[0] >= BigInt(this.ballots.length) ? 0n : message.data[1];

currentStateLeaves.unshift(this.stateLeaves[stateIndex].copy());
currentStateLeavesPathElements.unshift(this.stateTree!.genMerklePath(stateIndex).pathElements);
currentStateLeavesPathElements.unshift(this.stateTree!.genProof(stateIndex).pathElements);

// create a copy of the state leaf
const newStateLeaf = this.stateLeaves[stateIndex].copy();
Expand All @@ -564,7 +562,7 @@ export class Poll implements IPoll {
// we still need them as placeholder for vote command
const currentBallot = this.ballots[stateIndex].copy();
currentBallots.unshift(currentBallot);
currentBallotsPathElements.unshift(this.ballotTree!.genMerklePath(Number(stateIndex)).pathElements);
currentBallotsPathElements.unshift(this.ballotTree!.genProof(Number(stateIndex)).pathElements);
currentVoteWeights.unshift(currentBallot.votes[0]);

// create a quinary tree to fill with the votes of the current ballot
Expand All @@ -575,7 +573,7 @@ export class Poll implements IPoll {
}

// add to the first position the path elements of the vote weight tree
currentVoteWeightsPathElements.unshift(vt.genMerklePath(0).pathElements);
currentVoteWeightsPathElements.unshift(vt.genProof(0).pathElements);
} catch (e) {
// eslint-disable-next-line no-console
console.log("Error processing topup message: ", (e as Error).message);
Expand All @@ -588,10 +586,10 @@ export class Poll implements IPoll {
} else {
// Since we don't have a command at that position, use a blank state leaf
currentStateLeaves.unshift(this.stateLeaves[0].copy());
currentStateLeavesPathElements.unshift(this.stateTree!.genMerklePath(0).pathElements);
currentStateLeavesPathElements.unshift(this.stateTree!.genProof(0).pathElements);
// since the command is invliad we use the blank ballot
currentBallots.unshift(this.ballots[0].copy());
currentBallotsPathElements.unshift(this.ballotTree!.genMerklePath(0).pathElements);
currentBallotsPathElements.unshift(this.ballotTree!.genProof(0).pathElements);

// Since the command is invalid, we use a zero vote weight
currentVoteWeights.unshift(this.ballots[0].votes[0]);
Expand All @@ -601,7 +599,7 @@ export class Poll implements IPoll {
vt.insert(this.ballots[0].votes[0]);

// get the path elements for this empty vote weight leaf
currentVoteWeightsPathElements.unshift(vt.genMerklePath(0).pathElements);
currentVoteWeightsPathElements.unshift(vt.genProof(0).pathElements);
}
}

Expand Down Expand Up @@ -690,12 +688,9 @@ export class Poll implements IPoll {
}

// generate the path to the subroot of the message tree for this batch
const messageSubrootPath = this.messageTree.genMerkleSubrootPath(index, index + messageBatchSize);
const messageSubrootPath = this.messageTree.genSubrootProof(index, index + messageBatchSize);

assert(
IncrementalQuinTree.verifyMerklePath(messageSubrootPath, this.messageTree.hashFunc),
"The message subroot path is invalid",
);
assert(this.messageTree.verifyProof(messageSubrootPath), "The message subroot path is invalid");

// validate that the batch index is correct, if not fix it
// this means that the end will be the last message
Expand Down Expand Up @@ -815,8 +810,8 @@ export class Poll implements IPoll {
const colStartIndex = this.cbi * batchSize;
const [ballots1, ballots2] = this.subsidyCalculation(rowStartIndex, colStartIndex);

const ballotSubrootProof1 = this.ballotTree?.genMerkleSubrootPath(rowStartIndex, rowStartIndex + batchSize);
const ballotSubrootProof2 = this.ballotTree?.genMerkleSubrootPath(colStartIndex, colStartIndex + batchSize);
const ballotSubrootProof1 = this.ballotTree?.genSubrootProof(rowStartIndex, rowStartIndex + batchSize);
const ballotSubrootProof2 = this.ballotTree?.genSubrootProof(colStartIndex, colStartIndex + batchSize);

const newSubsidySalt = genRandomSalt();
saltIndex = `${this.rbi.toString()}-${this.cbi.toString()}`;
Expand Down Expand Up @@ -1087,7 +1082,7 @@ export class Poll implements IPoll {
const packedVals = packTallyVotesSmallVals(batchStartIndex, batchSize, this.maciStateRef.numSignUps);
const inputHash = sha256Hash([packedVals, sbCommitment, currentTallyCommitment, newTallyCommitment]);

const ballotSubrootProof = this.ballotTree?.genMerkleSubrootPath(batchStartIndex, batchStartIndex + batchSize);
const ballotSubrootProof = this.ballotTree?.genSubrootProof(batchStartIndex, batchStartIndex + batchSize);

const votes = ballots.map((x) => x.votes);

Expand Down
110 changes: 110 additions & 0 deletions core/ts/__benchmarks__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import benny from "benny";
import { Keypair, PCommand } from "maci-domainobjs";

import { MaciState } from "..";

import {
COORDINATOR_KEYPAIR,
DURATION,
MAX_VALUES,
MESSAGE_BATCH_SIZE,
STATE_TREE_DEPTH,
TREE_DEPTHS,
VOICE_CREDIT_BALANCE,
} from "./utils/constants";

const NAME = "maci-core";

export default function runCore(): void {
benny.suite(
NAME,

benny.add(`maci-core - Generate circuit inputs for 10 signups and 50 messages`, () => {
const voteWeight = 9n;

const users: Keypair[] = [];

const maciState = new MaciState(STATE_TREE_DEPTH);
// Sign up and vote
for (let i = 0; i < MESSAGE_BATCH_SIZE - 1; i += 1) {
const userKeypair = new Keypair();
users.push(userKeypair);

maciState.signUp(userKeypair.pubKey, VOICE_CREDIT_BALANCE, BigInt(Math.floor(Date.now() / 1000)));
}

const pollId = maciState.deployPoll(
BigInt(Math.floor(Date.now() / 1000) + DURATION),
MAX_VALUES,
TREE_DEPTHS,
MESSAGE_BATCH_SIZE,
COORDINATOR_KEYPAIR,
);
const poll = maciState.polls[pollId];

// 24 valid votes
for (let i = 0; i < MESSAGE_BATCH_SIZE - 1; i += 1) {
const userKeypair = users[i];

const command = new PCommand(
BigInt(i + 1),
userKeypair.pubKey,
BigInt(i), // vote option index
voteWeight,
1n,
BigInt(pollId),
);

const signature = command.sign(userKeypair.privKey);

const ecdhKeypair = new Keypair();
const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, COORDINATOR_KEYPAIR.pubKey);
const message = command.encrypt(signature, sharedKey);
poll.publishMessage(message, ecdhKeypair.pubKey);
}

// 24 invalid votes
for (let i = 0; i < MESSAGE_BATCH_SIZE - 1; i += 1) {
const userKeypair = users[i];
const command = new PCommand(
BigInt(i + 1),
userKeypair.pubKey,
BigInt(i), // vote option index
VOICE_CREDIT_BALANCE * 2n, // invalid vote weight
1n,
BigInt(pollId),
);

const signature = command.sign(userKeypair.privKey);

const ecdhKeypair = new Keypair();
const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, COORDINATOR_KEYPAIR.pubKey);
const message = command.encrypt(signature, sharedKey);
poll.publishMessage(message, ecdhKeypair.pubKey);
}

// Process messages
poll.processMessages(pollId);

// Process messages
poll.processMessages(pollId);

// Test processAllMessages
poll.processAllMessages();
}),

benny.cycle(),
benny.complete((results) => {
results.results.forEach((result) => {
// eslint-disable-next-line no-console
console.log(`${result.name}: mean time: ${result.details.mean.toFixed(2)}`);
});
}),

benny.save({ folder: "ts/__benchmarks__/results", file: NAME, version: "1.0.0", details: true }),
benny.save({ folder: "ts/__benchmarks__/results", file: NAME, format: "chart.html", details: true }),
benny.save({ folder: "ts/__benchmarks__/results", file: NAME, format: "table.html", details: true }),
);
}

runCore();
19 changes: 19 additions & 0 deletions core/ts/__benchmarks__/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Keypair } from "maci-domainobjs";

export const VOICE_CREDIT_BALANCE = 100n;
export const DURATION = 30;
export const MESSAGE_BATCH_SIZE = 25;
export const COORDINATOR_KEYPAIR = new Keypair();
export const STATE_TREE_DEPTH = 10;
export const MAX_VALUES = {
maxUsers: 25,
maxMessages: 25,
maxVoteOptions: 25,
};

export const TREE_DEPTHS = {
intStateTreeDepth: 2,
messageTreeDepth: 3,
messageTreeSubDepth: 2,
voteOptionTreeDepth: 4,
};
Loading

0 comments on commit 45896ab

Please sign in to comment.