Merge pull request #1 from yashgo0018/temp

partial commit
This commit is contained in:
Yash Goyal
2024-04-16 18:16:30 +05:30
committed by GitHub
16 changed files with 1918 additions and 558 deletions

View File

@@ -0,0 +1,496 @@
export default [
{
inputs: [],
name: "DepthCannotBeZero",
type: "error",
},
{
inputs: [
{
internalType: "uint256",
name: "_depth",
type: "uint256",
},
{
internalType: "uint256",
name: "max",
type: "uint256",
},
],
name: "DepthTooLarge",
type: "error",
},
{
inputs: [
{
internalType: "uint256",
name: "_depth",
type: "uint256",
},
{
internalType: "uint256",
name: "min",
type: "uint256",
},
],
name: "DepthTooSmall",
type: "error",
},
{
inputs: [],
name: "InvalidHashLength",
type: "error",
},
{
inputs: [
{
internalType: "uint256",
name: "_index",
type: "uint256",
},
],
name: "InvalidIndex",
type: "error",
},
{
inputs: [],
name: "InvalidLevel",
type: "error",
},
{
inputs: [],
name: "NothingToMerge",
type: "error",
},
{
inputs: [],
name: "SubDepthCannotBeZero",
type: "error",
},
{
inputs: [],
name: "SubTreesAlreadyMerged",
type: "error",
},
{
inputs: [],
name: "SubTreesNotMerged",
type: "error",
},
{
inputs: [
{
internalType: "uint256",
name: "_subDepth",
type: "uint256",
},
{
internalType: "uint256",
name: "max",
type: "uint256",
},
],
name: "SubdepthTooLarge",
type: "error",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "OwnershipTransferred",
type: "event",
},
{
inputs: [],
name: "MAX_DEPTH",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "calcMinHeight",
outputs: [
{
internalType: "uint256",
name: "depth",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_leaf",
type: "uint256",
},
],
name: "enqueue",
outputs: [
{
internalType: "uint256",
name: "leafIndex",
type: "uint256",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "fill",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_depth",
type: "uint256",
},
],
name: "getMainRoot",
outputs: [
{
internalType: "uint256",
name: "mainRoot",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getSmallSRTroot",
outputs: [
{
internalType: "uint256",
name: "smallSubTreeRoot",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getSrIndices",
outputs: [
{
internalType: "uint256",
name: "next",
type: "uint256",
},
{
internalType: "uint256",
name: "current",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_index",
type: "uint256",
},
],
name: "getSubRoot",
outputs: [
{
internalType: "uint256",
name: "subRoot",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256[2]",
name: "array",
type: "uint256[2]",
},
],
name: "hash2",
outputs: [
{
internalType: "uint256",
name: "result",
type: "uint256",
},
],
stateMutability: "pure",
type: "function",
},
{
inputs: [
{
internalType: "uint256[3]",
name: "array",
type: "uint256[3]",
},
],
name: "hash3",
outputs: [
{
internalType: "uint256",
name: "result",
type: "uint256",
},
],
stateMutability: "pure",
type: "function",
},
{
inputs: [
{
internalType: "uint256[4]",
name: "array",
type: "uint256[4]",
},
],
name: "hash4",
outputs: [
{
internalType: "uint256",
name: "result",
type: "uint256",
},
],
stateMutability: "pure",
type: "function",
},
{
inputs: [
{
internalType: "uint256[5]",
name: "array",
type: "uint256[5]",
},
],
name: "hash5",
outputs: [
{
internalType: "uint256",
name: "result",
type: "uint256",
},
],
stateMutability: "pure",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "left",
type: "uint256",
},
{
internalType: "uint256",
name: "right",
type: "uint256",
},
],
name: "hashLeftRight",
outputs: [
{
internalType: "uint256",
name: "result",
type: "uint256",
},
],
stateMutability: "pure",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_level",
type: "uint256",
},
{
internalType: "uint256",
name: "_leaf",
type: "uint256",
},
],
name: "hashLevelLeaf",
outputs: [
{
internalType: "uint256",
name: "_hash",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_subRoot",
type: "uint256",
},
],
name: "insertSubTree",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_depth",
type: "uint256",
},
],
name: "merge",
outputs: [
{
internalType: "uint256",
name: "root",
type: "uint256",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_numSrQueueOps",
type: "uint256",
},
],
name: "mergeSubRoots",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "numLeaves",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256[]",
name: "array",
type: "uint256[]",
},
],
name: "sha256Hash",
outputs: [
{
internalType: "uint256",
name: "result",
type: "uint256",
},
],
stateMutability: "pure",
type: "function",
},
{
inputs: [],
name: "subTreesMerged",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "treeMerged",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "view",
type: "function",
},
] as const;

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import { Dialog } from "@headlessui/react";
import { LuCross } from "react-icons/lu";
import { MdEdit } from "react-icons/md";
@@ -6,7 +6,15 @@ import { RxCross2 } from "react-icons/rx";
import Modal from "~~/components/Modal";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";
export default function Example({ show, setOpen }: { show: boolean; setOpen: (value: boolean) => void }) {
export default function Example({
show,
setOpen,
refetchPolls,
}: {
show: boolean;
setOpen: (value: boolean) => void;
refetchPolls: () => void;
}) {
const [pollData, setPollData] = useState({ title: "Dummy Title", options: [""] });
const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false);
@@ -41,19 +49,14 @@ export default function Example({ show, setOpen }: { show: boolean; setOpen: (va
const { writeAsync, data, isLoading } = useScaffoldContractWrite({
contractName: "PollManager",
functionName: "createPoll",
args: [pollData?.title, pollData?.options || [], "", 300n],
args: [pollData?.title, pollData?.options || [], "", 60n],
});
console.log(data);
useEffect(() => {
// setIsLoading(isLoading);
}, [isLoading]);
async function onSubmit() {
console.log("A");
try {
await writeAsync();
refetchPolls();
} catch (err) {
console.log(err);
}

View File

@@ -0,0 +1,126 @@
import { Dialog } from "@headlessui/react";
import { useContractRead } from "wagmi";
import AccQueueAbi from "~~/abi/AccQueue";
import PollAbi from "~~/abi/Poll";
import Modal from "~~/components/Modal";
import { useScaffoldContractRead } from "~~/hooks/scaffold-eth";
import { Poll } from "~~/types/poll";
import { mergeSignups } from "~~/utils/mergeSignups";
const stepNames = ["Merge SignUps", "Merge Main Roots", "Compute Main Root"];
export default function PollStatusModal({
poll,
show,
setOpen,
}: {
show: boolean;
setOpen: (value: boolean) => void;
poll: Poll | undefined;
}) {
// console.log("stateAq", stateAq, subTreesMerged);
const { data: stateAq } = useScaffoldContractRead({ contractName: "MACI", functionName: "stateAq" });
const { data: stateTreeDepth, refetch: refetchStateTreeDepth } = useScaffoldContractRead({
contractName: "MACI",
functionName: "stateTreeDepth",
});
const { data: mainRoot1, refetch: refetchMainRoot1 } = useContractRead({
abi: AccQueueAbi,
address: stateAq,
functionName: "getMainRoot",
args: stateTreeDepth ? [BigInt(stateTreeDepth)] : undefined,
});
const { data: treeDepths, refetch: refetchTreeDepths } = useContractRead({
abi: PollAbi,
address: poll?.pollContracts.poll,
functionName: "treeDepths",
});
const [, , messageTreeDepth] = treeDepths || [undefined, undefined, undefined, undefined];
console.log(messageTreeDepth);
const { data: extContracts, refetch: refetchExtContracts } = useContractRead({
abi: PollAbi,
address: poll?.pollContracts.poll,
functionName: "extContracts",
});
const [, messageAq] = extContracts || [undefined, undefined, undefined];
const { data: mainRoot2, refetch: refetchMainRoot2 } = useContractRead({
abi: AccQueueAbi,
address: messageAq,
functionName: "getMainRoot",
args: messageTreeDepth ? [BigInt(messageTreeDepth)] : undefined,
});
// check if the message AQ has been fully merged
// const messageTreeDepth = Number((await pollContract.treeDepths()).messageTreeDepth);
// check if the main root was not already computed
// const mainRoot = (await accQueueContract.getMainRoot(messageTreeDepth.toString())).toString();
console.log(mainRoot1, mainRoot2);
function refetch() {
refetchStateTreeDepth();
refetchTreeDepths();
refetchExtContracts();
refetchMainRoot1();
refetchMainRoot2();
}
let step = 1;
if (mainRoot1) {
step = 2;
if (mainRoot2) {
step = 3;
}
}
async function mergeSignupStep() {
if (!poll?.pollContracts.poll) return;
await mergeSignups({ pollContractAddress: poll?.pollContracts.poll });
}
return (
<Modal show={show} setOpen={setOpen}>
<div className="mt-3 text-center sm:mt-5 mb-6">
<Dialog.Title as="h3" className="font-bold leading-6 text-2xl text-neutral-content">
Required Actions
</Dialog.Title>
</div>
<div className=" ">
<div className="text-center text-lg">
Step {step} - {stepNames[step - 1]}
</div>
{step === 1 && (
<div className="text-center">
<div className="text-sm">Merge the signup subtrees of the accumulator queue</div>
<div className="mockup-code bg-primary text-primary-content text-left mt-5">
<pre data-prefix="$">
<code>node build/ts/index.js mergeSignups -o {poll?.maciPollId.toString()}</code>
</pre>
</div>
</div>
)}
<div className="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2 sm:gap-3">
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-primary text-primary-content px-3 py-2 font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 sm:col-start-2"
onClick={refetch}
>
Refresh
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:col-start-1 sm:mt-0"
onClick={() => setOpen(false)}
>
Cancel
</button>
</div>
</div>
</Modal>
);
}

View File

@@ -3,11 +3,13 @@
import { useEffect, useState } from "react";
import { redirect } from "next/navigation";
import CreatePollModal from "./_components/CreatePollModal";
import PollStatusModal from "./_components/PollStatusModal";
import { useAccount } from "wagmi";
import Paginator from "~~/components/Paginator";
import { useScaffoldContractRead } from "~~/hooks/scaffold-eth";
import { useFetchPolls } from "~~/hooks/useFetchPolls";
import { useTotalPages } from "~~/hooks/useTotalPages";
import { Poll, PollStatus } from "~~/types/poll";
export default function AdminPage() {
const { address } = useAccount();
@@ -15,8 +17,9 @@ export default function AdminPage() {
const [currentPage, setCurrentPage] = useState(1);
const { data: admin } = useScaffoldContractRead({ contractName: "MACI", functionName: "owner" });
const [limit] = useState(10);
const { totalPolls, polls } = useFetchPolls(currentPage, limit);
const { totalPolls, polls, refetch: refetchPolls } = useFetchPolls(currentPage, limit);
const totalPages = useTotalPages(totalPolls, limit);
const [selectedPollForStatusModal, setSelectedPollForStatusModal] = useState<Poll>();
useEffect(() => {
if (!admin || !address) return;
@@ -54,7 +57,18 @@ export default function AdminPage() {
<td>{poll.name}</td>
<td>{new Date(Number(poll.startTime) * 1000).toLocaleString()}</td>
<td>{new Date(Number(poll.endTime) * 1000).toLocaleString()}</td>
<td>Poll Open</td>
<td>
{poll.status == PollStatus.CLOSED ? (
<>
{poll.status}{" "}
<button className=" text-accent underline" onClick={() => setSelectedPollForStatusModal(poll)}>
(Required Actions)
</button>
</>
) : (
poll.status
)}
</td>
</tr>
))}
</tbody>
@@ -67,7 +81,13 @@ export default function AdminPage() {
<div>No polls found</div>
)}
<CreatePollModal setOpen={setOpenCreatePollModal} show={openCreatePollModal} />
<CreatePollModal refetchPolls={refetchPolls} show={openCreatePollModal} setOpen={setOpenCreatePollModal} />
<PollStatusModal
poll={selectedPollForStatusModal}
setOpen={() => setSelectedPollForStatusModal(undefined)}
show={Boolean(selectedPollForStatusModal)}
/>
</div>
);
}

View File

@@ -0,0 +1,7 @@
import { NextRequest, NextResponse } from "next/server";
import { coordinator } from "~~/utils/coordinator";
export const POST = async (req: NextRequest, { params: { id } }: { params: { id: string } }) => {
coordinator.closePoll(parseInt(id));
return NextResponse.json({ message: "Hello, World!" });
};

View File

@@ -5,7 +5,7 @@ import { useParams } from "next/navigation";
import { genRandomSalt } from "@se-2/hardhat/maci-ts/crypto";
import { Keypair, Message, PCommand, PubKey } from "@se-2/hardhat/maci-ts/domainobjs";
import { useContractRead, useContractWrite } from "wagmi";
import PollAbi from "~~/abi/Poll.json";
import PollAbi from "~~/abi/Poll";
import VoteCard from "~~/components/card/VoteCard";
import { useAuthContext } from "~~/contexts/AuthContext";
import { useAuthUserOnly } from "~~/hooks/useAuthUserOnly";
@@ -18,7 +18,7 @@ export default function PollDetail() {
useAuthUserOnly({});
const { keypair } = useAuthContext();
const { keypair, stateIndex } = useAuthContext();
const [clickedIndex, setClickedIndex] = useState<number | null>(null);
const handleCardClick = (index: number) => {
@@ -27,6 +27,7 @@ export default function PollDetail() {
const castVote = async () => {
console.log("Voting for candidate", clickedIndex);
console.log("A", message?.message.asContractParam(), message?.encKeyPair.pubKey.asContractParam());
// navigate to the home page
try {
// setLoaderMessage("Casting the vote, please wait...");
@@ -43,14 +44,12 @@ export default function PollDetail() {
abi: PollAbi,
address: poll?.pollContracts.poll,
functionName: "maxValues",
args: [],
});
const { data: coordinatorPubKeyResult } = useContractRead({
abi: PollAbi,
address: poll?.pollContracts.poll,
functionName: "coordinatorPubKey",
args: [],
});
const [message, setMessage] = useState<{ message: Message; encKeyPair: Keypair }>();
@@ -61,7 +60,7 @@ export default function PollDetail() {
abi: PollAbi,
address: poll?.pollContracts.poll,
functionName: "publishMessage",
args: [message?.message.asContractParam(), message?.encKeyPair.pubKey.asContractParam()],
args: message ? [message.message.asContractParam(), message.encKeyPair.pubKey.asContractParam()] : undefined,
});
const [coordinatorPubKey, setCoordinatorPubKey] = useState<PubKey>();
@@ -77,14 +76,15 @@ export default function PollDetail() {
]);
setCoordinatorPubKey(coordinatorPubKey_);
}, [`coordinatorPubKeyResult`]);
}, [coordinatorPubKeyResult]);
useEffect(() => {
if (!clickedIndex || !coordinatorPubKey || !keypair) {
if (!clickedIndex || !coordinatorPubKey || !keypair || !stateIndex) {
return;
}
const command: PCommand = new PCommand(
1n, // stateindex
stateIndex, // stateindex
keypair.pubKey, // userMaciPubKey
BigInt(clickedIndex),
1n,
@@ -100,7 +100,7 @@ export default function PollDetail() {
const message = command.encrypt(signature, Keypair.genEcdhSharedKey(encKeyPair.privKey, coordinatorPubKey));
setMessage({ message, encKeyPair });
}, [id, clickedIndex, coordinatorPubKey, keypair]);
}, [id, clickedIndex, coordinatorPubKey, keypair, stateIndex]);
console.log(maxValues && (maxValues as any)[1]);
console.log(coordinatorPubKeyResult);

View File

@@ -36,7 +36,7 @@ export default function Modal({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-neutral px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-neutral text-neutral-content px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
{children}
</Dialog.Panel>
</Transition.Child>

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,74 @@
import { useEffect, useState } from "react";
import { useScaffoldContractRead } from "./scaffold-eth";
import { Poll, PollStatus, RawPoll } from "~~/types/poll";
export function getPollStatus(poll: RawPoll) {
const now = Math.round(new Date().getTime() / 1000);
if (poll.startTime > BigInt(now)) {
return PollStatus.NOT_STARTED;
}
if (poll.endTime > BigInt(now)) {
return PollStatus.OPEN;
}
if (!poll.tallyJsonCID) {
return PollStatus.CLOSED;
}
return PollStatus.RESULT_COMPUTED;
}
export const useFetchPolls = (currentPage = 1, limit = 10, reversed = true) => {
const { data: totalPolls } = useScaffoldContractRead({
const [polls, setPolls] = useState<Poll[]>();
const { data: totalPolls, refetch: refetchTotalPolls } = useScaffoldContractRead({
contractName: "PollManager",
functionName: "totalPolls",
});
const { data: polls } = useScaffoldContractRead({
const { data: rawPolls, refetch: refetchPolls } = useScaffoldContractRead({
contractName: "PollManager",
functionName: "fetchPolls",
args: [BigInt(currentPage), BigInt(limit), reversed],
});
return { totalPolls: Number(totalPolls || 0n), polls };
const [lastTimer, setLastTimer] = useState<NodeJS.Timer>();
useEffect(() => {
if (lastTimer) {
clearInterval(lastTimer);
}
if (!rawPolls) {
setPolls([]);
return;
}
const interval = setInterval(() => {
const _polls: Poll[] = [];
for (const rawPoll of rawPolls) {
_polls.push({
...rawPoll,
status: getPollStatus(rawPoll),
});
}
setPolls(_polls);
}, 1000);
setLastTimer(interval);
() => {
clearInterval(interval);
};
}, [rawPolls]);
function refetch() {
refetchTotalPolls();
refetchPolls();
}
return { totalPolls: Number(totalPolls || 0n), polls, refetch };
};

View File

@@ -0,0 +1,43 @@
export enum PollStatus {
NOT_STARTED = "Not Started",
OPEN = "Open",
CLOSED = "Closed",
RESULT_COMPUTED = "Result Computed",
}
export interface RawPoll {
id: bigint;
maciPollId: bigint;
name: string;
encodedOptions: `0x${string}`;
ipfsHash: string;
pollContracts: {
poll: string;
messageProcessor: string;
tally: string;
};
startTime: bigint;
endTime: bigint;
numOfOptions: bigint;
options: readonly string[];
tallyJsonCID: string;
}
export interface Poll {
id: bigint;
maciPollId: bigint;
name: string;
encodedOptions: `0x${string}`;
ipfsHash: string;
pollContracts: {
poll: string;
messageProcessor: string;
tally: string;
};
startTime: bigint;
endTime: bigint;
numOfOptions: bigint;
options: readonly string[];
tallyJsonCID: string;
status: PollStatus;
}

View File

@@ -0,0 +1,66 @@
import { genMACIState } from "./genMaciState";
import { mergeMessages } from "./mergeMessages";
import { mergeSignups } from "./mergeSignups";
import { Keypair, PrivKey } from "@se-2/hardhat/maci-ts/domainobjs";
import { Address, PublicClient, createPublicClient, getContract, http } from "viem";
import deployedContracts from "~~/contracts/deployedContracts";
import scaffoldConfig from "~~/scaffold.config";
class Coordinator {
closing: Record<number, boolean> = {};
targetNetwork = scaffoldConfig.targetNetworks[0];
publicClient: PublicClient;
constructor() {
this.publicClient = createPublicClient({ chain: this.targetNetwork, transport: http() });
}
async closePoll(pollId: number) {
if (this.closing[pollId]) {
return;
}
const { address: pollManagerAddress, abi: pollManagerAbi } = deployedContracts[this.targetNetwork.id].PollManager;
const pollManager = getContract({
abi: pollManagerAbi,
address: pollManagerAddress,
publicClient: this.publicClient,
});
if (Number(await pollManager.read.totalPolls()) < pollId) {
return null;
}
const poll = await pollManager.read.fetchPoll([BigInt(pollId)]);
if (new Date(Number(poll.endTime) * 1000) > new Date()) {
return null;
}
// console.log(poll);
// TODO: check if the poll is already closed
// const provider = this.publicClient.provider();
const coordinatorPrivateKey = new PrivKey(process.env.COORDINATOR_PRIVATE_KEY as string);
console.log(process.env.COORDINATOR_PRIVATE_KEY);
const coordinatorKeypair = new Keypair(coordinatorPrivateKey);
await mergeSignups({ pollContractAddress: poll.pollContracts.poll as Address });
await mergeMessages({ pollContractAddress: poll.pollContracts.poll as Address });
await genMACIState({ pollContractAddress: poll.pollContracts.poll as Address, coordinatorKeypair });
// console.log(poll);
this.closing[pollId] = true;
// Close the poll
console.log(`Closing poll ${pollId}`);
}
}
export const coordinator = new Coordinator();

View File

View File

@@ -0,0 +1,658 @@
import { MaciState, STATE_TREE_ARITY } from "@se-2/hardhat/maci-ts/core";
import { Keypair, Message, PubKey } from "@se-2/hardhat/maci-ts/domainobjs";
import assert from "assert";
import { Address, createPublicClient, getContract, http } from "viem";
import PollAbi from "~~/abi/Poll";
import deployedContracts from "~~/contracts/deployedContracts";
import scaffoldConfig from "~~/scaffold.config";
export interface Action {
type: string;
data: Partial<{
pubKey: PubKey;
encPubKey: PubKey;
message: Message;
voiceCreditBalance: number;
timestamp: number;
stateIndex: number;
numSrQueueOps: number;
pollId: bigint;
pollAddr: string;
stateRoot: bigint;
messageRoot: bigint;
}>;
blockNumber: bigint;
transactionIndex: number;
}
export function sortActions(actions: Action[]): Action[] {
return actions.slice().sort((a, b) => {
if (a.blockNumber > b.blockNumber) {
return 1;
}
if (a.blockNumber < b.blockNumber) {
return -1;
}
if (a.transactionIndex > b.transactionIndex) {
return 1;
}
if (a.transactionIndex < b.transactionIndex) {
return -1;
}
return 0;
});
}
export async function genMACIState({
pollContractAddress,
coordinatorKeypair,
}: {
pollContractAddress: Address;
coordinatorKeypair: Keypair;
}) {
const chain = scaffoldConfig.targetNetworks[0];
const publicClient = createPublicClient({ chain, transport: http() });
const { address: MaciAddress, abi: MaciAbi, deploymentBlockNumber } = deployedContracts[chain.id].MACI;
console.log(deploymentBlockNumber);
const maciContract = getContract({ abi: MaciAbi, address: MaciAddress, publicClient });
const stateTreeDepth = await maciContract.read.stateTreeDepth();
const maciState = new MaciState(stateTreeDepth);
assert(stateTreeDepth === maciState.stateTreeDepth);
const lastBlock = await publicClient.getBlockNumber();
const startBlock = BigInt(0);
const blocksPerRequest = 50;
const deployPollLogs = await maciContract.getEvents.DeployPoll({}, { fromBlock: startBlock, toBlock: lastBlock });
const signUpLogs = await maciContract.getEvents.SignUp({}, { fromBlock: startBlock, toBlock: lastBlock });
let actions: Action[] = [];
signUpLogs.forEach(log => {
actions.push({
type: "SignUp",
blockNumber: log.blockNumber,
transactionIndex: log.transactionIndex,
data: {
stateIndex: Number(log.args._stateIndex),
pubKey: new PubKey([log.args._userPubKeyX || 0n, log.args._userPubKeyY || 0n]),
voiceCreditBalance: Number(log.args._voiceCreditBalance),
timestamp: Number(log.args._timestamp),
},
});
});
const pollContractAddresses = new Map();
let includesPollAddress = false;
let pollId = 0n;
deployPollLogs.forEach(log => {
const pollAddr = log.args.pollAddr?.poll;
actions.push({
type: "DeployPoll",
blockNumber: log.blockNumber,
transactionIndex: log.transactionIndex,
data: {
pollId: log.args._pollId,
pollAddr,
pubKey: new PubKey([log.args._coordinatorPubKeyX || 0n, log.args._coordinatorPubKeyY || 0n]),
},
});
if (pollContractAddress.toLowerCase() === pollAddr?.toLowerCase()) {
includesPollAddress = true;
pollId = log.args._pollId as bigint;
}
pollContractAddresses.set(log.args._pollId, pollAddr);
});
assert(includesPollAddress, "Error: the specified pollId does not exist on-chain");
// const pollContractAddress = pollContractAddresses.get(0n);
const pollContract = getContract({ abi: PollAbi, address: pollContractAddress, publicClient });
const coordinatorPubKeyOnChain = await pollContract.read.coordinatorPubKey();
// assert(coordinatorPubKeyOnChain[0].toString() === coordinatorKeypair.pubKey.rawPubKey[0].toString());
// assert(coordinatorPubKeyOnChain[1].toString() === coordinatorKeypair.pubKey.rawPubKey[1].toString());
const dd = await pollContract.read.getDeployTimeAndDuration();
const deployTime = Number(dd[0]);
const duration = Number(dd[1]);
const onChainMaxValues = await pollContract.read.maxValues();
const onChainTreeDepths = await pollContract.read.treeDepths();
const maxValues = {
maxMessages: Number(onChainMaxValues[0]),
maxVoteOptions: Number(onChainMaxValues[1]),
};
const treeDepths = {
intStateTreeDepth: Number(onChainTreeDepths[0]),
messageTreeDepth: Number(onChainTreeDepths[1]),
messageTreeSubDepth: Number(onChainTreeDepths[2]),
voteOptionTreeDepth: Number(onChainTreeDepths[3]),
};
const batchSizes = {
tallyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths[0]),
subsidyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths[0]),
messageBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths[2]),
};
const publishMessageLogs = await pollContract.getEvents.PublishMessage({ fromBlock: startBlock });
const topupLogs = await pollContract.getEvents.TopupMessage({ fromBlock: startBlock });
const mergeMaciStateAqSubRootsLogs = await pollContract.getEvents.MergeMaciStateAqSubRoots({ fromBlock: startBlock });
const mergeMaciStateAqLogs = await pollContract.getEvents.MergeMaciStateAq({ fromBlock: startBlock });
const mergeMessageAqSubRootsLogs = await pollContract.getEvents.MergeMessageAqSubRoots({ fromBlock: startBlock });
const mergeMessageAqLogs = await pollContract.getEvents.MergeMessageAq({ fromBlock: startBlock });
publishMessageLogs.forEach(log => {
if (log.args._message && log.args._encPubKey) {
const message = new Message(BigInt(log.args._message.msgType), log.args._message.data as any);
const encPubKey = new PubKey([log.args._encPubKey.x, log.args._encPubKey.y]);
actions.push({
type: "PublishMessage",
blockNumber: log.blockNumber,
transactionIndex: log.transactionIndex,
data: {
message,
encPubKey,
},
});
}
});
topupLogs.forEach(log => {
if (log.args._message) {
const message = new Message(BigInt(log.args._message.msgType), log.args._message.data as any);
actions.push({
type: "TopupMessage",
blockNumber: log.blockNumber,
transactionIndex: log.transactionIndex,
data: {
message,
},
});
}
});
mergeMessageAqSubRootsLogs.forEach(log => {
if (log.args._numSrQueueOps) {
const numSrQueueOps = Number(log.args._numSrQueueOps);
actions.push({
type: "MergeMessageAqSubRoots",
blockNumber: log.blockNumber,
transactionIndex: log.transactionIndex,
data: {
numSrQueueOps,
},
});
}
});
// mergeMessageAqSubRootsLogs.forEach(log => {
// assert(!!log);
// const mutableLogs = { ...log, topics: [...log.topics] };
// const event = pollIface.parseLog(mutableLogs) as unknown as { args: { _numSrQueueOps: string } };
// const numSrQueueOps = Number(event.args._numSrQueueOps);
// actions.push({
// type: "MergeMessageAqSubRoots",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: {
// numSrQueueOps,
// },
// });
// });
mergeMessageAqLogs.forEach(log => {
if (log.args._messageRoot) {
const messageRoot = BigInt(log.args._messageRoot);
actions.push({
type: "MergeMessageAq",
blockNumber: log.blockNumber,
transactionIndex: log.transactionIndex,
data: { messageRoot },
});
}
});
// mergeMessageAqLogs.forEach(log => {
// assert(!!log);
// const mutableLogs = { ...log, topics: [...log.topics] };
// const event = pollIface.parseLog(mutableLogs);
// const messageRoot = BigInt((event?.args as unknown as { _messageRoot: string })._messageRoot);
// actions.push({
// type: "MergeMessageAq",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: { messageRoot },
// });
// });
// // Sort actions
actions = sortActions(actions);
console.log(actions);
// // Reconstruct MaciState in order
actions.forEach(action => {
// console.log("action: ", action);
switch (true) {
case action.type === "SignUp": {
const { pubKey, voiceCreditBalance, timestamp } = action.data;
maciState.signUp(pubKey!, BigInt(voiceCreditBalance!), BigInt(timestamp!));
break;
}
case action.type === "DeployPoll" && action.data.pollAddr === pollContractAddress: {
maciState.deployPoll(
BigInt(deployTime + duration),
maxValues,
treeDepths,
batchSizes.messageBatchSize,
coordinatorKeypair,
);
break;
}
case action.type === "DeployPoll" && action.data.pollAddr !== pollContractAddress: {
maciState.deployNullPoll();
break;
}
case action.type === "PublishMessage": {
const { encPubKey, message } = action.data;
maciState.polls.get(pollId)?.publishMessage(message!, encPubKey!);
break;
}
case action.type === "TopupMessage": {
const { message } = action.data;
maciState.polls.get(pollId)?.topupMessage(message!);
break;
}
// ensure that the message root is correct (i.e. all messages have been published offchain)
case action.type === "MergeMessageAq": {
assert(maciState.polls.get(pollId)?.messageTree.root.toString() === action.data.messageRoot?.toString());
break;
}
default:
break;
}
});
// // Set numSignUps
const numSignUpsAndMessages = await pollContract.read.numSignUpsAndMessages();
console.log(numSignUpsAndMessages);
const poll = maciState.polls.get(pollId);
// // ensure all messages were recorded
assert(Number(numSignUpsAndMessages[1]) === poll?.messages.length);
// set the number of signups
poll.updatePoll(numSignUpsAndMessages[0]);
// // we need to ensure that the stateRoot is correct
assert(poll.stateTree?.root.toString() === (await pollContract.read.mergedStateRoot()).toString());
maciState.polls.set(pollId, poll);
return maciState;
}
// async function (provider: Provider,
// address: string,
// coordinatorKeypair: Keypair,
// pollId: bigint,
// fromBlock = 0,
// blocksPerRequest = 50,
// endBlock: number | undefined = undefined,
// sleepAmount: number | undefined = undefined,
// ): Promise<MaciState> {
// // ensure the pollId is valid
// assert(pollId >= 0);
// const [pollContractAbi] = parseArtifact("Poll");
// const [maciContractAbi] = parseArtifact("MACI");
// const maciContract = new BaseContract(address, maciContractAbi, provider) as MACI;
// const maciIface = new Interface(maciContractAbi);
// const pollIface = new Interface(pollContractAbi);
// // Check stateTreeDepth
// const stateTreeDepth = await maciContract.stateTreeDepth();
// // we need to pass the stateTreeDepth
// const maciState = new MaciState(Number(stateTreeDepth));
// // ensure it is set correctly
// assert(stateTreeDepth === BigInt(maciState.stateTreeDepth));
// let signUpLogs: Log[] = [];
// let deployPollLogs: Log[] = [];
// // if no last block is set then we fetch until the current block number
// const lastBlock = endBlock || (await provider.getBlockNumber());
// // Fetch event logs in batches (lastBlock inclusive)
// for (let i = fromBlock; i <= lastBlock; i += blocksPerRequest + 1) {
// // the last block batch will be either current iteration block + blockPerRequest
// // or the end block if it is set
// const toBlock = i + blocksPerRequest >= lastBlock ? lastBlock : i + blocksPerRequest;
// const [tmpSignUpLogs, tmpDeployPollLogs] =
// // eslint-disable-next-line no-await-in-loop
// await Promise.all([
// maciContract.queryFilter(maciContract.filters.SignUp(), i, toBlock),
// maciContract.queryFilter(maciContract.filters.DeployPoll(), i, toBlock),
// ]);
// signUpLogs = signUpLogs.concat(tmpSignUpLogs);
// deployPollLogs = deployPollLogs.concat(tmpDeployPollLogs);
// if (sleepAmount) {
// // eslint-disable-next-line no-await-in-loop
// await sleep(sleepAmount);
// }
// }
// let actions: Action[] = [];
// signUpLogs.forEach(log => {
// assert(!!log);
// const mutableLog = { ...log, topics: [...log.topics] };
// const event = maciIface.parseLog(mutableLog) as unknown as {
// args: {
// _stateIndex: number;
// _userPubKeyX: string;
// _userPubKeyY: string;
// _voiceCreditBalance: number;
// _timestamp: number;
// };
// };
// actions.push({
// type: "SignUp",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// 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),
// },
// });
// });
// let index = 0n;
// const foundPollIds: number[] = [];
// const pollContractAddresses = new Map<bigint, string>();
// deployPollLogs.forEach(log => {
// assert(!!log);
// const mutableLogs = { ...log, topics: [...log.topics] };
// const event = maciIface.parseLog(mutableLogs) as unknown as {
// args: {
// _coordinatorPubKeyX: string;
// _coordinatorPubKeyY: string;
// _pollId: bigint;
// pollAddr: {
// poll: string;
// messageProcessor: string;
// tally: string;
// };
// };
// };
// const pubKey = new PubKey([BigInt(event.args._coordinatorPubKeyX), BigInt(event.args._coordinatorPubKeyY)]);
// const p = event.args._pollId;
// assert(p === index);
// const pollAddr = event.args.pollAddr.poll;
// actions.push({
// type: "DeployPoll",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: { pollId: p, pollAddr, pubKey },
// });
// foundPollIds.push(Number(p));
// pollContractAddresses.set(BigInt(p), pollAddr);
// index += 1n;
// });
// Check whether each pollId exists
// assert(foundPollIds.includes(Number(pollId)), "Error: the specified pollId does not exist on-chain");
// const pollContractAddress = pollContractAddresses.get(pollId)!;
// const pollContract = new BaseContract(pollContractAddress, pollContractAbi, provider) as Poll;
// const coordinatorPubKeyOnChain = await pollContract.coordinatorPubKey();
// assert(coordinatorPubKeyOnChain[0].toString() === coordinatorKeypair.pubKey.rawPubKey[0].toString());
// assert(coordinatorPubKeyOnChain[1].toString() === coordinatorKeypair.pubKey.rawPubKey[1].toString());
// const dd = await pollContract.getDeployTimeAndDuration();
// const deployTime = Number(dd[0]);
// const duration = Number(dd[1]);
// const onChainMaxValues = await pollContract.maxValues();
// const onChainTreeDepths = await pollContract.treeDepths();
// const maxValues = {
// maxMessages: Number(onChainMaxValues.maxMessages),
// maxVoteOptions: Number(onChainMaxValues.maxVoteOptions),
// };
// const treeDepths = {
// intStateTreeDepth: Number(onChainTreeDepths.intStateTreeDepth),
// messageTreeDepth: Number(onChainTreeDepths.messageTreeDepth),
// messageTreeSubDepth: Number(onChainTreeDepths.messageTreeSubDepth),
// voteOptionTreeDepth: Number(onChainTreeDepths.voteOptionTreeDepth),
// };
// const batchSizes = {
// tallyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.intStateTreeDepth),
// subsidyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.intStateTreeDepth),
// messageBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.messageTreeSubDepth),
// };
// // fetch poll contract logs
// let publishMessageLogs: Log[] = [];
// let topupLogs: Log[] = [];
// let mergeMaciStateAqSubRootsLogs: Log[] = [];
// let mergeMaciStateAqLogs: Log[] = [];
// let mergeMessageAqSubRootsLogs: Log[] = [];
// let mergeMessageAqLogs: Log[] = [];
// for (let i = fromBlock; i <= lastBlock; i += blocksPerRequest + 1) {
// const toBlock = i + blocksPerRequest >= lastBlock ? lastBlock : i + blocksPerRequest;
// const [
// tmpPublishMessageLogs,
// tmpTopupLogs,
// tmpMergeMaciStateAqSubRootsLogs,
// tmpMergeMaciStateAqLogs,
// tmpMergeMessageAqSubRootsLogs,
// tmpMergeMessageAqLogs,
// // eslint-disable-next-line no-await-in-loop
// ] = await Promise.all([
// pollContract.queryFilter(pollContract.filters.PublishMessage(), i, toBlock),
// pollContract.queryFilter(pollContract.filters.TopupMessage(), i, toBlock),
// pollContract.queryFilter(pollContract.filters.MergeMaciStateAqSubRoots(), i, toBlock),
// pollContract.queryFilter(pollContract.filters.MergeMaciStateAq(), i, toBlock),
// pollContract.queryFilter(pollContract.filters.MergeMessageAqSubRoots(), i, toBlock),
// pollContract.queryFilter(pollContract.filters.MergeMessageAq(), i, toBlock),
// ]);
// publishMessageLogs = publishMessageLogs.concat(tmpPublishMessageLogs);
// topupLogs = topupLogs.concat(tmpTopupLogs);
// mergeMaciStateAqSubRootsLogs = mergeMaciStateAqSubRootsLogs.concat(tmpMergeMaciStateAqSubRootsLogs);
// mergeMaciStateAqLogs = mergeMaciStateAqLogs.concat(tmpMergeMaciStateAqLogs);
// mergeMessageAqSubRootsLogs = mergeMessageAqSubRootsLogs.concat(tmpMergeMessageAqSubRootsLogs);
// mergeMessageAqLogs = mergeMessageAqLogs.concat(tmpMergeMessageAqLogs);
// if (sleepAmount) {
// // eslint-disable-next-line no-await-in-loop
// await sleep(sleepAmount);
// }
// }
// publishMessageLogs.forEach(log => {
// assert(!!log);
// const mutableLogs = { ...log, topics: [...log.topics] };
// const event = pollIface.parseLog(mutableLogs) as unknown as {
// args: { _message: [string, string[]]; _encPubKey: string[] };
// };
// const message = new Message(
// BigInt(event.args._message[0]),
// event.args._message[1].map(x => BigInt(x)),
// );
// const encPubKey = new PubKey(event.args._encPubKey.map(x => BigInt(x.toString())) as [bigint, bigint]);
// actions.push({
// type: "PublishMessage",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: {
// message,
// encPubKey,
// },
// });
// });
// topupLogs.forEach(log => {
// assert(!!log);
// const mutableLog = { ...log, topics: [...log.topics] };
// const event = pollIface.parseLog(mutableLog) as unknown as {
// args: { _message: [string, string[]] };
// };
// const message = new Message(
// BigInt(event.args._message[0]),
// event.args._message[1].map(x => BigInt(x)),
// );
// actions.push({
// type: "TopupMessage",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: {
// message,
// },
// });
// });
// mergeMessageAqSubRootsLogs.forEach(log => {
// assert(!!log);
// const mutableLogs = { ...log, topics: [...log.topics] };
// const event = pollIface.parseLog(mutableLogs) as unknown as { args: { _numSrQueueOps: string } };
// const numSrQueueOps = Number(event.args._numSrQueueOps);
// actions.push({
// type: "MergeMessageAqSubRoots",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: {
// numSrQueueOps,
// },
// });
// });
// mergeMessageAqLogs.forEach(log => {
// assert(!!log);
// const mutableLogs = { ...log, topics: [...log.topics] };
// const event = pollIface.parseLog(mutableLogs);
// const messageRoot = BigInt((event?.args as unknown as { _messageRoot: string })._messageRoot);
// actions.push({
// type: "MergeMessageAq",
// blockNumber: log.blockNumber,
// transactionIndex: log.transactionIndex,
// data: { messageRoot },
// });
// });
// // Sort actions
// actions = sortActions(actions);
// // Reconstruct MaciState in order
// actions.forEach(action => {
// switch (true) {
// case action.type === "SignUp": {
// const { pubKey, voiceCreditBalance, timestamp } = action.data;
// maciState.signUp(pubKey!, BigInt(voiceCreditBalance!), BigInt(timestamp!));
// break;
// }
// case action.type === "DeployPoll" && action.data.pollId?.toString() === pollId.toString(): {
// maciState.deployPoll(
// BigInt(deployTime + duration),
// maxValues,
// treeDepths,
// batchSizes.messageBatchSize,
// coordinatorKeypair,
// );
// break;
// }
// case action.type === "DeployPoll" && action.data.pollId?.toString() !== pollId.toString(): {
// maciState.deployNullPoll();
// break;
// }
// case action.type === "PublishMessage": {
// const { encPubKey, message } = action.data;
// maciState.polls.get(pollId)?.publishMessage(message!, encPubKey!);
// break;
// }
// case action.type === "TopupMessage": {
// const { message } = action.data;
// maciState.polls.get(pollId)?.topupMessage(message!);
// break;
// }
// // ensure that the message root is correct (i.e. all messages have been published offchain)
// case action.type === "MergeMessageAq": {
// assert(maciState.polls.get(pollId)?.messageTree.root.toString() === action.data.messageRoot?.toString());
// break;
// }
// default:
// break;
// }
// });
// // Set numSignUps
// const numSignUpsAndMessages = await pollContract.numSignUpsAndMessages();
// const poll = maciState.polls.get(pollId);
// // ensure all messages were recorded
// assert(Number(numSignUpsAndMessages[1]) === poll?.messages.length);
// // set the number of signups
// poll.updatePoll(numSignUpsAndMessages[0]);
// // we need to ensure that the stateRoot is correct
// assert(poll.stateTree?.root.toString() === (await pollContract.mergedStateRoot()).toString());
// maciState.polls.set(pollId, poll);
// return maciState;
// }

View File

@@ -0,0 +1,93 @@
import { DEFAULT_SR_QUEUE_OPS } from "./mergeSignups";
import { createPublicClient, createWalletClient, getContract, http } from "viem";
import { privateKeyToAddress } from "viem/accounts";
import AccQueueAbi from "~~/abi/AccQueue";
import PollAbi from "~~/abi/Poll";
import deployedContracts from "~~/contracts/deployedContracts";
import scaffoldConfig from "~~/scaffold.config";
export const mergeMessages = async ({
pollContractAddress,
numQueueOps,
}: {
pollContractAddress: string;
numQueueOps?: number;
}): Promise<void> => {
const chain = scaffoldConfig.targetNetworks[0];
const publicClient = createPublicClient({ chain, transport: http() });
const ownerAddress = privateKeyToAddress(process.env.OWNER_PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
chain,
transport: http(),
key: process.env.OWNER_PRIVATE_KEY,
account: ownerAddress,
});
const { address: MaciAddress, abi: MaciAbi, deploymentBlockNumber } = deployedContracts[chain.id].MACI;
const maciContract = getContract({ abi: MaciAbi, address: MaciAddress, publicClient, walletClient });
const pollId = await maciContract.read.getPollId([pollContractAddress]);
const pollContract = getContract({ abi: PollAbi, address: pollContractAddress, publicClient, walletClient });
const extContracts = await pollContract.read.extContracts();
const messageAqContractAddr = extContracts[1];
const accQueueContract = getContract({
abi: AccQueueAbi,
address: messageAqContractAddr,
publicClient,
walletClient,
});
// check if it's time to merge the message AQ
const dd = await pollContract.read.getDeployTimeAndDuration();
const deadline = Number(dd[0]) + Number(dd[1]);
const { timestamp: now } = await publicClient.getBlock();
if (now < deadline) {
console.error("Voting period is not over");
}
let subTreesMerged = false;
// infinite loop to merge the sub trees
while (!subTreesMerged) {
// eslint-disable-next-line no-await-in-loop
subTreesMerged = await accQueueContract.read.subTreesMerged();
if (subTreesMerged) {
console.log("All message subtrees have been merged.");
} else {
// eslint-disable-next-line no-await-in-loop
await accQueueContract.read
.getSrIndices()
.then(data => data.map(x => Number(x)))
.then(indices => {
console.log(`Merging message subroots ${indices[0] + 1} / ${indices[1] + 1}`);
});
// eslint-disable-next-line no-await-in-loop
const tx = await pollContract.write.mergeMessageAqSubRoots([BigInt(numQueueOps || DEFAULT_SR_QUEUE_OPS)]);
// eslint-disable-next-line no-await-in-loop
console.log(`Transaction hash: ${tx}`);
}
}
// check if the message AQ has been fully merged
const messageTreeDepth = Number((await pollContract.read.treeDepths())[2]);
// check if the main root was not already computed
const mainRoot = (await accQueueContract.read.getMainRoot([BigInt(messageTreeDepth)])).toString();
if (mainRoot === "0") {
// go and merge the message tree
console.log("Merging subroots to a main message root...");
const tx = await pollContract.write.mergeMessageAq();
console.log(`Executed mergeMessageAq(); Transaction hash: ${tx}`);
console.log("The message tree has been merged.");
} else {
console.log("The message tree has already been merged.");
}
};

View File

@@ -0,0 +1,106 @@
import { createPublicClient, createWalletClient, getContract, http } from "viem";
import { privateKeyToAddress } from "viem/accounts";
import AccQueueAbi from "~~/abi/AccQueue";
import PollAbi from "~~/abi/Poll";
import deployedContracts from "~~/contracts/deployedContracts";
import scaffoldConfig from "~~/scaffold.config";
export const DEFAULT_SR_QUEUE_OPS = 4;
export const mergeSignups = async ({
pollContractAddress,
numQueueOps,
}: {
pollContractAddress: string;
numQueueOps?: number;
}): Promise<void> => {
const chain = scaffoldConfig.targetNetworks[0];
const publicClient = createPublicClient({ chain, transport: http() });
const ownerAddress = privateKeyToAddress(process.env.OWNER_PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
chain,
transport: http(),
key: process.env.OWNER_PRIVATE_KEY,
account: ownerAddress,
});
const { address: MaciAddress, abi: MaciAbi, deploymentBlockNumber } = deployedContracts[chain.id].MACI;
const maciContract = getContract({ abi: MaciAbi, address: MaciAddress, publicClient, walletClient });
const pollId = await maciContract.read.getPollId([pollContractAddress]);
const pollContract = getContract({ abi: PollAbi, address: pollContractAddress, publicClient, walletClient });
// if (pollId < 0) {
// logError("Invalid poll id");
// }
// const pollAddress = await maciContract.polls(pollId);
const accQueueContract = getContract({
abi: AccQueueAbi,
address: await maciContract.read.stateAq(),
publicClient,
walletClient,
});
// const accQueueContract = AccQueueFactory.connect(await maciContract.stateAq(), signer);
// check if it's time to merge the message AQ
const dd = await pollContract.read.getDeployTimeAndDuration();
const deadline = Number(dd[0]) + Number(dd[1]);
const { timestamp: now } = await publicClient.getBlock();
if (now < deadline) {
console.error("Voting period is not over");
}
let subTreesMerged = false;
// infinite loop to merge the sub trees
while (!subTreesMerged) {
// eslint-disable-next-line no-await-in-loop
subTreesMerged = await accQueueContract.read.subTreesMerged();
if (subTreesMerged) {
console.log("All state subtrees have been merged.");
} else {
// eslint-disable-next-line no-await-in-loop
await accQueueContract.read
.getSrIndices()
.then(data => data.map(x => Number(x)))
.then(indices => {
console.log(`Merging state subroots ${indices[0] + 1} / ${indices[1] + 1}`);
});
// first merge the subroots
// eslint-disable-next-line no-await-in-loop
const tx = await pollContract.write.mergeMaciStateAqSubRoots([
BigInt(numQueueOps || DEFAULT_SR_QUEUE_OPS),
pollId,
]);
// eslint-disable-next-line no-await-in-loop
// const receipt = await tx.wait();
// if (receipt?.status !== 1) {
// logError("Error merging state subroots");
// }
// logYellow(quiet, info(`Transaction hash: ${receipt!.hash}`));
// logGreen(quiet, success(`Executed mergeMaciStateAqSubRoots(); gas used: ${receipt!.gasUsed.toString()}`));
}
}
// check if the state AQ has been fully merged
const stateTreeDepth = await maciContract.read.stateTreeDepth();
const mainRoot = (await accQueueContract.read.getMainRoot([BigInt(stateTreeDepth)])).toString();
if (mainRoot === "0" || pollId > 0) {
// go and merge the state tree
console.log("Merging subroots to a main state root...");
const tx = await pollContract.write.mergeMaciStateAq([pollId]);
console.log(`Transaction hash: ${tx}`);
} else {
console.log("The state tree has already been merged.");
}
};