mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
add Register.sol and tests
This commit is contained in:
43
circuits/circuits/binary-merkle-root.circom
Normal file
43
circuits/circuits/binary-merkle-root.circom
Normal 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];
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
70
circuits/scripts/build_disclose_circuit.sh
Executable file
70
circuits/scripts/build_disclose_circuit.sh
Executable 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"
|
||||
70
circuits/scripts/build_register_circuit.sh
Executable file
70
circuits/scripts/build_register_circuit.sh
Executable 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"
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
131
common/src/utils/generateInputs_old.ts
Normal file
131
common/src/utils/generateInputs_old.ts
Normal 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()),
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
11
contracts/contracts/IVerifier.sol
Normal file
11
contracts/contracts/IVerifier.sol
Normal 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);
|
||||
}
|
||||
96
contracts/contracts/Register.sol
Normal file
96
contracts/contracts/Register.sol
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
69
contracts/contracts/interfaces/IRegister.sol
Normal file
69
contracts/contracts/interfaces/IRegister.sol
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
320
contracts/test/Register.ts
Normal 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);
|
||||
// }
|
||||
// });
|
||||
// })
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user