add Register.sol and tests

This commit is contained in:
turnoffthiscomputer
2024-05-09 12:13:54 +02:00
parent bb6c4b4e61
commit 631b3bc9f7
18 changed files with 936 additions and 135 deletions

View File

@@ -0,0 +1,43 @@
pragma circom 2.1.5;
include "circomlib/circuits/poseidon.circom";
include "circomlib/circuits/mux1.circom";
include "circomlib/circuits/comparators.circom";
// This circuit is designed to calculate the root of a binary Merkle
// tree given a leaf, its depth, and the necessary sibling
// information (aka proof of membership).
// A circuit is designed without the capability to iterate through
// a dynamic array. To address this, a parameter with the static maximum
// tree depth is defined (i.e. 'MAX_DEPTH'). And additionally, the circuit
// receives a dynamic depth as an input, which is utilized in calculating the
// true root of the Merkle tree. The actual depth of the Merkle tree
// may be equal to or less than the static maximum depth.
template BinaryMerkleRoot(MAX_DEPTH) {
signal input leaf, depth, indices[MAX_DEPTH], siblings[MAX_DEPTH];
signal output out;
signal nodes[MAX_DEPTH + 1];
nodes[0] <== leaf;
signal roots[MAX_DEPTH];
var root = 0;
for (var i = 0; i < MAX_DEPTH; i++) {
var isDepth = IsEqual()([depth, i]);
roots[i] <== isDepth * nodes[i];
root += roots[i];
var c[2][2] = [ [nodes[i], siblings[i]], [siblings[i], nodes[i]] ];
var childNodes[2] = MultiMux1(2)(c, indices[i]);
nodes[i + 1] <== Poseidon(2)(childNodes);
}
var isDepth = IsEqual()([depth, MAX_DEPTH]);
out <== root + isDepth * nodes[MAX_DEPTH];
}

View File

