Skip to content

Commit

Permalink
feat(cli): add get poll cli command
Browse files Browse the repository at this point in the history
- [x] Add get poll cli command
- [x] Add indexed params for deploy poll event
  • Loading branch information
0xmad committed Feb 20, 2024
1 parent 4444a4c commit 08075ea
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 11 deletions.
2 changes: 2 additions & 0 deletions cli/testScript.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ node build/ts/index.js create -s 10
node build/ts/index.js deployPoll \
--pubkey macipk.ea638a3366ed91f2e955110888573861f7c0fc0bb5fb8b8dca9cd7a08d7d6b93 \
-t 30 -i 1 -m 2 -b 1 -v 2 -se false
node build/ts/index.js getPoll \
--quiet false
node build/ts/index.js signup \
--pubkey macipk.e743ffb5298ef0f5c1f63b6464a48fea19ea7ee5a885c67ae1b24a1d04f03f07
node build/ts/index.js isRegisteredUser \
Expand Down
58 changes: 58 additions & 0 deletions cli/tests/unit/poll.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { expect } from "chai";
import { getDefaultSigner } from "maci-contracts";

import type { Signer } from "ethers";

import { deploy, deployPoll, deployVkRegistryContract, setVerifyingKeys, getPoll } from "../../ts/commands";
import { DeployedContracts, PollContracts } from "../../ts/utils";
import { deployPollArgs, setVerifyingKeysArgs, deployArgs } from "../constants";
import { cleanVanilla } from "../utils";

describe("poll", () => {
let maciAddresses: DeployedContracts;
let pollAddresses: PollContracts;
let signer: Signer;

// before all tests we deploy the vk registry contract and set the verifying keys
before(async () => {
signer = await getDefaultSigner();

// we deploy the vk registry contract
await deployVkRegistryContract({ signer });
// we set the verifying keys
await setVerifyingKeys({ ...setVerifyingKeysArgs, signer });
});

describe("check deploy and get poll", () => {
after(() => {
cleanVanilla();
});

before(async () => {
// deploy the smart contracts
maciAddresses = await deploy({ ...deployArgs, signer });
// deploy a poll contract
pollAddresses = await deployPoll({ ...deployPollArgs, signer });
});

it("should get current poll properly", async () => {
const pollData = await getPoll({ maciAddress: maciAddresses.maciAddress, signer });
const samePollData = await getPoll({ maciAddress: maciAddresses.maciAddress, pollId: pollData.id, signer });

expect(pollData.address).to.eq(pollAddresses.poll);
expect(pollData).to.deep.eq(samePollData);
});

it("should throw error if current poll id is invalid", async () => {
await expect(getPoll({ maciAddress: maciAddresses.maciAddress, pollId: -1n, signer })).eventually.rejectedWith(
"Invalid poll id",
);
});

it("should throw error if current poll is not deployed", async () => {
await expect(getPoll({ maciAddress: maciAddresses.maciAddress, pollId: 9000n, signer })).eventually.rejectedWith(
"MACI contract doesn't have any deployed poll",
);
});
});
});
1 change: 1 addition & 0 deletions cli/ts/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { airdrop } from "./airdrop";
export { deploy } from "./deploy";
export { deployPoll } from "./deployPoll";
export { getPoll } from "./poll";
export { deployVkRegistryContract } from "./deployVkRegistry";
export { genKeyPair } from "./genKeyPair";
export { genMaciPubKey } from "./genPubKey";
Expand Down
60 changes: 60 additions & 0 deletions cli/ts/commands/poll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ZeroAddress } from "ethers";
import { MACI__factory as MACIFactory, Poll__factory as PollFactory } from "maci-contracts";

import type { IGetPollArgs, IGetPollData } from "../utils/interfaces";

import { banner } from "../utils/banner";
import { logError, logGreen, success } from "../utils/theme";

