Skip to content

Commit

Permalink
chore(core): remove unnecessary code
Browse files Browse the repository at this point in the history
  • Loading branch information
baumstern authored and ctrlc03 committed Jan 10, 2024
1 parent fb8c927 commit b6a0d9e
Show file tree
Hide file tree
Showing 2 changed files with 356 additions and 4 deletions.
215 changes: 211 additions & 4 deletions core/ts/__tests__/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { expect } from "chai";
import { hash5, NOTHING_UP_MY_SLEEVE, IncrementalQuinTree, AccQueue } from "maci-crypto";
import { PCommand, Keypair, StateLeaf, blankStateLeafHash } from "maci-domainobjs";

import { MaciState } from "../MaciState";
import { coordinatorKeypair, duration, maxValues, messageBatchSize, treeDepths, voiceCreditBalance } from "./constants";
import { TestHarness, calculateTotal } from "./utils";

import { Poll } from "../Poll";
import { MaciState } from "../MaciState";

import { STATE_TREE_DEPTH, STATE_TREE_ARITY, STATE_TREE_SUBDEPTH } from "../utils/constants";
import { packProcessMessageSmallVals, unpackProcessMessageSmallVals } from "../utils/utils";

import { coordinatorKeypair, duration, maxValues, messageBatchSize, treeDepths, voiceCreditBalance } from "./constants";
import { calculateTotal } from "./utils";

describe("MaciState e2e", function test() {
this.timeout(300000);

Expand Down Expand Up @@ -594,4 +595,210 @@ describe("MaciState e2e", function test() {
}).to.throw();
});
});

describe("Sanity checks", () => {
let testHarness: TestHarness;
let poll: Poll;

beforeEach(async () => {
testHarness = new TestHarness();

poll = testHarness.poll;
});

it("processMessages() should process a valid message", async () => {
const voteOptionIndex = BigInt(0);
const voteWeight = BigInt(9);
const nonce = BigInt(1);

const users = testHarness.createUsers(1);
testHarness.vote(users[0], testHarness.getStateIndex(users[0]), voteOptionIndex, voteWeight, nonce);
testHarness.finalizePoll();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = users.length;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(9);
expect(tallyResult).to.eq(expectedTallyResult);
});

it("processMessages() should not process messages twice", async () => {
const voteOptionIndex = BigInt(0);
const voteWeight = BigInt(9);
const nonce = BigInt(1);

const users = testHarness.createUsers(1);
testHarness.vote(users[0], testHarness.getStateIndex(users[0]), voteOptionIndex, voteWeight, nonce);
poll.processMessages(testHarness.pollId);

expect(() => {
poll.processMessages(testHarness.pollId);
}).to.throw("No more messages to process");

poll.tallyVotes();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = users.length;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(9);
expect(tallyResult).to.eq(expectedTallyResult);
});

it("processMessages() should not process a message with an incorrect nonce", async () => {
const voteOptionIndex = BigInt(0);
const voteWeight = BigInt(9);

const users = testHarness.createUsers(5);
// generate a bunch of invalid votes with nonces that are not 1
let nonce: bigint;
for (let i = 0; i < users.length; i++) {
do {
nonce = BigInt(Math.floor(Math.random() * 100) - 50);
} while (nonce === BigInt(1));

testHarness.vote(users[i], testHarness.getStateIndex(users[i]), voteOptionIndex, voteWeight, nonce);
}

testHarness.finalizePoll();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = users.length;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(0);
expect(tallyResult).to.eq(expectedTallyResult);
});

// note: When voting, the voice credit is used. The amount of voice credit used is
// the square of the vote weight. Since the maximum voice credit is 100 here,
// the vote weight can only be a value between 1 and 10
// (as these are the square roots of numbers up to 100).
it("processMessages() should not process a message with an incorrect vote weight", async () => {
const voteOptionIndex = BigInt(0);
const nonce = BigInt(1);

const users = testHarness.createUsers(5);

// generate a bunch of invalid votes with vote weights that are not between 1 and 10
let voteWeight: bigint;
for (let i = 0; i < users.length; i++) {
do {
voteWeight = BigInt(Math.floor(Math.random() * 100) - 50);
} while (BigInt(1) <= voteWeight && voteWeight <= BigInt(10));

testHarness.vote(users[i], testHarness.getStateIndex(users[i]), voteOptionIndex, voteWeight, nonce);
}

testHarness.finalizePoll();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = users.length;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(0);
expect(tallyResult).to.eq(expectedTallyResult);
});

it("processMessages() should not process a message with an incorrect state tree index", async () => {
const voteOptionIndex = BigInt(0);
const nonce = BigInt(1);
const voteWeight = BigInt(9);
const numVotes = 5;

const users = testHarness.createUsers(5);

for (let i = 0; i < users.length; i++) {
// generate a bunch of invalid votes with incorrect state tree index
testHarness.vote(users[i], testHarness.getStateIndex(users[i]) + 1, voteOptionIndex, voteWeight, nonce);
}

testHarness.finalizePoll();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = numVotes;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(0);
expect(tallyResult).to.eq(expectedTallyResult);
});

it("processMessages() should not process a message with an incorrect signature", async () => {
const voteOptionIndex = BigInt(0);
const voteWeight = BigInt(9);
const nonce = BigInt(1);

const users = testHarness.createUsers(2);

const { command } = testHarness.createCommand(
users[0],
testHarness.getStateIndex(users[0]),
voteOptionIndex,
voteWeight,
nonce,
);

// create an invalid signature
const { signature: invalidSignature } = testHarness.createCommand(
users[1],
testHarness.getStateIndex(users[0]),
voteOptionIndex,
voteWeight,
nonce,
);

// sign the command with the invalid signature
const { message, encPubKey } = testHarness.createMessage(
command,
invalidSignature,
testHarness.coordinatorKeypair,
);

testHarness.poll.publishMessage(message, encPubKey);
testHarness.finalizePoll();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = users.length - 1;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(0);
expect(tallyResult).to.eq(expectedTallyResult);
});

it("processMessages() should not process a message with an invalid coordinator key", async () => {
const voteOptionIndex = BigInt(0);
const voteWeight = BigInt(9);
const nonce = BigInt(1);

const users = testHarness.createUsers(1);

const { command, signature } = testHarness.createCommand(
users[0],
testHarness.getStateIndex(users[0]),
voteOptionIndex,
voteWeight,
nonce,
);

const { message, encPubKey } = testHarness.createMessage(command, signature, new Keypair());

testHarness.poll.publishMessage(message, encPubKey);
testHarness.finalizePoll();

const messageLengthResult = poll.messages.length;
const expectedNumVotes = users.length;
expect(messageLengthResult).to.eq(expectedNumVotes);

const tallyResult = poll.tallyResult[0];
const expectedTallyResult = BigInt(0);
expect(tallyResult).to.eq(expectedTallyResult);
});
});
});
145 changes: 145 additions & 0 deletions core/ts/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,151 @@
import { Signature } from "maci-crypto";
import { PCommand, Message, Keypair, PubKey } from "maci-domainobjs";

