Skip to content

Commit

Permalink
chore(contracts): deploy poll
Browse files Browse the repository at this point in the history
- [x] Add deploy vk registry task
- [x] Add deploy poll task
- [x] Simplify deploy runner
  • Loading branch information
0xmad committed Feb 6, 2024
1 parent f69ad70 commit f3ae8f8
Show file tree
Hide file tree
Showing 16 changed files with 480 additions and 110 deletions.
1 change: 1 addition & 0 deletions contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ MNEMONIC=
ETHERSCAN_API_KEY=
INFURA_KEY=
OP_RPC_URL=
GAS_PRICE=
14 changes: 14 additions & 0 deletions contracts/deploy-config-example.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@
"MACI": {
"stateTreeDepth": 10,
"gatekeeper": "EASGatekeeper"
},
"VkRegistry": {
"stateTreeDepth": 10,
"intStateTreeDepth": 1,
"messageTreeDepth": 2,
"voteOptionTreeDepth": 2,
"messageBatchDepth": 1,
"processMessagesZkey": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey",
"tallyVotesZkey": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey"
},
"Poll": {
"pollDuration": 30,
"coordinatorPubkey": "macipk.ea638a3366ed91f2e955110888573861f7c0fc0bb5fb8b8dca9cd7a08d7d6b93",
"subsidyEnabled": false
}
}
}
8 changes: 7 additions & 1 deletion contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { HardhatUserConfig } from "hardhat/config";
import "./tasks/deploy";
import { EChainId, ESupportedChains, NETWORKS_DEFAULT_GAS, getNetworkRpcUrls } from "./tasks/helpers/constants";
import "./tasks/runner/deployFull";
import "./tasks/runner/deployPoll";
import "./tasks/runner/verifyFull";