/**
* Get deployed poll from MACI contract
* @param {IGetPollArgs} args - The arguments for the get poll command
* @returns {IGetPollData} poll data
*/
export const getPoll = async ({ maciAddress, signer, pollId, quiet = true }: IGetPollArgs): Promise<IGetPollData> => {
banner(quiet);

const maciContract = MACIFactory.connect(maciAddress, signer);
const id =
pollId === undefined ? await maciContract.nextPollId().then((nextPollId) => nextPollId - 1n) : BigInt(pollId);

if (id < 0n) {
logError("Invalid poll id");
}

const pollAddress = await maciContract.polls(id);

if (pollAddress === ZeroAddress) {
logError("MACI contract doesn't have any deployed poll");
}

const pollContract = PollFactory.connect(pollAddress, signer);

const [[deployTime, duration], isStateAqMerged, numSignups] = await Promise.all([
pollContract.getDeployTimeAndDuration(),
pollContract.stateAqMerged(),
pollContract.numSignups(),
]);

logGreen(
quiet,
success(
[
`ID: ${id}`,
`Deploy time: ${new Date(Number(deployTime) * 1000).toString()}`,
`End time: ${new Date(Number(deployTime + duration) * 1000).toString()}`,
`Number of signups ${numSignups}`,
`State Aq merged: ${isStateAqMerged}`,
].join("\n"),
),
);

return {
id,
address: pollAddress,
deployTime,
duration,
numSignups,
isStateAqMerged,
};
};
8 changes: 4 additions & 4 deletions cli/ts/commands/signup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MACI__factory as MACIFactory } from "maci-contracts/typechain-types";
import { PubKey } from "maci-domainobjs";

import type { IRegisteredUserArgs, SignupArgs } from "../utils/interfaces";
import type { IRegisteredUserArgs, ISignupData, SignupArgs } from "../utils/interfaces";
import type { ContractTransactionReceipt } from "ethers";

import { banner } from "../utils/banner";
Expand All @@ -11,8 +11,8 @@ import { info, logError, logGreen, logYellow, success } from "../utils/theme";

