diff --git a/contracts/IPseudoRand.sol b/contracts/IPseudoRand.sol index 9e8a041..1d05779 100644 --- a/contracts/IPseudoRand.sol +++ b/contracts/IPseudoRand.sol @@ -11,7 +11,7 @@ interface IPseudoRand { } struct PartialEval { - uint32 index; + uint32 indexPlus; Pairing.G1Point value; IPseudoRand.PartialEvalProof proof; } diff --git a/contracts/zkdvrf.sol b/contracts/zkdvrf.sol index 3633cec..e243d63 100644 --- a/contracts/zkdvrf.sol +++ b/contracts/zkdvrf.sol @@ -14,12 +14,20 @@ contract zkdvrf 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 ppIndex; + uint32 pkIndex; } enum Status { @@ -41,11 +49,14 @@ contract zkdvrf is Ownable { uint256 public currentRoundNum; uint256 public minNodeDeposit; + uint32 public pkListIndex; Grumpkin.Point[] public pkList; + address[] public pkListOrder; + uint256[][] public ppList; - // vk list order is also same as the ppList - uint32 public ppListIndex; address[] public ppListOrder; + + // The order in vkList is the same as pkList Pairing.G1Point[] public vkList; Pairing.G2Point internal gpkVal; @@ -59,7 +70,7 @@ contract zkdvrf is Ownable { mapping (address => dvrfNode) public addrToNode; mapping (uint256 => string) public roundInput; mapping (address => uint256) public lastSubmittedRound; - mapping (uint256 => mapping (uint32 => IPseudoRand.PartialEval)) public roundToEval; + mapping (uint256 => IPseudoRand.PartialEval[]) public roundToEval; mapping (uint256 => uint32) public roundSubmissionCount; mapping (uint256 => IPseudoRand.PseudoRandom) public roundToRandom; @@ -73,7 +84,6 @@ contract zkdvrf is Ownable { globalPublicParams = globalPublicParamsAddress; pseudoRand = pseudoRandAddress; minNodeDeposit = minDeposit; - ppList = new uint256[][](memberCount); } @@ -90,6 +100,7 @@ contract zkdvrf is Ownable { // each node registers with deposit and confirms function registerNode(Grumpkin.Point memory pubKey) public payable { + require(registeredCount < currentIndex, "All the permitted nodes have registered so far. Please try again later"); 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"); @@ -99,12 +110,17 @@ contract zkdvrf is Ownable { nodes[registeredCount] = msg.sender; addrToNode[msg.sender].deposit = msg.value; addrToNode[msg.sender].status = true; - addrToNode[msg.sender].ppIndex = ppListIndex; + addrToNode[msg.sender].pkIndex = pkListIndex; pkList.push(pubKey); - // ppListOrder is unutilized but added for public visibility - ppListOrder.push(msg.sender); - ppListIndex++; + // 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 Start Phase 1 @@ -114,6 +130,8 @@ contract zkdvrf is Ownable { require(contractPhase == Status.Unregistered, "NIDKG has already been completed"); require(registeredCount == memberCount, "Not all Members are ready"); contractPhase = Status.Nidkg; + + emit NidkgStarted(); } // each node can submit pp_i, zk_i @@ -127,12 +145,14 @@ contract zkdvrf is Ownable { addrToNode[msg.sender].statusPP = true; - uint32 nodeIndex = addrToNode[msg.sender].ppIndex; - ppList[nodeIndex] = pp; + 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); } } @@ -145,6 +165,8 @@ contract zkdvrf is Ownable { } gpkVal = gpkRet; contractPhase = Status.Ready; + + emit GlobalPublicParamsCreated(); } // 2nd Phase @@ -158,6 +180,8 @@ contract zkdvrf is Ownable { currentRoundNum++; uint256 currentTimestamp = block.timestamp; roundInput[currentRoundNum] = currentTimestamp.toString(); + + emit RandomInitiated(currentRoundNum, roundInput[currentRoundNum]); } function submitPartialEval(IPseudoRand.PartialEval memory pEval) public { @@ -167,13 +191,17 @@ contract zkdvrf is Ownable { // 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"); bytes memory currentX = bytes(roundInput[currentRoundNum]); - uint32 ppIndex = addrToNode[msg.sender].ppIndex; - require(pEval.index == ppIndex + 1); - Pairing.G1Point memory vkStored = vkList[ppIndex]; + uint32 pkIndex = addrToNode[msg.sender].pkIndex; + require(pEval.indexPlus == pkIndex + 1); + Pairing.G1Point memory vkStored = vkList[pkIndex]; require(IPseudoRand(pseudoRand).verifyPartialEval(currentX, pEval.value, pEval.proof, vkStored), "Verification of partial eval failed"); lastSubmittedRound[msg.sender] = currentRoundNum; - roundToEval[currentRoundNum][ppIndex] = pEval; + roundToEval[currentRoundNum].push(pEval); roundSubmissionCount[currentRoundNum]++; + + if (roundSubmissionCount[currentRoundNum] == threshold) { + emit RandomThresholdReached(currentRoundNum, roundInput[currentRoundNum]); + } } // accept a set of partial evals @@ -186,6 +214,8 @@ contract zkdvrf is Ownable { 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) { @@ -230,6 +260,11 @@ contract zkdvrf is Ownable { 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; } @@ -245,4 +280,8 @@ contract zkdvrf is Ownable { function getVkList() public view returns (Pairing.G1Point[] memory) { return vkList; } + + function getEvalList(uint roundNum) public view returns (IPseudoRand.PartialEval[] memory) { + return roundToEval[roundNum]; + } } diff --git a/demo-config.json b/demo-config.json new file mode 100644 index 0000000..f51d7df --- /dev/null +++ b/demo-config.json @@ -0,0 +1,17 @@ +{ + "zkdvrfAddress": "0x1C2FfFeEee7d396597D60adAdAC63c2c48B829AF", + "memberAddresses": [ + "0x85A6bCc74CB8570BEfF8526aec7d0Dfb6F128A60", + "0x7D9e5627B650650Fa3E848364Cb5B0b696B885f2", + "0xec21Cc95d30cc414922292790Bf2E9063bCEe94b", + "0xE7a9aB7B20EFE5B450d7AcB5ABe169166a26DfAC", + "0x5dbfFf2666C351b226cdCe7c1F82B9b0CEbb0bb9" + ], + "memberKeys": [ + "0x2e978a5709ac0dc3cbc2260519f8fcf5542af54dfaff489bcb4a69182be49429", + "0xa0a98d2341d78ed222b7a6314c026d8e85185203fd295f67b7146be956a05c58", + "0x742e271d909d026b2b4dcc0384ec0b8df8f674f0773c354b57c24858419e89d3", + "0xeeb82181766c7d0fe45d2cb5b3399b1da17a1a432938ec8c4d73daca85eedaea", + "0x88b68d7e8d96d8465cfe3dc4abf707e21aa49139189a784e2d50cc9ada9076c3" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index bb512cf..7918d94 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,11 @@ }, "scripts": { "test": "npx hardhat test", - "deploy": "npx hardhat run scripts/deploy.ts" + "deploy": "npx hardhat run scripts/deploy.ts", + "admin": "npx hardhat run scripts/admin.ts", + "register": "npx hardhat run scripts/register.ts", + "nidkg": "npx hardhat run scripts/nidkg.ts", + "random": "npx hardhat run scripts/random.ts" }, "repository": { "type": "git", diff --git a/scripts/admin.ts b/scripts/admin.ts new file mode 100644 index 0000000..f7c0441 --- /dev/null +++ b/scripts/admin.ts @@ -0,0 +1,209 @@ +import hre, {artifacts, ethers} from "hardhat"; +import {Contract, ContractFactory, providers, utils, Wallet} from "ethers"; +import { + dkgDir, + execPromise, + instancesPath, + memberDir, randDir, + readJsonFromFile, + sleep, + waitForWriteJsonToFile, + writeJsonToFile +} from "./utils"; + +const config = readJsonFromFile("demo-config.json") +const zkdvrfAddress = config.zkdvrfAddress +const memberAdresses = config.memberAddresses + +async function main() { + const netprovider = new providers.JsonRpcProvider(process.env.RPC_URL) + const accPrivateKey = process.env.PRIVATE_KEY ?? '' + const adminWallet = new Wallet(accPrivateKey, netprovider) + + const Zkdvrf = await ethers.getContractFactory('zkdvrf') + const contractABI = Zkdvrf.interface.format(); + const contract = new ethers.Contract(zkdvrfAddress, contractABI, netprovider).connect(adminWallet) + + for (let i = 0; i < memberAdresses.length; i++) { + const res = await contract.addPermissionedNodes(memberAdresses[i]) + // console.log(res) + console.log("added member", memberAdresses[i]) + } + + async function listenRegister() { + // This will run when the event is emitted + const eventReg = `RegistrationCompleted` + contract.on(eventReg, async (count, event) => { + console.log("\nevent", eventReg, count); + // Proceed to the next step here + const res = await contract.startNidkg() + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log("Transaction startNidkg() successful!"); + } else { + console.log("Transaction startNidkg() failed!"); + } + console.log("NIDKG begins...") + }); + } + + listenRegister() + + async function listenNidkg() { + // This will run when the event is emitted + const eventDkg = `NidkgCompleted` + contract.on(eventDkg, async (count, event) => { + console.log("\nevent", eventDkg, count); + // read all instances from contract + const ppList = await contract.getPpList() + // console.log("\nppList = ", ppList) + // save ppList for rust backend + const ppListHex = ppList.map(subList => + subList.map(num => num.toHexString()) + ); + const obj = JSON.stringify(ppListHex) + writeJsonToFile(obj, instancesPath) + //await waitForWriteJsonToFile(obj, instancesPath) + //console.log("retrieved all instances from contract") + + + console.log("begin sleep") + await sleep(2000) + console.log("end sleep") + + + // derive global public parameters + const cmd = `RUST_LOG=info ./target/release/client dkg derive` + let result = await execPromise(cmd) + console.log(result[`stderr`]) + + const filePath = dkgDir + "gpk.json" + const gpk = readJsonFromFile(filePath); + + const res = await contract.computeVk(gpk) + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log("Transaction computeVk(..) successful!"); + } else { + console.log("Transaction computeVk(..) failed!"); + } + + // read global public parameters from the contract and compare them with the local version + const gpkVal = await contract.getGpk() + if (BigInt(gpk.x[0]) != gpkVal.x[0] || BigInt(gpk.x[1]) != gpkVal.x[1] + || BigInt(gpk.y[0]) != gpkVal.y[0] || BigInt(gpk.y[1]) != gpkVal.y[1]) { + console.error("gpk doesn't match") + } + + const vkList = await contract.getVkList() + const vks = readJsonFromFile(dkgDir + "vks.json") + + if (vkList.length != vks.length) { + console.error("vk list length does not match") + } + + // Check if each element at corresponding indices is equal + for (let i = 0; i < vks.length; i++) { + if (BigInt(vks[i].x) != vkList[i].x || BigInt(vks[i].y) != vkList[i].y) { + console.error(`vk list does not match on ${i}-th vk`) + } + } + }); + } + + listenNidkg() + + async function listenGpp() { + // This will run when the event is emitted + const eventGpp = `GlobalPublicParamsCreated` + contract.on(eventGpp, async (event) => { + console.log("\nevent", eventGpp) + // Proceed to the next step here + const res = await contract.initiateRandom() + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log("Transaction initiateRandom() successful!"); + } else { + console.log("Transaction initiateRandom() failed!"); + } + + }); + } + + listenGpp() + + async function listenRandThreshold() { + const eventRandThreshold = `RandomThresholdReached` + contract.on(eventRandThreshold, async (roundNum, input, event) => { + console.log("\nevent", eventRandThreshold, `round ${roundNum} input ${input}`) + + console.log("begin sleep...") + await sleep(2000) + console.log("end sleep") + + const evals = await contract.getEvalList(roundNum) + + const pEvals = [] + for (let i = 0; i < evals.length; i++) { + const index = evals[i][0] + const value = {x: evals[i][1][0].toHexString(), y: evals[i][1][1].toHexString()} + const proof = {z: evals[i][2][0].toHexString(), c: evals[i][2][1].toHexString()} + + const sigma = { + index: index, + value: value, + proof: proof, + } + + pEvals.push(sigma) + } + + const obj = JSON.stringify(pEvals) + const evalsPath = randDir + `evals.json` + writeJsonToFile(obj, evalsPath) + + console.log("begin sleep...") + await sleep(2000) + console.log("end sleep") + + const cmdCombine = `RUST_LOG=info ./target/release/client rand combine ${input}` + console.log("running command <", cmdCombine, ">...") + let result = await execPromise(cmdCombine) + console.log(result[`stderr`]) + + const cmdVerify = `RUST_LOG=info ./target/release/client rand verify-final ${input}` + console.log("running command <", cmdVerify, ">...") + result = await execPromise(cmdVerify) + console.log(result[`stderr`]) + + const pseudoPath = randDir + `pseudo.json` + const pseudo = readJsonFromFile(pseudoPath) + console.log("pseudorandom computed", '0x' + Buffer.from(pseudo[`value`]).toString('hex')) + + const res = await contract.submitRandom(pseudo) + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log("Transaction submitRandom(..) successful!"); + } else { + console.log("Transaction submitRandom(..) failed!"); + } + + const rand = await contract.getLatestRandom() + console.log("pseudorandom from contract", rand.value) + + + }); + } + + listenRandThreshold() +} + + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/nidkg.ts b/scripts/nidkg.ts new file mode 100644 index 0000000..4599b9b --- /dev/null +++ b/scripts/nidkg.ts @@ -0,0 +1,112 @@ +import hre, {artifacts, ethers} from "hardhat"; +import {Contract, ContractFactory, providers, utils, Wallet} from "ethers"; +import { + readJsonFromFile, + writeJsonToFile, + memberDir, + mpksPath, + dkgDir, + readBytesFromFile, + execPromise, + instancesPath +} from "./utils"; + + +const config = readJsonFromFile("demo-config.json") +const zkdvrfAddress = config.zkdvrfAddress +const memberKeys = config.memberKeys +async function main() { + const netprovider = new providers.JsonRpcProvider(process.env.RPC_URL) + + const Zkdvrf = await ethers.getContractFactory('zkdvrf') + const contractABI = Zkdvrf.interface.format(); + const contract = new ethers.Contract(zkdvrfAddress, contractABI, netprovider); + + // get all members' indices from contract + const indices = []; + for (let i = 0; i < memberKeys.length; i++) { + const memberWallet = new Wallet(memberKeys[i], netprovider) + const memberAddress = memberWallet.address + + const index = await contract.getIndexPlus(memberAddress) + indices.push(index) + } + + // This will run when the event is emitted + async function listenNidkg() { + const eventName = `NidkgCompleted` + contract.on(eventName, async (count, event) => { + console.log("\nevent", eventName, count) + + // read all instances from contract + const ppList = await contract.getPpList() + // save ppList for rust backend + const ppListHex = ppList.map(subList => + subList.map(num => num.toHexString()) + ); + const obj = JSON.stringify(ppListHex) + writeJsonToFile(obj, instancesPath) + + // each member derives its own secret share and global public parameters + const cmd = `RUST_LOG=info ./target/release/client dkg derive` + for (let i = 0; i < memberKeys.length; i++) { + const index = indices[i] + const cmdMember = cmd + ` ${index} -f member_${i+1}` + console.log("running command <", cmdMember, ">...") + const res = await execPromise(cmdMember) + console.log(res[`stderr`]) + } + + // todo: compare the local global public parameters with the one from the contract + }); + } + + listenNidkg(); + + const total = memberKeys.length; +// const total = 1; + for (let i = 0; i < total; i++) { + const memberWallet = new Wallet(memberKeys[i], netprovider) + const memberAddress = memberWallet.address + const memberContract = contract.connect(memberWallet) + + const cmdProve = `RUST_LOG=info ./target/release/client dkg prove ${indices[i]}` + const cmdVerify = `RUST_LOG=info ./target/release/client dkg verify ${indices[i]}` + + // generate snark proof and instance + console.log("running command <", cmdProve, ">...") + let result = await execPromise(cmdProve) + console.log(result[`stderr`]) + + // verify snark proof and instance + console.log("running command <", cmdVerify, ">...") + result = await execPromise(cmdVerify) + console.log(result[`stdout`]) + + // read snark proof and instance + const proofPath = dkgDir + `proofs/proof_${indices[i]}.dat` + const instancePath = dkgDir + `proofs/instance_${indices[i]}.json` + + const proof = readBytesFromFile(proofPath) + const instance = readJsonFromFile(instancePath) + + // submit proof and instance to contract + const res = await memberContract.submitPublicParams(instance, proof) + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log(`Transaction submitPublicParams(..) from ${memberAddress} successful!`); + } else { + console.log(`Transaction submitPublicParams(..) from ${memberAddress} failed!`); + } + + } + +} + + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); + diff --git a/scripts/random.ts b/scripts/random.ts new file mode 100644 index 0000000..9b5462a --- /dev/null +++ b/scripts/random.ts @@ -0,0 +1,64 @@ +import hre, {artifacts, ethers} from "hardhat"; +import {Contract, ContractFactory, providers, utils, Wallet} from "ethers"; +import { promisify } from 'util'; +import { exec } from "child_process"; +import {readJsonFromFile, writeJsonToFile, memberDir, mpksPath, execPromise, randDir} from "./utils"; + +const config = readJsonFromFile("demo-config.json") +const zkdvrfAddress = config.zkdvrfAddress +const memberKeys = config.memberKeys + +async function main() { + const netprovider = new providers.JsonRpcProvider(process.env.RPC_URL) + + const Zkdvrf = await ethers.getContractFactory('zkdvrf') + const contractABI = Zkdvrf.interface.format(); + const contract = new ethers.Contract(zkdvrfAddress, contractABI, netprovider); + + const currentRound = await contract.currentRoundNum() + console.log("current round number = ", currentRound) + const input = await contract.roundInput(currentRound) + console.log("current input = ", input) + + //Members creates partial evaluations + for (let i = 0; i < memberKeys.length; i++) { + const memberWallet = new Wallet(memberKeys[i], netprovider) + const memberAddress = memberWallet.address + const memberContract = contract.connect(memberWallet) + + const index = await memberContract.getIndexPlus(memberAddress) + const cmdEval = `RUST_LOG=info ./target/release/client rand eval ${index} ${input}` + const cmdVerify = `RUST_LOG=info ./target/release/client rand verify ${index} ${input}` + + console.log("running command <", cmdEval, ">...") + let result = await execPromise(cmdEval) + console.log(result[`stderr`]) + + console.log("running command <", cmdVerify, ">...") + result = await execPromise(cmdVerify) + console.log(result[`stderr`]) + + const evalPath = randDir + `eval_${index}.json` + const evalJson = readJsonFromFile(evalPath) + const pEval = { + indexPlus: evalJson[`index`], + value: evalJson[`value`], + proof: evalJson[`proof`] + } + + const res = await memberContract.submitPartialEval(pEval) + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log(`Transaction submitPartialEval(..) from member ${memberAddress} successful!`); + } else { + console.log(`Transaction submitPartialEval(..) from member ${memberAddress} failed!`); + } + } + +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); \ No newline at end of file diff --git a/scripts/register.ts b/scripts/register.ts new file mode 100644 index 0000000..b72cc75 --- /dev/null +++ b/scripts/register.ts @@ -0,0 +1,65 @@ +import hre, {artifacts, ethers} from "hardhat"; +import {Contract, ContractFactory, providers, utils, Wallet} from "ethers"; +import { promisify } from 'util'; +import { exec } from "child_process"; +import {readJsonFromFile, writeJsonToFile, memberDir, mpksPath, execPromise} from "./utils"; + +const config = readJsonFromFile("demo-config.json") +const zkdvrfAddress = config.zkdvrfAddress +const memberKeys = config.memberKeys + +async function main() { + const netprovider = new providers.JsonRpcProvider(process.env.RPC_URL) + + const Zkdvrf = await ethers.getContractFactory('zkdvrf') + const contractABI = Zkdvrf.interface.format(); + const contract = new ethers.Contract(zkdvrfAddress, contractABI, netprovider); + + // This will run when the event is emitted + const eventName = `RegistrationCompleted` + contract.on(eventName, async (count, event) => { + console.log("event", eventName, count); + // Proceed to the next step here + const res = await contract.getPkList() + console.log("downloaded all member public keys from contract") + + const pks = res.map(pk => ({x: pk[0].toHexString(), y: pk[1].toHexString()})) + const obj = JSON.stringify(pks); + writeJsonToFile(obj, mpksPath) + }); + + + for (let i = 0; i < memberKeys.length; i++) { + const memberWallet = new Wallet(memberKeys[i], netprovider) + const memberAddress = memberWallet.address + const memberContract = contract.connect(memberWallet) + + // generate member secret key and member public key on grumpkin curve + const index = i+1 + const file = `member_${index}` + const command = `RUST_LOG=info ./target/release/client keygen -f ${file}` + + const result = await execPromise(command); + console.log(result[`stderr`]); + + const filePath = memberDir + file + ".json" + const data = readJsonFromFile(filePath); + const mpk = data[`pk`] + + const res = await memberContract.registerNode(mpk) + const receipt = await netprovider.getTransactionReceipt(res.hash); + // Check if the transaction was successful + if (receipt.status === 1) { + console.log(`Transaction registerNode(..) from ${memberAddress} successful!`); + } else { + console.log(`Transaction registerNode(..) from ${memberAddress} failed!`); + } + console.log("member ", index, "registered\n") + } + +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/utils.ts b/scripts/utils.ts new file mode 100644 index 0000000..9ca950c --- /dev/null +++ b/scripts/utils.ts @@ -0,0 +1,66 @@ +import fs from "fs"; +import {promisify} from "util"; +import {exec} from "child_process"; +import {ethers} from "hardhat"; + +export const memberDir = `./data/members/` +export const mpksPath = `./data/mpks.json` +export const dkgDir = `./data/dkg/` +export const instancesPath = `./data/dkg/all_instances.json` +export const randDir = `./data/random/` + +export const execPromise = promisify(exec); +export function readJsonFromFile(filePath: string): any { + try { + // Read file content + let rawdata = fs.readFileSync(filePath, 'utf-8'); + + // Parse JSON + let jsonData = JSON.parse(rawdata); + + return jsonData; + } catch (error) { + console.error(error); + return null; + } +} + +export function writeJsonToFile(obj: string, filePath: string): void { + // Write the JSON string to a file + fs.writeFile(filePath, obj, 'utf8', function(err) { + if (err) { + console.log("An error occurred while writing JSON Object to File."); + return console.log(err); + } + + console.log(`JSON file has been saved at ${filePath}`); + }); +} + +export async function waitForWriteJsonToFile(obj: string, filePath: string) { + console.log('Before writeJsonToFile'); + return new Promise((resolve, reject) => { + writeJsonToFile(obj, filePath); // Call the function without a callback + console.log('After writeJsonToFile'); + resolve(); + }); +} + +export function readBytesFromFile(filePath: string): Uint8Array | null { + try { + // Read the file synchronously + const fileData: Buffer = fs.readFileSync(filePath); + + // Access the bytes of the file + const bytes: Uint8Array = new Uint8Array(fileData); + + return bytes; + } catch (err) { + console.error('Error reading file:', err); + return null; + } +} + +export function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/src/lib.rs b/src/lib.rs index 5d84023..7d01d7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,8 +107,8 @@ impl MemberKey { let k = index - 1; let mut sk = BnScalar::zero(); - for i in 0..dkg_config.number_of_members() { - let s = self.decrypt_share(&pps[i].gr, &pps[i].ciphers[k]); + for &pp in pps.iter() { + let s = self.decrypt_share(&pp.gr, &pp.ciphers[k]); sk += s; } diff --git a/test/zkdvrf.spec.ts b/test/zkdvrf.spec.ts index 33380a5..cc0d4a7 100644 --- a/test/zkdvrf.spec.ts +++ b/test/zkdvrf.spec.ts @@ -50,25 +50,25 @@ let gpk = {x: ['0x124c4b7c4838cb025a33347ed0311ebc59cae6809670a1868f377980ee38ff // pEvals for input: 2655988642 let valueAcc1 = {x: '0x0aa06e25ae7be82895afb10f9eb859671b929cb18f590b1f7eca7e5ed2f6edb8', y: '0x25d82353f39628544aec24a491447c44dea9da639cae8a1c70bfa1415a121e71'} let proofAcc1 = {z: '0x01d11529b4d25e5b97860184f5c5c4a20466bc254666b07525f5fd1c413cf0d1', c: '0x0af43b75018b04c5af8546f734d090e0320bcdf0f095b601af4d91c0a9e20633'} -let pEvalAcc1 = {index: 1, value: valueAcc1, proof: proofAcc1} +let pEvalAcc1 = {indexPlus: 1, value: valueAcc1, proof: proofAcc1} let valueAcc2 = {x: '0x24cc41179416462fa60951d33d37d92cbf537bdaed45f710c3cc920a9cc29a91', y: '0x1b2ee70238dd732c9203f6050a92dff7de975269396738be03e585eb923c5160'} let proofAcc2 = {z: '0x1134eb0fde7885ecc3aeae80fc083d70280e9627ca1970cadf392cd389f49b7f', c: '0x155afb118357ed7ea6a6504529a0149f507ffc9e0b66d44c9fe90cce7fe28669'} -let pEvalAcc2 = {index: 2, value: valueAcc2, proof: proofAcc2} +let pEvalAcc2 = {indexPlus: 2, value: valueAcc2, proof: proofAcc2} let valueAcc3 = {x: '0x23381a99bb649a8b4912c27c11362947ed457661ae4ce19c0e539c2244924b07', y: '0x027f8e63851920a00f55ad60a3bdc5a9f8db4521aa44cd03533fbbee94dc58a9'} let proofAcc3 = {z: '0x213b1aad2d26a8ec0a2299bd0dfc91390c5649444174f0142dbfe9355c135c6b', c: '0x1308b9206b6531f8b3486b64903fbbb3e844d1774e9fe977de7f166c1bb97205'} -let pEvalAcc3 = {index: 3, value: valueAcc3, proof: proofAcc3} +let pEvalAcc3 = {indexPlus: 3, value: valueAcc3, proof: proofAcc3} let valueAcc4 = {x: '0x0204b5d48536ba70995480323ea102bb085c321ace441c46d7941a83575edb64', y: '0x027311080fe325c61bf97d1e1413515bc92967efef10e180ddea4737e0012483'} let proofAcc4 = {z: '0x05b51059891996db457c1cd49a8c6c4ba7a03146fe56c17cfa50f1187e0a28a8', c: '0x20ab8d582e8ff821cabf9db6573c5ec1498fac4151814ed718379f3a60527f16'} -let pEvalAcc4 = {index: 4, value: valueAcc4, proof: proofAcc4} +let pEvalAcc4 = {indexPlus: 4, value: valueAcc4, proof: proofAcc4} let valueAcc5 = {x: '0x0764e44dc3fd4f311c677b8482639a409ea71be1f13bc7171e1312affcc47543', y: '0x22e8c46aebc87318220ecf5184f5f539e57f8ee65098f7f1b3b4fbef694e70ec'} let proofAcc5 = {z: '0x1b27e3dc0b5202fa969a8a02a1a63617eb10d8f7d1357123e6212f08279ddfb4', c: '0x1fcf444fd698d2d7aebb139b70143151c697704a63d29020fa1f342fe21fc579'} -let pEvalAcc5 = {index: 5, value: valueAcc5, proof: proofAcc5} +let pEvalAcc5 = {indexPlus: 5, value: valueAcc5, proof: proofAcc5} -let pEvalInvalid = {index: 1, value: valueAcc1, proof: proofAcc2} +let pEvalInvalid = {indexPlus: 1, value: valueAcc1, proof: proofAcc2} let combinedSigma = {x: '0x2acbaf7515bc51af1b7237fbe9678c0507eb51894b3aefc37bab2bdb30e2bed9', y: '0x04dfaa96489890f327bdd8d6900631e82fe4c7f83f4360c678838aaee3bb006f'} let expectedValue = '0xc6e38ed65c903e42501071b33f39be8e70128cc4cb7d393f219211a135d4a1e4' @@ -191,6 +191,11 @@ describe('ZKDVRF on-chain tests', async () => { 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) }) })