@@ -5,6 +5,7 @@ include "@zk-email/circuits/helpers/extract.circom";
include "./merkle_tree/tree.circom";
include "./isOlderThan.circom";
include "./isValid.circom";
include "binary-merkle-root.circom";
template Disclose(nLevels) {
@@ -12,6 +13,7 @@ template Disclose(nLevels) {
signal input secret;
signal input mrz[93];
signal input merkle_root;
signal input merkletree_size;
signal input path[nLevels];
signal input siblings[nLevels];
signal input bitmap[90];
@@ -33,7 +35,9 @@ template Disclose(nLevels) {
commitment === poseidon_hasheur.out;
// Verify the proof inclusion of the commitment
signal computedRoot <== MerkleTreeInclusionProof(nLevels)(commitment, path, siblings);
// signal computedRoot <== MerkleTreeInclusionProof(nLevels)(commitment, path, siblings);
// merkle_root === computedRoot;
signal computedRoot <== BinaryMerkleRoot(nLevels)(commitment, merkletree_size, path, siblings);
merkle_root === computedRoot;
// Disclose optional data

View File

@@ -63,4 +63,5 @@ template Register(n, k, max_datahashes_bytes, nLevels, pubkeySize) {
signal output nullifier <== poseidon_nullifier.out;
}
component main { public [ merkle_root ] } = Register(64, 32, 320, 16, 32);
component main { public [ merkle_root, signature_algorithm ] } = Register(64, 32, 320, 16, 32);

View File

@@ -10,9 +10,10 @@
"@types/chai-as-promised": "^7.1.6",
"@types/node": "^20.11.19",
"@types/node-forge": "^1.3.5",
"@zk-email/zk-regex-circom": "^1.2.1",
"@zk-email/circuits": "^3.2.2",
"@zk-email/helpers": "^3.1.3",
"@zk-email/zk-regex-circom": "^1.2.1",
"@zk-kit/circuits": "^1.0.0-beta",
"@zk-kit/imt": "https://gitpkg.now.sh/0xturboblitz/zk-kit/packages/imt?6d417675",
"chai-as-promised": "^7.1.1",
"circom_tester": "github:Atomic-Buy/circom_tester#main",

View File

@@ -0,0 +1,70 @@
#!/bin/bash
# Record the start time
START_TIME=$(date +%s)
# Check if the first argument is "app-only"
if [ "$1" == "app-only" ]; then
echo "Building only for the app"
APP_ONLY=1
else
APP_ONLY=0
fi
mkdir -p build
cd build
if [ ! -f powersOfTau28_hez_final_20.ptau ]; then
echo "Download power of tau...."
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_20.ptau
echo "Finished download!"
else
echo "Powers of tau file already downloaded... Skip download action!"
fi
cd ..
echo "compiling circuit"
circom circuits/disclose.circom -l node_modules --r1cs --O1 --wasm -c --output build
echo "building zkey"
yarn snarkjs groth16 setup build/disclose.r1cs build/powersOfTau28_hez_final_20.ptau build/disclose.zkey
if command -v openssl &> /dev/null
then
RAND_STR=$(openssl rand -hex 64)
else
RAND_STR="random text"
fi
echo "building vkey"
echo $RAND_STR | yarn snarkjs zkey contribute build/disclose.zkey build/disclose_final.zkey
yarn snarkjs zkey export verificationkey build/disclose_final.zkey build/disclose_vkey.json
yarn snarkjs zkey export solidityVerifier_disclose build/disclose_final.zkey build/Verifier_disclose.sol
cp build/Verifier_disclose.sol ../contracts/contracts/Verifier_disclose.sol
echo "copied Verifier_disclose.sol to contracts"
# Install arkzkey-util binary in ark-zkey
cd ../app/ark-zkey
echo "[ark-zkey] Installing arkzkey-util..."
if ! command -v arkzkey-util &> /dev/null
then
cargo install --bin arkzkey-util --path .
else
echo "arkzkey-util already installed, skipping."
fi
cd ../../circuits/build
arkzkey-util disclose_final.zkey
echo "Arkzkey generated"
cd ..
# Calculate and print the time taken by the whole script
END_TIME=$(date +%s)
ELAPSED_TIME=$(($END_TIME - $START_TIME))
echo "Build completed in $ELAPSED_TIME seconds"
echo "file sizes:"
echo "Size of disclose.r1cs: $(wc -c <build/disclose.r1cs) bytes"
echo "Size of disclose.wasm: $(wc -c <build/disclose_js/disclose.wasm) bytes"
echo "Size of disclose_final.zkey: $(wc -c <build/disclose_final.zkey) bytes"
echo "Size of disclose_final.arkzkey: $(wc -c <build/disclose_final.arkzkey) bytes"

View File

@@ -0,0 +1,70 @@
#!/bin/bash
# Record the start time
START_TIME=$(date +%s)
# Check if the first argument is "app-only"
if [ "$1" == "app-only" ]; then
echo "Building only for the app"
APP_ONLY=1
else
APP_ONLY=0
fi
mkdir -p build
cd build
if [ ! -f powersOfTau28_hez_final_20.ptau ]; then
echo "Download power of tau...."
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_20.ptau
echo "Finished download!"
else
echo "Powers of tau file already downloaded... Skip download action!"
fi
cd ..
echo "compiling circuit"
circom circuits/register_sha256WithRSAEncryption65537.circom -l node_modules --r1cs --O1 --wasm -c --output build
echo "building zkey"
yarn snarkjs groth16 setup build/register_sha256WithRSAEncryption65537.r1cs build/powersOfTau28_hez_final_20.ptau build/register_sha256WithRSAEncryption65537.zkey
if command -v openssl &> /dev/null
then
RAND_STR=$(openssl rand -hex 64)
else
RAND_STR="random text"
fi
echo "building vkey"
echo $RAND_STR | yarn snarkjs zkey contribute build/register_sha256WithRSAEncryption65537.zkey build/register_sha256WithRSAEncryption65537_final.zkey
yarn snarkjs zkey export verificationkey build/register_sha256WithRSAEncryption65537_final.zkey build/register_sha256WithRSAEncryption65537_vkey.json
yarn snarkjs zkey export solidityverifier build/register_sha256WithRSAEncryption65537_final.zkey build/Verifier.sol
cp build/Verifier.sol ../contracts/contracts/Verifier.sol
echo "copied Verifier.sol to contracts"
# Install arkzkey-util binary in ark-zkey
cd ../app/ark-zkey
echo "[ark-zkey] Installing arkzkey-util..."
if ! command -v arkzkey-util &> /dev/null
then
cargo install --bin arkzkey-util --path .
else
echo "arkzkey-util already installed, skipping."
fi
cd ../../circuits/build
arkzkey-util register_sha256WithRSAEncryption65537_final.zkey
echo "Arkzkey generated"
cd ..
# Calculate and print the time taken by the whole script
END_TIME=$(date +%s)
ELAPSED_TIME=$(($END_TIME - $START_TIME))
echo "Build completed in $ELAPSED_TIME seconds"
echo "file sizes:"
echo "Size of register_sha256WithRSAEncryption65537.r1cs: $(wc -c <build/register_sha256WithRSAEncryption65537.r1cs) bytes"
echo "Size of register_sha256WithRSAEncryption65537.wasm: $(wc -c <build/register_sha256WithRSAEncryption65537_js/register_sha256WithRSAEncryption65537.wasm) bytes"
echo "Size of register_sha256WithRSAEncryption65537_final.zkey: $(wc -c <build/register_sha256WithRSAEncryption65537_final.zkey) bytes"
echo "Size of register_sha256WithRSAEncryption65537_final.arkzkey: $(wc -c <build/register_sha256WithRSAEncryption65537_final.arkzkey) bytes"

View File

@@ -17,6 +17,7 @@ describe("start testing register.circom", function () {
let mrz: any;
let passportData: any;
let poseidon: any;
let imt: any;
before(async () => {
@@ -42,6 +43,7 @@ describe("start testing register.circom", function () {
secret: secret,
mrz: mrz,
merkle_root: tree.root,
merkletree_size: BigInt(proof.pathIndices.length).toString(),
path: proof.pathIndices.map(index => index.toString()),
siblings: proof.siblings.flat().map(index => index.toString()),
bitmap: Array(90).fill(1).map(num => BigInt(num).toString()),
@@ -49,6 +51,7 @@ describe("start testing register.circom", function () {
current_date: [2, 4, 0, 5, 0, 3].map(num => BigInt(num)),
majority: ["1", "8"].map(char => BigInt(char.charCodeAt(0)).toString()),
};
console.log("inputs", inputs);
convertScopeToBinaryAndComputeValue(inputs.scope);
w = await circuit.calculateWitness(inputs);
});
@@ -62,9 +65,11 @@ describe("start testing register.circom", function () {
const scope = BigInt(convertScopeToBinaryAndComputeValue(inputs.scope)).toString();
const nullifier_js = poseidon.F.toString(poseidon([inputs.secret, scope]));
let nullifier_circom = await circuit.getOutput(w, ["nullifier"]);
let commitment_circom = await circuit.getOutput(w, ["commitment"]);
nullifier_circom = nullifier_circom.nullifier;
console.log("nullifier_circom", nullifier_circom);
console.log("nullifier_js", nullifier_js);
console.log("commitment_circom", commitment_circom);
//expect(nullifier_circom).to.equal(nullifier_js); //TODO: fix this
});
});

View File

@@ -8,7 +8,7 @@ import { MAX_DATAHASHES_LEN, SignatureAlgorithm, TREE_DEPTH } from "../../common
import { poseidon2 } from "poseidon-lite";
import { IMT } from "@zk-kit/imt";
import { mockPassportData_sha256WithRSAEncryption_65537 } from "../../common/src/utils/mockPassportData";
import { generateCircuitInputs } from '../../common/src/utils/generateInputs';
import { generateCircuitInputs_Register } from '../../common/src/utils/generateInputs';
describe("start testing register.circom", function () {
this.timeout(0);
@@ -30,40 +30,54 @@ describe("start testing register.circom", function () {
const reveal_bitmap = Array(90).fill('1');
const address = "0x70997970c51812dc3a010c7d01b50e0d17dc79c8";
const generated_inputs = generateCircuitInputs(
// const generated_inputs = generateCircuitInputs(
// passportData,
// reveal_bitmap,
// address,
// 18,
// { developmentMode: true }
// );
inputs = generateCircuitInputs_Register(
passportData,
reveal_bitmap,
address,
18,
{ developmentMode: true }
);
inputs = {
secret: BigInt(112111112).toString(),
mrz: generated_inputs.mrz,
econtent: generated_inputs.dataHashes,
datahashes_padded_length: generated_inputs.dataHashes.length,
signed_attributes: generated_inputs.eContentBytes,
pubkey: generated_inputs.pubkey,
merkle_root: generated_inputs.root,
path: generated_inputs.pathIndices,
siblings: generated_inputs.siblings,
signature_algorithm: generated_inputs.signatureAlgorithm,
signature: generated_inputs.signature,
}
// inputs = {
// secret: BigInt(112111112).toString(),
// mrz: generated_inputs.mrz,
// econtent: generated_inputs.dataHashes,
// datahashes_padded_length: generated_inputs.dataHashes.length,
// signed_attributes: generated_inputs.eContentBytes,
// pubkey: generated_inputs.pubkey,
// merkle_root: generated_inputs.root,
// path: generated_inputs.pathIndices,
// siblings: generated_inputs.siblings,
// signature_algorithm: generated_inputs.signatureAlgorithm,
// signature: generated_inputs.signature,
// }
console.log(JSON.stringify(inputs, null, 2));
w = await circuit.calculateWitness(inputs);
// console.log(JSON.stringify(inputs, null, 2));
// w = await circuit.calculateWitness(inputs);
});
it("compile and load the circuit", async function () {
expect(circuit).to.not.be.undefined;
});
it("log commitment", async function () {
it("calculate witness", async function () {
w = await circuit.calculateWitness(inputs);
let commitment = await circuit.getOutput(w, ["commitment"]);
let nullifier = await circuit.getOutput(w, ["nullifier"]);
console.log("commitment", commitment);
console.log("nullifier", nullifier);
});
it("try to calculate witness with bad inputs", async function () {
try {
const trashmrz = Array(93).fill(0).map(byte => BigInt(byte).toString());
inputs.mrz = trashmrz;
w = await circuit.calculateWitness(inputs);
expect.fail("Expected an error but none was thrown.");
} catch (error) {
expect(error.message).to.include("Assert Failed");
}
});
});

View File

@@ -1,35 +1,33 @@
import { MAX_DATAHASHES_LEN, SignatureAlgorithm, TREE_DEPTH } from "../constants/constants";
import { assert, shaPad } from "./shaPad";
import { PassportData } from "./types";
import { arraysAreEqual, bytesToBigDecimal, formatMrz, formatSigAlg, hash, splitToWords,
toUnsignedByte, getCurrentDateYYMMDD, getDigestLengthBytes } from "./utils";
import {
arraysAreEqual, bytesToBigDecimal, formatMrz, formatSigAlg, hash, splitToWords,
toUnsignedByte, getDigestLengthBytes
} from "./utils";
import { IMT } from "@zk-kit/imt";
import { getLeaf } from "./pubkeyTree";
import serializedTree from "../../pubkeys/serialized_tree.json";
import { poseidon2 } from "poseidon-lite";
export function generateCircuitInputs(
export function generateCircuitInputs_Register(
passportData: PassportData,
reveal_bitmap: string[],
address: string,
majority: number,
options: { developmentMode?: boolean } = { developmentMode: false }
) {
const tree = new IMT(poseidon2, TREE_DEPTH, 0, 2)
tree.setNodes(serializedTree)
const tree = new IMT(poseidon2, TREE_DEPTH, 0, 2);
tree.setNodes(serializedTree);
if (options.developmentMode) {
// This adds the pubkey of the passportData to the registry so that it's always found for testing purposes.
tree.insert(getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
issuer: 'C = TS, O = Government of Syldavia, OU = Ministry of tests, CN = CSCA-TEST',
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent
}).toString())
}).toString());
}
if (!["sha256WithRSAEncryption", "sha1WithRSAEncryption"].includes(passportData.signatureAlgorithm)) {
console.log(`${passportData.signatureAlgorithm} not supported for proof right now.`);
console.error(`${passportData.signatureAlgorithm} not supported for proof right now.`);
throw new Error(`${passportData.signatureAlgorithm} not supported for proof right now.`);
}
@@ -39,34 +37,31 @@ export function generateCircuitInputs(
assert(
arraysAreEqual(passportData.eContent.slice(72, 72 + getDigestLengthBytes(passportData.signatureAlgorithm)),
concatenatedDataHashesHashDigest),
concatenatedDataHashesHashDigest),
'concatenatedDataHashesHashDigest is at the right place in passportData.eContent'
)
);
console.log('passportData.pubKey.exponent', passportData.pubKey.exponent)
console.log('passportData.pubKey.exponent', passportData.pubKey.exponent);
const sigAlgFormatted = formatSigAlg(
passportData.signatureAlgorithm,
passportData.pubKey.exponent
)
const sigAlgFormatted = formatSigAlg(passportData.signatureAlgorithm, passportData.pubKey.exponent);
const leaf = getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
...passportData.pubKey,
}).toString()
console.log('leaf', leaf)
}).toString();
console.log('leaf', leaf);
const index = tree.indexOf(leaf) // this index is not the index in public_keys_parsed.json, but the index in the tree
console.log(`Index of pubkey in the registry: ${index}`)
const index = tree.indexOf(leaf);
console.log(`Index of pubkey in the registry: ${index}`);
if (index === -1) {
throw new Error("Your public key was not found in the registry");
}
const proof = tree.createProof(index)
console.log("verifyProof", tree.verifyProof(proof))
const proof = tree.createProof(index);
console.log("verifyProof", tree.verifyProof(proof));
if (passportData.dataGroupHashes.length > MAX_DATAHASHES_LEN) {
console.log(`Data hashes too long. Max length is ${MAX_DATAHASHES_LEN} bytes.`);
console.error(`Data hashes too long. Max length is ${MAX_DATAHASHES_LEN} bytes.`);
throw new Error(`This number of datagroups is currently unsupported. Please contact us so we add support!`);
}
@@ -76,29 +71,25 @@ export function generateCircuitInputs(
MAX_DATAHASHES_LEN
);
// don't forget to wrap everything in arrays for mopro bindings
return {
secret: [BigInt(0).toString()],
mrz: formattedMrz.map(byte => String(byte)),
reveal_bitmap: reveal_bitmap.map(byte => String(byte)),
dataHashes: Array.from(messagePadded).map((x) => x.toString()),
econtent: Array.from(messagePadded).map((x) => x.toString()),
datahashes_padded_length: [messagePaddedLen.toString()],
eContentBytes: passportData.eContent.map(toUnsignedByte).map(byte => String(byte)),
signed_attributes: passportData.eContent.map(toUnsignedByte).map(byte => String(byte)),
signature: splitToWords(
BigInt(bytesToBigDecimal(passportData.encryptedDigest)),
BigInt(64),
BigInt(32)
),
signatureAlgorithm: [SignatureAlgorithm[sigAlgFormatted].toString()],
signature_algorithm: [SignatureAlgorithm[sigAlgFormatted].toString()],
pubkey: splitToWords(
BigInt(passportData.pubKey.modulus as string),
BigInt(64),
BigInt(32)
),
pathIndices: proof.pathIndices.map(index => index.toString()),
merkle_root: [tree.root.toString()],
path: proof.pathIndices.map(index => index.toString()),
siblings: proof.siblings.flat().map(index => index.toString()),
root: [tree.root.toString()],
address: [BigInt(address).toString()],
majority: [BigInt(Math.floor(majority / 10) + 48).toString(), BigInt(majority % 10 + 48).toString()],
current_date: getCurrentDateYYMMDD().map(datePart => BigInt(datePart).toString()),
}
}
};
}

