Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: voice credits per poll #1967

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/subgraph/src/maci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion apps/subgraph/templates/subgraph.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
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
Expand All @@ -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
Expand Up @@ -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",
Expand Down
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";
Expand Down
8 changes: 4 additions & 4 deletions packages/circuits/ts/__tests__/CeremonyParams.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -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;
Expand Down
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";
Expand All @@ -23,7 +22,6 @@ describe("Poll Joining circuit", function test() {
"siblings",
"indices",
"nullifier",
"credits",
"stateRoot",
"actualStateTreeDepth",
"pollId",
Expand Down Expand Up @@ -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(
Expand All @@ -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];
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Loading