Skip to content

Commit

Permalink
Merge pull request privacy-scaling-explorations#866 from privacy-scal…
Browse files Browse the repository at this point in the history
…ing-explorations/newstr

refactor(core): remove unnecessary `accumulatorQueue` in offchain instance
  • Loading branch information
ctrlc03 authored Dec 7, 2023
2 parents ddc1061 + 0588ef6 commit 477368c
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 285 deletions.
82 changes: 48 additions & 34 deletions circuits/ts/__tests__/ProcessMessages.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import fs from "fs";
import path from "path";

import { MaciState, packProcessMessageSmallVals } from "maci-core";
import { expect } from "chai";

import { MaciState, packProcessMessageSmallVals, STATE_TREE_ARITY } from "maci-core";
import { PrivKey, Keypair, PCommand, Message, Ballot } from "maci-domainobjs";

import { hash5, IncrementalQuinTree, stringifyBigInts, NOTHING_UP_MY_SLEEVE } from "maci-crypto";
import { AccQueue, hash5, IncrementalQuinTree, stringifyBigInts, NOTHING_UP_MY_SLEEVE } from "maci-crypto";

import { STATE_TREE_DEPTH, getSignal } from "./utils";
import path from "path";
import { expect } from "chai";

const tester = require("circom_tester").wasm;

const voiceCreditBalance = BigInt(100);
Expand Down Expand Up @@ -64,9 +64,6 @@ describe("ProcessMessage circuit", function () {
BigInt(Math.floor(Date.now() / 1000)),
);

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);

pollId = maciState.deployPoll(
duration,
// BigInt(2 + duration),
Expand Down Expand Up @@ -117,17 +114,27 @@ describe("ProcessMessage circuit", function () {
commands.push(command2);
poll.publishMessage(message2, ecdhKeypair2.pubKey);

poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(treeDepths.messageTreeDepth);
// Use the accumulator queue to compare the root of the message tree
const accumulatorQueue: AccQueue = new AccQueue(
treeDepths.messageTreeSubDepth,
STATE_TREE_ARITY,
NOTHING_UP_MY_SLEEVE,
);
accumulatorQueue.enqueue(message.hash(ecdhKeypair.pubKey));
accumulatorQueue.enqueue(message2.hash(ecdhKeypair2.pubKey));
accumulatorQueue.mergeSubRoots(0);
accumulatorQueue.merge(treeDepths.messageTreeDepth);

expect(poll.messageTree.root.toString()).to.be.eq(poll.messageAq.getRoot(treeDepths.messageTreeDepth).toString());
expect(poll.messageTree.root.toString()).to.be.eq(
accumulatorQueue.mainRoots[treeDepths.messageTreeDepth].toString(),
);
});

it("should produce the correct state root and ballot root", async () => {
// The current roots
const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth);
const emptyBallotHash = emptyBallot.hash();
const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), poll.STATE_TREE_ARITY, hash5);
const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash5);

ballotTree.insert(emptyBallot.hash());

Expand Down Expand Up @@ -197,9 +204,6 @@ describe("ProcessMessage circuit", function () {
BigInt(1), //BigInt(Math.floor(Date.now() / 1000)),
);

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);

pollId = maciState.deployPoll(
duration,
BigInt(2 + duration), //BigInt(Math.floor(Date.now() / 1000) + duration),
Expand Down Expand Up @@ -230,18 +234,26 @@ describe("ProcessMessage circuit", function () {

poll.publishMessage(message, ecdhKeypair.pubKey);

// Merge
poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(treeDepths.messageTreeDepth);
// Use the accumulator queue to compare the root of the message tree
const accumulatorQueue: AccQueue = new AccQueue(
treeDepths.messageTreeSubDepth,
STATE_TREE_ARITY,
NOTHING_UP_MY_SLEEVE,
);
accumulatorQueue.enqueue(message.hash(ecdhKeypair.pubKey));
accumulatorQueue.mergeSubRoots(0);
accumulatorQueue.merge(treeDepths.messageTreeDepth);

expect(poll.messageTree.root.toString()).to.be.eq(poll.messageAq.getRoot(treeDepths.messageTreeDepth).toString());
expect(poll.messageTree.root.toString()).to.be.eq(
accumulatorQueue.getRoot(treeDepths.messageTreeDepth).toString(),
);
});

it("should produce the correct state root and ballot root", async () => {
// The current roots
const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth);
const emptyBallotHash = emptyBallot.hash();
const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), poll.STATE_TREE_ARITY, hash5);
const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash5);