View File

@@ -0,0 +1,131 @@
import { MAX_DATAHASHES_LEN, SignatureAlgorithm, TREE_DEPTH } from "../constants/constants";
import { assert, shaPad } from "./shaPad";
import { PassportData } from "./types";
import {
arraysAreEqual, bytesToBigDecimal, formatMrz, formatSigAlg, hash, splitToWords,
toUnsignedByte, getCurrentDateYYMMDD, getDigestLengthBytes
} from "./utils";
import { IMT } from "@zk-kit/imt";
import { getLeaf } from "./pubkeyTree";
import serializedTree from "../../pubkeys/serialized_tree.json";
import { poseidon2 } from "poseidon-lite";
export function generateCircuitInputs(
passportData: PassportData,
reveal_bitmap: string[],
address: string,
majority: number,
options: { developmentMode?: boolean } = { developmentMode: false }
) {
const tree = new IMT(poseidon2, TREE_DEPTH, 0, 2)
tree.setNodes(serializedTree)
if (options.developmentMode) {
// This adds the pubkey of the passportData to the registry so that it's always found for testing purposes.
tree.insert(getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
issuer: 'C = TS, O = Government of Syldavia, OU = Ministry of tests, CN = CSCA-TEST',
modulus: passportData.pubKey.modulus,
exponent: passportData.pubKey.exponent
}).toString())
}
if (!["sha256WithRSAEncryption", "sha1WithRSAEncryption"].includes(passportData.signatureAlgorithm)) {
console.log(`${passportData.signatureAlgorithm} not supported for proof right now.`);
throw new Error(`${passportData.signatureAlgorithm} not supported for proof right now.`);
}
const formattedMrz = formatMrz(passportData.mrz);
const concatenatedDataHashesHashDigest = hash(passportData.signatureAlgorithm, passportData.dataGroupHashes);
console.log('concatenatedDataHashesHashDigest', concatenatedDataHashesHashDigest);
assert(
arraysAreEqual(passportData.eContent.slice(72, 72 + getDigestLengthBytes(passportData.signatureAlgorithm)),
concatenatedDataHashesHashDigest),
'concatenatedDataHashesHashDigest is at the right place in passportData.eContent'
)
console.log('passportData.pubKey.exponent', passportData.pubKey.exponent)
const sigAlgFormatted = formatSigAlg(
passportData.signatureAlgorithm,
passportData.pubKey.exponent
)
const leaf = getLeaf({
signatureAlgorithm: passportData.signatureAlgorithm,
...passportData.pubKey,
}).toString()
console.log('leaf', leaf)
const index = tree.indexOf(leaf) // this index is not the index in public_keys_parsed.json, but the index in the tree
console.log(`Index of pubkey in the registry: ${index}`)
if (index === -1) {
throw new Error("Your public key was not found in the registry");
}
const proof = tree.createProof(index)
console.log("verifyProof", tree.verifyProof(proof))
if (passportData.dataGroupHashes.length > MAX_DATAHASHES_LEN) {
console.log(`Data hashes too long. Max length is ${MAX_DATAHASHES_LEN} bytes.`);
throw new Error(`This number of datagroups is currently unsupported. Please contact us so we add support!`);
}
const [messagePadded, messagePaddedLen] = shaPad(
passportData.signatureAlgorithm,
new Uint8Array(passportData.dataGroupHashes),
MAX_DATAHASHES_LEN
);
return {
secret: [BigInt(0).toString()],
mrz: formattedMrz.map(byte => String(byte)),
econtent: Array.from(messagePadded).map((x) => x.toString()),
datahashes_padded_length: [messagePaddedLen.toString()],
signed_attributes: passportData.eContent.map(toUnsignedByte).map(byte => String(byte)),
signature: splitToWords(
BigInt(bytesToBigDecimal(passportData.encryptedDigest)),
BigInt(64),
BigInt(32)
),
signature_algorithm: [SignatureAlgorithm[sigAlgFormatted].toString()],
pubkey: splitToWords(
BigInt(passportData.pubKey.modulus as string),
BigInt(64),
BigInt(32)
),
merkle_root: [tree.root.toString()],
path: proof.pathIndices.map(index => index.toString()),
siblings: proof.siblings.flat().map(index => index.toString()),
}
// don't forget to wrap everything in arrays for mopro bindings
// return {
// mrz: formattedMrz.map(byte => String(byte)),
// reveal_bitmap: reveal_bitmap.map(byte => String(byte)),
// dataHashes: Array.from(messagePadded).map((x) => x.toString()),
// datahashes_padded_length: [messagePaddedLen.toString()],
// eContentBytes: passportData.eContent.map(toUnsignedByte).map(byte => String(byte)),
// signature: splitToWords(
// BigInt(bytesToBigDecimal(passportData.encryptedDigest)),
// BigInt(64),
// BigInt(32)
// ),
// signatureAlgorithm: [SignatureAlgorithm[sigAlgFormatted].toString()],
// pubkey: splitToWords(
// BigInt(passportData.pubKey.modulus as string),
// BigInt(64),
// BigInt(32)
// ),
// pathIndices: proof.pathIndices.map(index => index.toString()),
// siblings: proof.siblings.flat().map(index => index.toString()),
// root: [tree.root.toString()],
// address: [BigInt(address).toString()],
// majority: [BigInt(Math.floor(majority / 10) + 48).toString(), BigInt(majority % 10 + 48).toString()],
// current_date: getCurrentDateYYMMDD().map(datePart => BigInt(datePart).toString()),
// }
}

