Nodejsとsanrkjsを使用してゼロ知識証明を生成し、検証します.


Merkle Treeパス証明を例に、nodejsとsanrkjsを使用してゼロ知識証明を生成し、ローカル検証を行います.
バージョン#バージョン#
"circom": "0.0.35",
"circomlib": "0.0.20",
"snarkjs": "0.1.20"

ソースコード
genKeypair.js setup、pkとvkを生成
const { compiler } = require("circom");
const { Circuit, groth ,stringifyBigInts } = require("snarkjs");
const path = require("path");
const fs = require("fs");

const genKeypair = async () => {
  console.log("start....");
  
  console.log("Get circtui definition....");
  const circuitDef = await compiler(  
	path.join(__dirname, "./withdraw.circom"),{}
  );
  const circuit = new Circuit(circuitDef);
  
  console.log("Setup....");
  const {vk_proof,vk_verifier} = groth.setup(circuit);
  //save pk,vk file
  fs.writeFileSync("proving_key.json", JSON.stringify(stringifyBigInts(vk_proof)), 'utf8');
  fs.writeFileSync("verification_key.json", JSON.stringify(stringifyBigInts(vk_verifier)), 'utf8');

  console.log("finish....");
  process.exit(0);
};

genKeypair();

index.jsはproofを生成し、ローカル検証を行う
const { binarifyWitness } = require("./utils/binarify");
const bigInt = require("big-integer");
const { compiler } = require("circom");
const { Circuit, groth ,stringifyBigInts ,unstringifyBigInts} = require("snarkjs");
const path = require("path");
const fs = require("fs");

const SNARK_FIELD_SIZE = bigInt(
  "21888242871839275222246405745257275088548364400416034343698204186575808495617"
);

//read input
const fileData = fs.readFileSync('input.json');
const inputObj = JSON.parse(fileData);
const root = bigInt(inputObj.root);
const nullifierHash = bigInt(inputObj.nullifierHash);
const secret =  bigInt(inputObj.secret);
const paths2_root = [
	bigInt(inputObj.paths2_root[0]),
	bigInt(inputObj.paths2_root[1]),
	bigInt(inputObj.paths2_root[2])
];
const paths2_root_pos = bigInt(inputObj.paths2_root_pos);

const provingKey = require("./proving_key.json");
const verifyingKey = require("./verification_key.json");

//gen wintness proof
const generateProofAndVerify = async circuitInputs => {
  console.log("Get circtui definition....");
  const circuitDef = await compiler(  
	path.join(__dirname, "./withdraw.circom"),{}
  );
  const circuit = new Circuit(circuitDef);
  
  console.log("loading pk,vk....");
  const vk_proof = unstringifyBigInts(provingKey);
  const vk_verifier = unstringifyBigInts(verifyingKey);

  console.log("Generating witness....");
  const witness = circuit.calculateWitness(stringifyBigInts(circuitInputs));  
  //save witness file
  const witnessString = witness.map(x => x.toString());
  fs.writeFileSync("witness.json",JSON.stringify(witnessString));   

  console.log("Generating proof and publicSignals....");
  const {proof,publicSignals} = await groth.genProof(vk_proof,witness); 
  //save proof file
  const proofJson = JSON.stringify({
	    pi_a: stringifyBigInts(proof.pi_a).slice(0, 3),
		pi_b: stringifyBigInts(proof.pi_b).slice(0, 3), 
		pi_c: stringifyBigInts(proof.pi_c).slice(0, 3),
		protocol: "groth"
	});
  fs.writeFileSync("proof.json", proofJson);
   //save public file
  console.log("Generating public.json....");
  const  pS = publicSignals.map(x => bigInt(x));
  const publicInputs = pS.map(x => x.mod(SNARK_FIELD_SIZE).toString());
  console.log("Generating public.json....");
  const publicJson = [
     publicInputs[0],publicInputs[1]
  ];
  fs.writeFileSync("public.json", JSON.stringify(publicJson)); 
 
  
  console.log("Check isValid....");
  const isValid = groth.isValid(vk_verifier,proof,publicSignals);
  console.log(`Passed local zk-snark verification: ${isValid}`);
   
   
  console.log("Generating solidityProof....");
  const solidityProof = {
    a: stringifyBigInts(proof.pi_a).slice(0, 2),
    b: stringifyBigInts(proof.pi_b).slice(0, 2),
    c: stringifyBigInts(proof.pi_c).slice(0, 2),
    inputs: publicInputs
  };
  //save solidityProof file
  fs.writeFileSync("solidityProof.json", JSON.stringify(solidityProof)); 

  //// Submit to smart contract
  //const solidityIsValid = await zkIdentityContract.isInGroup(
  //  solidityProof.a,
  //  solidityProof.b,
  //  solidityProof.c,
  //  solidityProof.inputs
  //);
  //console.log(`Verified user is in group (via solidity): ${solidityIsValid}`);
  
};

const main = async () => {
  console.log("zkp start....");

  await generateProofAndVerify({
    root: root,
    nullifierHash: nullifierHash,
	secret: secret,
	paths2_root: paths2_root,
	paths2_root_pos: paths2_root_pos
  });

  console.log("zkp finish....");
  process.exit(0);
};

main();

verify.solとインタラクティブにチェーン上で検証する部分も追加できます.
完全なコードは私のgithubの上に置いてあります.
あった問題
  • Cannot read property’prime’of undefined解決:compile()の2番目のパラメータは{}
  • と書く
  • Circuit is not a constructor解決:古いバージョンのcircomとsnarkjsを変更し、circomのindex.jsをmodule.exports.compiler=require(./src/compiler.js)に変更します.
  • WebAssembly.Memory():could not allocate memory解決:コンピュータのメモリが足りないはずです.サーバーに変えてok
  • を実行します.
  • index.js実行中にverifyが通過し、コマンドラインverifyを使用して失敗しました.proof.json出力フォーマットが
  • と書き間違えました.
    リファレンス
    https://www.jianshu.com/p/45ef81cc77b9 https://github.com/kendricktan/hello-world-zk-dapp