ballotTree.insert(emptyBallot.hash());

Expand Down Expand Up @@ -288,9 +300,6 @@ describe("ProcessMessage circuit", function () {
BigInt(1), //BigInt(Math.floor(Date.now() / 1000)),
);

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);

pollId = maciState.deployPoll(
duration,
BigInt(2 + duration), //BigInt(Math.floor(Date.now() / 1000) + duration),
Expand Down Expand Up @@ -362,18 +371,28 @@ describe("ProcessMessage circuit", function () {
commands.push(command3);
poll.publishMessage(message3, ecdhKeypair3.pubKey);

// Merge
poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(treeDepths.messageTreeDepth);

expect(poll.messageTree.root.toString()).to.be.eq(poll.messageAq.getRoot(treeDepths.messageTreeDepth).toString());
// Use the accumulator queue to compare the root of the message tree
const accumulatorQueue: AccQueue = new AccQueue(
treeDepths.messageTreeSubDepth,
STATE_TREE_ARITY,
NOTHING_UP_MY_SLEEVE,
);
accumulatorQueue.enqueue(message.hash(ecdhKeypair.pubKey));
accumulatorQueue.enqueue(message2.hash(ecdhKeypair2.pubKey));
accumulatorQueue.enqueue(message3.hash(ecdhKeypair3.pubKey));
accumulatorQueue.mergeSubRoots(0);
accumulatorQueue.merge(treeDepths.messageTreeDepth);

expect(poll.messageTree.root.toString()).to.be.eq(
accumulatorQueue.getRoot(treeDepths.messageTreeDepth).toString(),
);
});

it("should produce the correct state root and ballot root", async () => {
// The current roots
const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth);
const emptyBallotHash = emptyBallot.hash();
const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), poll.STATE_TREE_ARITY, hash5);
const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash5);

ballotTree.insert(NOTHING_UP_MY_SLEEVE);

Expand Down Expand Up @@ -410,8 +429,6 @@ describe("ProcessMessage circuit", function () {
BigInt(Math.floor(Date.now() / 1000)),
);

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);
// Sign up and publish
const pollId = maciState.deployPoll(
duration,
Expand Down Expand Up @@ -444,9 +461,6 @@ describe("ProcessMessage circuit", function () {
poll.publishMessage(message, ecdhKeypair.pubKey);
}

poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(treeDepths.messageTreeDepth);

for (let i = 0; i < NUM_BATCHES; i++) {
const generatedInputs = poll.processMessages(pollId);

Expand Down
37 changes: 19 additions & 18 deletions circuits/ts/__tests__/TallyVotes.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { STATE_TREE_DEPTH } from "./utils";

import { MaciState } from "maci-core";

import { Keypair, PCommand, Message } from "maci-domainobjs";

import path from "path";
import { expect } from "chai";
import { beforeEach } from "mocha";

import { Keypair, PCommand, Message } from "maci-domainobjs";
import { MaciState, STATE_TREE_ARITY } from "maci-core";
import { AccQueue, NOTHING_UP_MY_SLEEVE } from "maci-crypto";

import { STATE_TREE_DEPTH } from "./utils";

const tester = require("circom_tester").wasm;

const voiceCreditBalance = BigInt(100);
Expand Down Expand Up @@ -54,9 +55,6 @@ describe("TallyVotes circuit", function () {
const userKeypair = new Keypair();
stateIndex = maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000)));

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);

pollId = maciState.deployPoll(
duration,
BigInt(Math.floor(Date.now() / 1000) + duration),
Expand Down Expand Up @@ -88,10 +86,19 @@ describe("TallyVotes circuit", function () {

poll.publishMessage(message, ecdhKeypair.pubKey);

poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(treeDepths.messageTreeDepth);
// Use the accumulator queue to compare the root of the message tree
const accumulatorQueue: AccQueue = new AccQueue(
treeDepths.messageTreeSubDepth,
STATE_TREE_ARITY,
NOTHING_UP_MY_SLEEVE,
);
accumulatorQueue.enqueue(message.hash(ecdhKeypair.pubKey));
accumulatorQueue.mergeSubRoots(0);
accumulatorQueue.merge(treeDepths.messageTreeDepth);

expect(poll.messageTree.root.toString()).to.be.eq(poll.messageAq.getRoot(treeDepths.messageTreeDepth).toString());
expect(poll.messageTree.root.toString()).to.be.eq(
accumulatorQueue.mainRoots[treeDepths.messageTreeDepth].toString(),
);
// Process messages
poll.processMessages();
});
Expand Down Expand Up @@ -173,9 +180,6 @@ describe("TallyVotes circuit", function () {
maciState.signUp(k.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000) + duration));
}

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);