View File

@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVerifier {
function verifyProof(
uint[2] calldata _pA,
uint[2][2] calldata _pB,
uint[2] calldata _pC,
uint[4] calldata _pubSignals
) external view returns (bool);
}

View File

@@ -0,0 +1,96 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
//import {Groth16Verifier} from "./Verifier.sol";
import {IRegister} from "./interfaces/IRegister.sol";
import {Formatter} from "./Formatter.sol";
import {Registry} from "./Registry.sol";
import {Base64} from "./libraries/Base64.sol";
import {IVerifier} from "./IVerifier.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@zk-kit/imt.sol/internal/InternalLeanIMT.sol";
contract Register is IRegister {
Formatter public immutable formatter;
Registry public immutable registry;
using Base64 for *;
using Strings for uint256;
//LeanIMT for commitments
using InternalLeanIMT for LeanIMTData;
LeanIMTData internal imt;
mapping(uint256 => bool) public nullifiers;
mapping(uint256 => bool) public merkleRootsCreated;
mapping(uint256 => address) public verifiers;
constructor(Formatter f, Registry r) {
formatter = f;
registry = r;
}
function validateProof(RegisterProof calldata proof) external override {
if (!registry.checkRoot(bytes32(proof.merkle_root))) {
revert Register__InvalidMerkleRoot();
}
if (nullifiers[proof.nullifier]) {
revert Register__YouAreUsingTheSameNullifierTwice();
}
if (!verifyProof(proof)) {
revert Register__InvalidProof();
}
nullifiers[proof.nullifier] = true;
_addCommitment(proof.commitment);
emit ProofValidated(
proof.merkle_root,
proof.nullifier,
proof.commitment
);
}
function verifyProof(RegisterProof calldata proof) public view override returns (bool) {
return IVerifier(verifiers[proof.signature_algorithm]).verifyProof(
proof.a,
proof.b,
proof.c,
[uint(proof.commitment),uint(proof.nullifier),uint(proof.signature_algorithm), uint(proof.merkle_root)]
);
}
function _addCommitment(uint256 commitment) internal {
uint256 index = getMerkleTreeSize();
uint256 imt_root = imt._insert(commitment);
merkleRootsCreated[imt_root] = true;
emit AddCommitment(index, commitment, imt_root);
}
function checkRoot(uint256 root) external view returns (bool) {
return merkleRootsCreated[root];
}
function getMerkleTreeSize(
) public view returns (uint256) {
return imt.size;
}
function indexOf(uint commitment) public view returns (uint256) {
return imt._indexOf(commitment);
}
/*** DEV FUNCTIONS ***/
function dev_add_commitment(uint256 commitment) external {
_addCommitment(commitment);
}
function dev_set_signature_algorithm(uint256 signature_algorithm, address verifier_address) external {
verifiers[signature_algorithm] = verifier_address;
}
}

