From d7a2143c013dece84ca16dccce43281f23267ccd Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:32:08 +0000 Subject: [PATCH] feat: voice credits per poll --- apps/subgraph/src/maci.ts | 2 +- .../subgraph/templates/subgraph.template.yaml | 2 +- .../circuits/circom/anon/pollJoining.circom | 36 ++------ packages/circuits/circom/circuits.json | 2 +- .../circom/trees/incrementalMerkleTree.circom | 2 + .../ts/__tests__/CeremonyParams.test.ts | 8 +- .../circuits/ts/__tests__/PollJoining.test.ts | 19 +--- .../ts/__tests__/ProcessMessages.test.ts | 58 +++++------- .../circuits/ts/__tests__/TallyVotes.test.ts | 12 +-- packages/cli/.gitignore | 1 + packages/cli/deploy-config.json | 1 + packages/cli/deployed-contracts.json | 1 + packages/cli/tests/constants.ts | 4 +- packages/cli/tests/e2e/e2e.test.ts | 15 ---- packages/cli/tests/e2e/keyChange.test.ts | 3 - packages/cli/tests/unit/joinPoll.test.ts | 5 -- packages/cli/ts/commands/deploy.ts | 1 - packages/cli/ts/commands/deployPoll.ts | 17 +++- packages/cli/ts/commands/genProofs.ts | 2 +- packages/cli/ts/commands/joinPoll.ts | 89 ++++++------------- packages/cli/ts/commands/signup.ts | 21 ++--- packages/cli/ts/index.ts | 7 +- packages/cli/ts/utils/interfaces.ts | 22 ++--- packages/contracts/contracts/MACI.sol | 62 +++++-------- packages/contracts/contracts/Poll.sol | 36 ++++---- .../interfaces/IInitialVoiceCreditProxy.sol | 11 +++ .../contracts/contracts/interfaces/IPoll.sol | 4 +- .../contracts/contracts/trees/LeanIMT.sol | 4 +- .../contracts/contracts/utilities/Params.sol | 2 + .../01-constantInitialVoiceCreditProxy.ts | 0 .../{01-gatekeepers.ts => 02-gatekeepers.ts} | 0 .../deploy/poll/{02-poll.ts => 03-poll.ts} | 6 ++ .../contracts/tasks/helpers/ProofGenerator.ts | 2 +- packages/contracts/tasks/helpers/constants.ts | 2 +- packages/contracts/tests/MACI.test.ts | 80 +++-------------- .../contracts/tests/MessageProcessor.test.ts | 7 +- packages/contracts/tests/Poll.test.ts | 42 ++++----- packages/contracts/tests/PollFactory.test.ts | 1 + packages/contracts/tests/Tally.test.ts | 24 ++--- packages/contracts/tests/TallyNonQv.test.ts | 7 +- packages/contracts/tests/constants.ts | 1 + .../gatekeepers/AnonAadhaarGatekeeper.test.ts | 40 +++------ .../tests/gatekeepers/EASGatekeeper.test.ts | 19 ++-- .../GitcoinPassportGatekeeper.test.ts | 6 +- .../tests/gatekeepers/HatsGatekeeper.test.ts | 48 ++-------- .../gatekeepers/MerkleProofGatekeeper.test.ts | 2 - .../gatekeepers/SemaphoreGatekeeper.test.ts | 29 ++---- .../gatekeepers/SignUpGatekeeper.test.ts | 7 +- .../gatekeepers/ZupassGatekeeper.test.ts | 21 ++--- packages/contracts/tests/utils.ts | 4 +- packages/contracts/ts/deploy.ts | 2 - packages/contracts/ts/genMaciState.ts | 10 +-- packages/contracts/ts/genSignUpTree.ts | 17 ++-- packages/contracts/ts/types.ts | 9 +- packages/core/ts/MaciState.ts | 37 ++++---- packages/core/ts/Poll.ts | 34 +++---- packages/core/ts/__benchmarks__/index.ts | 4 +- packages/core/ts/__tests__/MaciState.test.ts | 11 +-- packages/core/ts/__tests__/Poll.test.ts | 26 +++--- packages/core/ts/__tests__/e2e.test.ts | 53 +++++------ packages/core/ts/__tests__/utils/utils.ts | 10 +-- packages/core/ts/utils/types.ts | 7 +- packages/crypto/ts/constants.ts | 2 + packages/crypto/ts/index.ts | 2 +- packages/domainobjs/ts/constants.ts | 2 + packages/domainobjs/ts/index.ts | 2 +- packages/domainobjs/ts/publicKey.ts | 17 ++++ .../ts/__tests__/integration.test.ts | 5 +- .../ts/__tests__/maci-keys.test.ts | 3 +- .../ts/__tests__/utils/interfaces.ts | 2 + .../ts/__tests__/utils/utils.ts | 5 +- 71 files changed, 397 insertions(+), 660 deletions(-) create mode 100644 packages/cli/deploy-config.json create mode 100644 packages/cli/deployed-contracts.json create mode 100644 packages/contracts/contracts/interfaces/IInitialVoiceCreditProxy.sol rename packages/contracts/tasks/deploy/{maci => poll}/01-constantInitialVoiceCreditProxy.ts (100%) rename packages/contracts/tasks/deploy/poll/{01-gatekeepers.ts => 02-gatekeepers.ts} (100%) rename packages/contracts/tasks/deploy/poll/{02-poll.ts => 03-poll.ts} (95%) diff --git a/apps/subgraph/src/maci.ts b/apps/subgraph/src/maci.ts index 45d1aacf6b..5e3d572bf2 100644 --- a/apps/subgraph/src/maci.ts +++ b/apps/subgraph/src/maci.ts @@ -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); diff --git a/apps/subgraph/templates/subgraph.template.yaml b/apps/subgraph/templates/subgraph.template.yaml index a5a5b0e768..9742d913c1 100644 --- a/apps/subgraph/templates/subgraph.template.yaml +++ b/apps/subgraph/templates/subgraph.template.yaml @@ -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: diff --git a/packages/circuits/circom/anon/pollJoining.circom b/packages/circuits/circom/anon/pollJoining.circom index b66513bac0..581cf0bfd1 100644 --- a/packages/circuits/circom/anon/pollJoining.circom +++ b/packages/circuits/circom/anon/pollJoining.circom @@ -1,64 +1,44 @@ 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 signal input actualStateTreeDepth; - var computedNullifier = PoseidonHasher(1)([privKey]); - nullifier === computedNullifier; - // 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 @@ -66,10 +46,6 @@ template PollJoining(stateTreeDepth) { 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; diff --git a/packages/circuits/circom/circuits.json b/packages/circuits/circom/circuits.json index bcf56fbade..9e3f58a510 100644 --- a/packages/circuits/circom/circuits.json +++ b/packages/circuits/circom/circuits.json @@ -3,7 +3,7 @@ "file": "./anon/pollJoining", "template": "PollJoining", "params": [10], - "pubs": ["nullifier", "credits", "stateRoot", "pollPubKey"] + "pubs": ["nullifier", "stateRoot", "pollPubKey"] }, "ProcessMessages_10-20-2_test": { "file": "./core/qv/processMessages", diff --git a/packages/circuits/circom/trees/incrementalMerkleTree.circom b/packages/circuits/circom/trees/incrementalMerkleTree.circom index 36fcd05012..e67e052783 100644 --- a/packages/circuits/circom/trees/incrementalMerkleTree.circom +++ b/packages/circuits/circom/trees/incrementalMerkleTree.circom @@ -1,5 +1,7 @@ pragma circom 2.0.0; +// zk-kit imports +include "./safe-comparators.circom"; // circomlib import include "./mux1.circom"; include "./comparators.circom"; diff --git a/packages/circuits/ts/__tests__/CeremonyParams.test.ts b/packages/circuits/ts/__tests__/CeremonyParams.test.ts index d8a705056b..1bcbe8b9fd 100644 --- a/packages/circuits/ts/__tests__/CeremonyParams.test.ts +++ b/packages/circuits/ts/__tests__/CeremonyParams.test.ts @@ -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; diff --git a/packages/circuits/ts/__tests__/PollJoining.test.ts b/packages/circuits/ts/__tests__/PollJoining.test.ts index 5bfedadd8e..1b3add24a4 100644 --- a/packages/circuits/ts/__tests__/PollJoining.test.ts +++ b/packages/circuits/ts/__tests__/PollJoining.test.ts @@ -23,7 +23,6 @@ describe("Poll Joining circuit", function test() { "siblings", "indices", "nullifier", - "credits", "stateRoot", "actualStateTreeDepth", ]; @@ -52,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( @@ -63,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]; @@ -100,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; @@ -116,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; @@ -130,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"); - }); }); }); diff --git a/packages/circuits/ts/__tests__/ProcessMessages.test.ts b/packages/circuits/ts/__tests__/ProcessMessages.test.ts index 21e810435f..d252a86e31 100644 --- a/packages/circuits/ts/__tests__/ProcessMessages.test.ts +++ b/packages/circuits/ts/__tests__/ProcessMessages.test.ts @@ -71,7 +71,7 @@ describe("ProcessMessage circuit", function test() { const pollKeys: Keypair[] = []; users.forEach((userKeypair) => { - maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(userKeypair.pubKey); pollKeys.push(new Keypair()); }); @@ -83,7 +83,7 @@ describe("ProcessMessage circuit", function test() { ); poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); // Join the poll for (let i = 0; i < users.length; i += 1) { @@ -159,7 +159,7 @@ describe("ProcessMessage circuit", function test() { 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), @@ -169,7 +169,7 @@ describe("ProcessMessage 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 } = userKeypair; @@ -227,7 +227,7 @@ describe("ProcessMessage circuit", function test() { ballotTree.insert(emptyBallot.hash()); - poll.stateLeaves.forEach(() => { + poll.pubKeys.forEach(() => { ballotTree.insert(emptyBallotHash); }); @@ -262,16 +262,8 @@ describe("ProcessMessage circuit", function test() { const userKeypair = new Keypair(new PrivKey(BigInt(123))); const userKeypair2 = new Keypair(new PrivKey(BigInt(456))); - maciState.signUp( - userKeypair.pubKey, - voiceCreditBalance, - BigInt(1), // BigInt(Math.floor(Date.now() / 1000)), - ); - maciState.signUp( - userKeypair2.pubKey, - voiceCreditBalance, - BigInt(1), // BigInt(Math.floor(Date.now() / 1000)), - ); + maciState.signUp(userKeypair.pubKey); + maciState.signUp(userKeypair2.pubKey); pollId = maciState.deployPoll( BigInt(2 + duration), // BigInt(Math.floor(Date.now() / 1000) + duration), @@ -282,7 +274,7 @@ describe("ProcessMessage 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 } = userKeypair; @@ -321,7 +313,7 @@ describe("ProcessMessage circuit", function test() { ballotTree.insert(emptyBallot.hash()); - poll.stateLeaves.forEach(() => { + poll.pubKeys.forEach(() => { ballotTree.insert(emptyBallotHash); }); @@ -355,11 +347,7 @@ describe("ProcessMessage circuit", function test() { // Sign up and publish const userKeypair = new Keypair(new PrivKey(BigInt(123))); - maciState.signUp( - userKeypair.pubKey, - voiceCreditBalance, - BigInt(1), // BigInt(Math.floor(Date.now() / 1000)), - ); + maciState.signUp(userKeypair.pubKey); pollId = maciState.deployPoll( BigInt(2 + duration), // BigInt(Math.floor(Date.now() / 1000) + duration), @@ -370,7 +358,7 @@ describe("ProcessMessage circuit", function test() { poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); const { privKey } = userKeypair; const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair(); @@ -448,7 +436,7 @@ describe("ProcessMessage circuit", function test() { ballotTree.insert(emptyBallot.hash()); - poll.stateLeaves.forEach(() => { + poll.pubKeys.forEach(() => { ballotTree.insert(emptyBallotHash); }); @@ -480,7 +468,7 @@ describe("ProcessMessage circuit", function test() { before(() => { const userKeypair = new Keypair(new PrivKey(BigInt(1))); - maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(userKeypair.pubKey); // Sign up and publish pollId = maciState.deployPoll( @@ -492,7 +480,7 @@ describe("ProcessMessage 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 } = userKeypair; @@ -547,7 +535,7 @@ describe("ProcessMessage circuit", function test() { 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), treeDepths, @@ -556,7 +544,7 @@ describe("ProcessMessage 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 } = userKeypair; @@ -634,7 +622,7 @@ describe("ProcessMessage circuit", function test() { ballotTree.insert(emptyBallot.hash()); - poll.stateLeaves.forEach(() => { + poll.pubKeys.forEach(() => { ballotTree.insert(emptyBallotHash); }); @@ -669,7 +657,7 @@ describe("ProcessMessage circuit", function test() { 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), @@ -679,7 +667,7 @@ describe("ProcessMessage 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 } = userKeypair; @@ -762,7 +750,7 @@ describe("ProcessMessage circuit", function test() { ballotTree.insert(emptyBallot.hash()); - poll.stateLeaves.forEach(() => { + poll.pubKeys.forEach(() => { ballotTree.insert(emptyBallotHash); }); @@ -800,7 +788,7 @@ describe("ProcessMessage circuit", function test() { 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), treeDepths, @@ -809,7 +797,7 @@ describe("ProcessMessage 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 } = userKeypair; @@ -911,7 +899,7 @@ describe("ProcessMessage circuit", function test() { ballotTree.insert(emptyBallot.hash()); - poll.stateLeaves.forEach(() => { + poll.pubKeys.forEach(() => { ballotTree.insert(emptyBallotHash); }); diff --git a/packages/circuits/ts/__tests__/TallyVotes.test.ts b/packages/circuits/ts/__tests__/TallyVotes.test.ts index 24fc8eaa05..d9ae595eea 100644 --- a/packages/circuits/ts/__tests__/TallyVotes.test.ts +++ b/packages/circuits/ts/__tests__/TallyVotes.test.ts @@ -73,7 +73,7 @@ describe("TallyVotes circuit", function test() { 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), @@ -83,7 +83,7 @@ describe("TallyVotes 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 } = userKeypair; @@ -153,7 +153,7 @@ describe("TallyVotes circuit", function test() { 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), @@ -163,7 +163,7 @@ describe("TallyVotes 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 } = userKeypair; @@ -234,7 +234,7 @@ describe("TallyVotes circuit", function test() { const k = new Keypair(); userKeypairs.push(k); pollKeypairs.push(new Keypair()); - maciState.signUp(k.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000) + duration)); + maciState.signUp(k.pubKey); } // Deploy poll @@ -246,7 +246,7 @@ describe("TallyVotes circuit", function test() { ); const poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); // Join the poll for (let i = 0; i < x; i += 1) { diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore index d195e77ec3..72eb5b3175 100644 --- a/packages/cli/.gitignore +++ b/packages/cli/.gitignore @@ -4,3 +4,4 @@ contractAddress.old contractAddress.txt localState.json zkeys +state.json diff --git a/packages/cli/deploy-config.json b/packages/cli/deploy-config.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/packages/cli/deploy-config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/cli/deployed-contracts.json b/packages/cli/deployed-contracts.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/packages/cli/deployed-contracts.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/cli/tests/constants.ts b/packages/cli/tests/constants.ts index 14397f0067..053f77a49f 100644 --- a/packages/cli/tests/constants.ts +++ b/packages/cli/tests/constants.ts @@ -46,13 +46,13 @@ export const testProcessMessagesWasmPath = export const testTallyVotesWasmPath = "./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm"; export const testRapidsnarkPath = `${homedir()}/rapidsnark/build/prover`; -export const ceremonyPollJoiningZkeyPath = "./zkeys/PollJoining_10/pollJoining_10.zkey"; +export const ceremonyPollJoiningZkeyPath = "./zkeys/PollJoining_10_test/PollJoining_10_test.0.zkey"; export const ceremonyProcessMessagesZkeyPath = "./zkeys/ProcessMessages_6-9-2-3/processMessages_6-9-2-3.zkey"; export const ceremonyProcessMessagesNonQvZkeyPath = "./zkeys/ProcessMessagesNonQv_6-9-2-3/processMessagesNonQv_6-9-2-3.zkey"; export const ceremonyTallyVotesZkeyPath = "./zkeys/TallyVotes_6-2-3/tallyVotes_6-2-3.zkey"; export const ceremonyTallyVotesNonQvZkeyPath = "./zkeys/TallyVotesNonQv_6-2-3/tallyVotesNonQv_6-2-3.zkey"; -export const ceremonyPollJoiningWitnessPath = "./zkeys/PollJoining/PollJoining_10_cpp/PollJoining_10"; +export const ceremonyPollJoiningWitnessPath = "./zkeys/PollJoining_10_test/PollJoining_10_test_cpp/PollJoining_10_test"; export const ceremonyProcessMessagesWitnessPath = "./zkeys/ProcessMessages_14-9-2-3/ProcessMessages_14-9-2-3_cpp/ProcessMessages_14-9-2-3"; export const ceremonyProcessMessagesNonQvWitnessPath = diff --git a/packages/cli/tests/e2e/e2e.test.ts b/packages/cli/tests/e2e/e2e.test.ts index 4508fd8743..bc9e1fa347 100644 --- a/packages/cli/tests/e2e/e2e.test.ts +++ b/packages/cli/tests/e2e/e2e.test.ts @@ -138,7 +138,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 10n, quiet: true, }); }); @@ -203,7 +202,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 10n, quiet: true, }); }); @@ -277,7 +275,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); } @@ -451,7 +448,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); } @@ -517,7 +513,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); }); @@ -588,7 +583,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); } @@ -696,7 +690,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); // publish @@ -740,7 +733,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); }); @@ -797,7 +789,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); @@ -832,7 +823,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); @@ -871,7 +861,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); // joinPoll @@ -887,7 +876,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); }); @@ -997,7 +985,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); // eslint-disable-next-line no-await-in-loop @@ -1061,7 +1048,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); // eslint-disable-next-line no-await-in-loop @@ -1240,7 +1226,6 @@ describe("e2e tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 1n, quiet: true, }); }); diff --git a/packages/cli/tests/e2e/keyChange.test.ts b/packages/cli/tests/e2e/keyChange.test.ts index 7fc5441f0e..4513607dfe 100644 --- a/packages/cli/tests/e2e/keyChange.test.ts +++ b/packages/cli/tests/e2e/keyChange.test.ts @@ -124,7 +124,6 @@ describe("keyChange tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 99n, quiet: true, }); await publish({ @@ -214,7 +213,6 @@ describe("keyChange tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 99n, quiet: true, }); await publish({ @@ -304,7 +302,6 @@ describe("keyChange tests", function test() { pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, signer, - newVoiceCreditBalance: 99n, quiet: true, }); diff --git a/packages/cli/tests/unit/joinPoll.test.ts b/packages/cli/tests/unit/joinPoll.test.ts index 6e70e4cff7..a20c86c413 100644 --- a/packages/cli/tests/unit/joinPoll.test.ts +++ b/packages/cli/tests/unit/joinPoll.test.ts @@ -31,7 +31,6 @@ describe("joinPoll", function test() { const userPublicKey = user.pubKey.serialize(); const { privKey: pollPrivateKey, pubKey: pollPublicKey } = new Keypair(); - const mockNewVoiceCreditBalance = 10n; const mockStateIndex = 1n; const mockPollId = 9000n; @@ -71,7 +70,6 @@ describe("joinPoll", function test() { pollWasm: testPollJoiningWasmPath, pollWitgen: testPollJoiningWitnessPath, rapidsnark: testRapidsnarkPath, - newVoiceCreditBalance: mockNewVoiceCreditBalance, quiet: true, }); @@ -98,7 +96,6 @@ describe("joinPoll", function test() { pollId: mockPollId, pollPrivKey: pollPrivateKey.serialize(), pollJoiningZkey: pollJoiningTestZkeyPath, - newVoiceCreditBalance: mockNewVoiceCreditBalance, quiet: true, }), ).eventually.rejectedWith("PollDoesNotExist(9000)"); @@ -114,7 +111,6 @@ describe("joinPoll", function test() { pollId: 0n, pollPrivKey: pollPrivateKey.serialize(), pollJoiningZkey: pollJoiningTestZkeyPath, - newVoiceCreditBalance: mockNewVoiceCreditBalance, quiet: true, }), ).eventually.rejectedWith("Invalid state index"); @@ -130,7 +126,6 @@ describe("joinPoll", function test() { pollId: -1n, pollPrivKey: pollPrivateKey.serialize(), pollJoiningZkey: pollJoiningTestZkeyPath, - newVoiceCreditBalance: mockNewVoiceCreditBalance, quiet: true, }), ).eventually.rejectedWith("Invalid poll id"); diff --git a/packages/cli/ts/commands/deploy.ts b/packages/cli/ts/commands/deploy.ts index 1ae2983b3e..2e8bacead0 100644 --- a/packages/cli/ts/commands/deploy.ts +++ b/packages/cli/ts/commands/deploy.ts @@ -78,7 +78,6 @@ export const deploy = async ({ // deploy MACI, PollFactory and poseidon const { maciContract, pollFactoryContract, poseidonAddrs } = await deployMaci({ signUpTokenGatekeeperContractAddress: signupGatekeeperContractAddress, - initialVoiceCreditBalanceAddress: initialVoiceCreditProxyContractAddress, poseidonAddresses: { poseidonT3, poseidonT4, diff --git a/packages/cli/ts/commands/deployPoll.ts b/packages/cli/ts/commands/deployPoll.ts index b69fb1a631..cc29dfced2 100644 --- a/packages/cli/ts/commands/deployPoll.ts +++ b/packages/cli/ts/commands/deployPoll.ts @@ -1,4 +1,9 @@ -import { MACI__factory as MACIFactory, EMode, deployFreeForAllSignUpGatekeeper } from "maci-contracts"; +import { + MACI__factory as MACIFactory, + EMode, + deployFreeForAllSignUpGatekeeper, + deployConstantInitialVoiceCreditProxy, +} from "maci-contracts"; import { PubKey } from "maci-domainobjs"; import { @@ -11,6 +16,7 @@ import { logGreen, type DeployPollArgs, type PollContracts, + DEFAULT_INITIAL_VOICE_CREDITS, } from "../utils"; /** @@ -27,6 +33,7 @@ export const deployPoll = async ({ maciAddress, vkRegistryAddress, gatekeeperAddress, + voiceCreditProxyAddress, signer, quiet = true, useQuadraticVoting = false, @@ -62,6 +69,13 @@ export const deployPoll = async ({ signupGatekeeperContractAddress = await contract.getAddress(); } + let initialVoiceCreditProxyAddress = + voiceCreditProxyAddress || (await readContractAddress("VoiceCreditProxy", network?.name)); + if (!initialVoiceCreditProxyAddress) { + const contract = await deployConstantInitialVoiceCreditProxy(DEFAULT_INITIAL_VOICE_CREDITS, signer, true); + initialVoiceCreditProxyAddress = await contract.getAddress(); + } + // required arg -> poll duration if (pollDuration <= 0) { logError("Duration cannot be <= 0"); @@ -115,6 +129,7 @@ export const deployPoll = async ({ vkRegistry, useQuadraticVoting ? EMode.QV : EMode.NON_QV, signupGatekeeperContractAddress, + initialVoiceCreditProxyAddress, { gasLimit: 10000000 }, ); diff --git a/packages/cli/ts/commands/genProofs.ts b/packages/cli/ts/commands/genProofs.ts index e302a10c63..8415577757 100644 --- a/packages/cli/ts/commands/genProofs.ts +++ b/packages/cli/ts/commands/genProofs.ts @@ -272,7 +272,7 @@ export const genProofs = async ({ const tallyStartTime = Date.now(); const { tallyBatchSize } = poll.batchSizes; - const numStateLeaves = poll.stateLeaves.length; + const numStateLeaves = poll.pubKeys.length; let totalTallyBatches = numStateLeaves <= tallyBatchSize ? 1 : Math.floor(numStateLeaves / tallyBatchSize); if (numStateLeaves > tallyBatchSize && numStateLeaves % tallyBatchSize > 0) { totalTallyBatches += 1; diff --git a/packages/cli/ts/commands/joinPoll.ts b/packages/cli/ts/commands/joinPoll.ts index a78b52a6dc..7ce72ddefb 100644 --- a/packages/cli/ts/commands/joinPoll.ts +++ b/packages/cli/ts/commands/joinPoll.ts @@ -4,53 +4,44 @@ import { formatProofForVerifierContract, genSignUpTree, IGenSignUpTree } from "m import { MACI__factory as MACIFactory, Poll__factory as PollFactory } from "maci-contracts/typechain-types"; import { CircuitInputs, IJsonMaciState, MaciState, IPollJoiningCircuitInputs } from "maci-core"; import { poseidon, stringifyBigInts } from "maci-crypto"; -import { IVkObjectParams, Keypair, PrivKey, PubKey, StateLeaf } from "maci-domainobjs"; +import { IVkObjectParams, Keypair, PrivKey, PubKey } from "maci-domainobjs"; import fs from "fs"; import type { IJoinPollArgs, IJoinedUserArgs, IParsePollJoinEventsArgs, IJoinPollData } from "../utils"; -import { contractExists, logError, logYellow, info, logGreen, success, BLOCKS_STEP, DEFAULT_SG_DATA } from "../utils"; +import { + contractExists, + logError, + logYellow, + info, + logGreen, + success, + BLOCKS_STEP, + DEFAULT_SG_DATA, + DEFAULT_IVCP_DATA, +} from "../utils"; import { banner } from "../utils/banner"; /** * Get state index and credit balance * either from command line or * from maci state leaves or from sign up leaves - * @param stateIndex State index from the command - * @param newVoiceCreditBalance Credit balance from the command - * @param stateLeaves State leaves from maci state or sign up tree + * @param pubKeys Public keys from maci state or sign up tree * @param userMaciPubKey Public key of the maci user - * @returns State index and credit balance + * @param stateIndex State index from the command + * @returns State index */ -const getStateIndexAndCreditBalance = ( - stateIndex: bigint | null, - newVoiceCreditBalance: bigint | null, - stateLeaves: StateLeaf[], - userMaciPubKey: PubKey, -): [bigint | null, bigint | null] => { - let loadedStateIndex = stateIndex; - let loadedCreditBalance = newVoiceCreditBalance; - +const getStateIndex = (pubKeys: PubKey[], userMaciPubKey: PubKey, stateIndex?: bigint): bigint | undefined => { if (!stateIndex) { - const index = stateLeaves.findIndex((leaf) => leaf.pubKey.equals(userMaciPubKey)); + const index = pubKeys.findIndex((key) => key.equals(userMaciPubKey)); if (index > 0) { - loadedStateIndex = BigInt(index); - } else { - logError("State leaf not found"); + return BigInt(index); } + logError("State leaf not found"); } - if (!newVoiceCreditBalance) { - const balance = stateLeaves[Number(loadedStateIndex!)].voiceCreditBalance; - if (balance) { - loadedCreditBalance = balance; - } else { - logError("Voice credit balance not found"); - } - } - - return [loadedStateIndex, loadedCreditBalance]; + return stateIndex; }; /** @@ -98,7 +89,6 @@ const generateAndVerifyProof = async ( * @param stateTreeDepth Maci state tree depth * @param maciPrivKey User's private key for signing up * @param stateLeafIndex Index where the user is stored in the state leaves - * @param credits Credits for voting * @param pollPrivKey Poll's private key for the poll joining * @param pollPubKey Poll's public key for the poll joining * @returns stringified circuit inputs @@ -108,20 +98,11 @@ const joiningCircuitInputs = ( stateTreeDepth: bigint, maciPrivKey: PrivKey, stateLeafIndex: bigint, - credits: bigint, pollPrivKey: PrivKey, pollPubKey: PubKey, ): IPollJoiningCircuitInputs => { // Get the state leaf on the index position - const { signUpTree: stateTree, stateLeaves } = signUpData; - const stateLeaf = stateLeaves[Number(stateLeafIndex)]; - const { pubKey, voiceCreditBalance, timestamp } = stateLeaf; - const [pubKeyX, pubKeyY] = pubKey.asArray(); - const stateLeafArray = [pubKeyX, pubKeyY, voiceCreditBalance, timestamp]; - - if (credits > voiceCreditBalance) { - logError("Credits must be lower than signed up credits"); - } + const { signUpTree: stateTree } = signUpData; // calculate the path elements for the state tree given the original state tree const { siblings, index } = stateTree.generateProof(Number(stateLeafIndex)); @@ -160,11 +141,9 @@ const joiningCircuitInputs = ( privKey: maciPrivKey.asCircuitInputs(), pollPrivKey: pollPrivKey.asCircuitInputs(), pollPubKey: pollPubKey.asCircuitInputs(), - stateLeaf: stateLeafArray, siblings: siblingsArray, indices, nullifier, - credits, stateRoot, actualStateTreeDepth, }; @@ -182,7 +161,6 @@ export const joinPoll = async ({ privateKey, pollPrivKey, stateIndex, - newVoiceCreditBalance, stateFile, pollId, signer, @@ -196,6 +174,7 @@ export const joinPoll = async ({ pollWitgen, pollWasm, sgDataArg, + ivcpDataArg, quiet = true, }: IJoinPollArgs): Promise => { banner(quiet); @@ -230,8 +209,7 @@ export const joinPoll = async ({ const pollContract = PollFactory.connect(pollContracts.poll, signer); - let loadedStateIndex: bigint | null; - let loadedCreditBalance: bigint | null; + let loadedStateIndex: bigint | undefined; let maciState: MaciState | undefined; let signUpData: IGenSignUpTree | undefined; let currentStateRootIndex: number; @@ -251,12 +229,7 @@ export const joinPoll = async ({ throw new Error("User the given nullifier has already joined"); } - [loadedStateIndex, loadedCreditBalance] = getStateIndexAndCreditBalance( - stateIndex, - newVoiceCreditBalance, - maciState!.stateLeaves, - userMaciPubKey, - ); + loadedStateIndex = getStateIndex(maciState!.pubKeys, userMaciPubKey, stateIndex); // check < 1 cause index zero is a blank state leaf if (loadedStateIndex! < 1) { @@ -265,12 +238,11 @@ export const joinPoll = async ({ currentStateRootIndex = poll.maciStateRef.numSignUps - 1; - poll.updatePoll(BigInt(maciState!.stateLeaves.length)); + poll.updatePoll(BigInt(maciState!.pubKeys.length)); circuitInputs = poll.joiningCircuitInputs({ maciPrivKey: userMaciPrivKey, stateLeafIndex: loadedStateIndex!, - credits: loadedCreditBalance!, pollPrivKey: pollPrivKeyDeserialized, pollPubKey, }) as unknown as CircuitInputs; @@ -305,12 +277,7 @@ export const joinPoll = async ({ currentStateRootIndex = Number(numSignups) - 1; - [loadedStateIndex, loadedCreditBalance] = getStateIndexAndCreditBalance( - stateIndex, - newVoiceCreditBalance, - signUpData.stateLeaves, - userMaciPubKey, - ); + loadedStateIndex = getStateIndex(signUpData.pubKeys, userMaciPubKey, stateIndex); // check < 1 cause index zero is a blank state leaf if (loadedStateIndex! < 1) { @@ -322,7 +289,6 @@ export const joinPoll = async ({ stateTreeDepth, userMaciPrivKey, loadedStateIndex!, - loadedCreditBalance!, pollPrivKeyDeserialized, pollPubKey, ) as unknown as CircuitInputs; @@ -334,6 +300,7 @@ export const joinPoll = async ({ let receipt: ContractTransactionReceipt | null = null; const sgData = sgDataArg || DEFAULT_SG_DATA; + const ivcpData = ivcpDataArg || DEFAULT_IVCP_DATA; try { // generate the proof for this batch @@ -351,10 +318,10 @@ export const joinPoll = async ({ const tx = await pollContract.joinPoll( nullifier, pollPubKey.asContractParam(), - loadedCreditBalance!, currentStateRootIndex, proof, sgData, + ivcpData, ); receipt = await tx.wait(); logYellow(quiet, info(`Transaction hash: ${receipt!.hash}`)); diff --git a/packages/cli/ts/commands/signup.ts b/packages/cli/ts/commands/signup.ts index f7e3aa1859..4aa6514e86 100644 --- a/packages/cli/ts/commands/signup.ts +++ b/packages/cli/ts/commands/signup.ts @@ -26,7 +26,7 @@ import type { import { banner } from "../utils/banner"; import { contractExists } from "../utils/contracts"; -import { DEFAULT_IVCP_DATA, DEFAULT_SG_DATA } from "../utils/defaults"; +import { DEFAULT_SG_DATA } from "../utils/defaults"; import { GatekeeperTrait } from "../utils/interfaces"; import { info, logError, logGreen, logYellow, success } from "../utils/theme"; @@ -39,7 +39,6 @@ export const signup = async ({ maciPubKey, maciAddress, sgDataArg, - ivcpDataArg, signer, quiet = true, }: SignupArgs): Promise => { @@ -57,17 +56,12 @@ export const signup = async ({ } const sgData = sgDataArg || DEFAULT_SG_DATA; - const ivcpData = ivcpDataArg || DEFAULT_IVCP_DATA; // we validate that the signup data and voice credit data is valid if (!isBytesLike(sgData)) { logError("invalid signup gateway data"); } - if (!isBytesLike(ivcpData)) { - logError("invalid initial voice credit proxy data"); - } - const maciContract = MACIFactory.connect(maciAddress, signer); let stateIndex = ""; @@ -76,7 +70,7 @@ export const signup = async ({ try { // sign up to the MACI contract - const tx = await maciContract.signUp(userMaciPubKey.asContractParam(), sgData, ivcpData); + const tx = await maciContract.signUp(userMaciPubKey.asContractParam(), sgData); receipt = await tx.wait(); logYellow(quiet, info(`Transaction hash: ${tx.hash}`)); @@ -110,13 +104,13 @@ export const signup = async ({ /** * Parse the signup events from the MACI contract */ -const parseSignupEvents = async ({ maciContract, startBlock, currentBlock, publicKey }: IParseSignupEventsArgs) => { +const parseSignupEvents = async ({ maciContract, startBlock, currentBlock, publicKey }: IParseSignupEventsArgs): Promise<{ stateIndex?: string }> => { // 1000 blocks at a time for (let block = startBlock; block <= currentBlock; block += 1000) { const toBlock = Math.min(block + 999, currentBlock); // eslint-disable-next-line no-await-in-loop const newEvents = await maciContract.queryFilter( - maciContract.filters.SignUp(undefined, publicKey.rawPubKey[0], publicKey.rawPubKey[1]), + maciContract.filters.SignUp(undefined, undefined, publicKey.rawPubKey[0], publicKey.rawPubKey[1]), block, toBlock, ); @@ -126,14 +120,12 @@ const parseSignupEvents = async ({ maciContract, startBlock, currentBlock, publi return { stateIndex: event.args[0].toString(), - voiceCredits: event.args[3].toString(), }; } } return { stateIndex: undefined, - voiceCredits: undefined, }; }; @@ -148,7 +140,7 @@ export const isRegisteredUser = async ({ signer, startBlock, quiet = true, -}: IRegisteredUserArgs): Promise<{ isRegistered: boolean; stateIndex?: string; voiceCredits?: string }> => { +}: IRegisteredUserArgs): Promise<{ isRegistered: boolean; stateIndex?: string }> => { banner(quiet); const maciContract = MACIFactory.connect(maciAddress, signer); @@ -156,7 +148,7 @@ export const isRegisteredUser = async ({ const startBlockNumber = startBlock || 0; const currentBlock = await signer.provider!.getBlockNumber(); - const { stateIndex, voiceCredits } = await parseSignupEvents({ + const { stateIndex } = await parseSignupEvents({ maciContract, startBlock: startBlockNumber, currentBlock, @@ -168,7 +160,6 @@ export const isRegisteredUser = async ({ return { isRegistered: stateIndex !== undefined, stateIndex, - voiceCredits, }; }; diff --git a/packages/cli/ts/index.ts b/packages/cli/ts/index.ts index c40a5166e9..cf4cfa7ce3 100644 --- a/packages/cli/ts/index.ts +++ b/packages/cli/ts/index.ts @@ -218,6 +218,7 @@ program .option("-i, --state-index ", "the user's state index", BigInt) .requiredOption("-s, --sg-data ", "the signup gateway data") .requiredOption("-esk, --poll-priv-key ", "the user ephemeral private key for the poll") + .option("-i, --ivcp-data ", "the initial voice credit proxy data") .option( "-nv, --new-voice-credit-balance ", "the voice credit balance of the user for the poll", @@ -251,8 +252,7 @@ program maciAddress, privateKey, pollPrivKey: cmdObj.pollPrivKey, - stateIndex: cmdObj.stateIndex || null, - newVoiceCreditBalance: cmdObj.newVoiceCreditBalance || null, + stateIndex: cmdObj.stateIndex || undefined, stateFile: cmdObj.stateFile, pollId: cmdObj.pollId, signer, @@ -267,6 +267,7 @@ program rapidsnark: cmdObj.rapidsnark, pollWitgen: cmdObj.pollWitnessgen, sgDataArg: cmdObj.sgData, + ivcpDataArg: cmdObj.ivcpData, }); } catch (error) { program.error((error as Error).message, { exitCode: 1 }); @@ -456,7 +457,6 @@ program .requiredOption("-p, --pubkey ", "the MACI public key") .option("-x, --maci-address ", "the MACI contract address") .option("-s, --sg-data ", "the signup gateway data") - .option("-i, --ivcp-data ", "the initial voice credit proxy data") .option("-q, --quiet ", "whether to print values to the console", (value) => value === "true", false) .option("-r, --rpc-provider ", "the rpc provider URL") .action(async (cmdObj) => { @@ -470,7 +470,6 @@ program maciPubKey: cmdObj.pubkey, maciAddress, sgDataArg: cmdObj.sgData, - ivcpDataArg: cmdObj.ivcpData, quiet: cmdObj.quiet, signer, }); diff --git a/packages/cli/ts/utils/interfaces.ts b/packages/cli/ts/utils/interfaces.ts index 68467b9f7a..16a70ebf5c 100644 --- a/packages/cli/ts/utils/interfaces.ts +++ b/packages/cli/ts/utils/interfaces.ts @@ -324,6 +324,11 @@ export interface DeployPollArgs { * The address of the gatekeeper contract */ gatekeeperAddress?: string; + + /** + * The address of the initial voice credit proxy contract + */ + voiceCreditProxyAddress?: string; } /** @@ -374,11 +379,6 @@ export interface IJoinPollArgs { */ privateKey: string; - /** - * User's credit balance for voting within this poll - */ - newVoiceCreditBalance: bigint | null; - /** * The id of the poll */ @@ -387,7 +387,7 @@ export interface IJoinPollArgs { /** * The index of the state leaf */ - stateIndex: bigint | null; + stateIndex: bigint | undefined; /** * Whether to log the output @@ -458,6 +458,11 @@ export interface IJoinPollArgs { * The signup gatekeeper data */ sgDataArg?: string; + + /** + * The initial voice credit proxy data + */ + ivcpDataArg?: string; } /** @@ -945,11 +950,6 @@ export interface SignupArgs { */ sgDataArg?: string; - /** - * The initial voice credit proxy data - */ - ivcpDataArg?: string; - /** * Whether to log the output */ diff --git a/packages/contracts/contracts/MACI.sol b/packages/contracts/contracts/MACI.sol index 3575d6a7cf..285d33b8b5 100644 --- a/packages/contracts/contracts/MACI.sol +++ b/packages/contracts/contracts/MACI.sol @@ -7,18 +7,18 @@ import { ITallyFactory } from "./interfaces/ITallyFactory.sol"; import { IVerifier } from "./interfaces/IVerifier.sol"; import { IVkRegistry } from "./interfaces/IVkRegistry.sol"; import { ISignUpGatekeeper } from "./interfaces/ISignUpGatekeeper.sol"; -import { InitialVoiceCreditProxy } from "./initialVoiceCreditProxy/InitialVoiceCreditProxy.sol"; +import { IInitialVoiceCreditProxy } from "./interfaces/IInitialVoiceCreditProxy.sol"; import { SignUpGatekeeper } from "./gatekeepers/SignUpGatekeeper.sol"; import { IMACI } from "./interfaces/IMACI.sol"; import { Params } from "./utilities/Params.sol"; -import { Utilities } from "./utilities/Utilities.sol"; import { DomainObjs } from "./utilities/DomainObjs.sol"; import { CurveBabyJubJub } from "./crypto/BabyJubJub.sol"; +import { Hasher } from "./crypto/Hasher.sol"; import { InternalLeanIMT, LeanIMTData } from "./trees/LeanIMT.sol"; /// @title MACI - Minimum Anti-Collusion Infrastructure Version 1 /// @notice A contract which allows users to sign up, and deploy new polls -contract MACI is IMACI, DomainObjs, Params, Utilities { +contract MACI is IMACI, DomainObjs, Params, Hasher { /// @notice The state tree depth is fixed. As such it should be as large as feasible /// so that there can be as many users as possible. i.e. 2 ** 23 = 8388608 /// this should also match the parameter of the circom circuits. @@ -31,9 +31,8 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { uint8 internal constant STATE_TREE_ARITY = 2; - /// @notice The hash of a blank state leaf - uint256 internal constant BLANK_STATE_LEAF_HASH = - uint256(6769006970205099520508948723718471724660867171122235270773600567925038008762); + /// @notice This is the poseidon hash of the pad key + uint256 internal constant PAD_KEY_HASH = 1309255631273308531193241901289907343161346846555918942743921933037802809814; /// @notice The roots of the empty ballot trees uint256[5] public emptyBallotRoots; @@ -53,18 +52,13 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// @notice Factory contract that deploy a Tally contract ITallyFactory public immutable tallyFactory; - /// @notice The state tree. Represents a mapping between each user's public key - /// and their voice credit balance. + /// @notice The state tree. Stores users' public keys LeanIMTData public leanIMTData; /// @notice Address of the SignUpGatekeeper, a contract which determines whether a /// user may sign up to vote SignUpGatekeeper public immutable signUpGatekeeper; - /// @notice The contract which provides the values of the initial voice credit - /// balance per user - InitialVoiceCreditProxy public immutable initialVoiceCreditProxy; - /// @notice The array of the state tree roots for each sign up /// For the N'th sign up, the state tree root will be stored at the index N uint256[] public stateRootsOnSignUp; @@ -77,14 +71,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { } // Events - event SignUp( - uint256 _stateIndex, - uint256 indexed _userPubKeyX, - uint256 indexed _userPubKeyY, - uint256 _voiceCreditBalance, - uint256 _timestamp, - uint256 _stateLeaf - ); + event SignUp(uint256 _stateIndex, uint256 _timestamp, uint256 indexed _userPubKeyX, uint256 indexed _userPubKeyY); event DeployPoll( uint256 _pollId, uint256 indexed _coordinatorPubKeyX, @@ -103,7 +90,6 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// @param _messageProcessorFactory The MessageProcessorFactory contract /// @param _tallyFactory The TallyFactory contract /// @param _signUpGatekeeper The SignUpGatekeeper contract - /// @param _initialVoiceCreditProxy The InitialVoiceCreditProxy contract /// @param _stateTreeDepth The depth of the state tree /// @param _emptyBallotRoots The roots of the empty ballot trees constructor( @@ -111,19 +97,17 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { IMessageProcessorFactory _messageProcessorFactory, ITallyFactory _tallyFactory, SignUpGatekeeper _signUpGatekeeper, - InitialVoiceCreditProxy _initialVoiceCreditProxy, uint8 _stateTreeDepth, uint256[5] memory _emptyBallotRoots ) payable { // initialize and insert the blank leaf - InternalLeanIMT._insert(leanIMTData, BLANK_STATE_LEAF_HASH); - stateRootsOnSignUp.push(BLANK_STATE_LEAF_HASH); + InternalLeanIMT._insert(leanIMTData, PAD_KEY_HASH); + stateRootsOnSignUp.push(PAD_KEY_HASH); pollFactory = _pollFactory; messageProcessorFactory = _messageProcessorFactory; tallyFactory = _tallyFactory; signUpGatekeeper = _signUpGatekeeper; - initialVoiceCreditProxy = _initialVoiceCreditProxy; stateTreeDepth = _stateTreeDepth; maxSignups = uint256(STATE_TREE_ARITY) ** uint256(_stateTreeDepth); emptyBallotRoots = _emptyBallotRoots; @@ -140,14 +124,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// register() function. For instance, the POAPGatekeeper or /// SignUpTokenGatekeeper requires this value to be the ABI-encoded /// token ID. - /// @param _initialVoiceCreditProxyData Data to pass to the - /// InitialVoiceCreditProxy, which allows it to determine how many voice - /// credits this user should have. - function signUp( - PubKey memory _pubKey, - bytes memory _signUpGatekeeperData, - bytes memory _initialVoiceCreditProxyData - ) public virtual { + function signUp(PubKey memory _pubKey, bytes memory _signUpGatekeeperData) public virtual { // ensure we do not have more signups than what the circuits support if (leanIMTData.size >= maxSignups) revert TooManySignups(); @@ -160,17 +137,14 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { // throw if the user has already registered or if ineligible to do so. signUpGatekeeper.register(msg.sender, _signUpGatekeeperData); - // Get the user's voice credit balance. - uint256 voiceCreditBalance = initialVoiceCreditProxy.getVoiceCredits(msg.sender, _initialVoiceCreditProxyData); - - // Create a state leaf and insert it into the tree. - uint256 stateLeaf = hashStateLeaf(StateLeaf(_pubKey, voiceCreditBalance, block.timestamp)); - uint256 stateRoot = InternalLeanIMT._insert(leanIMTData, stateLeaf); + // Hash the public key and insert it into the tree. + uint256 pubKeyHash = hashLeftRight(_pubKey.x, _pubKey.y); + uint256 stateRoot = InternalLeanIMT._insert(leanIMTData, pubKeyHash); // Store the current state tree root in the array stateRootsOnSignUp.push(stateRoot); - emit SignUp(leanIMTData.size - 1, _pubKey.x, _pubKey.y, voiceCreditBalance, block.timestamp, stateLeaf); + emit SignUp(leanIMTData.size - 1, block.timestamp, _pubKey.x, _pubKey.y); } /// @notice Deploy a new Poll contract. @@ -181,6 +155,8 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// @param _verifier The Verifier Contract /// @param _vkRegistry The VkRegistry Contract /// @param _mode Voting mode + /// @param _gatekeeper The gatekeeper contract + /// @param _initialVoiceCreditProxy The initial voice credit proxy contract function deployPoll( uint256 _duration, TreeDepths memory _treeDepths, @@ -189,7 +165,8 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { address _verifier, address _vkRegistry, Mode _mode, - address _gatekeeper + address _gatekeeper, + address _initialVoiceCreditProxy ) public virtual { // cache the poll to a local variable so we can increment it uint256 pollId = nextPollId; @@ -211,7 +188,8 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { maci: IMACI(address(this)), verifier: IVerifier(_verifier), vkRegistry: IVkRegistry(_vkRegistry), - gatekeeper: ISignUpGatekeeper(_gatekeeper) + gatekeeper: ISignUpGatekeeper(_gatekeeper), + initialVoiceCreditProxy: IInitialVoiceCreditProxy(_initialVoiceCreditProxy) }); address p = pollFactory.deploy( diff --git a/packages/contracts/contracts/Poll.sol b/packages/contracts/contracts/Poll.sol index d620c03906..3835482e06 100644 --- a/packages/contracts/contracts/Poll.sol +++ b/packages/contracts/contracts/Poll.sol @@ -109,7 +109,7 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll { event PollJoined( uint256 indexed _pollPubKeyX, uint256 indexed _pollPubKeyY, - uint256 _newVoiceCreditBalance, + uint256 _voiceCreditBalance, uint256 _timestamp, uint256 _nullifier, uint256 _pollStateIndex @@ -277,17 +277,18 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll { /// @notice Join the poll for voting /// @param _nullifier Hashed user's private key to check whether user has already voted /// @param _pubKey Poll user's public key - /// @param _newVoiceCreditBalance User's credit balance for voting within this poll - /// @param _stateRootIndex Index of the MACI's stateRootOnSignUp when the user signed up + /// @param _stateRootIndex Index of the MACI's stateRootOnSignUp for which the inclusion proof is generated /// @param _proof The zk-SNARK proof + /// @param _signUpGatekeeperData Data to pass to the SignUpGatekeeper + /// @param _initialVoiceCreditProxyData Data to pass to the InitialVoiceCreditProxy function joinPoll( uint256 _nullifier, PubKey calldata _pubKey, - uint256 _newVoiceCreditBalance, uint256 _stateRootIndex, uint256[8] calldata _proof, - bytes memory _signUpGatekeeperData - ) public virtual isWithinVotingDeadline { + bytes memory _signUpGatekeeperData, + bytes memory _initialVoiceCreditProxyData + ) external virtual isWithinVotingDeadline { // Whether the user has already joined if (pollNullifier[_nullifier]) { revert UserAlreadyJoined(); @@ -297,31 +298,35 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll { pollNullifier[_nullifier] = true; // Verify user's proof - if (!verifyPollProof(_nullifier, _newVoiceCreditBalance, _stateRootIndex, _pubKey, _proof)) { + if (!verifyPollProof(_nullifier, _stateRootIndex, _pubKey, _proof)) { revert InvalidPollProof(); } // Check if the user is eligible to join the poll extContracts.gatekeeper.register(msg.sender, _signUpGatekeeperData); + // Get the user's voice credit balance. + uint256 voiceCreditBalance = extContracts.initialVoiceCreditProxy.getVoiceCredits( + msg.sender, + _initialVoiceCreditProxyData + ); + // Store user in the pollStateTree - uint256 stateLeaf = hashStateLeaf(StateLeaf(_pubKey, _newVoiceCreditBalance, block.timestamp)); + uint256 stateLeaf = hashStateLeaf(StateLeaf(_pubKey, voiceCreditBalance, block.timestamp)); InternalLazyIMT._insert(pollStateTree, stateLeaf); uint256 pollStateIndex = pollStateTree.numberOfLeaves - 1; - emit PollJoined(_pubKey.x, _pubKey.y, _newVoiceCreditBalance, block.timestamp, _nullifier, pollStateIndex); + emit PollJoined(_pubKey.x, _pubKey.y, voiceCreditBalance, block.timestamp, _nullifier, pollStateIndex); } /// @notice Verify the proof for Poll /// @param _nullifier Hashed user's private key to check whether user has already voted - /// @param _voiceCreditBalance User's credit balance for voting /// @param _index Index of the MACI's stateRootOnSignUp when the user signed up /// @param _pubKey Poll user's public key /// @param _proof The zk-SNARK proof /// @return isValid Whether the proof is valid function verifyPollProof( uint256 _nullifier, - uint256 _voiceCreditBalance, uint256 _index, PubKey calldata _pubKey, uint256[8] memory _proof @@ -333,30 +338,27 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll { ); // Generate the circuit public input - uint256[] memory circuitPublicInputs = getPublicCircuitInputs(_nullifier, _voiceCreditBalance, _index, _pubKey); + uint256[] memory circuitPublicInputs = getPublicCircuitInputs(_nullifier, _index, _pubKey); isValid = extContracts.verifier.verify(_proof, vk, circuitPublicInputs); } /// @notice Get public circuit inputs for poll joining circuit /// @param _nullifier Hashed user's private key to check whether user has already voted - /// @param _voiceCreditBalance User's credit balance for voting /// @param _index Index of the MACI's stateRootOnSignUp when the user signed up /// @param _pubKey Poll user's public key /// @return publicInputs Public circuit inputs function getPublicCircuitInputs( uint256 _nullifier, - uint256 _voiceCreditBalance, uint256 _index, PubKey calldata _pubKey ) public view returns (uint256[] memory publicInputs) { - publicInputs = new uint256[](5); + publicInputs = new uint256[](4); publicInputs[0] = _pubKey.x; publicInputs[1] = _pubKey.y; publicInputs[2] = _nullifier; - publicInputs[3] = _voiceCreditBalance; - publicInputs[4] = extContracts.maci.getStateRootOnIndexedSignUp(_index); + publicInputs[3] = extContracts.maci.getStateRootOnIndexedSignUp(_index); } /// @inheritdoc IPoll diff --git a/packages/contracts/contracts/interfaces/IInitialVoiceCreditProxy.sol b/packages/contracts/contracts/interfaces/IInitialVoiceCreditProxy.sol new file mode 100644 index 0000000000..546e0e9b6f --- /dev/null +++ b/packages/contracts/contracts/interfaces/IInitialVoiceCreditProxy.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +/// @title IInitialVoiceCreditProxy +/// @notice InitialVoiceCreditProxy interface +interface IInitialVoiceCreditProxy { + /// @notice Get the voice credits of a user + /// @param _user User address + /// @param _data Data to get voice credits + function getVoiceCredits(address _user, bytes memory _data) external view returns (uint256); +} diff --git a/packages/contracts/contracts/interfaces/IPoll.sol b/packages/contracts/contracts/interfaces/IPoll.sol index b17491e6ad..2deeb5e9c5 100644 --- a/packages/contracts/contracts/interfaces/IPoll.sol +++ b/packages/contracts/contracts/interfaces/IPoll.sol @@ -11,10 +11,10 @@ interface IPoll { function joinPoll( uint256 _nullifier, DomainObjs.PubKey calldata _pubKey, - uint256 _newVoiceCreditBalance, uint256 _stateRootIndex, uint256[8] calldata _proof, - bytes memory _signUpGatekeeperData + bytes memory _signUpGatekeeperData, + bytes memory _initialVoiceCreditProxyData ) external; /// @notice The number of messages which have been processed and the number of signups diff --git a/packages/contracts/contracts/trees/LeanIMT.sol b/packages/contracts/contracts/trees/LeanIMT.sol index cc3fe22448..ba7f8ac83a 100644 --- a/packages/contracts/contracts/trees/LeanIMT.sol +++ b/packages/contracts/contracts/trees/LeanIMT.sol @@ -45,9 +45,7 @@ library InternalLeanIMT { revert LeafGreaterThanSnarkScalarField(); } else if (leaf == 0) { revert LeafCannotBeZero(); - } else if (_has(self, leaf)) { - revert LeafAlreadyExists(); - } + } uint256 index = self.size; diff --git a/packages/contracts/contracts/utilities/Params.sol b/packages/contracts/contracts/utilities/Params.sol index 91d9fae8e0..b6195451a2 100644 --- a/packages/contracts/contracts/utilities/Params.sol +++ b/packages/contracts/contracts/utilities/Params.sol @@ -5,6 +5,7 @@ import { IMACI } from "../interfaces/IMACI.sol"; import { IVerifier } from "../interfaces/IVerifier.sol"; import { IVkRegistry } from "../interfaces/IVkRegistry.sol"; import { ISignUpGatekeeper } from "../interfaces/ISignUpGatekeeper.sol"; +import { IInitialVoiceCreditProxy } from "../interfaces/IInitialVoiceCreditProxy.sol"; /// @title Params /// @notice This contracts contains a number of structures @@ -26,5 +27,6 @@ contract Params { IVerifier verifier; IVkRegistry vkRegistry; ISignUpGatekeeper gatekeeper; + IInitialVoiceCreditProxy initialVoiceCreditProxy; } } diff --git a/packages/contracts/tasks/deploy/maci/01-constantInitialVoiceCreditProxy.ts b/packages/contracts/tasks/deploy/poll/01-constantInitialVoiceCreditProxy.ts similarity index 100% rename from packages/contracts/tasks/deploy/maci/01-constantInitialVoiceCreditProxy.ts rename to packages/contracts/tasks/deploy/poll/01-constantInitialVoiceCreditProxy.ts diff --git a/packages/contracts/tasks/deploy/poll/01-gatekeepers.ts b/packages/contracts/tasks/deploy/poll/02-gatekeepers.ts similarity index 100% rename from packages/contracts/tasks/deploy/poll/01-gatekeepers.ts rename to packages/contracts/tasks/deploy/poll/02-gatekeepers.ts diff --git a/packages/contracts/tasks/deploy/poll/02-poll.ts b/packages/contracts/tasks/deploy/poll/03-poll.ts similarity index 95% rename from packages/contracts/tasks/deploy/poll/02-poll.ts rename to packages/contracts/tasks/deploy/poll/03-poll.ts index 98b48ee5b8..23b38b782b 100644 --- a/packages/contracts/tasks/deploy/poll/02-poll.ts +++ b/packages/contracts/tasks/deploy/poll/03-poll.ts @@ -53,6 +53,11 @@ deployment.deployTask(EDeploySteps.Poll, "Deploy poll").then((task) => deployment.getDeployConfigField(EContracts.Poll, "gatekeeper") || EContracts.FreeForAllGatekeeper; const gatekeeperContractAddress = storage.mustGetAddress(gatekeeper, hre.network.name, `poll-${pollId}`); + const initialVoiceCreditProxyContractAddress = storage.mustGetAddress( + EContracts.ConstantInitialVoiceCreditProxy, + hre.network.name, + `poll-${pollId}`, + ); const tx = await maciContract.deployPoll( pollDuration, @@ -66,6 +71,7 @@ deployment.deployTask(EDeploySteps.Poll, "Deploy poll").then((task) => vkRegistryContractAddress, mode, gatekeeperContractAddress, + initialVoiceCreditProxyContractAddress, ); const receipt = await tx.wait(); diff --git a/packages/contracts/tasks/helpers/ProofGenerator.ts b/packages/contracts/tasks/helpers/ProofGenerator.ts index 7583a0c550..ac1bd3e313 100644 --- a/packages/contracts/tasks/helpers/ProofGenerator.ts +++ b/packages/contracts/tasks/helpers/ProofGenerator.ts @@ -252,7 +252,7 @@ export class ProofGenerator { console.log(`Generating proofs of vote tallying...`); const { tallyBatchSize } = this.poll.batchSizes; - const numStateLeaves = this.poll.stateLeaves.length; + const numStateLeaves = this.poll.pollStateLeaves.length; let totalTallyBatches = numStateLeaves <= tallyBatchSize ? 1 : Math.floor(numStateLeaves / tallyBatchSize); if (numStateLeaves > tallyBatchSize && numStateLeaves % tallyBatchSize > 0) { totalTallyBatches += 1; diff --git a/packages/contracts/tasks/helpers/constants.ts b/packages/contracts/tasks/helpers/constants.ts index f8bcfec6c2..e0bbbbeac7 100644 --- a/packages/contracts/tasks/helpers/constants.ts +++ b/packages/contracts/tasks/helpers/constants.ts @@ -2,7 +2,6 @@ * Deploy steps */ export enum EDeploySteps { - ConstantInitialVoiceCreditProxy = "full:deploy-constant-initial-voice-credit-proxy", Gatekeepers = "full:deploy-gatekeepers", Verifier = "full:deploy-verifier", Poseidon = "full:deploy-poseidon", @@ -11,6 +10,7 @@ export enum EDeploySteps { TallyFactory = "full:deploy-tally-factory", Maci = "full:deploy-maci", VkRegistry = "full:deploy-vk-registry", + ConstantInitialVoiceCreditProxy = "poll:deploy-constant-initial-voice-credit-proxy", PollGatekeeper = "poll:deploy-gatekeeper", Poll = "poll:deploy-poll", } diff --git a/packages/contracts/tests/MACI.test.ts b/packages/contracts/tests/MACI.test.ts index 8c8d6a03ae..6d853f7ed5 100644 --- a/packages/contracts/tests/MACI.test.ts +++ b/packages/contracts/tests/MACI.test.ts @@ -15,6 +15,7 @@ import { Verifier, VkRegistry, SignUpGatekeeper, + ConstantInitialVoiceCreditProxy, } from "../typechain-types"; import { STATE_TREE_DEPTH, duration, initialVoiceCreditBalance, messageBatchSize, treeDepths } from "./constants"; @@ -27,6 +28,7 @@ describe("MACI", function test() { let vkRegistryContract: VkRegistry; let verifierContract: Verifier; let signupGatekeeperContract: SignUpGatekeeper; + let initialVoiceCreditProxy: ConstantInitialVoiceCreditProxy; let pollId: bigint; const coordinator = new Keypair(); @@ -49,6 +51,7 @@ describe("MACI", function test() { vkRegistryContract = r.vkRegistryContract; verifierContract = r.mockVerifierContract as Verifier; signupGatekeeperContract = r.gatekeeperContract; + initialVoiceCreditProxy = r.constantInitialVoiceCreditProxyContract; }); it("should have set the correct stateTreeDepth", async () => { @@ -83,7 +86,6 @@ describe("MACI", function test() { const tx = await maciContract.signUp( user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ); // eslint-disable-next-line no-await-in-loop const receipt = await tx.wait(); @@ -101,11 +103,7 @@ describe("MACI", function test() { expect(event.args._stateIndex.toString()).to.eq((index + 1).toString()); - maciState.signUp( - user.pubKey, - BigInt(event.args._voiceCreditBalance.toString()), - BigInt(event.args._timestamp.toString()), - ); + maciState.signUp(user.pubKey); } }); @@ -117,7 +115,6 @@ describe("MACI", function test() { y: "0", }, AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ), ).to.be.revertedWithCustomError(maciContract, "InvalidPubKey"); }); @@ -130,7 +127,6 @@ describe("MACI", function test() { y: "21888242871839275222246405745257275088548364400416034343698204186575808495617", }, AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ), ).to.be.revertedWithCustomError(maciContract, "InvalidPubKey"); }); @@ -143,7 +139,6 @@ describe("MACI", function test() { y: "21888242871839275222246405745257275088548364400416034343698204186575808495617", }, AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ), ).to.be.revertedWithCustomError(maciContract, "InvalidPubKey"); }); @@ -156,7 +151,6 @@ describe("MACI", function test() { y: "1", }, AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ), ).to.be.revertedWithCustomError(maciContract, "InvalidPubKey"); }); @@ -175,20 +169,12 @@ describe("MACI", function test() { // start from one as we already have one signup (blank state leaf) for (let i = 1; i < maxUsers; i += 1) { // eslint-disable-next-line no-await-in-loop - await maci.signUp( - keypair.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ); + await maci.signUp(keypair.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])); } // the next signup should fail await expect( - maci.signUp( - keypair.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ), + maci.signUp(keypair.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])), ).to.be.revertedWithCustomError(maciContract, "TooManySignups"); }); @@ -205,11 +191,7 @@ describe("MACI", function test() { // start from one as we already have one signup (blank state leaf) for (let i = 1; i < maxUsers; i += 1) { // eslint-disable-next-line no-await-in-loop - await maci.signUp( - keypair.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ); + await maci.signUp(keypair.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])); } }); }); @@ -228,6 +210,7 @@ describe("MACI", function test() { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxy, ); const receipt = await tx.wait(); @@ -263,6 +246,7 @@ describe("MACI", function test() { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxy, ); const receipt = await tx.wait(); expect(receipt?.status).to.eq(1); @@ -282,6 +266,7 @@ describe("MACI", function test() { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxy, ); const receipt = await tx.wait(); expect(receipt?.status).to.eq(1); @@ -289,51 +274,6 @@ describe("MACI", function test() { }); }); - describe("Merge sign-ups", () => { - let pollContract: PollContract; - - before(async () => { - const pollContracts = await maciContract.getPoll(pollId); - pollContract = PollFactory.connect(pollContracts.poll, signer); - }); - - it("should allow a Poll contract to merge the state tree (calculate the state root)", async () => { - await timeTravel(signer.provider as unknown as EthereumProvider, Number(duration) + 1); - - const tx = await pollContract.mergeState(); - const receipt = await tx.wait(); - expect(receipt?.status).to.eq(1); - }); - - it("should allow a user to signup after the state tree root was calculated", async () => { - const tx = await maciContract.signUp( - users[0].pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ); - const receipt = await tx.wait(); - expect(receipt?.status).to.eq(1); - - const iface = maciContract.interface; - - // Store the state index - const log = receipt!.logs[receipt!.logs.length - 1]; - const event = iface.parseLog(log as unknown as { topics: string[]; data: string }) as unknown as { - args: { - _stateIndex: BigNumberish; - _voiceCreditBalance: BigNumberish; - _timestamp: BigNumberish; - }; - }; - - maciState.signUp( - users[0].pubKey, - BigInt(event.args._voiceCreditBalance.toString()), - BigInt(event.args._timestamp.toString()), - ); - }); - }); - describe("getPoll", () => { it("should return an object for a valid id", async () => { const pollContracts = await maciContract.getPoll(pollId); diff --git a/packages/contracts/tests/MessageProcessor.test.ts b/packages/contracts/tests/MessageProcessor.test.ts index 6702b30619..1060b17d03 100644 --- a/packages/contracts/tests/MessageProcessor.test.ts +++ b/packages/contracts/tests/MessageProcessor.test.ts @@ -16,6 +16,7 @@ import { Verifier, VkRegistry, SignUpGatekeeper, + ConstantInitialVoiceCreditProxy, } from "../typechain-types"; import { @@ -36,6 +37,7 @@ describe("MessageProcessor", () => { let vkRegistryContract: VkRegistry; let mpContract: MessageProcessor; let signupGatekeeperContract: SignUpGatekeeper; + let initialVoiceCreditProxyContract: ConstantInitialVoiceCreditProxy; let pollId: bigint; // local poll and maci state @@ -59,6 +61,8 @@ describe("MessageProcessor", () => { verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; signupGatekeeperContract = r.gatekeeperContract; + initialVoiceCreditProxyContract = r.constantInitialVoiceCreditProxyContract; + // deploy on chain poll const tx = await maciContract.deployPoll( duration, @@ -69,6 +73,7 @@ describe("MessageProcessor", () => { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxyContract, ); let receipt = await tx.wait(); @@ -107,7 +112,7 @@ describe("MessageProcessor", () => { } // update the poll state - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); generatedInputs = poll.processMessages(pollId); diff --git a/packages/contracts/tests/Poll.test.ts b/packages/contracts/tests/Poll.test.ts index 935a731807..68861d3393 100644 --- a/packages/contracts/tests/Poll.test.ts +++ b/packages/contracts/tests/Poll.test.ts @@ -17,6 +17,7 @@ import { Verifier, VkRegistry, SignUpGatekeeper, + ConstantInitialVoiceCreditProxy, } from "../typechain-types"; import { @@ -38,6 +39,7 @@ describe("Poll", () => { let verifierContract: Verifier; let vkRegistryContract: VkRegistry; let signupGatekeeperContract: SignUpGatekeeper; + let initialVoiceCreditProxyContract: ConstantInitialVoiceCreditProxy; let signer: Signer; let deployTime: number; const coordinator = new Keypair(); @@ -46,7 +48,7 @@ describe("Poll", () => { const keypair = new Keypair(); - const NUM_USERS = 2; + const NUM_USERS = 3; describe("deployment", () => { before(async () => { @@ -60,18 +62,14 @@ describe("Poll", () => { verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; signupGatekeeperContract = r.gatekeeperContract; + initialVoiceCreditProxyContract = r.constantInitialVoiceCreditProxyContract; for (let i = 0; i < NUM_USERS; i += 1) { - const timestamp = Math.floor(Date.now() / 1000); const user = new Keypair(); - maciState.signUp(user.pubKey, BigInt(initialVoiceCreditBalance), BigInt(timestamp), BigInt(0)); + maciState.signUp(user.pubKey); // eslint-disable-next-line no-await-in-loop - await maciContract.signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ); + await maciContract.signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])); } // deploy on chain poll @@ -84,6 +82,7 @@ describe("Poll", () => { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxyContract, ); const receipt = await tx.wait(); @@ -164,7 +163,7 @@ describe("Poll", () => { it("should have numMessages set to 1 (blank message)", async () => { const numMessages = await pollContract.numMessages(); expect(numMessages.toString()).to.eq("1"); - }); + }); it("should fail when passing an invalid coordinator public key", async () => { const r = await deployTestContracts({ @@ -188,35 +187,36 @@ describe("Poll", () => { r.vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxyContract, ), ).to.be.revertedWithCustomError(testMaciContract, "InvalidPubKey"); }); }); describe("Poll join", () => { - it("The users have joined the poll", async () => { + it("should let users join the poll", async () => { const iface = pollContract.interface; const pubkey = keypair.pubKey.asContractParam(); const mockProof = [0, 0, 0, 0, 0, 0, 0, 0]; for (let i = 0; i < NUM_USERS; i += 1) { const mockNullifier = AbiCoder.defaultAbiCoder().encode(["uint256"], [i]); - const voiceCreditBalance = AbiCoder.defaultAbiCoder().encode(["uint256"], [i]); const response = await pollContract.joinPoll( mockNullifier, pubkey, - voiceCreditBalance, i, mockProof, AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), + AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), ); const receipt = await response.wait(); const logs = receipt!.logs[0]; const event = iface.parseLog(logs as unknown as { topics: string[]; data: string }) as unknown as { - args: { _pollStateIndex: bigint }; + args: { _pollStateIndex: bigint, _voiceCreditBalance: bigint }; }; const index = event.args._pollStateIndex; + const voiceCredits = event.args._voiceCreditBalance; expect(receipt!.status).to.eq(1); @@ -225,32 +225,31 @@ describe("Poll", () => { const expectedIndex = maciState.polls .get(pollId) - ?.joinPoll(BigInt(mockNullifier), keypair.pubKey, BigInt(voiceCreditBalance), BigInt(timestamp)); + ?.joinPoll(BigInt(mockNullifier), keypair.pubKey, BigInt(voiceCredits), BigInt(timestamp)); expect(index).to.eq(expectedIndex); } }); - it("Poll state tree size after user's joining", async () => { + it("should have the correct tree size", async () => { const pollStateTree = await pollContract.pollStateTree(); const size = Number(pollStateTree.numberOfLeaves); expect(size).to.eq(maciState.polls.get(pollId)?.pollStateLeaves.length); }); - it("The first user has been rejected for the second join", async () => { + it("should not allow a user to join twice", async () => { const mockNullifier = AbiCoder.defaultAbiCoder().encode(["uint256"], [0]); const pubkey = keypair.pubKey.asContractParam(); - const voiceCreditBalance = AbiCoder.defaultAbiCoder().encode(["uint256"], [0]); const mockProof = [0, 0, 0, 0, 0, 0, 0, 0]; await expect( pollContract.joinPoll( mockNullifier, pubkey, - voiceCreditBalance, 0, mockProof, AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), + AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), ), ).to.be.revertedWithCustomError(pollContract, "UserAlreadyJoined"); }); @@ -380,12 +379,12 @@ describe("Poll", () => { it("should allow a Poll contract to merge the state tree (calculate the state root)", async () => { await timeTravel(signer.provider as unknown as EthereumProvider, Number(duration) + 1); - const tx = await pollContract.mergeState({ - gasLimit: 3000000, - }); + const tx = await pollContract.mergeState(); const receipt = await tx.wait(); expect(receipt?.status).to.eq(1); + + expect(await pollContract.stateMerged()).to.eq(true); }); it("should get the correct numSignUps", async () => { @@ -396,6 +395,7 @@ describe("Poll", () => { it("should get the correct mergedStateRoot", async () => { const mergedStateRoot = await pollContract.mergedStateRoot(); + expect(mergedStateRoot.toString()).to.eq(maciState.polls.get(pollId)?.pollStateTree?.root.toString()); }); }); diff --git a/packages/contracts/tests/PollFactory.test.ts b/packages/contracts/tests/PollFactory.test.ts index 55c3586818..bee5351bf2 100644 --- a/packages/contracts/tests/PollFactory.test.ts +++ b/packages/contracts/tests/PollFactory.test.ts @@ -38,6 +38,7 @@ describe("pollFactory", () => { verifier: verifierContract, vkRegistry: vkRegistryContract, gatekeeper: r.gatekeeperContract, + initialVoiceCreditProxy: r.constantInitialVoiceCreditProxyContract, }; pollFactory = (await deployPollFactory(signer, undefined, true)) as BaseContract as PollFactory; diff --git a/packages/contracts/tests/Tally.test.ts b/packages/contracts/tests/Tally.test.ts index d8a25fd2de..7024dc9178 100644 --- a/packages/contracts/tests/Tally.test.ts +++ b/packages/contracts/tests/Tally.test.ts @@ -20,6 +20,7 @@ import { Poll__factory as PollFactory, Tally__factory as TallyFactory, SignUpGatekeeper, + ConstantInitialVoiceCreditProxy, } from "../typechain-types"; import { @@ -43,6 +44,7 @@ describe("TallyVotes", () => { let verifierContract: Verifier; let vkRegistryContract: VkRegistry; let signupGatekeeperContract: SignUpGatekeeper; + let initialVoiceCreditProxyContract: ConstantInitialVoiceCreditProxy; const coordinator = new Keypair(); let users: Keypair[]; @@ -66,6 +68,7 @@ describe("TallyVotes", () => { verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; signupGatekeeperContract = r.gatekeeperContract; + initialVoiceCreditProxyContract = r.constantInitialVoiceCreditProxyContract; // deploy a poll // deploy on chain poll @@ -78,6 +81,7 @@ describe("TallyVotes", () => { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxyContract, ); const receipt = await tx.wait(); @@ -113,7 +117,7 @@ describe("TallyVotes", () => { poll.publishMessage(message, padKey); // update the poll state - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); // process messages locally generatedInputs = poll.processMessages(pollId); @@ -209,16 +213,14 @@ describe("TallyVotes", () => { // signup all users // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < users.length; i += 1) { - const timestamp = Math.floor(Date.now() / 1000); // signup locally - maciState.signUp(users[i].pubKey, BigInt(initialVoiceCreditBalance), BigInt(timestamp)); + maciState.signUp(users[i].pubKey); // signup on chain // eslint-disable-next-line no-await-in-loop await maciContract.signUp( users[i].pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ); } @@ -236,6 +238,7 @@ describe("TallyVotes", () => { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxyContract, ); const receipt = await tx.wait(); @@ -279,7 +282,7 @@ describe("TallyVotes", () => { poll.publishMessage(message, padKey); // update the poll state - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); await vkRegistryContract.setPollVkKey( STATE_TREE_DEPTH, @@ -310,10 +313,10 @@ describe("TallyVotes", () => { await pollContract.joinPoll( nullifier, pollKeys[i].pubKey.asContractParam(), - BigInt(initialVoiceCreditBalance), i, [0, 0, 0, 0, 0, 0, 0, 0], AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), + AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ); } @@ -518,16 +521,14 @@ describe("TallyVotes", () => { // signup all users // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < users.length; i += 1) { - const timestamp = Math.floor(Date.now() / 1000); // signup locally - maciState.signUp(users[i].pubKey, BigInt(initialVoiceCreditBalance), BigInt(timestamp)); + maciState.signUp(users[i].pubKey); // signup on chain // eslint-disable-next-line no-await-in-loop await maciContract.signUp( users[i].pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ); } @@ -545,6 +546,7 @@ describe("TallyVotes", () => { vkRegistryContract, EMode.QV, signupGatekeeperContract, + initialVoiceCreditProxyContract, ); const receipt = await tx.wait(); @@ -588,7 +590,7 @@ describe("TallyVotes", () => { poll.publishMessage(message, padKey); // update the poll state - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); // set the verification keys on the vk smart contract await vkRegistryContract.setPollVkKey( @@ -620,10 +622,10 @@ describe("TallyVotes", () => { await pollContract.joinPoll( nullifier, pollKeys[i].pubKey.asContractParam(), - BigInt(initialVoiceCreditBalance), i, [0, 0, 0, 0, 0, 0, 0, 0], AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), + AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), ); } diff --git a/packages/contracts/tests/TallyNonQv.test.ts b/packages/contracts/tests/TallyNonQv.test.ts index 9187925de8..3148e18805 100644 --- a/packages/contracts/tests/TallyNonQv.test.ts +++ b/packages/contracts/tests/TallyNonQv.test.ts @@ -20,6 +20,7 @@ import { Poll__factory as PollFactory, Tally__factory as TallyFactory, SignUpGatekeeper, + ConstantInitialVoiceCreditProxy, } from "../typechain-types"; import { STATE_TREE_DEPTH, duration, messageBatchSize, testProcessVk, testTallyVk, treeDepths } from "./constants"; @@ -34,7 +35,7 @@ describe("TallyVotesNonQv", () => { let verifierContract: Verifier; let vkRegistryContract: VkRegistry; let gatekeeperContract: SignUpGatekeeper; - + let initialVoiceCreditProxyContract: ConstantInitialVoiceCreditProxy; const coordinator = new Keypair(); let maciState: MaciState; @@ -53,6 +54,7 @@ describe("TallyVotesNonQv", () => { verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; gatekeeperContract = r.gatekeeperContract; + initialVoiceCreditProxyContract = r.constantInitialVoiceCreditProxyContract; // deploy a poll // deploy on chain poll @@ -65,6 +67,7 @@ describe("TallyVotesNonQv", () => { vkRegistryContract, EMode.NON_QV, gatekeeperContract, + initialVoiceCreditProxyContract, ); const receipt = await tx.wait(); @@ -100,7 +103,7 @@ describe("TallyVotesNonQv", () => { poll.publishMessage(message, padKey); // update the poll state - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); // process messages locally generatedInputs = poll.processMessages(pollId, false); diff --git a/packages/contracts/tests/constants.ts b/packages/contracts/tests/constants.ts index ac2b747dcf..7b543b9b24 100644 --- a/packages/contracts/tests/constants.ts +++ b/packages/contracts/tests/constants.ts @@ -8,6 +8,7 @@ export interface ExtContractsStruct { verifier: AddressLike; vkRegistry: AddressLike; gatekeeper: AddressLike; + initialVoiceCreditProxy: AddressLike; } export const duration = 2_000; diff --git a/packages/contracts/tests/gatekeepers/AnonAadhaarGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/AnonAadhaarGatekeeper.test.ts index 815ba7d0aa..213f9418af 100644 --- a/packages/contracts/tests/gatekeepers/AnonAadhaarGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/AnonAadhaarGatekeeper.test.ts @@ -118,11 +118,7 @@ describe("AnonAadhaar Gatekeeper", () => { ); await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedInvalidNullifierSeedProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + maciContract.signUp(user.pubKey.asContractParam(), encodedInvalidNullifierSeedProof), ).to.be.revertedWithCustomError(anonAadhaarGatekeeper, "InvalidNullifierSeed"); }); @@ -139,32 +135,21 @@ describe("AnonAadhaar Gatekeeper", () => { ], ); await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedInvalidProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ), + maciContract.signUp(user.pubKey.asContractParam(), encodedInvalidProof), ).to.be.revertedWithCustomError(anonAadhaarGatekeeper, "InvalidSignal"); }); it("should revert if the proof is invalid (mock)", async () => { await mockAnonAadhaar.flipValid(); - await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), - ).to.be.revertedWithCustomError(anonAadhaarGatekeeper, "InvalidProof"); + await expect(maciContract.signUp(user.pubKey.asContractParam(), encodedProof)).to.be.revertedWithCustomError( + anonAadhaarGatekeeper, + "InvalidProof", + ); await mockAnonAadhaar.flipValid(); }); it("should register a user if the register function is called with the valid data", async () => { - const tx = await maciContract.signUp( - user.pubKey.asContractParam(), - encodedProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + const tx = await maciContract.signUp(user.pubKey.asContractParam(), encodedProof); const receipt = await tx.wait(); @@ -172,13 +157,10 @@ describe("AnonAadhaar Gatekeeper", () => { }); it("should prevent signing up twice", async () => { - await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), - ).to.be.revertedWithCustomError(anonAadhaarGatekeeper, "AlreadyRegistered"); + await expect(maciContract.signUp(user.pubKey.asContractParam(), encodedProof)).to.be.revertedWithCustomError( + anonAadhaarGatekeeper, + "AlreadyRegistered", + ); }); }); }); diff --git a/packages/contracts/tests/gatekeepers/EASGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/EASGatekeeper.test.ts index eddd1cff0a..d56c3b7d41 100644 --- a/packages/contracts/tests/gatekeepers/EASGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/EASGatekeeper.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { AbiCoder, Signer, ZeroAddress, toBeArray } from "ethers"; +import { Signer, ZeroAddress, toBeArray } from "ethers"; import { Keypair } from "maci-domainobjs"; import { deployContract } from "../../ts/deploy"; @@ -124,11 +124,7 @@ describe("EAS Gatekeeper", () => { await easGatekeeper.setMaciInstance(await maciContract.getAddress()).then((tx) => tx.wait()); // signup via MACI - const tx = await maciContract.signUp( - user.pubKey.asContractParam(), - attestation, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + const tx = await maciContract.signUp(user.pubKey.asContractParam(), attestation); const receipt = await tx.wait(); @@ -136,13 +132,10 @@ describe("EAS Gatekeeper", () => { }); it("should prevent signing up twice", async () => { - await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - attestation, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), - ).to.be.revertedWithCustomError(easGatekeeper, "AlreadyRegistered"); + await expect(maciContract.signUp(user.pubKey.asContractParam(), attestation)).to.be.revertedWithCustomError( + easGatekeeper, + "AlreadyRegistered", + ); }); }); }); diff --git a/packages/contracts/tests/gatekeepers/GitcoinPassportGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/GitcoinPassportGatekeeper.test.ts index 797e8c26c0..9fd0178716 100644 --- a/packages/contracts/tests/gatekeepers/GitcoinPassportGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/GitcoinPassportGatekeeper.test.ts @@ -103,11 +103,7 @@ describe("GitcoinPassport Gatekeeper", () => { const tx = await maciContract .connect(secondSigner) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])); const receipt = await tx.wait(); diff --git a/packages/contracts/tests/gatekeepers/HatsGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/HatsGatekeeper.test.ts index 02a03020d9..99d0cddb69 100644 --- a/packages/contracts/tests/gatekeepers/HatsGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/HatsGatekeeper.test.ts @@ -94,11 +94,7 @@ describe("HatsProtocol Gatekeeper", () => { await expect( maciContract .connect(signer) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])), ).to.be.revertedWithCustomError(hatsGatekeeperSingle, "OnlyMACI"); }); @@ -108,11 +104,7 @@ describe("HatsProtocol Gatekeeper", () => { // signup via MACI const tx = await maciContract .connect(voter) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId])); const receipt = await tx.wait(); @@ -123,11 +115,7 @@ describe("HatsProtocol Gatekeeper", () => { await expect( maciContract .connect(voter) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId])), ).to.be.revertedWithCustomError(hatsGatekeeperSingle, "AlreadyRegistered"); }); }); @@ -186,11 +174,7 @@ describe("HatsProtocol Gatekeeper", () => { await expect( maciContract .connect(signer) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])), ).to.be.revertedWithCustomError(hatsGatekeeperMultiple, "OnlyMACI"); }); @@ -200,11 +184,7 @@ describe("HatsProtocol Gatekeeper", () => { // signup via MACI const tx = await maciContract .connect(signer) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId])); const receipt = await tx.wait(); @@ -215,11 +195,7 @@ describe("HatsProtocol Gatekeeper", () => { await expect( maciContract .connect(voter) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [secondHatId]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [secondHatId])), ).to.be.revertedWithCustomError(hatsGatekeeperMultiple, "NotCriterionHat"); }); @@ -230,11 +206,7 @@ describe("HatsProtocol Gatekeeper", () => { await expect( maciContract .connect(another) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [thirdHatId]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [thirdHatId])), ).to.be.revertedWithCustomError(hatsGatekeeperMultiple, "NotWearingCriterionHat"); }); @@ -242,11 +214,7 @@ describe("HatsProtocol Gatekeeper", () => { await expect( maciContract .connect(signer) - .signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + .signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId])), ).to.be.revertedWithCustomError(hatsGatekeeperMultiple, "AlreadyRegistered"); }); }); diff --git a/packages/contracts/tests/gatekeepers/MerkleProofGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/MerkleProofGatekeeper.test.ts index bf3200b830..db340b0d8f 100644 --- a/packages/contracts/tests/gatekeepers/MerkleProofGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/MerkleProofGatekeeper.test.ts @@ -102,7 +102,6 @@ describe("MerkleProof Gatekeeper", () => { const tx = await maciContract.signUp( user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["bytes32[]"], [validProof]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), ); const receipt = await tx.wait(); @@ -115,7 +114,6 @@ describe("MerkleProof Gatekeeper", () => { maciContract.signUp( user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["bytes32[]"], [validProof]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), ), ).to.be.revertedWithCustomError(merkleProofGatekeeper, "AlreadyRegistered"); }); diff --git a/packages/contracts/tests/gatekeepers/SemaphoreGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/SemaphoreGatekeeper.test.ts index 05d8a7fc4f..afc60c612d 100644 --- a/packages/contracts/tests/gatekeepers/SemaphoreGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/SemaphoreGatekeeper.test.ts @@ -112,32 +112,20 @@ describe("Semaphore Gatekeeper", () => { await semaphoreGatekeeper.setMaciInstance(await maciContract.getAddress()).then((tx) => tx.wait()); await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedProofInvalidGroupId, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + maciContract.signUp(user.pubKey.asContractParam(), encodedProofInvalidGroupId), ).to.be.revertedWithCustomError(semaphoreGatekeeper, "InvalidGroup"); }); it("should revert if the proof is invalid (mock)", async () => { await mockSemaphore.flipValid(); await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedInvalidProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + maciContract.signUp(user.pubKey.asContractParam(), encodedInvalidProof), ).to.be.revertedWithCustomError(semaphoreGatekeeper, "InvalidProof"); await mockSemaphore.flipValid(); }); it("should register a user if the register function is called with the valid data", async () => { - const tx = await maciContract.signUp( - user.pubKey.asContractParam(), - encodedProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + const tx = await maciContract.signUp(user.pubKey.asContractParam(), encodedProof); const receipt = await tx.wait(); @@ -145,13 +133,10 @@ describe("Semaphore Gatekeeper", () => { }); it("should prevent signing up twice", async () => { - await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - encodedProof, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), - ).to.be.revertedWithCustomError(semaphoreGatekeeper, "AlreadyRegistered"); + await expect(maciContract.signUp(user.pubKey.asContractParam(), encodedProof)).to.be.revertedWithCustomError( + semaphoreGatekeeper, + "AlreadyRegistered", + ); }); }); }); diff --git a/packages/contracts/tests/gatekeepers/SignUpGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/SignUpGatekeeper.test.ts index 8b69986c20..024b088b0f 100644 --- a/packages/contracts/tests/gatekeepers/SignUpGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/SignUpGatekeeper.test.ts @@ -64,11 +64,7 @@ describe("SignUpGatekeeper", () => { await signUpToken.giveToken(await signer.getAddress(), 0); await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - ), + maciContract.signUp(user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1])), ).to.be.revertedWithCustomError(signUpTokenGatekeeperContract, "OnlyMACI"); }); @@ -82,7 +78,6 @@ describe("SignUpGatekeeper", () => { const tx = await maciContract.signUp( user.pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [0]), - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), ); const receipt = await tx.wait(); diff --git a/packages/contracts/tests/gatekeepers/ZupassGatekeeper.test.ts b/packages/contracts/tests/gatekeepers/ZupassGatekeeper.test.ts index 6d766a973a..058564b036 100644 --- a/packages/contracts/tests/gatekeepers/ZupassGatekeeper.test.ts +++ b/packages/contracts/tests/gatekeepers/ZupassGatekeeper.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { AbiCoder, Signer, ZeroAddress } from "ethers"; +import { Signer, ZeroAddress } from "ethers"; import { Keypair } from "maci-domainobjs"; import { deployContract } from "../../ts/deploy"; @@ -92,20 +92,12 @@ describe("Zupass Gatekeeper", () => { await zupassGatekeeper.setMaciInstance(await maciContract.getAddress()).then((tx) => tx.wait()); await expect( - maciContract.signUp( - user.pubKey.asContractParam(), - dataWithInvalidWatermark, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ), + maciContract.signUp(user.pubKey.asContractParam(), dataWithInvalidWatermark), ).to.be.revertedWithCustomError(zupassGatekeeper, "InvalidWatermark"); }); it("should register a user if the register function is called with the valid data", async () => { - const tx = await maciContract.signUp( - user.pubKey.asContractParam(), - data, - AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), - ); + const tx = await maciContract.signUp(user.pubKey.asContractParam(), data); const receipt = await tx.wait(); @@ -113,9 +105,10 @@ describe("Zupass Gatekeeper", () => { }); it("should prevent signing up twice", async () => { - await expect( - maciContract.signUp(user.pubKey.asContractParam(), data, AbiCoder.defaultAbiCoder().encode(["uint256"], [1])), - ).to.be.revertedWithCustomError(zupassGatekeeper, "AlreadyRegistered"); + await expect(maciContract.signUp(user.pubKey.asContractParam(), data)).to.be.revertedWithCustomError( + zupassGatekeeper, + "AlreadyRegistered", + ); }); }); }); diff --git a/packages/contracts/tests/utils.ts b/packages/contracts/tests/utils.ts index 035d559402..d7a83d5cce 100644 --- a/packages/contracts/tests/utils.ts +++ b/packages/contracts/tests/utils.ts @@ -86,14 +86,12 @@ export const deployTestContracts = async ({ // VkRegistry const vkRegistryContract = await deployVkRegistry(signer, true); - const [gatekeeperContractAddress, constantInitialVoiceCreditProxyContractAddress] = await Promise.all([ + const [gatekeeperContractAddress] = await Promise.all([ gatekeeperContract.getAddress(), - constantInitialVoiceCreditProxyContract.getAddress(), ]); const { maciContract } = await deployMaci({ signUpTokenGatekeeperContractAddress: gatekeeperContractAddress, - initialVoiceCreditBalanceAddress: constantInitialVoiceCreditProxyContractAddress, signer, stateTreeDepth, factories, diff --git a/packages/contracts/ts/deploy.ts b/packages/contracts/ts/deploy.ts index 7b652912ff..1f40edf7c3 100644 --- a/packages/contracts/ts/deploy.ts +++ b/packages/contracts/ts/deploy.ts @@ -294,7 +294,6 @@ export const deployPollFactory = async ( */ export const deployMaci = async ({ signUpTokenGatekeeperContractAddress, - initialVoiceCreditBalanceAddress, signer, poseidonAddresses, stateTreeDepth = 10, @@ -360,7 +359,6 @@ export const deployMaci = async ({ messageProcessorAddress, tallyAddress, signUpTokenGatekeeperContractAddress, - initialVoiceCreditBalanceAddress, stateTreeDepth, emptyBallotRoots, ); diff --git a/packages/contracts/ts/genMaciState.ts b/packages/contracts/ts/genMaciState.ts index 3e0bfb6120..60df574527 100644 --- a/packages/contracts/ts/genMaciState.ts +++ b/packages/contracts/ts/genMaciState.ts @@ -78,9 +78,7 @@ export const genMaciStateFromContract = async ( data: { stateIndex: Number(event.args._stateIndex), pubKey: new PubKey([BigInt(event.args._userPubKeyX), BigInt(event.args._userPubKeyY)]), - voiceCreditBalance: Number(event.args._voiceCreditBalance), timestamp: Number(event.args._timestamp), - stateLeaf: BigInt(event.args._stateLeaf), }, }); }); @@ -156,7 +154,7 @@ export const genMaciStateFromContract = async ( const pubKeyY = BigInt(event.args._pollPubKeyY); const timestamp = Number(event.args._timestamp); - const newVoiceCreditBalance = BigInt(event.args._newVoiceCreditBalance); + const voiceCreditBalance = BigInt(event.args._voiceCreditBalance); actions.push({ type: "PollJoined", @@ -164,7 +162,7 @@ export const genMaciStateFromContract = async ( transactionIndex: event.transactionIndex, data: { pubKey: new PubKey([pubKeyX, pubKeyY]), - newVoiceCreditBalance, + newVoiceCreditBalance: voiceCreditBalance, timestamp, nullifier, }, @@ -245,9 +243,9 @@ export const genMaciStateFromContract = async ( sortActions(actions).forEach((action) => { switch (true) { case action.type === "SignUp": { - const { pubKey, voiceCreditBalance, timestamp, stateLeaf } = action.data; + const { pubKey } = action.data; - maciState.signUp(pubKey!, BigInt(voiceCreditBalance!), BigInt(timestamp!), stateLeaf); + maciState.signUp(pubKey!); break; } diff --git a/packages/contracts/ts/genSignUpTree.ts b/packages/contracts/ts/genSignUpTree.ts index 598c47b795..c16b8241d2 100644 --- a/packages/contracts/ts/genSignUpTree.ts +++ b/packages/contracts/ts/genSignUpTree.ts @@ -1,7 +1,7 @@ /* eslint-disable no-underscore-dangle */ import { LeanIMT, LeanIMTHashFunction } from "@zk-kit/lean-imt"; -import { hashLeanIMT } from "maci-crypto"; -import { PubKey, StateLeaf, blankStateLeaf, blankStateLeafHash } from "maci-domainobjs"; +import { hashLeanIMT, hashLeftRight, PAD_KEY_HASH } from "maci-crypto"; +import { PubKey } from "maci-domainobjs"; import { assert } from "console"; @@ -32,8 +32,8 @@ export const genSignUpTree = async ({ const maciContract = MACIFactory.connect(address, provider); const signUpTree = new LeanIMT(hashLeanIMT as LeanIMTHashFunction); - signUpTree.insert(blankStateLeafHash); - const stateLeaves: StateLeaf[] = [blankStateLeaf]; + signUpTree.insert(PAD_KEY_HASH); + const pubKeys: PubKey[] = []; // Fetch event logs in batches (lastBlock inclusive) for (let i = fromBlock; i <= lastBlock; i += blocksPerRequest + 1) { @@ -47,14 +47,11 @@ export const genSignUpTree = async ({ assert(!!event); const pubKeyX = event.args._userPubKeyX; const pubKeyY = event.args._userPubKeyY; - const voiceCreditBalance = event.args._voiceCreditBalance; - const timestamp = event.args._timestamp; const pubKey = new PubKey([pubKeyX, pubKeyY]); - const stateLeaf = new StateLeaf(pubKey, voiceCreditBalance, timestamp); - stateLeaves.push(stateLeaf); - signUpTree.insert(event.args._stateLeaf); + pubKeys.push(pubKey); + signUpTree.insert(hashLeftRight(pubKeyX, pubKeyY)); }); if (sleepAmount) { @@ -64,6 +61,6 @@ export const genSignUpTree = async ({ } return { signUpTree, - stateLeaves, + pubKeys, }; }; diff --git a/packages/contracts/ts/types.ts b/packages/contracts/ts/types.ts index 505054a970..aa14b57385 100644 --- a/packages/contracts/ts/types.ts +++ b/packages/contracts/ts/types.ts @@ -14,7 +14,7 @@ import type { } from "../typechain-types"; import type { BigNumberish, Provider, Signer, ContractFactory } from "ethers"; import type { CircuitInputs } from "maci-core"; -import type { Message, PubKey, StateLeaf } from "maci-domainobjs"; +import type { Message, PubKey } from "maci-domainobjs"; import type { PublicSignals } from "snarkjs"; /** @@ -141,11 +141,6 @@ export interface IDeployMaciArgs { */ signUpTokenGatekeeperContractAddress: string; - /** - * The address of the ConstantInitialVoiceCreditProxy contract - */ - initialVoiceCreditBalanceAddress: string; - /** * The signer to use to deploy the contract */ @@ -238,5 +233,5 @@ export interface IGenSignUpTree { /** * State leaves */ - stateLeaves: StateLeaf[]; + pubKeys: PubKey[]; } diff --git a/packages/core/ts/MaciState.ts b/packages/core/ts/MaciState.ts index 506c51fbb1..c676837980 100644 --- a/packages/core/ts/MaciState.ts +++ b/packages/core/ts/MaciState.ts @@ -1,5 +1,5 @@ -import { hash4, IncrementalQuinTree } from "maci-crypto"; -import { type PubKey, type Keypair, StateLeaf, blankStateLeaf } from "maci-domainobjs"; +import { IncrementalQuinTree } from "maci-crypto"; +import { PubKey, type Keypair, padKey } from "maci-domainobjs"; import type { IJsonMaciState, IJsonPoll, IMaciState, TreeDepths } from "./utils/types"; @@ -13,8 +13,8 @@ export class MaciState implements IMaciState { // a MaciState can hold multiple polls polls: Map = new Map(); - // the leaves of the state tree - stateLeaves: StateLeaf[] = []; + // the public keys of the users + pubKeys: PubKey[] = []; // how deep the state tree is stateTreeDepth: number; @@ -37,29 +37,22 @@ export class MaciState implements IMaciState { this.stateTreeDepth = stateTreeDepth; // we put a blank state leaf to prevent a DoS attack - this.stateLeaves.push(blankStateLeaf); + this.pubKeys.push(padKey); // we need to increase the number of signups by one given // that we already added the blank leaf this.numSignUps += 1; } /** - * Sign up a user with the given public key, initial voice credit balance, and timestamp. + * Sign up a user with the given public key. * @param pubKey - The public key of the user. - * @param initialVoiceCreditBalance - The initial voice credit balance of the user. - * @param timestamp - The timestamp of the sign-up. - * @param stateLeaf - The hash state leaf. * @returns The index of the newly signed-up user in the state tree. */ - signUp(pubKey: PubKey, initialVoiceCreditBalance: bigint, timestamp: bigint, stateLeaf?: bigint): number { + signUp(pubKey: PubKey): number { this.numSignUps += 1; - const stateLeafObj = new StateLeaf(pubKey, initialVoiceCreditBalance, timestamp); - const pubKeyAsArray = pubKey.asArray(); - const stateLeafHash = - stateLeaf || hash4([pubKeyAsArray[0], pubKeyAsArray[1], initialVoiceCreditBalance, timestamp]); - this.stateTree?.insert(stateLeafHash); - return this.stateLeaves.push(stateLeafObj.copy()) - 1; + this.stateTree?.insert(pubKey.hash()); + return this.pubKeys.push(pubKey.copy()) - 1; } /** @@ -105,7 +98,7 @@ export class MaciState implements IMaciState { copy = (): MaciState => { const copied = new MaciState(this.stateTreeDepth); - copied.stateLeaves = this.stateLeaves.map((x: StateLeaf) => x.copy()); + copied.pubKeys = this.pubKeys.map((x: PubKey) => x.copy()); copied.polls = new Map(Array.from(this.polls, ([key, value]) => [key, value.copy()])); @@ -121,7 +114,7 @@ export class MaciState implements IMaciState { const result = this.stateTreeDepth === m.stateTreeDepth && this.polls.size === m.polls.size && - this.stateLeaves.length === m.stateLeaves.length; + this.pubKeys.length === m.pubKeys.length; if (!result) { return false; @@ -132,8 +125,8 @@ export class MaciState implements IMaciState { return false; } } - for (let i = 0; i < this.stateLeaves.length; i += 1) { - if (!this.stateLeaves[i].equals(m.stateLeaves[i])) { + for (let i = 0; i < this.pubKeys.length; i += 1) { + if (!this.pubKeys[i].equals(m.pubKeys[i])) { return false; } } @@ -149,7 +142,7 @@ export class MaciState implements IMaciState { return { stateTreeDepth: this.stateTreeDepth, polls: Array.from(this.polls.values()).map((poll) => poll.toJSON()), - stateLeaves: this.stateLeaves.map((leaf) => leaf.toJSON()), + pubKeys: this.pubKeys.map((pubKey) => pubKey.toJSON()), pollBeingProcessed: Boolean(this.pollBeingProcessed), currentPollBeingProcessed: this.currentPollBeingProcessed ? this.currentPollBeingProcessed.toString() : "", numSignUps: this.numSignUps, @@ -165,7 +158,7 @@ export class MaciState implements IMaciState { const maciState = new MaciState(json.stateTreeDepth); // assign the json values to the new instance - maciState.stateLeaves = json.stateLeaves.map((leaf) => StateLeaf.fromJSON(leaf)); + maciState.pubKeys = json.pubKeys.map((pubKey) => PubKey.fromJSON(pubKey)); maciState.pollBeingProcessed = json.pollBeingProcessed; maciState.currentPollBeingProcessed = BigInt(json.currentPollBeingProcessed); maciState.numSignUps = json.numSignUps; diff --git a/packages/core/ts/Poll.ts b/packages/core/ts/Poll.ts index 1511410425..53b55fee33 100644 --- a/packages/core/ts/Poll.ts +++ b/packages/core/ts/Poll.ts @@ -25,6 +25,7 @@ import { type IMessageContractParams, type IJsonPCommand, blankStateLeafHash, + padKey, } from "maci-domainobjs"; import assert from "assert"; @@ -81,7 +82,7 @@ export class Poll implements IPoll { stateCopied = false; - stateLeaves: StateLeaf[] = [blankStateLeaf]; + pubKeys: PubKey[] = [padKey]; stateTree?: LeanIMT; @@ -202,6 +203,9 @@ export class Poll implements IPoll { * Update a Poll with data from MaciState. * This is the step where we copy the state from the MaciState instance, * and set the number of signups we have so far. + * @note It should be called to generate the state for poll joining with numSignups set as + * the number of signups in the MaciState. For message processing, you should set numSignups as + * the number of users who joined the poll. */ updatePoll = (numSignups: bigint): void => { // there might be occasions where we fetch logs after new signups have been made @@ -215,14 +219,15 @@ export class Poll implements IPoll { // start by setting the number of signups this.setNumSignups(numSignups); // copy up to numSignups state leaves - this.stateLeaves = this.maciStateRef.stateLeaves.slice(0, Number(this.numSignups)).map((x) => x.copy()); + this.pubKeys = this.maciStateRef.pubKeys.slice(0, Number(this.numSignups)).map((x) => x.copy()); // ensure we have the correct actual state tree depth value this.actualStateTreeDepth = Math.max(1, Math.ceil(Math.log2(Number(this.numSignups)))); this.stateTree = new LeanIMT(hashLeanIMT as LeanIMTHashFunction); + // add all leaves - this.stateLeaves.forEach((stateLeaf) => { - this.stateTree?.insert(stateLeaf.hash()); + this.pubKeys.forEach((pubKey) => { + this.stateTree?.insert(pubKey.hash()); }); // create a poll state tree @@ -243,7 +248,7 @@ export class Poll implements IPoll { this.ballotTree.insert(this.emptyBallotHash); // we fill the ballotTree with empty ballots hashes to match the number of signups in the tree - while (this.ballots.length < this.stateLeaves.length) { + while (this.ballots.length < this.pubKeys.length) { this.ballotTree.insert(this.emptyBallotHash); this.ballots.push(this.emptyBallot); } @@ -334,7 +339,7 @@ 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 + // would result in a different state leaf const originalStateLeafPathElements = this.pollStateTree?.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 @@ -428,7 +433,6 @@ export class Poll implements IPoll { * Create circuit input for pollJoining * @param maciPrivKey User's private key for signing up * @param stateLeafIndex Index where the user is stored in the state leaves - * @param credits Credits for voting * @param pollPrivKey Poll's private key for the poll joining * @param pollPubKey Poll's public key for the poll joining * @returns stringified circuit inputs @@ -436,19 +440,9 @@ export class Poll implements IPoll { joiningCircuitInputs = ({ maciPrivKey, stateLeafIndex, - credits, pollPrivKey, pollPubKey, }: IJoiningCircuitArgs): IPollJoiningCircuitInputs => { - // Get the state leaf on the index position - const stateLeaf = this.stateLeaves[Number(stateLeafIndex)]; - const { pubKey, voiceCreditBalance, timestamp } = stateLeaf; - const pubKeyX = pubKey.asArray()[0]; - const pubKeyY = pubKey.asArray()[1]; - const stateLeafArray = [pubKeyX, pubKeyY, voiceCreditBalance, timestamp]; - - assert(credits <= voiceCreditBalance, "Credits must be lower than signed up credits"); - // calculate the path elements for the state tree given the original state tree const { siblings, index } = this.stateTree!.generateProof(Number(stateLeafIndex)); const siblingsLength = siblings.length; @@ -483,11 +477,9 @@ export class Poll implements IPoll { privKey: maciPrivKey.asCircuitInputs(), pollPrivKey: pollPrivKey.asCircuitInputs(), pollPubKey: pollPubKey.asCircuitInputs(), - stateLeaf: stateLeafArray, siblings: siblingsArray, indices, nullifier, - credits, stateRoot, actualStateTreeDepth, }; @@ -1303,7 +1295,7 @@ export class Poll implements IPoll { this.maciStateRef, ); - copied.stateLeaves = this.stateLeaves.map((x) => x.copy()); + copied.pubKeys = this.pubKeys.map((x) => x.copy()); copied.pollStateLeaves = this.pollStateLeaves.map((x) => x.copy()); copied.messages = this.messages.map((x) => x.copy()); copied.commands = this.commands.map((x) => x.copy()); @@ -1400,7 +1392,7 @@ export class Poll implements IPoll { ballots: this.ballots.map((ballot) => ballot.toJSON()), encPubKeys: this.encPubKeys.map((encPubKey) => encPubKey.serialize()), currentMessageBatchIndex: this.currentMessageBatchIndex, - stateLeaves: this.stateLeaves.map((leaf) => leaf.toJSON()), + pubKeys: this.pubKeys.map((leaf) => leaf.toJSON()), pollStateLeaves: this.pollStateLeaves.map((leaf) => leaf.toJSON()), results: this.tallyResult.map((result) => result.toString()), numBatchesProcessed: this.numBatchesProcessed, diff --git a/packages/core/ts/__benchmarks__/index.ts b/packages/core/ts/__benchmarks__/index.ts index ef9bb44ccf..4e92a12ef8 100644 --- a/packages/core/ts/__benchmarks__/index.ts +++ b/packages/core/ts/__benchmarks__/index.ts @@ -29,7 +29,7 @@ export default function runCore(): void { const userKeypair = new Keypair(); users.push(userKeypair); - maciState.signUp(userKeypair.pubKey, VOICE_CREDIT_BALANCE, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(userKeypair.pubKey); } const pollId = maciState.deployPoll( @@ -40,7 +40,7 @@ export default function runCore(): void { ); const poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); // 4 valid votes for (let i = 0; i < MESSAGE_BATCH_SIZE - 1; i += 1) { diff --git a/packages/core/ts/__tests__/MaciState.test.ts b/packages/core/ts/__tests__/MaciState.test.ts index 1c6ce09c10..b41d7bcbef 100644 --- a/packages/core/ts/__tests__/MaciState.test.ts +++ b/packages/core/ts/__tests__/MaciState.test.ts @@ -7,7 +7,7 @@ import { MaciState } from "../MaciState"; import { STATE_TREE_DEPTH } from "../utils/constants"; import { IJsonMaciState } from "../utils/types"; -import { coordinatorKeypair, duration, messageBatchSize, treeDepths, voiceCreditBalance } from "./utils/constants"; +import { coordinatorKeypair, duration, messageBatchSize, treeDepths } from "./utils/constants"; describe("MaciState", function test() { this.timeout(100000); @@ -26,7 +26,7 @@ describe("MaciState", function test() { before(() => { m1 = new MaciState(STATE_TREE_DEPTH); - m1.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + m1.signUp(userKeypair.pubKey); pollId = m1.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), treeDepths, @@ -53,14 +53,9 @@ describe("MaciState", function test() { // modify user.pubKey const m3 = m1.copy(); - m3.stateLeaves[0].pubKey = new Keypair().pubKey; + m3.pubKeys[0] = new Keypair().pubKey; expect(m1.equals(m3)).not.to.eq(true); - // modify user.voiceCreditBalance - const m4 = m1.copy(); - m4.stateLeaves[0].voiceCreditBalance = BigInt(m4.stateLeaves[0].voiceCreditBalance) + 1n; - expect(m1.equals(m4)).not.to.eq(true); - // modify poll.coordinatorKeypair const m6 = m1.copy(); m6.polls.get(pollId)!.coordinatorKeypair = new Keypair(); diff --git a/packages/core/ts/__tests__/Poll.test.ts b/packages/core/ts/__tests__/Poll.test.ts index 2be83d427f..955c4d2774 100644 --- a/packages/core/ts/__tests__/Poll.test.ts +++ b/packages/core/ts/__tests__/Poll.test.ts @@ -24,10 +24,10 @@ describe("Poll", function test() { const user1Keypair = new Keypair(); // signup the user - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); // copy the state from the MaciState ref - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); const { privKey } = user1Keypair; const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair(); @@ -244,11 +244,11 @@ describe("Poll", function test() { ); const poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); const user1Keypair = new Keypair(); // signup the user - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); const { privKey } = user1Keypair; const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair(); @@ -291,7 +291,7 @@ describe("Poll", function test() { poll.publishMessage(message, ecdhKeypair.pubKey); poll.publishMessage(message, ecdhKeypair.pubKey); - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); expect(() => { poll.processMessage(message, ecdhKeypair.pubKey); @@ -323,9 +323,9 @@ describe("Poll", function test() { const user1Keypair = new Keypair(); // signup the user - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); const { privKey } = user1Keypair; const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair(); @@ -418,11 +418,11 @@ describe("Poll", function test() { const user2Keypair = new Keypair(); // signup the user - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); - maciState.signUp(user2Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user2Keypair.pubKey); - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); const { privKey: privKey1 } = user1Keypair; const { privKey: pollPrivKey1, pubKey: pollPubKey1 } = new Keypair(); @@ -481,7 +481,7 @@ describe("Poll", function test() { ); const secondPoll = maciState.polls.get(secondPollId)!; - secondPoll.updatePoll(BigInt(maciState.stateLeaves.length)); + secondPoll.updatePoll(BigInt(maciState.pubKeys.length)); const stateIndex2 = secondPoll.joinPoll(nullifier2, pollPubKey2, voiceCreditBalance, timestamp2); @@ -548,11 +548,11 @@ describe("Poll", function test() { coordinatorKeypair, ); - maciState.signUp(new Keypair().pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(new Keypair().pubKey); const poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); expect(poll.getNumSignups()).to.eq(2n); diff --git a/packages/core/ts/__tests__/e2e.test.ts b/packages/core/ts/__tests__/e2e.test.ts index 3827ed5feb..f9a5384342 100644 --- a/packages/core/ts/__tests__/e2e.test.ts +++ b/packages/core/ts/__tests__/e2e.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; -import { hash5, IncrementalQuinTree, hash2, poseidon } from "maci-crypto"; -import { PCommand, Keypair, StateLeaf, blankStateLeafHash } from "maci-domainobjs"; +import { hash5, IncrementalQuinTree, poseidon, PAD_KEY_HASH, hashLeanIMT } from "maci-crypto"; +import { PCommand, Keypair, blankStateLeafHash, padKey } from "maci-domainobjs"; import { MaciState } from "../MaciState"; import { Poll } from "../Poll"; @@ -8,6 +8,7 @@ import { STATE_TREE_DEPTH, STATE_TREE_ARITY } from "../utils/constants"; import { coordinatorKeypair, duration, messageBatchSize, treeDepths, voiceCreditBalance } from "./utils/constants"; import { TestHarness, calculateTotal } from "./utils/utils"; +import { LeanIMT, LeanIMTHashFunction } from "@zk-kit/lean-imt"; describe("MaciState/Poll e2e", function test() { this.timeout(300000); @@ -46,8 +47,8 @@ describe("MaciState/Poll e2e", function test() { before(() => { // Sign up - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); - maciState.signUp(user2Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); + maciState.signUp(user2Keypair.pubKey); // deploy a poll pollId = maciState.deployPoll( @@ -57,7 +58,7 @@ describe("MaciState/Poll e2e", function test() { coordinatorKeypair, ); - maciState.polls.get(pollId)?.updatePoll(BigInt(maciState.stateLeaves.length)); + maciState.polls.get(pollId)?.updatePoll(BigInt(maciState.pubKeys.length)); }); it("should submit a vote for each user", () => { const poll = maciState.polls.get(pollId)!; @@ -141,8 +142,8 @@ describe("MaciState/Poll e2e", function test() { before(() => { // Sign up - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); - maciState.signUp(user2Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); + maciState.signUp(user2Keypair.pubKey); // deploy a poll pollId = maciState.deployPoll( @@ -153,7 +154,7 @@ describe("MaciState/Poll e2e", function test() { ); poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); }); it("should submit a vote for each user", () => { user1StateIndex = poll.joinPoll(nullifier1, pollPubKey1, voiceCreditBalance, timestamp1); @@ -252,7 +253,7 @@ describe("MaciState/Poll e2e", function test() { before(() => { // Sign up - maciState.signUp(user1Keypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(user1Keypair.pubKey); // deploy a poll pollId = maciState.deployPoll( @@ -263,7 +264,7 @@ describe("MaciState/Poll e2e", function test() { ); poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); }); it("should submit a vote for one user in one batch", () => { @@ -357,17 +358,14 @@ describe("MaciState/Poll e2e", function test() { // The end result should be that option 0 gets 3 votes // because the user spends 9 voice credits on it it("the state root should be correct", () => { - const timestamp = BigInt(Math.floor(Date.now() / 1000)); - const stateLeaf = new StateLeaf(userKeypair.pubKey, voiceCreditBalance, timestamp); + stateIndex = maciState.signUp(userKeypair.pubKey); + poll.updatePoll(BigInt(maciState.pubKeys.length)); - stateIndex = maciState.signUp(userKeypair.pubKey, voiceCreditBalance, timestamp); - poll.updatePoll(BigInt(maciState.stateLeaves.length)); - - const stateTree = new IncrementalQuinTree(poll.actualStateTreeDepth, blankStateLeafHash, STATE_TREE_ARITY, hash2); - - stateTree.insert(blankStateLeafHash); - stateTree.insert(stateLeaf.hash()); + const stateTree = new LeanIMT(hashLeanIMT as LeanIMTHashFunction); + stateTree.insert(PAD_KEY_HASH); + stateTree.insert(userKeypair.pubKey.hash()); + expect(stateIndex.toString()).to.eq("1"); expect(stateTree.root.toString()).to.eq(poll.stateTree?.root.toString()); }); @@ -434,7 +432,7 @@ describe("MaciState/Poll e2e", function test() { const pollKeypair = new Keypair(); pollKeys.push(pollKeypair); - maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + maciState.signUp(userKeypair.pubKey); } pollId = maciState.deployPoll( @@ -444,7 +442,7 @@ describe("MaciState/Poll e2e", function test() { coordinatorKeypair, ); poll = maciState.polls.get(pollId)!; - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); for (let i = 0; i < messageBatchSize - 1; i += 1) { const nullifier = poseidon([BigInt(pollKeys[i].privKey.rawPrivKey.toString())]); @@ -592,14 +590,11 @@ describe("MaciState/Poll e2e", function test() { poll = maciState.polls.get(pollId)!; - const timestamp = BigInt(Math.floor(Date.now() / 1000)); - const stateLeaf = new StateLeaf(userKeypair.pubKey, voiceCreditBalance, timestamp); - - maciState.signUp(userKeypair.pubKey, voiceCreditBalance, timestamp); - stateTree.insert(blankStateLeafHash); - stateTree.insert(stateLeaf.hash()); + maciState.signUp(userKeypair.pubKey); + stateTree.insert(PAD_KEY_HASH); + stateTree.insert(userKeypair.pubKey.hash()); - poll.updatePoll(BigInt(maciState.stateLeaves.length)); + poll.updatePoll(BigInt(maciState.pubKeys.length)); const { privKey } = userKeypair; const { privKey: pollPrivKey, pubKey: pollPubKey } = new Keypair(); @@ -697,7 +692,7 @@ describe("MaciState/Poll e2e", function test() { const pollStateIndex = testHarness.joinPoll(nullifier, pollKeypair.pubKey, voiceCreditBalance, timestamp); testHarness.vote(pollKeypair, pollStateIndex, voteOptionIndex, voteWeight, nonce); - poll.updatePoll(BigInt(testHarness.maciState.stateLeaves.length)); + poll.updatePoll(BigInt(testHarness.maciState.pubKeys.length)); poll.processMessages(testHarness.pollId); expect(() => { diff --git a/packages/core/ts/__tests__/utils/utils.ts b/packages/core/ts/__tests__/utils/utils.ts index 85f86ffb15..c31b356cae 100644 --- a/packages/core/ts/__tests__/utils/utils.ts +++ b/packages/core/ts/__tests__/utils/utils.ts @@ -5,7 +5,7 @@ import { MaciState } from "../../MaciState"; import { Poll } from "../../Poll"; import { STATE_TREE_DEPTH } from "../../utils/constants"; -import { duration, messageBatchSize, treeDepths, voiceCreditBalance } from "./constants"; +import { duration, messageBatchSize, treeDepths } from "./constants"; /** * Calculates the total of a tally result @@ -63,11 +63,7 @@ export class TestHarness { * @param user - The keypair of the user. * @returns The index of the newly signed-up user in the state tree. */ - signup = (user: Keypair): number => { - const timestamp = BigInt(Math.floor(Date.now() / 1000)); - const stateIndex = this.maciState.signUp(user.pubKey, voiceCreditBalance, timestamp); - return stateIndex; - }; + signup = (user: Keypair): number => this.maciState.signUp(user.pubKey); joinPoll = (nullifier: bigint, pubKey: PubKey, newVoiceCreditBalance: bigint, timestamp: bigint): number => this.poll.joinPoll(nullifier, pubKey, newVoiceCreditBalance, timestamp); @@ -142,7 +138,7 @@ export class TestHarness { * This should be called after all votes have been cast. */ finalizePoll = (): void => { - this.poll.updatePoll(BigInt(this.maciState.stateLeaves.length)); + this.poll.updatePoll(BigInt(this.maciState.pubKeys.length)); this.poll.processMessages(this.pollId); this.poll.tallyVotes(); }; diff --git a/packages/core/ts/utils/types.ts b/packages/core/ts/utils/types.ts index d1b9a79708..d7f569f782 100644 --- a/packages/core/ts/utils/types.ts +++ b/packages/core/ts/utils/types.ts @@ -5,6 +5,7 @@ import type { Ballot, IJsonBallot, IJsonPCommand, + IJsonPublicKey, IJsonStateLeaf, Keypair, Message, @@ -95,7 +96,7 @@ export interface IJsonPoll { ballots: IJsonBallot[]; encPubKeys: string[]; currentMessageBatchIndex: number; - stateLeaves: IJsonStateLeaf[]; + pubKeys: IJsonPublicKey[]; pollStateLeaves: IJsonStateLeaf[]; results: string[]; numBatchesProcessed: number; @@ -110,7 +111,7 @@ export interface IJsonPoll { export interface IJsonMaciState { stateTreeDepth: number; polls: IJsonPoll[]; - stateLeaves: IJsonStateLeaf[]; + pubKeys: IJsonPublicKey[]; pollBeingProcessed: boolean; currentPollBeingProcessed: string; numSignUps: number; @@ -138,7 +139,6 @@ export interface IProcessMessagesOutput { export interface IJoiningCircuitArgs { maciPrivKey: PrivKey; stateLeafIndex: bigint; - credits: bigint; pollPrivKey: PrivKey; pollPubKey: PubKey; } @@ -153,7 +153,6 @@ export interface IPollJoiningCircuitInputs { siblings: string[][]; indices: string[]; nullifier: string; - credits: string; stateRoot: string; actualStateTreeDepth: string; } diff --git a/packages/crypto/ts/constants.ts b/packages/crypto/ts/constants.ts index ce10528f2f..3118f7a65b 100644 --- a/packages/crypto/ts/constants.ts +++ b/packages/crypto/ts/constants.ts @@ -10,3 +10,5 @@ export const SNARK_FIELD_SIZE = r; export const NOTHING_UP_MY_SLEEVE = BigInt(keccak256(toUtf8Bytes("Maci"))) % SNARK_FIELD_SIZE; assert(NOTHING_UP_MY_SLEEVE === BigInt("8370432830353022751713833565135785980866757267633941821328460903436894336785")); + +export const PAD_KEY_HASH = BigInt("1309255631273308531193241901289907343161346846555918942743921933037802809814"); diff --git a/packages/crypto/ts/index.ts b/packages/crypto/ts/index.ts index bfaf8e003c..8ed437e972 100644 --- a/packages/crypto/ts/index.ts +++ b/packages/crypto/ts/index.ts @@ -4,7 +4,7 @@ export { IncrementalQuinTree } from "./quinTree"; export { bigInt2Buffer, stringifyBigInts, unstringifyBigInts, deepCopyBigIntArray } from "./bigIntUtils"; -export { NOTHING_UP_MY_SLEEVE, SNARK_FIELD_SIZE } from "./constants"; +export { NOTHING_UP_MY_SLEEVE, SNARK_FIELD_SIZE, PAD_KEY_HASH } from "./constants"; export { genPrivKey, diff --git a/packages/domainobjs/ts/constants.ts b/packages/domainobjs/ts/constants.ts index aae4709cb1..bc727d7b03 100644 --- a/packages/domainobjs/ts/constants.ts +++ b/packages/domainobjs/ts/constants.ts @@ -1,4 +1,6 @@ +import { PubKey } from "./publicKey"; import { StateLeaf } from "./stateLeaf"; export const blankStateLeaf = StateLeaf.genBlankLeaf(); export const blankStateLeafHash = blankStateLeaf.hash(); +export const padKey = PubKey.genPadKey(); diff --git a/packages/domainobjs/ts/index.ts b/packages/domainobjs/ts/index.ts index ad8081b858..6aabb2e6dc 100644 --- a/packages/domainobjs/ts/index.ts +++ b/packages/domainobjs/ts/index.ts @@ -10,7 +10,7 @@ export { Keypair } from "./keyPair"; export { StateLeaf } from "./stateLeaf"; -export { blankStateLeaf, blankStateLeafHash } from "./constants"; +export { blankStateLeaf, blankStateLeafHash, padKey } from "./constants"; export type { Proof, diff --git a/packages/domainobjs/ts/publicKey.ts b/packages/domainobjs/ts/publicKey.ts index 905107975f..92b4e707ee 100644 --- a/packages/domainobjs/ts/publicKey.ts +++ b/packages/domainobjs/ts/publicKey.ts @@ -135,4 +135,21 @@ export class PubKey { static fromJSON(json: IJsonPublicKey): PubKey { return PubKey.deserialize(json.pubKey); } + + /** + * Generate a default pad key + * @returns a default pad key + */ + static genPadKey(): PubKey { + // This public key is the first Pedersen base + // point from iden3's circomlib implementation of the Pedersen hash. + // Since it is generated using a hash-to-curve function, we are + // confident that no-one knows the private key associated with this + // public key. See: + // https://github.com/iden3/circomlib/blob/d5ed1c3ce4ca137a6b3ca48bec4ac12c1b38957a/src/pedersen_printbases.js + return new PubKey([ + BigInt("10457101036533406547632367118273992217979173478358440826365724437999023779287"), + BigInt("19824078218392094440610104313265183977899662750282163392862422243483260492317"), + ]); + } } diff --git a/packages/integrationTests/ts/__tests__/integration.test.ts b/packages/integrationTests/ts/__tests__/integration.test.ts index c3666f40f5..1f6d6050b3 100644 --- a/packages/integrationTests/ts/__tests__/integration.test.ts +++ b/packages/integrationTests/ts/__tests__/integration.test.ts @@ -34,7 +34,6 @@ import { VOTE_OPTION_TREE_DEPTH, duration, initialVoiceCredits, - ivcpData, maxMessages, } from "./utils/constants"; import { ITestSuite } from "./utils/interfaces"; @@ -168,7 +167,6 @@ describe("Integration tests", function test() { maciPubKey: user.keypair.pubKey.serialize(), maciAddress: contracts.maciAddress, sgDataArg: SG_DATA, - ivcpDataArg: ivcpData, signer, }).then((result) => result.stateIndex), ); @@ -191,12 +189,11 @@ describe("Integration tests", function test() { ), rapidsnark: `${homedir()}/rapidsnark/build/prover`, signer, - newVoiceCreditBalance: BigInt(initialVoiceCredits), quiet: true, }); // signup on local maci state - maciState.signUp(user.keypair.pubKey, BigInt(initialVoiceCredits), BigInt(timestamp)); + maciState.signUp(user.keypair.pubKey); // join the poll on local const inputNullifier = BigInt(user.keypair.privKey.asCircuitInputs()); diff --git a/packages/integrationTests/ts/__tests__/maci-keys.test.ts b/packages/integrationTests/ts/__tests__/maci-keys.test.ts index 081553e52f..97d59a8b97 100644 --- a/packages/integrationTests/ts/__tests__/maci-keys.test.ts +++ b/packages/integrationTests/ts/__tests__/maci-keys.test.ts @@ -72,7 +72,7 @@ describe("integration tests private/public/keypair", () => { before(async () => { signer = await getDefaultSigner(); - const { maci, verifier, vkRegistry, gatekeeper } = await deployTestContracts( + const { maci, verifier, vkRegistry, gatekeeper, initialVoiceCreditProxy } = await deployTestContracts( initialVoiceCredits, STATE_TREE_DEPTH, signer, @@ -92,6 +92,7 @@ describe("integration tests private/public/keypair", () => { vkRegistry, EMode.NON_QV, gatekeeper, + initialVoiceCreditProxy, ); // we know it's the first poll so id is 0 diff --git a/packages/integrationTests/ts/__tests__/utils/interfaces.ts b/packages/integrationTests/ts/__tests__/utils/interfaces.ts index 647a0b6ee5..abaf203580 100644 --- a/packages/integrationTests/ts/__tests__/utils/interfaces.ts +++ b/packages/integrationTests/ts/__tests__/utils/interfaces.ts @@ -1,4 +1,5 @@ import { MACI, Verifier, VkRegistry, FreeForAllGatekeeper } from "maci-contracts"; +import { ConstantInitialVoiceCreditProxy } from "maci-contracts/typechain-types"; /** * A util interface that represents a vote object @@ -48,4 +49,5 @@ export interface IDeployedTestContracts { verifier: Verifier; vkRegistry: VkRegistry; gatekeeper: FreeForAllGatekeeper; + initialVoiceCreditProxy: ConstantInitialVoiceCreditProxy; } diff --git a/packages/integrationTests/ts/__tests__/utils/utils.ts b/packages/integrationTests/ts/__tests__/utils/utils.ts index 5b59cb923b..5b435907aa 100644 --- a/packages/integrationTests/ts/__tests__/utils/utils.ts +++ b/packages/integrationTests/ts/__tests__/utils/utils.ts @@ -185,14 +185,12 @@ export const deployTestContracts = async ( // VkRegistry const vkRegistryContract = await deployVkRegistry(signer, true); - const [gatekeeperContractAddress, constantInitialVoiceCreditProxyContractAddress] = await Promise.all([ + const [gatekeeperContractAddress] = await Promise.all([ gatekeeperContract.getAddress(), - constantInitialVoiceCreditProxyContract.getAddress(), ]); const { maciContract } = await deployMaci({ signUpTokenGatekeeperContractAddress: gatekeeperContractAddress, - initialVoiceCreditBalanceAddress: constantInitialVoiceCreditProxyContractAddress, signer, stateTreeDepth, quiet, @@ -203,5 +201,6 @@ export const deployTestContracts = async ( verifier: mockVerifierContract as Verifier, vkRegistry: vkRegistryContract, gatekeeper: gatekeeperContract, + initialVoiceCreditProxy: constantInitialVoiceCreditProxyContract, }; };