const pollId = maciState.deployPoll(
duration,
BigInt(Math.floor(Date.now() / 1000) + duration),
Expand Down Expand Up @@ -206,9 +210,6 @@ describe("TallyVotes circuit", function () {
poll.publishMessage(message, ecdhKeypair.pubKey);
}

poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(treeDepths.messageTreeDepth);

for (let i = 0; i < NUM_BATCHES; i++) {
poll.processMessages(pollId);
}
Expand Down
4 changes: 1 addition & 3 deletions cli/ts/commands/genProofs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Keypair, PrivKey, VerifyingKey } from "maci-domainobjs";
import { extractVk, genProof, verifyProof } from "maci-circuits";
import { genMaciStateFromContract, getDefaultSigner, parseArtifact } from "maci-contracts";
import { Contract } from "ethers";
import { MaciState, Poll } from "maci-core";
import { MaciState } from "maci-core";
import { hash3, hashLeftRight, genTreeCommitment } from "maci-crypto";
import { join } from "path";

Expand Down Expand Up @@ -184,8 +184,6 @@ export const genProofs = async (
} catch (error: any) {
logError(error.message);
}
// ensure we merge all messages
maciState.polls.forEach((poll: Poll) => poll.mergeAllMessages());
} else {
maciState = await genMaciStateFromContract(
signer.provider,
Expand Down
27 changes: 10 additions & 17 deletions contracts/tests/MACI.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { utils, Contract } from "ethers";
import { timeTravel } from "./utils";
import { parseArtifact, getDefaultSigner } from "../ts/deploy";
import { deployTestContracts } from "../ts/utils";
import { PCommand, VerifyingKey, Keypair, PubKey, Message } from "maci-domainobjs";
import { expect } from "chai";

import { PCommand, VerifyingKey, Keypair, PubKey, Message } from "maci-domainobjs";
import {
MaciState,
genProcessVkSig,
Expand All @@ -12,10 +10,12 @@ import {
packProcessMessageSmallVals,
packTallyVotesSmallVals,
} from "maci-core";
import { expect } from "chai";

import { G1Point, G2Point, NOTHING_UP_MY_SLEEVE } from "maci-crypto";

import { timeTravel } from "./utils";
import { parseArtifact, getDefaultSigner } from "../ts/deploy";
import { deployTestContracts } from "../ts/utils";

const STATE_TREE_DEPTH = 10;
const STATE_TREE_ARITY = 5;
const MESSAGE_TREE_DEPTH = 4;
Expand Down Expand Up @@ -404,17 +404,13 @@ describe("MACI", () => {
tx = await pollContract.mergeMessageAq({ gasLimit: 4000000 });
receipt = await tx.wait();
expect(receipt.status).to.eq(1);

const poll = maciState.polls[pollId];
poll.messageAq.mergeSubRoots(0);
poll.messageAq.merge(MESSAGE_TREE_DEPTH);
});

it("the message root must be correct", async () => {
const onChainMessageRoot = await messageAqContract.getMainRoot(MESSAGE_TREE_DEPTH);
expect(onChainMessageRoot.toString()).to.eq(
maciState.polls[pollId].messageAq.mainRoots[MESSAGE_TREE_DEPTH].toString(),
);
const offChainMessageRoot = maciState.polls[pollId].messageTree.root;

expect(onChainMessageRoot.toString()).to.eq(offChainMessageRoot.toString());
});
});

Expand Down Expand Up @@ -457,14 +453,11 @@ describe("MACI", () => {
});
receipt = await tx.wait();
expect(receipt.status).to.eq(1);

maciState.stateAq.mergeSubRoots(0);
maciState.stateAq.merge(STATE_TREE_DEPTH);
});