View File

@@ -37,50 +37,26 @@ contract Groth16Verifier {
uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant deltax1 = 2588669228216666389983837873148641426746353512252299031240304118286769521917;
uint256 constant deltax2 = 2552606576776338848330930219520907279764651930311004863336984726393262481390;
uint256 constant deltay1 = 6726215593913847530033416575708614413619076079997252220420830742072820639334;
uint256 constant deltay2 = 10560699865046748678886239819897855827741460195803489097560811811255627958125;
uint256 constant deltax1 = 13718730262782944445181266816991478953330240478907880032762976644004368769095;
uint256 constant deltax2 = 7647651767722145750577189591717487950066620799757713802411559019371908183121;
uint256 constant deltay1 = 18010692145584587833144505345217015081328719391252797096374613328440473671203;
uint256 constant deltay2 = 13706681280646876136066673893856043326817428414046853828230867042603723549797;
uint256 constant IC0x = 7651938682675872430563127057472046021479975536438268227065917076263512562994;
uint256 constant IC0y = 7610349476628321571751543737434580974256206550287501515251636422367359302696;
uint256 constant IC0x = 2394933690274366268096991608149586771617971086637005980462219572107262376870;
uint256 constant IC0y = 20927027790259021399853252662654342078146798621788037849745258289666464636902;
uint256 constant IC1x = 8577966619419709036745788710439922378153606439331664836269825697600541039752;
uint256 constant IC1y = 14693667220247562231599883792112002851332470333790224039046011090696845053903;
uint256 constant IC1x = 13876425163898693107635206634047444862664971380881780412439850273749661285967;
uint256 constant IC1y = 16777623889483121666624233379167123051090322578350587265904738964473689452703;
uint256 constant IC2x = 11993270738201992332019152398600037918464905034861945889025516325463380684019;
uint256 constant IC2y = 4288513816532252045725638221764518388358150115449958572456021523324280198976;
uint256 constant IC2x = 5310688978221496060716496531464778504908883772123742197171973900066799783599;
uint256 constant IC2y = 17477652341271748409633673187342334217694853293766246040511432220721862839133;
uint256 constant IC3x = 940999512712767256185089842749343883389471536645304010012183633317954916250;
uint256 constant IC3y = 1388207124990330270628304278795456656531387929869385052976203121610742954615;
uint256 constant IC3x = 12762959695051053667359449806462934775904437068030106478730216400079759114404;
uint256 constant IC3y = 17098717629853905758017539864954036959564007100593843727379115113743695258998;
uint256 constant IC4x = 8226785734831370800402510434454950883815285904569777820917793561180387283221;
uint256 constant IC4y = 13246621575992080283866745733764049448256389096858731262120990404226324795650;
uint256 constant IC5x = 7104768452793868924173131639223722856622354127467895672404301695119568512211;
uint256 constant IC5y = 6592131367087324950065468946673769859075787905380485396812659222727687599480;
uint256 constant IC6x = 18763987295686652229707359036617694049788246072232157614143469335986721230278;
uint256 constant IC6y = 12263984057860722217844880797489148440309854394334355523281110406266450651350;
uint256 constant IC7x = 18896741775760994275680696224735029443695460721550773389203724970225518714091;
uint256 constant IC7y = 11845888324893761767825152066947322348743508462980705569558200218330651589996;
uint256 constant IC8x = 18064326399650972549546803929477518582315194890085734520538437506296207321992;
uint256 constant IC8y = 16729742704257844538756706936053690551141525664429612989151133033296974178685;
uint256 constant IC9x = 20915028880216231935281908280772055953208057913496767149956752590627506812661;
uint256 constant IC9y = 19561158966601571201747654555461456963398870961626144900281426254207714108610;
uint256 constant IC10x = 2300340876136595722234467278848026056761648886775038025226455648310524861727;
uint256 constant IC10y = 2180761701414103965801284790235352143758123737165533916477644079182145789267;
uint256 constant IC11x = 8443999228576799203017982606631056270314016350574813836495806914183210813365;
uint256 constant IC11y = 18684762080716214900157370404466767251351242647218540138463970785543631332181;
uint256 constant IC12x = 1019553941582198282641785890089754722857994207929949436864940120158950252301;
uint256 constant IC12y = 9423940610950621196516827458576711569376020743027766722841646239172450136891;
uint256 constant IC4x = 13907753377762343563458660256001554781898388463873283253157116097107044008641;
uint256 constant IC4y = 12279848778142745019550123108319735218426905843417883245775305911902342481257;
// Memory data
@@ -89,7 +65,7 @@ contract Groth16Verifier {
uint16 constant pLastMem = 896;
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[12] calldata _pubSignals) public view returns (bool) {
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[4] calldata _pubSignals) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, q)) {
@@ -141,22 +117,6 @@ contract Groth16Verifier {
g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96)))
g1_mulAccC(_pVk, IC5x, IC5y, calldataload(add(pubSignals, 128)))
g1_mulAccC(_pVk, IC6x, IC6y, calldataload(add(pubSignals, 160)))
g1_mulAccC(_pVk, IC7x, IC7y, calldataload(add(pubSignals, 192)))
g1_mulAccC(_pVk, IC8x, IC8y, calldataload(add(pubSignals, 224)))
g1_mulAccC(_pVk, IC9x, IC9y, calldataload(add(pubSignals, 256)))
g1_mulAccC(_pVk, IC10x, IC10y, calldataload(add(pubSignals, 288)))
g1_mulAccC(_pVk, IC11x, IC11y, calldataload(add(pubSignals, 320)))
g1_mulAccC(_pVk, IC12x, IC12y, calldataload(add(pubSignals, 352)))
// -A
mstore(_pPairing, calldataload(pA))
@@ -220,22 +180,6 @@ contract Groth16Verifier {
checkField(calldataload(add(_pubSignals, 128)))
checkField(calldataload(add(_pubSignals, 160)))
checkField(calldataload(add(_pubSignals, 192)))
checkField(calldataload(add(_pubSignals, 224)))
checkField(calldataload(add(_pubSignals, 256)))
checkField(calldataload(add(_pubSignals, 288)))
checkField(calldataload(add(_pubSignals, 320)))
checkField(calldataload(add(_pubSignals, 352)))
checkField(calldataload(add(_pubSignals, 384)))
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)

