Skip to content

Commit

Permalink
feat: voice credits per poll (#1967)
Browse files Browse the repository at this point in the history
ctrlc03 authored Jan 7, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent c9e7efc commit f59e9c3
Showing 80 changed files with 438 additions and 690 deletions.
2 changes: 1 addition & 1 deletion apps/subgraph/src/maci.ts
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ export function handleDeployPoll(event: DeployPollEvent): void {

export function handleSignUp(event: SignUpEvent): void {
const user = createOrLoadUser(event.params._userPubKeyX, event.params._userPubKeyY, event);
createOrLoadAccount(event.params._stateIndex, event, user.id, event.params._voiceCreditBalance);
createOrLoadAccount(event.params._stateIndex, event, user.id);

const maci = createOrLoadMACI(event);
maci.numSignUps = maci.numSignUps.plus(ONE_BIG_INT);
2 changes: 1 addition & 1 deletion apps/subgraph/templates/subgraph.template.yaml
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ dataSources:
eventHandlers:
- event: DeployPoll(uint256,indexed uint256,indexed uint256,uint8)
handler: handleDeployPoll
- event: SignUp(uint256,indexed uint256,indexed uint256,uint256,uint256,uint256)
- event: SignUp(uint256,uint256,indexed uint256,indexed uint256)
handler: handleSignUp
file: ./src/maci.ts
templates:
33 changes: 6 additions & 27 deletions packages/circuits/circom/anon/pollJoining.circom
Original file line number Diff line number Diff line change
@@ -1,42 +1,26 @@
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;
// Constants defining the tree structure
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
@@ -50,26 +34,21 @@ template PollJoining(stateTreeDepth) {

// 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
// Hash the public key
var pubKeyHash = PoseidonHasher(2)([derivedPubKey[0], derivedPubKey[1]]);

// Poll private to public key to verify the correct one is used to join the poll (public input)
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,
pubKeyHash,
actualStateTreeDepth,
indices,
siblings
);

stateLeafQip === stateRoot;

// Check credits
var isCreditsValid = SafeLessEqThan(N_BITS)([credits, stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_IDX]]);
isCreditsValid === 1;
}
2 changes: 1 addition & 1 deletion packages/circuits/circom/circuits.json
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
"file": "./anon/pollJoining",
"template": "PollJoining",
"params": [10],
"pubs": ["nullifier", "credits", "stateRoot", "pollPubKey", "pollId"]
"pubs": ["nullifier", "stateRoot", "pollPubKey", "pollId"]
},
"ProcessMessages_10-20-2_test": {
"file": "./core/qv/processMessages",
2 changes: 2 additions & 0 deletions packages/circuits/circom/trees/incrementalMerkleTree.circom
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pragma circom 2.0.0;

// zk-kit imports
include "./safe-comparators.circom";
// circomlib import
include "./mux1.circom";
include "./comparators.circom";
8 changes: 4 additions & 4 deletions packages/circuits/ts/__tests__/CeremonyParams.test.ts
Original file line number Diff line number Diff line change
@@ -78,7 +78,7 @@ describe("Ceremony param tests", () => {
before(() => {
// Sign up and publish
const userKeypair = new Keypair(new PrivKey(BigInt(1)));
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000)));
maciState.signUp(userKeypair.pubKey);

pollId = maciState.deployPoll(
BigInt(Math.floor(Date.now() / 1000) + duration),
@@ -90,7 +90,7 @@ describe("Ceremony param tests", () => {
poll = maciState.polls.get(pollId)!;

// update the state
poll.updatePoll(BigInt(maciState.stateLeaves.length));
poll.updatePoll(BigInt(maciState.pubKeys.length));

// Join the poll
const { privKey } = userKeypair;
@@ -221,7 +221,7 @@ describe("Ceremony param tests", () => {
const commands: PCommand[] = [];
// Sign up and publish
const userKeypair = new Keypair();
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000)));
maciState.signUp(userKeypair.pubKey);

pollId = maciState.deployPoll(
BigInt(Math.floor(Date.now() / 1000) + duration),
@@ -233,7 +233,7 @@ describe("Ceremony param tests", () => {
poll = maciState.polls.get(pollId)!;

// update the state
poll.updatePoll(BigInt(maciState.stateLeaves.length));
poll.updatePoll(BigInt(maciState.pubKeys.length));

// Join the poll
const { privKey } = userKeypair;
20 changes: 2 additions & 18 deletions packages/circuits/ts/__tests__/PollJoining.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { expect } from "chai";
import { type WitnessTester } from "circomkit";
import { MaciState, Poll } from "maci-core";
import { poseidon } from "maci-crypto";
@@ -23,7 +22,6 @@ describe("Poll Joining circuit", function test() {
"siblings",
"indices",
"nullifier",
"credits",
"stateRoot",
"actualStateTreeDepth",
"pollId",
@@ -53,7 +51,7 @@ describe("Poll Joining circuit", function test() {
users = new Array(NUM_USERS).fill(0).map(() => new Keypair());

users.forEach((userKeypair) => {
maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000)));
maciState.signUp(userKeypair.pubKey);
});

pollId = maciState.deployPoll(
@@ -64,7 +62,7 @@ describe("Poll Joining circuit", function test() {
);

poll = maciState.polls.get(pollId)!;
poll.updatePoll(BigInt(maciState.stateLeaves.length));
poll.updatePoll(BigInt(maciState.pubKeys.length));

// Join the poll
const { privKey } = users[0];
@@ -101,12 +99,10 @@ describe("Poll Joining circuit", function test() {
it("should produce a proof", async () => {
const privateKey = users[0].privKey;
const stateLeafIndex = BigInt(1);
const credits = BigInt(10);

const inputs = poll.joiningCircuitInputs({
maciPrivKey: privateKey,
stateLeafIndex,
credits,
pollPrivKey,
pollPubKey,
}) as unknown as IPollJoiningInputs;
@@ -117,12 +113,10 @@ describe("Poll Joining circuit", function test() {
it("should fail for fake witness", async () => {
const privateKey = users[0].privKey;
const stateLeafIndex = BigInt(1);
const credits = BigInt(10);

const inputs = poll.joiningCircuitInputs({
maciPrivKey: privateKey,
stateLeafIndex,
credits,
pollPrivKey,
pollPubKey,
}) as unknown as IPollJoiningInputs;
@@ -131,15 +125,5 @@ describe("Poll Joining circuit", function test() {
const fakeWitness = Array(witness.length).fill(1n) as bigint[];
await circuit.expectConstraintFail(fakeWitness);
});

it("should fail for improper credits", () => {
const privateKey = users[0].privKey;
const stateLeafIndex = BigInt(1);
const credits = BigInt(105);

expect(() =>
poll.joiningCircuitInputs({ maciPrivKey: privateKey, stateLeafIndex, credits, pollPrivKey, pollPubKey }),
).to.throw("Credits must be lower than signed up credits");
});
});
});
Loading

0 comments on commit f59e9c3

Please sign in to comment.