mirror of
https://github.com/selfxyz/self.git
synced 2026-01-10 07:08:10 -05:00
Chore/refactor contracts (#582)
* add eu id support * add ofac and disclosure euid support * add contract support for euid cards * update contracts * add comment out to remember the interface what we need to implement * prettier * get combine verifyVcAndDisclose function and get bytes data * unified library and clean constant V2 * fix verifyVcAndDisclose interface * add prettier and run prettier except .sol files * prettier * circuit compilation for local environment * add register id circuit in the contracts dir * clean up ignition deploy scritps * refactor deploy scripts * prettier * update serialized dsc tree * fix ofac check * fix passport attestation id --------- Co-authored-by: turnoffthiscomputer <colin.remi07@gmail.com>
This commit is contained in:
@@ -4,10 +4,11 @@
|
||||
"license": "MIT",
|
||||
"author": "",
|
||||
"scripts": {
|
||||
"build-all": "bash scripts/build/build_register_circuits.sh && bash scripts/build/build_dsc_circuits.sh && bash scripts/build/build_disclose_circuits.sh",
|
||||
"build-all": "bash scripts/build/build_register_circuits.sh && bash scripts/build/build_register_circuits_id.sh && bash scripts/build/build_dsc_circuits.sh && bash scripts/build/build_disclose_circuits.sh",
|
||||
"build-disclose": "bash scripts/build/build_disclose_circuits.sh",
|
||||
"build-dsc": "bash scripts/build/build_dsc_circuits.sh",
|
||||
"build-register": "bash scripts/build/build_register_circuits.sh",
|
||||
"build-register-id": "bash scripts/build/build_register_circuits_id.sh",
|
||||
"download": "bash scripts/server/download_circuits_from_AWS.sh",
|
||||
"format": "prettier --write .",
|
||||
"nice": "prettier --write .",
|
||||
@@ -74,4 +75,4 @@
|
||||
"ts-mocha": "^10.0.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
source "scripts/build/common.sh"
|
||||
|
||||
# Set environment (change this value as needed)
|
||||
# ENV="prod"
|
||||
ENV="staging"
|
||||
|
||||
echo -e "${GREEN}Building disclose circuits for $ENV environment${NC}"
|
||||
|
||||
# Circuit-specific configurations
|
||||
CIRCUIT_TYPE="disclose"
|
||||
OUTPUT_DIR="build/${CIRCUIT_TYPE}"
|
||||
@@ -12,4 +18,7 @@ CIRCUITS=(
|
||||
"vc_and_disclose:20:true"
|
||||
)
|
||||
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
|
||||
echo -e "${GREEN}Disclose circuits build completed for $ENV environment!${NC}"
|
||||
echo -e "${YELLOW}Generated files are located in: contracts/verifiers/local/${ENV}/${CIRCUIT_TYPE}/${NC}"
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
source "scripts/build/common.sh"
|
||||
|
||||
# Set environment (change this value as needed)
|
||||
# ENV="prod"
|
||||
ENV="staging"
|
||||
|
||||
echo -e "${GREEN}Building DSC circuits for $ENV environment${NC}"
|
||||
|
||||
# Circuit-specific configurations
|
||||
CIRCUIT_TYPE="dsc"
|
||||
OUTPUT_DIR="build/${CIRCUIT_TYPE}"
|
||||
@@ -33,4 +39,7 @@ CIRCUITS=(
|
||||
"dsc_sha512_rsapss_65537_64_4096:22:false"
|
||||
)
|
||||
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
|
||||
echo -e "${GREEN}DSC circuits build completed for $ENV environment!${NC}"
|
||||
echo -e "${YELLOW}Generated files are located in: contracts/verifiers/local/${ENV}/${CIRCUIT_TYPE}/${NC}"
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
source "scripts/build/common.sh"
|
||||
|
||||
# Set environment (change this value as needed)
|
||||
# ENV="prod"
|
||||
ENV="staging"
|
||||
|
||||
echo -e "${GREEN}Building register circuits for $ENV environment${NC}"
|
||||
|
||||
# Circuit-specific configurations
|
||||
CIRCUIT_TYPE="register"
|
||||
OUTPUT_DIR="build/${CIRCUIT_TYPE}"
|
||||
@@ -31,4 +37,7 @@ CIRCUITS=(
|
||||
"register_sha512_sha512_sha512_rsa_65537_4096:21:false"
|
||||
)
|
||||
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
|
||||
echo -e "${GREEN}Register circuits build completed for $ENV environment!${NC}"
|
||||
echo -e "${YELLOW}Generated files are located in: contracts/verifiers/local/${ENV}/${CIRCUIT_TYPE}/${NC}"
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
source "scripts/build/common.sh"
|
||||
|
||||
# Set environment (change this value as needed)
|
||||
# ENV="prod"
|
||||
ENV="staging"
|
||||
|
||||
echo -e "${GREEN}Building register_id circuits for $ENV environment${NC}"
|
||||
|
||||
# Circuit-specific configurations
|
||||
CIRCUIT_TYPE="register_id"
|
||||
OUTPUT_DIR="build/${CIRCUIT_TYPE}"
|
||||
@@ -12,4 +18,7 @@ CIRCUITS=(
|
||||
"register_id_sha256_sha256_sha256_rsa_65537_4096:20:true"
|
||||
)
|
||||
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
|
||||
echo -e "${GREEN}Register_id circuits build completed for $ENV environment!${NC}"
|
||||
echo -e "${YELLOW}Generated files are located in: contracts/verifiers/local/${ENV}/${CIRCUIT_TYPE}/${NC}"
|
||||
|
||||
@@ -7,6 +7,24 @@ YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Environment validation
|
||||
validate_environment() {
|
||||
if [ -z "$ENV" ]; then
|
||||
echo -e "${RED}Error: ENV variable is not set${NC}"
|
||||
echo -e "${YELLOW}Please set ENV to 'prod' or 'staging' in your build script${NC}"
|
||||
echo -e "${YELLOW}Example: ENV=\"prod\"${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$ENV" != "prod" ] && [ "$ENV" != "staging" ]; then
|
||||
echo -e "${RED}Error: ENV must be 'prod' or 'staging'${NC}"
|
||||
echo -e "${YELLOW}Current value: $ENV${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Environment set to: $ENV${NC}"
|
||||
}
|
||||
|
||||
download_ptau() {
|
||||
local POWEROFTAU=$1
|
||||
mkdir -p build
|
||||
@@ -15,7 +33,7 @@ download_ptau() {
|
||||
echo -e "${YELLOW}Download power of tau....${NC}"
|
||||
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_${POWEROFTAU}.ptau
|
||||
echo -e "${GREEN}Finished download!${NC}"
|
||||
else
|
||||
else
|
||||
echo -e "${YELLOW}Powers of tau file already downloaded${NC}"
|
||||
fi
|
||||
cd ..
|
||||
@@ -36,11 +54,14 @@ build_circuit() {
|
||||
local OUTPUT_DIR=$4
|
||||
local START_TIME=$(date +%s)
|
||||
|
||||
echo -e "${BLUE}Compiling circuit: $CIRCUIT_NAME${NC}"
|
||||
|
||||
# Validate environment before building
|
||||
validate_environment
|
||||
|
||||
echo -e "${BLUE}Compiling circuit: $CIRCUIT_NAME for $ENV environment${NC}"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p ${OUTPUT_DIR}/${CIRCUIT_NAME}/
|
||||
|
||||
|
||||
# Set circuit path based on CIRCUIT_TYPE
|
||||
local CIRCUIT_PATH
|
||||
if [ "$CIRCUIT_TYPE" = "register" ] || [ "$CIRCUIT_TYPE" = "dsc" ] || [ "$CIRCUIT_TYPE" = "register_id" ]; then
|
||||
@@ -48,7 +69,7 @@ build_circuit() {
|
||||
else
|
||||
CIRCUIT_PATH="circuits/${CIRCUIT_TYPE}/${CIRCUIT_NAME}.circom"
|
||||
fi
|
||||
|
||||
|
||||
# Compile circuit
|
||||
circom ${CIRCUIT_PATH} \
|
||||
-l node_modules \
|
||||
@@ -74,10 +95,21 @@ build_circuit() {
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/${CIRCUIT_NAME}_final.zkey \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/${CIRCUIT_NAME}_vkey.json
|
||||
|
||||
# Generate and copy Solidity verifier
|
||||
# Generate Solidity verifier with environment-specific naming
|
||||
local VERIFIER_CONTRACT_NAME
|
||||
local VERIFIER_FILE_NAME
|
||||
|
||||
if [ "$ENV" = "staging" ]; then
|
||||
VERIFIER_CONTRACT_NAME="Verifier_${CIRCUIT_NAME}_${ENV}"
|
||||
VERIFIER_FILE_NAME="Verifier_${CIRCUIT_NAME}_${ENV}.sol"
|
||||
else
|
||||
VERIFIER_CONTRACT_NAME="Verifier_${CIRCUIT_NAME}"
|
||||
VERIFIER_FILE_NAME="Verifier_${CIRCUIT_NAME}.sol"
|
||||
fi
|
||||
|
||||
yarn snarkjs zkey export solidityverifier \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/${CIRCUIT_NAME}_final.zkey \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/Verifier_${CIRCUIT_NAME}.sol
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/${VERIFIER_FILE_NAME}
|
||||
|
||||
OS=""
|
||||
|
||||
@@ -94,23 +126,27 @@ build_circuit() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# Replace contract name with environment-specific name
|
||||
if [ "$OS" = 'Mac' ]; then
|
||||
sed -i '' "s/Groth16Verifier/Verifier_${CIRCUIT_NAME}/g" \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/Verifier_${CIRCUIT_NAME}.sol
|
||||
sed -i '' "s/Groth16Verifier/${VERIFIER_CONTRACT_NAME}/g" \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/${VERIFIER_FILE_NAME}
|
||||
elif [ "$OS" = 'Linux' ]; then
|
||||
sed -i "s/Groth16Verifier/Verifier_${CIRCUIT_NAME}/g" \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/Verifier_${CIRCUIT_NAME}.sol
|
||||
sed -i "s/Groth16Verifier/${VERIFIER_CONTRACT_NAME}/g" \
|
||||
${OUTPUT_DIR}/${CIRCUIT_NAME}/${VERIFIER_FILE_NAME}
|
||||
fi
|
||||
|
||||
# Copy verifier to contracts directory
|
||||
mkdir -p ../contracts/contracts/verifiers/local/${CIRCUIT_TYPE}/
|
||||
cp ${OUTPUT_DIR}/${CIRCUIT_NAME}/Verifier_${CIRCUIT_NAME}.sol \
|
||||
../contracts/contracts/verifiers/local/${CIRCUIT_TYPE}/Verifier_${CIRCUIT_NAME}.sol
|
||||
|
||||
echo -e "${BLUE}Copied Verifier_${CIRCUIT_NAME}.sol to contracts${NC}"
|
||||
# Copy verifier to environment-specific contracts directory
|
||||
local CONTRACTS_OUTPUT_DIR="../contracts/contracts/verifiers/local/${ENV}/${CIRCUIT_TYPE}/"
|
||||
mkdir -p ${CONTRACTS_OUTPUT_DIR}
|
||||
cp ${OUTPUT_DIR}/${CIRCUIT_NAME}/${VERIFIER_FILE_NAME} \
|
||||
${CONTRACTS_OUTPUT_DIR}${VERIFIER_FILE_NAME}
|
||||
|
||||
echo -e "${BLUE}Copied ${VERIFIER_FILE_NAME} to contracts/${ENV}/${CIRCUIT_TYPE}/${NC}"
|
||||
|
||||
# Print build statistics
|
||||
echo -e "${GREEN}Build of $CIRCUIT_NAME completed in $(($(date +%s) - START_TIME)) seconds${NC}"
|
||||
echo -e "${GREEN}Build of $CIRCUIT_NAME for $ENV completed in $(($(date +%s) - START_TIME)) seconds${NC}"
|
||||
echo -e "${BLUE}Contract name: ${VERIFIER_CONTRACT_NAME}${NC}"
|
||||
echo -e "${BLUE}Output path: ${CONTRACTS_OUTPUT_DIR}${VERIFIER_FILE_NAME}${NC}"
|
||||
echo -e "${BLUE}Size of ${CIRCUIT_NAME}.r1cs: $(wc -c < ${OUTPUT_DIR}/${CIRCUIT_NAME}/${CIRCUIT_NAME}.r1cs) bytes${NC}"
|
||||
echo -e "${BLUE}Size of ${CIRCUIT_NAME}.wasm: $(wc -c < ${OUTPUT_DIR}/${CIRCUIT_NAME}/${CIRCUIT_NAME}_js/${CIRCUIT_NAME}.wasm) bytes${NC}"
|
||||
echo -e "${BLUE}Size of ${CIRCUIT_NAME}_final.zkey: $(wc -c < ${OUTPUT_DIR}/${CIRCUIT_NAME}/${CIRCUIT_NAME}_final.zkey) bytes${NC}"
|
||||
@@ -120,7 +156,7 @@ build_circuits() {
|
||||
local CIRCUITS=("$@")
|
||||
local CIRCUIT_TYPE="$1"
|
||||
local OUTPUT_DIR="$2"
|
||||
shift 2
|
||||
shift 2
|
||||
local TOTAL_START_TIME=$(date +%s)
|
||||
|
||||
# Build circuits
|
||||
@@ -139,4 +175,4 @@ build_circuits() {
|
||||
done
|
||||
|
||||
echo -e "${GREEN}Total completed in $(($(date +%s) - TOTAL_START_TIME)) seconds${NC}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { assert, expect } from 'chai';
|
||||
import path from 'path';
|
||||
import { wasm as wasm_tester } from 'circom_tester';
|
||||
import {
|
||||
attributeToPosition_ID,
|
||||
ID_CARD_ATTESTATION_ID
|
||||
attributeToPosition_ID,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import { poseidon1, poseidon2 } from 'poseidon-lite';
|
||||
import { LeanIMT } from '@openpassport/zk-kit-lean-imt';
|
||||
@@ -15,283 +15,276 @@ import nameAndDobjson from '../../../common/ofacdata/outputs/nameAndDobSMT_ID.js
|
||||
import nameAndYobjson from '../../../common/ofacdata/outputs/nameAndYobSMT_ID.json';
|
||||
import passportNojson from '../../../common/ofacdata/outputs/passportNoAndNationalitySMT.json';
|
||||
import {
|
||||
formatAndUnpackForbiddenCountriesList,
|
||||
formatAndUnpackReveal,
|
||||
getAttributeFromUnpackedReveal,
|
||||
formatAndUnpackForbiddenCountriesList,
|
||||
formatAndUnpackReveal,
|
||||
getAttributeFromUnpackedReveal,
|
||||
} from '../../../common/src/utils/circuits/formatOutputs';
|
||||
import { generateCommitment } from '../../../common/src/utils/passports/passport';
|
||||
import { hashEndpointWithScope } from '../../../common/src/utils/scope';
|
||||
import { genMockIdDoc } from '../../../common/src/utils/passports/genMockIdDoc';
|
||||
|
||||
describe('Disclose', function () {
|
||||
this.timeout(0);
|
||||
let inputs: any;
|
||||
let circuit: any;
|
||||
let w: any;
|
||||
const passportData = genMockIdDoc({
|
||||
idType: 'mock_id_card'
|
||||
});
|
||||
console.log(passportData.mrz);
|
||||
const forbidden_countries_list = ['ALG', 'DZA'];
|
||||
this.timeout(0);
|
||||
let inputs: any;
|
||||
let circuit: any;
|
||||
let w: any;
|
||||
const passportData = genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
});
|
||||
console.log(passportData.mrz);
|
||||
const forbidden_countries_list = ['ALG', 'DZA'];
|
||||
|
||||
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
|
||||
const majority = '18';
|
||||
const user_identifier = crypto.randomUUID();
|
||||
const selector_dg1 = Array(90).fill('1');
|
||||
const selector_older_than = '1';
|
||||
const endpoint = 'https://example.com';
|
||||
const scope = 'scope';
|
||||
const fullScope = hashEndpointWithScope(endpoint, scope);
|
||||
const attestation_id = ID_CARD_ATTESTATION_ID;
|
||||
const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString();
|
||||
const majority = '18';
|
||||
const user_identifier = crypto.randomUUID();
|
||||
const selector_dg1 = Array(90).fill('1');
|
||||
const selector_older_than = '1';
|
||||
const endpoint = 'https://example.com';
|
||||
const scope = 'scope';
|
||||
const fullScope = hashEndpointWithScope(endpoint, scope);
|
||||
const attestation_id = ID_CARD_ATTESTATION_ID;
|
||||
|
||||
// compute the commitment and insert it in the tree
|
||||
const commitment = generateCommitment(secret, attestation_id, passportData);
|
||||
console.log('commitment in js ', commitment);
|
||||
const tree: any = new LeanIMT((a, b) => poseidon2([a, b]), []);
|
||||
tree.insert(BigInt(commitment));
|
||||
// compute the commitment and insert it in the tree
|
||||
const commitment = generateCommitment(secret, attestation_id, passportData);
|
||||
console.log('commitment in js ', commitment);
|
||||
const tree: any = new LeanIMT((a, b) => poseidon2([a, b]), []);
|
||||
tree.insert(BigInt(commitment));
|
||||
|
||||
const passportNo_smt = new SMT(poseidon2, true);
|
||||
passportNo_smt.import(passportNojson);
|
||||
const passportNo_smt = new SMT(poseidon2, true);
|
||||
passportNo_smt.import(passportNojson);
|
||||
|
||||
const nameAndDob_smt = new SMT(poseidon2, true);
|
||||
nameAndDob_smt.import(nameAndDobjson);
|
||||
const nameAndDob_smt = new SMT(poseidon2, true);
|
||||
nameAndDob_smt.import(nameAndDobjson);
|
||||
|
||||
const nameAndYob_smt = new SMT(poseidon2, true);
|
||||
nameAndYob_smt.import(nameAndYobjson);
|
||||
const nameAndYob_smt = new SMT(poseidon2, true);
|
||||
nameAndYob_smt.import(nameAndYobjson);
|
||||
|
||||
const selector_ofac = 1;
|
||||
const selector_ofac = 1;
|
||||
|
||||
before(async () => {
|
||||
circuit = await wasm_tester(
|
||||
path.join(__dirname, '../../circuits/disclose/vc_and_disclose_id.circom'),
|
||||
{
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
}
|
||||
);
|
||||
before(async () => {
|
||||
circuit = await wasm_tester(
|
||||
path.join(__dirname, '../../circuits/disclose/vc_and_disclose_id.circom'),
|
||||
{
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
inputs = generateCircuitInputsVCandDisclose(
|
||||
secret,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData,
|
||||
fullScope,
|
||||
selector_dg1,
|
||||
selector_older_than,
|
||||
tree,
|
||||
majority,
|
||||
passportNo_smt,
|
||||
nameAndDob_smt,
|
||||
nameAndYob_smt,
|
||||
selector_ofac,
|
||||
forbidden_countries_list,
|
||||
user_identifier
|
||||
);
|
||||
});
|
||||
inputs = generateCircuitInputsVCandDisclose(
|
||||
secret,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData,
|
||||
fullScope,
|
||||
selector_dg1,
|
||||
selector_older_than,
|
||||
tree,
|
||||
majority,
|
||||
passportNo_smt,
|
||||
nameAndDob_smt,
|
||||
nameAndYob_smt,
|
||||
selector_ofac,
|
||||
forbidden_countries_list,
|
||||
user_identifier
|
||||
);
|
||||
});
|
||||
|
||||
// it('should compile and load the circuit', async function () {
|
||||
// expect(circuit).to.not.be.undefined;
|
||||
// });
|
||||
// it('should compile and load the circuit', async function () {
|
||||
// expect(circuit).to.not.be.undefined;
|
||||
// });
|
||||
|
||||
it('should have nullifier == poseidon(secret, scope)', async function () {
|
||||
w = await circuit.calculateWitness(inputs);
|
||||
const nullifier_js = poseidon2([inputs.secret, inputs.scope]).toString();
|
||||
const nullifier_circom = (await circuit.getOutput(w, ['nullifier'])).nullifier;
|
||||
|
||||
console.log('nullifier_circom', nullifier_circom);
|
||||
console.log('nullifier_js', nullifier_js);
|
||||
expect(nullifier_circom).to.equal(nullifier_js);
|
||||
});
|
||||
|
||||
describe('MRZ selective disclosure', function () {
|
||||
const attributeCombinations = [
|
||||
['issuing_state', 'name'],
|
||||
['passport_number', 'nationality', 'date_of_birth'],
|
||||
['gender', 'expiry_date'],
|
||||
];
|
||||
|
||||
attributeCombinations.forEach((combination) => {
|
||||
it(`Disclosing ${combination.join(', ')}`, async function () {
|
||||
const attributeToReveal = Object.keys(attributeToPosition_ID).reduce((acc, attribute) => {
|
||||
acc[attribute] = combination.includes(attribute);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
|
||||
Object.entries(attributeToReveal).forEach(([attribute, reveal]) => {
|
||||
if (reveal) {
|
||||
const [start, end] = attributeToPosition_ID[attribute];
|
||||
selector_dg1.fill('1', start, end + 1);
|
||||
}
|
||||
});
|
||||
|
||||
inputs = {
|
||||
...inputs,
|
||||
selector_dg1: selector_dg1.map(String),
|
||||
};
|
||||
|
||||
it('should have nullifier == poseidon(secret, scope)', async function () {
|
||||
w = await circuit.calculateWitness(inputs);
|
||||
const nullifier_js = poseidon2([inputs.secret, inputs.scope]).toString();
|
||||
const nullifier_circom = (await circuit.getOutput(w, ['nullifier'])).nullifier;
|
||||
|
||||
console.log('nullifier_circom', nullifier_circom);
|
||||
console.log('nullifier_js', nullifier_js);
|
||||
expect(nullifier_circom).to.equal(nullifier_js);
|
||||
});
|
||||
|
||||
|
||||
describe('MRZ selective disclosure', function () {
|
||||
const attributeCombinations = [
|
||||
['issuing_state', 'name'],
|
||||
['passport_number', 'nationality', 'date_of_birth'],
|
||||
['gender', 'expiry_date'],
|
||||
];
|
||||
|
||||
attributeCombinations.forEach((combination) => {
|
||||
it(`Disclosing ${combination.join(', ')}`, async function () {
|
||||
const attributeToReveal = Object.keys(attributeToPosition_ID).reduce((acc, attribute) => {
|
||||
acc[attribute] = combination.includes(attribute);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
|
||||
Object.entries(attributeToReveal).forEach(([attribute, reveal]) => {
|
||||
if (reveal) {
|
||||
const [start, end] = attributeToPosition_ID[attribute];
|
||||
selector_dg1.fill('1', start, end + 1);
|
||||
}
|
||||
});
|
||||
|
||||
inputs = {
|
||||
...inputs,
|
||||
selector_dg1: selector_dg1.map(String),
|
||||
};
|
||||
|
||||
w = await circuit.calculateWitness(inputs);
|
||||
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
|
||||
for (let i = 0; i < 88; i++) {
|
||||
if (selector_dg1[i] == '1') {
|
||||
const char = String.fromCharCode(Number(inputs.dg1[i + 5]));
|
||||
assert(reveal_unpacked[i] == char, 'Should reveal the right character');
|
||||
} else {
|
||||
assert(reveal_unpacked[i] == '\x00', 'Should not reveal');
|
||||
}
|
||||
}
|
||||
|
||||
const forbidden_countries_list_packed = await circuit.getOutput(w, [
|
||||
'forbidden_countries_list_packed[4]',
|
||||
]);
|
||||
const forbidden_countries_list_unpacked = formatAndUnpackForbiddenCountriesList(
|
||||
forbidden_countries_list_packed
|
||||
);
|
||||
expect(forbidden_countries_list_unpacked).to.deep.equal(forbidden_countries_list);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow disclosing majority', async function () {
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
|
||||
w = await circuit.calculateWitness({
|
||||
...inputs,
|
||||
selector_dg1: selector_dg1.map(String),
|
||||
});
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
const older_than = getAttributeFromUnpackedReveal(reveal_unpacked, 'older_than', 'id');
|
||||
expect(older_than).to.equal('18');
|
||||
});
|
||||
|
||||
it("shouldn't allow disclosing wrong majority", async function () {
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
|
||||
w = await circuit.calculateWitness({
|
||||
...inputs,
|
||||
majority: ['5', '0'].map((char) => BigInt(char.charCodeAt(0)).toString()),
|
||||
selector_dg1: selector_dg1.map(String),
|
||||
});
|
||||
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
expect(reveal_unpacked[88]).to.equal('\x00');
|
||||
expect(reveal_unpacked[89]).to.equal('\x00');
|
||||
|
||||
for (let i = 0; i < 88; i++) {
|
||||
if (selector_dg1[i] == '1') {
|
||||
const char = String.fromCharCode(Number(inputs.dg1[i + 5]));
|
||||
assert(reveal_unpacked[i] == char, 'Should reveal the right character');
|
||||
} else {
|
||||
assert(reveal_unpacked[i] == '\x00', 'Should not reveal');
|
||||
}
|
||||
}
|
||||
|
||||
const forbidden_countries_list_packed = await circuit.getOutput(w, [
|
||||
'forbidden_countries_list_packed[4]',
|
||||
]);
|
||||
const forbidden_countries_list_unpacked = formatAndUnpackForbiddenCountriesList(
|
||||
forbidden_countries_list_packed
|
||||
);
|
||||
expect(forbidden_countries_list_unpacked).to.deep.equal(forbidden_countries_list);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow disclosing majority', async function () {
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
|
||||
w = await circuit.calculateWitness({
|
||||
...inputs,
|
||||
selector_dg1: selector_dg1.map(String),
|
||||
});
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
const older_than = getAttributeFromUnpackedReveal(reveal_unpacked, 'older_than', 'id');
|
||||
expect(older_than).to.equal('18');
|
||||
});
|
||||
|
||||
it("shouldn't allow disclosing wrong majority", async function () {
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
|
||||
w = await circuit.calculateWitness({
|
||||
...inputs,
|
||||
majority: ['5', '0'].map((char) => BigInt(char.charCodeAt(0)).toString()),
|
||||
selector_dg1: selector_dg1.map(String),
|
||||
});
|
||||
|
||||
describe('OFAC disclosure', function () {
|
||||
it('should allow disclosing OFAC check result when selector is 1', async function () {
|
||||
w = await circuit.calculateWitness(inputs);
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
const ofac_results = reveal_unpacked.slice(92, 94);
|
||||
expect(ofac_results).to.deep.equal(
|
||||
['\x01', '\x01'],
|
||||
'OFAC result bits should be [1, 1]'
|
||||
);
|
||||
expect(ofac_results).to.not.equal(['\x00', '\x00'], 'OFAC result should be revealed');
|
||||
});
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
|
||||
it('should not disclose OFAC check result when selector is 0', async function () {
|
||||
w = await circuit.calculateWitness({
|
||||
...inputs,
|
||||
selector_ofac: '0',
|
||||
});
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
expect(reveal_unpacked[88]).to.equal('\x00');
|
||||
expect(reveal_unpacked[89]).to.equal('\x00');
|
||||
});
|
||||
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
|
||||
// OFAC result should be hidden (null byte)
|
||||
const ofac_results = reveal_unpacked.slice(92, 94);
|
||||
expect(ofac_results).to.deep.equal(
|
||||
['\x00', '\x00'],
|
||||
'OFAC result bits should be [0, 0]'
|
||||
);
|
||||
expect(ofac_results).to.not.equal(['\x01', '\x01'], 'OFAC result should not be revealed');
|
||||
});
|
||||
|
||||
it('should show different levels of OFAC matching', async function () {
|
||||
// Test cases for different matching scenarios
|
||||
const testCases = [
|
||||
{
|
||||
desc: 'No details match',
|
||||
data: genMockIdDoc({ idType: 'mock_id_card' }),
|
||||
expectedBits: ['\x01', '\x01'],
|
||||
},
|
||||
{
|
||||
desc: 'Name and DOB matches (so YOB matches too)',
|
||||
data: genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
passportNumber: 'DIF123456',
|
||||
lastName: 'HENAO MONTOYA',
|
||||
firstName: 'ARCANGEL DE JESUS',
|
||||
birthDate: '541007',
|
||||
expiryDate: '300101',
|
||||
}),
|
||||
expectedBits: ['\x00', '\x00'],
|
||||
},
|
||||
{
|
||||
desc: 'Only name and YOB match',
|
||||
data: genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
passportNumber: 'DIF123456',
|
||||
lastName: 'HENAO MONTOYA',
|
||||
firstName: 'ARCANGEL DE JESUS',
|
||||
birthDate: '541299',
|
||||
expiryDate: '300101', // Same year (54) different month/day
|
||||
}),
|
||||
expectedBits: ['\x01', '\x00'],
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`Testing: ${testCase.desc}`);
|
||||
const passportData = testCase.data;
|
||||
const sanctionedCommitment = generateCommitment(
|
||||
secret,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData
|
||||
);
|
||||
tree.insert(BigInt(sanctionedCommitment));
|
||||
|
||||
const testInputs = generateCircuitInputsVCandDisclose(
|
||||
secret,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData,
|
||||
fullScope,
|
||||
Array(90).fill('0'), // selector_dg1
|
||||
selector_older_than,
|
||||
tree,
|
||||
majority,
|
||||
passportNo_smt,
|
||||
nameAndDob_smt,
|
||||
nameAndYob_smt,
|
||||
'1', // selector_ofac
|
||||
forbidden_countries_list,
|
||||
user_identifier
|
||||
);
|
||||
|
||||
w = await circuit.calculateWitness(testInputs);
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
const ofac_results = reveal_unpacked.slice(92, 94);
|
||||
|
||||
console.log(`${testCase.desc} - OFAC bits:`, ofac_results);
|
||||
expect(ofac_results).to.deep.equal(
|
||||
testCase.expectedBits,
|
||||
`Failed matching pattern for: ${testCase.desc}`
|
||||
);
|
||||
}
|
||||
});
|
||||
describe('OFAC disclosure', function () {
|
||||
it('should allow disclosing OFAC check result when selector is 1', async function () {
|
||||
w = await circuit.calculateWitness(inputs);
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
const ofac_results = reveal_unpacked.slice(92, 94);
|
||||
expect(ofac_results).to.deep.equal(['\x01', '\x01'], 'OFAC result bits should be [1, 1]');
|
||||
expect(ofac_results).to.not.equal(['\x00', '\x00'], 'OFAC result should be revealed');
|
||||
});
|
||||
|
||||
it('should not disclose OFAC check result when selector is 0', async function () {
|
||||
w = await circuit.calculateWitness({
|
||||
...inputs,
|
||||
selector_ofac: '0',
|
||||
});
|
||||
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
|
||||
// OFAC result should be hidden (null byte)
|
||||
const ofac_results = reveal_unpacked.slice(92, 94);
|
||||
expect(ofac_results).to.deep.equal(['\x00', '\x00'], 'OFAC result bits should be [0, 0]');
|
||||
expect(ofac_results).to.not.equal(['\x01', '\x01'], 'OFAC result should not be revealed');
|
||||
});
|
||||
|
||||
it('should show different levels of OFAC matching', async function () {
|
||||
// Test cases for different matching scenarios
|
||||
const testCases = [
|
||||
{
|
||||
desc: 'No details match',
|
||||
data: genMockIdDoc({ idType: 'mock_id_card' }),
|
||||
expectedBits: ['\x01', '\x01'],
|
||||
},
|
||||
{
|
||||
desc: 'Name and DOB matches (so YOB matches too)',
|
||||
data: genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
passportNumber: 'DIF123456',
|
||||
lastName: 'HENAO MONTOYA',
|
||||
firstName: 'ARCANGEL DE JESUS',
|
||||
birthDate: '541007',
|
||||
expiryDate: '300101',
|
||||
}),
|
||||
expectedBits: ['\x00', '\x00'],
|
||||
},
|
||||
{
|
||||
desc: 'Only name and YOB match',
|
||||
data: genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
passportNumber: 'DIF123456',
|
||||
lastName: 'HENAO MONTOYA',
|
||||
firstName: 'ARCANGEL DE JESUS',
|
||||
birthDate: '541299',
|
||||
expiryDate: '300101', // Same year (54) different month/day
|
||||
}),
|
||||
expectedBits: ['\x01', '\x00'],
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`Testing: ${testCase.desc}`);
|
||||
const passportData = testCase.data;
|
||||
const sanctionedCommitment = generateCommitment(
|
||||
secret,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData
|
||||
);
|
||||
tree.insert(BigInt(sanctionedCommitment));
|
||||
|
||||
const testInputs = generateCircuitInputsVCandDisclose(
|
||||
secret,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData,
|
||||
fullScope,
|
||||
Array(90).fill('0'), // selector_dg1
|
||||
selector_older_than,
|
||||
tree,
|
||||
majority,
|
||||
passportNo_smt,
|
||||
nameAndDob_smt,
|
||||
nameAndYob_smt,
|
||||
'1', // selector_ofac
|
||||
forbidden_countries_list,
|
||||
user_identifier
|
||||
);
|
||||
|
||||
w = await circuit.calculateWitness(testInputs);
|
||||
const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[4]']);
|
||||
const reveal_unpacked = formatAndUnpackReveal(revealedData_packed, 'id');
|
||||
const ofac_results = reveal_unpacked.slice(92, 94);
|
||||
|
||||
console.log(`${testCase.desc} - OFAC bits:`, ofac_results);
|
||||
expect(ofac_results).to.deep.equal(
|
||||
testCase.expectedBits,
|
||||
`Failed matching pattern for: ${testCase.desc}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,9 +15,7 @@ import { genAndInitMockPassportData } from '../../../common/src/utils/passports/
|
||||
let circuit: any;
|
||||
|
||||
// Mock passport not added in ofac list
|
||||
const mockIdData = genMockIdDoc(
|
||||
{ 'idType': 'mock_passport' }
|
||||
);
|
||||
const mockIdData = genMockIdDoc({ idType: 'mock_passport' });
|
||||
const passportData = genAndInitMockPassportData(
|
||||
'sha256',
|
||||
'sha256',
|
||||
@@ -40,28 +38,27 @@ const passportDataInOfac = genAndInitMockPassportData(
|
||||
);
|
||||
|
||||
const mockIdDataInOfac = genMockIdDoc({
|
||||
'idType': 'mock_passport',
|
||||
'nationality': 'FRA',
|
||||
'birthDate': '541007',
|
||||
'lastName': 'HENAO MONTOYA',
|
||||
'passportNumber': '98lh90556',
|
||||
'firstName': 'ARCANGEL DE JESUS'
|
||||
})
|
||||
idType: 'mock_passport',
|
||||
nationality: 'FRA',
|
||||
birthDate: '541007',
|
||||
lastName: 'HENAO MONTOYA',
|
||||
passportNumber: '98lh90556',
|
||||
firstName: 'ARCANGEL DE JESUS',
|
||||
});
|
||||
|
||||
// Mock ID Card not in OFAC list
|
||||
const idCardData = genMockIdDoc({
|
||||
'idType': 'mock_id_card'
|
||||
})
|
||||
idType: 'mock_id_card',
|
||||
});
|
||||
|
||||
// Mock ID Card in OFAC list
|
||||
const idCardDataInOfac = genMockIdDoc({
|
||||
'idType': 'mock_id_card',
|
||||
'nationality': 'FRA',
|
||||
'birthDate': '541007',
|
||||
'firstName': 'ARCANGEL DE JESUS',
|
||||
'lastName': 'HENAO MONTOYA'
|
||||
})
|
||||
|
||||
idType: 'mock_id_card',
|
||||
nationality: 'FRA',
|
||||
birthDate: '541007',
|
||||
firstName: 'ARCANGEL DE JESUS',
|
||||
lastName: 'HENAO MONTOYA',
|
||||
});
|
||||
|
||||
// POSSIBLE TESTS (for each of 3 circuits):
|
||||
// 0. Cicuits compiles and loads
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
generateNullifier,
|
||||
} from '../../../common/src/utils/passports/passport';
|
||||
import { poseidon6 } from 'poseidon-lite';
|
||||
import { hashAlgosTypes, PASSPORT_ATTESTATION_ID } from '../../../common/src/constants/constants';
|
||||
import { hashAlgosTypes, ID_CARD_ATTESTATION_ID } from '../../../common/src/constants/constants';
|
||||
import { parseCertificateSimple } from '../../../common/src/utils/certificate_parsing/parseCertificateSimple';
|
||||
import serialized_dsc_tree from '../../../common/pubkeys/serialized_dsc_tree.json';
|
||||
import { genMockIdDoc } from '../../../common/src/utils/passports/genMockIdDoc';
|
||||
@@ -31,300 +31,302 @@ testSuite.forEach(
|
||||
keyLength,
|
||||
saltLength,
|
||||
}) => {
|
||||
describe(`Register - ${dgHashAlgo.toUpperCase()} ${eContentHashAlgo.toUpperCase()} ${hashFunction.toUpperCase()} ${sigAlg.toUpperCase()} ${domainParameter
|
||||
} ${keyLength}${saltLength ? ` Salt:${saltLength}` : ''}`, function () {
|
||||
this.timeout(0);
|
||||
let circuit: any;
|
||||
describe(`Register - ${dgHashAlgo.toUpperCase()} ${eContentHashAlgo.toUpperCase()} ${hashFunction.toUpperCase()} ${sigAlg.toUpperCase()} ${
|
||||
domainParameter
|
||||
} ${keyLength}${saltLength ? ` Salt:${saltLength}` : ''}`, function () {
|
||||
this.timeout(0);
|
||||
let circuit: any;
|
||||
|
||||
const passportData = genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
dgHashAlgo: dgHashAlgo as hashAlgosTypes,
|
||||
eContentHashAlgo: eContentHashAlgo as hashAlgosTypes,
|
||||
signatureType: `${sigAlg}_${hashFunction}_${domainParameter}_${keyLength}${saltLength ? `_${saltLength}` : ''}` as SignatureAlgorithm,
|
||||
})
|
||||
const secret = poseidon6('SECRET'.split('').map((x) => BigInt(x.charCodeAt(0)))).toString();
|
||||
|
||||
const inputs = generateCircuitInputsRegister(
|
||||
secret,
|
||||
passportData,
|
||||
serialized_dsc_tree as string
|
||||
);
|
||||
|
||||
before(async () => {
|
||||
circuit = await wasm_tester(
|
||||
path.join(
|
||||
__dirname,
|
||||
`../../circuits/register_id/instances/${getCircuitNameFromPassportData(passportData, 'register')}.circom`
|
||||
),
|
||||
{
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should compile and load the circuit', async function () {
|
||||
expect(circuit).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('should calculate the witness with correct inputs, and have the right nullifier and commitment', async function () {
|
||||
const w = await circuit.calculateWitness(inputs);
|
||||
await circuit.checkConstraints(w);
|
||||
|
||||
const nullifier_js = generateNullifier(passportData);
|
||||
console.log('\x1b[35m%s\x1b[0m', 'js: nullifier:', nullifier_js);
|
||||
const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier;
|
||||
console.log('\x1b[34m%s\x1b[0m', 'circom: nullifier', nullifier);
|
||||
expect(nullifier).to.be.equal(nullifier_js);
|
||||
|
||||
const commitment_js = generateCommitment(
|
||||
secret.toString(),
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
passportData
|
||||
);
|
||||
console.log('\x1b[35m%s\x1b[0m', 'js: commitment:', commitment_js);
|
||||
const commitment = (await circuit.getOutput(w, ['commitment'])).commitment;
|
||||
console.log('\x1b[34m%s\x1b[0m', 'circom commitment', commitment);
|
||||
expect(commitment).to.be.equal(commitment_js);
|
||||
});
|
||||
|
||||
it('should fail if dsc_pubKey_actual_size is lower than the minimum key length', async () => {
|
||||
try {
|
||||
const dscParsed = parseCertificateSimple(passportData.dsc);
|
||||
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
if (dscParsed.signatureAlgorithm === 'rsa') {
|
||||
tamperedInputs.dsc_pubKey_actual_size = (256 - 1).toString(); // 256 is the minimum key length for RSA
|
||||
} else {
|
||||
// for ecdsa and rsapss, the minimum key length is fixed for each circuit
|
||||
tamperedInputs.dsc_pubKey_actual_size = (
|
||||
Number(tamperedInputs.dsc_pubKey_actual_size) - 1
|
||||
).toString();
|
||||
}
|
||||
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Tests for dsc_pubKey offset and size checks -----
|
||||
it('should fail if dsc_pubKey_offset + dsc_pubKey_actual_size > raw_dsc_actual_length', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.dsc_pubKey_offset = (
|
||||
Number(tamperedInputs.raw_dsc_actual_length) -
|
||||
Number(tamperedInputs.dsc_pubKey_actual_size) +
|
||||
1
|
||||
).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if dsc_pubKey_actual_size is larger than the actual key size in certificate', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.dsc_pubKey_actual_size = (
|
||||
Number(tamperedInputs.dsc_pubKey_actual_size) + 8
|
||||
).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Tests for Merkle tree inclusion -----
|
||||
it('should fail if merkle_root is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.merkle_root = (BigInt(tamperedInputs.merkle_root) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if leaf_depth is tampered', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
// Change leaf_depth to an incorrect value (e.g., add 1)
|
||||
tamperedInputs.leaf_depth = (Number(tamperedInputs.leaf_depth) + 1).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if a value in the merkle path is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.path[0] = (BigInt(tamperedInputs.path[0]) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if a sibling in the merkle proof is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.siblings[0] = (BigInt(tamperedInputs.siblings[0]) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Tests for passport signature and data integrity -----
|
||||
it('should fail to calculate witness with invalid mrz', async function () {
|
||||
try {
|
||||
const ininputs = {
|
||||
...inputs,
|
||||
dg1: Array(95)
|
||||
.fill(0)
|
||||
.map((byte) => BigInt(byte).toString()),
|
||||
};
|
||||
await circuit.calculateWitness(ininputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail to calculate witness with invalid eContent', async function () {
|
||||
try {
|
||||
const ininputs = {
|
||||
...inputs,
|
||||
eContent: inputs.eContent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
|
||||
};
|
||||
await circuit.calculateWitness(ininputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if signed_attr is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.signed_attr = tamperedInputs.signed_attr.map((byte: string) =>
|
||||
((parseInt(byte, 10) + 1) % 256).toString()
|
||||
);
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if signature_passport is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.signature_passport = tamperedInputs.signature_passport.map(
|
||||
(byte: string) => ((parseInt(byte, 10) + 1) % 256).toString()
|
||||
);
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Test for tampering with csca_hash (used in commitment) -----
|
||||
it('should fail if csca_hash is tampered', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.csca_tree_leaf = (BigInt(tamperedInputs.csca_tree_leaf) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Test for tampering with secret (affects commitment and nullifier) -----
|
||||
it('should compute different outputs if secret is changed', async function () {
|
||||
const wValid = await circuit.calculateWitness(inputs);
|
||||
await circuit.checkConstraints(wValid);
|
||||
const nullifierValid = (await circuit.getOutput(wValid, ['nullifier'])).nullifier;
|
||||
const commitmentValid = (await circuit.getOutput(wValid, ['commitment'])).commitment;
|
||||
|
||||
const tamperedInputs = { ...inputs, secret: (BigInt(inputs.secret[0]) + 1n).toString() };
|
||||
const wTampered = await circuit.calculateWitness(tamperedInputs);
|
||||
await circuit.checkConstraints(wTampered);
|
||||
const nullifierTampered = (await circuit.getOutput(wTampered, ['nullifier'])).nullifier;
|
||||
const commitmentTampered = (await circuit.getOutput(wTampered, ['commitment'])).commitment;
|
||||
|
||||
expect(nullifierTampered).to.equal(nullifierValid);
|
||||
expect(commitmentTampered).to.not.be.equal(commitmentValid);
|
||||
});
|
||||
|
||||
if (sigAlg.startsWith('rsa') || sigAlg.startsWith('rsapss')) {
|
||||
it('should fail if RSA public key prefix is invalid', async function () {
|
||||
const invalidPrefixes = [
|
||||
[0x03, 0x82, 0x01, 0x01, 0x00],
|
||||
[0x02, 0x83, 0x01, 0x01, 0x00],
|
||||
[0x02, 0x82, 0x02, 0x02, 0x00],
|
||||
];
|
||||
|
||||
for (const invalidPrefix of invalidPrefixes) {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
for (let i = 0; i < invalidPrefix.length; i++) {
|
||||
tamperedInputs.raw_dsc[
|
||||
Number(tamperedInputs.dsc_pubKey_offset) - invalidPrefix.length + i
|
||||
] = invalidPrefix[i].toString();
|
||||
}
|
||||
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should pass with valid RSA prefix for the key length', async function () {
|
||||
const keyLengthToPrefix = {
|
||||
2048: [0x02, 0x82, 0x01, 0x01, 0x00],
|
||||
3072: [0x02, 0x82, 0x01, 0x81, 0x00],
|
||||
4096: [0x02, 0x82, 0x02, 0x01, 0x00],
|
||||
};
|
||||
|
||||
const expectedPrefix = keyLengthToPrefix[keyLength];
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const prefixByte = parseInt(inputs.raw_dsc[Number(inputs.dsc_pubKey_offset) - 5 + i]);
|
||||
expect(prefixByte).to.equal(
|
||||
expectedPrefix[i],
|
||||
`Prefix byte ${i} mismatch for ${keyLength} bit key`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it('should fail if raw_dsc has a signal that is longer than a byte', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.raw_dsc[0] = (parseInt(tamperedInputs.raw_dsc[0], 10) + 256).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
const passportData = genMockIdDoc({
|
||||
idType: 'mock_id_card',
|
||||
dgHashAlgo: dgHashAlgo as hashAlgosTypes,
|
||||
eContentHashAlgo: eContentHashAlgo as hashAlgosTypes,
|
||||
signatureType:
|
||||
`${sigAlg}_${hashFunction}_${domainParameter}_${keyLength}${saltLength ? `_${saltLength}` : ''}` as SignatureAlgorithm,
|
||||
});
|
||||
const secret = poseidon6('SECRET'.split('').map((x) => BigInt(x.charCodeAt(0)))).toString();
|
||||
|
||||
const inputs = generateCircuitInputsRegister(
|
||||
secret,
|
||||
passportData,
|
||||
serialized_dsc_tree as string
|
||||
);
|
||||
|
||||
before(async () => {
|
||||
circuit = await wasm_tester(
|
||||
path.join(
|
||||
__dirname,
|
||||
`../../circuits/register_id/instances/${getCircuitNameFromPassportData(passportData, 'register')}.circom`
|
||||
),
|
||||
{
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should compile and load the circuit', async function () {
|
||||
expect(circuit).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('should calculate the witness with correct inputs, and have the right nullifier and commitment', async function () {
|
||||
const w = await circuit.calculateWitness(inputs);
|
||||
await circuit.checkConstraints(w);
|
||||
|
||||
const nullifier_js = generateNullifier(passportData);
|
||||
console.log('\x1b[35m%s\x1b[0m', 'js: nullifier:', nullifier_js);
|
||||
const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier;
|
||||
console.log('\x1b[34m%s\x1b[0m', 'circom: nullifier', nullifier);
|
||||
expect(nullifier).to.be.equal(nullifier_js);
|
||||
|
||||
const commitment_js = generateCommitment(
|
||||
secret.toString(),
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
passportData
|
||||
);
|
||||
console.log('\x1b[35m%s\x1b[0m', 'js: commitment:', commitment_js);
|
||||
const commitment = (await circuit.getOutput(w, ['commitment'])).commitment;
|
||||
console.log('\x1b[34m%s\x1b[0m', 'circom commitment', commitment);
|
||||
expect(commitment).to.be.equal(commitment_js);
|
||||
});
|
||||
|
||||
it('should fail if dsc_pubKey_actual_size is lower than the minimum key length', async () => {
|
||||
try {
|
||||
const dscParsed = parseCertificateSimple(passportData.dsc);
|
||||
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
if (dscParsed.signatureAlgorithm === 'rsa') {
|
||||
tamperedInputs.dsc_pubKey_actual_size = (256 - 1).toString(); // 256 is the minimum key length for RSA
|
||||
} else {
|
||||
// for ecdsa and rsapss, the minimum key length is fixed for each circuit
|
||||
tamperedInputs.dsc_pubKey_actual_size = (
|
||||
Number(tamperedInputs.dsc_pubKey_actual_size) - 1
|
||||
).toString();
|
||||
}
|
||||
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Tests for dsc_pubKey offset and size checks -----
|
||||
it('should fail if dsc_pubKey_offset + dsc_pubKey_actual_size > raw_dsc_actual_length', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.dsc_pubKey_offset = (
|
||||
Number(tamperedInputs.raw_dsc_actual_length) -
|
||||
Number(tamperedInputs.dsc_pubKey_actual_size) +
|
||||
1
|
||||
).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if dsc_pubKey_actual_size is larger than the actual key size in certificate', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.dsc_pubKey_actual_size = (
|
||||
Number(tamperedInputs.dsc_pubKey_actual_size) + 8
|
||||
).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Tests for Merkle tree inclusion -----
|
||||
it('should fail if merkle_root is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.merkle_root = (BigInt(tamperedInputs.merkle_root) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if leaf_depth is tampered', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
// Change leaf_depth to an incorrect value (e.g., add 1)
|
||||
tamperedInputs.leaf_depth = (Number(tamperedInputs.leaf_depth) + 1).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if a value in the merkle path is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.path[0] = (BigInt(tamperedInputs.path[0]) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if a sibling in the merkle proof is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.siblings[0] = (BigInt(tamperedInputs.siblings[0]) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Tests for passport signature and data integrity -----
|
||||
it('should fail to calculate witness with invalid mrz', async function () {
|
||||
try {
|
||||
const ininputs = {
|
||||
...inputs,
|
||||
dg1: Array(95)
|
||||
.fill(0)
|
||||
.map((byte) => BigInt(byte).toString()),
|
||||
};
|
||||
await circuit.calculateWitness(ininputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail to calculate witness with invalid eContent', async function () {
|
||||
try {
|
||||
const ininputs = {
|
||||
...inputs,
|
||||
eContent: inputs.eContent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)),
|
||||
};
|
||||
await circuit.calculateWitness(ininputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if signed_attr is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.signed_attr = tamperedInputs.signed_attr.map((byte: string) =>
|
||||
((parseInt(byte, 10) + 1) % 256).toString()
|
||||
);
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail if signature_passport is invalid', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.signature_passport = tamperedInputs.signature_passport.map(
|
||||
(byte: string) => ((parseInt(byte, 10) + 1) % 256).toString()
|
||||
);
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Test for tampering with csca_hash (used in commitment) -----
|
||||
it('should fail if csca_hash is tampered', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.csca_tree_leaf = (BigInt(tamperedInputs.csca_tree_leaf) + 1n).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Test for tampering with secret (affects commitment and nullifier) -----
|
||||
it('should compute different outputs if secret is changed', async function () {
|
||||
const wValid = await circuit.calculateWitness(inputs);
|
||||
await circuit.checkConstraints(wValid);
|
||||
const nullifierValid = (await circuit.getOutput(wValid, ['nullifier'])).nullifier;
|
||||
const commitmentValid = (await circuit.getOutput(wValid, ['commitment'])).commitment;
|
||||
|
||||
const tamperedInputs = { ...inputs, secret: (BigInt(inputs.secret[0]) + 1n).toString() };
|
||||
const wTampered = await circuit.calculateWitness(tamperedInputs);
|
||||
await circuit.checkConstraints(wTampered);
|
||||
const nullifierTampered = (await circuit.getOutput(wTampered, ['nullifier'])).nullifier;
|
||||
const commitmentTampered = (await circuit.getOutput(wTampered, ['commitment'])).commitment;
|
||||
|
||||
expect(nullifierTampered).to.equal(nullifierValid);
|
||||
expect(commitmentTampered).to.not.be.equal(commitmentValid);
|
||||
});
|
||||
|
||||
if (sigAlg.startsWith('rsa') || sigAlg.startsWith('rsapss')) {
|
||||
it('should fail if RSA public key prefix is invalid', async function () {
|
||||
const invalidPrefixes = [
|
||||
[0x03, 0x82, 0x01, 0x01, 0x00],
|
||||
[0x02, 0x83, 0x01, 0x01, 0x00],
|
||||
[0x02, 0x82, 0x02, 0x02, 0x00],
|
||||
];
|
||||
|
||||
for (const invalidPrefix of invalidPrefixes) {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
for (let i = 0; i < invalidPrefix.length; i++) {
|
||||
tamperedInputs.raw_dsc[
|
||||
Number(tamperedInputs.dsc_pubKey_offset) - invalidPrefix.length + i
|
||||
] = invalidPrefix[i].toString();
|
||||
}
|
||||
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should pass with valid RSA prefix for the key length', async function () {
|
||||
const keyLengthToPrefix = {
|
||||
2048: [0x02, 0x82, 0x01, 0x01, 0x00],
|
||||
3072: [0x02, 0x82, 0x01, 0x81, 0x00],
|
||||
4096: [0x02, 0x82, 0x02, 0x01, 0x00],
|
||||
};
|
||||
|
||||
const expectedPrefix = keyLengthToPrefix[keyLength];
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const prefixByte = parseInt(inputs.raw_dsc[Number(inputs.dsc_pubKey_offset) - 5 + i]);
|
||||
expect(prefixByte).to.equal(
|
||||
expectedPrefix[i],
|
||||
`Prefix byte ${i} mismatch for ${keyLength} bit key`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it('should fail if raw_dsc has a signal that is longer than a byte', async function () {
|
||||
try {
|
||||
const tamperedInputs = JSON.parse(JSON.stringify(inputs));
|
||||
tamperedInputs.raw_dsc[0] = (parseInt(tamperedInputs.raw_dsc[0], 10) + 256).toString();
|
||||
await circuit.calculateWitness(tamperedInputs);
|
||||
expect.fail('Expected an error but none was thrown.');
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ export const sigAlgs: TestCase[] = [
|
||||
hashFunction: 'sha256',
|
||||
sigAlg: 'rsa',
|
||||
domainParameter: '65537',
|
||||
keyLength: '2048'
|
||||
keyLength: '2048',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -38,57 +38,17 @@ export type document_type = 'passport' | 'id_card';
|
||||
|
||||
/**
|
||||
* Maximum number of countries in the forbidden countries list.
|
||||
*
|
||||
*
|
||||
* IMPORTANT: This value must match in both backend and frontend SDK.
|
||||
* Any mismatch will result in an INVALID_FORBIDDEN_COUNTRIES error.
|
||||
*/
|
||||
export const MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH = 40;
|
||||
|
||||
export const DEPLOYED_CIRCUITS_REGISTER = [
|
||||
'register_sha1_sha1_sha1_rsa_65537_4096',
|
||||
'register_sha1_sha256_sha256_rsa_65537_4096',
|
||||
'register_sha224_sha224_sha224_ecdsa_brainpoolP224r1',
|
||||
'register_sha256_sha224_sha224_ecdsa_secp224r1',
|
||||
'register_sha256_sha256_sha256_ecdsa_brainpoolP256r1',
|
||||
'register_sha256_sha256_sha256_ecdsa_brainpoolP384r1',
|
||||
'register_sha256_sha256_sha256_ecdsa_secp256r1',
|
||||
'register_sha256_sha256_sha256_ecdsa_secp384r1',
|
||||
'register_sha256_sha256_sha256_rsa_3_4096',
|
||||
'register_sha256_sha256_sha256_rsa_65537_4096',
|
||||
'register_sha256_sha256_sha256_rsapss_3_32_2048',
|
||||
'register_sha256_sha256_sha256_rsapss_65537_32_2048',
|
||||
'register_sha256_sha256_sha256_rsapss_65537_32_3072',
|
||||
'register_sha384_sha384_sha384_ecdsa_brainpoolP384r1',
|
||||
'register_sha384_sha384_sha384_ecdsa_brainpoolP512r1',
|
||||
'register_sha384_sha384_sha384_ecdsa_secp384r1',
|
||||
'register_sha384_sha384_sha384_rsapss_65537_48_2048',
|
||||
'register_sha1_sha1_sha1_ecdsa_brainpoolP224r1',
|
||||
'register_sha512_sha512_sha512_ecdsa_brainpoolP512r1',
|
||||
'register_sha512_sha512_sha512_rsa_65537_4096',
|
||||
'register_sha512_sha512_sha512_rsapss_65537_64_2048',
|
||||
]
|
||||
// Note: Circuit lists are now managed through RegisterVerifierId and DscVerifierId enums below
|
||||
// instead of separate arrays for better type safety and maintainability
|
||||
|
||||
export const OFAC_TREE_LEVELS = 64;
|
||||
|
||||
export const DEPLOYED_CIRCUITS_DSC = [
|
||||
'dsc_sha1_ecdsa_brainpoolP256r1',
|
||||
'dsc_sha1_rsa_65537_4096',
|
||||
'dsc_sha256_ecdsa_brainpoolP256r1',
|
||||
'dsc_sha256_ecdsa_brainpoolP384r1',
|
||||
'dsc_sha256_ecdsa_secp256r1',
|
||||
'dsc_sha256_ecdsa_secp384r1',
|
||||
'dsc_sha256_rsa_65537_4096',
|
||||
'dsc_sha256_rsapss_3_32_3072',
|
||||
'dsc_sha256_rsapss_65537_32_3072',
|
||||
'dsc_sha256_rsapss_65537_32_4096',
|
||||
'dsc_sha384_ecdsa_brainpoolP384r1',
|
||||
'dsc_sha384_ecdsa_brainpoolP512r1',
|
||||
'dsc_sha384_ecdsa_secp384r1',
|
||||
'dsc_sha512_ecdsa_brainpoolP512r1',
|
||||
'dsc_sha512_rsa_65537_4096',
|
||||
'dsc_sha512_rsapss_65537_64_4096',
|
||||
]
|
||||
|
||||
export const MAX_PADDED_ECONTENT_LEN: Partial<Record<(typeof hashAlgos)[number], number>> = {
|
||||
sha1: 384,
|
||||
sha224: 512,
|
||||
|
||||
@@ -42,7 +42,7 @@ function genDG1IdCard(idDocInput: IdDocInput) {
|
||||
const nationality = formatDG1Attribute(nationality_index, idDocInput.nationality);
|
||||
const optional_data_2 = formatDG1Attribute(optional_data_2_index, '');
|
||||
const overall_check_digit = formatDG1Attribute(overall_check_digit_index, '1');
|
||||
const name = formatDG1Attribute(name_index, formatName(idDocInput.firstName, idDocInput.lastName));
|
||||
const name = formatDG1Attribute(name_index, formatName(idDocInput.firstName, idDocInput.lastName, name_index[1] - name_index[0] + 1));
|
||||
|
||||
const dg1 = `${doc_type}${issuing_state}${document_number}${document_number_check_digit}${optional_data}${date_of_birth}${date_of_birth_check_digit}${sex}${expiration_date}${expiration_date_check_digit}${nationality}${optional_data_2}${overall_check_digit}${name}`;
|
||||
if (dg1.length !== 90) {
|
||||
@@ -69,7 +69,7 @@ function genDG1Passport(idDocInput: IdDocInput) {
|
||||
|
||||
const doc_type = formatDG1Attribute(doc_type_index, 'P');
|
||||
const issuing_state = formatDG1Attribute(issuing_state_index, idDocInput.nationality);
|
||||
const name = formatDG1Attribute(name_index, formatName(idDocInput.firstName, idDocInput.lastName));
|
||||
const name = formatDG1Attribute(name_index, formatName(idDocInput.firstName, idDocInput.lastName, name_index[1] - name_index[0] + 1));
|
||||
const document_number = formatDG1Attribute(document_number_index, idDocInput.passportNumber);
|
||||
const document_number_check_digit = formatDG1Attribute(document_number_check_digit_index, '4');
|
||||
const nationality = formatDG1Attribute(nationality_index, idDocInput.nationality);
|
||||
|
||||
@@ -130,11 +130,25 @@ export function formatDg2Hash(dg2Hash: number[]) {
|
||||
export function formatDG1Attribute(index: number[], value: string) {
|
||||
const max_length = index[1] - index[0] + 1;
|
||||
if (value.length > max_length) {
|
||||
throw new Error(`Value is too long for index ${index[0]}-${index[1]}`);
|
||||
throw new Error(`Value is too long for index ${index[0]}-${index[1]} value: ${value} value.length: ${value.length} maxLength: ${max_length}`);
|
||||
}
|
||||
return value.padEnd(max_length, '<');
|
||||
}
|
||||
|
||||
export function formatName(firstName: string, lastName: string) {
|
||||
return `${lastName.toUpperCase()}<<${firstName.toUpperCase()}`;
|
||||
export function formatName(firstName: string, lastName: string, targetLength: number) {
|
||||
// Split names by spaces and join parts with '<'
|
||||
const formattedLastName = lastName.toUpperCase().split(' ').join('<');
|
||||
const formattedFirstName = firstName.toUpperCase().split(' ').join('<');
|
||||
|
||||
// Combine with '<<' separator
|
||||
let result = `${formattedLastName}<<${formattedFirstName}`;
|
||||
|
||||
// Pad with '<' or truncate to target length
|
||||
if (result.length < targetLength) {
|
||||
result = result.padEnd(targetLength, '<');
|
||||
} else if (result.length > targetLength) {
|
||||
result = result.substring(0, targetLength);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
NETWORK=
|
||||
|
||||
PRIVATE_KEY='0x123'
|
||||
CELO_KEY=
|
||||
|
||||
MAINNET_RPC_URL=
|
||||
SEPOLIA_RPC_URL=https://rpc.sepolia.org
|
||||
@@ -6,4 +9,5 @@ CELO_RPC_URL=
|
||||
CELO_ALFAJORES_RPC_URL=
|
||||
CELO_BAKLAVA_RPC_URL=
|
||||
|
||||
ETHERSCAN_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
CELOSCAN_API_KEY=
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
/*
|
||||
Copyright 2021 0KIMS association.
|
||||
|
||||
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
|
||||
|
||||
snarkJS is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
snarkJS is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
contract Verifier_register_id_sha256_sha256_sha256_rsa_65537_4096 {
|
||||
// Scalar field size
|
||||
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
// Base field size
|
||||
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// Verification Key data
|
||||
uint256 constant alphax = 20491192805390485299153009773594534940189261866228447918068658471970481763042;
|
||||
uint256 constant alphay = 9383485363053290200918347156157836566562967994039712273449902621266178545958;
|
||||
uint256 constant betax1 = 4252822878758300859123897981450591353533073413197771768651442665752259397132;
|
||||
uint256 constant betax2 = 6375614351688725206403948262868962793625744043794305715222011528459656738731;
|
||||
uint256 constant betay1 = 21847035105528745403288232691147584728191162732299865338377159692350059136679;
|
||||
uint256 constant betay2 = 10505242626370262277552901082094356697409835680220590971873171140371331206856;
|
||||
uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
|
||||
uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
|
||||
uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
|
||||
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
|
||||
uint256 constant deltax1 = 4792639757706688844115711424239360806333190024413309473231737437875226477670;
|
||||
uint256 constant deltax2 = 17405709437999930743609060641158524978458283912947831892566166150949870424294;
|
||||
uint256 constant deltay1 = 17093903636529404715977441649383548797208493053670591927268936136905628890632;
|
||||
uint256 constant deltay2 = 4972965029582644081071578284460620299080512423640837516215418184958287485742;
|
||||
|
||||
|
||||
uint256 constant IC0x = 10355093401382030671225395655869217898600304666279529568537338332467402365304;
|
||||
uint256 constant IC0y = 4965486376320532678254373789150183805614443765077280090063627634214290673841;
|
||||
|
||||
uint256 constant IC1x = 15499415295419376168702866885907180620949913073936284071089537160198730049399;
|
||||
uint256 constant IC1y = 9281988302763410034273237781396743554700695834575740098855226007996985888530;
|
||||
|
||||
uint256 constant IC2x = 14098031970434182418452511377672297745474345532972352145312731453903292311930;
|
||||
uint256 constant IC2y = 5496357494362502413877905425401698232434560336945612609023708765434745421701;
|
||||
|
||||
uint256 constant IC3x = 6540495825150439480926083011463382216384205445522489211977434270583326380648;
|
||||
uint256 constant IC3y = 4164688383627561794431395614711252825194458846441353030452544157419373614848;
|
||||
|
||||
|
||||
// Memory data
|
||||
uint16 constant pVk = 0;
|
||||
uint16 constant pPairing = 128;
|
||||
|
||||
uint16 constant pLastMem = 896;
|
||||
|
||||
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[3] calldata _pubSignals) public view returns (bool) {
|
||||
assembly {
|
||||
function checkField(v) {
|
||||
if iszero(lt(v, r)) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
// G1 function to multiply a G1 value(x,y) to value in an address
|
||||
function g1_mulAccC(pR, x, y, s) {
|
||||
let success
|
||||
let mIn := mload(0x40)
|
||||
mstore(mIn, x)
|
||||
mstore(add(mIn, 32), y)
|
||||
mstore(add(mIn, 64), s)
|
||||
|
||||
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
|
||||
|
||||
if iszero(success) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
|
||||
mstore(add(mIn, 64), mload(pR))
|
||||
mstore(add(mIn, 96), mload(add(pR, 32)))
|
||||
|
||||
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
|
||||
|
||||
if iszero(success) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
|
||||
let _pPairing := add(pMem, pPairing)
|
||||
let _pVk := add(pMem, pVk)
|
||||
|
||||
mstore(_pVk, IC0x)
|
||||
mstore(add(_pVk, 32), IC0y)
|
||||
|
||||
// Compute the linear combination vk_x
|
||||
|
||||
g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0)))
|
||||
|
||||
g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32)))
|
||||
|
||||
g1_mulAccC(_pVk, IC3x, IC3y, calldataload(add(pubSignals, 64)))
|
||||
|
||||
|
||||
// -A
|
||||
mstore(_pPairing, calldataload(pA))
|
||||
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
|
||||
|
||||
// B
|
||||
mstore(add(_pPairing, 64), calldataload(pB))
|
||||
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
|
||||
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
|
||||
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
|
||||
|
||||
// alpha1
|
||||
mstore(add(_pPairing, 192), alphax)
|
||||
mstore(add(_pPairing, 224), alphay)
|
||||
|
||||
// beta2
|
||||
mstore(add(_pPairing, 256), betax1)
|
||||
mstore(add(_pPairing, 288), betax2)
|
||||
mstore(add(_pPairing, 320), betay1)
|
||||
mstore(add(_pPairing, 352), betay2)
|
||||
|
||||
// vk_x
|
||||
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
|
||||
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
|
||||
|
||||
|
||||
// gamma2
|
||||
mstore(add(_pPairing, 448), gammax1)
|
||||
mstore(add(_pPairing, 480), gammax2)
|
||||
mstore(add(_pPairing, 512), gammay1)
|
||||
mstore(add(_pPairing, 544), gammay2)
|
||||
|
||||
// C
|
||||
mstore(add(_pPairing, 576), calldataload(pC))
|
||||
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
|
||||
|
||||
// delta2
|
||||
mstore(add(_pPairing, 640), deltax1)
|
||||
mstore(add(_pPairing, 672), deltax2)
|
||||
mstore(add(_pPairing, 704), deltay1)
|
||||
mstore(add(_pPairing, 736), deltay2)
|
||||
|
||||
|
||||
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
|
||||
|
||||
isOk := and(success, mload(_pPairing))
|
||||
}
|
||||
|
||||
let pMem := mload(0x40)
|
||||
mstore(0x40, add(pMem, pLastMem))
|
||||
|
||||
// Validate that all evaluations ∈ F
|
||||
|
||||
checkField(calldataload(add(_pubSignals, 0)))
|
||||
|
||||
checkField(calldataload(add(_pubSignals, 32)))
|
||||
|
||||
checkField(calldataload(add(_pubSignals, 64)))
|
||||
|
||||
|
||||
// Validate all evaluations
|
||||
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
|
||||
|
||||
mstore(0, isValid)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { DEPLOYED_CIRCUITS_REGISTER, DEPLOYED_CIRCUITS_DSC } from "../../../common/src/constants/constants";
|
||||
|
||||
export default buildModule("DeployAllVerifiers", (m) => {
|
||||
const deployedContracts: Record<string, any> = {};
|
||||
|
||||
deployedContracts.vcAndDiscloseVerifier = m.contract("Verifier_vc_and_disclose");
|
||||
|
||||
DEPLOYED_CIRCUITS_REGISTER.forEach((circuit) => {
|
||||
const contractName = `Verifier_${circuit}`;
|
||||
deployedContracts[circuit] = m.contract(contractName);
|
||||
});
|
||||
|
||||
DEPLOYED_CIRCUITS_DSC.forEach((circuit) => {
|
||||
const contractName = `Verifier_${circuit}`;
|
||||
deployedContracts[circuit] = m.contract(contractName);
|
||||
});
|
||||
|
||||
return deployedContracts;
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { artifacts, ethers } from "hardhat";
|
||||
import hre from "hardhat";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
function getHubInitializeData() {
|
||||
const hubArtifact = artifacts.readArtifactSync("IdentityVerificationHubImplV1");
|
||||
return new ethers.Interface(hubArtifact.abi);
|
||||
}
|
||||
|
||||
export default buildModule("DeployHub", (m) => {
|
||||
const networkName = hre.network.config.chainId;
|
||||
|
||||
const deployedAddressesPath = path.join(__dirname, `../deployments/chain-${networkName}/deployed_addresses.json`);
|
||||
const deployedAddresses = JSON.parse(fs.readFileSync(deployedAddressesPath, "utf8"));
|
||||
|
||||
const registryAddress = deployedAddresses["DeployRegistryModule#IdentityRegistry"];
|
||||
const vcAndDiscloseVerifierAddress = deployedAddresses["DeployAllVerifiers#Verifier_vc_and_disclose"];
|
||||
|
||||
const identityVerificationHubImpl = m.contract("IdentityVerificationHubImplV1");
|
||||
|
||||
const hubInterface = getHubInitializeData();
|
||||
const initializeData = hubInterface.encodeFunctionData("initialize", [
|
||||
registryAddress,
|
||||
vcAndDiscloseVerifierAddress,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
]);
|
||||
|
||||
const hub = m.contract("IdentityVerificationHub", [identityVerificationHubImpl, initializeData]);
|
||||
|
||||
return {
|
||||
hub,
|
||||
identityVerificationHubImpl,
|
||||
};
|
||||
});
|
||||
37
contracts/ignition/modules/hub/deployHub.ts
Normal file
37
contracts/ignition/modules/hub/deployHub.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { artifacts, ethers } from "hardhat";
|
||||
import hre from "hardhat";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
function getHubInitializeData() {
|
||||
const hubArtifact = artifacts.readArtifactSync("IdentityVerificationHubImplV1");
|
||||
return new ethers.Interface(hubArtifact.abi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy Identity Verification Hub V1
|
||||
* This module deploys the V1 implementation of the Identity Verification Hub
|
||||
*/
|
||||
export default buildModule("DeployHubV1", (m) => {
|
||||
// Deploy V1 implementation
|
||||
const identityVerificationHubImplV1 = m.contract("IdentityVerificationHubImplV1");
|
||||
|
||||
const hubInterface = getHubInitializeData();
|
||||
const initializeData = hubInterface.encodeFunctionData("initialize", [
|
||||
"",
|
||||
"",
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
]);
|
||||
|
||||
// Deploy proxy with V1 implementation
|
||||
const hubV1 = m.contract("IdentityVerificationHub", [identityVerificationHubImplV1, initializeData]);
|
||||
|
||||
return {
|
||||
hubV1,
|
||||
identityVerificationHubImplV1,
|
||||
};
|
||||
});
|
||||
32
contracts/ignition/modules/hub/deployHubV2.ts
Normal file
32
contracts/ignition/modules/hub/deployHubV2.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { artifacts, ethers } from "hardhat";
|
||||
import hre from "hardhat";
|
||||
|
||||
function getHubImplV2InitializeData() {
|
||||
const hubArtifact = artifacts.readArtifactSync("IdentityVerificationHubImplV2");
|
||||
return new ethers.Interface(hubArtifact.abi);
|
||||
}
|
||||
|
||||
export default buildModule("DeployHubV2", (m) => {
|
||||
const identityVerificationHubImplV2 = m.contract("IdentityVerificationHubImplV2");
|
||||
|
||||
const hubInterface = getHubImplV2InitializeData();
|
||||
|
||||
// Initialize with empty values as per instructions
|
||||
const initializeData = hubInterface.encodeFunctionData("initialize", [
|
||||
[], // attestationIds
|
||||
[], // registryAddresses
|
||||
[], // vcAndDiscloseCircuitVerifierAddresses
|
||||
[], // registerCircuitVerifierIds
|
||||
[], // registerCircuitVerifierAddresses
|
||||
[], // dscCircuitVerifierIds
|
||||
[], // dscCircuitVerifierAddresses
|
||||
]);
|
||||
|
||||
const hub = m.contract("IdentityVerificationHub", [identityVerificationHubImplV2, initializeData]);
|
||||
|
||||
return {
|
||||
hub,
|
||||
identityVerificationHubImplV2,
|
||||
};
|
||||
});
|
||||
30
contracts/ignition/modules/registry/deployIdCardRegistry.ts
Normal file
30
contracts/ignition/modules/registry/deployIdCardRegistry.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { artifacts } from "hardhat";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export default buildModule("DeployIdCardRegistryModule", (m) => {
|
||||
const poseidonT3 = m.library("PoseidonT3");
|
||||
|
||||
const identityRegistryIdCardImpl = m.contract("IdentityRegistryIdCardImplV1", [], {
|
||||
libraries: { PoseidonT3: poseidonT3 },
|
||||
});
|
||||
|
||||
const registryInterface = getRegistryInitializeData();
|
||||
const registryInitData = registryInterface.encodeFunctionData("initialize", [
|
||||
"0x0000000000000000000000000000000000000000",
|
||||
]);
|
||||
|
||||
const idCardRegistry = m.contract("IdentityRegistryIdCard", [identityRegistryIdCardImpl, registryInitData]);
|
||||
|
||||
return {
|
||||
poseidonT3,
|
||||
identityRegistryIdCardImpl,
|
||||
idCardRegistry,
|
||||
};
|
||||
});
|
||||
|
||||
function getRegistryInitializeData() {
|
||||
const registryArtifact = artifacts.readArtifactSync("IdentityRegistryIdCardImplV1");
|
||||
const registryInterface = new ethers.Interface(registryArtifact.abi);
|
||||
return registryInterface;
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import hre from "hardhat";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
export default buildModule("DeployPCR0", (m) => {
|
||||
const networkName = hre.network.config.chainId;
|
||||
|
||||
// Deploy the PCR0Manager contract (implementation from PCR0.sol)
|
||||
const pcr0Manager = m.contract("PCR0Manager");
|
||||
|
||||
return {
|
||||
72
contracts/ignition/modules/verifiers/deployAllVerifiers.ts
Normal file
72
contracts/ignition/modules/verifiers/deployAllVerifiers.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { RegisterVerifierId, DscVerifierId } from "../../../../common/src/constants/constants";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* Get enum keys (circuit names) excluding numeric values
|
||||
*/
|
||||
function getEnumKeys<T extends Record<string, string | number>>(enumObject: T): string[] {
|
||||
return Object.keys(enumObject).filter(key => isNaN(Number(key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a contract file exists
|
||||
*/
|
||||
function contractExists(contractName: string): boolean {
|
||||
const contractsDir = path.join(__dirname, "../../../contracts");
|
||||
const possiblePaths = [
|
||||
path.join(contractsDir, "verifiers/register", `${contractName}.sol`),
|
||||
path.join(contractsDir, "verifiers/dsc", `${contractName}.sol`),
|
||||
path.join(contractsDir, "verifiers/disclose", `${contractName}.sol`),
|
||||
path.join(contractsDir, "verifiers", `${contractName}.sol`),
|
||||
];
|
||||
|
||||
return possiblePaths.some(filePath => fs.existsSync(filePath));
|
||||
}
|
||||
|
||||
export default buildModule("DeployAllVerifiers", (m) => {
|
||||
const deployedContracts: Record<string, any> = {};
|
||||
|
||||
// Deploy VC and Disclose verifier
|
||||
console.log("Deploying VC and Disclose verifier...");
|
||||
deployedContracts.vcAndDiscloseVerifier = m.contract("Verifier_vc_and_disclose");
|
||||
|
||||
// Deploy Register verifiers using RegisterVerifierId enum
|
||||
console.log("Deploying Register verifiers...");
|
||||
const registerCircuits = getEnumKeys(RegisterVerifierId);
|
||||
let successfulRegisterDeployments = 0;
|
||||
registerCircuits.forEach((circuitName) => {
|
||||
const contractName = `Verifier_${circuitName}`;
|
||||
if (contractExists(contractName)) {
|
||||
console.log(` - Deploying ${contractName}`);
|
||||
deployedContracts[circuitName] = m.contract(contractName);
|
||||
successfulRegisterDeployments++;
|
||||
} else {
|
||||
console.warn(` - Warning: Contract ${contractName} not found, skipping...`);
|
||||
}
|
||||
});
|
||||
|
||||
// Deploy DSC verifiers using DscVerifierId enum
|
||||
console.log("Deploying DSC verifiers...");
|
||||
const dscCircuits = getEnumKeys(DscVerifierId);
|
||||
let successfulDscDeployments = 0;
|
||||
dscCircuits.forEach((circuitName) => {
|
||||
const contractName = `Verifier_${circuitName}`;
|
||||
if (contractExists(contractName)) {
|
||||
console.log(` - Deploying ${contractName}`);
|
||||
deployedContracts[circuitName] = m.contract(contractName);
|
||||
successfulDscDeployments++;
|
||||
} else {
|
||||
console.warn(` - Warning: Contract ${contractName} not found, skipping...`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Total verifiers deployment summary:`);
|
||||
console.log(` - VC and Disclose: 1`);
|
||||
console.log(` - Register: ${successfulRegisterDeployments}/${registerCircuits.length} (${registerCircuits.length - successfulRegisterDeployments} skipped)`);
|
||||
console.log(` - DSC: ${successfulDscDeployments}/${dscCircuits.length} (${dscCircuits.length - successfulDscDeployments} skipped)`);
|
||||
console.log(` - Total successful deployments: ${1 + successfulRegisterDeployments + successfulDscDeployments}`);
|
||||
|
||||
return deployedContracts;
|
||||
});
|
||||
14
contracts/ignition/modules/verifiers/deployIdCardVerifier.ts
Normal file
14
contracts/ignition/modules/verifiers/deployIdCardVerifier.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
|
||||
/**
|
||||
* This module deploys the ID Card Verifier contract specifically for register_id
|
||||
* with SHA256+SHA256+SHA256+RSA verifier
|
||||
*/
|
||||
export default buildModule("DeployIdCardVerifier", (m) => {
|
||||
// Deploy the ID Card Verifier contract
|
||||
const idCardVerifier = m.contract("Verifier_register_id_sha256_sha256_sha256_rsa_65537_4096");
|
||||
|
||||
return {
|
||||
idCardVerifier,
|
||||
};
|
||||
});
|
||||
16795
contracts/package-lock.json
generated
16795
contracts/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,57 +15,44 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npx hardhat clean && npx hardhat compile",
|
||||
"deploy:all:localhost": "npm run deploy:verifiers:localhost && npm run deploy:registry:localhost && npm run deploy:hub:localhost",
|
||||
"deploy:hub:localhost": "npx hardhat ignition deploy ignition/modules/deployHub.ts --network localhost --verify",
|
||||
"deploy:registry:localhost": "npx hardhat ignition deploy ignition/modules/deployRegistry.ts --network localhost --verify",
|
||||
"deploy:verifiers:localhost": "npx hardhat ignition deploy ignition/modules/deployVerifiers.ts --network localhost --verify",
|
||||
"deploy:verifyall:localhost": "npx hardhat ignition deploy ignition/modules/deployVerifyAll.ts --network localhost --verify",
|
||||
"update:cscaroot:localhost": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryCscaRoot.ts --network localhost",
|
||||
"update:hub:localhost": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryHub.ts --network localhost",
|
||||
"update:ofacroot:localhost": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryOfacRoot.ts --network localhost",
|
||||
"deploy:all:sepolia": "npm run deploy:verifiers:sepolia && npm run deploy:registry:sepolia && npm run deploy:hub:sepolia",
|
||||
"deploy:hub:sepolia": "npx hardhat ignition deploy ignition/modules/deployHub.ts --network sepolia --verify",
|
||||
"deploy:registry:sepolia": "npx hardhat ignition deploy ignition/modules/deployRegistry.ts --network sepolia --verify",
|
||||
"deploy:verifiers:sepolia": "npx hardhat ignition deploy ignition/modules/deployVerifiers.ts --network sepolia --verify",
|
||||
"deploy:verifyall:sepolia": "npx hardhat ignition deploy ignition/modules/deployVerifyAll.ts --network sepolia --verify",
|
||||
"deploy:all:celo": "npm run deploy:verifiers:celo && npm run deploy:registry:celo && npm run deploy:hub:celo",
|
||||
"deploy:hub:celo": "npx hardhat ignition deploy ignition/modules/deployHub.ts --network celo --verify",
|
||||
"deploy:registry:celo": "npx hardhat ignition deploy ignition/modules/deployRegistry.ts --network celo --verify",
|
||||
"deploy:verifiers:celo": "npx hardhat ignition deploy ignition/modules/deployVerifiers.ts --network celo --verify",
|
||||
"deploy:allverifiers:celo": "npx hardhat ignition deploy ignition/modules/deployAllVerifiers.ts --network celo --verify",
|
||||
"deploy:verifyall:celo": "npx hardhat ignition deploy ignition/modules/deployVerifyAll.ts --network celo --verify",
|
||||
"upgrade:hub:celo": "npx hardhat ignition deploy ignition/modules/deployNewHubAndUpgrade.ts --network celo --verify",
|
||||
"upgrade:registry:celo": "npx hardhat ignition deploy ignition/modules/deployNewRegistryAndUpgrade.ts --network celo --verify",
|
||||
"deploy_all": "npx hardhat ignition deploy ignition/modules/Deploy_All.ts --network optimism --verify",
|
||||
"deploy:all": "npm run deploy:verifiers:all && npm run deploy:registry && npm run deploy:registry:idcard && npm run deploy:hub:v2",
|
||||
"deploy:hub": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/hub/deployHub.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"deploy:hub:v2": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/hub/deployHubV2.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"deploy:registry": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/registry/deployRegistry.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"deploy:registry:idcard": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/registry/deployIdCardRegistry.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"deploy:verifiers": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/verifiers/deployVerifiers.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"deploy:verifiers:all": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/verifiers/deployAllVerifiers.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"deploy:pcr0": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/utils/deployPCR0.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"set:registry": "npx dotenv-cli -- bash -c 'NETWORK=${NETWORK:-staging} npx ts-node scripts/setRegistry.ts'",
|
||||
"set:registry:idcard": "npx dotenv-cli -- bash -c 'NETWORK=${NETWORK:-staging} npx ts-node scripts/setRegistryId.ts'",
|
||||
"set:hub:v2": "npx dotenv-cli -- bash -c 'NETWORK=${NETWORK:-staging} npx ts-node scripts/setHubV2.ts'",
|
||||
"update:cscaroot": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/scripts/updateRegistryCscaRoot.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"update:hub": "npx dotenv-cli -- bash -c 'NETWORK=${NETWORK:-staging} npx ts-node scripts/setRegistry.ts'",
|
||||
"update:ofacroot": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/scripts/updateRegistryOfacRoot.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"update:pcr0": "npx dotenv-cli -- bash -c 'PCR0_ACTION=${PCR0_ACTION:-add} PCR0_KEY=${PCR0_KEY} npx hardhat ignition deploy ignition/modules/scripts/updatePCR0.ts --network ${NETWORK:-localhost} --reset'",
|
||||
"upgrade:hub": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/upgrade/deployNewHubAndUpgrade.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"upgrade:registry": "npx dotenv-cli -- bash -c 'npx hardhat ignition deploy ignition/modules/upgrade/deployNewRegistryAndUpgrade.ts --network ${NETWORK:-localhost} ${VERIFY:+--verify}'",
|
||||
"publish": "npm publish --access public",
|
||||
"export-prod": "bash ./scripts/prod.sh",
|
||||
"test": "npx hardhat test",
|
||||
"test:airdrop:local": "TEST_ENV=local npx hardhat test test/example/airdrop.test.ts",
|
||||
"test:attribute:local": "TEST_ENV=local npx hardhat test test/unit/CircuitAttributeHandler.test.ts",
|
||||
"test:airdrop": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/example/airdrop.test.ts'",
|
||||
"test:attribute": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/unit/CircuitAttributeHandler.test.ts'",
|
||||
"test:coverage": "npx hardhat coverage",
|
||||
"test:coverage:local": "TEST_ENV=local npx hardhat coverage",
|
||||
"test:disclose:local": "TEST_ENV=local npx hardhat test test/integration/vcAndDisclose.test.ts",
|
||||
"test:endtoend:local": "TEST_ENV=local npx hardhat test test/Integration/endToEnd.test.ts",
|
||||
"test:verifyall:local": "TEST_ENV=local npx hardhat test test/integration/verifyAll.test.ts",
|
||||
"test:sdkcore:local": "TEST_ENV=local npx hardhat test test/sdk/sdkCore.test.ts --network localhost",
|
||||
"test:example:local": "TEST_ENV=local npx hardhat test test/example/*",
|
||||
"test:formatter:local": "TEST_ENV=local npx hardhat test test/unit/formatter.test.ts",
|
||||
"test:hub:local": "TEST_ENV=local npx hardhat test test/unit/IdentityVerificationHub.test.ts",
|
||||
"test:integration:local": "TEST_ENV=local npx hardhat test test/integration/*",
|
||||
"test:disclose": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/integration/vcAndDisclose.test.ts'",
|
||||
"test:endtoend": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/Integration/endToEnd.test.ts'",
|
||||
"test:verifyall": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/integration/verifyAll.test.ts'",
|
||||
"test:sdkcore": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/sdk/sdkCore.test.ts --network localhost'",
|
||||
"test:example": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/example/*'",
|
||||
"test:formatter": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/unit/formatter.test.ts'",
|
||||
"test:hub": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/unit/IdentityVerificationHub.test.ts'",
|
||||
"test:integration": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/integration/*'",
|
||||
"test:local": "TEST_ENV=local npx hardhat test",
|
||||
"test:register:local": "TEST_ENV=local npx hardhat test test/integration/commitmentRegistration.test.ts",
|
||||
"test:registry:local": "TEST_ENV=local npx hardhat test test/unit/IdentityRegistry.test.ts",
|
||||
"test:pcr:local": "TEST_ENV=local npx hardhat test test/unit/PCR0Manager.test.ts",
|
||||
"test:unit:local": "TEST_ENV=local npx hardhat test test/unit/*",
|
||||
"test:register": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/integration/commitmentRegistration.test.ts'",
|
||||
"test:registry": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/unit/IdentityRegistry.test.ts'",
|
||||
"test:pcr": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/unit/PCR0Manager.test.ts'",
|
||||
"test:unit": "npx dotenv-cli -- bash -c 'TEST_ENV=${TEST_ENV:-local} npx hardhat test test/unit/*'",
|
||||
"test:view": "npx hardhat test test/view.ts",
|
||||
"update:cscaroot:sepolia": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryCscaRoot.ts --network sepolia",
|
||||
"update:hub:sepolia": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryHub.ts --network sepolia",
|
||||
"update:ofacroot:sepolia": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryOfacRoot.ts --network sepolia",
|
||||
"update:cscaroot:celo": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryCscaRoot.ts --network celo --verify",
|
||||
"update:hub:celo": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryHub.ts --network celo --verify",
|
||||
"update:ofacroot:celo": "npx hardhat ignition deploy ignition/modules/scripts/updateRegistryOfacRoot.ts --network celo --verify",
|
||||
"deploy:pcr0:celo": "npx hardhat ignition deploy ignition/modules/deployPCR0.ts --network celo --verify",
|
||||
"update:pcr0:celo": "PCR0_ACTION=add PCR0_KEY=123 npx hardhat ignition deploy ignition/modules/scripts/updatePCR0.ts --network celo --reset",
|
||||
"prettier:write": "prettier --write '**/*.{json,md,yml,sol,ts}'",
|
||||
"prettier:check": "prettier --list-different '**/*.{json,md,yml,sol,ts}'"
|
||||
},
|
||||
@@ -107,6 +94,7 @@
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/snarkjs": "^0.7.7",
|
||||
"chai": "^4.4.1",
|
||||
"dotenv-cli": "^7.4.2",
|
||||
"ethers": "^6.12.1",
|
||||
"hardhat": "^2.22.6",
|
||||
"hardhat-gas-reporter": "^1.0.10",
|
||||
|
||||
232
contracts/scripts/setHubV2.ts
Normal file
232
contracts/scripts/setHubV2.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import { ethers } from "ethers";
|
||||
import * as dotenv from "dotenv";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { RegisterVerifierId, DscVerifierId } from "../../common/src/constants/constants";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
// Environment configuration
|
||||
const NETWORK = process.env.NETWORK || "localhost"; // Default to staging
|
||||
const RPC_URL_KEY = NETWORK === "celo" ? "CELO_RPC_URL" : "CELO_ALFAJORES_RPC_URL";
|
||||
const PRIVATE_KEY = process.env.CELO_KEY;
|
||||
|
||||
// Network to Chain ID mapping
|
||||
const NETWORK_TO_CHAIN_ID: Record<string, string> = {
|
||||
"localhost": "31337",
|
||||
"celoAlfajores": "44787",
|
||||
"celo": "42220",
|
||||
};
|
||||
|
||||
// Get chain ID from network name
|
||||
const getChainId = (network: string): string => {
|
||||
return NETWORK_TO_CHAIN_ID[network] || NETWORK_TO_CHAIN_ID["staging"];
|
||||
};
|
||||
|
||||
const CHAIN_ID = getChainId(NETWORK);
|
||||
|
||||
// Define AttestationId constants directly based on values from AttestationId.sol
|
||||
const AttestationId = {
|
||||
// Pad with zeros to create full 32 bytes length
|
||||
E_PASSPORT: "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
EU_ID_CARD: "0x0000000000000000000000000000000000000000000000000000000000000002",
|
||||
};
|
||||
|
||||
// Dynamic paths based on chain ID
|
||||
const deployedAddressesPath = path.join(__dirname, `../ignition/deployments/chain-${CHAIN_ID}/deployed_addresses.json`);
|
||||
const contractAbiPath = path.join(__dirname, `../ignition/deployments/chain-${CHAIN_ID}/artifacts/DeployHubV2#IdentityVerificationHubImplV2.json`);
|
||||
|
||||
// Debug logs for paths and files
|
||||
console.log("Network:", NETWORK);
|
||||
console.log("Chain ID:", CHAIN_ID);
|
||||
console.log("Current directory:", __dirname);
|
||||
console.log("Deployed addresses path:", deployedAddressesPath);
|
||||
console.log("Contract ABI path:", contractAbiPath);
|
||||
|
||||
// Debug logs for environment variables (redacted for security)
|
||||
console.log(`${RPC_URL_KEY} configured:`, !!process.env[RPC_URL_KEY]);
|
||||
console.log("CELO_KEY configured:", !!PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
const deployedAddresses = JSON.parse(fs.readFileSync(deployedAddressesPath, "utf-8"));
|
||||
console.log("Deployed addresses loaded:", deployedAddresses);
|
||||
|
||||
const identityVerificationHubAbiFile = fs.readFileSync(contractAbiPath, "utf-8");
|
||||
console.log("ABI file loaded");
|
||||
|
||||
const identityVerificationHubAbi = JSON.parse(identityVerificationHubAbiFile).abi;
|
||||
console.log("ABI parsed");
|
||||
|
||||
function getContractAddressByPartialName(partialName: string): string | unknown {
|
||||
for (const [key, value] of Object.entries(deployedAddresses)) {
|
||||
if (key.includes(partialName)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getContractAddressByExactName(exactName: string): string | unknown {
|
||||
if (exactName in deployedAddresses) {
|
||||
return deployedAddresses[exactName];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getAttestationIdBytes32(attestationIdName: string): string {
|
||||
return AttestationId[attestationIdName as keyof typeof AttestationId];
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const provider = new ethers.JsonRpcProvider(process.env[RPC_URL_KEY] as string);
|
||||
console.log("Provider created");
|
||||
|
||||
const wallet = new ethers.Wallet(PRIVATE_KEY as string, provider);
|
||||
console.log("Wallet created");
|
||||
|
||||
// const hubAddress = deployedAddresses["DeployHub#IdentityVerificationHub"];
|
||||
const hubAddress = "0xbaB9B3c376e84FEB4aFD9423e3aB278B47d34a0a"; // Update with your actual hub address
|
||||
console.log("Hub address:", hubAddress);
|
||||
|
||||
if (!hubAddress) {
|
||||
throw new Error("Hub address not found in deployed_addresses.json");
|
||||
}
|
||||
|
||||
const identityVerificationHub = new ethers.Contract(hubAddress, identityVerificationHubAbi, wallet);
|
||||
console.log("Contract instance created");
|
||||
|
||||
// Update registry addresses for different attestation types
|
||||
const attestationTypes = ["E_PASSPORT", "EU_ID_CARD"];
|
||||
for (const attestationType of attestationTypes) {
|
||||
let registryName: any;
|
||||
if (attestationType == "E_PASSPORT") {
|
||||
registryName = "DeployRegistryModule#IdentityRegistry";
|
||||
} else if (attestationType == "EU_ID_CARD") {
|
||||
registryName = "DeployIdCardRegistryModule#IdentityRegistryIdCard";
|
||||
}
|
||||
|
||||
const registryAddress = getContractAddressByExactName(registryName);
|
||||
|
||||
if (!registryAddress) {
|
||||
console.log(`Skipping registry update for ${attestationType} because no deployed address was found.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Updating registry for ${attestationType}`);
|
||||
const attestationId = getAttestationIdBytes32(attestationType);
|
||||
|
||||
try {
|
||||
const tx = await identityVerificationHub.updateRegistry(attestationId, registryAddress);
|
||||
const receipt = await tx.wait();
|
||||
console.log(`Registry for ${attestationType} updated with tx: ${receipt.hash}`);
|
||||
} catch (error) {
|
||||
console.error(`Error updating registry for ${attestationType}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Update VC and Disclose circuit verifiers for different attestation types
|
||||
for (const attestationType of attestationTypes) {
|
||||
let verifierName: any;
|
||||
if (attestationType == "E_PASSPORT") {
|
||||
verifierName = "DeployAllVerifiers#Verifier_vc_and_disclose";
|
||||
} else if (attestationType == "EU_ID_CARD") {
|
||||
verifierName = "";
|
||||
}
|
||||
const verifierAddress = getContractAddressByExactName(verifierName);
|
||||
|
||||
if (!verifierAddress) {
|
||||
console.log(
|
||||
`Skipping VC and Disclose circuit update for ${attestationType} because no deployed address was found.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Updating VC and Disclose circuit for ${attestationType}`);
|
||||
const attestationId = getAttestationIdBytes32(attestationType);
|
||||
|
||||
try {
|
||||
const tx = await identityVerificationHub.updateVcAndDiscloseCircuit(attestationId, verifierAddress);
|
||||
const receipt = await tx.wait();
|
||||
console.log(`VC and Disclose circuit for ${attestationType} updated with tx: ${receipt.hash}`);
|
||||
} catch (error) {
|
||||
console.error(`Error updating VC and Disclose circuit for ${attestationType}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch update register circuit verifiers
|
||||
const registerVerifierKeys = Object.keys(RegisterVerifierId).filter((key) => isNaN(Number(key)));
|
||||
const registerCircuitVerifierIds: number[] = [];
|
||||
const registerCircuitVerifierAddresses: string[] = [];
|
||||
|
||||
for (const key of registerVerifierKeys) {
|
||||
const verifierName = `Verifier_${key}`;
|
||||
const verifierAddress = getContractAddressByPartialName(verifierName);
|
||||
|
||||
if (!verifierAddress) {
|
||||
console.log(`Skipping ${verifierName} because no deployed address was found.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const verifierId = RegisterVerifierId[key as keyof typeof RegisterVerifierId];
|
||||
registerCircuitVerifierIds.push(verifierId);
|
||||
registerCircuitVerifierAddresses.push(verifierAddress as string);
|
||||
}
|
||||
|
||||
if (registerCircuitVerifierIds.length > 0) {
|
||||
console.log("Batch updating register circuit verifiers");
|
||||
|
||||
try {
|
||||
const tx = await identityVerificationHub.batchUpdateRegisterCircuitVerifiers(
|
||||
registerCircuitVerifierIds,
|
||||
registerCircuitVerifierAddresses,
|
||||
);
|
||||
const receipt = await tx.wait();
|
||||
console.log(`Register circuit verifiers updated with tx: ${receipt.hash}`);
|
||||
} catch (error) {
|
||||
console.error("Error batch updating register circuit verifiers:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch update DSC circuit verifiers
|
||||
const dscKeys = Object.keys(DscVerifierId).filter((key) => isNaN(Number(key)));
|
||||
const dscCircuitVerifierIds: number[] = [];
|
||||
const dscCircuitVerifierAddresses: string[] = [];
|
||||
|
||||
for (const key of dscKeys) {
|
||||
const verifierName = `Verifier_${key}`;
|
||||
const verifierAddress = getContractAddressByPartialName(verifierName);
|
||||
|
||||
if (!verifierAddress) {
|
||||
console.log(`Skipping ${verifierName} because no deployed address was found.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const verifierId = DscVerifierId[key as keyof typeof DscVerifierId];
|
||||
dscCircuitVerifierIds.push(verifierId);
|
||||
dscCircuitVerifierAddresses.push(verifierAddress as string);
|
||||
}
|
||||
|
||||
if (dscCircuitVerifierIds.length > 0) {
|
||||
console.log("Batch updating DSC circuit verifiers");
|
||||
|
||||
try {
|
||||
const tx = await identityVerificationHub.batchUpdateDscCircuitVerifiers(
|
||||
dscCircuitVerifierIds,
|
||||
dscCircuitVerifierAddresses,
|
||||
);
|
||||
const receipt = await tx.wait();
|
||||
console.log(`DSC circuit verifiers updated with tx: ${receipt.hash}`);
|
||||
} catch (error) {
|
||||
console.error("Error batch updating DSC circuit verifiers:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Execution error:", error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Initial setup error:", error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
132
contracts/scripts/setRegistry.ts
Normal file
132
contracts/scripts/setRegistry.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { ethers } from "ethers";
|
||||
import * as dotenv from "dotenv";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
// import { getCscaTreeRoot } from "../../common/src/utils/trees";
|
||||
// import serialized_csca_tree from "../../common/pubkeys/serialized_csca_tree.json";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
// Environment configuration
|
||||
const NETWORK = process.env.NETWORK || "localhost"; // Default to localhost
|
||||
const RPC_URL_KEY = NETWORK === "celo" ? "CELO_RPC_URL" : "CELO_ALFAJORES_RPC_URL";
|
||||
const PRIVATE_KEY = process.env.CELO_KEY;
|
||||
const SKIP_CSCA_UPDATE = process.env.SKIP_CSCA_UPDATE === "true";
|
||||
const CSCA_ROOT = process.env.CSCA_ROOT; // Allow manual CSCA root setting
|
||||
|
||||
// Network to Chain ID mapping
|
||||
const NETWORK_TO_CHAIN_ID: Record<string, string> = {
|
||||
"localhost": "31337",
|
||||
"celoAlfajores": "44787",
|
||||
"celo": "42220",
|
||||
};
|
||||
|
||||
// Get chain ID from network name
|
||||
const getChainId = (network: string): string => {
|
||||
return NETWORK_TO_CHAIN_ID[network] || NETWORK_TO_CHAIN_ID["localhost"];
|
||||
};
|
||||
|
||||
const CHAIN_ID = getChainId(NETWORK);
|
||||
|
||||
// Dynamic paths based on chain ID
|
||||
const deployedAddressesPath = path.join(__dirname, `../ignition/deployments/chain-${CHAIN_ID}/deployed_addresses.json`);
|
||||
const contractAbiPath = path.join(__dirname, `../ignition/deployments/chain-${CHAIN_ID}/artifacts/DeployRegistryModule#IdentityRegistryImplV1.json`);
|
||||
|
||||
// Debug logs for paths and files
|
||||
console.log("Network:", NETWORK);
|
||||
console.log("Chain ID:", CHAIN_ID);
|
||||
console.log("Current directory:", __dirname);
|
||||
console.log("Deployed addresses path:", deployedAddressesPath);
|
||||
console.log("Contract ABI path:", contractAbiPath);
|
||||
|
||||
// Debug logs for environment variables (redacted for security)
|
||||
console.log(`${RPC_URL_KEY} configured:`, !!process.env[RPC_URL_KEY]);
|
||||
console.log("CELO_KEY configured:", !!PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
const deployedAddresses = JSON.parse(fs.readFileSync(deployedAddressesPath, "utf-8"));
|
||||
console.log("Deployed addresses loaded:", deployedAddresses);
|
||||
|
||||
const identityRegistryAbiFile = fs.readFileSync(contractAbiPath, "utf-8");
|
||||
console.log("Registry ABI file loaded");
|
||||
|
||||
const identityRegistryAbi = JSON.parse(identityRegistryAbiFile).abi;
|
||||
console.log("Registry ABI parsed");
|
||||
|
||||
function getContractAddressByExactName(exactName: string): string | unknown {
|
||||
if (exactName in deployedAddresses) {
|
||||
return deployedAddresses[exactName];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const provider = new ethers.JsonRpcProvider(process.env[RPC_URL_KEY] as string);
|
||||
console.log("Provider created");
|
||||
|
||||
const wallet = new ethers.Wallet(PRIVATE_KEY as string, provider);
|
||||
console.log("Wallet created");
|
||||
|
||||
// Get registry address
|
||||
const registryAddress = getContractAddressByExactName("DeployRegistryModule#IdentityRegistry");
|
||||
console.log("Registry address:", registryAddress);
|
||||
|
||||
if (!registryAddress) {
|
||||
throw new Error("Registry address not found in deployed_addresses.json");
|
||||
}
|
||||
|
||||
// Get hub address
|
||||
const hubAddress = getContractAddressByExactName("DeployHubV2#IdentityVerificationHubImplV2");
|
||||
console.log("Hub address:", hubAddress);
|
||||
|
||||
if (!hubAddress) {
|
||||
throw new Error("Hub address not found in deployed_addresses.json");
|
||||
}
|
||||
|
||||
const identityRegistry = new ethers.Contract(registryAddress as string, identityRegistryAbi, wallet);
|
||||
console.log("Registry contract instance created");
|
||||
|
||||
// Update hub address
|
||||
console.log("Updating hub address...");
|
||||
try {
|
||||
const tx1 = await identityRegistry.updateHub(hubAddress);
|
||||
const receipt1 = await tx1.wait();
|
||||
console.log(`Hub address updated with tx: ${receipt1.hash}`);
|
||||
} catch (error) {
|
||||
console.error("Error updating hub address:", error);
|
||||
}
|
||||
|
||||
// Update CSCA root
|
||||
console.log("Updating CSCA root...");
|
||||
try {
|
||||
if (SKIP_CSCA_UPDATE) {
|
||||
console.log("Skipping CSCA root update as per configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CSCA_ROOT) {
|
||||
console.log("CSCA_ROOT environment variable not set, skipping CSCA root update");
|
||||
console.log("To set CSCA root, use: CSCA_ROOT=<your_root_value> npm run set:registry");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("CSCA Merkle root:", CSCA_ROOT);
|
||||
|
||||
const tx2 = await identityRegistry.updateCscaRoot(CSCA_ROOT);
|
||||
const receipt2 = await tx2.wait();
|
||||
console.log(`CSCA root updated with tx: ${receipt2.hash}`);
|
||||
} catch (error) {
|
||||
console.error("Error updating CSCA root:", error);
|
||||
}
|
||||
|
||||
console.log("Registry setup completed successfully!");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Execution error:", error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Initial setup error:", error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
132
contracts/scripts/setRegistryId.ts
Normal file
132
contracts/scripts/setRegistryId.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { ethers } from "ethers";
|
||||
import * as dotenv from "dotenv";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
// import { getCscaTreeRoot } from "../../common/src/utils/trees";
|
||||
// import serialized_csca_tree from "../../common/pubkeys/serialized_csca_tree.json";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
// Environment configuration
|
||||
const NETWORK = process.env.NETWORK || "localhost"; // Default to localhost
|
||||
const RPC_URL_KEY = NETWORK === "celo" ? "CELO_RPC_URL" : "CELO_ALFAJORES_RPC_URL";
|
||||
const PRIVATE_KEY = process.env.CELO_KEY;
|
||||
const SKIP_CSCA_UPDATE = process.env.SKIP_CSCA_UPDATE === "true";
|
||||
const CSCA_ROOT = process.env.CSCA_ROOT; // Allow manual CSCA root setting
|
||||
|
||||
// Network to Chain ID mapping
|
||||
const NETWORK_TO_CHAIN_ID: Record<string, string> = {
|
||||
"localhost": "31337",
|
||||
"celoAlfajores": "44787",
|
||||
"celo": "42220",
|
||||
};
|
||||
|
||||
// Get chain ID from network name
|
||||
const getChainId = (network: string): string => {
|
||||
return NETWORK_TO_CHAIN_ID[network] || NETWORK_TO_CHAIN_ID["localhost"];
|
||||
};
|
||||
|
||||
const CHAIN_ID = getChainId(NETWORK);
|
||||
|
||||
// Dynamic paths based on chain ID
|
||||
const deployedAddressesPath = path.join(__dirname, `../ignition/deployments/chain-${CHAIN_ID}/deployed_addresses.json`);
|
||||
const contractAbiPath = path.join(__dirname, `../ignition/deployments/chain-${CHAIN_ID}/artifacts/DeployIdCardRegistryModule#IdentityRegistryIdCardImplV1.json`);
|
||||
|
||||
// Debug logs for paths and files
|
||||
console.log("Network:", NETWORK);
|
||||
console.log("Chain ID:", CHAIN_ID);
|
||||
console.log("Current directory:", __dirname);
|
||||
console.log("Deployed addresses path:", deployedAddressesPath);
|
||||
console.log("Contract ABI path:", contractAbiPath);
|
||||
|
||||
// Debug logs for environment variables (redacted for security)
|
||||
console.log(`${RPC_URL_KEY} configured:`, !!process.env[RPC_URL_KEY]);
|
||||
console.log("CELO_KEY configured:", !!PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
const deployedAddresses = JSON.parse(fs.readFileSync(deployedAddressesPath, "utf-8"));
|
||||
console.log("Deployed addresses loaded:", deployedAddresses);
|
||||
|
||||
const identityRegistryIdCardAbiFile = fs.readFileSync(contractAbiPath, "utf-8");
|
||||
console.log("Registry ID Card ABI file loaded");
|
||||
|
||||
const identityRegistryIdCardAbi = JSON.parse(identityRegistryIdCardAbiFile).abi;
|
||||
console.log("Registry ID Card ABI parsed");
|
||||
|
||||
function getContractAddressByExactName(exactName: string): string | unknown {
|
||||
if (exactName in deployedAddresses) {
|
||||
return deployedAddresses[exactName];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const provider = new ethers.JsonRpcProvider(process.env[RPC_URL_KEY] as string);
|
||||
console.log("Provider created");
|
||||
|
||||
const wallet = new ethers.Wallet(PRIVATE_KEY as string, provider);
|
||||
console.log("Wallet created");
|
||||
|
||||
// Get registry ID card address
|
||||
const registryIdCardAddress = getContractAddressByExactName("DeployIdCardRegistryModule#IdentityRegistryIdCard");
|
||||
console.log("Registry ID Card address:", registryIdCardAddress);
|
||||
|
||||
if (!registryIdCardAddress) {
|
||||
throw new Error("Registry ID Card address not found in deployed_addresses.json");
|
||||
}
|
||||
|
||||
// Get hub address
|
||||
const hubAddress = getContractAddressByExactName("DeployHubV2#IdentityVerificationHubImplV2");
|
||||
console.log("Hub address:", hubAddress);
|
||||
|
||||
if (!hubAddress) {
|
||||
throw new Error("Hub address not found in deployed_addresses.json");
|
||||
}
|
||||
|
||||
const identityRegistryIdCard = new ethers.Contract(registryIdCardAddress as string, identityRegistryIdCardAbi, wallet);
|
||||
console.log("Registry ID Card contract instance created");
|
||||
|
||||
// Update hub address
|
||||
console.log("Updating hub address...");
|
||||
try {
|
||||
const tx1 = await identityRegistryIdCard.updateHub(hubAddress);
|
||||
const receipt1 = await tx1.wait();
|
||||
console.log(`Hub address updated with tx: ${receipt1.hash}`);
|
||||
} catch (error) {
|
||||
console.error("Error updating hub address:", error);
|
||||
}
|
||||
|
||||
// Update CSCA root (same value as passport registry)
|
||||
console.log("Updating CSCA root...");
|
||||
try {
|
||||
if (SKIP_CSCA_UPDATE) {
|
||||
console.log("Skipping CSCA root update as per configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CSCA_ROOT) {
|
||||
console.log("CSCA_ROOT environment variable not set, skipping CSCA root update");
|
||||
console.log("To set CSCA root, use: CSCA_ROOT=<your_root_value> npm run set:registry:idcard");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("CSCA Merkle root:", CSCA_ROOT);
|
||||
|
||||
const tx2 = await identityRegistryIdCard.updateCscaRoot(CSCA_ROOT);
|
||||
const receipt2 = await tx2.wait();
|
||||
console.log(`CSCA root updated with tx: ${receipt2.hash}`);
|
||||
} catch (error) {
|
||||
console.error("Error updating CSCA root:", error);
|
||||
}
|
||||
|
||||
console.log("Registry ID Card setup completed successfully!");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Execution error:", error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Initial setup error:", error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user