View File

@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Interface for Register contract
/// @notice This interface declares the functions and events for the Register contract
interface IRegister {
/// @notice Error thrown when an invalid Merkle root is provided
error Register__InvalidMerkleRoot();
/// @notice Error thrown when the same nullifier is used more than once
error Register__YouAreUsingTheSameNullifierTwice();
/// @notice Error thrown when the proof provided is invalid
error Register__InvalidProof();
/// @notice Event emitted when a proof is successfully validated
/// @param merkle_root The Merkle root used in the proof
/// @param nullifier The nullifier used in the proof
/// @param commitment The commitment used in the proof
event ProofValidated(
uint merkle_root,
uint nullifier,
uint commitment
);
event AddCommitment(
uint index,
uint commitment,
uint merkle_root
);
/// @notice Struct to hold data for Register proofs
/// @param merkle_root The Merkle root used in the proof
/// @param commitment The commitment used in the proof
/// @param nullifier The nullifier used in the proof
/// @param a The 'a' parameter of the zkSNARK proof
/// @param b The 'b' parameter of the zkSNARK proof
/// @param c The 'c' parameter of the zkSNARK proof
struct RegisterProof {
uint commitment;
uint nullifier;
uint signature_algorithm;
uint merkle_root;
uint[2] a;
uint[2][2] b;
uint[2] c;
}
/// @notice Validates a Register proof
/// @param proof The Register proof to validate
function validateProof(RegisterProof calldata proof) external;
/// @notice Verifies a Register proof
/// @param proof The Register proof to verify
/// @return bool Returns true if the proof is valid, false otherwise
function verifyProof(RegisterProof calldata proof) external view returns (bool);
/// @notice Checks if a given root is valid
/// @param root The root to check
/// @return bool Returns true if the root is valid, false otherwise
function checkRoot(uint root) external view returns (bool);
/// @notice Gets the size of the Merkle tree
/// @return uint Returns the size of the Merkle tree
function getMerkleTreeSize() external view returns (uint);
/// @notice Finds the index of a given commitment in the Merkle tree
/// @param commitment The commitment to find
/// @return uint Returns the index of the commitment
function indexOf(uint commitment) external view returns (uint);
}

View File

@@ -10,10 +10,23 @@ const config: HardhatUserConfig = {
optimizer: {
enabled: true,
runs: 200,
details: {
yul: true
}
},
viaIR: true,
metadata: {
bytecodeHash: "none"
},
viaIR: false,
},
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
},
defaultNetwork: "hardhat",
networks: {
goerli: {
@@ -35,4 +48,4 @@ const config: HardhatUserConfig = {
},
};
export default config;
export default config;

View File

@@ -1,19 +1,37 @@
{
"name": "hardhat-project",
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.6",
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@nomicfoundation/hardhat-network-helpers": "^1.0.10",
"@nomicfoundation/hardhat-toolbox": "^3.0.0",
"@nomicfoundation/hardhat-verify": "^1.1.1",
"@typechain/ethers-v6": "^0.4.3",
"@typechain/hardhat": "^8.0.3",
"@types/chai": "^4.3.16",
"@types/mocha": "^10.0.6",
"@types/snarkjs": "^0.7.7",
"chai": "^4.4.1",
"ethers": "^6.12.1",
"hardhat": "^2.17.0",
"hardhat-gas-reporter": "^1.0.10",
"solidity-coverage": "^0.8.12",
"ts-node": "^10.9.1",
"typechain": "^8.3.2",
"typescript": "^5.1.6"
},
"dependencies": {
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@openzeppelin/contracts": "^4.9.2",
"@zk-kit/imt": "https://gitpkg.now.sh/0xturboblitz/zk-kit/packages/imt?6d417675",
"@types/jest": "^29.5.12",
"@zk-kit/imt": "^2.0.0-beta.4",
"@zk-kit/imt.sol": "^2.0.0-beta.12",
"axios": "^1.6.2",
"dotenv": "^16.3.1",
"hardhat-contract-sizer": "^2.10.0",
"mocha": "^10.4.0",
"poseidon-lite": "^0.2.0",
"poseidon-solidity": "^0.0.5",
"snarkjs": "^0.7.1"
}
}

320
contracts/test/Register.ts Normal file
View File