/**
* Signup a user to the MACI contract
* @param SignupArgs - The arguments for the signup command
* @returns The state index of the user and transaction hash
* @param {SignupArgs} args - The arguments for the signup command
* @returns {ISignupData} The state index of the user and transaction hash
*/
export const signup = async ({
maciPubKey,
Expand All @@ -21,7 +21,7 @@ export const signup = async ({
ivcpDataArg,
signer,
quiet = true,
}: SignupArgs): Promise<{ stateIndex: string; hash: string }> => {
}: SignupArgs): Promise<ISignupData> => {
banner(quiet);

// validate user key
Expand Down
25 changes: 25 additions & 0 deletions cli/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
deploy,
showContracts,
deployPoll,
getPoll,
mergeMessages,
publish,
setVerifyingKeys,
Expand Down Expand Up @@ -432,6 +433,29 @@ program
program.error((error as Error).message, { exitCode: 1 });
}
});
program
.command("getPoll")
.description("Get deployed poll from MACI contract")
.option("-p, --poll <poll>", "the poll id")
.option("-x, --maci-address <maciAddress>", "the MACI contract address")
.option("-q, --quiet <quiet>", "whether to print values to the console", (value) => value === "true", false)
.action(async (cmdObj) => {
try {
const signer = await getSigner();
const network = await signer.provider?.getNetwork();

const maciContractAddress = cmdObj.maciAddress || readContractAddress("MACI", network?.name);

await getPoll({
pollId: cmdObj.poll,
maciAddress: maciContractAddress,
signer,
quiet: cmdObj.quiet,
});
} catch (error) {
program.error((error as Error).message, { exitCode: 1 });
}
});
program
.command("topup")
.description("Top up an account with voice credits")
Expand Down Expand Up @@ -691,6 +715,7 @@ export {
checkVerifyingKeys,
deploy,
deployPoll,
getPoll,
deployVkRegistryContract,
fundWallet,
genLocalState,
Expand Down
9 changes: 6 additions & 3 deletions cli/ts/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { genKeyPair } from "../commands/genKeyPair";
import { genMaciPubKey } from "../commands/genPubKey";
import { getPoll } from "../commands/poll";
import { publish } from "../commands/publish";
import { signup, isRegisteredUser } from "../commands/signup";
import { verify } from "../commands/verify";

export { genKeyPair, genMaciPubKey, publish, signup, isRegisteredUser, verify };
export { genKeyPair, genMaciPubKey, publish, signup, isRegisteredUser, verify, getPoll };

export type { Signer } from "ethers";

export type {
DeployedContracts,
PollContracts,
TallyData,
SubsidyData,
PublishArgs,
SignupArgs,
ISignupData,
VerifyArgs,
IGetPollArgs,
IGetPollData,
IRegisteredUserArgs,
} from "../utils";
3 changes: 3 additions & 0 deletions cli/ts/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type {
FundWalletArgs,
TimeTravelArgs,
SignupArgs,
ISignupData,
SetVerifyingKeysArgs,
MergeMessagesArgs,
MergeSignupsArgs,
Expand All @@ -40,6 +41,8 @@ export type {
SubsidyData,
IRegisteredUserArgs,
IGenKeypairArgs,
IGetPollArgs,
IGetPollData,
} from "./interfaces";
export { compareVks } from "./vks";
export { delay } from "./time";
Expand Down
75 changes: 75 additions & 0 deletions cli/ts/utils/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,21 @@ export interface SignupArgs {
quiet?: boolean;
}

/**
* Interface for the return data to the signup command
*/
export interface ISignupData {
/**
* The state index of the user
*/
stateIndex: string;

/**
* The signup transaction hash
*/
hash: string;
}

/**
* Interface for the arguments to the register check command
*/
Expand All @@ -885,6 +900,66 @@ export interface IRegisteredUserArgs {
quiet?: boolean;
}

/**
* Interface for the arguments to the get poll command
*/
export interface IGetPollArgs {
/**
* A signer object
*/
signer: Signer;

/**
* The address of the MACI contract
*/
maciAddress: string;

/**
* The poll id. If not specified, latest poll id will be used
*/
pollId?: BigNumberish;

/**
* Whether to log the output
*/
quiet?: boolean;
}

/**
* Interface for the return data to the get poll command
*/
export interface IGetPollData {
/**
* The poll id
*/
id: BigNumberish;

/**
* The poll address
*/
address: string;

/**
* The poll deployment time
*/
deployTime: BigNumberish;

/**
* The poll duration
*/
duration: BigNumberish;

/**
* The poll number of signups
*/
numSignups: BigNumberish;

/**
* Whether the MACI contract's stateAq has been merged by this contract
*/
isStateAqMerged: boolean;
}

/**
* Interface for the arguments to the topup command
*/
Expand Down
9 changes: 7 additions & 2 deletions contracts/contracts/MACI.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@ contract MACI is IMACI, Params, Utilities, Ownable {
uint256 _voiceCreditBalance,
uint256 _timestamp
);
event DeployPoll(uint256 _pollId, PubKey _pubKey, PollContracts pollAddr);
event DeployPoll(
uint256 _pollId,
uint256 indexed _coordinatorPubKeyX,
uint256 indexed _coordinatorPubKeyY,
PollContracts pollAddr
);

/// @notice Only allow a Poll contract to call the modified function.
modifier onlyPoll(uint256 _pollId) {
Expand Down Expand Up @@ -254,7 +259,7 @@ contract MACI is IMACI, Params, Utilities, Ownable {
// store the addresses in a struct so they can be returned
pollAddr = PollContracts({ poll: p, messageProcessor: mp, tally: tally, subsidy: subsidy });

emit DeployPoll(pollId, _coordinatorPubKey, pollAddr);
emit DeployPoll(pollId, _coordinatorPubKey.x, _coordinatorPubKey.y, pollAddr);
}

/// @inheritdoc IMACI
Expand Down
5 changes: 3 additions & 2 deletions contracts/ts/genMaciState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ export const genMaciStateFromContract = async (
const mutableLogs = { ...log, topics: [...log.topics] };
const event = maciIface.parseLog(mutableLogs) as unknown as {
args: {
_pubKey: string[];
_coordinatorPubKeyX: string;
_coordinatorPubKeyY: string;
_pollId: bigint;
pollAddr: {
poll: string;
Expand All @@ -127,7 +128,7 @@ export const genMaciStateFromContract = async (
};
};

const pubKey = new PubKey(event.args._pubKey.map((x) => BigInt(x.toString())) as [bigint, bigint]);
const pubKey = new PubKey([BigInt(event.args._coordinatorPubKeyX), BigInt(event.args._coordinatorPubKeyY)]);

const p = event.args._pollId;
assert(p === index);
Expand Down

0 comments on commit 08075ea

Please sign in to comment.