dotenv.config();
Expand All @@ -24,7 +25,7 @@ const getCommonNetworkConfig = (networkName: ESupportedChains, chainId: number,
url: NETWORKS_RPC_URL[networkName],
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
gasMultiplier: DEFAULT_GAS_MUL,
gasPrice: NETWORKS_DEFAULT_GAS[networkName],
gasPrice: process.env.GAS_PRICE ? Number(process.env.GAS_PRICE) : NETWORKS_DEFAULT_GAS[networkName],
saveDeployments: true,
chainId,
accounts: {
Expand All @@ -45,9 +46,14 @@ const config: HardhatUserConfig = {
},
},
},
defaultNetwork: "localhost",
networks: {
sepolia: getCommonNetworkConfig(ESupportedChains.Sepolia, EChainId.Sepolia),
coverage: getCommonNetworkConfig(ESupportedChains.Coverage, EChainId.Coverage, TEST_MNEMONIC),
localhost: {
url: "http://localhost:8545",
loggingEnabled: false,
},
hardhat: {
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
gasMultiplier: DEFAULT_GAS_MUL,
Expand Down
5 changes: 4 additions & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@
"test:subsidy": "hardhat test ./tests/Subsidy.test.ts",
"test:eas_gatekeeper": "hardhat test ./tests/EASGatekeeper.test.ts",
"deploy": "hardhat deploy-full",
"deploy-poll": "hardhat deploy-poll",
"verify": "hardhat verify-full",
"deploy:hardhat": "pnpm run deploy",
"deploy:localhost": "pnpm run deploy",
"deploy:sepolia": "pnpm run deploy --network sepolia",
"deploy-poll:localhost": "pnpm run deploy-poll",
"deploy-poll:sepolia": "pnpm run deploy-poll --network sepolia",
"verify:sepolia": "pnpm run verify --network sepolia"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion contracts/tasks/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from "path";
/**
* The same as individual imports but doesn't require to add new import line everytime
*/
["maci"].forEach((folder) => {
["maci", "poll"].forEach((folder) => {
const tasksPath = path.resolve(__dirname, folder);

if (fs.existsSync(tasksPath)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const storage = ContractStorage.getInstance();
*/
deployment
.deployTask("full:deploy-constant-initial-voice-credit-proxy", "Deploy constant initial voice credit proxy")
.setAction(async ({ incremental }: IDeployParams & { amount: number }, hre) => {
.setAction(async ({ incremental }: IDeployParams, hre) => {
deployment.setHre(hre);
const deployer = await deployment.getDeployer();

Expand Down
90 changes: 90 additions & 0 deletions contracts/tasks/deploy/maci/11-vkRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { extractVk } from "maci-circuits";
import { VerifyingKey } from "maci-domainobjs";

import type { IVerifyingKeyStruct } from "../../../ts";
import type { BaseContract, BaseContractMethod, BigNumberish, TransactionResponse } from "ethers";

import { ContractStorage } from "../../helpers/ContractStorage";
import { Deployment } from "../../helpers/Deployment";
import { EContracts, IDeployParams } from "../../helpers/types";

// Add type manually because typechain is not available during compilation
interface VkRegistry extends BaseContract {
setVerifyingKeys: BaseContractMethod<
[BigNumberish, BigNumberish, BigNumberish, BigNumberish, BigNumberish, IVerifyingKeyStruct, IVerifyingKeyStruct],
TransactionResponse
>;
setSubsidyKeys: BaseContractMethod<
[BigNumberish, BigNumberish, BigNumberish, IVerifyingKeyStruct],
TransactionResponse
>;
}

const deployment = Deployment.getInstance();
const storage = ContractStorage.getInstance();

/**
* Deploy step registration and task itself
*/
deployment
.deployTask("full:deploy-vk-registry", "Deploy Vk Registry and set keys")
.setAction(async ({ incremental }: IDeployParams, hre) => {
deployment.setHre(hre);
const deployer = await deployment.getDeployer();

const vkRegistryContractAddress = storage.getAddress(EContracts.VkRegistry, hre.network.name);

if (incremental && vkRegistryContractAddress) {
return;
}

const stateTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "stateTreeDepth");
const intStateTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "intStateTreeDepth");
const messageTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "messageTreeDepth");
const messageBatchDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "messageBatchDepth");
const voteOptionTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "voteOptionTreeDepth");
const processMessagesZkeyPath = deployment.getDeployConfigField<string>(
EContracts.VkRegistry,
"processMessagesZkey",
);
const tallyVotesZkeyPath = deployment.getDeployConfigField<string>(EContracts.VkRegistry, "tallyVotesZkey");
const subsidyZkeyPath = deployment.getDeployConfigField<string | null>(EContracts.VkRegistry, "subsidyZkey");

const [processVk, tallyVk, subsidyVk] = await Promise.all([
extractVk(processMessagesZkeyPath),
extractVk(tallyVotesZkeyPath),
subsidyZkeyPath && extractVk(subsidyZkeyPath),
]).then((vks) => vks.map((vk) => (vk ? VerifyingKey.fromObj(vk) : null)));

const vkRegistryContract = await deployment.deployContract<VkRegistry>(EContracts.VkRegistry, deployer);

await vkRegistryContract
.setVerifyingKeys(
stateTreeDepth,
intStateTreeDepth,
messageTreeDepth,
voteOptionTreeDepth,
5 ** messageBatchDepth,
processVk!.asContractParam() as IVerifyingKeyStruct,
tallyVk!.asContractParam() as IVerifyingKeyStruct,
)
.then((tx) => tx.wait());

if (subsidyVk) {
await vkRegistryContract
.setSubsidyKeys(
stateTreeDepth,
intStateTreeDepth,
voteOptionTreeDepth,
subsidyVk.asContractParam() as IVerifyingKeyStruct,
)
.then((tx) => tx.wait());
}

await storage.register({
id: EContracts.VkRegistry,
contract: vkRegistryContract,
args: [],
network: hre.network.name,
});
});
133 changes: 133 additions & 0 deletions contracts/tasks/deploy/poll/01-poll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* eslint-disable no-console */
import { BaseContract, BaseContractMethod, BigNumberish, TransactionResponse } from "ethers";
import { IG1ContractParams, PubKey } from "maci-domainobjs";

import { parseArtifact } from "../../../ts/abi";
import { ContractStorage } from "../../helpers/ContractStorage";
import { Deployment } from "../../helpers/Deployment";
import { EContracts } from "../../helpers/types";

const deployment = Deployment.getInstance();
const storage = ContractStorage.getInstance();

// Add type manually because typechain is not available during compilation
type TDeployPollParams = [
BigNumberish,
{
intStateTreeDepth: BigNumberish;
messageTreeSubDepth: BigNumberish;
messageTreeDepth: BigNumberish;
voteOptionTreeDepth: BigNumberish;
},
IG1ContractParams,
string,
string,
boolean,
];

interface MACI extends BaseContract {
nextPollId: () => Promise<bigint>;
stateAq: () => Promise<string>;
deployPoll: BaseContractMethod<TDeployPollParams, TransactionResponse & [string]>;
}

interface StateAq extends BaseContract {
treeMerged: () => Promise<boolean>;
}

interface Poll extends BaseContract {
maxValues: () => Promise<[BigNumberish, BigNumberish]>;
extContracts: () => Promise<[string, string, string]>;
}

/**
* Deploy step registration and task itself
*/
deployment.deployTask("poll:deploy-poll", "Deploy poll").setAction(async (_, hre) => {
deployment.setHre(hre);
const deployer = await deployment.getDeployer();

const maciContractAddress = storage.getAddress(EContracts.MACI, hre.network.name);
const verifierContractAddress = storage.getAddress(EContracts.Verifier, hre.network.name);
const vkRegistryContractAddress = storage.getAddress(EContracts.VkRegistry, hre.network.name);

if (!maciContractAddress) {
throw new Error("Need to deploy MACI contract first");
}

if (!verifierContractAddress) {
throw new Error("Need to deploy Verifier contract first");
}

if (!vkRegistryContractAddress) {
throw new Error("Need to deploy VkRegistry contract first");
}

const [maciAbi] = parseArtifact("MACI");
const maciContract = new BaseContract(maciContractAddress, maciAbi, deployer) as MACI;
const pollId = await maciContract.nextPollId();
const stateAqContractAddress = await maciContract.stateAq();

const [stateAqAbi] = parseArtifact("AccQueue");
const stateAq = new BaseContract(stateAqContractAddress, stateAqAbi, deployer) as StateAq;
const isTreeMerged = await stateAq.treeMerged();

if (pollId > 0n && !isTreeMerged) {
console.log("Previous poll is not completed");
return;
}

const coordinatorPubkey = deployment.getDeployConfigField<string>(EContracts.Poll, "coordinatorPubkey");
const pollDuration = deployment.getDeployConfigField<number>(EContracts.Poll, "pollDuration");
const intStateTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "intStateTreeDepth");
const messageTreeSubDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "messageBatchDepth");
const messageTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "messageTreeDepth");
const voteOptionTreeDepth = deployment.getDeployConfigField<number>(EContracts.VkRegistry, "voteOptionTreeDepth");
const subsidyEnabled = deployment.getDeployConfigField<boolean | null>(EContracts.Poll, "subsidyEnabled") ?? false;
const unserializedKey = PubKey.deserialize(coordinatorPubkey);

const deployPollParams: TDeployPollParams = [
pollDuration,
{
intStateTreeDepth,
messageTreeSubDepth,
messageTreeDepth,
voteOptionTreeDepth,
},
unserializedKey.asContractParam(),
verifierContractAddress,
vkRegistryContractAddress,
subsidyEnabled,
];

const [pollAddress] = await maciContract.deployPoll.staticCall(...deployPollParams);
const tx = await maciContract.deployPoll(...deployPollParams);
const receipt = await tx.wait();

if (receipt?.status !== 1) {
throw new Error("Deploy poll transaction is failed");
}

const [pollAbi] = parseArtifact("Poll");
const pollContract = new BaseContract(pollAddress, pollAbi, deployer) as Poll;
const [maxValues, extContracts] = await Promise.all([pollContract.maxValues(), pollContract.extContracts()]);

await storage.register({
id: EContracts.Poll,
key: pollId,
contract: pollContract,
args: [
pollDuration,
maxValues.map((value) => value.toString()),
{
intStateTreeDepth,
messageTreeSubDepth,
messageTreeDepth,
voteOptionTreeDepth,
},
unserializedKey.asContractParam(),
extContracts,
],
network: hre.network.name,
});
});
40 changes: 28 additions & 12 deletions contracts/tasks/helpers/ContractStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { EContracts, IRegisterContract, IStorageInstanceEntry, IStorageName
type TStorage = Record<
string,
Partial<{
named: Record<string, IStorageNamedEntry>;
named: Record<string, IStorageNamedEntry | Record<string, IStorageNamedEntry>>;
instance: Record<string, IStorageInstanceEntry>;
verified: Record<string, boolean>;
}>
Expand Down Expand Up @@ -59,7 +59,7 @@ export class ContractStorage {
*
* @param {IRegisterContract} args - register arguments
*/
async register({ id, contract, network, args }: IRegisterContract): Promise<void> {
async register({ id, key, contract, network, args }: IRegisterContract): Promise<void> {
const contractAddress = await contract.getAddress();

const deploymentTx = contract.deploymentTransaction();
Expand Down Expand Up @@ -90,10 +90,12 @@ export class ContractStorage {

this.db.set(`${network}.instance.${contractAddress}`, logEntry).write();

const namedEntry = this.db.get(`${network}.named.${id}`).value() as IStorageNamedEntry | undefined;
const namedEntry = this.db.get(`${network}.named.${id}${key !== undefined ? `.poll-${key}` : ""}`).value() as
| IStorageNamedEntry
| undefined;
const count = namedEntry?.count ?? 0;
this.db
.set(`${network}.named.${id}`, {
.set(`${network}.named.${id}${key !== undefined ? `.poll-${key}` : ""}`, {
address: contractAddress,
count: count + 1,
})
Expand Down Expand Up @@ -142,8 +144,8 @@ export class ContractStorage {
* @param network - selected network
* @returns contract address
*/
getAddress(id: EContracts, network: string): string | undefined {
const collection = this.db.get(`${network}.named.${id}`);
getAddress(id: EContracts, network: string, key?: string): string | undefined {
const collection = this.db.get(`${network}.named.${id}${key !== undefined ? `.poll-${key}` : ""}`);
const namedEntry = collection.value() as IStorageNamedEntry | undefined;

return namedEntry?.address;
Expand Down Expand Up @@ -180,7 +182,7 @@ export class ContractStorage {

const entryMap = new Map<string, string>();
const { named, instance } = this.db.get(network).value();
const namedEntries = Object.entries<IStorageNamedEntry>(named || {});
const namedEntries = Object.entries<IStorageNamedEntry | Record<string, IStorageNamedEntry>>(named || {});
const instanceEntries = Object.entries<IStorageInstanceEntry>(instance || {});

let multiCount = 0;
Expand All @@ -190,12 +192,26 @@ export class ContractStorage {
return;
}

if (value.count > 1) {
console.log(`\t${key}: N=${value.count}`);
multiCount += 1;
if (typeof value.count === "number" && typeof value.address === "string") {
if (value.count > 1) {
console.log(`\t${key}: N=${value.count}`);
multiCount += 1;
} else {
console.log(`\t${key}: ${value.address}`);
entryMap.set(key, value.address);
}
} else {
console.log(`\t${key}: ${value.address}`);
entryMap.set(key, value.address);
const entries = Object.entries<IStorageNamedEntry>(value as Record<string, IStorageNamedEntry>);

entries.forEach(([id, nested]) => {
if (nested.count > 1) {
console.log(`\t${key}-${id}: N=${nested.count}`);
multiCount += 1;
} else {
console.log(`\t${key}-${id}: ${nested.address}`);
entryMap.set(id, nested.address);
}
});
}
});

Expand Down
Loading

0 comments on commit f3ae8f8

Please sign in to comment.