it("the state root must be correct", async () => {
const onChainStateRoot = await stateAqContract.getMainRoot(STATE_TREE_DEPTH);
expect(onChainStateRoot.toString()).to.eq(maciState.stateAq.mainRoots[STATE_TREE_DEPTH].toString());
expect(onChainStateRoot.toString()).to.eq(maciState.stateTree.root.toString());
});
});

Expand Down
12 changes: 1 addition & 11 deletions contracts/ts/genMaciState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,18 +449,8 @@ const genMaciStateFromContract = async (
maciState.polls[pollId].publishMessage(action.data.message, action.data.encPubKey);
} else if (action["type"] === "TopupMessage") {
maciState.polls[pollId].topupMessage(action.data.message);
} else if (action["type"] === "MergeMaciStateAqSubRoots") {
maciState.stateAq.mergeSubRoots(action.data.numSrQueueOps);
} else if (action["type"] === "MergeMaciStateAq") {
if (pollId == 0) {
maciState.stateAq.merge(stateTreeDepth);
}
} else if (action["type"] === "MergeMessageAqSubRoots") {
maciState.polls[pollId].messageAq.mergeSubRoots(action.data.numSrQueueOps);
} else if (action["type"] === "MergeMessageAq") {
maciState.polls[pollId].messageAq.merge(treeDepths.messageTreeDepth);
const poll = maciState.polls[pollId];
assert(poll.messageAq.mainRoots[treeDepths.messageTreeDepth] === action.data.messageRoot);
assert(maciState.polls[pollId].messageTree.root === action.data.messageRoot);
}
}

Expand Down
19 changes: 5 additions & 14 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,9 @@ A function that deep-copies an object.

### Key data structures

#### **`stateAq`**, **`stateTree`**
#### **`stateTree`**

The Merkle tree of state leaves. `stateAq` must be merged (subroots and/or main
root) whenever the MACI contract's `mergeStateAqSubRoots()` and
`mergeStateAq()` are invoked.

They should contain the same leaves, even if the `stateAq` is not yet merged.
`stateTree` exists for developer convenience.
`stateTree` is a quinary Merkle tree, chosen over a binary tree due to the gas and circuit constraints associated with the Poseidon hash function ([details here][ethresearch-link]). Each leaf in this tree represents a participant's public key, their voice credit balance and the block timestamp at which they signed up. The tree features a configurable depth and an arity of five, with insertions starting at index 1. The zeroth leaf is reserved as a security measure against denial-of-service attacks.

## **`Poll`**

Expand Down Expand Up @@ -91,14 +86,9 @@ Deep-copies and returns this object.

### Key data structures

#### **`messageAq`**, **`messageTree`**

The Merkle tree of message leaves. `messageAq` must be merged (subroots and/or
main root) whenever the MACI contract's `mergeMessageAqSubRoots()` and
`mergeMessageAq()` are invoked.
#### **`messageTree`**

They should contain the same leaves, even if the `messageAq` is not yet merged.
`messageTree` exists for developer convenience.
`messageTree` is also a quinary Merkle tree. Each message in this tree represents an encrypted command, such as a user casting a vote in a poll or changing their public key.

## Testing

Expand All @@ -108,6 +98,7 @@ For more details about testing please refer to the [tests documentation](https:/
[core-npm-link]: https://www.npmjs.com/package/maci-core
[core-actions-badge]: https://github.com/privacy-scaling-explorations/maci/actions/workflows/core-build.yml/badge.svg
[core-actions-link]: https://github.com/privacy-scaling-explorations/maci/actions?query=workflow%3Acore
[ethresearch-link]: https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-merkle-trees-using-the-poseidon-hash-function/7446

## Directory Structure

Expand Down
Loading

0 comments on commit 477368c

Please sign in to comment.