From 2231a21167958d03f088cdf58e4570cb5e7e4308 Mon Sep 17 00:00:00 2001 From: Jia Liu Date: Mon, 8 Jul 2024 16:26:51 +0100 Subject: [PATCH] optimisation using hash precomputation --- contracts/IPseudoRand.sol | 4 + contracts/PseudoRand.sol | 5 + contracts/zkdvrf_pre.sol | 286 +++++++++++++++++++++++++++ hardhat.config.ts | 1 + test/zkdvrf_pre.spec.ts | 401 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 697 insertions(+) create mode 100644 contracts/zkdvrf_pre.sol create mode 100644 test/zkdvrf_pre.spec.ts diff --git a/contracts/IPseudoRand.sol b/contracts/IPseudoRand.sol index 1d05779..0afafc5 100644 --- a/contracts/IPseudoRand.sol +++ b/contracts/IPseudoRand.sol @@ -21,6 +21,10 @@ interface IPseudoRand { bytes32 value; } + function hashToG1( + bytes memory message + ) external returns (Pairing.G1Point memory); + function verifyPartialEvalFast( Pairing.G1Point memory h, Pairing.G1Point memory sigma, diff --git a/contracts/PseudoRand.sol b/contracts/PseudoRand.sol index 8f0a9b3..f5e0a3c 100644 --- a/contracts/PseudoRand.sol +++ b/contracts/PseudoRand.sol @@ -12,6 +12,11 @@ contract PseudoRand is IPseudoRand{ bytes public constant DOMAIN = bytes("DVRF pseudorandom generation 2023"); uint public constant R = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + function hashToG1(bytes memory message) public view returns (Pairing.G1Point memory) { + Pairing.G1Point memory h = Hash.hashToG1(DOMAIN, message); + return h; + } + // verify partial eval without computing hash to point function verifyPartialEvalFast( Pairing.G1Point memory h, diff --git a/contracts/zkdvrf_pre.sol b/contracts/zkdvrf_pre.sol new file mode 100644 index 0000000..90dc973 --- /dev/null +++ b/contracts/zkdvrf_pre.sol @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Halo2Verifier} from "./Halo2Verifier.sol"; +import {GlobalPublicParams} from "./GlobalPublicParams.sol"; +import {Pairing} from "./libs/Pairing.sol"; +import {IPseudoRand} from "./IPseudoRand.sol"; +import {Grumpkin} from "./libs/Grumpkin.sol"; + +import "@openzeppelin/contracts/utils/Strings.sol"; +import '@openzeppelin/contracts/access/Ownable.sol'; + +// zkdvrf with precomputation for hash2curve +contract zkdvrf_pre is Ownable { + using Strings for uint256; + using Grumpkin for *; + + event RegistrationCompleted(uint32 count); + event NidkgStarted(); + event NidkgCompleted(uint32 count); + event GlobalPublicParamsCreated(); + event RandomInitiated(uint roundNum, string input); + event RandomThresholdReached(uint roundNum, string input); + event RandomReady(uint roundNum, string input); + + struct dvrfNode { + address nodeAddress; + bool status; + uint256 deposit; + bool statusPP; + uint32 pkIndex; + } + + enum Status { + Unregistered, + Nidkg, + NidkgComplete, + Ready + } + + string public constant INPUT_PREFIX = "zkRand-v1-2024:"; + + uint32 public memberCount; + uint32 public threshold; + uint32 public ppLength; + // current count of members added + uint32 internal currentIndex; + // current count of members deposited and registered + uint32 internal registeredCount; + uint32 internal ppSubmissionCount; + + uint256 public currentRoundNum; + uint256 public minNodeDeposit; + + uint32 public pkListIndex; + Grumpkin.Point[] public pkList; + address[] public pkListOrder; + + uint256[][] public ppList; + // address[] public ppListOrder; + + // The order in vkList is the same as pkList + Pairing.G1Point[] public vkList; + Pairing.G2Point internal gpkVal; + + Status public contractPhase; + address public halo2Verifier; + address public halo2VerifyingKey; + address public globalPublicParams; + address public pseudoRand; + + mapping (uint32 => address) public nodes; + mapping (address => dvrfNode) public addrToNode; + mapping (uint256 => string) public roundInput; + mapping (uint256 => Pairing.G1Point) public roundHash; + mapping (address => uint256) public lastSubmittedRound; + mapping (uint256 => mapping (uint32 => IPseudoRand.PartialEval)) public roundToEval; + mapping (uint256 => uint32) public roundSubmissionCount; + mapping (uint256 => IPseudoRand.PseudoRandom) public roundToRandom; + + + constructor(uint32 thresholdValue, uint32 numberValue, address halo2VerifierAddress, address halo2VerifyingKeyAddress, address globalPublicParamsAddress, address pseudoRandAddress, uint256 minDeposit) Ownable(msg.sender) { + require (halo2VerifierAddress != address(0) && globalPublicParamsAddress != address(0) && pseudoRandAddress != address(0), "Cannot be zero addresses"); + memberCount = numberValue; + threshold = thresholdValue; + ppLength = 7 * memberCount + 14; + halo2Verifier = halo2VerifierAddress; + halo2VerifyingKey = halo2VerifyingKeyAddress; + globalPublicParams = globalPublicParamsAddress; + pseudoRand = pseudoRandAddress; + minNodeDeposit = minDeposit; + } + + + // works until all members added, + // to move to the next phase registeredCount has to be equal to memberCount + function addPermissionedNodes(address nodeAddress) public onlyOwner { + require(currentIndex < memberCount, "All members added"); + require(nodeAddress != address(0), "Node cannot be zero address"); + require(addrToNode[nodeAddress].nodeAddress == address(0), "Node has already been added"); + + addrToNode[nodeAddress] = dvrfNode(nodeAddress, false, 0, false, 0); + currentIndex++; + } + + // each member registers with deposit and confirms + function registerNode(Grumpkin.Point memory pubKey) public payable { + require(contractPhase == Status.Unregistered, "Registration has already been completed"); + require(msg.sender == addrToNode[msg.sender].nodeAddress, "Unauthorized call"); + require(!addrToNode[msg.sender].status, "Node Already registered"); + require(msg.value >= minNodeDeposit, "Must provide enough node deposit"); + require(Grumpkin.isOnCurve(pubKey), "Invalid Public Key submitted"); + + nodes[registeredCount] = msg.sender; + addrToNode[msg.sender].deposit = msg.value; + addrToNode[msg.sender].status = true; + addrToNode[msg.sender].pkIndex = pkListIndex; + pkList.push(pubKey); + // pkListOrder is unutilized but added for public visibility + pkListOrder.push(msg.sender); + pkListIndex++; + registeredCount++; + + // all the permitted nodes have registered + if (registeredCount == memberCount) { + emit RegistrationCompleted(registeredCount); + } + } + + // owner starts nidkg protocol + // can't add members after this process + function startNidkg() public onlyOwner { + require(contractPhase == Status.Unregistered, "NIDKG has already been completed"); + require(registeredCount == memberCount, "Not all Members are ready"); + contractPhase = Status.Nidkg; + + emit NidkgStarted(); + } + + // each member can submit pp_i, zk_i + // contract validates zk_i here for each submission and then accepts it + function submitPublicParams(uint256[] calldata pp, bytes calldata zkProof) public { + require(msg.sender == addrToNode[msg.sender].nodeAddress, "Unauthorized call"); + require(contractPhase == Status.Nidkg, "Contract not in NIDKG phase"); + require(!addrToNode[msg.sender].statusPP, "Node already submitted"); + require(checkPublicParams(pp), "Invalid public parameters"); + require(Halo2Verifier(halo2Verifier).verifyProof(halo2VerifyingKey, zkProof, pp), "SNARK proof verification failed"); + + addrToNode[msg.sender].statusPP = true; + + ppList.push(pp); + // ppListOrder is unutilized but added for public visibility + // ppListOrder.push(msg.sender); + ppSubmissionCount++; + + if (ppSubmissionCount == memberCount) { + contractPhase = Status.NidkgComplete; + emit NidkgCompleted(ppSubmissionCount); + } + } + + // compute gpk and vk and store on the contract + function computeVk(Pairing.G2Point calldata gpk) public { + require(contractPhase == Status.NidkgComplete, "Partial Parameter submission not complete"); + (Pairing.G2Point memory gpkRet, Pairing.G1Point[] memory vk) = GlobalPublicParams(globalPublicParams).createGpp(memberCount, gpk, ppList); + for (uint i = 0; i < vk.length; i++) { + vkList.push(vk[i]); + } + gpkVal = gpkRet; + contractPhase = Status.Ready; + + emit GlobalPublicParamsCreated(); + } + + // initiate public inputs for generating randoms + function initiateRandom() public onlyOwner { + require(contractPhase == Status.Ready, "Contract not ready"); + + if (currentRoundNum != 0) { + require(roundToRandom[currentRoundNum].value != bytes32(0), "Earlier round not completed"); + } + + currentRoundNum++; + bytes memory input = abi.encodePacked(INPUT_PREFIX, currentRoundNum.toString()); + roundInput[currentRoundNum] = string(input); + roundHash[currentRoundNum] = IPseudoRand(pseudoRand).hashToG1(input); + + emit RandomInitiated(currentRoundNum, roundInput[currentRoundNum]); + } + + // each member can submit their partial evaluation. + // this function can be taken offchain. The onchain storage and verification can help determine which node to reward or punish. + function submitPartialEval(IPseudoRand.PartialEval memory pEval) public { + require(msg.sender == addrToNode[msg.sender].nodeAddress, "Unauthorized call"); + // check valid round + require(roundToRandom[currentRoundNum].value == bytes32(0), "Round already computed"); + // this will help revert calls if the contract status is not Ready and the first initiateRandom() is not called + require (lastSubmittedRound[msg.sender] < currentRoundNum, "Already submitted for round"); + uint32 pkIndex = addrToNode[msg.sender].pkIndex; + require(pEval.indexPlus == pkIndex + 1); + Pairing.G1Point memory vkStored = vkList[pkIndex]; + require(IPseudoRand(pseudoRand).verifyPartialEvalFast(roundHash[currentRoundNum], pEval.value, pEval.proof, vkStored), "Verification of partial eval failed"); + lastSubmittedRound[msg.sender] = currentRoundNum; + roundToEval[currentRoundNum][pkIndex] = pEval; + roundSubmissionCount[currentRoundNum]++; + + if (roundSubmissionCount[currentRoundNum] == threshold) { + emit RandomThresholdReached(currentRoundNum, roundInput[currentRoundNum]); + } + } + + // submit the final pseudorandom value which is computed by combining t partial evaluations offchain + function submitRandom(IPseudoRand.PseudoRandom memory pseudo) public onlyOwner { + require(roundToRandom[currentRoundNum].value == bytes32(0), "Answer for round already exists"); + require(roundSubmissionCount[currentRoundNum] >= threshold, "Partial evaluation threshold not reached"); + require(IPseudoRand(pseudoRand).verifyPseudoRandFast(roundHash[currentRoundNum], pseudo.proof, gpkVal), "Incorrect random submitted"); + bytes32 value = keccak256(abi.encodePacked(pseudo.proof.x, pseudo.proof.y)); + require(pseudo.value == value, "Incorrect pseudorandom value"); + roundToRandom[currentRoundNum] = pseudo; + + emit RandomReady(currentRoundNum, roundInput[currentRoundNum]); + } + + function getLatestRandom() public view returns (IPseudoRand.PseudoRandom memory pseudo) { + if (roundToRandom[currentRoundNum].value != bytes32(0)) { + return roundToRandom[currentRoundNum]; + } + + if (currentRoundNum == 1) { + revert("Answer does not exist for the round yet"); + } + + return roundToRandom[currentRoundNum - 1]; + } + + function getRandomAtRound(uint256 roundNum) public view returns (IPseudoRand.PseudoRandom memory pseudo) { + if (roundToRandom[roundNum].value != bytes32(0)) { + return roundToRandom[roundNum]; + } + + revert("Answer does not exist for the round yet"); + } + + function checkPublicParams(uint256[] calldata pp) public view returns (bool) { + require(pkList.length == memberCount, "Not enough member public keys"); + + require(pp.length == ppLength, "Wrong size of public parameters"); + if (pp.length != ppLength) { + return false; + } + + // check if the last 2n elements in pp are public keys + uint j = pp.length - 2 * memberCount; + for (uint i = 0; i < memberCount; i++) { + require(pp[j] == pkList[i].x, "Wrong public key x"); + require(pp[j+1] == pkList[i].y, "Wrong public key y"); + if (pp[j] != pkList[i].x || pp[j+1] != pkList[i].y) { + return false; + } + j = j+2; + } + + return true; + } + + function getIndexPlus(address nodeAdress) public view returns (uint32) { + uint32 pkIndex = addrToNode[nodeAdress].pkIndex; + return pkIndex + 1; + } + + function getPkList() public view returns (Grumpkin.Point[] memory) { + return pkList; + } + + function getPpList() public view returns (uint256[][] memory) { + return ppList; + } + + function getGpk() public view returns (Pairing.G2Point memory) { + return gpkVal; + } + + function getVkList() public view returns (Pairing.G1Point[] memory) { + return vkList; + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index e82c76d..fa6791f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -38,6 +38,7 @@ module.exports = { } }], overrides: { + 'contracts/GlobalPublicParams.sol': altCompilerSettings, 'contracts/PseudoRand.sol': altCompilerSettings, }, gasReporter: { diff --git a/test/zkdvrf_pre.spec.ts b/test/zkdvrf_pre.spec.ts new file mode 100644 index 0000000..cd408ca --- /dev/null +++ b/test/zkdvrf_pre.spec.ts @@ -0,0 +1,401 @@ +import chai, { expect } from 'chai' +import chaiAsPromised from 'chai-as-promised' +chai.use(chaiAsPromised) +import {solidity} from "ethereum-waffle"; +import hre, { ethers } from 'hardhat' +import { Contract, Signer, BigNumber, utils, BigNumberish, ContractFactory, providers, Wallet } from 'ethers' +chai.use(solidity); + +let Zkdvrf: Contract +let Halo2Verifier: Contract +let Halo2VerifyingKey: Contract +let GlobalPublicParams: Contract +let PseudoRand: Contract +let Lottery: Contract + +let minDeposit = utils.parseEther('0.01') +let minBet = utils.parseEther('0.5') + +let account1: Signer +let account2: Signer +let account3: Signer +let account4: Signer +let account5: Signer +let account1Address: string +let account2Address: string +let account3Address: string +let account4Address: string +let account5Address: string + +let lotteryAdmin: Signer +let player1: Signer +let player2: Signer +let player3: Signer +let player4: Signer +let lotteryAdminAddress: string +let player1Address: string +let player2Address: string +let player3Address: string + + +let pubKeyAcc1 = {x:"0x1c5dd8ff5b131cbcd6c38d8143a5db4c6bd1593f436390b24880a4ce41ca5a47", y:"0x23976573e74b5ed2daa1ae1870a90d3463d86cf751381e2486c36a83ae2ee5dd"} +let pubKeyAcc2 = {x:"0x213c1d8acf39ba783cbc7d5f678c768736c0c1a1aee852adb041c9d66294391a", y:"0x2468b7c61830e845f883d6469479d7a5b4b33692f45ffaf24e22159c91413992"} +let pubKeyAcc3 = {x:"0x0cbe88d0c6a16c258431d5112881bb24fd2e3fc7f00b88a09dd2673c3756edc6", y:"0x1fa10c321ccd822b0f1389541fd3b86f2ded61a21200509f5efedfc785c13f67"} +let pubKeyAcc4 = {x:"0x2784164c9c9091b30707853f7a22b593b20607993cd3d079ca9b0bac2a8c1dd8", y:"0x0607d39d53891e8a62f3ccd0dc683a10e659cabc5416a3b8850149b20ddfeb1e"} +let pubKeyAcc5 = {x:"0x0b693d44636ce447d4ce0e77c2fdde4b3adf8ee3f2e57c1e57eae257dca4225f", y:"0x178b850fa92de9125afcffb4b950019f46363f9e2491fe63684ec402e7617206"} + + +let ppAcc1 = ["0x000000000000000000000000000000bec848ba0584770858c436ad386a21e488","0x0000000000000000000000000000000000069f7212f1ee2a77f94ea7551c0489","0x00000000000000000000000000000073f80deade224336b03e82fecc1a366d7b","0x000000000000000000000000000000000028fcfa184dded7d9412b911b69a0d9","0x000000000000000000000000000000642417e7427648505964a0edee3c0c9c6f","0x000000000000000000000000000000000011d42f6e34480ba6912db51c2221c4","0x0000000000000000000000000000007eb790464c9f43f81d8460c10bc3c0e23b","0x00000000000000000000000000000000002dd147f96911bdde04cfc32f3246b1","0x000000000000000000000000000000fef0c970a83472760f2b94866bf4867037","0x000000000000000000000000000000000012fd07cc66d172379038019f230f32","0x000000000000000000000000000000f47b8865fbd14822b478b0379fe4beb7f3","0x00000000000000000000000000000000000eb02fe0aa7a22d583b445bcd04bf9","0x00000000000000000000000000000086d12b842dc3139d0fd543edd109a9e252","0x0000000000000000000000000000000000259e13c822b07facb69f47de01a599","0x00000000000000000000000000000060aba00424f1aaba71019b5711a7be656a","0x000000000000000000000000000000000010f4ac1f7d75f6e28dc423cdea3965","0x000000000000000000000000000000b40506de4a2533e3fbc737f02e971ba953","0x000000000000000000000000000000000014e1bb6ce5041fb7a4be28f3819ca2","0x00000000000000000000000000000089218cd33cd60b2c194aacf30c7c74b7cc","0x000000000000000000000000000000000024d0352b63835d8d133b4a2346e12e","0x000000000000000000000000000000d72578e0345a148518e9aaac44833d16e5","0x00000000000000000000000000000000002cb60c70a694abf327d6c6a2357f43","0x0000000000000000000000000000000aa30117f8d7c43cd32f2545b8e2503c68","0x00000000000000000000000000000000002055a669bae9c742047c4da0c2bda5","0x000000000000000000000000000000da5c65e9a191d0aa7c03f79595b33d158f","0x0000000000000000000000000000000000230a1eefdf776f021f4b8036889819","0x000000000000000000000000000000876e1aae01db46da713fcd691e6ed2f1a9","0x000000000000000000000000000000000001458e890f3645e0dd23f8051c1866","0x00000000000000000000000000000013444c20b069a61c905a99726171e9678f","0x0000000000000000000000000000000000174711713c34227aee946f257e7995","0x00000000000000000000000000000014100bb15cf0d6cccc64a342893b3eea2b","0x000000000000000000000000000000000028d9db6f7efa93466079e4d59ddce0","0x2223ede3b4d6499b8aefb3b928ac03e23334182c1095bb6729bc2073565a51c0","0x28ac9eb3e0cdec13bcbad68ad88adc0994810802d271ad50a1eaa6c4d196d758","0x18d4d700c353a7840260d83259c9e7123df6cc3fbd36257e4edb7a8026255420","0x2398a05ac0ce6e5680c7d5d80acb04dce242fe9f390bb183084b7a8d24dcbdf9","0x02e436842fdceb7005cd3008a8d616b3eb6c9ed58a70324c8091adc8e669140a","0x0e9b7e2b253f94ef95c94809049e4a20bf5bd11870e28d97c15757901b46b626","0x047b387a4ee41e374646a576a4b239ae1a64ad0afa1aa96881d7af75f0628ad9","0x1c5dd8ff5b131cbcd6c38d8143a5db4c6bd1593f436390b24880a4ce41ca5a47","0x23976573e74b5ed2daa1ae1870a90d3463d86cf751381e2486c36a83ae2ee5dd","0x213c1d8acf39ba783cbc7d5f678c768736c0c1a1aee852adb041c9d66294391a","0x2468b7c61830e845f883d6469479d7a5b4b33692f45ffaf24e22159c91413992","0x0cbe88d0c6a16c258431d5112881bb24fd2e3fc7f00b88a09dd2673c3756edc6","0x1fa10c321ccd822b0f1389541fd3b86f2ded61a21200509f5efedfc785c13f67","0x2784164c9c9091b30707853f7a22b593b20607993cd3d079ca9b0bac2a8c1dd8","0x0607d39d53891e8a62f3ccd0dc683a10e659cabc5416a3b8850149b20ddfeb1e","0x0b693d44636ce447d4ce0e77c2fdde4b3adf8ee3f2e57c1e57eae257dca4225f","0x178b850fa92de9125afcffb4b950019f46363f9e2491fe63684ec402e7617206"] +let ppAcc2 = ["0x0000000000000000000000000000005593dace49895d3bfba9e9d9b0b8b5430d","0x000000000000000000000000000000000007648758b999d0a050806ba3445596","0x00000000000000000000000000000078552e32531acf75de776688a2e332de02","0x00000000000000000000000000000000001677368f8f47f4efedb5747b27b0e0","0x000000000000000000000000000000a9fd2255e2e2836304b8ab2197c0a6eb34","0x000000000000000000000000000000000019866769ffae027e51197b8b810a93","0x0000000000000000000000000000001fd87c6c9645fcc04d032c5a1c1df527cd","0x000000000000000000000000000000000008c384a13af806b09d2476c3e1d52c","0x000000000000000000000000000000ea4fa422a1809a2e5c699ffaaec75512c0","0x000000000000000000000000000000000028288f576c8c9b05f7a506380fd8cc","0x00000000000000000000000000000055bd91d1f9237b7998606d0941fb3c5e88","0x0000000000000000000000000000000000023cd0bad506bef61f7a52c0878ee5","0x0000000000000000000000000000006144fe9d9c1c0159951aa8e03044560e4c","0x000000000000000000000000000000000004062f15f53f0f089de1ceb5d48c52","0x0000000000000000000000000000007807800070e01d11186414652689ac40bb","0x000000000000000000000000000000000028bf01a8eaec3eac536404ec8531a9","0x0000000000000000000000000000000cc8fcd2682a1235daea9d704bf97987c1","0x0000000000000000000000000000000000096cbe8fdfd1be7e318fd560006fa7","0x0000000000000000000000000000002378182b1b4e8d8258054fb2dc2c4eaf33","0x0000000000000000000000000000000000000b02b77c8e63e5b6eb80ff46d47c","0x00000000000000000000000000000078064d8f2da32775083714b19879ca0e2b","0x00000000000000000000000000000000000ecaa0f8a65bbcce8651395e08c682","0x0000000000000000000000000000004c3329bc106a3905fe908bb8b2a70c7768","0x00000000000000000000000000000000002df9c1900a613f997d731c4aee46c3","0x000000000000000000000000000000b68eb2894a7fb12ed957dc0964f7c0ff26","0x00000000000000000000000000000000002963fa4cea2610570d039f6c9275ae","0x0000000000000000000000000000003ab6f1d08df00ee30ba174d3dc3051a704","0x0000000000000000000000000000000000078f8508371b207db7d208471bcaa0","0x0000000000000000000000000000004a8a12039b5c1a013840974309e8cab5ea","0x000000000000000000000000000000000015656e1dd1804b118fce7c1d92d24f","0x00000000000000000000000000000086801e5fe728021163491b65cae2cafe1c","0x00000000000000000000000000000000001db08fe814c24ea5660999c16210a6","0x28bff878056df71a82a153ec4a907c19b6685cbc91bb9cfec31322dba9e2c2a7","0x12cd409104c5deb65756876980c6b1252ae590ef9b63b200c7d1aeb11e4a2282","0x0705da9bd7fc9b261de8089e56192c80905949826fb333d6545234bde2f56157","0x2e5fa5dfbfab42ade5925c4ef460b24789190e8428763f712b7cea1d32883c17","0x17e9429f5985073655d4dbbd49821daf0220d3ddf92a82bd86db0ea8dd49409b","0x01e717b393c0f1df84a842b1c04780625be2f741dd0458902795a7fa8421507d","0x23d85d468a45c74860ca944e0e8e20fd17fd3fda58c2e617b0000a3c2fe6f261","0x1c5dd8ff5b131cbcd6c38d8143a5db4c6bd1593f436390b24880a4ce41ca5a47","0x23976573e74b5ed2daa1ae1870a90d3463d86cf751381e2486c36a83ae2ee5dd","0x213c1d8acf39ba783cbc7d5f678c768736c0c1a1aee852adb041c9d66294391a","0x2468b7c61830e845f883d6469479d7a5b4b33692f45ffaf24e22159c91413992","0x0cbe88d0c6a16c258431d5112881bb24fd2e3fc7f00b88a09dd2673c3756edc6","0x1fa10c321ccd822b0f1389541fd3b86f2ded61a21200509f5efedfc785c13f67","0x2784164c9c9091b30707853f7a22b593b20607993cd3d079ca9b0bac2a8c1dd8","0x0607d39d53891e8a62f3ccd0dc683a10e659cabc5416a3b8850149b20ddfeb1e","0x0b693d44636ce447d4ce0e77c2fdde4b3adf8ee3f2e57c1e57eae257dca4225f","0x178b850fa92de9125afcffb4b950019f46363f9e2491fe63684ec402e7617206"] +let ppAcc3 = ["0x000000000000000000000000000000f7130638e4614f3a91dbcc357d91d67d2b","0x0000000000000000000000000000000000266ee7e7960fe3533b91838dbde40d","0x000000000000000000000000000000dbd35a23f7eb885084959a1fae1e584a12","0x000000000000000000000000000000000010d4434d37085bcf110f7fcc5641c3","0x0000000000000000000000000000004c584a26c0efc2360a71b2496ce442f33b","0x000000000000000000000000000000000019a6feabbce721b70ab4dae2d48802","0x00000000000000000000000000000069dcaec71c043745084da51b9ff86df28b","0x0000000000000000000000000000000000111b6ee775a3947da7fb33cb9b0efe","0x00000000000000000000000000000051f397b0ca54c427e6880070e607664600","0x000000000000000000000000000000000005e11f31c7fb0179b9eb41b61dd5e1","0x000000000000000000000000000000b75c68912c35168cc00a489423e7e5fd7c","0x000000000000000000000000000000000030476f11d6ddc69cf8ec14bae43c79","0x000000000000000000000000000000dcb84ff118d3a869e8191a49fc0b16bfc3","0x00000000000000000000000000000000001026ee4879ee5e290729101df47f88","0x000000000000000000000000000000d3f24de0fa4341ed6019770e43118b44c4","0x00000000000000000000000000000000001272bca832bb05d90c76107650bd9c","0x000000000000000000000000000000fa17570671ab58466c80e087a7f91c4d11","0x000000000000000000000000000000000023fbd7c3e74559d857090e3245ec00","0x0000000000000000000000000000001029d372c22ec0c93e0386fae82533c431","0x00000000000000000000000000000000001bec563de97dc55115af3705726e22","0x000000000000000000000000000000acf75b6adcb04b6d280edf8b9f6283d28b","0x00000000000000000000000000000000000f0d7acb2656f0b2b9c33dbf6aee52","0x000000000000000000000000000000e3d101a803dc5be58c902dd88ff82ad53c","0x00000000000000000000000000000000001d4eddc920a4e30d6bf40465c48586","0x000000000000000000000000000000b610d7fc9f069b146d7170c1c721a41be7","0x000000000000000000000000000000000014726f59bd295d82c6b72a965c621e","0x000000000000000000000000000000b521bbe1f0d03f8762f8d31dad1c06f0e0","0x00000000000000000000000000000000001381de4bc31fe4d5d2aace28f1295b","0x0000000000000000000000000000003680644415740070c9232bb419d27a3560","0x00000000000000000000000000000000001bfcd1cab072968fb5ac9487296ac1","0x0000000000000000000000000000008a2020ac05dafff1ff1591c2b347ee7276","0x000000000000000000000000000000000014570f0bfdffa7b091e7319605f5cd","0x099d8c5af7ccadd761ef0ac90b77693087c29ef1789a661b2d63b17fe93b5e6e","0x03f74745798100ae3cca0b7e6214db687dad8d32ff425bcd317931594550b189","0x1ad2d82480432434024757298a4170301a47315e2598a3f6a64449ba1502af23","0x2d780a19293bd309a956b803949a5c2982e54717686e705195f84fff8f36ec8f","0x11939320a63dbd36ade526f89b033106c5d5f7b849a1382749037d703c6c3ca8","0x23792c312753516c2b428686dbb2d810b85e56fec961e5e1e1b9dc767f67afba","0x2f56f7c8f45c909e0eaf41f40d726dc15b1b0fe83bf0a03c540de2c59305f4f9","0x1c5dd8ff5b131cbcd6c38d8143a5db4c6bd1593f436390b24880a4ce41ca5a47","0x23976573e74b5ed2daa1ae1870a90d3463d86cf751381e2486c36a83ae2ee5dd","0x213c1d8acf39ba783cbc7d5f678c768736c0c1a1aee852adb041c9d66294391a","0x2468b7c61830e845f883d6469479d7a5b4b33692f45ffaf24e22159c91413992","0x0cbe88d0c6a16c258431d5112881bb24fd2e3fc7f00b88a09dd2673c3756edc6","0x1fa10c321ccd822b0f1389541fd3b86f2ded61a21200509f5efedfc785c13f67","0x2784164c9c9091b30707853f7a22b593b20607993cd3d079ca9b0bac2a8c1dd8","0x0607d39d53891e8a62f3ccd0dc683a10e659cabc5416a3b8850149b20ddfeb1e","0x0b693d44636ce447d4ce0e77c2fdde4b3adf8ee3f2e57c1e57eae257dca4225f","0x178b850fa92de9125afcffb4b950019f46363f9e2491fe63684ec402e7617206"] +let ppAcc4 = ["0x000000000000000000000000000000f53552e94db2457ded0374efa9a9f7c068","0x000000000000000000000000000000000013f980d9522c08bbd07d23ee3bb24a","0x0000000000000000000000000000001bfa09d68dd2b7109ce5d25560cc0078d0","0x00000000000000000000000000000000001c690a101b791dbb5de0d1a9c9987d","0x000000000000000000000000000000e8674b1f3a5a55e6151caadf462331c157","0x000000000000000000000000000000000023dcbbd2d301609ba55e864e669858","0x0000000000000000000000000000007814030ecafd624043b4f4d389cf6cf254","0x0000000000000000000000000000000000215239cae7397e6a1951281219cba5","0x000000000000000000000000000000e53882478b6ecb7203fdacd879e386610f","0x000000000000000000000000000000000016cddf13e285f83da28ddba9cfe36d","0x0000000000000000000000000000006380b57c44e9362f7584ffc71b9fe5696e","0x00000000000000000000000000000000002879bdd60cf70cae1ad9a110b2d6e3","0x0000000000000000000000000000003ed0750999587b35c30b778aa4135ee975","0x00000000000000000000000000000000000cf7423b1a88020452b7a59f23f96b","0x000000000000000000000000000000da183963cb15f7f1074c951b310702bb12","0x000000000000000000000000000000000021f7e4201534a7531eac8b84bfe24b","0x00000000000000000000000000000064002a48eefd50e545039a2bf1bd47c42a","0x0000000000000000000000000000000000167fcf573d1b86af7e4bef58aa5484","0x0000000000000000000000000000009374b100eb51de6bd1690399aefdd3c23b","0x000000000000000000000000000000000013a1985e6584c020af8507a5df3e77","0x0000000000000000000000000000003631d0b0874335be6299cc1404991655fc","0x000000000000000000000000000000000021f838cc06b62f8a80205f1f29bcac","0x0000000000000000000000000000007d2450c2234ffc24dc142aebfc9d3d615d","0x0000000000000000000000000000000000124f23a3245ee842cfa6253f4b7ab2","0x000000000000000000000000000000919e22e65198b3c9a3f35bf7e24c37a2eb","0x00000000000000000000000000000000001d7caa6a02d64b6328f51413cd78ca","0x0000000000000000000000000000003b5c92f46cbaf385217e89b761758493ed","0x0000000000000000000000000000000000129cf7de7b48b5a2995cc275f4a08e","0x0000000000000000000000000000005270a31ef95f28e98cdabb1a55c539aed1","0x0000000000000000000000000000000000239619fd1a19308acca93893921c7e","0x0000000000000000000000000000009cd3e1ac01a11601647c91ce067160b347","0x00000000000000000000000000000000001f5d1180d71f42dcd2b32cfbadc20d","0x13e2d243a134c70d09910cc5f78c3478aa7b19d86552a4e58b7da3aa8f3f94c9","0x014887d277d9896eeb6ca31cd1be038215c9a2167f3bf542efc8bae2f72a7a92","0x034e14b6f534919fef894faaf006d8a3319666a299ed8790e2edc355a996535d","0x17e23cca4129d7f61f4b1e11c2d407eb1327e6920d7a068b1feea2cf2526dcb4","0x21d885f7d96b9ea1a1b1747d7c6041408d5c014bf8fbc54e364e3058b06f87f5","0x12fc78fa56022e86282b8c711b79809118845dbb8aa3f9fb7195dae0025e3b27","0x08adb10fe8503de1e328482e089481f2dfc21f329cc8352270db52667c2871b2","0x1c5dd8ff5b131cbcd6c38d8143a5db4c6bd1593f436390b24880a4ce41ca5a47","0x23976573e74b5ed2daa1ae1870a90d3463d86cf751381e2486c36a83ae2ee5dd","0x213c1d8acf39ba783cbc7d5f678c768736c0c1a1aee852adb041c9d66294391a","0x2468b7c61830e845f883d6469479d7a5b4b33692f45ffaf24e22159c91413992","0x0cbe88d0c6a16c258431d5112881bb24fd2e3fc7f00b88a09dd2673c3756edc6","0x1fa10c321ccd822b0f1389541fd3b86f2ded61a21200509f5efedfc785c13f67","0x2784164c9c9091b30707853f7a22b593b20607993cd3d079ca9b0bac2a8c1dd8","0x0607d39d53891e8a62f3ccd0dc683a10e659cabc5416a3b8850149b20ddfeb1e","0x0b693d44636ce447d4ce0e77c2fdde4b3adf8ee3f2e57c1e57eae257dca4225f","0x178b850fa92de9125afcffb4b950019f46363f9e2491fe63684ec402e7617206"] +let ppAcc5 = ["0x00000000000000000000000000000041508d336e54ddcdefa185f93a0436ae44","0x00000000000000000000000000000000000fc0010e61ddb315d4d43ec5f2b7df","0x0000000000000000000000000000004be120216aacd904bcdca82c91445d2819","0x000000000000000000000000000000000021d43ba6c56266247d7e4f88e9f08d","0x0000000000000000000000000000009b68ce840240996509004176a36390a8a3","0x00000000000000000000000000000000000ea0f220592ca759d963dbe381e398","0x000000000000000000000000000000b759acec329b4edf2a88ba18ea974bde79","0x0000000000000000000000000000000000019d86a8b7cd2d74893dfd89a7db2c","0x000000000000000000000000000000e6abad546e9538b054b4350deeb3380fea","0x00000000000000000000000000000000000c7095752a5c95592035e5e3275142","0x00000000000000000000000000000037745330c0b059c8e4960a131458872f37","0x00000000000000000000000000000000000e371a034fa6c796d0f2ea405d3f23","0x000000000000000000000000000000ef09a87b655d12fcd4f632d8944869bc64","0x00000000000000000000000000000000000a158e9c84e72a05cfda7331fbca4d","0x00000000000000000000000000000022237ecf83c187a3d137b4a921a41582f8","0x00000000000000000000000000000000001d55b9f38c6dc050d437ba8d325a5b","0x00000000000000000000000000000088721ca0c4a54728946f6093a5c1b3a7b0","0x00000000000000000000000000000000001d3a9dcfd52ac9df33d8754ad25ca5","0x000000000000000000000000000000bf381db327346a23cf121a2ca17458c137","0x0000000000000000000000000000000000220aa2a76a60eaa82c80c633b58cce","0x000000000000000000000000000000708dedd8f14282960a1463b39b8cb0cdb7","0x00000000000000000000000000000000000095c171ff49da2b919c7a76dd1eee","0x000000000000000000000000000000c10012f50a78016fd0d674ae7059180cea","0x000000000000000000000000000000000025a803842ab85da633d17fdbcadfdf","0x0000000000000000000000000000006382211864ee835934bf08b652ff422752","0x0000000000000000000000000000000000133b1ca47e76115f534545b81211d5","0x000000000000000000000000000000bf23c818b0822d562e5ff5afcd8bdfb466","0x00000000000000000000000000000000000de8c4931bc5605e7b81984ceba5ee","0x000000000000000000000000000000904229fd90abb1d41607e640df70175b30","0x0000000000000000000000000000000000232c97f61512562ce2f682c01c6a17","0x00000000000000000000000000000054643c1ef88496fc1711b36043a6af58ac","0x000000000000000000000000000000000000147e171c5e2809a737adf795991b","0x2efb642e2efd8c6ce0ae2ad3bb27ad9cb78570b320086af885f11c712dd957c7","0x0447426cba8ba869c0102821d36a8dc190e33507f2c6b8e8c6e7e706b7578322","0x169450135a581fea8739167fc629d9b929d49572db690c19dea6913d5d738f01","0x2391469c46e482bb93b2afbc315637ae2822078401c40d2fbeff596e2c039544","0x16edde52d8e5f9c57cbbed48b39a63c0562b0f1bd7f74fcc94fb165a44803de2","0x305ace9a03b84645e2024091e1b0946529d3133a1479f875d6b0bff33bc9dc34","0x088eabcb807a09a6d0fdb2d20728e7ca2dea3b3cc5d23b4d39a88d7f0f3a8866","0x1c5dd8ff5b131cbcd6c38d8143a5db4c6bd1593f436390b24880a4ce41ca5a47","0x23976573e74b5ed2daa1ae1870a90d3463d86cf751381e2486c36a83ae2ee5dd","0x213c1d8acf39ba783cbc7d5f678c768736c0c1a1aee852adb041c9d66294391a","0x2468b7c61830e845f883d6469479d7a5b4b33692f45ffaf24e22159c91413992","0x0cbe88d0c6a16c258431d5112881bb24fd2e3fc7f00b88a09dd2673c3756edc6","0x1fa10c321ccd822b0f1389541fd3b86f2ded61a21200509f5efedfc785c13f67","0x2784164c9c9091b30707853f7a22b593b20607993cd3d079ca9b0bac2a8c1dd8","0x0607d39d53891e8a62f3ccd0dc683a10e659cabc5416a3b8850149b20ddfeb1e","0x0b693d44636ce447d4ce0e77c2fdde4b3adf8ee3f2e57c1e57eae257dca4225f","0x178b850fa92de9125afcffb4b950019f46363f9e2491fe63684ec402e7617206"] + +let ppZkAcc1 = '0x1a195aa0f0baf4f3077432879cad5a400dfd368e00e6c715cac1001d6d5e22ef1d739a3c849eaf1738860a24906f900d3e2d0f6d9bf8dc779cf7fed1a343e27b1804a4021ac130b94502aca5d579159416ef30cff94d12864b8cf3eaed97facb0f93412a38cdf022fdaae6c6f73028fe52876636abf955128eba351d6d1d63f7105e02a97e7b74b0667f008f25bed1219ecb018f1516563fcab7977b3b16de4c19e99044e016775483945f291cf3cee8023283a6e3938c34c9585ae826ca2b2c24cbf47486a1228324e0884b83a3a1c523ecebc55c3089e3a17629e2324010db0d1d682b0211638c87b03449111890f49167c104e7a3ccad79f8a6439ee78647256fbe1da92054864e8b47eba11de8c12ff33c29e4238efa08ae487e49dc06782fe9d65762df64288e7ff3fd30004695bb20f44d495435602c20fd5d032fab3320cb1e9f4ef53042c2e74e64a27c85ccf1aee11314c8a3abbace47406404b85c13561b2860b23247b0566838eda7638c6df258bd68803948a6996774f9368ee807bffd41ed6c547104770c9a88d4531992bf40795244a4f613a4a996ed5df8ac001d379507afee4965f54443669748f2bacb68ad4bc344b606c6c8036d67ff8416af06c786698597dc9fd6b370bddc3bdb0f3d677875787fbe9d1529c913653c11fbf29bc70acc1000304c98bdb522896bc5ef1bd92ef1955261ea2b5452d94d2eff1de9803eed78e3ba3b7bfbb084801b92e09cba74d8cef999c51125e035810f0a45cdcef83b8c475966528b927289dd0c1a23d3183ed87d9322c2bee35b9307784124ba719742bcded15edf471969d0b569a541c15352bc942ff0b18efa4d0b82a505d92a42e783f5eba6e4fe863ebb476487bff275caaf7272e265b5039f2a164d6c20eea8b7a7c972b6a61feedfd6ac03dc7c4181dedc76477e9188de8e143a56819f178d83a2154a4fc5b04eae3c0b937887306dd22fcf21721cbc73ba09bf053a82a8dca54a324d245b07d7707fd9fe67f67adc1cd9efbe2254f0a70b1ee03338bbc284f1dba52d3d6aa9607a85d0ea7ca8af744194b3ad39dde710c708dd6406b5a5bad0fe7b39a9a77c196b2c5d991a60e88aa1e99917a081af55cb2a6265e7f95770866945becbac7b3d48207ebbb61bd4b2bd19ff5770446fe3a40df86e4e323218af12736fb8677fc999f1b1c2e79d361249142d1606bd26c6162a651c95489617d56de48569736ebe82559bbd8d0e22a648073df6fd71953d0007ca8934ba6b92fbf832fa6ecb5cf5426af6ad0c80fdc5ef2f575a9f6f0b43c81ba5c642eccbd79e083ea6e8dcf2faeb55b29436edaffdf50d4dd0286aefe6772dd78111f6420f5a0dbecb1d8a26387f05fda831bf346d3d5292bdca808558b9140ff93b90cfdabe37197f6facabe80378eaa2348744a6fee6854c361ef633742efdd5f2d83b93b5e5464891c39d6d3cfdbde2685d2a67b368a0af845922df52009d796b12c478c3740834a06837b7f7dd4954c34228bcaacff32acc1b07d07c1992145aca06a0a616574b06e8f19f5129a9e4647b600801fb58df9f01dedc071521950e9baada6a690b010b9e03aa9bc4f436baa06fba27deef3791af7b309817fb45b5d79dd283e8637c95747275a1f39883a97cc5b31f28bee93b696752590a4bf4519bffea3086b4c6341ccd83cbb5922a64e5e0e0b11bbe96b23839d13e0dbc76461dffe6618ff55901ca82eb1c7ec3fabfa729d5339c02bb3ca5f0d66a10adbdb38b1883bfaa86fdde90041509e75f22c1e761772d3d4a397a4e2887660fd7c54f0d7d616bd240ee65167bb88996cf8a6a513eabe55803240072f1ecbe1d13cddb2361f05d1d0ab611e6ea487ee2671ec9d85f36a7d590b6dfaf4ce1351b5f513c1b96084ef135362382351067eb095accdc3d94439aae0a121625f28c13288f58993cd58f629b5f59377ab1744e311f10854fdecfbfbd38b108d56e6c2bc137dde01cbfc1605cbefa206c567c5fb05f14e4f202c9b36c1e213e9042d3075d42b6c0e658bf0e4181cea5b1494eb1d81e9925adb6e311bf21598a80bd291186b321be213e8438766cc609bdd2fdc61cdcd6b0bc84abdc696e3cecc830d92954acdcfc27fd13cee0c6132ecd3b89cef25e6ea1336b04b4cb938480f816c32b58679fa0e6449feddb8ce055cf956e6d2061fbfb304a6a6a96fee4c5e1a6fe1e0e6be8e2f4cf1dbe6006201fc97563c43fea958c003a410764a4d2ad7f39a02ede4823d9d8702bed2503eafdeb8e17e1a79aa4d1d897d6d3121435070a36961f731d9fa81fa67730ac04ea2ea1a347be86e23eb6afdefb57fba36622c3ced611e768164902efe87005f55ff1d57da2ca511e367c6a79496aae9d45c862b2140e135d4888974fb0a3cd23de6cbfcb381bdf60ca62334332311d715f04e8cd3118c48f63aeb66eb9afafaa81556d5b0c0664c063eef4e71aca011fd1596e3d3c1903785ad18266d77286a9b911c51ff78cbd091e40cf11f2c6f34e95e9fd6d7320d1592a61b6efa3c8192e0c53ae5281abe00d972b8868eb94f2e9b8d892479418ca68699f661a1f14d196fab44f6eb25d3f108ed79f2c06396c7be00ffaa2e812b61caa7638be4cc52c1eea2026b542d8ccae51fe301b26869c20bbc40d1941123532099c499ba48a667805efd11635d3732745f9d983faee8e3cec51d03bb22db625d250148f4ed1088b6b713b1c580a41c5d4f864dc323d1bc0af155cd4d01ba574c68f9c2dfe683d99b1ca300c1b4001a81fe03fc20ef97bb9c9b426d35c2a800e79df779ea6ce3e8c91eef9ae970067befc101ad13452c79626a517c806155cbb387299bd018c0e9c9effda5f04aa23f00f5a09dedf0468ba6bb05c48ba0750a337c4a904a3f2cf7440260437309a635de47339cc67aa69338625830c9605a7a9c135b00078810179b4bfb2fcae7b7b704f137b86c4d98a538bca9fbeb5057e22bdba22608392eda1831c19c08e928b2fea12ba1d52b9624e5d92a5b1cf177195afe9c114b1985628a88b5c04d26e9012d66755b285bdbe1ed0a9b4c7972738d090912be34b49182f97a7ac98d10f65f35d0926a27764eb8403407f05e813107ff9fcb711abbcdb3346b485a07dfe4fba30ea2d37045f542c818e4edb0c100fd3ef92fc95b3d7705ea54349f00c46a8b3e73955441037add9adb39bd1730c27da5655c1f5d55398951fcfc48a3eefa4c2d2de373314ee992d6578eda575110db386b4671cecf18d80e13d4c40d1ff66ecb8c2aea37af3d367ba784720d01adb3a719e14d5af1582baaa2cad0c3daa2649482a7c17565c121edad98b11601ef1b4e7b9e669d0c758afd4df58b920f9ec67d5f1aa5a1a77ed65e65af6e4ce0e6ce8b30b5b01988670db71129ed20f697a404f02236c8a3bd13bedf3c9949e2d71ced64337b6e8c9dafa6ba58dd4b2844e867e5e1bf649d51d31e8650ab756148be1c32e8a2a255038e6ca66267a8ee34146d9feeb55268e3be3c47f3c2c231829d0e9174a1957067a2b2ceb4132a422a4dd18bc838a796f1f7d229d97aa521ffff6beaa9dbf87f7e3cd7da84c5bff2de801fe0e8333118127d2eb1f4f0c6b1d59e48c06a36eb362179eb8c4af1052c3514f55cb4e37f48774f53e36709ff50d4876a9e34c0db4395e0741546dcce9870aea4d4e20632ebe842015a892fef31fe05b27edc26d8354597e24d44948d2443926190d8dbba343ab43f8008d87b81163dc3b48ff7056d6ee92d26ca2a0dc9750a8675fe5cbad924e243d9dd4d08a05446fdfc156ff977e348f200472d3924e4d96e3bfd2d8fbe6b22c47b591a6860234fe4db9ea62f78a951f709b7fd17d97aa036dcca0cd661c384dcd5259019205ce783dd859610ba49521c5f505c6fcf2c9e05f5e2a63361dfe491732f261c528a154e28346cead684e46f314c26424fe0440e7b7749e9d23aa17533260731826f7ef409a2ac19448f488cea6da5aea9c55884928c13d46d626f98b87d79410004945c1e47d2fb0f4132694e5a85dcdd22c16785e62fda87a6e63d92f1f21ad18d056e855193684d83232dce38cc9cf0a93639148b3df165a7abf2f005306e603f577c2350a857d01839fd9066e96d4a172daa61ca0fa27339e6c7a8aa06f001377e45066e01c22ebfa56ecda9ce0ef13dd0c404b8130813926cfdb626a67c80897b7f50ce1e5416a63e0c0385d4c1503633a0c546fd69cf571af2e2fdf0694271578c2f5456d38327e74cbbd1a1fe1eed2082340347bc04d1532fbff309c25254dd159e2ff25cc0b654bca02e74c5008cdb056b958d9aa6d722f259f1b367c280b886be62b53c2be1e07e7380d9a0f22e82305ad728d394e8aa078e985db292d816103cdba2e0ec1e0be82e2820ea885ebc7a555faeac03abfc4511009836c0b0313446adcbe3835714226fd26cf70cf553d5f32a740aecb8fb76f8ee3503223c874431ae31dfa7e4b1c56126977ccaa1157e6399c3aff65f9e0c655b7f7851c84a774653452a5fea3d04b1c9de3f20011e53103b127c1bac1172e97f52d2e11f6b12f8705acaff50fb518d78878abb3b6060dde937ac77a26db571aa7d0a31d18f1a17c2bca095d1df2712d1ad1e4025e6a3c1dc225808e8a2987f05aea6c0e820f1440c87dcf3c23d5d6fb27ce207683c736e83e5b27181964d61783555a202aa77b27dd3a792339a544841f8fb3de4e332118fe57e34f71b1cf06c125b7076c6fb9a4f366c6b6278766a0d057029d7ca2b4930bdb338a12ea2148db88e0001ddd73281549ed85a0c550cdcee498201376e1e8ff07a9f5c98e9f82e467dd30063e7bcb0916e602389531020c26377843114fc08a8f0d564ee47de24387af109f857edb784497aef05231819f8d18aed8456fbe9a98975b0411f317ca30af2534a2e4e9507989524b5f0de53426c0f2d75348251874a6beeb79f74abfba8d2fb4aeb32be8cb40092f1b58077b101e9846af46a17e4878941929ea1ceec1af214dd3021582e36db7041f89f1ac477ea27fe40626fded5e2283e7f7e6d78b901a5f06e155eccf7f5f46a3d793996a0798f7637b69bec464a3c64ebb4793260321b003c785cfd33c452873b543bab369d7d64b9762ad4951a7aa4e770350f7542c2f7e30a3096fdf61f014d1af9204737eb669da041708d1b4f9ccb587c172e12c7ec5802d3e3359fc9358532ab8e972bb55d65373a2b233a7724e2d2981ea8b1f18062e5cc0da663259e11fbc26b7dbd2d4cba70d2720820a3391bb27d8405c110249c3b7e8de261cfe70db4c124bc75d36d3781f16aa172776c496229bee340d4035e83b92f3c3dfa7b3271ee9a08612427e578b1dc46d0da2c7ad52e017f0258f7876a7bdac4ede06510967517d89013ec989a197b373fde006aac47568aa28201f73e80507db95dea3832f4bf704bafa05093b1ca569d3f5264fecc72f5f1c540bef17e2865aa319bd092fd3866c22427ab4d5ddc7efe5cba2b9e196e81f2308951d6cf18418fdc3eb9bbb5ccc4793b710a7df11caaee21fbcdcfdcb19b326343e4a34cf9d4b429b33d96fead0365e1c8ff81672c8c2bcf222ee80d40b820bc7a4d5d01f363eb3536c2d538f367accc7a7b3ff8a81d4bdc58e9f1667358a2bf276a94fc2182f021ff171f954f67a95d68fc3b0f1a4f72630e8d46ee0d7fb08c51d9fb3ff42f039f62b8d204d860b2b540fc5711e81daeae4dc12c98313a22d14d92af847800c223e2af301c6dcbd44c2e68349569fa06497057d4686be7017081c016c9bce96aeb4c0c68d6b9162f4b7eebabad7fc3076a2759a1b67d12a0510640992ead9d859c297e9fed4e3dd5373f6483d0a03988b41dc4c2e0c8ad8058669ed38e6cc3ed77c9305c83d91dff5ecdbb79eda88749da948dbf5123f602ef4dfb6042860dfd4588c718caa8d62147847681c68cef28a9c254b118a791d05eae6d2fe74e6732b365423ad18db7a9f244a9eb589dc3b0145d1da892782462e0270b5ab8a230fd2493df4f540fbfa4f5dad8de672d95ad181355d2b3f41a5041bfa65ce7242d28a4e8eb79c522718faf5dc7d2d4199cdbe6c3a809ca314211aeb1d1867c02281803a41abcfd880907f4f532b4e3d1537d159a0e331452e12272029bc75fdb7df75fb648271bab51888fa8c9200ae3a67f5c3c94cb17eca2b2219a19bd26d101b1757da30a4db50bac6e41215677cccfafc636dbfb6441c931fc330718f1dee91f26377858ac7d27f8f4b69889b5d79fb14c85267d08009362365dc86273f68f2b11ad789fd49e0ba64d55dfbca056641d9b92d93ddfe07b8' +let ppZkAcc2 = '0x01db4829efe4ffebc98cca833d6460c974ceda3d80a621f64a387256ce0a48530f4b83896c03e2c11e52845ca040a352e546db64791b7d98e5ea9c61c706e3b125c04ecb1c8c8e52dbfd21806a1adc56489d695fdf4a912023bccbf12bcc61301179b2bd02398d2f04a936a64cc6f5eb545a8ce4b8eb776c86206947cb06a8e01527f987aeb5041de87fe2a80fb9b325b74e509dd23111c3957d0bf89a2509b42d36a05eac9675ee9665ff1f9a11f3c411c058aebb1cc4abf919eecee6095aa7256a6590b83ba8e7abe7a00090416906b4c60318985737a7d562f4e5104438b9057e08d1d304fcc344631bd14cd07d32e159db2133ecf76f70aa9ee8ef9390d511c97f4842316048681ea222b745def845e38b9aee7a51efff67535c13a4cf840c84ac7f4c0b0510e0f5ba39118e11db7015bca260697592397af4514e53e0e8253f087ec69fbe18c111125b1854a62d98e9d9cb867c378118bfb394a69b73751fbbbee1170228cabd24543f3cb4a81ad75f9e57e7d29525c021a9cb43dc454e01f279b18299a2a7996c11b86e34438332a64cf30fa28204ff50a1c231b03dcd2c0534878145adadc0057d089dd1eaa99bf1bbbe6bdffbf1c5b3e6459a12fa9d12e4c05bbf84c2031412252bbbf0dd7edad4a77f9fd1784276d78e4a8492f76e24da93678f38883a9c235b40a666149b3c306f4230109eaf58b904ca3daa54131d45dcd5f49995f49f7a2e438f00dfe063b3535d2ee41a33118b05d2f6b4594018b86c8ff78c651fcb825d54d3046b54b1b4967c2542ab36da9ea53e101f54872c7714ee2899d7d4c4533bd72bee480df8afe52933454331974ac32b9ae530b900a95623347a2b7c4fae5cbc167f14adaa2d0bdd044ce1cf575a23b4d1816d4f0fa8ed1dd5a8d179ab55b9732e3347704838828e385d578d25d91f48e28420ea0988a892215ef5b66b2acd009bf4f457edf8ea6930f735d6d91f9737d4eabb27027e97e5107008a954305e72ac4fd5039403031ed7710364cc8da3f45875fa7e003c5400cb41f0940a2d5ec578e30403248ab1b0e8f621290c821a5c875e2ea42e8d665e47f6feb8117bc94ad1fc4f0f26fd7023429fb676fd81dac2afb4c2d92bdbfc7868ef182c11b3f4e440e6c94fc8a5d1a10450515cf0527726ae14f5132b0312672a7f6cf6cdb4397c9bace141bce7728f295dca0117bccc47d9a726c217e3532b1608b6c5934c1419e5bb0c9a789c574c1485541087a67956224f99621d310d3c02403764fe397521f3ab4806f75a77faf6d0daba473b8cb1f7c48ff60d01867450826023fe8b624d2ab747f31ed1c55a99ce44efbd1eeaea77fbba010d4e6ab77deac13c60912950c0fe87286064b8c73bfc6b56b205afa2d0d89fac2dfa9fe53c0306fa8b8c0ddf22c88cb7358a121e1e220da7877f0b73d2611ea409ad4baade75972e236d0068f443aa9130a20c575e833cc8578f3160b54a12860cad49c99f59c53a6657c62bc449be8711325e35a3088ccbce286ebb5a658fe7284f8eaf9580918f5e981caf0916c0f150c8626cffa12236cb7abf3bbbebf8632ebd88adf648c2c20eee5d8dfa6442604620730d7422a1e40ab8e25c43eb9a030d66c9ca508a9f63efad530d8a1f5490fe87c0a0aa56565ffcb1a418e3eacf0b10737fde424856ad8bdedc5da6b7aa24b3c487f4e02f6448005f64852c946c7c0710b0783bff917bb1f3069a7846f26483cccbde48283a0868b88c10d28b42ae113167848a9162d5f9fbf44f984b1aec1b7caa673840ee1365aa08474b01c5f02c3eca2bc6c7e5c7881eacacf636a0b6b57649a328688c8aa066d27e353506f70e7fb07b081087ae304b1d29279f4b7fde8352ae8e9b7fe82acc327730816b680012bebf4e2c67c74e7a908b5918caa2e0dd926cc9126df7e003af12cd026d522675d2ca37a711f80accb7a48546ed7d451ffe5fd2f7fd62c696d38a8c06fd06126b6ff7a527602f3c0f03fbfa61ddfb3a4383bd44aee68accbed81784b951901e714cd121d16473b54c9611376b4e15cc42f288b79688d99f412881fc583d851d5b5c93ace8535dcd652b0af02871bf494a0f71e9d0b1f5f8a259fe5dd65c192ac9b5565b275363904ff1c069c9885fd794c9bcab157f0241348ba55a65bd2525dd46e20dbd8c68885959be21a826ef3341abc19166c365f682987f9bfde52c2ef5528daa165a942d9c4f23f0ba08ab267eb6633ea8819fbacf886fe45e848e1fd42f2a82f13912dc9b2720a09437f35fdf69ece319130bd3b45df221e982a6002ff48a0a88353037fd203c9ab2d870e56ca520f8887586c182e30fab7050791d4cc8bfc015ca8f923d203bd654381a88b61f13322fcf382752f522562538c827323e9ce783ffcd5180171e960d7d4917b15dc804e30443b9de7cd54fc4e2120d3e657d0931358362630509bcd4fb74bb4953228812c71e7ac9af2a1761eb2e1c32d3a24e65a1265d533d44f2c2df9f3be56936cb51290118a297edfcb4eb2700295eda130a3fab3fed6ac749f5b0b370a72b7c60e009619544d089085915e124546a2794c85a1e5e6792e909695ade14f807731e909aa89ea8d2b46eb581412e171458819cb535e37a8a886263021d2f25bbb928dbde95f1f84a2f2714946127d86fd818d6b1c6ae6c0fd8d5f9b1c3c64e0d06b30743b30682bff3ef665bcb002b1ff453d8ed609351589a526c2cebbe80ff36b96a4ea76cb4a4fe6c57257014dccd729ce2e8f3abbbe549e34200616a3b532b5b6b9e29c363121e761f50c700e80d2120b1f9616f29b5200a2b433ce890bb23465e4e5771decb7e177150670dff21cc66648a85b9ddb6c62abc58cad212f28be5e3efdc9d8010f1522f214e086fa5cb8041940bbeda9ee3a44734613202a6bb8bcf1724adfcaa18142b46590c4fafa0d2ad9041013cfd0bcc8b1a7854b0ae11708659637506ca3d6bd6bd840176b131ad7a0f46b3e7fe2b849c7ab9e9c1a12a16825a721e3dcacf8987f02c154da4b9b0e1a054ed062d1940dc912f057741e602c787c78b8e72a5ed3af8e9156f753ee7407f82296bc4b82800531a8eb988e5438ded9d8a69689e26539f382c5c05e66319d21aa5e86dddecc8798c3f9b2459f780937eaf146fdf710790481eff1876dfb086360c61301deac708cbe7a4e9f2d6879f58cd6a13ba58d4e39d0ba98ae7ea21bec27bee0fc8c4bd8b8ec80b94762e9662f1324c8b538b64237c2bcddb8e31ead68950abdadf301feea9478630069ad6006867ecaa5bf78bf72a0e6107423f6e0dc8e9b19f94faf97aeb744f8aa4938a1e91db4522e3c53b8f040be40c176202b120f893e54dcb14702c0268f1c5376d5adb831fa50fb14fd9831b9c841e8552663e591f96fa7cee8f2e948c51c8d40c4b1d537f4bede08804032d6b803c35d2cb3a3d381b68801a95755f6dd81092640f3666ae8e4427c9d973186d5ea2294d63da991413a34aae7dbcf1b7a900c276daa2a1dc99a530ac4f4928cf715b0beca3edf39f1cc8b3ff1e3137dfb9179a78501b0f41881eb97027280f96cc235e13f2d0229ff030864e6796d1648f25d8ffcfd4a44fd2505cf93eeb2d37c2542e8e689f526696196937500c43127f681bb2a59fa569da119611eb8d268f02b7df7b686b9b80b36de884bb868923b5af10aa16252f17ce9398e15bdb100b87b9d7d92769feaadf48d6d4dfc4b7ad1dbba174e1d9821e894ecb59f9692855802125f879ac04ae747a427c630c15ede0455838864fadc177d760d770e92e120aee0ac8a099cef99d9a0b9b5f678fa7d7e4c2ed4c8fa5e00ec01049827809fffbcb1cbd821e31d2eef16d04e31a2a64492ff8669591fe461e5c91bde7c508c8042f6060ef0124ac16b5ef0949c53767de4613f7c3b347173df1f0978cf61a6d535d68da4aa19fd20d7ccbe8790b351c93658b314f507d63aaf34c75d3be1818e5810d8bdf7a3801d7704a98d88fa3bffafa84fc11d28e130fff9714206323299dc298e5e39b7ba61c2992a57aafcf1b9e61a7b13fa0defb356ff5c153511ccd148efde93b1c5a3c9c76c6652a68cdaafad0721b86c0a4414fa61e91155f0d50cb20d90978cd8661985088c35ba284a8cf6867b54b319a703be798942f762079cdd61ef0ba3434db3c335c08486005b2799123c4dbda5ed5232e7947064f08daa5aafa73d6b2ad61434b9816116fab2c84462aecea7c9aadbfe3e2cf13b72479bd68fc9121aae3a7341385b93b4605b3081b782072275354da66187ad3291e1e0f0509fefe59dfffa1ebb20bcf3f6f8f9293b1d34be309bc9e8a5458884a2f62f3bfb12e8181f0b2338147c4d8fa95237b71a886369c3ff862e9845e84fd2925d4cad1a7b0d87f4be022b1efb70cf9d7506f0dbee955e07c60a64d900d78114e573f16c09db67fc06de89f3c984782381980a46c60b00350e7ce087983cb18faaee94763afac708119a2c2a3c9fce8f682e508ebc628ed72d38e3d485fdd0bbc668cb625500bcd177d6a1defcda3cb808115da81bb3a716e5cd647a664d82ded5e4e57a86fb3e56d25fb4907a811fa2ff920a0868b466c8a289ec87ad73311755f8cfcbbd2d150f38408e781e226af021f93fc81f70023663ba3cca1cff82c83293659a569422a8f424d81f528cbcbc1a021c7fd4e08c276d53af84849ef148fb67de432cf29d87920f81bc46c4fb5e5f5ac4a2b713a52de60215c4e70ad1bc501bb4d4d08875d16987e424525cf951cd4e75b3e129d2c3e8d83a63c211d26dbb9e09b7bee6a7b916e7d84829a8a0bf0844196a594728edd1acfa576974702bbac59ffbc22cb2208a70bcafe98153b539a431d4c0bdb5137980078eb2a6825b9da74bce46f19f13f9cf2906cf1ce764a47cd3f7d62a1e7bee40468e15e2229859bf218558d6a975e2685a0602f779615ea98461f586ba490189a1e777bcd1ada49667299d5462bdd987d795c4205399fdf75f621ba5b4c4d25995c30216d2cf838821e7ba04bfc6b7861298fa6c653e292076df8318893a1f7e9246a7a71120fccfe07db8f137795bd9ae27956dee9843ebf619acdf3b749d2aa411d22502d6c67dc22ae9bddf37f44c6ecb8c59f5a8690aed0e803857c75750bfe83e1471516fc74f950b9ed5e201b4a432c2788c9706aef40409bcb74a0d21b13015c2e0409225e19efda85e2e915af836ec0d96036c9f567a960db3b8fad6a2cd2573e22fd5933c27ba06c40e7fb411bb349631e9a6544fba506b8203aafecc2b05439041295a3c77afaff90c39c8e1b4ed5c9c15d1fbbaf9a35f5112ddbd103fddc8c0d64c0ebdaec5521f4eaa6ebec1cc4a881b6aa0837e1544589c4277f65dc541d218c61060ddd3525804cf95fec4cf6afc882332de58363e578e20c0e08b52e802772660169037dc28c561de8e7b94366a6ae3b0f6634f602eaaa1b64f311686422dc81b00fcec09310afc4e831ff6158d7b10f9ff09f04febb4f3024d3fbc1a321800b54db798b52c8c8f377a7e1d430578223ae4e0130dce44b30ff95c677c31d73d640d921ab93301f1cfa74bc3bf82b6127a0c4a4007409b271300d9acd7e1900e9aa186d3ae5bcba8a14e19b66b4344b8bfde4e1cbb9dbd9214e86b85091111d1baf651f1067eb4c52f32e1da297b467248679097e4864a39e3c955e9d831dcbd6987a1815ec2187eb8a939bc6fe92bf7b92ba86082e319086fef878b5c52e327694c55ccc98b861a5560fdea1f65abc3edc87ecb35b205451174b06f1641a1a840897d40d2f143e3bbf6579615357ea6942a0593591bd85aaa2fc57658814547abe9eb407276ffbf8473bc5abac5a7336c0c990f4d407f02ee34861e75110f1b08bef5b7eae3912925fbdf70a46191a728b6a737440abeeeed89cc75e5316a8e30311dbcf60c2391d1813b2d5f79c2b47d1dd871c922c620b5b5aa6de9d1d8b2aa7f7f5f3ec1d15ad141eab810822bfdc75954bdce0b8ef47d79f6b08e02bd17796d42513d74dcd0209361f5c9980d5310c3cde7e7cfabd12631709293b09e081fd5914809c88a4c6f71ea7dc32960591f808abe193536367da7cde962f2a749063737f8c1e47d398499a279349f22451ab0311dfe705011fd0bc053fbc1b67780bd2604cdad60ba95cbd4adcd42d291e0c6da48cd3fef9b650bbe8d8941662775ae2d5808d079587d5bb6e46dc3734ca632d0b5b2be3c88f40816971df2abc362ddd2b2462f7cc09827c05a326ab292c336ece62da0e4f99da891092eb13f1ea7d67afcedfca17b43e60faac38cce1de487a41d3ee19edef1c95963673' +let ppZkAcc3 = '0x279d80bbc97bbf3da646186b99c54e23aa1f625f13b7c289405076d10c3d1eda08682cc5cb24d51ceb3e68f48a18ff0dadd25eefb72174ce53515f60ad243cc70861730c93685ec81cd3c5c2a43639ee2930709b5e98338c2b7c4c438cd5dfa81b565eeabae1289ef20ad1a55292b525f1d1f59f8ef6ad75bb7c565ef1d2ab011415e8cd1c8676ae97305a83f4726239ac610ffe4200274cb137a809dcf9aba7293d766ad16c35ad5a56c8267e126b8f1d68ba168525ce0da0ed62fd6fd4c7071ddba6ab41ea19c415bbf2d6ae3161b6f701f1318857516c52281abe0bfd2d92139839cb43f6d7e3cec4b4934642a1e9439c74f5d0b78a1f81e8c4bdeaf153c22cd00303303000dcf213a1213a65304207969cf7f9bb28b5ebe4708fa924fb1215243982fbf3b381f2368cb402a0f77f08ad781b87f1b8560808af9f6043bd2512210ad35bd03ae74c2baa0637d67f1eea32d919049d5229a57aa1f33d3f638e1819292caf37f1fccbb14707e886a8b09c1dfbd4ecca597bb584083ee4187f351b3e0da7f66f251f6bafcf9950be6fc4fd86b1c225cc3d5594e4889d420a18411c0263c044d25741eda74e5bb52317b8c33fdf760030c31dbfb059ea7582af201d680886e445d7009220e83069891ab78921cdcd61aa21bac0a3e1f40adf8c9d11477aa14b6ddb0f0d5bf7c6beea9e1fa8ad35964432cfee9fe7f69bd03b478d24313909d3a56ec35e8d511c500342b55989fc5ce62a3a8d4603e716736e6ef000815cde83dc8b1c2c5d1521986ee95d8a65a4ec03d59da1a27e162c160d2781027ea2e3358c7c1b9c3476b8b5881f873e85c372035856ffc3fed244ff4c577a0099faaf4047aa0d36d9b23cf1c9805d196908cc84874d7d842b5152502d22b31d6f1122c3d94d788757728651f4f310391aa341aa71c6fa69c6f2067a72fa7d2c0cd8f64ad5c335602cf348fedf0a19dd05d42968cef4ab46dd1e441a57fba50a9c157d3e090a8a04051374267c57c805c62da0989d55bc639109df14e4892408ce7d4f8a64bc26d13ca1fd474b102227fbbd41018c062bf54c7dcaaa39ff172d6b0823a35c71c79b394f95e6662232edfafe83db19119898e894a07f1b47522eedd09612eb33ff04d558ad9eee120924f635b71d8a167447f9a865d783e3ad18ec2692d63135e12ac6d469314c8bc2841cf770505a684eb69289a5840e06d22a3649fff0cca952f4efb84f1e7acbad8fa8d30a58bc6bc9f32e8e732b5400321f67f138c4b3814a8acbdb30fa854b35060406dc150194b8be06e70d42efb5e20c5e23035d4d00f2b4398604135972054d529741fb253b079461357bf750c9eb28b89a4883b6e303625c43b3ccb716d3795dac8ca4a7d6079d41e44dd3ea734821ddcf3cdafe9905d65591d595183417ef09753f82fa88e347152267cc1134950aecc1c634bfb269815b767f51cb93531a1e5357d9286ec903779dab9333d30a2539b3d218935d784300d98a6c0ed4a96c9a4b852bc830e599c55e1cc53d39d00497c3a3bfa9ad08f6d575b3a44d6784089ebb4da5006ee42ce8d6c2b620060d08d62ccd41bf16f651ae8735b04f5d5d4992ec4c1943f9f20af5633789e020dd1de4bcc44363448b951493187633c11895e6c32469664e6562c3f0e8406837391ac8ae3ae3a3f07cb11484dc3c182957628d876ee4a08020c8ef8e13816d7d8e1700a3381d55d4d2d3bfa95901e81c98f6b682be97c1d001755d2654fd7243b8115fa70fbb679b5e6b23b17f14352ba9dd8c9860aeb95745f75a54bb9952c7d22dbe506b4cdcb066a2a5dbe69ad2aae6dc7e4e00f5b891d0d6e1996a580e97f21ef570a0a6b2907a0222d3efcc24fb313b9871c5a023d7ec45122c1bc5759e7114f8b95daec1012b7422f47042f75411c6e2b64359b9fba729daea8041c251c909da06c5d1577b9b43bdbbbc0fc6eb845e7e4b123afb08b78a50ca9279ad774028bedffa7e3e731a5c2e13d55158d789033bffd2acc42fecf9d63021862db81b23c770b2da5e88e47a7d47cd1c632a63b6b1de4073483f0e6294010c2b6e8c45142e99562b3dceb20f2410c6604b237da9b68af4fd8daaa389fbef987693efd0079a802b254cf84c618a2c8375ed6fbe85e6504d691c1d39197ac61e91368206273e4dd6a4a933058b66418eaed154cf9873d9986db31152e0064d2d1a8f8b4d1559dd7b5c3667c0b1d02a5053c96832bba4f980e13bacfda401c2f8cd60d83e253e3870a895fa2873a0ac901250a7f60f3668d1f23bdf8d5df4bffaa892346122750510d98208de50ff69dd29fdccbb6ebfd2d54af83a18f2a5c91c84cd0d7f2f82211c075c3d966109983bd1b911af43da870248b40a081c09e17ce914e0b11570bc537a117611f855168cbad2ed38017f62f590ca499d5bdcacb5b1d8f2822e3dfd77db3246946cd62114e6e8d9dfcc4ab00586ca61183b90a1897cb2183c0eb2d677e69d85abaffbd1d28a900874987deed1143dfee2a92274f11323dfba1e750ef69c2e315d0058d0b8b21d7a8da0d30ac95ded5bf5add3b60082479a180adcecc8eaec204c37d010b21ad661bd0272950b0f338fa697aefab395bbaad910fd7ed65b09f2206beeeaa99b2f39d01eb8b7a3e4f34bd87338f21e640aae542bbaab7850041e9ef879c0ec8b73c4329386ffbfaee5a8e52908e1a32b43c3e213e224ec10479bb76d9dba5f69114e92f22fc07653230589c24c010b00e6ef971449f71c87114ac0ac9abdedd0a4352b217f182692a4108a58c9560a1f9ab32909892318e8d5ea2c8a6dc34289b91c68c574be05f6ac570e9f4be3cafd3324e80d45729fb64689a44418a36c9d8ac28b2ae6a42be22e5cdbc4f44ccd0d9134b820f28dd32c72e4e2bdfbaa089d8966bd7fd6be50e732739ea5a2d116a09df37a0a3d60f10dab6cf4a1120724a6ff07e4d7de274c2d455b78270f3d4b99c3a5362210ee817527d2f460c48d1ccb4769c746df8c960011490650721c3edff15de92837f619dacc37aeab85f92517e11a64c1593103bc9509dbadf647e497fef79e10fa68786f0c49b423b94df88ede84f1c0af967c743462ada7b15241de9a24171d13037eb6648828471da2849d4fcda7bc48a911c98018e2079bffc63ad059020d668cd894ed95c3db53704fa5dd8cc50e486896699f45f7a839adcfedc9ced70c8e03e082696cb50f9789cdaa69a91cf5a4cb03870d4a4aec1af45ac4af02c1221ec99b09e04f9a717be27de289ff92c810e6b6eafc16d4a8e489dece4c53820a4842d32c9679a29ee08d70c3abc4222eb2afb66775876d8fc48eee2d8c7a911d489313f2de5d70740494ae116b4a06bdc65fcf7468c2d68509a1e534acfaa90c32d9a93cfc36b79b04836009e4bd6d5a7a6541a46abeeeeb25cd0db79eac8a1e0a73c06f48be90e8dee989c027d2dc96fc9f782ec874c6c89f770fc569ee0202b6d9323e9065264dc06326ef1ea0874494fa2003607dfed9f2142a383884ae0d93b823694a396590dfca93810782a24f0afffaaae78002c5a74b47a77943272e82a11c2e497bed4919838dce1195956b63be3e258aec920b8e95f8a8e8ee131aa7b1f613449cd657a02fe13ebb9efc65d968e87a569b66db71db00a4a8ae9e0ecc0929f9cb87522b86766667b85370e6c4f8035659e6d94956dfcd8443572d19e768a88e11746d9773775d2022128edc0efc3923b936b99ec812690b14bc83166fcb127fc958ac9f69be28120b326c8797ba458ed7f308105394a61d365197295cea44e8b569c66348de6f978fafe025240be8b6f61445b9758f0902a7888727d3a21ff230dddd67dbe04aca776ac65a177240e752c74f8f7067d21b85484b21e2255b0a848e18dc1ea6c4014936ed69b1fabb73668023ba5688d201fb83980d0ed7517c70c70df89efbbf8e194d3afc8167797c05ae910f08e444bd0faabb292353993318a219314fd93483afa39bb291e38af3c0ff326292d9597f7b6ec71af10621da1ff2af072c7c1577d73762cf0b8c6884c876487a4af6af6baa49972dfa41eef9bd5edf6d213c23a363701b784935d553bd950c8e86edb6392014e1299c35f7a7f1ea104238989784fd033489be4c691e60357a8f12db28f0571ce4086f3359d0dfa01a13e05464c3d28017492c1b2a61b0ca2aa43d695d69f311e62ab6a71d7b2f5eb08d7a3d27bed65297c7e220bbbd5c8013f9eb8bad68f9eac10f584989e26cef37934bbdab2047c235981068294e75079a55142523488b2d081834ec3e755718c7597a2ccc848deea3bd48ae32897cff1634ffeac2c19464e6037361a4c2800615617ae417f0988fe5ca30f0c4c5e6667e81ce44b8d36f171c03d70999dffad17b251d5dbaaf0b38d5b251779266f200f56e8f81fbf0861fc2092b6e78dc2f59390115bd47080dc22bc1dd8fca422cc59dc75e29a3757e477622e2b094062c8d1943fc81ef5f2bdd8072cb5b7ea832838d735394682bb3b120257633d51837449c61afa03c209ec360f2c2581aa207efbb534c9e77b93d56da01d870f80588898d49471b78cae60d4e1a74c006800dc15795328256f87fda8222f9d3661ce9b8021c950f8f4c926b34ce5c75a1fe2bb938de7d68eaa54b2a742e4a8975dc2b49f56feefbdb1dd7978b24003ff09e06b7faedb7c597c68842a81e2a519a6c5c88b52c253428c3609b3581831c2d7542f8ca4a71a847ea83b0591eb8dd2eda8a7b1d27ec95f4fbe9e5506af336985b7d14cf1cc7687acdb36f4726a6388bec50a541611ddb5feb67e9e1933e1d41e7c3e7817b06a5d4b6387b020cbdd1d38c989fc2651ef61957aa2fe1f72819c1b7f64ce0928c346e7bb883a02b445081f06e7a161a4ca0b322ded1453a5f324038a154493b73a4b77349939b0136fc9af53adfac90197c35e3e4615b6fde2e2dce8a52b7ecac130afd186dc110a59a99726bee65274bcd599fade27a09221465b59ac4799104619d0ce206f00fe8a0d6448d0dec7343f03419b9a91bf32b8f3584908c9e80b0bd19e7fd3fe6202de885ee5f670cbb8a3f57ce9e776824f5bae94df004f5a599efacec39692a055ac04a0f40b7490eebb721a7f97cb94195b8fa11ff6f4d0223a327a394cdf30058f818de0478ef3e814969f707096da59610bc5dfe79c521b9f00ff328c9be0d3e1000b24d30987e23673dd345ba71d948c8516e6e1f27403387bd11421c822ed1075bf8c5f8fe8b8797cba9af2865ccc03409b007d8b452c4b2d6887a562c2fc84ae53da4ceeb538c74c3ca83a4bf101b18b2aa8f5c6a83d7980c96ee32bb2efdea8e52f723bfb92bd316314a854961dd753826113213f7e342e10b94f43721e0a4c44532988a206f9498a166f27bd8530768be391568711554ef380e7cf91449ef1f39f3f3a87209e0db3d93a97c2d7738d67028b9092aacc8c5f836f07702afa458741fbf395fe331d84a1618a448d93e083709f31f49cdef07e25379bf01f4c077cdba373285a79b14bae2810e9e1a862264a1d23731b4db32fdc5125825c7f929de4ed0686990b22ebc8d2214577638ca365e64fa960b8093771c73131b3d4eb3e070d649f0fd439994c52eb7c0be11abc95bf04605085052cf7dae081d02769af2e4d88a64a89a94c108e8ffd98bb92a2f8ab18b2f4f0d501a9abefd03ae24f136c64e267846dcf3d4739909fc5564a84cedcd91faf79088392b07542c1a0971e17146a74467b57f5ae3e5de027f0087e2430d0d58c5711ceb54a4612b6d00fa0079a104b5060593757803741ddbbc331e19a53267ee2f445cc06a3917cfd863b2cd070387153ead416dc46ac166b57addcc580a65425fc31d11077d0c0c045b572d121dee9df864d3b83ee40f031d2a6ac1f5118db0c8e05df48ac518dab225056a4515eadc56dad2b5532bfd35b9ceb3e28ed22341cfee79003f970b355dc6de736a52449584f4ed000c554c261c078083772712ef31c786777c782024c40b9da9870008139845ea8071ce9c011f9ec724e2b076b67f71c569e9eb1858ad7b7c041672ddca776a13d1231a21aa455530fa4df30bf89a0f7963f7b1055f3c39618e52c381db970b435b7a25ebf35019da2e6961c7a13bb1d8f8b076153dfe7be151a826c0e438d3e9e5b53d9ce4753bfe8d2116db86313135ceb3d81a37f05f187f0f74d460e8d456d5d4d70b627f542dadc87560843c07a4ccbab9109b36185ff6a0f4e51cab0c01cb25b2191628dce7c725695cf7a84ec752cb8c12f5c5ebe71acca471fb61c0eab1d93b2c64a1a1588d1bc13f728b390d50c0cc' +let ppZkAcc4 = '0x151792fc96b169c4a23d557ebd5ea3be0a755097e7b85aad488f0e291aabf1fb25053c0dcd19a01ba7c97b9485c260ba2869d81f4d65495a659a1af50a4ea5fa08640abe66a42e896449c32e673dd621e5a7d5f5826869945272dd2e4e53bfc825a1675d12e8f6463338d1f91457e51ac8b091ca91ab0bee5142680ba205f26008e0bbed4e41ca8f4628cee41923f2fa439fd0c9c204ab1004f0b755fd6fa33b1683aa343f8990b8d20e9565b71091a8f24cc69e51b3bcb2bd9c6e991358f69711eee28a96ce0e8953dae8190b029f076a87c9d1081c6287f2ccb29d8d053e31277c9d26478e93fefdbd4a4a970de988810666785cc170c1de887a58ecc2bd1f203525d51cb016563f71e28764d543dc1a8dab8641736c6c36295126b64291651b4ba424acffacb4f8a4dd36b0f30a761321f67ea75ae360e02910fdba61ed7b0cef01649eaa410dd4c244534aa77463ea8896d979bc250119f89a2c5b9523e8184c87e59b9877999ec2218078132248a9b6d727cd99aa0a484354c001bf6bed29b0a2a4ca1175acb93cad4a9b28ca2869189606490a9df8cc008dca715525862d318afad5ed391a6178a5b13975b10e1b1d971e2aa1337808fe76c8bb1627621c28571ce9885df5fdc0576e06a49975f52a05db7462359b7a6556808f6749170f765485495c29f16c3eda4cad74335ef776578ae7426a9dda4f2ffdd6f0ad9227b6fce3e0659f655556ea2c42ad8daaeb5ec16dbec27d08d0bbe8639119474213cde04ddef74fb1bcf3df464cb7bee99037d04eb7e9d1e42cb59a76da89681513f0705752e6f4079d0c1a9d4bef8446bf03121af31e25a41c0a2bbe389b867a26bae3dbdfa1ddab5afcfafd37b342fa1af9167f896b741164a7c9140fa6573c2fcf0b0a87e72bf6b8094f96ff517a5be5fa275b56099d8a02682c277de0cf55007b5d91a6fb32ac8f4c7e479d2fc0ef5e06e1ee2cfe69f4f8ea365f54640bf62d87d48e23fc96b25973a5c61a4625c3c1406420436aa6177793980c506475b120a6fd97b1c3726ef1a0fe5794632af507df24e255fffcfa9f425707c3f7989800d9925aa2ee60a645297126ba838eae4928bf7e85761fa494f3510fa3928eaa248b5009792a792c15cad7341fc60e794909c7fa39575d3a812632701eec04f9105427e7c7b0b43f0a05096663ea7a86cbf139769675a3b225697a69ad965d3f2250ab9d4f45e852ed487824ce88d67059080e64b154d178592202113c20502c1e7b41733d9c4605a34a44111093d0a7c06b24b3b918ec3bbe15528431210b2a05d9e501b2f1f52aa0314f6dff5d11439286d9d1c763cf60d4e506799e2165b81298c5ea868966338d8afd400afdec152c49861d3c7ad6f1c55c0782472e0d9110d07cc1dd6622325e9b5be2cc04b072dfa553b092dad62a32c793ae644b96661ff7539907696960c1cff8e866e34f9c79bc65a0cb102225eff9154332b6673e1118522121a1562942a6c495941ccc3a5ce706613e6e0c3dadb4a4a22e38431911ea6755290dcb3a0804cf87c0e6fce85a64c6663ce54c1874925d160f9e7d2630644458dbd558bdfeae40e21f308e7dc6219d9b3a77b511a67974302275fcdd1fa95da53eae08724e831f1a4b43862e96dd58aeb14b50e3aeb4cfda28fa9294140d61343acfb2fce262bdb1bab0a2cae8d8974f669c0c86c946f4f9d51086ac0633ac388e39155b9429a1f56d374b4dd18f2b374b9f6bc1cc960cde3176c9400d6c9229e5c00647442368889383c7a1d6d6799f06ae479b2ec4ac7202a568651a7fee16dd229a11d7f18629d7464f1bbecfa1b08b77bb9394ae38f65fc5270115eb134fd4144da570a6d2e1829b249500213f067038379b7fbc5c8dbdbeadf92ee7aa4e0bb798e9a4c51057883172ea6c1cff1a82e060594dd26b04365c2d711857bdb7cbd069408a153afc062104bc2b4877f6e3de92b27771032a9628d2911c935e73b72f6739ef0e2b41b6de44e44909ae0e118dcd832c30c30b39aeff2d29c22f78349117217cc0713b2035d4250f018239fa82fec79b4d2fc6369c9b8106a84ac57ddd7b05512d27e89be75e967efe24c17628cfbcf2140493f034775e0fe7c9094d7cc5c3546b37b74e73c5c8fd2ac51ff4b333b59d4d028e811c21710f37418d7fac8bcd35eb10a490825f022808d8e243f1178aa008dcaa549b4ad522fceeefa403d95a8ae9902b70e8c6602ac5042b0edeb80a34d64676d5260c661f7e33d7aa162538693927ded33f592aa6fc0111ccac879b66d1903555b1c4af0cf6a8fe572efcd3fe46621a1ac7bc842e89c84d5abb88e17019b232a88e6e6e0e537024b50c67cca58aecdb5ac73a005f24ce26e7f017ca6acdd00d97ceafb127b02d79fabeebf7180b576b5b1fc4d6bb3cfa59e987a4f52820478926da6c400b67ed6d99a271cf5ad20a5dc159389f307c4ccf944a57389d0b562d3dcbf3db2b8371b3f34ec0e4ed97d3101b1016fcba933545c3993ba6ec85e73d60bd257c0d92aeef65bbf149f2af9208f277b1962953d3dcd78133660989c1ce51ea64150533a139d4adfd2d059934dfa60571c24e90423103d18783dbc4f3fad24a55521905e90b3bec14192a6f73a7268fa0ecf1c5b51ba0d424ac833adf8888d7aa6a2537842f98e12d01832664a5448301d1b9eb3099927fe814ee31f01bda6de22f2dcdf22c614c1412a8dde94faddbbcadec6b9239c28a7b6413df3fdb8de5249121ac5b907e8388fc8eeb5de7674a7d36d586ddc6646a17ebd3812ee5e67c2e6610f1203b4176959e8b9d4a3dd094aa5f3014a2a337b557e339073210c0efc36b1d49365c99998571cb1fca4177453e092b2ee4715f8bf5200b0ce1869923665a0d813706b049301d2578a5b9e6b56cb556c0a66ff2271a72b2872d9a060e47db2fbc5047e0eb929d48e9c217d0f695f44875a81a639893351edb1ca6da7679692c1af02a816e53dc877be0c191dcb619e0c61fff30796fbe33b023697d8ebf37245c95ab7e1b69c16fd4492c4d0941c4ec05e52e83b9da36676cabcfdcee03532fe9d7dd3fe25485709606aeaa96611b1f297e5d906fa1d16cccf83d1276eb440c539122901298222749642596a7bc9401727016f721f5f6b0b3205346c06a172793aec8da535ee34d6997f5ab6e0c1c83660109657d70765e6013b4345d968823b91c6d07229328b27a62da3b31bb31f4118e862fb4f4387f2c20e9718d0704087d61d2597db7737984b5f1700f80e0c5d201599bf16e01638d5177f023bf44074ace682bfff893cd543142c7e9ecce7bb6401bd81499ec9ad19cd838f4a3690f40eae3a523ed55f1af4f4c056848752d8a0fde181dbf5b873ad7b9d4659c752c5ee8cfa882675ca8407229820c0b7df6ee93ade9ff66f9bade6a2da6a72e6f1f9b323ee5e3fbc106bd79be9fdfc5d8532d3464b5ba420ac32be3bca7ea710e167f4ad380e406b5556bfd796e0c2c72765409a44f297a88a34042ec2974c34f0caed241e9bc9ed3f6b5b506b49f890fd76c59d7b7708515475021915248bdd72f031001e65e061c918652db8d23080a419685a8bd34eeb92487f85382984df01b0ebc642c3c63690acb0852fe9f0b18ed849e52f862b44842b1ed1c40ad33e12930b96d8b043e83930140363216995344d94e3d58d1ed00c16a1d35919ce7ef23842340d16b03fe41f2235cbcd3c2dcfd9e8ef0747e567bc9420759fb1893ea25fd96ea8efe627772abc6c1d45994178cf29af234f79dcbb16c025ea49231a025ffd6738b969264806f1926154a0aedb6fc8b77f2aa86de178a2d760d6046d719524bb403445d1dc91de60a7d96132d9710e42b79f27baa46280162b88bc6c61c1e9d3ea194717483960b64e357368a4b0f8f4aca95891e55fe5a86a750897006bcbe65dd917c3074beef4b17963a662c32d8b31f68ff07a07057c38796edad15a783cd1dd4263f8a15e9a4491c3e816a8b1dda7711f0474ef567d4e9d8eba8239fe190df0d99be7869d0df2d1cf1fa2761f82a5a2b5019bd1e647b5c611fa90a35de7250650603766bdd3fd0d137e3d864e083f933c62b85a66921ac862277275cbb45f085f2cf24f1e85504f724d4745868be16066bb2c331afba7254fe940b27ee075e784479a5ee637c32af3a4fd28766fda0f6dc52287fcb9f5ac7ec2305d220da02015d98af3ac3226fabff5c73828f352b45481d8c74d78ac1138cd823c9cfa949d49daea04235a9a1f4534abbbb94d32e8436a83880fb369a97151e0b9d4ebe9fb45cf28a7c8a3661a5865db3c569da9be80676ec1eebb14853635103fc01a054760d98383b4150ec5cb4d02a86ba1de02e6ef22ac04a2f494f713c2a68e32af373f02e686ad7b3bbece2cf1250732ce86b8dcc3e7c1a6ff75f500a13c3ff38762e1443ab905405df5cfb849c1545332f695759564386b4fb70f9bf0e9a6ac787595056639047a205d22a8311085c0c61834454473b7e46e4ed4ecf0aefd1e60b726be3389e0b9b8fe7e0f155524e27f417895d20768e16bfd601250b84160a37fb6ae9b2d05c10070e3f81e5531048b9d296bfd363c1da7b036c842817171e0a1e676e4247a304a5092188b9913cda1341b877259d7ae2916d762e1abc14017f9c570be765b45655c309a84eab1ee9de8773865599830669a12db725b20b577642aeede80e25e94af09e14fa3898b70443f542e936c590906ca6072b973c909ff5593bc36b227bab8293e1d4dd8c1e5318af0838b533d1b65eb31d0b836c4da24ba5f3083076ad8ecfd915acf0d647d313f1190f899c2f2ee8421222ba7a7ae243e0ed5726207e77e8bd71e9d924ca4c723cf336c75be1a10454ef0614f07b26666ba7ebe64d45b8d301cadf8ea639df9b71b97eb1ac2dedafd7ed115034026f04db577d71e9eabe81126532e51fc6e4319fc5ce903ec36a53ee1b21c27704b6355a61a458d8f2c056b829d5a7f7a6f3d212ab0f69df2ef3f7b272168b69ddb1afd12cfe8c7a04414811dbe7d8c23d2dfb6df6c5cc8b8f1052c41409fb8a02328f1887ba6b45264e1cf8e346522652258e9547d6cd4967018a34cc0cebc7bc8bd21f01ffb8b1fb9afbe2094f6d09cd6eaa4cb1a0d59a4acec6b7c51c48f71c0d550b8d4b2aaa5d283ff204abc02e1f57f0df6cb43363dbb635d6982e137a78e7c1d8dc6cd313c6d52073da3a804c3da3d56a45d9e5fcc040631e93285aa12e01c3ef0210f8713b108d7f98348a2dd189f9a6abaa75e631e7226e7512c66c80b9b7350683e3441e2d6de9477b1eaac60cf911ce2ebf6c27b221987328553c885add19788697806ead7b38c15fa7221e8af1fd9bdca11159eb4c021113b21f092f55a8e675710ae2f70e3b32121a7e373659c01403b836a2867a23e609269117548f6a141a06e7d0fbb2ffb940a7dfd41dc4a3a6f49f2f714f4be461073095c5741e429cb25756056f99b7222ed41580cc3b5d9e491411ac374733f70a222aac98dd4215568e8ee0564e266001ca5af8db522c2312e976379ddaa32f1b95d56954c588dcbbd645f6dc10617eed2fc2120427930f3998f678b1100c9f296e2ac1ccf0042fa4ba1f03b68ef0d866e76d7e51b8968e1518a1e2f0349a3129c26c2a8df014be3f5e590099f4df27b950c165aa85c37b700768b7057ef0ec1115a3d08afabf458047d4f2b6f37338c49f4a2b24815f0a95f5b591c6462667005ca9e10f932b857c011cc95bb616ffb31181fcd8bb720b720c611b7c265f3d0c14b38f536683516155a1bb115831ef6472404bb4c6e6f4fbf880afd478658b28421a042c976b0fd65fd9036cda84dc90a003715929794f66398869bd17cdbb01c33e7cbd44ecff8d7f2fc246d14714da26cac027f97a44ca0be9848510ed5a0ab46c8303a83b7969289a916a02cc588ed0517ea3c9b375a19bdac2599508dd09868ba65092102b610a3758e16aa6d5c43c920784478b9ca85a9ef8188753a82a83e06761cf43b85ec12dc86fb5348c349c36184e9e1f11f27f13eee7b85af80f8dc70be9cc3bdbbaeff5ef7368074b463cc51410f3b5652d02713e193b63dc099a9cb957aff94cdaffd3ebc2cf8e9f1e93cf22f77306fed7e01a806c64e6b6078c641704cd6ef47bf5fbaf11f0c3f99708d415b06291ec5c5f746fdd4f0f840b0b926cf4c761f4a7c952c7358ac24a82c40c5dcf0545b6d6d27846754aba712d1b51829d1ec0059c7d8931e4e010cd309486a4bffbcb7ef857b7aefc8242d1143d7dfb066a1633c1be6cd395d6b825a00a1558149c6308ac432149ab56ce41' +let ppZkAcc5 = '0x2dc87c995b5b2c865be9732ab2f39eb6b822b9eb217479228aaf6a7286ffafa028de764f99a69bf28723754b5f1dad020db1c3d1e2ac5d695ce0747966041d6b0e1b0c6e2116ff7a9686863e7ed0d27965643b22ad72fb1efa85e5f7112e3a520085d96c67af69b8133670c5776c450febb0db6e64069dcff14a6f57e11ff370090d8a1205160bdff1b88b18863fdd61de2d6372146fdd24ab61706355c0662105b2666d81833d56ca94462dbe40d07ba619c044c9bc21e02469b97aea6c6af417f93da3d45cdd30c4b7a3bee9c7f156a228331c0341f926884d0da480d06b340987694f4cd27fdf8b21f85d9683a7ded7794b1ccae1dac17aeac124f86de80325cc461bbcad2afa4392b743f6775e50f0d3e7b1ded590870b5b8fc8e983bd9f0aa2f1ab562adb26de79a4c849eec93ce37582d725bbbd8a28ec43c5b15d31b81be3a9621c3dfca9b4dc18a3ba426a4a38433aea6212b3ea67b8715eaac7ba730c0e6b73e6e45a32fc98dbc2c281768d7c87b5f151eab07b138ad9ed4943327e2853dc101e304b7eb087197cc0967f167c75c9b999350be1193c8743ec85323422f47033beaaf40ae6bb022490b2d5c9139d4430d01eb6b326f3f671b07883bb0001c7bbcd15e7badf98d5260aa7d60b6f4fbcdd469c80fc3dcc825aec4e4c8115683e87922c1ac8e70caf46d55f1e5b7491b8dd489ef59983d195b9510865970c4a30c7ca6d62f1132a667d01f2e7a66dc9bc61f72a285c882bcaf02c8f0d7f19fd7d4d0dc25fd6db9375281868a43d43ca4640a8c491984252fc409073bb160e7abf7daf47c7ce9201c0a227d8395290d14b8dcb54cb518ef6c54a3173fa9c2e72e95c5a52080a9b8f35c70ba32edfc4a5cda3677c9b1f2d8b39b70d039c2f2f8bc85de417181b005943c63a1ff38a2b819eb98dae45cf0c2e6ebedd067104014a204685c8b3b9fba148a1500da5ef41e5b252faa7878c33536a62688a153128a679dff150c42d856d896006642e543cf05bb46d73848ec0bb75bf02d7834a0ebe74a0a34d4511eb76aee4e8011fac466d353d0b6867097bd65b64995005d92c117f0943b788324c0e5dd2cc14ac79d7068894024f6b8775f3a833273160650d4f64ead2a40c885a4d3e4c0aa8fed6248e7081968e6fcd19b0bc58a652db242704e5e56731d4e7768c7b217c7e492b2dc934c50d4c5be4deec7243e548e24d24c4652f579f57d046bbde3ac3e4ed334fd8b8ed5eff4ee72ffb851ec89bf546090e5d39def72767cad881c40ceb7f4abbdea24088601fe277851fb229e5dbe50bc2b595b6ea534805b6568ace6fde2d2a8fe03383dee5b79445317e0b8c2e3f2746625e630ebc6e71fd16712cd14792f50f1ffe4361119a487b496a338ab4bc0eb57f430438f3ec352e0902106d7d7f64f9f2ff736276a63963951913b9f99c14201ba8f8e54b6cd99c16ba122a47b4a15e56429cb88715734056fc76e8c0820557ee87a58b4343f54b1923ed78cf73f958fd69157213f23333253669c3693d2ab65caffee399e32753bb8005fc5509e2dd084d4bb9decb7a68b733c929a1c30433d4b424b4d7b8492a37e1b35697bfda34bb40b9473284d9cd8f2ec598a2312930fdfdbe955af221b775177a318d34e135f66c6356345273ee8910af72426303c0d2fae755adfc36f3cb2e261e000b94fe4b620796a6c3f021aeb1a87c32991ae98e0b5d6a7854ce513301ecd3d4336e7b6bb2a82052df92df6df408e8d39222d8e63ee7f0dda6e53ba5f181930fdebb2f5625aedab52210c0ff8c5362a444016e1de6db904c383d07575b6ad5b7d0b6c3434951a1d4d809043cb43e3b03110315b0fc2ceaaef281f9ffa86e99b6257a578aaa8ea24af6d49cb221339eca9d2d376cd25c85f5f7cc03a3a96ba42b424d85e86b06db70dabb4b3985c6a5d09a2946dca524bc47b5b61a2bc8370f861878e4f2c24bf42818b355473cda673c762893d7d34680ecc02e900285c80efdf796819011805333f1aa07853803455fb30e5bf50f7c47ff0f9faa30ca89cf5a41236ab89a5f7d3c74fd3eee1fbb5b4f5e1fac70b5b9c83d0d9829b0a79cb0335c990f65ccaa6262d518329108a0d7423c0be12640febc565e23599b7a85f8c8cbc928d4aa7460acd77c48f78762bd52e916b8209b11b1f36bc171f40fd98bc75359e920a433dfb8dfcaada3b5c4ee51ad0171c24328ee5ff7c47fc2074b7978210d9573e08462004f93793f14c991d7a4210b707621b1b036b4a842500c5a5f9f90dc53b8052b80494f011fb384bf4922068ce6670def45da5038a954bf65793caeaf397c1b88181c8fc961356b7d017c0c5f3883b0fc711c4cb3c29bced53acd0d8de6fa92793ad48fc0e6b23724537513c430bf6124b10e8ef1aef8030da89d3d65ae3e9adc9f8c6249fca5320e1c541ed7cce8afe1dfcfe24d3f1953bb4073c18b83da4ee01a45f995c93191c0f121278906cc6136b47a7e49993c7efda76b3c5957db60939b48792d601d1e194bf827a2b6f0261d031f3787b4a5033496e191383d02fcbafbcbaced8266679dc22a062f5b4ee3e2b8ebef5e26dfca692140c2ee399a94efa7d8dfd9c126c247118d1f4d185da3ce8867198cc28d2ab851a09c51608b212b81e79e9e8189c117e41d0ecac2a6177b12b343648cd8ba15ccc4ba49c141b3cdb5b8d470ecd5f90dcd3519192f763445996cf808d6ff6ec971e99e32ac067c1b2433c7994ee568efbbac11b9370f14053c34dfce8356b71de3080d9d035270011d9171339e6a8f94ceba03eb42e9c483f99a25e2a643cd81ae8e76e2608e5fb9165385e3e380f9c76493009d116f3e8ad6cd6e1c60d2a2d48f4fb8d7e2dfe780d019176fcd469f666ce31a79157da7b624c8c737dca76a2186121f015554780260d3246f2e1307f1d42e238fc117b318f822f10a8217c4ad0ea5b3b56ef15b89aa418f129b8723468987034badab516e3819b84c57295f678a1cb154e90920cfebd1a970e67714f6eb94248092ce617e38a131cc12f99e8b08b623d4d9340d27b0f2d34140ce8b00250723973c107d08c3fc8c631b143c442d6aa107e48231dc75725bafb7fcf84046c714618e3bef602ac1a5ae9b8ffa262c9639d5e5cf7a859928e407bd172b8bce5b1a053fce2265d0b661a3fc93429d7ea47d78eac6f52dea0daec07e3d3586b3df2e16f01c2086ab64e623bb49819012979c652f1043bdcf30af8f0bc60c82602b2b642f63fb4eb517cb519d1f8b1cf18b236c9ad731cc1db0e1b75fcddc448aab15db9f0c5affb9e1704d508adeb13ebeadd1832c8c16a162ef80b0f6161a71f71746677d821e4ffc25230bbdeeb2feaf5b5c57c23379c27c0a021af225c265c203614babd288f8b3a891073c0630b9b014461b07a65a4508bc48f556bd1df5f82c2424606e5e7d29b1a39d48b6f586056d41120fcc0c4330d8330fc07b4060470a755d19af2a020fefbf688038239c59573dbabc3246ddc8ece106a41bccd07e2502d37413360eb59f60481e1223c2b1f774a12e728642dc47bba05d4dccebc807e99b0ba0ceef8f35133261588cb6eb5cf6203ed45a27f28a0677f0f9d5431122ded8a6f0f2cbe05dab919e545129f7fc1e320d6fed06c22e61f9aa00ae95a50bad727a7282a6f0e16992640bf8cce79a82fff0efd142bbd4499b44ec4cb1db0b9f7250ee4258f73dc66f83099ec9c6902bc8500e32ee6e2fd75a2ca66628e62b77195fed4a2a0d63bc78d7e206ed45c2e3c3de8630d2281a401034f1fc680804f26906e218f40aa53f4895cf28d3e62179161f906cb8d17240a000e81253520256a0fa6ea9d180463a452c11e1d91249189b5a23ddf5a3b7659d5240b0d04f08abc81c711d0757c2b9341ef1b4024349d65155ecbea863f1a24af306d0450e20af6d6c2c1f5f4f1c02a897992226f489f09ee077974ef8f3eb240acf191bc3292962eb9edbdb467a343ce1634879a2060a7773727cf5eb9fe7850ca526cb9c093e52a7cd9057df05c40c5a8e114e6121d27407b5605befae1b6d133ca5d0072576d3553696e3d370227df523d88bf9800d27748e0fe439a6255fc87eceff1d04a4f4f067544e9cbc09a63750b7f7999a31df00db25a8b9711d21020f6f3a190ae063d4a6aa51963a1c737a092ada16f9303b9c1a9aeac1f2bcf269a80543c60f5bb122059b01e628d8c696f6d0d0095350107759713d74f59ba8e64f6751eb23a9de192f8787d14cdc3b2fca53444b7cf4d3c39f7583aca9f140325f69a30c0fa78fca1939a7080a6bfaab732935a3b810c45ca6e34192f5c285fdc7feb05928ed61727872415db27075e2dab5acc4a730bb09daca797beebf6d8e226dcee22e325eb8ec511002c95bc1dfea10cc063913b54a81bcece86336ecb9b389635b1597963d24293abbe835a49902288a4e023c873451753c613a25fb26e658d0470152c952a92bd717db5c093b85af98d431c0e43e37a5f67845502a9c2fcec1431aeb34256900c8fc13eac0806be4a17ab2227073fa10772802a1c56737f9e57d236c20f4f49b38a937f499a78091bb43ba6e6d7f95e31acd6e7550c270dddabc0645119d4d28cab0e80b6179d42831ffb2688653e9dca3d951626bc9cf36a3a62a1cb77ec251064ef98339690b5750111d6b0e959a774bcb23abc4477d3a56d4012e4e6c7e60e1c9af3ed0f877bdd360c9e4a3f15d750062b22c168a0699ebca24d1133ecd4a5769d581be82f802b4c0b61d367275e1a47c24880add3045fd6b298055b0786880e7cae3eadd7013d705fb3692de33c0a165bdbe4158ffbd627c08a395f41ace8c5d06a6e025657f356653f347be35295b369a138cf9c6a48e8a15ff2aa7c5b05f4ba18cfe0cd5a245baed1b66ca3721326a523d7f9f2375acdb19e99482ad2e065d5816da36a1af8849e074e269ed35a4621e34a5b99c709bf1032ee6a23496bdcc277df8b7409e45beb0eee30d40a18b82c3c0085d31f4183d2c5c8917746c097cc28553ebf10ccb654de61dcc7f177af0f43aeabb80565d6211a6ef0d3e0b2d0db2e6d2416407d1044ef3bcab6472dcdd3331afe471f3ea791b9192a582a13b33b014a3621b1c669c59bdef7283cd6f31aba46f2abedc703d0e8cf4726d87bd8540453ead5a372f174c2e02bce4dd238011f82fd3c2d4466601ce62d2bb181f17921956423a047c188ef4ab0761aae757533fc94d4c555a7224059f3afebde13a268f592d3921c09fe023eab94c822f56e694dca6b6d0e5cf1d365e0e385a8bc6fcfb780c2ab655b9568b70cad914a5247aac464700969d4c2cf3d06a62eba79ca64a64611a0982b15ff386de5793d4f96fa42652114da03d1ddfdc57fbd2840ada1d5a58d5b031364c49720b563e424eceb68203da91439b167690da0306976d290feb0dd329d8d907692617b1e98db2cbea0e6bf9a6514207936f3ff344e97eae95fcf3aa2e62dc62a4333b606d61541c123bb81b8680fc2ad66a58819ffb26cc99c7dfe9d7f266b355fc9c11a47e77095c56216baa4f592aeeb3c03d4353813584bb94b099c69d5242d1e8c45c9fcf51b19e454cce98a21219c2b9c44ddf9ea36b76294b20795d959506f9fb71d8fb7cd805cbdefbadb31e98cde2641a410e01ea78fc8b6a8c1e28510868b6ecfd0aaba481e8186b7a402737343b692e335b2f625a516f534063030158395e0ac9a51a0538945a6dce34289cc9904af58e90b5f39673b75e93dc63c8963a39ddff571178eedf03b57de2181b237eb06b575b674f63e135822da407c66607bfcb85b308f2bb10437250b21a8b1421173984fd1e216cb81b3bff170e6a58813b20333224d98b73ed785ea100679fd2047f8ecf080855d32abae097cab6419ab623f4a6abaddf0785938a0d002bdc85b99da38c6f65677c90290531534efb64cb43c0e033785b6f5ccfebd0155c8bf020931c5dfbff18c804b6e13c1348792b4aec72c5be3ad2eee6d25dc01c7c3b7edfb4d42ddfbc7df89b12dd42245a99543fe74b982aff7b23708dad301b8df63b036ad8917b2099cb13ea3d5ce0e53b80d6eafc47afdf03dd6535ad2f2f01881e6de67732ac4629706d01a11cedb592bc1a868ff51906717dc29af3393040c719ee0e3b74c00dcc0e5cfd4d6c29134723c1820cf365cd697014f1918703bfaa49ba8ae6461074932feb0169fd1d449c2ec3328f4eeab74d990c4806dc2e119159d50400124c24dcfa146f3956dc805263a198d512d40b729b969fc31f2ac1ed939fb8840dfc5553158580c910cb5c79b4adb6f9a4d44ebd5f28b3897f' + +let gpk = {x:["0x2a69fc6c9e39bce2a7bf7c37d9766494243948f0119370a06f317005d70f9304","0x16cea21f93a20dc52769e8714618f9ddccbf97343a927ba2a559b4c1fe8fc8ed"],y:["0x21975b057269f1962d7e637ab7f110b9edbe79705824c5293d714ca2c651a7ad","0x2f90279a7c5a60a9e18257f3431ac08156c09d51439c17ce5eabcca999fecffb"]} + +// pEvals for input: "zkRand-v1-2024:1" +let pEvals = [ + {indexPlus: 1, value: {"x": "0x2e71e34ef44d8fc2e84d96337ca05234085d8b200668d225f15704a2fe9cae48", "y": "0x21733d9b459e1fd3fd2ac73470e8d147874d7c347694b82316e4d723c81c3a71"}, proof: {"z": "0x2b67e64aa7e8243292ed0dfed1590b31188f805480161de96b1efbc358334e38", "c": "0x0a5dd30c30b6dd6f0110b42d40b890b3749015ddab846700e1d5717ab6690032"}}, + {indexPlus: 2, value: {"x": "0x00634933fa612e1813283b4316146c14ec4a497b95191ac9ab530a978a079ca5", "y": "0x07edff02b0b6ea9e9c4dd32cef005110ca2b3f77892ad8d95e23ebd2856c11e6"}, proof: {"z": "0x0ecc798a159496903719d68e011039ad0eec1c6ba069366dc6610dbcbf6bf094", "c": "0x014b6614e1be8489b1ce07cf4cb4c5e8846b417f99bccc852ce57140d43b2389"}}, + {indexPlus: 3, value: {"x": "0x0cd9e6985ef4c2e08875832d595737e19c57a2fe6a0b678d995f4eea9aeaca33", "y": "0x1c22ed464da535f3f6cd9af94e6e1d40adfe68905340f23f6ddd23ec3ff4c9ed"}, proof: {"z": "0x0f2d2a3214bc7cb08ae5916fdc621358b1fb584647dd7a9684540e02103ba37b", "c": "0x22ca5d1d69e77f306dbd929ec05de88642ada0a918fcb483922c9893f400f74a"}}, + {indexPlus: 4, value: {"x": "0x03a8c7bc077575bcda83aceb165dd6c84a7bf825cbba1c6e8c1c7fe8821a9a45", "y": "0x24335c3a2746992fb50efa146b11b2d8f6124f0dfde2c0b574556ea2c989dc09"}, proof: {"z": "0x11674197fa615eea4961feb5d58fb913342dfc27787f733b52de39123b0a7ebd", "c": "0x11591c9b8834e2555043e62d1ede43c08971d62297ff9785a5416e8d2cf65d6a"}}, + {indexPlus: 5, value: {"x": "0x2e5002bd840108a25b59ae32ac914e3420e40a5de687a436bdb71c90d83c3707", "y": "0x136514a72a62c60ee8476847a197803d21acc64477855aab2e184b43301199d5"}, proof: {"z": "0x2d7b09f5a4e45222971297c223a351b2896f0b727864ea4c48c3eb727b14efed", "c": "0x0fbe5c46c1d6377d53f04d55c9846cacf000ba44e6085761761b2c84b5477b12"}} +] + +let pEvalInvalid = {indexPlus: 1, value: pEvals[0].value, proof: pEvals[1].proof} + +let combinedSigma = {x: '0x20f4174fbb447a6800e1aa7d98645cc35d4f935963577c09a697a3c07c3536e3', y: '0x2d2e6293260e3092d024adce73ae7ef3f219c55950a3b174832a48c88c33d375'} +let expectedValue = '0xc2345a834612b9be480f1007e098485a91b2573b9bd6147f549047b84194ecd3' +let pseudoRandom = {proof: combinedSigma, value: expectedValue} + +const cfg = hre.network.config + +describe('ZKDVRF (with precomputation of hash) on-chain tests', async () => { + before(async () => { + Halo2Verifier = await(await ethers.getContractFactory('contracts/Halo2Verifier.sol:Halo2Verifier')).deploy() + Halo2VerifyingKey = await(await ethers.getContractFactory('contracts/Halo2VerifyingKey-3-5-18-g2.sol:Halo2VerifyingKey')).deploy() + GlobalPublicParams = await(await ethers.getContractFactory('GlobalPublicParams')).deploy() + PseudoRand = await(await ethers.getContractFactory('PseudoRand')).deploy() + Zkdvrf = await ( + await ethers.getContractFactory('zkdvrf_pre') + ).deploy(3, 5, Halo2Verifier.address, Halo2VerifyingKey.address, GlobalPublicParams.address, PseudoRand.address, minDeposit) + + account1 = (await ethers.getSigners())[0] + account2 = (await ethers.getSigners())[1] + account3 = (await ethers.getSigners())[2] + account4 = (await ethers.getSigners())[3] + account5 = (await ethers.getSigners())[4] + account1Address = await account1.getAddress() + account2Address = await account2.getAddress() + account3Address = await account3.getAddress() + account4Address = await account4.getAddress() + account5Address = await account5.getAddress() + + lotteryAdmin = (await ethers.getSigners())[5] + player1 = (await ethers.getSigners())[6] + player2 = (await ethers.getSigners())[7] + player3 = (await ethers.getSigners())[8] + player4 = (await ethers.getSigners())[9] + lotteryAdminAddress = await lotteryAdmin.getAddress() + player1Address = await player1.getAddress() + player2Address = await player2.getAddress() + player3Address = await player3.getAddress() + + Lottery = await ( + await ethers.getContractFactory('Lottery') + ).connect(lotteryAdmin).deploy(Zkdvrf.address) + + }) + + describe('Initialization', async () => { + it('should be initialized', async () => { + const memberCount = await Zkdvrf.memberCount() + const threshold = await Zkdvrf.threshold() + expect(memberCount).to.be.eq(5) + expect(threshold).to.be.eq(3) + expect(await Zkdvrf.owner()).to.be.eq(account1Address) + }) + }) + + describe('Lottery Initialization', async () => { + it('lottery should be initialized', async () => { + expect(await Lottery.zkdvrfAddr()).to.be.eq(Zkdvrf.address) + expect(await Lottery.owner()).to.be.eq(lotteryAdminAddress) + }) + + it('lottery setup', async () => { + await Lottery.connect(lotteryAdmin).setup(1, minBet) + expect(await Lottery.randRoundNum()).to.be.eq(1) + expect(await Lottery.minBet()).to.be.eq(minBet) + expect(await Lottery.contractPhase()).to.be.eq(1) + }) + + it('lottery enter', async () => { + await Lottery.connect(player1).enter({value: minBet}) + await Lottery.connect(player2).enter({value: minBet}) + await Lottery.connect(player3).enter({value: minBet}) + expect(await Lottery.players(0)).to.be.eq(player1Address) + expect(await Lottery.players(1)).to.be.eq(player2Address) + expect(await Lottery.players(2)).to.be.eq(player3Address) + }) + + it('should not be able to enter again', async () => { + await expect(Lottery.connect(player1).enter({value: minBet})).to.be.revertedWith("You have already entered the lottery"); + }) + }) + + describe('Phase 0 - Adding Nodes', async () => { + it('should be able to add nodes', async () => { + await Zkdvrf.addPermissionedNodes(account1Address); + expect((await Zkdvrf.addrToNode(account1Address)).nodeAddress).to.be.eq(account1Address) + }) + + it('should not be able to add same node again', async () => { + await expect(Zkdvrf.addPermissionedNodes(account1Address)).to.be.revertedWith('Node has already been added'); + }) + + it('non-owner should not be able to add nodes', async () => { + await expect(Zkdvrf.connect(account2).addPermissionedNodes(account2Address)).to.be.revertedWith('OwnableUnauthorizedAccount("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")'); + }) + + it('should not be able to add more nodes than predefined count', async () => { + await Zkdvrf.addPermissionedNodes(account2Address); + await Zkdvrf.addPermissionedNodes(account3Address); + await Zkdvrf.addPermissionedNodes(account4Address); + await Zkdvrf.addPermissionedNodes(account5Address); + await expect(Zkdvrf.addPermissionedNodes(await ((await ethers.getSigners())[5]).getAddress())).to.be.revertedWith('All members added'); + }) + + it('should not be able to start NIDKG', async () => { + await expect(Zkdvrf.startNidkg()).to.be.revertedWith('Not all Members are ready') + }) + }) + + describe('Phase 0 - Confirming Registration', async () => { + it('should not be able to register through a non-permissioned node', async () => { + await expect(Zkdvrf.connect((await ethers.getSigners())[5]).registerNode(pubKeyAcc1, {value: minDeposit})).to.be.revertedWith('Unauthorized call') + }) + + it('should not be able to register without deposit', async () => { + await expect(Zkdvrf.registerNode(pubKeyAcc1, {value: minDeposit.sub(1)})).to.be.revertedWith('Must provide enough node deposit') + }) + + it('should not be able to register with invalid pub key', async () => { + const invalidPubKey = {x: '13022564727105651777525980989432672761973150245441769669319632814966958345930', y: '784968079987676224643066681266495342411838584902826230352199930579969951847'} + await expect(Zkdvrf.registerNode(invalidPubKey, {value: minDeposit})).to.be.revertedWith('Invalid Public Key submitted') + }) + + it('should be able to register with deposit', async () => { + await Zkdvrf.registerNode(pubKeyAcc1, {value: minDeposit}) + expect(await Zkdvrf.nodes(0)).to.be.eq(account1Address) + expect((await Zkdvrf.addrToNode(account1Address))[2]).to.be.eq(minDeposit) + expect((await Zkdvrf.pkList(0))[0]).to.eq(pubKeyAcc1.x) + expect((await Zkdvrf.pkList(0))[1]).to.eq(pubKeyAcc1.y) + }) + + it('should not be able to register the same node again', async () => { + await expect(Zkdvrf.registerNode(pubKeyAcc1, {value: minDeposit})).to.be.revertedWith('Node Already registered') + }) + + it('should not be able to start NIDKG', async () => { + await expect(Zkdvrf.startNidkg()).to.be.revertedWith('Not all Members are ready') + }) + + it('should be able to register all nodes', async () => { + await Zkdvrf.connect(account2).registerNode(pubKeyAcc2, {value: minDeposit}) + await Zkdvrf.connect(account3).registerNode(pubKeyAcc3, {value: minDeposit}) + await Zkdvrf.connect(account4).registerNode(pubKeyAcc4, {value: minDeposit}) + await Zkdvrf.connect(account5).registerNode(pubKeyAcc5, {value: minDeposit}) + expect(await Zkdvrf.nodes(1)).to.be.eq(account2Address) + expect(await Zkdvrf.nodes(2)).to.be.eq(account3Address) + expect(await Zkdvrf.nodes(3)).to.be.eq(account4Address) + expect(await Zkdvrf.nodes(4)).to.be.eq(account5Address) + expect((await Zkdvrf.addrToNode(account2Address))[2]).to.be.eq(minDeposit) + expect((await Zkdvrf.addrToNode(account3Address))[2]).to.be.eq(minDeposit) + expect((await Zkdvrf.addrToNode(account4Address))[2]).to.be.eq(minDeposit) + expect((await Zkdvrf.addrToNode(account5Address))[2]).to.be.eq(minDeposit) + expect((await Zkdvrf.pkList(1))[0]).to.eq(pubKeyAcc2.x) + expect((await Zkdvrf.pkList(1))[1]).to.eq(pubKeyAcc2.y) + expect((await Zkdvrf.pkList(2))[0]).to.eq(pubKeyAcc3.x) + expect((await Zkdvrf.pkList(2))[1]).to.eq(pubKeyAcc3.y) + expect((await Zkdvrf.pkList(3))[0]).to.eq(pubKeyAcc4.x) + expect((await Zkdvrf.pkList(3))[1]).to.eq(pubKeyAcc4.y) + expect((await Zkdvrf.pkList(4))[0]).to.eq(pubKeyAcc5.x) + expect((await Zkdvrf.pkList(4))[1]).to.eq(pubKeyAcc5.y) + expect(await Zkdvrf.getIndexPlus(account1Address)).to.eq(1) + expect(await Zkdvrf.getIndexPlus(account2Address)).to.eq(2) + expect(await Zkdvrf.getIndexPlus(account3Address)).to.eq(3) + expect(await Zkdvrf.getIndexPlus(account4Address)).to.eq(4) + expect(await Zkdvrf.getIndexPlus(account5Address)).to.eq(5) + }) + }) + + describe('Phase 1 - Start NIDKG', async () => { + it('non-owner should not be able to start NIDKG', async () => { + await expect(Zkdvrf.connect(account2).startNidkg()).to.be.revertedWith('OwnableUnauthorizedAccount("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")') + }) + + it('should be able to start NIDKG', async () => { + await Zkdvrf.startNidkg() + expect(await Zkdvrf.contractPhase()).to.be.eq(1) + }) + + it('should not be able to start NIDKG again', async () => { + await expect(Zkdvrf.startNidkg()).to.be.revertedWith('NIDKG has already been completed') + }) + }) + + describe('Phase 1 - Submit PP', async () => { + it('should not be able to submit public params with invalid proof', async () => { + await expect(Zkdvrf.submitPublicParams(ppAcc1, ppZkAcc2)).to.be.reverted + }) + + it('non-registered nodes should not be able to submit public params', async () => { + await expect(Zkdvrf.connect((await ethers.getSigners())[5]).submitPublicParams(ppAcc2, ppZkAcc2)).to.be.revertedWith('Unauthorized call') + }) + + it('should be able to submit public params', async () => { + await Zkdvrf.submitPublicParams(ppAcc1, ppZkAcc1) + expect((await Zkdvrf.addrToNode(account1Address))[3]).to.be.eq(true) + expect((await Zkdvrf.addrToNode(account1Address))[4]).to.be.eq(0) + }) + + it('should not be able to submit public params again', async () => { + await expect(Zkdvrf.submitPublicParams(ppAcc1, ppZkAcc1)).to.be.revertedWith('Node already submitted') + }) + + it('all nodes should be able to submit public params', async () => { + await Zkdvrf.connect(account2).submitPublicParams(ppAcc2, ppZkAcc2) + expect((await Zkdvrf.addrToNode(account2Address))[4]).to.be.eq(1) + await Zkdvrf.connect(account3).submitPublicParams(ppAcc3, ppZkAcc3) + expect((await Zkdvrf.addrToNode(account3Address))[4]).to.be.eq(2) + expect(await Zkdvrf.contractPhase()).to.be.eq(1) + await Zkdvrf.connect(account4).submitPublicParams(ppAcc4, ppZkAcc4) + expect((await Zkdvrf.addrToNode(account4Address))[4]).to.be.eq(3) + await Zkdvrf.connect(account5).submitPublicParams(ppAcc5, ppZkAcc5) + expect((await Zkdvrf.addrToNode(account5Address))[4]).to.be.eq(4) + expect(await Zkdvrf.contractPhase()).to.be.eq(2) + }) + }) + + describe('Phase 1 - Compute VK', async () => { + it('should not be able to compute vk with invalid gpk', async () => { + const invalidGpk = {x: ['5723195899829869360355735301475034960183765743883956670260556700304561939780', '15030566888555072222300603577241433726177959843784551443730472582972215820810'], y: ['21136863447758371055855435029370840801392490189244715848977694295806880691066', '19918577776357251387334608329875665792259141288711508547524739451628191582134']} + await expect(Zkdvrf.computeVk(invalidGpk)).to.be.reverted + }) + + it('should be able to compute vk', async () => { + await Zkdvrf.computeVk(gpk) + expect((await Zkdvrf.vkList(0))[0]).to.be.eq('0x1ebd0ff79221958f20f0b558e9938733f89a158b68086bba25a8b30f1212a944') + expect((await Zkdvrf.vkList(0))[1]).to.be.eq('0x063ab75c90f51025caec756f3d18690c6d01dbd3d1e375ec0f4961a733cf8bff') + expect(await Zkdvrf.contractPhase()).to.be.eq(3) + }) + }) + + describe('Phase 2 - Initiate Random generation', async () => { + it('non-owner should not be able to initiate random', async () => { + await expect(Zkdvrf.connect(account2).initiateRandom()).to.be.revertedWith('OwnableUnauthorizedAccount("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")') + }) + + it('should be able to initiate random', async () => { + await Zkdvrf.initiateRandom() + expect(await Zkdvrf.roundInput(1)).to.be.equal(`zkRand-v1-2024:1`) + }) + + it('should not be able to initiate random without completing previous round', async () => { + await expect(Zkdvrf.initiateRandom()).to.be.revertedWith('Earlier round not completed') + }) + }) + + describe('Lottery Deadline', async () => { + it('should not be able to enter lottery after initiation of target round', async () => { + await expect(Lottery.connect(player4).enter({value: minBet})).to.be.revertedWith(`Too late. Random has been produced or is being produced`) + }) + }) + + describe('Phase 2 - Submit Partial Evaluation', async () => { + it('should not be able to submit partial eval with invalid proof', async () => { + await expect(Zkdvrf.submitPartialEval(pEvalInvalid)).to.be.revertedWith('Verification of partial eval failed') + }) + + it('non-registered nodes should not be able to submit partial eval', async () => { + await expect(Zkdvrf.connect((await ethers.getSigners())[5]).submitPartialEval(pEvals[0])).to.be.revertedWith('Unauthorized call') + }) + + it('should be able to submit partial eval', async () => { + await Zkdvrf.submitPartialEval(pEvals[0]) + expect(await Zkdvrf.lastSubmittedRound(account1Address)).to.be.eq(1) + expect(await Zkdvrf.roundSubmissionCount(1)).to.be.eq(1) + }) + + it('should not be able to submit partial eval again', async () => { + await expect(Zkdvrf.submitPartialEval(pEvals[0])).to.be.revertedWith('Already submitted for round') + }) + + it('should not be able to generate random below threshold', async () => { + await expect(Zkdvrf.submitRandom(pseudoRandom)).to.be.revertedWith('Partial evaluation threshold not reached') + }) + + it('all nodes should be able to submit partial eval', async () => { + await Zkdvrf.connect(account2).submitPartialEval(pEvals[1]) + await Zkdvrf.connect(account3).submitPartialEval(pEvals[2]) + await Zkdvrf.connect(account4).submitPartialEval(pEvals[3]) + await Zkdvrf.connect(account5).submitPartialEval(pEvals[4]) + expect(await Zkdvrf.roundSubmissionCount(1)).to.be.eq(5) + }) + }) + + describe('Phase 2 - Submit Random', async () => { + it('should not be able to submit random with invalid sigma', async () => { + const invalidSigma = {x: '19530533679215397651485398553050948879466713746327407509791012205473365859090', y: '222745608289019522547805804936443557730246285411524202516818394002813364755'} + const invalidPseudo = {proof: invalidSigma, value: expectedValue} + await expect(Zkdvrf.submitRandom(invalidPseudo)).to.be.reverted + }) + + it('non-owner should not be able to submit random', async () => { + await expect(Zkdvrf.connect(account2).submitRandom(pseudoRandom)).to.be.revertedWith('OwnableUnauthorizedAccount("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")') + }) + + it('should be able to submit random', async () => { + await Zkdvrf.submitRandom(pseudoRandom) + const result = await Zkdvrf.roundToRandom(1) + expect(result.value).to.be.eq(pseudoRandom.value) + }) + + it('should not be able to generate random again without initiating', async () => { + await expect(Zkdvrf.submitRandom(pseudoRandom)).to.be.revertedWith('Answer for round already exists') + }) + }) + + describe('Fetch Random Values', async () => { + it('getLatestRandom()', async () => { + const result = await Zkdvrf.getLatestRandom() + expect(result.value).to.be.eq(pseudoRandom.value) + }) + + it('getRandomAtRound()', async () => { + const result = await Zkdvrf.getRandomAtRound(1) + expect(result.value).to.be.eq(pseudoRandom.value) + }) + + it('getRandomAtRound() at invalid round', async () => { + await expect(Zkdvrf.getRandomAtRound(2)).to.be.revertedWith('Answer does not exist for the round yet') + }) + }) + + describe('Lottery Pick Winner', async () => { + it('lottery pickWinner()', async () => { + await Lottery.connect(lotteryAdmin).pickWinner() + expect(await Lottery.contractPhase()).to.be.eq(2) + expect(await Lottery.players(0)).to.be.eq(player2Address) + expect(await Lottery.players(1)).to.be.eq(player1Address) + expect(await Lottery.players(2)).to.be.eq(player3Address) + }) + }) +})