Feat/sumsub (#1654)

* fix: circuits and contracts

* feat: add reverse ofac logic

* feat: add onlyRole modifiers to functions

* style: replace onlyOwner reference in comment code to role-based access

* test: unskip and update governance tests for access control

* test: fix PCR0 setup in kyc test

---------

Co-authored-by: Evi Nova <tranquil_flow@protonmail.com>
This commit is contained in:
Nesopie
2026-01-27 14:49:34 +05:30
committed by GitHub
parent ba856226d8
commit bcfd284ca8
30 changed files with 242 additions and 139 deletions

View File

@@ -0,0 +1,52 @@
// import * as fs from 'fs';
// import { fileURLToPath } from 'url';
// import { dirname, join } from 'path';
// import { buildKycSMT } from '../trees.js';
// async function build_kyc_ofac_smt() {
// let startTime = performance.now();
// const __filename = fileURLToPath(import.meta.url);
// const __dirname = dirname(__filename);
// const baseInputPath = __dirname;
// const baseOutputPath = join(__dirname, '../../../../circuits/tests/consts/ofac/');
// const names = JSON.parse(fs.readFileSync(join(baseInputPath, 'names.json'), 'utf-8'));
// // -----KYC DATA-----
// console.log(`Reading data from ${baseInputPath}`);
// // -----KYC DATA-----
// console.log('\nBuilding KYC SMTs...');
// const nameAndDobKycTree = buildKycSMT(names, 'name_and_dob');
// const nameAndYobKycTree = buildKycSMT(names, 'name_and_yob');
// console.log('\n--- Results ---');
// console.log(
// `KYC - Name & DOB Tree: Processed ${nameAndDobKycTree[0]}/${names.length} entries in ${nameAndDobKycTree[1].toFixed(2)} ms`
// );
// console.log(
// `KYC - Name & YOB Tree: Processed ${nameAndYobKycTree[0]}/${names.length} entries in ${nameAndYobKycTree[1].toFixed(2)} ms`
// );
// console.log('Total Time:', (performance.now() - startTime).toFixed(2), 'ms');
// console.log(`\nExporting SMTs to ${baseOutputPath}...`);
// const nameAndDobKycOfacJSON = nameAndDobKycTree[2].export();
// const nameAndYobKycOfacJSON = nameAndYobKycTree[2].export();
// fs.writeFileSync(
// `${baseOutputPath}nameAndDobKycSMT.json`,
// JSON.stringify(nameAndDobKycOfacJSON)
// );
// fs.writeFileSync(
// `${baseOutputPath}nameAndYobKycSMT.json`,
// JSON.stringify(nameAndYobKycOfacJSON)
// );
// console.log('✅ SMT export complete.');
// }
// build_kyc_ofac_smt().catch((error) => {
// console.error('Error building KYC OFAC SMTs:', error);
// process.exit(1);
// });

View File

@@ -5,7 +5,7 @@ export const KYC_ID_TYPE_INDEX = KYC_COUNTRY_INDEX + KYC_COUNTRY_LENGTH;
export const KYC_ID_TYPE_LENGTH = 27;
export const KYC_ID_NUMBER_INDEX = KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH;
export const KYC_ID_NUMBER_LENGTH = 32; // Updated: max(20, 32) = 32
export const KYC_ID_NUMBER_LENGTH = 32;
export const KYC_ISSUANCE_DATE_INDEX = KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH;
export const KYC_ISSUANCE_DATE_LENGTH = 8;
@@ -25,11 +25,8 @@ export const KYC_PHOTO_HASH_LENGTH = 32;
export const KYC_PHONE_NUMBER_INDEX = KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH;
export const KYC_PHONE_NUMBER_LENGTH = 12;
export const KYC_DOCUMENT_INDEX = KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH;
export const KYC_DOCUMENT_LENGTH = 32; // Updated: max(2, 32) = 32
export const KYC_GENDER_INDEX = KYC_DOCUMENT_INDEX + KYC_DOCUMENT_LENGTH;
export const KYC_GENDER_LENGTH = 6;
export const KYC_GENDER_INDEX = KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH;
export const KYC_GENDER_LENGTH = 1;
export const KYC_ADDRESS_INDEX = KYC_GENDER_INDEX + KYC_GENDER_LENGTH;
export const KYC_ADDRESS_LENGTH = 100;
@@ -49,8 +46,7 @@ export const KYC_FIELD_LENGTHS = {
DOB: KYC_DOB_LENGTH, // 8
PHOTO_HASH: KYC_PHOTO_HASH_LENGTH, // 32
PHONE_NUMBER: KYC_PHONE_NUMBER_LENGTH, // 12
DOCUMENT: KYC_DOCUMENT_LENGTH, // 32 (updated)
GENDER: KYC_GENDER_LENGTH, // 6
GENDER: KYC_GENDER_LENGTH, // 1
ADDRESS: KYC_ADDRESS_LENGTH, // 100
} as const;
@@ -67,8 +63,7 @@ export const KYC_REVEAL_DATA_INDICES = {
DOB: KYC_FULL_NAME_INDEX + KYC_FULL_NAME_LENGTH, // 142 (updated)
PHOTO_HASH: KYC_DOB_INDEX + KYC_DOB_LENGTH, // 150 (updated)
PHONE_NUMBER: KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH, // 182 (updated)
DOCUMENT: KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH, // 194 (updated)
GENDER: KYC_DOCUMENT_INDEX + KYC_DOCUMENT_LENGTH, // 226 (updated)
GENDER: KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH, // 194 (updated)
ADDRESS: KYC_GENDER_INDEX + KYC_GENDER_LENGTH, // 232 (updated)
} as const;
@@ -106,14 +101,10 @@ export const KYC_SELECTOR_BITS = {
{ length: KYC_PHONE_NUMBER_LENGTH },
(_, i) => i + KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH
) as number[], // 182-193 (updated)
DOCUMENT: Array.from(
{ length: KYC_DOCUMENT_LENGTH },
(_, i) => i + KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH
) as number[], // 194-225 (updated)
GENDER: Array.from(
{ length: KYC_GENDER_LENGTH },
(_, i) => i + KYC_DOCUMENT_INDEX + KYC_DOCUMENT_LENGTH
) as number[], // 226-231 (updated)
(_, i) => i + KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH
) as number[], // 194-194 (updated)
ADDRESS: Array.from(
{ length: KYC_ADDRESS_LENGTH },
(_, i) => i + KYC_GENDER_INDEX + KYC_GENDER_LENGTH

View File

@@ -25,8 +25,7 @@ export const OFAC_DUMMY_INPUT: KycData = {
dob: '19481210',
photoHash: '1234567890',
phoneNumber: '1234567890',
document: 'ID',
gender: 'Male',
gender: 'M',
address: '1234567890',
user_identifier: '1234567890',
current_date: '20250101',
@@ -44,8 +43,7 @@ export const NON_OFAC_DUMMY_INPUT: KycData = {
dob: '19900101',
photoHash: '1234567890',
phoneNumber: '1234567890',
document: 'ID',
gender: 'Male',
gender: 'M',
address: '1234567890',
user_identifier: '1234567890',
current_date: '20250101',
@@ -124,9 +122,16 @@ export const generateKycDiscloseInput = (
forbiddenCountriesList?: string[],
minimumAge?: number,
updateTree?: boolean,
secret: string = '1234'
secret: string = '1234',
reverse?: boolean
) => {
const data = ofac_input ? OFAC_DUMMY_INPUT : NON_OFAC_DUMMY_INPUT;
let data = ofac_input ? OFAC_DUMMY_INPUT : NON_OFAC_DUMMY_INPUT;
if (reverse) {
data = {
...data,
fullName: data.fullName.split(' ').reverse().join(' '),
};
}
const serializedData = serializeKycData(data).padEnd(KYC_MAX_LENGTH, '\0');
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
const commitment = poseidon2([secret, packBytesAndPoseidon(msgPadded)]);

View File

@@ -11,7 +11,6 @@ export type KycData = {
dob: string;
photoHash: string;
phoneNumber: string;
document: string;
gender: string;
address: string;
user_identifier: string;
@@ -32,7 +31,6 @@ export const serializeKycData = (kycData: KycData) => {
serializedData += kycData.dob.padEnd(constants.KYC_DOB_LENGTH, '\0');
serializedData += kycData.photoHash.padEnd(constants.KYC_PHOTO_HASH_LENGTH, '\0');
serializedData += kycData.phoneNumber.padEnd(constants.KYC_PHONE_NUMBER_LENGTH, '\0');
serializedData += kycData.document.padEnd(constants.KYC_DOCUMENT_LENGTH, '\0');
serializedData += kycData.gender.padEnd(constants.KYC_GENDER_LENGTH, '\0');
serializedData += kycData.address.padEnd(constants.KYC_ADDRESS_LENGTH, '\0');

View File

@@ -723,20 +723,29 @@ export function buildKycSMT(field: any[], treetype: string): [number, number, SM
console.log(`Processing ${providerName}`, treetype, 'number', i, 'out of', field.length);
}
let leaf = BigInt(0);
let leafs = [BigInt(0), BigInt(0)];
if (treetype == 'name_and_dob') {
leaf = processNameAndDobKyc(entry, i);
leafs[0] = processNameAndDobKyc(entry, i, false);
leafs[1] = processNameAndDobKyc(entry, i, true);
} else if (treetype == 'name_and_yob') {
leaf = processNameAndYobKyc(entry, i);
leafs[0] = processNameAndYobKyc(entry, i, false);
leafs[1] = processNameAndYobKyc(entry, i, true);
}
if (leaf == BigInt(0) || tree.createProof(leaf).membership) {
if (leafs[0] == BigInt(0) || tree.createProof(leafs[0]).membership) {
console.log('This entry already exists in the tree, skipping...');
continue;
} else if (leafs[1] == BigInt(0) || tree.createProof(leafs[1]).membership) {
console.log('This entry already exists in the tree, skipping...');
continue;
}
tree.add(leafs[0], BigInt(1));
count += 1;
tree.add(leaf, BigInt(1));
if (leafs[0] != leafs[1]) {
tree.add(leafs[1], BigInt(1));
count += 1;
}
}
console.log(`Total ${providerName}`, treetype, 'parsed are : ', count, ' over ', field.length);
@@ -744,7 +753,7 @@ export function buildKycSMT(field: any[], treetype: string): [number, number, SM
return [count, performance.now() - startTime, tree];
}
const processNameAndDobKyc = (entry: any, i: number): bigint => {
const processNameAndDobKyc = (entry: any, i: number, reverse: boolean): bigint => {
const firstName = entry.First_Name;
const lastName = entry.Last_Name;
const day = entry.day;
@@ -756,7 +765,7 @@ const processNameAndDobKyc = (entry: any, i: number): bigint => {
return BigInt(0);
}
const nameHash = processNameKyc(firstName, lastName, i);
const nameHash = processNameKyc(firstName, lastName, i, reverse);
const dobHash = processDobKyc(day, month, year, i);
return generateSmallKey(poseidon2([dobHash, nameHash]));
@@ -773,7 +782,7 @@ export const getNameDobLeafKyc = (name: string, dob: string) => {
return generateSmallKey(poseidon2([dobHash, nameHash]));
};
const processNameKyc = (firstName: string, lastName: string, i: number): bigint => {
const processNameKyc = (firstName: string, lastName: string, i: number, reverse: boolean): bigint => {
const namePaddingLength = 64;
firstName = firstName.replace(/'/g, '');
@@ -783,8 +792,8 @@ const processNameKyc = (firstName: string, lastName: string, i: number): bigint
lastName = lastName.replace(/[- ]/g, '<');
lastName = lastName.replace(/\./g, '');
//TODO: check if smile id does first name and last name || last name and first name
const nameArr = (lastName + ' ' + firstName)
let nameStr = reverse ? (lastName + ' ' + firstName) : (firstName + ' ' + lastName);
const nameArr = nameStr
.padEnd(namePaddingLength, '\0')
.split('')
.map((char) => char.charCodeAt(0));
@@ -825,7 +834,7 @@ export const getNameYobLeafKyc = (name: string, yob: string) => {
return generateSmallKey(poseidon2([yearHash, nameHash]));
};
const processNameAndYobKyc = (entry: any, i: number): bigint => {
const processNameAndYobKyc = (entry: any, i: number, reverse: boolean): bigint => {
const firstName = entry.First_Name;
const lastName = entry.Last_Name;
const year = entry.year;
@@ -834,7 +843,7 @@ const processNameAndYobKyc = (entry: any, i: number): bigint => {
return BigInt(0);
}
const nameHash = processNameKyc(firstName, lastName, i);
const nameHash = processNameKyc(firstName, lastName, i, reverse);
const yearHash = processYearKyc(year, i);
return generateSmallKey(poseidon2([yearHash, nameHash]));
};