@@ -0,0 +1,320 @@
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect, assert } from "chai";
import { ethers } from "hardhat";
import { mockPassportData_sha256WithRSAEncryption_65537 } from "../../common/src/utils/mockPassportData";
import { countryCodes } from "../../common/src/constants/constants";
import { formatRoot, getCurrentDateYYMMDD } from "../../common/src/utils/utils";
import { groth16 } from 'snarkjs'
import { time } from "@nomicfoundation/hardhat-toolbox/network-helpers";
import axios from 'axios';
import { revealBitmapFromMapping } from "../../common/src/utils/revealBitmap";
import { generateCircuitInputs_Register } from "../../common/src/utils/generateInputs";
import fs from 'fs';
describe("Register", function () {
this.timeout(0);
let passportData, proof, inputs: any, publicSignals, revealChars, callData: any[], formattedCallData: any;
before(async function generateProof() {
// // Log the current block timestamp
// const latestBlock = await ethers.provider.getBlock('latest');
// // console.log(`Current block timestamp: ${latestBlock?.timestamp}`);
// // Set the next block timestamp to the current computer's timestamp
// const currentTimestamp = Math.floor(Date.now() / 1000) + 10;
// await ethers.provider.send('evm_setNextBlockTimestamp', [currentTimestamp]);
// await ethers.provider.send('evm_mine', []); // Mine a new block for the timestamp to take effect
// // Log the new block's timestamp to confirm
// const newBlock = await ethers.provider.getBlock('latest');
// // console.log(`New block timestamp set to: ${newBlock?.timestamp}`);
passportData = mockPassportData_sha256WithRSAEncryption_65537;
inputs = generateCircuitInputs_Register(
passportData,
{ developmentMode: true }
);
console.log('generating proof...');
({ proof, publicSignals } = await groth16.fullProve(
inputs,
"../circuits/build/register_sha256WithRSAEncryption65537_js/register_sha256WithRSAEncryption65537.wasm",
"../circuits/build/register_sha256WithRSAEncryption65537_final.zkey"
))
console.log('proof done');
revealChars = publicSignals.slice(0, 89).map((byte: string) => String.fromCharCode(parseInt(byte, 10))).join('');
const vKey = JSON.parse(fs.readFileSync("../circuits/build/register_sha256WithRSAEncryption65537_vkey.json") as unknown as string);
const verified = await groth16.verify(
vKey,
publicSignals,
proof
)
assert(verified == true, 'Should verify')
const cd = await groth16.exportSolidityCallData(proof, publicSignals);
callData = JSON.parse(`[${cd}]`);
formattedCallData = {
commitment: callData[3][0],
nullifier: callData[3][1],
signature_algorithm: callData[3][2],
merkle_root: callData[3][3],
a: callData[0],
b: [callData[1][0], callData[1][1]],
c: callData[2],
};
console.log('callData', callData);
console.log('Formatted callData for RegisterProof:', formattedCallData);
});
describe("PoP Register", function () {
async function deployHardhatFixture() {
const [owner, otherAccount, thirdAccount] = await ethers.getSigners();
const Verifier = await ethers.getContractFactory("Groth16Verifier");
const verifier = await Verifier.deploy();
await verifier.waitForDeployment();
console.log(`Verifier deployed to ${verifier.target}`);
const Formatter = await ethers.getContractFactory("Formatter");
const formatter = await Formatter.deploy();
await formatter.waitForDeployment();
await formatter.addCountryCodes(Object.entries(countryCodes));
console.log(`Formatter deployed to ${formatter.target}`);
const Registry = await ethers.getContractFactory("Registry");
const registry = await Registry.deploy(formatRoot(inputs.merkle_root));
await registry.waitForDeployment();
console.log(`Registry deployed to ${registry.target}`);
const PoseidonT3 = await ethers.getContractFactory("PoseidonT3");
const poseidonT3 = await PoseidonT3.deploy();
await poseidonT3.waitForDeployment();
console.log("PoseidonT3 deployed to:", poseidonT3.target);
const poseidonT3Address = poseidonT3.target;
const Register = await ethers.getContractFactory("Register", {
libraries: {
PoseidonT3: poseidonT3Address
}
});
const register = await Register.deploy(formatter.target, registry.target);
await register.waitForDeployment();
console.log(`Register deployed to ${register.target}`);
return { verifier, register, registry, formatter, owner, otherAccount, thirdAccount }
//registry.target
}
it("Verifier verifies a correct proof", async () => {
const { verifier } = await loadFixture(deployHardhatFixture);
expect(
await verifier.verifyProof(callData[0], callData[1], callData[2], callData[3])
).to.be.true;
});
it("Register should succeed", async function () {
const { verifier, register, registry, otherAccount, thirdAccount } = await loadFixture(
deployHardhatFixture
);
const commitments = [1, 2, 3, 4, 5]; // Example array of commitments, all set to zero as per instructions
for (const commitment of commitments) {
await register.dev_add_commitment(commitment);
}
await register.dev_set_signature_algorithm(1, verifier.target);
expect(await register
.connect(thirdAccount) // fine that it's not the same account as address is taken from the proof
.validateProof(formattedCallData)).not.to.be.reverted;
const indexOfCommitment = await register.indexOf(formattedCallData.commitment);
console.log(`Index of commitment: ${indexOfCommitment}`);
const merkleTreeSize = await register.getMerkleTreeSize();
console.log(`Merkle tree size: ${merkleTreeSize}`);
});
// it("Shouldn't allow minting with an invalid proof", async function () {
// const { register, otherAccount } = await loadFixture(
// deployHardhatFixture
// );
// const badCallData = JSON.parse(JSON.stringify(callData));
// badCallData[0][1] = "0x1cdbaf59a0439d55f19162ee0be5a501f5b55c669a6e1f8d27b75d95ff31ff7b";
// expect(
// register
// .connect(otherAccount)
// .mint(...badCallData as any)
// ).to.be.revertedWith("Invalid proof");
// });
// it("Should have a valid tokenURI", async function () {
// const { register, otherAccount } = await loadFixture(
// deployHardhatFixture
// );
// console.log('callData', callData)
// const tx = await register
// .connect(otherAccount)
// .mint(...callData);
// await tx.wait();
// const tokenURI = await register.tokenURI(0);
// console.log('tokenURI', tokenURI);
// const decodedTokenURI = Buffer.from(tokenURI.split(',')[1], 'base64').toString();
// let parsedTokenURI;
// try {
// parsedTokenURI = JSON.parse(decodedTokenURI);
// } catch (e) {
// assert(false, 'TokenURI is not a valid JSON');
// }
// console.log('parsedTokenURI', parsedTokenURI);
// });
// it("Should convert ISO dates to unix timestamps correctly", async function () {
// const { formatter } = await loadFixture(
// deployHardhatFixture
// );
// const unix_timestamp = await formatter.dateToUnixTimestamp("230512") // 2023 05 12
// console.log('unix_timestamp', unix_timestamp.toString());
// var date = new Date(Number(unix_timestamp) * 1000);
// console.log("date:", date.toUTCString());
// expect(date.getUTCFullYear()).to.equal(2023);
// expect(date.getUTCMonth()).to.equal(4);
// expect(date.getUTCDate()).to.equal(12);
// })
// it("Should support expiry", async function () {
// const { register, otherAccount } = await loadFixture(
// deployHardhatFixture
// );
// const tx = await register
// .connect(otherAccount)
// .mint(...callData);
// await tx.wait();
// const tokenURI = await register.tokenURI(0);
// const decodedTokenURI = Buffer.from(tokenURI.split(',')[1], 'base64').toString();
// const parsedTokenURI = JSON.parse(decodedTokenURI);
// const expired = parsedTokenURI.attributes.find((attribute: any) => attribute.trait_type === 'Expired');
// expect(expired.value).to.equal('No');
// await time.increaseTo(2240161656); // 2040
// const tokenURIAfter = await register.tokenURI(0);
// const decodedTokenURIAfter = Buffer.from(tokenURIAfter.split(',')[1], 'base64').toString();
// const parsedTokenURIAfter = JSON.parse(decodedTokenURIAfter);
// const expiredAfter = parsedTokenURIAfter.attributes.find((attribute: any) => attribute.trait_type === 'Expired');
// expect(expiredAfter.value).to.equal('Yes');
// })
// it("Should revert minting if the current date is set in the past", async function () {
// const { register, otherAccount } = await loadFixture(deployHardhatFixture);
// // Adjust inputs to set a current date 3 days in the past
// const pastDateYYMMDD = getCurrentDateYYMMDD(-3);
// inputs = { ...inputs, current_date: pastDateYYMMDD.map(datePart => BigInt(datePart).toString()) };
// console.log('pastDateYYMMDD', pastDateYYMMDD);
// console.log('inputs', inputs);
// // Generate proof with modified inputs
// console.log('generating proof with past date...');
// const { proof: invalidProof, publicSignals: invalidPublicSignals } = await groth16.fullProve(
// inputs,
// "../circuits/build/register_sha256WithRSAEncryption65537_js/register_sha256WithRSAEncryption65537.wasm",
// "../circuits/build/register_sha256WithRSAEncryption65537_final.zkey"
// );
// const invalidCd = await groth16.exportSolidityCallData(invalidProof, invalidPublicSignals);
// const invalidCallData = JSON.parse(`[${invalidCd}]`);
// // Attempt to mint with the proof generated with a past date
// await expect(
// register
// .connect(otherAccount)
// .mint(...invalidCallData)
// ).to.be.revertedWith("Current date is not within the valid range");
// });
});
// describe("Minting on mumbai", function () {
// it.skip("Should allow minting using a proof generated by ark-circom", async function () {
// const newCallDataFromArkCircom = [["0x089e5850e432d76f949cedc26527a7fb093194dd4026d5efb07c8ce6093fa977", "0x0154b01b5698e6249638be776d3641392cf89a5ad687beb2932c0ccf33f271d4"], [["0x2692dbce207361b048e6eff874fdc5d50433baa546fa754348a87373710044c0", "0x1db8ddab0dc204d41728efc05d2dae690bebb782b6088d92dda23a87b6bed0a2"], ["0x106be642690f0fe3562d139ed09498d979c8b35ecfb04e5a49422015cafa2705", "0x0b133e53cd0b4944ce2d34652488a16d1a020905dc1972ccc883d364dd3bb4ee"]], ["0x09eda5d551b150364ecb3efb432e4568b2be8f83c2db1dd1e1285c45a428b32b", "0x008ee9e870e5416849b3c94b8b9e4759580659f5a6535652d0a6634df23db2f5"], ["0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000000000006df9dd0914f215fafa1513e51ac9f1e2", "0x00000000000000000000000000000000000000000000093e703cd030e286890e", "0x0000000000000000000000000000000000000000000004770a914f3ae4e1288b", "0x000000000000000000000000000000000000000000000bf7e8ecb4e9609a489d", "0x00000000000000000000000000000000000000000000035762de41038bc2dcf1", "0x00000000000000000000000000000000000000000000050442c4055d62e9c4af", "0x0000000000000000000000000000000000000000000004db2bdc79a477a0fce0", "0x000000000000000000000000000000000000000000000acdbf649c76ec3df9ad", "0x000000000000000000000000000000000000000000000aaa0e6798ee3694f5ca", "0x000000000000000000000000000000000000000000000a1eaac37f80dd5e2879", "0x00000000000000000000000000000000000000000000033e063fba83c27efbce", "0x00000000000000000000000000000000000000000000045b9b05cab95025b000", "0x000000000000000000000000e6e4b6a802f2e0aee5676f6010e0af5c9cdd0a50"]];
// // const callDataFromArkCircomGeneratedInTest = [ [ '0x07a378ec2b5bafc15a21fb9c549ba2554a4ef22cfca3d835f44d270f547d0913', '0x089bb81fb68200ef64652ada5edf71a98dcc8a931a54162b03b61647acbae1fe' ], [ [ '0x2127ae75494aed0c384567cc890639d7609040373d0a549e665a26a39b264449', '0x2f0ea6c99648171b7e166086108131c9402f9c5ac4a3759705a9c9217852e328' ], [ '0x04efcb825be258573ffe8c9149dd2b040ea3b8a9fa3dfa1c57a87b11c20c21ec', '0x2b500aece0e5a5a64a5c7262ec379efc1a23f4e46d968aebd42337642ea2bd3e' ] ], [ '0x1964dc2231bcd1e0de363c3d2a790346b7e634b5878498ce6e8db0ac972b8125', '0x0d94cd74a89b0ed777bb309ce960191acd23d5e9c5f418722d03f80944c5e3ed' ], [ '0x000000000000000000544e45524f4c4600000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000df267467de87516584863641a75504b', '0x00000000000000000000000000000000000000000000084c754a8650038f4c82', '0x000000000000000000000000000000000000000000000d38447935bb72a5193c', '0x000000000000000000000000000000000000000000000cac133b01f78ab24970', '0x0000000000000000000000000000000000000000000006064295cda88310ce6e', '0x000000000000000000000000000000000000000000001026cd8776cbd52df4b0', '0x000000000000000000000000000000000000000000000d4748d254334ce92b36', '0x0000000000000000000000000000000000000000000005c1b0ba7159834b0bf1', '0x00000000000000000000000000000000000000000000029d91f03395b916792a', '0x000000000000000000000000000000000000000000000bcfbb30f8ea70a224df', '0x00000000000000000000000000000000000000000000003dcd943c93e565aa3e', '0x0000000000000000000000000000000000000000000009e8ce7916ab0fb0b000', '0x000000000000000000000000ede0fa5a7b196f512204f286666e5ec03e1005d2' ] ];
// const registerOnMumbaiAddress = '0x7D459347d092D35f043f73021f06c19f834f8c3E';
// const registerOnMumbai = await ethers.getContractAt('Register', registerOnMumbaiAddress);
// try {
// const tx = await registerOnMumbai.mint(...newCallDataFromArkCircom as any);
// console.log('txHash', tx.hash);
// const receipt = await tx.wait();
// console.log('receipt', receipt)
// expect(receipt?.status).to.equal(1);
// } catch (error) {
// console.error(error);
// expect(true).to.equal(false);
// }
// });
// it.skip("Should allow minting using lambda function", async function () {
// const registerOnMumbaiAddress = '0x0AAd39A080129763c8E1e2E9DC44E777DB0362a3';
// const provider = new ethers.JsonRpcProvider('https://polygon-mumbai-bor.publicnode.com');
// const registerOnMumbai = await ethers.getContractAt('Register', registerOnMumbaiAddress);
// try {
// const transactionRequest = await registerOnMumbai
// .mint.populateTransaction(...callData);
// console.log('transactionRequest', transactionRequest);
// const apiEndpoint = process.env.AWS_ENDPOINT;
// if (!apiEndpoint) {
// throw new Error('AWS_ENDPOINT env variable is not set');
// }
// const response = await axios.post(apiEndpoint, {
// chain: "mumbai",
// tx_data: transactionRequest
// });
// console.log('response status', response.status)
// console.log('response data', response.data)
// const receipt = await provider.waitForTransaction(response.data.hash);
// console.log('receipt', receipt)
// } catch (err) {
// console.log('err', err);
// }
// });
// })
});