Skip to content

Commit

Permalink
feat: anonymous poll joining milestone 2 and 3 (#1750)
Browse files Browse the repository at this point in the history
* feat(poll): add chain hash features

BREAKING CHANGE: message processing is changed

* fix(ipoll): add missing parameter

* fix(poll-tests): add missing parameter maxMessagebatchSize

* feat(poll.ts): add chain hash updating

* test(poll tests): add test for checking chain hash computation

* feat(poll.ts): add batch hashes array computation

* feat(poll.sol): pad zeroes to the maximum size of batch

* feat(messageprocessor): update process messages to use chain hash

* refactor(vkregistry): refactor function call

* feat(processmessages.circom): add chainHash feature in circuits and test for that

* test(processmessages): rearrange test for key-change

* refactor(mergemessages): refactor functions calls which include mergemessages

* refactor(mergemessages): add some more changes about functions call which  include mergemessages

* test(all tests): fixing tests after refactoring code

* refactor(accqueue): remove all calls for accqueue

* fix(currentmessagebatchindex): fix message batch indexing

* refactor(circuit tests): refactor code for circuit testing

* test(ceremonyparams.test): correct constants for CeremonyParams test

* perf(processmessages.circom + contracts): optimize last batch padding, remove unused inputs

* docs(padlastbatch method): update doc comment

* docs(poll.ts): remove stale comments

* docs(test comments): fix typos

* ci(treedepths mock): modify interface for mocked function

* fix(ceremony params test): fix circuit inputs

* test(messagevalidator): fix function calls for messagevalidator circuit in tests

* chore(comments): fix unusefull comments

* refactor(poll.sol): replace external contracts with maci only

* perf(messageprocessor.sol): hardcode initialization for batchHashes array

* docs(comments): fix some more comments

* test(test for pr checks): correct some of tests for PR checks

* ci: 🎡 renamed old ProcessMessages_10-2-1-2_test

* ci: 🎡 correct rapidsnark/build/prover path

* style(reviews): solve some reviews for merging

* refactor(messageaqq): remove more message merging and message aqq

* style(messageaqq): remove more message merging and message aqq

* refactor(messageaqq): remove message aqq from subgraph

* test(coordinator): hide NOT_MERGED_MESSAGE_TREE error

* test(coordinator): fix test about message merging

* test(proveonchain): change chainHash calculation

* test(proveonchain): fix chainHashes declaration

* test(proveonchain): fix chainHash calculation

* test(proveonchain): fix chainHashes calculations

* test(proveonchain): fix chainHashes calculation

* test(proveonchain): fix loop limit

* style(review comments): resolve some of review comments

* style(review comments): resolve some of review comments

* test(lint:ts): fix e2e test because of lint:ts check

* docs(wrong changes): fix wrong changes about documentation that is not in our scope

* refactor(batchsizes): change batchSizes struct with messageBatchSize variable

* refactor(contracts): rollback to provide external contract references

* docs(messageprocessor.sol): fix typo

* refactor(messagebatchsize): chenge messageBatchSize location from Params.sol to Poll.sol

* refactor(maxmessages): remove maxMessages from maxValues

* refactor(sltimestemp): remove slTimestamp from circuits

* refactor(review comments): resolve more review comments

* fix(subgraph): fix bug about maxVoteOptions dunction call

* fix(sltimestamp): fix test for removing slTimestap signal

* refactor(promise.all): refactor promise.all for only one async call

* fix(subgraph): try to fix subgraph build

* revert(.nx folder): remove .nx folder from cli folder

* fix(merge): tmp-anon-poll-joining merge

* fix(merge): tmp-anon-poll-joining merge

* test(ceremonyparams): add poll joining in the test

* test(processmessages): add poll joining for the test

without key-change test

* test(polljoining): add poll joining in the test

* test(tallyvotes): add poll joining in the test

* test(core): add joinPoll function in tests

* style(typo): inclusion proof

* style(todo): remove finished todo

* style(merge): after merge style

* style(return): inline return

* style(eslint): remove unnecessary eslint-disable

* refactor(joiningcircuitargs): add interface IJoiningCircuitArgs

* refactor(joinpoll): async read state file

* style(genmacisignup): add function description

* refactor(gensignuptree): add IGenSignUpTreeArgs interface

* style(polljoining): remove extra inlcudes and comments

* feat(pollvkkeys): init

* feat(vkregistry): separate set functions (process/tally/poll)

* test(pollvkkey): adjust test to setPollVkKey

* refactor(vkregistry): use setVerifyingKeys in setVerifyingKeysBatch

* refactor(poll): add verifier and vkRegystry in constructor

* refactor(poll): put verifier and vkRegistry into extContracts

* test(core e2e): fix sanity checks test for incorrect signature

* refactor(test): removing only from tests

* refactor(macistatetree): use LeanIMT instead of QuinTree

* refactor(crypto): export hashLeanIMT from index

* feat(joinpoll): use genSignUpTree instead of genMaciStateFromContract

* feat(joinpoll cli): add optional parameters

* test(coordinator): add pollJoiningZkeyPath in app.test

* refactor(joinpoll): prettier

* test(coordinator): add joinPoll

* fix(poll): joiningCircuitInputs with correct siblings, indices and actualStateTreeDepth

* test(integration): add joinPoll

* build(coordinator): add COORDINATOR_POLL_ZKEY_NAME

* refactor(mergestate): remove Maci from MergeState

* test(e2e): test:e2e add joinPoll

* test(e2e): test:keyChange add joinPoll

* docs(complete documentation): complete documentation of the new workflow

* docs(documentation): add v3 docs, revert v2 docs

* style(docs): prettier

* refactor(joinpoll): add generateAndVerifyProof and getStateIndexAndCreditBalance

* docs(blogpost): blogpost cuvering the latest updates

* docs(blogpost): kudos to our team members!

* style(prettier): blog

* fix(joinpoll): index value of the user in the state tree leaves

* docs(blog): remove poll-joining

---------

Co-authored-by: radojevicMihailo <[email protected]>
Co-authored-by: Aleksandar Veljković <[email protected]>
  • Loading branch information
3 people authored and 0xmad committed Dec 2, 2024
1 parent f5df5de commit f842751
Show file tree
Hide file tree
Showing 70 changed files with 3,154 additions and 526 deletions.
7 changes: 2 additions & 5 deletions apps/subgraph/src/poll.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
/* eslint-disable no-underscore-dangle */

import { Poll, Vote, MACI } from "../generated/schema";
import {
MergeMaciState as MergeMaciStateEvent,
PublishMessage as PublishMessageEvent,
} from "../generated/templates/Poll/Poll";
import { MergeState as MergeStateEvent, PublishMessage as PublishMessageEvent } from "../generated/templates/Poll/Poll";

import { ONE_BIG_INT } from "./utils/constants";

export function handleMergeMaciState(event: MergeMaciStateEvent): void {
export function handleMergeState(event: MergeStateEvent): void {
const poll = Poll.load(event.address);

if (poll) {
Expand Down
6 changes: 3 additions & 3 deletions apps/subgraph/templates/subgraph.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dataSources:
eventHandlers:
- event: DeployPoll(uint256,indexed uint256,indexed uint256,uint8)
handler: handleDeployPoll
- event: SignUp(uint256,indexed uint256,indexed uint256,uint256,uint256)
- event: SignUp(uint256,indexed uint256,indexed uint256,uint256,uint256,uint256)
handler: handleSignUp
file: ./src/maci.ts
templates:
Expand All @@ -54,8 +54,8 @@ templates:
- name: Poll
file: ./node_modules/maci-contracts/build/artifacts/contracts/Poll.sol/Poll.json
eventHandlers:
- event: MergeMaciState(indexed uint256,indexed uint256)
handler: handleMergeMaciState
- event: MergeState(indexed uint256,indexed uint256)
handler: handleMergeState
- event: PublishMessage((uint256[10]),(uint256,uint256))
handler: handlePublishMessage
file: ./src/poll.ts
10 changes: 5 additions & 5 deletions apps/subgraph/tests/poll/poll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { test, describe, afterEach, clearStore, assert, beforeEach } from "match

import { MACI, Poll } from "../../generated/schema";
import { handleDeployPoll } from "../../src/maci";
import { handleMergeMaciState, handlePublishMessage } from "../../src/poll";
import { handleMergeState, handlePublishMessage } from "../../src/poll";
import { DEFAULT_POLL_ADDRESS, mockPollContract } from "../common";
import { createDeployPollEvent } from "../maci/utils";

import { createMergeMaciStateEvent, createPublishMessageEvent } from "./utils";
import { createMergeStateEvent, createPublishMessageEvent } from "./utils";

export { handleMergeMaciState, handlePublishMessage };
export { handleMergeState, handlePublishMessage };

describe("Poll", () => {
beforeEach(() => {
Expand All @@ -27,9 +27,9 @@ describe("Poll", () => {
});

test("should handle merge maci state properly", () => {
const event = createMergeMaciStateEvent(DEFAULT_POLL_ADDRESS, BigInt.fromI32(1), BigInt.fromI32(3));
const event = createMergeStateEvent(DEFAULT_POLL_ADDRESS, BigInt.fromI32(1), BigInt.fromI32(3));

handleMergeMaciState(event);
handleMergeState(event);

const poll = Poll.load(event.address)!;
const maci = MACI.load(poll.maci)!;
Expand Down
6 changes: 3 additions & 3 deletions apps/subgraph/tests/poll/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Address, BigInt as GraphBN, ethereum } from "@graphprotocol/graph-ts";
// eslint-disable-next-line import/no-extraneous-dependencies
import { newMockEvent } from "matchstick-as";

import { MergeMaciState, PublishMessage } from "../../generated/templates/Poll/Poll";
import { MergeState, PublishMessage } from "../../generated/templates/Poll/Poll";

export function createMergeMaciStateEvent(address: Address, stateRoot: GraphBN, numSignups: GraphBN): MergeMaciState {
const event = changetype<MergeMaciState>(newMockEvent());
export function createMergeStateEvent(address: Address, stateRoot: GraphBN, numSignups: GraphBN): MergeState {
const event = changetype<MergeState>(newMockEvent());

event.parameters.push(new ethereum.EventParam("_stateRoot", ethereum.Value.fromUnsignedBigInt(stateRoot)));
event.parameters.push(new ethereum.EventParam("_numSignups", ethereum.Value.fromUnsignedBigInt(numSignups)));
Expand Down
6 changes: 6 additions & 0 deletions packages/circuits/circom/circuits.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"PollJoining_10_test": {
"file": "./core/qv/pollJoining",
"template": "PollJoining",
"params": [10],
"pubs": ["inputHash"]
},
"ProcessMessages_10-20-2_test": {
"file": "./core/qv/processMessages",
"template": "ProcessMessages",
Expand Down
81 changes: 81 additions & 0 deletions packages/circuits/circom/core/qv/pollJoining.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
pragma circom 2.0.0;


// circomlib import
include "./mux1.circom";
// zk-kit imports
include "./safe-comparators.circom";
// local imports
include "../../utils/hashers.circom";
include "../../utils/privToPubKey.circom";
include "../../trees/incrementalMerkleTree.circom";

template PollJoining(stateTreeDepth) {

// Constants defining the structure and size of state.
var STATE_LEAF_LENGTH = 4;
var STATE_TREE_ARITY = 2;

// Public key IDs.
var STATE_LEAF_PUB_X_IDX = 0;
var STATE_LEAF_PUB_Y_IDX = 1;
// Voice Credit balance id
var STATE_LEAF_VOICE_CREDIT_BALANCE_IDX = 2;
var N_BITS = 252;

// User's private key
signal input privKey;
// Poll's private key
signal input pollPrivKey;
// Poll's public key
signal input pollPubKey[2];
// The state leaf and related path elements.
signal input stateLeaf[STATE_LEAF_LENGTH];
// Siblings
signal input siblings[stateTreeDepth][STATE_TREE_ARITY - 1];
// Indices
signal input indices[stateTreeDepth];
// User's hashed private key
signal input nullifier;
// User's credits for poll joining (might be <= oldCredits)
signal input credits;
// MACI State tree root which proves the user is signed up
signal input stateRoot;
// The actual tree depth (might be <= stateTreeDepth) Used in BinaryMerkleRoot
signal input actualStateTreeDepth;
// Public input hash (nullifier, credits, stateRoot)
signal input inputHash;

// Check public input hash
var computedInputHash = Sha256Hasher(5)([nullifier, credits, stateRoot, pollPubKey[0], pollPubKey[1]]);
inputHash === computedInputHash;

// User private to public key
var derivedPubKey[2] = PrivToPubKey()(privKey);
derivedPubKey[0] === stateLeaf[STATE_LEAF_PUB_X_IDX];
derivedPubKey[1] === stateLeaf[STATE_LEAF_PUB_Y_IDX];

// Poll private to public key
var derivedPollPubKey[2] = PrivToPubKey()(pollPrivKey);
derivedPollPubKey[0] === pollPubKey[0];
derivedPollPubKey[1] === pollPubKey[1];

// Inclusion proof
var stateLeafHash = PoseidonHasher(4)(stateLeaf);
var stateLeafQip = BinaryMerkleRoot(stateTreeDepth)(
stateLeafHash,
actualStateTreeDepth,
indices,
siblings
);

stateLeafQip === stateRoot;

// Check credits
var isCreditsValid = SafeLessEqThan(N_BITS)([credits, stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_IDX]]);
isCreditsValid === 1;

// Check nullifier
var hashedPrivKey = PoseidonHasher(1)([privKey]);
hashedPrivKey === nullifier;
}
3 changes: 2 additions & 1 deletion packages/circuits/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"test:processMessages": "pnpm run mocha-test ts/__tests__/ProcessMessages.test.ts",
"test:tallyVotes": "pnpm run mocha-test ts/__tests__/TallyVotes.test.ts",
"test:ceremonyParams": "pnpm run mocha-test ts/__tests__/CeremonyParams.test.ts",
"test:incrementalQuinaryTree": "pnpm run mocha-test ts/__tests__/IncrementalQuinaryTree.test.ts"
"test:incrementalQuinaryTree": "pnpm run mocha-test ts/__tests__/IncrementalQuinaryTree.test.ts",
"test:pollJoining": "pnpm run mocha-test ts/__tests__/PollJoining.test.ts"
},
"dependencies": {
"@zk-kit/circuits": "^0.4.0",
Expand Down
44 changes: 29 additions & 15 deletions packages/circuits/ts/__tests__/CeremonyParams.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from "chai";
import { type WitnessTester } from "circomkit";
import { MaciState, Poll, STATE_TREE_ARITY, VOTE_OPTION_TREE_ARITY, MESSAGE_BATCH_SIZE } from "maci-core";
import { hash5, IncrementalQuinTree } from "maci-crypto";
import { hash5, IncrementalQuinTree, poseidon } from "maci-crypto";
import { PrivKey, Keypair, PCommand, Message, Ballot } from "maci-domainobjs";

import { IProcessMessagesInputs, ITallyVotesInputs } from "../types";
Expand Down Expand Up @@ -85,9 +85,7 @@ describe("Ceremony param tests", () => {
before(() => {
// Sign up and publish
const userKeypair = new Keypair(new PrivKey(BigInt(1)));
stateIndex = BigInt(
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))),
);
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000)));

pollId = maciState.deployPoll(
BigInt(Math.floor(Date.now() / 1000) + duration),
Expand All @@ -102,17 +100,26 @@ describe("Ceremony param tests", () => {
// update the state
poll.updatePoll(BigInt(maciState.stateLeaves.length));

// Join the poll
const { privKey } = userKeypair;
const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair();

const nullifier = poseidon([BigInt(privKey.rawPrivKey.toString())]);
const timestamp = BigInt(Math.floor(Date.now() / 1000));

stateIndex = BigInt(poll.joinPoll(nullifier, pollPubKey, voiceCreditBalance, timestamp));

// First command (valid)
const command = new PCommand(
stateIndex, // BigInt(1),
userKeypair.pubKey,
pollPubKey,
voteOptionIndex, // voteOptionIndex,
voteWeight, // vote weight
BigInt(2), // nonce
BigInt(pollId),
);

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

const ecdhKeypair = new Keypair();
const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, coordinatorKeypair.pubKey);
Expand All @@ -131,7 +138,7 @@ describe("Ceremony param tests", () => {
BigInt(1), // nonce
BigInt(pollId),
);
const signature2 = command2.sign(userKeypair.privKey);
const signature2 = command2.sign(pollPrivKey);

const ecdhKeypair2 = new Keypair();
const sharedKey2 = Keypair.genEcdhSharedKey(ecdhKeypair2.privKey, coordinatorKeypair.pubKey);
Expand All @@ -148,11 +155,11 @@ describe("Ceremony param tests", () => {
const ballotTree = new IncrementalQuinTree(params.stateTreeDepth, emptyBallot.hash(), STATE_TREE_ARITY, hash5);
ballotTree.insert(emptyBallot.hash());

poll.stateLeaves.forEach(() => {
poll.pollStateLeaves.forEach(() => {
ballotTree.insert(emptyBallotHash);
});

const currentStateRoot = poll.stateTree?.root;
const currentStateRoot = poll.pollStateLeaves?.root;
const currentBallotRoot = ballotTree.root;

const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs;
Expand All @@ -163,7 +170,7 @@ describe("Ceremony param tests", () => {

// The new roots, which should differ, since at least one of the
// messages modified a Ballot or State Leaf
const newStateRoot = poll.stateTree?.root;
const newStateRoot = poll.pollStateLeaves?.root;
const newBallotRoot = poll.ballotTree?.root;

expect(newStateRoot?.toString()).not.to.be.eq(currentStateRoot?.toString());
Expand Down Expand Up @@ -222,9 +229,7 @@ describe("Ceremony param tests", () => {
const commands: PCommand[] = [];
// Sign up and publish
const userKeypair = new Keypair();
stateIndex = BigInt(
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))),
);
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000)));

pollId = maciState.deployPoll(
BigInt(Math.floor(Date.now() / 1000) + duration),
Expand All @@ -239,17 +244,26 @@ describe("Ceremony param tests", () => {
// update the state
poll.updatePoll(BigInt(maciState.stateLeaves.length));

// Join the poll
const { privKey } = userKeypair;
const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair();

const nullifier = poseidon([BigInt(privKey.rawPrivKey.toString())]);
const timestamp = BigInt(Math.floor(Date.now() / 1000));

stateIndex = BigInt(poll.joinPoll(nullifier, pollPubKey, voiceCreditBalance, timestamp));

// First command (valid)
const command = new PCommand(
stateIndex,
userKeypair.pubKey,
pollPubKey,
voteOptionIndex, // voteOptionIndex,
voteWeight, // vote weight
BigInt(1), // nonce
BigInt(pollId),
);

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

const ecdhKeypair = new Keypair();
const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, coordinatorKeypair.pubKey);
Expand Down
Loading

0 comments on commit f842751

Please sign in to comment.