import { duration, maxValues, messageBatchSize, treeDepths, voiceCreditBalance } from "./constants";

import { Poll } from "../Poll";
import { MaciState } from "../MaciState";

import { STATE_TREE_DEPTH } from "../utils/constants"

/**
* Calculates the total of a tally result
* @param tallyResult - the tally result
* @returns the total of the tally result
*/
export const calculateTotal = (tallyResult: bigint[]): bigint => tallyResult.reduce((acc, v) => acc + v, BigInt(0));

/**
* A test harness for the MACI contract.
*/
export class TestHarness {
maciState = new MaciState(STATE_TREE_DEPTH);
coordinatorKeypair = new Keypair();
poll: Poll;
pollId: number;
users: Keypair[] = [];
stateIndices = new Map<Keypair, number>();

/**
* Constructs a new TestHarness object.
*/
constructor() {
this.pollId = this.maciState.deployPoll(
BigInt(Math.floor(Date.now() / 1000) + duration),
maxValues,
treeDepths,
messageBatchSize,
this.coordinatorKeypair,
);
this.poll = this.maciState.polls[this.pollId];
}

/**
* Creates a number of users and signs them up to the MACI state tree.
* @param numUsers - The number of users to create.
* @returns The keypairs of the newly created users.
*/
createUsers = (numUsers: number): Keypair[] => {
for (let i = 0; i < numUsers; i++) {
const user = new Keypair();
this.users.push(user);
const stateIndex = this.signup(user);
this.stateIndices.set(user, stateIndex);
}
return this.users;
};

/**
* Signs up a user to the MACI state tree.
* @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;
};

/**
* Publishes a message to the MACI poll instance.
* @param user - The keypair of the user.
* @param stateIndex - The index of the user in the state tree.
* @param voteOptionIndex - The index of the vote option.
* @param voteWeight - The weight of the vote.
* @param nonce - The nonce of the vote.
*/
vote = (user: Keypair, stateIndex: number, voteOptionIndex: bigint, voteWeight: bigint, nonce: bigint): void => {
const { command, signature } = this.createCommand(user, stateIndex, voteOptionIndex, voteWeight, nonce);

const { message, encPubKey } = this.createMessage(command, signature, this.coordinatorKeypair);

this.poll.publishMessage(message, encPubKey);
};

/**
* Creates a message from a command and signature.
* @param command - The command to be encrypted.
* @param signature - The signature of the command signer.
* @param coordinatorKeypair - The keypair of the MACI round coordinator.
* @returns The message and the ephemeral public key used to encrypt the message.
*/
createMessage = (
command: PCommand,
signature: Signature,
coordinatorKeypair: Keypair,
): { message: Message; encPubKey: PubKey } => {
const ecdhKeypair = new Keypair();
const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, coordinatorKeypair.pubKey);
const message = command.encrypt(signature, sharedKey);
return { message, encPubKey: ecdhKeypair.pubKey };
};

/**
* Creates a command and signature.
* @param user - The keypair of the user.
* @param stateIndex - The index of the user in the state tree.
* @param voteOptionIndex - The index of the vote option.
* @param voteWeight - The weight of the vote.
* @param nonce - The nonce of the vote.
* @returns The command and signature of the command.
*/
createCommand = (
user: Keypair,
stateIndex: number,
voteOptionIndex: bigint,
voteWeight: bigint,
nonce: bigint,
): { command: PCommand; signature: Signature } => {
const command = new PCommand(
BigInt(stateIndex),
user.pubKey,
voteOptionIndex,
voteWeight,
nonce,
BigInt(this.pollId),
);

const signature = command.sign(user.privKey);

return { command, signature };
};

/**
* Finalizes the poll.
* This processes all messages and tallies the votes.
* This should be called after all votes have been cast.
*/
finalizePoll = (): void => {
this.poll.processMessages(this.pollId);
this.poll.tallyVotes();
};

/**
* Returns the state index of a signed-up user.
* @param user - The keypair of the user.
* @returns The state index of the user.
*/
getStateIndex = (user: Keypair): number => {
return this.stateIndices.get(user)!;
};
}

0 comments on commit b6a0d9e

Please sign in to comment.