Fix/contract test fixes (#1490)

* fix: remove outdated tests

* fix: failing tests fixed and updated

* fix: proper gitcommit entry with metadata

* chore: yarn prettier
This commit is contained in:
Evi Nova
2025-12-12 22:43:00 +10:00
committed by GitHub
parent 4b09e5b96f
commit a3ec81b8fa
16 changed files with 343 additions and 1585 deletions

View File

@@ -398,7 +398,7 @@ export async function getAadharRegistrationWindow() {
}
export function returnNewDateString(timestamp?: string): string {
const newDate = timestamp ? new Date(+timestamp) : new Date();
const newDate = timestamp ? new Date(+timestamp * 1000) : new Date();
// Convert the UTC date to IST by adding 5 hours and 30 minutes
const offsetHours = 5;

View File

@@ -705,15 +705,11 @@ contract IdentityVerificationHubImplV2 is ImplRoot {
_performUserIdentifierCheck(userContextData, vcAndDiscloseProof, header.attestationId, indices);
}
// Scope 2: Root and date checks
// Scope 2: Root, OFAC, and current date checks
{
_performRootCheck(header.attestationId, vcAndDiscloseProof, indices);
_performOfacCheck(header.attestationId, vcAndDiscloseProof, indices);
if (header.attestationId == AttestationId.AADHAAR) {
_performNumericCurrentDateCheck(vcAndDiscloseProof, indices);
} else {
_performCurrentDateCheck(vcAndDiscloseProof, indices);
}
_performCurrentDateCheck(header.attestationId, vcAndDiscloseProof, indices);
}
// Scope 3: Groth16 proof verification
@@ -1009,41 +1005,56 @@ contract IdentityVerificationHubImplV2 is ImplRoot {
}
/**
* @notice Performs current date validation
* @notice Performs current date validation with format-aware parsing
* @dev Handles three date formats:
* - E_PASSPORT/EU_ID_CARD: 6 ASCII chars (YYMMDD)
* - SELFRICA_ID_CARD: 8 ASCII digits (YYYYMMDD)
* - AADHAAR: 3 numeric signals (year, month, day)
* @param attestationId The attestation type to determine date format
* @param vcAndDiscloseProof The proof containing date information
* @param indices Circuit-specific indices for extracting date values
*/
function _performCurrentDateCheck(
bytes32 attestationId,
GenericProofStruct memory vcAndDiscloseProof,
CircuitConstantsV2.DiscloseIndices memory indices
) internal view {
uint256[6] memory dateNum;
for (uint256 i = 0; i < 6; i++) {
dateNum[i] = vcAndDiscloseProof.pubSignals[indices.currentDateIndex + i];
uint256 currentTimestamp;
uint256 startIndex = indices.currentDateIndex;
if (attestationId == AttestationId.E_PASSPORT || attestationId == AttestationId.EU_ID_CARD) {
// E_PASSPORT, EU_ID_CARD: 6 ASCII chars (YYMMDD)
uint256[6] memory dateNum;
unchecked {
for (uint256 i; i < 6; ++i) {
dateNum[i] = vcAndDiscloseProof.pubSignals[startIndex + i];
}
}
currentTimestamp = Formatter.proofDateToUnixTimestamp(dateNum);
} else {
// AADHAAR: 3 numeric signals [year, month, day]
currentTimestamp = Formatter.proofDateToUnixTimestampNumeric(
[
vcAndDiscloseProof.pubSignals[startIndex],
vcAndDiscloseProof.pubSignals[startIndex + 1],
vcAndDiscloseProof.pubSignals[startIndex + 2]
]
);
}
uint256 currentTimestamp = Formatter.proofDateToUnixTimestamp(dateNum);
uint256 startOfDay = _getStartOfDayTimestamp();
uint256 endOfDay = startOfDay + 1 days - 1;
if (currentTimestamp < startOfDay - 1 days + 1 || currentTimestamp > endOfDay + 1 days) {
revert CurrentDateNotInValidRange();
}
_validateDateInRange(currentTimestamp);
}
function _performNumericCurrentDateCheck(
GenericProofStruct memory vcAndDiscloseProof,
CircuitConstantsV2.DiscloseIndices memory indices
) internal view {
// date is going to be 2025, 12, 13
uint256[3] memory dateNum;
dateNum[0] = vcAndDiscloseProof.pubSignals[indices.currentDateIndex];
dateNum[1] = vcAndDiscloseProof.pubSignals[indices.currentDateIndex + 1];
dateNum[2] = vcAndDiscloseProof.pubSignals[indices.currentDateIndex + 2];
/**
* @notice Validates that a timestamp is within the acceptable range
* @param currentTimestamp The timestamp to validate
*/
function _validateDateInRange(uint256 currentTimestamp) internal view {
// Calculate the timestamp for the start of current date by subtracting the remainder of block.timestamp modulo 1 day
uint256 startOfDay = block.timestamp - (block.timestamp % 1 days);
uint256 currentTimestamp = Formatter.proofDateToUnixTimestampNumeric(dateNum);
uint256 startOfDay = _getStartOfDayTimestamp();
uint256 endOfDay = startOfDay + 1 days - 1;
if (currentTimestamp < startOfDay - 1 days + 1 || currentTimestamp > endOfDay + 1 days) {
// Check if timestamp is within range
if (currentTimestamp < startOfDay - 1 days + 1 || currentTimestamp > startOfDay + 1 days - 1) {
revert CurrentDateNotInValidRange();
}
}

View File

@@ -49,3 +49,21 @@ interface IVcAndDiscloseAadhaarCircuitVerifier {
uint256[19] calldata pubSignals
) external view returns (bool);
}
interface IVcAndDiscloseSelfricaCircuitVerifier {
/**
* @notice Verifies a given VC and Disclose zero-knowledge proof.
* @dev This function checks the validity of the provided proof parameters.
* @param a The 'a' component of the proof.
* @param b The 'b' component of the proof.
* @param c The 'c' component of the proof.
* @param pubSignals The public signals associated with the proof.
* @return A boolean value indicating whether the proof is valid (true) or not (false).
*/
function verifyProof(
uint256[2] calldata a,
uint256[2][2] calldata b,
uint256[2] calldata c,
uint256[30] calldata pubSignals
) external view returns (bool);
}

View File

@@ -22,17 +22,17 @@ pragma solidity >=0.7.0 <0.9.0;
contract Verifier_register_aadhaar {
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
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 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;
@@ -42,7 +42,6 @@ contract Verifier_register_aadhaar {
uint256 constant deltay1 = 18162968189172780580333095558539690618880186036957545736311404283407493778880;
uint256 constant deltay2 = 7343682947937413219111184190299798376421430633278379635221606345245958931239;
uint256 constant IC0x = 18984838814932147425072354846429508676387686524229308734161716095463360490134;
uint256 constant IC0y = 11220857659665071811279473081460089783437970319349511357818317231596300603739;
@@ -58,14 +57,18 @@ contract Verifier_register_aadhaar {
uint256 constant IC4x = 17894574390662839711557891944994831304061930223868407277717041388786423798517;
uint256 constant IC4y = 10747262533817845366080322542335489573224940900925614580771284497946454436591;
// 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[4] calldata _pubSignals) public view returns (bool) {
function verifyProof(
uint[2] calldata _pA,
uint[2][2] calldata _pB,
uint[2] calldata _pC,
uint[4] calldata _pubSignals
) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, r)) {
@@ -117,7 +120,6 @@ contract Verifier_register_aadhaar {
g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96)))
// -A
mstore(_pPairing, calldataload(pA))
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
@@ -142,7 +144,6 @@ contract Verifier_register_aadhaar {
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)
@@ -159,7 +160,6 @@ contract Verifier_register_aadhaar {
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))
@@ -178,12 +178,11 @@ contract Verifier_register_aadhaar {
checkField(calldataload(add(_pubSignals, 96)))
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
mstore(0, isValid)
return(0, 0x20)
}
}
}
return(0, 0x20)
}
}
}

View File

@@ -22,7 +22,7 @@ const config: HardhatUserConfig = {
evmVersion: "cancun",
optimizer: {
enabled: true,
runs: 100000,
runs: 200,
},
},
},

View File

@@ -29,6 +29,7 @@ import {
readContractVersion,
getContractFilePath,
addVersion,
updateVersionGitCommit,
getExplorerUrl,
shortenAddress,
createGitTag,
@@ -628,7 +629,16 @@ task("upgrade", "Deploy new implementation and create Safe proposal for upgrade"
const committed = gitCommit(commitMessage);
if (committed) {
const newGitCommit = getGitCommitShort();
log.success(`Committed: ${newGitCommit}`);
// Update registry with the actual commit hash and amend the commit
try {
updateVersionGitCommit(contractId, network, newVersion, newGitCommit);
execSync("git add -A && git commit --amend --no-edit", { cwd: process.cwd(), stdio: "pipe" });
log.success(`Committed: ${newGitCommit} (with gitCommit in registry)`);
} catch (e: any) {
log.warning(`Could not update gitCommit in registry: ${e.message}`);
log.success(`Committed: ${newGitCommit}`);
}
const tagName = `${contractId.toLowerCase()}-v${newVersion}`;
try {
@@ -648,6 +658,17 @@ task("upgrade", "Deploy new implementation and create Safe proposal for upgrade"
} else {
log.warning("Could not create git commit - please commit manually");
}
} else {
// If skipping commit, record current HEAD as reference
const currentCommit = getGitCommitShort();
if (currentCommit !== "unknown") {
try {
updateVersionGitCommit(contractId, network, newVersion, currentCommit);
log.info(`Recorded current commit reference: ${currentCommit}`);
} catch {
// Non-critical, ignore
}
}
}
// ========================================================================

View File

@@ -246,6 +246,25 @@ export function addVersion(
writeRegistry(registry);
}
/**
* Update gitCommit for a specific version deployment
*/
export function updateVersionGitCommit(
contractId: string,
network: SupportedNetwork,
version: string,
gitCommit: string,
): void {
const registry = readRegistry();
if (!registry.versions[contractId]?.[version]?.deployments?.[network]) {
throw new Error(`Deployment not found: ${contractId} v${version} on ${network}`);
}
registry.versions[contractId][version].deployments[network].gitCommit = gitCommit;
writeRegistry(registry);
}
/**
* Update proxy address for a contract on a network
*/

View File

@@ -1,481 +0,0 @@
import { expect } from "chai";
import { ethers, upgrades } from "hardhat";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import {
MockOwnableHub,
MockOwnableRegistry,
IdentityVerificationHubImplV2,
IdentityRegistryImplV1,
IdentityRegistryIdCardImplV1,
PCR0Manager,
CustomVerifier,
PoseidonT3,
} from "../../typechain-types";
/**
* FULL PRODUCTION UPGRADE INTEGRATION TEST
*
* This test simulates upgrading production contracts from Ownable to AccessControl governance.
*
* PRODUCTION SCENARIO:
* - Current: IdentityVerificationHubImplV2 with OLD ImplRoot (uses Ownable2StepUpgradeable)
* - Current: IdentityRegistryImplV1 with OLD ImplRoot (uses Ownable2StepUpgradeable)
* - Current: IdentityRegistryIdCardImplV1 with OLD ImplRoot (uses Ownable2StepUpgradeable)
* - Current: PCR0Manager with Ownable
*
* UPGRADE TO:
* - New: IdentityVerificationHubImplV2 with NEW ImplRoot (uses AccessControlUpgradeable)
* - New: IdentityRegistryImplV1 with NEW ImplRoot (uses AccessControlUpgradeable)
* - New: IdentityRegistryIdCardImplV1 with NEW ImplRoot (uses AccessControlUpgradeable)
* - New: PCR0Manager with AccessControlUpgradeable
*
* Test Flow:
* 1. Deploy OLD contracts with Ownable (MockOwnableHub = V2 with old ImplRoot)
* 2. Populate with production data
* 3. Upgrade to NEW contracts with AccessControl (MockUpgradedHub = V2 with new ImplRoot)
* 4. Verify state preservation (no data loss)
* 5. Transfer roles to multisigs
* 6. Verify multisig control and deployer has no control
* 7. Verify all functionality still works
*
* Note: MockOwnableHub/MockUpgradedHub represent IdentityVerificationHubImplV2
* with old ImplRoot vs new ImplRoot. We use mocks because the real contracts are
* already compiled with the new ImplRoot.
*/
describe("🚀 PRODUCTION UPGRADE: Ownable → AccessControl Governance", function () {
this.timeout(120000);
let deployer: SignerWithAddress;
let securityMultisig: SignerWithAddress;
let operationsMultisig: SignerWithAddress;
let user1: SignerWithAddress;
let user2: SignerWithAddress;
// Contracts
let oldHubProxy: MockOwnableHub;
let upgradedHub: IdentityVerificationHubImplV2;
let oldRegistryProxy: MockOwnableRegistry;
let upgradedRegistry: IdentityRegistryImplV1;
let idCardRegistryProxy: IdentityRegistryIdCardImplV1;
let pcr0Manager: PCR0Manager;
// Libraries
let customVerifier: CustomVerifier;
let poseidonT3: PoseidonT3;
// Test constants
const SECURITY_ROLE = ethers.keccak256(ethers.toUtf8Bytes("SECURITY_ROLE"));
const OPERATIONS_ROLE = ethers.keccak256(ethers.toUtf8Bytes("OPERATIONS_ROLE"));
// Sample production data
const SAMPLE_CSCA_ROOT = "0x1111111111111111111111111111111111111111111111111111111111111111";
const SAMPLE_PCR0 = "0x" + "22".repeat(48);
before(async function () {
[deployer, securityMultisig, operationsMultisig, user1, user2] = await ethers.getSigners();
console.log("\n🎯 Production Upgrade Simulation");
console.log(` Deployer: ${deployer.address}`);
console.log(` Critical Multisig: ${securityMultisig.address}`);
console.log(` Standard Multisig: ${operationsMultisig.address}`);
console.log("\n📝 Scenario: Upgrade IdentityVerificationHubImplV2 & Registries");
console.log(" From: Ownable2StepUpgradeable (old ImplRoot)");
console.log(" To: AccessControlUpgradeable (new ImplRoot)");
});
describe("📦 Phase 1: Deploy Current Production State (with Ownable)", function () {
it("should deploy libraries", async function () {
console.log("\n📚 Deploying libraries...");
const CustomVerifierFactory = await ethers.getContractFactory("CustomVerifier");
customVerifier = await CustomVerifierFactory.deploy();
await customVerifier.waitForDeployment();
console.log(` ✅ CustomVerifier: ${await customVerifier.getAddress()}`);
const PoseidonT3Factory = await ethers.getContractFactory("PoseidonT3");
poseidonT3 = await PoseidonT3Factory.deploy();
await poseidonT3.waitForDeployment();
console.log(` ✅ PoseidonT3: ${await poseidonT3.getAddress()}`);
});
it("should deploy HubV2 with Ownable (current production)", async function () {
console.log("\n🏢 Deploying HubV2 (Ownable)...");
console.log(" (Simulates current production: V2 with old ImplRoot)");
const MockOwnableHubFactory = await ethers.getContractFactory("MockOwnableHub");
oldHubProxy = (await upgrades.deployProxy(MockOwnableHubFactory, [], {
kind: "uups",
initializer: "initialize",
unsafeAllow: ["constructor", "state-variable-immutable", "state-variable-assignment"],
})) as unknown as MockOwnableHub;
await oldHubProxy.waitForDeployment();
console.log(` ✅ HubV2: ${await oldHubProxy.getAddress()}`);
expect(await oldHubProxy.owner()).to.equal(deployer.address);
console.log(` ✅ Current owner: ${deployer.address}`);
});
it("should deploy Registry with Ownable (current production)", async function () {
console.log("\n📝 Deploying Registry (Ownable)...");
console.log(" (Simulates current production: IdentityRegistryImplV1 with old ImplRoot)");
const MockOwnableRegistryFactory = await ethers.getContractFactory("MockOwnableRegistry");
oldRegistryProxy = (await upgrades.deployProxy(
MockOwnableRegistryFactory,
[ethers.ZeroAddress], // hubAddress
{
kind: "uups",
initializer: "initialize",
unsafeAllow: [
"constructor",
"state-variable-immutable",
"state-variable-assignment",
"external-library-linking",
],
},
)) as unknown as MockOwnableRegistry;
await oldRegistryProxy.waitForDeployment();
console.log(` ✅ Registry: ${await oldRegistryProxy.getAddress()}`);
expect(await oldRegistryProxy.owner()).to.equal(deployer.address);
console.log(` ✅ Current owner: ${deployer.address}`);
});
it("should configure contracts", async function () {
console.log("\n🔗 Configuring contracts...");
await oldHubProxy.updateRegistry(await oldRegistryProxy.getAddress());
await oldRegistryProxy.setHub(await oldHubProxy.getAddress());
console.log(" ✅ Hub ← → Registry configured");
});
});
describe("📊 Phase 2: Populate with Production Data", function () {
it("should add production data", async function () {
console.log("\n📊 Adding production data...");
// Add CSCA root to registry
await oldRegistryProxy.updateCscaRoot(SAMPLE_CSCA_ROOT);
expect(await oldRegistryProxy.getCscaRoot()).to.equal(SAMPLE_CSCA_ROOT);
console.log(" ✅ CSCA root: " + SAMPLE_CSCA_ROOT.substring(0, 20) + "...");
// Set circuit version in hub
await oldHubProxy.updateCircuitVersion(2);
expect(await oldHubProxy.getCircuitVersion()).to.equal(2);
console.log(" ✅ Circuit version: 2");
});
});
describe("⚡ Phase 3: CRITICAL - Execute Governance Upgrade", function () {
let registryAddressBefore: string;
let cscaRootBefore: string;
let circuitVersionBefore: bigint;
before(async function () {
console.log("\n💾 Capturing pre-upgrade state...");
registryAddressBefore = await oldHubProxy.getRegistry();
cscaRootBefore = await oldRegistryProxy.getCscaRoot();
circuitVersionBefore = await oldHubProxy.getCircuitVersion();
console.log(` Registry address: ${registryAddressBefore}`);
console.log(` CSCA root: ${cscaRootBefore.substring(0, 20)}...`);
console.log(` Circuit version: ${circuitVersionBefore}`);
});
it("should upgrade HubV2 to AccessControl governance", async function () {
console.log("\n⚡ CRITICAL: Upgrading HubV2 governance...");
console.log(" From: MockOwnableHub (simulates V2 with old Ownable ImplRoot)");
console.log(" To: IdentityVerificationHubImplV2 (real contract with new AccessControl ImplRoot)");
const HubV2Factory = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: { CustomVerifier: await customVerifier.getAddress() },
});
upgradedHub = (await upgrades.upgradeProxy(await oldHubProxy.getAddress(), HubV2Factory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for Ownable → AccessControl
unsafeAllow: ["constructor", "external-library-linking"],
call: { fn: "initializeGovernance", args: [] }, // Initialize governance roles (reinitializer)
})) as unknown as IdentityVerificationHubImplV2;
console.log(` ✅ Upgraded to: ${await upgradedHub.getAddress()}`);
// Verify proxy address unchanged
expect(await upgradedHub.getAddress()).to.equal(await oldHubProxy.getAddress());
console.log(" ✅ Same proxy address (in-place upgrade)");
// Verify governance roles initialized
expect(await upgradedHub.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await upgradedHub.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
console.log(" ✅ Governance roles initialized (deployer has both roles)");
// Verify ALL state preserved
expect(await upgradedHub.getRegistry()).to.equal(registryAddressBefore);
expect(await upgradedHub.getCircuitVersion()).to.equal(circuitVersionBefore);
console.log(" ✅ ALL STATE PRESERVED - NO DATA LOSS!");
});
it("should upgrade Registry to AccessControl governance", async function () {
console.log("\n⚡ CRITICAL: Upgrading Registry governance...");
console.log(" From: MockOwnableRegistry (simulates Registry with old Ownable ImplRoot)");
console.log(" To: IdentityRegistryImplV1 (real contract with new AccessControl ImplRoot)");
const RegistryFactory = await ethers.getContractFactory("IdentityRegistryImplV1", {
libraries: { PoseidonT3: await poseidonT3.getAddress() },
});
upgradedRegistry = (await upgrades.upgradeProxy(await oldRegistryProxy.getAddress(), RegistryFactory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for Ownable → AccessControl
unsafeAllow: ["constructor", "external-library-linking"],
call: { fn: "initializeGovernance", args: [] }, // Initialize governance roles
})) as unknown as IdentityRegistryImplV1;
console.log(` ✅ Upgraded to: ${await upgradedRegistry.getAddress()}`);
// Verify proxy address unchanged
expect(await upgradedRegistry.getAddress()).to.equal(await oldRegistryProxy.getAddress());
console.log(" ✅ Same proxy address (in-place upgrade)");
// Verify governance roles initialized
expect(await upgradedRegistry.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await upgradedRegistry.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
console.log(" ✅ Governance roles initialized (deployer has both roles)");
// Verify ALL state preserved
expect(await upgradedRegistry.getCscaRoot()).to.equal(cscaRootBefore);
expect(await upgradedRegistry.hub()).to.equal(await upgradedHub.getAddress());
console.log(" ✅ ALL STATE PRESERVED - NO DATA LOSS!");
});
});
describe("🆕 Phase 4: Deploy Additional Contracts with New Governance", function () {
it("should deploy ID Card Registry with AccessControl", async function () {
console.log("\n🆔 Deploying ID Card Registry (AccessControl from start)...");
const IdCardRegistryFactory = await ethers.getContractFactory("IdentityRegistryIdCardImplV1", {
libraries: { PoseidonT3: await poseidonT3.getAddress() },
});
idCardRegistryProxy = (await upgrades.deployProxy(IdCardRegistryFactory, [await upgradedHub.getAddress()], {
kind: "uups",
initializer: "initialize",
unsafeAllow: ["constructor", "external-library-linking"],
})) as unknown as IdentityRegistryIdCardImplV1;
await idCardRegistryProxy.waitForDeployment();
console.log(` ✅ ID Card Registry: ${await idCardRegistryProxy.getAddress()}`);
expect(await idCardRegistryProxy.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
console.log(" ✅ Deployer has governance roles");
});
it("should deploy PCR0Manager with AccessControl", async function () {
console.log("\n🔧 Deploying PCR0Manager (AccessControl from start)...");
const PCR0ManagerFactory = await ethers.getContractFactory("PCR0Manager");
pcr0Manager = await PCR0ManagerFactory.deploy();
await pcr0Manager.waitForDeployment();
console.log(` ✅ PCR0Manager: ${await pcr0Manager.getAddress()}`);
expect(await pcr0Manager.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
console.log(" ✅ Deployer has governance roles");
});
});
describe("🔑 Phase 5: Transfer Roles to Multisigs", function () {
it("should transfer HubV2 roles to multisigs and remove deployer", async function () {
console.log("\n🔑 Transferring HubV2 roles to multisigs...");
await upgradedHub.grantRole(SECURITY_ROLE, securityMultisig.address);
await upgradedHub.grantRole(OPERATIONS_ROLE, operationsMultisig.address);
console.log(" ✅ Granted roles to multisigs");
await upgradedHub.renounceRole(SECURITY_ROLE, deployer.address);
await upgradedHub.renounceRole(OPERATIONS_ROLE, deployer.address);
console.log(" ✅ Deployer renounced roles");
expect(await upgradedHub.hasRole(SECURITY_ROLE, securityMultisig.address)).to.be.true;
expect(await upgradedHub.hasRole(OPERATIONS_ROLE, operationsMultisig.address)).to.be.true;
expect(await upgradedHub.hasRole(SECURITY_ROLE, deployer.address)).to.be.false;
expect(await upgradedHub.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.false;
console.log(" ✅ HubV2 now controlled by multisigs only");
});
it("should transfer Registry roles to multisigs and remove deployer", async function () {
console.log("\n🔑 Transferring Registry roles to multisigs...");
await upgradedRegistry.grantRole(SECURITY_ROLE, securityMultisig.address);
await upgradedRegistry.grantRole(OPERATIONS_ROLE, operationsMultisig.address);
await upgradedRegistry.renounceRole(SECURITY_ROLE, deployer.address);
await upgradedRegistry.renounceRole(OPERATIONS_ROLE, deployer.address);
expect(await upgradedRegistry.hasRole(SECURITY_ROLE, securityMultisig.address)).to.be.true;
expect(await upgradedRegistry.hasRole(OPERATIONS_ROLE, operationsMultisig.address)).to.be.true;
expect(await upgradedRegistry.hasRole(SECURITY_ROLE, deployer.address)).to.be.false;
console.log(" ✅ Registry now controlled by multisigs only");
});
it("should transfer ID Card Registry roles to multisigs", async function () {
console.log("\n🔑 Transferring ID Card Registry roles to multisigs...");
await idCardRegistryProxy.grantRole(SECURITY_ROLE, securityMultisig.address);
await idCardRegistryProxy.grantRole(OPERATIONS_ROLE, operationsMultisig.address);
await idCardRegistryProxy.renounceRole(SECURITY_ROLE, deployer.address);
await idCardRegistryProxy.renounceRole(OPERATIONS_ROLE, deployer.address);
expect(await idCardRegistryProxy.hasRole(SECURITY_ROLE, securityMultisig.address)).to.be.true;
expect(await idCardRegistryProxy.hasRole(SECURITY_ROLE, deployer.address)).to.be.false;
console.log(" ✅ ID Card Registry now controlled by multisigs only");
});
it("should transfer PCR0Manager roles to multisigs", async function () {
console.log("\n🔑 Transferring PCR0Manager roles to multisigs...");
await pcr0Manager.grantRole(SECURITY_ROLE, securityMultisig.address);
await pcr0Manager.grantRole(OPERATIONS_ROLE, operationsMultisig.address);
await pcr0Manager.renounceRole(SECURITY_ROLE, deployer.address);
await pcr0Manager.renounceRole(OPERATIONS_ROLE, deployer.address);
expect(await pcr0Manager.hasRole(SECURITY_ROLE, securityMultisig.address)).to.be.true;
expect(await pcr0Manager.hasRole(SECURITY_ROLE, deployer.address)).to.be.false;
console.log(" ✅ PCR0Manager now controlled by multisigs only");
});
});
describe("✅ Phase 6: Verify Multisig Control Works", function () {
it("should allow critical multisig to update HubV2", async function () {
console.log("\n✅ Testing HubV2 multisig control...");
const newRegistry = user1.address;
await upgradedHub.connect(securityMultisig).updateRegistry(newRegistry);
expect(await upgradedHub.getRegistry()).to.equal(newRegistry);
console.log(" ✅ Critical multisig can update HubV2");
});
it("should allow operations multisig to update Registry CSCA root", async function () {
console.log("\n✅ Testing Registry multisig control...");
const newRoot = "0x" + "33".repeat(32);
await upgradedRegistry.connect(operationsMultisig).updateCscaRoot(newRoot);
expect(await upgradedRegistry.getCscaRoot()).to.equal(newRoot);
console.log(" ✅ Operations multisig can update Registry CSCA root");
});
it("should allow critical multisig to manage PCR0", async function () {
console.log("\n✅ Testing PCR0Manager multisig control...");
await pcr0Manager.connect(securityMultisig).addPCR0(SAMPLE_PCR0);
expect(await pcr0Manager.isPCR0Set(SAMPLE_PCR0)).to.be.true;
console.log(" ✅ Critical multisig can manage PCR0");
});
});
describe("🚫 Phase 7: Verify Deployer Has ZERO Control", function () {
it("should prevent deployer from updating HubV2", async function () {
console.log("\n🚫 Verifying deployer CANNOT update HubV2...");
await expect(upgradedHub.connect(deployer).updateRegistry(ethers.ZeroAddress)).to.be.revertedWithCustomError(
upgradedHub,
"AccessControlUnauthorizedAccount",
);
console.log(" ✅ Deployer blocked ✓");
});
it("should prevent deployer from updating Registry", async function () {
console.log("\n🚫 Verifying deployer CANNOT update Registry...");
await expect(
upgradedRegistry.connect(deployer).updateCscaRoot("0x" + "44".repeat(32)),
).to.be.revertedWithCustomError(upgradedRegistry, "AccessControlUnauthorizedAccount");
console.log(" ✅ Deployer blocked ✓");
});
it("should prevent deployer from managing PCR0", async function () {
console.log("\n🚫 Verifying deployer CANNOT manage PCR0...");
await expect(pcr0Manager.connect(deployer).addPCR0("0x" + "55".repeat(48))).to.be.revertedWithCustomError(
pcr0Manager,
"AccessControlUnauthorizedAccount",
);
console.log(" ✅ Deployer blocked ✓");
});
it("should prevent ANY unauthorized user from operations", async function () {
console.log("\n🚫 Verifying unauthorized users blocked...");
await expect(upgradedHub.connect(user2).updateRegistry(ethers.ZeroAddress)).to.be.revertedWithCustomError(
upgradedHub,
"AccessControlUnauthorizedAccount",
);
await expect(
upgradedRegistry.connect(user2).updateCscaRoot("0x" + "66".repeat(32)),
).to.be.revertedWithCustomError(upgradedRegistry, "AccessControlUnauthorizedAccount");
await expect(pcr0Manager.connect(user2).addPCR0("0x" + "77".repeat(48))).to.be.revertedWithCustomError(
pcr0Manager,
"AccessControlUnauthorizedAccount",
);
console.log(" ✅ All unauthorized access blocked ✓");
});
});
describe("🎯 Phase 8: Final Functionality Verification", function () {
it("should verify HubV2 is fully functional with new governance", async function () {
console.log("\n🎯 Final HubV2 verification...");
const newRegistry = user2.address;
await upgradedHub.connect(securityMultisig).updateRegistry(newRegistry);
expect(await upgradedHub.getRegistry()).to.equal(newRegistry);
await upgradedHub.connect(securityMultisig).updateCircuitVersion(3);
expect(await upgradedHub.getCircuitVersion()).to.equal(3);
console.log(" ✅ HubV2 fully functional with multisig control");
});
it("should verify Registry is fully functional with new governance", async function () {
console.log("\n🎯 Final Registry verification...");
const finalRoot = "0x" + "99".repeat(32);
await upgradedRegistry.connect(operationsMultisig).updateCscaRoot(finalRoot);
expect(await upgradedRegistry.getCscaRoot()).to.equal(finalRoot);
console.log(" ✅ Registry fully functional with multisig control");
});
it("should verify PCR0Manager is fully functional with new governance", async function () {
console.log("\n🎯 Final PCR0Manager verification...");
const newPCR0 = "0x" + "88".repeat(48);
await pcr0Manager.connect(securityMultisig).addPCR0(newPCR0);
expect(await pcr0Manager.isPCR0Set(newPCR0)).to.be.true;
console.log(" ✅ PCR0Manager fully functional with multisig control");
});
});
describe("🎉 Phase 9: Success Summary", function () {
it("should print comprehensive upgrade success report", async function () {
console.log("\n" + "=".repeat(80));
console.log("🎉 PRODUCTION GOVERNANCE UPGRADE: 100% SUCCESSFUL");
console.log("=".repeat(80));
console.log("\n📋 Upgraded Contracts:");
console.log(` ✓ IdentityVerificationHubImplV2: ${await upgradedHub.getAddress()}`);
console.log(` ✓ IdentityRegistryImplV1 (Passport): ${await upgradedRegistry.getAddress()}`);
console.log(` ✓ IdentityRegistryIdCardImplV1: ${await idCardRegistryProxy.getAddress()}`);
console.log(` ✓ PCR0Manager: ${await pcr0Manager.getAddress()}`);
console.log("\n✅ Verification Checklist:");
console.log(" ✓ Upgraded from Ownable2StepUpgradeable to AccessControlUpgradeable");
console.log(" ✓ ALL production data preserved (zero data loss)");
console.log(" ✓ Proxy addresses unchanged (in-place upgrade)");
console.log(" ✓ Multi-tier governance active (Critical + Standard roles)");
console.log(" ✓ Roles transferred to multisigs");
console.log(" ✓ Deployer has ZERO control");
console.log(" ✓ Multisigs have full control");
console.log(" ✓ All contract functionality verified working");
console.log(" ✓ Access control properly enforced");
console.log(" ✓ Unauthorized access blocked");
console.log(" ✓ NO storage corruption");
console.log("\n🔑 Governance Configuration:");
console.log(` Critical Multisig (3/5): ${securityMultisig.address}`);
console.log(` Standard Multisig (2/5): ${operationsMultisig.address}`);
console.log(" Critical Role: Upgrades, critical parameters, role management");
console.log(" Standard Role: Standard operational parameters");
console.log("\n✅ PRODUCTION UPGRADE IS SAFE TO EXECUTE");
console.log("=".repeat(80) + "\n");
});
});
});

View File

@@ -1,454 +0,0 @@
import { expect } from "chai";
import { ethers, upgrades } from "hardhat";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import {
IdentityVerificationHubImplV2,
IdentityRegistryImplV1,
PCR0Manager,
VerifyAll,
MockImplRoot,
CustomVerifier,
PoseidonT3,
} from "../../typechain-types";
describe("Governance Upgrade Tests", function () {
let deployer: SignerWithAddress;
let securityMultisig: SignerWithAddress;
let operationsMultisig: SignerWithAddress;
let user: SignerWithAddress;
// Contract instances for testing
let hubProxy: IdentityVerificationHubImplV2;
let registryProxy: IdentityRegistryImplV1;
let pcr0Manager: PCR0Manager;
let verifyAll: VerifyAll;
let testProxy: MockImplRoot;
// Libraries
let customVerifier: CustomVerifier;
let poseidonT3: PoseidonT3;
// Test constants
const SECURITY_ROLE = ethers.keccak256(ethers.toUtf8Bytes("SECURITY_ROLE"));
const OPERATIONS_ROLE = ethers.keccak256(ethers.toUtf8Bytes("OPERATIONS_ROLE"));
const DEFAULT_ADMIN_ROLE = ethers.ZeroHash;
beforeEach(async function () {
// Set up test signers representing different roles in the governance system
[deployer, securityMultisig, operationsMultisig, user] = await ethers.getSigners();
// Deploy CustomVerifier library once for reuse across tests
const CustomVerifierFactory = await ethers.getContractFactory("CustomVerifier");
customVerifier = await CustomVerifierFactory.deploy();
await customVerifier.waitForDeployment();
// Deploy PoseidonT3 library once for reuse across tests
const PoseidonT3Factory = await ethers.getContractFactory("PoseidonT3");
poseidonT3 = await PoseidonT3Factory.deploy();
await poseidonT3.waitForDeployment();
});
describe("Hub Upgrade to Governance", function () {
beforeEach(async function () {
// Deploy initial hub implementation (V2 without governance)
// This simulates an existing deployed contract that needs to be upgraded
const IdentityVerificationHubV2 = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
// Deploy implementation and proxy manually to bypass OpenZeppelin validation
const implementation = await IdentityVerificationHubV2.deploy();
await implementation.waitForDeployment();
// Encode the initialize call (empty for now, will initialize after upgrade)
const initData = "0x"; // No initialization data
// Deploy proxy manually
const ProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
const proxy = await ProxyFactory.deploy(await implementation.getAddress(), initData);
await proxy.waitForDeployment();
// Attach the interface to the proxy
hubProxy = IdentityVerificationHubV2.attach(await proxy.getAddress()) as unknown as IdentityVerificationHubImplV2;
// Initialize the proxy with the old Ownable pattern (simulate existing deployment)
await hubProxy.initialize();
// Force import the proxy into OpenZeppelin's system for upgrade management
await upgrades.forceImport(await proxy.getAddress(), IdentityVerificationHubV2, {
kind: "uups",
});
});
it("should successfully upgrade hub to governance system", async function () {
// Test: Verify that we can upgrade from an existing Ownable contract to AccessControl governance
// This simulates upgrading a production contract to the new governance system
// Verify initial state - deployer should have roles after initialization
expect(await hubProxy.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
// Deploy new implementation with governance using the same library instance
const IdentityVerificationHubV3 = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
// Upgrade to governance system (no initialization call needed for upgrades)
const upgradedHub = await upgrades.upgradeProxy(await hubProxy.getAddress(), IdentityVerificationHubV3, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking"],
});
// After upgrade, the contract now has governance capabilities
const hubWithGovernance = upgradedHub as unknown as IdentityVerificationHubImplV2;
// For this test, we'll simulate that the migration script has already run
// In production, this would be done by a separate migration transaction
try {
await hubWithGovernance.grantRole(SECURITY_ROLE, deployer.address);
await hubWithGovernance.grantRole(OPERATIONS_ROLE, deployer.address);
// Verify governance roles are set correctly
expect(await hubWithGovernance.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await hubWithGovernance.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
// Verify role hierarchy (set up during __ImplRoot_init)
expect(await hubWithGovernance.getRoleAdmin(SECURITY_ROLE)).to.equal(SECURITY_ROLE);
expect(await hubWithGovernance.getRoleAdmin(OPERATIONS_ROLE)).to.equal(SECURITY_ROLE);
} catch (error) {
// If role setup fails, it might mean the roles are already set up or the contract doesn't support it yet
console.log("Role setup skipped:", (error as Error).message);
expect(true).to.be.true; // Pass the test - upgrade was successful
}
});
it("should validate upgrade safety", async function () {
// Test: Verify that the upgrade process validates storage layout compatibility
// This ensures that upgrading won't corrupt existing contract state
const IdentityVerificationHubV3 = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
// The upgrade should succeed without throwing storage layout errors
// OpenZeppelin's upgrades plugin validates storage compatibility automatically
const upgradedContract = await upgrades.upgradeProxy(await hubProxy.getAddress(), IdentityVerificationHubV3, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking"],
});
// Verify the upgrade was successful
expect(await upgradedContract.getAddress()).to.equal(await hubProxy.getAddress());
});
it("should preserve contract state after upgrade", async function () {
// Test: Verify that contract state (roles, storage variables) is preserved during upgrade
// This is critical for production upgrades to maintain existing permissions and data
// Verify initial state - check that roles are preserved
const initialHasCriticalRole = await hubProxy.hasRole(SECURITY_ROLE, deployer.address);
// Upgrade using the same library instance to avoid redeployment
const IdentityVerificationHubV3 = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
await upgrades.upgradeProxy(await hubProxy.getAddress(), IdentityVerificationHubV3, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking"],
});
// Verify state is preserved - roles should still exist
const finalHasCriticalRole = await hubProxy.hasRole(SECURITY_ROLE, deployer.address);
expect(finalHasCriticalRole).to.equal(initialHasCriticalRole);
});
});
describe("Registry Upgrade to Governance", function () {
beforeEach(async function () {
// Deploy initial registry implementation using the shared library instance
// This simulates upgrading an existing registry contract to governance
const IdentityRegistryV1 = await ethers.getContractFactory("IdentityRegistryImplV1", {
libraries: {
PoseidonT3: await poseidonT3.getAddress(),
},
});
// Deploy implementation and proxy manually to bypass OpenZeppelin validation
const implementation = await IdentityRegistryV1.deploy();
await implementation.waitForDeployment();
// Encode the initialize call
const initData = IdentityRegistryV1.interface.encodeFunctionData("initialize", [ethers.ZeroAddress]);
// Deploy proxy manually
const ProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
const proxy = await ProxyFactory.deploy(await implementation.getAddress(), initData);
await proxy.waitForDeployment();
// Attach the interface to the proxy
registryProxy = IdentityRegistryV1.attach(await proxy.getAddress()) as unknown as IdentityRegistryImplV1;
// Force import the proxy into OpenZeppelin's system for upgrade management
await upgrades.forceImport(await proxy.getAddress(), IdentityRegistryV1, {
kind: "uups",
});
});
it("should successfully upgrade registry to governance system", async function () {
// Test: Verify that the registry contract can be upgraded to use role-based governance
// This ensures the registry upgrade process works similarly to the hub upgrade
// Verify initial state - deployer should have roles after initialization
expect(await registryProxy.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
// Upgrade to governance using the shared library instance
const IdentityRegistryV2 = await ethers.getContractFactory("IdentityRegistryImplV1", {
libraries: {
PoseidonT3: await poseidonT3.getAddress(),
},
});
await upgrades.upgradeProxy(await registryProxy.getAddress(), IdentityRegistryV2, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking"],
});
// After upgrade, the contract now has governance capabilities
// For this test, we'll simulate that the migration script has already run
try {
await registryProxy.grantRole(SECURITY_ROLE, deployer.address);
await registryProxy.grantRole(OPERATIONS_ROLE, deployer.address);
// Verify governance roles are set correctly
expect(await registryProxy.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await registryProxy.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
} catch (error) {
// If role setup fails, it might mean the roles are already set up or the contract doesn't support it yet
console.log("Role setup skipped:", (error as Error).message);
expect(true).to.be.true; // Pass the test - upgrade was successful
}
});
});
describe("New Utility Contracts with Governance", function () {
beforeEach(async function () {
// Deploy new utility contracts that are designed with governance from the start
// These contracts use AccessControl instead of Ownable from deployment
// Deploy PCR0Manager with built-in governance
const PCR0Manager = await ethers.getContractFactory("PCR0Manager");
pcr0Manager = await PCR0Manager.deploy();
await pcr0Manager.waitForDeployment();
// Deploy VerifyAll with mock addresses for hub and registry
// In production, these would be real contract addresses
const mockHub = ethers.Wallet.createRandom().address;
const mockRegistry = ethers.Wallet.createRandom().address;
const VerifyAll = await ethers.getContractFactory("VerifyAll");
verifyAll = await VerifyAll.deploy(mockHub, mockRegistry);
await verifyAll.waitForDeployment();
});
it("should deploy PCR0Manager with deployer having initial roles", async function () {
// Test: Verify that PCR0Manager is deployed with the deployer having both governance roles
// This follows the pattern where deployer gets initial control before transferring to multisigs
expect(await pcr0Manager.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await pcr0Manager.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
});
it("should deploy VerifyAll with deployer having initial roles", async function () {
// Test: Verify that VerifyAll is deployed with the deployer having both governance roles
// This ensures consistent role initialization across all governance contracts
expect(await verifyAll.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await verifyAll.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
});
it("should allow role transfer and then critical multisig to manage PCR0", async function () {
// Test: Verify the complete workflow of transferring roles and using them for PCR0 management
// This simulates the production process of deploying, transferring roles, and operating the contract
// First transfer roles to multisigs (simulating production deployment workflow)
await pcr0Manager.connect(deployer).grantRole(SECURITY_ROLE, securityMultisig.address);
await pcr0Manager.connect(deployer).grantRole(OPERATIONS_ROLE, operationsMultisig.address);
const testPCR0 = "0x" + "00".repeat(48); // 48 zero bytes (valid PCR0 format)
// Critical multisig should be able to add PCR0 (testing governance functionality)
await expect(pcr0Manager.connect(securityMultisig).addPCR0(testPCR0)).to.emit(pcr0Manager, "PCR0Added");
// Verify PCR0 was added successfully
expect(await pcr0Manager.isPCR0Set(testPCR0)).to.be.true;
// Critical multisig should be able to remove PCR0 (testing full CRUD operations)
await expect(pcr0Manager.connect(securityMultisig).removePCR0(testPCR0)).to.emit(pcr0Manager, "PCR0Removed");
// Verify PCR0 was removed successfully
expect(await pcr0Manager.isPCR0Set(testPCR0)).to.be.false;
});
it("should prevent unauthorized access to PCR0 functions", async function () {
// Test: Verify that access control is properly enforced for PCR0 management functions
// This ensures that only authorized roles can modify the PCR0 registry
const testPCR0 = "0x" + "00".repeat(48);
// Random user should not be able to add PCR0 (testing access control enforcement)
await expect(pcr0Manager.connect(user).addPCR0(testPCR0)).to.be.revertedWithCustomError(
pcr0Manager,
"AccessControlUnauthorizedAccount",
);
});
it("should allow role transfer and then critical multisig to update VerifyAll addresses", async function () {
// Test: Verify that VerifyAll contract addresses can be updated by the critical multisig
// This tests the governance of contract dependencies and configuration updates
// First transfer roles to multisigs (following production deployment pattern)
await verifyAll.connect(deployer).grantRole(SECURITY_ROLE, securityMultisig.address);
await verifyAll.connect(deployer).grantRole(OPERATIONS_ROLE, operationsMultisig.address);
// Generate new addresses for testing (simulating contract upgrades or migrations)
const newHubAddress = ethers.Wallet.createRandom().address;
const newRegistryAddress = ethers.Wallet.createRandom().address;
// Critical multisig should be able to update hub address
await expect(verifyAll.connect(securityMultisig).setHub(newHubAddress)).to.not.be.reverted;
// Critical multisig should be able to update registry address
await expect(verifyAll.connect(securityMultisig).setRegistry(newRegistryAddress)).to.not.be.reverted;
// Verify addresses were updated correctly
expect(await verifyAll.hub()).to.equal(newHubAddress);
expect(await verifyAll.registry()).to.equal(newRegistryAddress);
});
it("should prevent unauthorized access to VerifyAll functions", async function () {
// Test: Verify that VerifyAll access control prevents unauthorized configuration changes
// This ensures that only critical multisig can modify contract dependencies
const newHubAddress = ethers.Wallet.createRandom().address;
// Random user should not be able to update hub address (testing access control)
await expect(verifyAll.connect(user).setHub(newHubAddress)).to.be.revertedWithCustomError(
verifyAll,
"AccessControlUnauthorizedAccount",
);
});
});
describe("Role Management", function () {
beforeEach(async function () {
// Deploy a fresh PCR0Manager for role management testing
// This ensures clean state for testing role hierarchy and permissions
const PCR0Manager = await ethers.getContractFactory("PCR0Manager");
pcr0Manager = await PCR0Manager.deploy();
await pcr0Manager.waitForDeployment();
// Grant roles to multisigs (deployer has initial roles from constructor)
await pcr0Manager.connect(deployer).grantRole(SECURITY_ROLE, securityMultisig.address);
await pcr0Manager.connect(deployer).grantRole(OPERATIONS_ROLE, operationsMultisig.address);
});
it("should allow critical multisig to manage roles", async function () {
// Test: Verify that SECURITY_ROLE can manage other roles (role hierarchy)
// This tests the admin functionality where critical multisig manages all roles
const newStandardUser = user.address;
// Critical multisig (admin) should be able to grant standard role
await expect(pcr0Manager.connect(securityMultisig).grantRole(OPERATIONS_ROLE, newStandardUser)).to.not.be
.reverted;
// Verify role was granted successfully
expect(await pcr0Manager.hasRole(OPERATIONS_ROLE, newStandardUser)).to.be.true;
// Critical multisig should be able to revoke role (testing full role management)
await expect(pcr0Manager.connect(securityMultisig).revokeRole(OPERATIONS_ROLE, newStandardUser)).to.not.be
.reverted;
// Verify role was revoked successfully
expect(await pcr0Manager.hasRole(OPERATIONS_ROLE, newStandardUser)).to.be.false;
});
it("should prevent non-admin from managing roles", async function () {
// Test: Verify that only SECURITY_ROLE can manage roles (enforce role hierarchy)
// This ensures that standard multisig and regular users cannot escalate privileges
// Standard multisig should not be able to grant roles (lacks admin privileges)
await expect(
pcr0Manager.connect(operationsMultisig).grantRole(OPERATIONS_ROLE, user.address),
).to.be.revertedWithCustomError(pcr0Manager, "AccessControlUnauthorizedAccount");
// Random user should not be able to grant roles (no privileges at all)
await expect(pcr0Manager.connect(user).grantRole(OPERATIONS_ROLE, user.address)).to.be.revertedWithCustomError(
pcr0Manager,
"AccessControlUnauthorizedAccount",
);
});
});
describe("Upgrade Authorization", function () {
beforeEach(async function () {
// Deploy MockImplRoot contract for testing upgrade authorization
// This contract inherits from ImplRoot and exposes the upgrade functionality for testing
const TestContract = await ethers.getContractFactory("MockImplRoot");
testProxy = await upgrades.deployProxy(TestContract, [], {
kind: "uups",
initializer: "exposed__ImplRoot_init()",
});
await testProxy.waitForDeployment();
});
it("should allow critical multisig to authorize upgrades", async function () {
// Test: Verify that SECURITY_ROLE can authorize contract upgrades
// This is essential for secure upgrade governance in production
// Grant SECURITY_ROLE to securityMultisig for this test
await testProxy.connect(deployer).grantRole(SECURITY_ROLE, securityMultisig.address);
// Deploy new implementation for upgrade testing
const NewImplementation = await ethers.getContractFactory("MockImplRoot");
// The upgrade should succeed when called by critical multisig
const upgradeTx = await upgrades.upgradeProxy(await testProxy.getAddress(), NewImplementation, {
kind: "uups",
});
await upgradeTx.waitForDeployment();
expect(await upgradeTx.getAddress()).to.equal(await testProxy.getAddress());
});
it("should prevent non-critical roles from authorizing upgrades", async function () {
// Test: Verify that only SECURITY_ROLE can authorize upgrades
// This prevents unauthorized upgrades by standard multisig or regular users
// Grant OPERATIONS_ROLE to operationsMultisig (but not SECURITY_ROLE)
await testProxy.connect(deployer).grantRole(OPERATIONS_ROLE, operationsMultisig.address);
// The upgrade should fail when attempted without SECURITY_ROLE
// This tests the _authorizeUpgrade function's access control directly
await expect(
testProxy.connect(operationsMultisig).exposed_authorizeUpgrade(ethers.ZeroAddress),
).to.be.revertedWithCustomError(testProxy, "AccessControlUnauthorizedAccount");
});
});
});

View File

@@ -1,267 +0,0 @@
import { expect } from "chai";
import { ethers, upgrades } from "hardhat";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { MockOwnableHub, MockUpgradedHub, MockOwnableRegistry } from "../../typechain-types";
/**
* ERC-7201 Namespaced Storage Upgrade Tests
*
* These tests demonstrate OpenZeppelin's storage validation for upgrades from
* Ownable2StepUpgradeable to AccessControlUpgradeable.
*
* Key insights:
* 1. OpenZeppelin v5+ uses ERC-7201 namespaced storage (calculated hash locations)
* 2. Storage validation correctly detects namespace changes during upgrades
* 3. For production: The real contracts already use AccessControlUpgradeable (ImplRoot)
* 4. This test shows what would happen if upgrading from old Ownable contracts
*
* Production Reality:
* - Current contracts already inherit from ImplRoot (AccessControlUpgradeable)
* - No actual Ownable → AccessControl upgrade needed in production
* - This test validates OpenZeppelin's safety mechanisms work correctly
*/
describe("ERC-7201 Namespaced Storage Upgrade Tests", function () {
let deployer: SignerWithAddress;
let securityMultisig: SignerWithAddress;
let operationsMultisig: SignerWithAddress;
let user: SignerWithAddress;
// Test constants
const SECURITY_ROLE = ethers.keccak256(ethers.toUtf8Bytes("SECURITY_ROLE"));
const OPERATIONS_ROLE = ethers.keccak256(ethers.toUtf8Bytes("OPERATIONS_ROLE"));
beforeEach(async function () {
[deployer, securityMultisig, operationsMultisig, user] = await ethers.getSigners();
});
describe("Ownable to AccessControl Upgrade (Storage Validation Demo)", function () {
let ownableHubProxy: MockOwnableHub;
let upgradedHub: MockUpgradedHub;
beforeEach(async function () {
// Step 1: Deploy the OLD Ownable-based Hub (simulates production state)
// OpenZeppelin v5+ uses ERC-7201 namespaced storage, making this upgrade inherently safe
console.log("📦 Deploying OLD Ownable Hub (simulating production)...");
const MockOwnableHubFactory = await ethers.getContractFactory("MockOwnableHub");
// Deploy as upgradeable proxy using OpenZeppelin
ownableHubProxy = (await upgrades.deployProxy(MockOwnableHubFactory, [], {
kind: "uups",
initializer: "initialize",
unsafeAllow: ["constructor", "state-variable-immutable", "state-variable-assignment"],
})) as unknown as MockOwnableHub;
await ownableHubProxy.waitForDeployment();
console.log(` ✅ OLD Hub deployed at: ${await ownableHubProxy.getAddress()}`);
// Verify initial state
const owner = await ownableHubProxy.owner();
expect(owner).to.equal(deployer.address);
console.log(` ✅ Initial owner: ${owner}`);
// Set some state to verify it's preserved
await ownableHubProxy.updateRegistry(user.address);
expect(await ownableHubProxy.getRegistry()).to.equal(user.address);
console.log(` ✅ Initial registry set: ${user.address}`);
});
it("should demonstrate storage validation for Ownable to AccessControl upgrade", async function () {
console.log("\n🔄 Testing Ownable → AccessControl upgrade (demonstrates storage validation)...");
// Step 2: Upgrade to the NEW AccessControl-based Hub
const MockUpgradedHubFactory = await ethers.getContractFactory("MockUpgradedHub");
console.log(" 📦 Deploying NEW AccessControl implementation...");
// Perform the upgrade
upgradedHub = (await upgrades.upgradeProxy(await ownableHubProxy.getAddress(), MockUpgradedHubFactory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for test: simulates Ownable→AccessControl upgrade
unsafeAllow: [
"constructor",
"state-variable-immutable",
"state-variable-assignment",
"missing-public-upgradeto",
"missing-initializer",
],
})) as unknown as MockUpgradedHub;
console.log(` ✅ Upgrade completed to: ${await upgradedHub.getAddress()}`);
// Step 3: Initialize governance (this sets up AccessControl)
console.log(" 🔧 Initializing governance...");
await upgradedHub.initialize();
console.log(" ✅ Governance initialized");
// Step 4: Verify storage preservation
console.log("\n🔍 Verifying storage preservation...");
// Check that old state is preserved
const preservedRegistry = await upgradedHub.getRegistry();
expect(preservedRegistry).to.equal(user.address);
console.log(` ✅ Registry preserved: ${preservedRegistry}`);
// Check that new governance is working
expect(await upgradedHub.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await upgradedHub.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
console.log(" ✅ New governance roles active");
// Verify the upgrade worked (unsafeSkipStorageCheck allows this test scenario)
// In production, this upgrade path would require careful namespace management
console.log(" ✅ Test upgrade completed (with storage validation bypassed)");
});
it("should allow governance functions to work after upgrade", async function () {
// Perform the upgrade first
const MockUpgradedHubFactory = await ethers.getContractFactory("MockUpgradedHub");
upgradedHub = (await upgrades.upgradeProxy(await ownableHubProxy.getAddress(), MockUpgradedHubFactory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for test: simulates Ownable→AccessControl upgrade
unsafeAllow: [
"constructor",
"state-variable-immutable",
"state-variable-assignment",
"missing-public-upgradeto",
"missing-initializer",
],
})) as unknown as MockUpgradedHub;
await upgradedHub.initialize();
// Test that governance functions work
const newRegistry = securityMultisig.address;
await upgradedHub.updateRegistry(newRegistry);
expect(await upgradedHub.getRegistry()).to.equal(newRegistry);
// Test that circuit version can be updated
await upgradedHub.updateCircuitVersion(2);
expect(await upgradedHub.getCircuitVersion()).to.equal(2);
});
it("should prevent unauthorized access after upgrade", async function () {
// Perform the upgrade first
const MockUpgradedHubFactory = await ethers.getContractFactory("MockUpgradedHub");
upgradedHub = (await upgrades.upgradeProxy(await ownableHubProxy.getAddress(), MockUpgradedHubFactory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for test: simulates Ownable→AccessControl upgrade
unsafeAllow: [
"constructor",
"state-variable-immutable",
"state-variable-assignment",
"missing-public-upgradeto",
"missing-initializer",
],
})) as unknown as MockUpgradedHub;
await upgradedHub.initialize();
// Test that unauthorized users cannot call governance functions
await expect(upgradedHub.connect(user).updateRegistry(user.address)).to.be.revertedWithCustomError(
upgradedHub,
"AccessControlUnauthorizedAccount",
);
await expect(upgradedHub.connect(user).updateCircuitVersion(3)).to.be.revertedWithCustomError(
upgradedHub,
"AccessControlUnauthorizedAccount",
);
});
it("should allow role transfer to multisigs", async function () {
// Perform the upgrade first
const MockUpgradedHubFactory = await ethers.getContractFactory("MockUpgradedHub");
upgradedHub = (await upgrades.upgradeProxy(await ownableHubProxy.getAddress(), MockUpgradedHubFactory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for test: simulates Ownable→AccessControl upgrade
unsafeAllow: [
"constructor",
"state-variable-immutable",
"state-variable-assignment",
"missing-public-upgradeto",
"missing-initializer",
],
})) as unknown as MockUpgradedHub;
await upgradedHub.initialize();
// Transfer roles to multisigs
await upgradedHub.grantRole(SECURITY_ROLE, securityMultisig.address);
await upgradedHub.grantRole(OPERATIONS_ROLE, operationsMultisig.address);
// Verify multisigs have roles
expect(await upgradedHub.hasRole(SECURITY_ROLE, securityMultisig.address)).to.be.true;
expect(await upgradedHub.hasRole(OPERATIONS_ROLE, operationsMultisig.address)).to.be.true;
// Test that multisig can perform governance functions
await upgradedHub.connect(securityMultisig).updateRegistry(securityMultisig.address);
expect(await upgradedHub.getRegistry()).to.equal(securityMultisig.address);
// Renounce deployer roles
await upgradedHub.renounceRole(SECURITY_ROLE, deployer.address);
await upgradedHub.renounceRole(OPERATIONS_ROLE, deployer.address);
// Verify deployer no longer has roles
expect(await upgradedHub.hasRole(SECURITY_ROLE, deployer.address)).to.be.false;
expect(await upgradedHub.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.false;
});
});
describe("Storage Validation Analysis", function () {
it("should demonstrate OpenZeppelin's storage validation mechanisms", async function () {
console.log("\n📊 OpenZeppelin Storage Validation Analysis");
console.log("This test shows how OpenZeppelin detects storage layout changes during upgrades");
// Deploy Ownable version
const MockOwnableHubFactory = await ethers.getContractFactory("MockOwnableHub");
const ownableHub = (await upgrades.deployProxy(MockOwnableHubFactory, [], {
kind: "uups",
initializer: "initialize",
unsafeAllow: ["constructor", "state-variable-immutable", "state-variable-assignment"],
})) as unknown as MockOwnableHub;
await ownableHub.waitForDeployment();
// Set some state
await ownableHub.updateRegistry(user.address);
console.log("📋 BEFORE UPGRADE:");
console.log(` Owner (namespaced): ${await ownableHub.owner()}`);
console.log(` Registry: ${await ownableHub.getRegistry()}`);
console.log(` Circuit Version: ${await ownableHub.getCircuitVersion()}`);
// Upgrade
const MockUpgradedHubFactory = await ethers.getContractFactory("MockUpgradedHub");
const upgradedHub = (await upgrades.upgradeProxy(await ownableHub.getAddress(), MockUpgradedHubFactory, {
kind: "uups",
unsafeSkipStorageCheck: true, // Required for test: simulates Ownable→AccessControl upgrade
unsafeAllow: [
"constructor",
"state-variable-immutable",
"state-variable-assignment",
"missing-public-upgradeto",
"missing-initializer",
],
})) as unknown as MockUpgradedHub;
await upgradedHub.initialize();
console.log("\n📋 AFTER UPGRADE:");
console.log(` Registry (preserved): ${await upgradedHub.getRegistry()}`);
console.log(` Circuit Version (preserved): ${await upgradedHub.getCircuitVersion()}`);
console.log(` Has SECURITY_ROLE: ${await upgradedHub.hasRole(SECURITY_ROLE, deployer.address)}`);
console.log(` Has OPERATIONS_ROLE: ${await upgradedHub.hasRole(OPERATIONS_ROLE, deployer.address)}`);
// Verify storage preservation - application state is preserved
expect(await upgradedHub.getRegistry()).to.equal(user.address);
expect(await upgradedHub.getCircuitVersion()).to.equal(1);
console.log("\n🎯 Key Insights:");
console.log(" • OpenZeppelin detected namespace deletion during upgrade");
console.log(" • ERC-7201 storage prevents collisions but requires namespace management");
console.log(" • Production contracts already use AccessControlUpgradeable (ImplRoot)");
console.log(" • This test validates OpenZeppelin's safety mechanisms work correctly");
});
});
});

View File

@@ -1,229 +0,0 @@
import { expect } from "chai";
import { ethers, upgrades } from "hardhat";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
describe("Upgrade Safety Validation Tests", function () {
let deployer: SignerWithAddress;
let securityMultisig: SignerWithAddress;
let operationsMultisig: SignerWithAddress;
beforeEach(async function () {
[deployer, securityMultisig, operationsMultisig] = await ethers.getSigners();
});
describe("Storage Layout Validation", function () {
it("should validate storage layout compatibility using OpenZeppelin", async function () {
// Deploy CustomVerifier library
const CustomVerifier = await ethers.getContractFactory("CustomVerifier");
const customVerifier = await CustomVerifier.deploy();
await customVerifier.waitForDeployment();
// Test IdentityVerificationHub storage layout validation
const IdentityVerificationHub = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
// OpenZeppelin's validateImplementation should pass for our contracts
await expect(
upgrades.validateImplementation(IdentityVerificationHub, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeAllow: ["constructor", "external-library-linking"],
}),
).to.not.be.reverted;
// Deploy PoseidonT3 library for IdentityRegistry
const PoseidonT3 = await ethers.getContractFactory("PoseidonT3");
const poseidonT3 = await PoseidonT3.deploy();
await poseidonT3.waitForDeployment();
// Test IdentityRegistry storage layout validation
const IdentityRegistry = await ethers.getContractFactory("IdentityRegistryImplV1", {
libraries: {
PoseidonT3: await poseidonT3.getAddress(),
},
});
await expect(
upgrades.validateImplementation(IdentityRegistry, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeAllow: ["constructor", "external-library-linking"],
}),
).to.not.be.reverted;
});
});
describe("Implementation Validation", function () {
it("should validate PCR0Manager implementation", async function () {
const PCR0Manager = await ethers.getContractFactory("PCR0Manager");
// PCR0Manager is not upgradeable, but we can still validate it's safe
await expect(PCR0Manager.deploy()).to.not.be.reverted;
});
it("should validate VerifyAll implementation", async function () {
const VerifyAll = await ethers.getContractFactory("VerifyAll");
const mockHub = ethers.Wallet.createRandom().address;
const mockRegistry = ethers.Wallet.createRandom().address;
// VerifyAll is not upgradeable, but we can still validate deployment
await expect(VerifyAll.deploy(mockHub, mockRegistry)).to.not.be.reverted;
});
});
describe("Library Compatibility", function () {
it("should validate CustomVerifier library is upgrade-safe", async function () {
const CustomVerifier = await ethers.getContractFactory("CustomVerifier");
// Libraries should deploy without issues
await expect(CustomVerifier.deploy()).to.not.be.reverted;
});
it("should validate library linking in upgraded contracts", async function () {
// Deploy library
const CustomVerifier = await ethers.getContractFactory("CustomVerifier");
const customVerifier = await CustomVerifier.deploy();
await customVerifier.waitForDeployment();
// Deploy contract with library
const IdentityVerificationHub = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
// Should deploy successfully with library linking
const proxy = await upgrades.deployProxy(IdentityVerificationHub, [], {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeAllowConstructors: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking", "storage-check"],
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
await expect(proxy.waitForDeployment()).to.not.be.reverted;
});
});
describe("Initialization Safety", function () {
it("should validate governance initialization is safe", async function () {
const PCR0Manager = await ethers.getContractFactory("PCR0Manager");
// Should initialize with valid addresses
const pcr0Manager = await PCR0Manager.deploy();
await pcr0Manager.waitForDeployment();
// Verify initialization worked correctly
const DEFAULT_ADMIN_ROLE = ethers.ZeroHash;
const OPERATIONS_ROLE = ethers.keccak256(ethers.toUtf8Bytes("OPERATIONS_ROLE"));
// PCR0Manager now grants initial roles to deployer
const SECURITY_ROLE = ethers.keccak256(ethers.toUtf8Bytes("SECURITY_ROLE"));
expect(await pcr0Manager.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
expect(await pcr0Manager.hasRole(OPERATIONS_ROLE, deployer.address)).to.be.true;
});
it("should prevent initialization with zero addresses", async function () {
const PCR0Manager = await ethers.getContractFactory("PCR0Manager");
// PCR0Manager no longer takes constructor arguments, so this test is no longer relevant
// The contract now grants initial roles to msg.sender (deployer)
const pcr0Manager = await PCR0Manager.deploy();
await pcr0Manager.waitForDeployment();
// Verify deployer has initial roles
const SECURITY_ROLE = ethers.keccak256(ethers.toUtf8Bytes("SECURITY_ROLE"));
expect(await pcr0Manager.hasRole(SECURITY_ROLE, deployer.address)).to.be.true;
});
});
describe("Proxy Compatibility", function () {
it("should validate UUPS proxy compatibility", async function () {
// Deploy a test contract that inherits from ImplRoot
const TestContract = await ethers.getContractFactory("MockImplRoot");
// Should deploy as UUPS proxy successfully
const proxy = await upgrades.deployProxy(TestContract, [], {
kind: "uups",
initializer: "exposed__ImplRoot_init()",
unsafeAllowConstructors: true,
unsafeSkipStorageCheck: true,
});
await expect(proxy.waitForDeployment()).to.not.be.reverted;
});
it("should validate proxy admin functions work correctly", async function () {
const TestContract = await ethers.getContractFactory("MockImplRoot");
const proxy = await upgrades.deployProxy(TestContract, [], {
kind: "uups",
initializer: "exposed__ImplRoot_init()",
unsafeAllowConstructors: true,
unsafeSkipStorageCheck: true,
});
await proxy.waitForDeployment();
// Verify proxy admin functions are accessible
const proxyAddress = await proxy.getAddress();
expect(proxyAddress).to.not.equal(ethers.ZeroAddress);
});
});
describe("Gas Usage Validation", function () {
it("should validate upgrade gas costs are reasonable", async function () {
// Deploy initial implementation
const CustomVerifier = await ethers.getContractFactory("CustomVerifier");
const customVerifier = await CustomVerifier.deploy();
await customVerifier.waitForDeployment();
const IdentityVerificationHub = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
const proxy = await upgrades.deployProxy(IdentityVerificationHub, [], {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeAllowConstructors: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking", "storage-check"],
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
await proxy.waitForDeployment();
// Upgrade and measure gas
const NewImplementation = await ethers.getContractFactory("IdentityVerificationHubImplV2", {
libraries: {
CustomVerifier: await customVerifier.getAddress(),
},
});
const upgradeTx = await upgrades.upgradeProxy(await proxy.getAddress(), NewImplementation, {
kind: "uups",
unsafeAllowLinkedLibraries: true,
unsafeAllowConstructors: true,
unsafeSkipStorageCheck: true,
unsafeAllow: ["constructor", "external-library-linking", "storage-check"],
});
const receipt = await upgradeTx.deploymentTransaction()?.wait();
// Verify gas usage is reasonable (adjust threshold as needed)
if (receipt) {
expect(receipt.gasUsed).to.be.lessThan(ethers.parseUnits("5000000", "wei")); // 5M gas limit
}
});
});
});

View File

@@ -2,27 +2,55 @@ import { expect } from "chai";
import { BigNumberish, TransactionReceipt } from "ethers";
import { ethers } from "hardhat";
import { poseidon2 } from "poseidon-lite";
import { createHash } from "crypto";
import { CIRCUIT_CONSTANTS, DscVerifierId, RegisterVerifierId } from "@selfxyz/common/constants/constants";
import { formatCountriesList, reverseBytes } from "@selfxyz/common/utils/circuits/formatInputs";
import { castFromScope } from "@selfxyz/common/utils/circuits/uuid";
import { ATTESTATION_ID } from "../utils/constants";
import { deploySystemFixtures } from "../utils/deployment";
import { deploySystemFixturesV2 } from "../utils/deploymentV2";
import BalanceTree from "../utils/example/balance-tree";
import { Formatter } from "../utils/formatter";
import { generateDscProof, generateRegisterProof, generateVcAndDiscloseProof } from "../utils/generateProof";
import { LeanIMT } from "@openpassport/zk-kit-lean-imt";
import serialized_dsc_tree from "../../../common/pubkeys/serialized_dsc_tree.json";
import { DeployedActors, VcAndDiscloseHubProof } from "../utils/types";
import { DeployedActorsV2 } from "../utils/types";
import { generateRandomFieldElement, splitHexFromBack } from "../utils/utils";
// Helper function to calculate user identifier hash
function calculateUserIdentifierHash(userContextData: string): string {
const sha256Hash = createHash("sha256")
.update(Buffer.from(userContextData.slice(2), "hex"))
.digest();
const ripemdHash = createHash("ripemd160").update(sha256Hash).digest();
return "0x" + ripemdHash.toString("hex").padStart(40, "0");
}
// Helper function to create V2 proof data format for verifySelfProof
function createV2ProofData(proof: any, userAddress: string, userData: string = "airdrop-user-data") {
const destChainId = ethers.zeroPadValue(ethers.toBeHex(31337), 32);
const userContextData = ethers.solidityPacked(
["bytes32", "bytes32", "bytes"],
[destChainId, ethers.zeroPadValue(userAddress, 32), ethers.toUtf8Bytes(userData)],
);
const attestationId = ethers.zeroPadValue(ethers.toBeHex(BigInt(ATTESTATION_ID.E_PASSPORT)), 32);
const encodedProof = ethers.AbiCoder.defaultAbiCoder().encode(
["tuple(uint256[2] a, uint256[2][2] b, uint256[2] c, uint256[] pubSignals)"],
[[proof.a, proof.b, proof.c, proof.pubSignals]],
);
const proofData = ethers.solidityPacked(["bytes32", "bytes"], [attestationId, encodedProof]);
return { proofData, userContextData };
}
describe("End to End Tests", function () {
this.timeout(0);
let deployedActors: DeployedActors;
let deployedActors: DeployedActorsV2;
let snapshotId: string;
before(async () => {
deployedActors = await deploySystemFixtures();
deployedActors = await deploySystemFixturesV2();
snapshotId = await ethers.provider.send("evm_snapshot", []);
});
@@ -32,7 +60,10 @@ describe("End to End Tests", function () {
});
it("register dsc key commitment, register identity commitment, verify commitment and disclose attrs and claim airdrop", async () => {
const { hub, registry, mockPassport, owner, user1 } = deployedActors;
const { hub, registry, mockPassport, owner, user1, testSelfVerificationRoot, poseidonT3 } = deployedActors;
// V2 hub requires attestationId as bytes32
const attestationIdBytes32 = ethers.zeroPadValue(ethers.toBeHex(BigInt(ATTESTATION_ID.E_PASSPORT)), 32);
// register dsc key
// To increase test performance, we will just set one dsc key with groth16 proof
@@ -45,7 +76,11 @@ describe("End to End Tests", function () {
if (BigInt(dscKeys[0][i]) == dscProof.pubSignals[CIRCUIT_CONSTANTS.DSC_TREE_LEAF_INDEX]) {
const previousRoot = await registry.getDscKeyCommitmentMerkleRoot();
const previousSize = await registry.getDscKeyCommitmentTreeSize();
registerDscTx = await hub.registerDscKeyCommitment(DscVerifierId.dsc_sha256_rsa_65537_4096, dscProof);
registerDscTx = await hub.registerDscKeyCommitment(
attestationIdBytes32,
DscVerifierId.dsc_sha256_rsa_65537_4096,
dscProof,
);
const receipt = (await registerDscTx.wait()) as TransactionReceipt;
const event = receipt?.logs.find(
(log) => log.topics[0] === registry.interface.getEvent("DscKeyCommitmentRegistered").topicHash,
@@ -90,7 +125,8 @@ describe("End to End Tests", function () {
const imt = new LeanIMT<bigint>(hashFunction);
await imt.insert(BigInt(registerProof.pubSignals[CIRCUIT_CONSTANTS.REGISTER_COMMITMENT_INDEX]));
const tx = await hub.registerPassportCommitment(
const tx = await hub.registerCommitment(
attestationIdBytes32,
RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096,
registerProof,
);
@@ -104,7 +140,7 @@ describe("End to End Tests", function () {
registerProof.pubSignals[CIRCUIT_CONSTANTS.REGISTER_COMMITMENT_INDEX],
);
const identityNullifier = await registry.nullifiers(
ATTESTATION_ID.E_PASSPORT,
attestationIdBytes32,
registerProof.pubSignals[CIRCUIT_CONSTANTS.REGISTER_NULLIFIER_INDEX],
);
@@ -134,11 +170,25 @@ describe("End to End Tests", function () {
reverseBytes(Formatter.bytesToHexString(new Uint8Array(formatCountriesList(forbiddenCountriesList)))),
);
// Get the scope from testSelfVerificationRoot
const testRootScope = await testSelfVerificationRoot.scope();
// Calculate user identifier hash for verification
const destChainId = ethers.zeroPadValue(ethers.toBeHex(31337), 32);
const user1Address = await user1.getAddress();
const userData = ethers.toUtf8Bytes("test-user-data");
const tempUserContextData = ethers.solidityPacked(
["bytes32", "bytes32", "bytes"],
[destChainId, ethers.zeroPadValue(user1Address, 32), userData],
);
const userIdentifierHash = calculateUserIdentifierHash(tempUserContextData);
// Generate proof for V2 verification
const vcAndDiscloseProof = await generateVcAndDiscloseProof(
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
mockPassport,
"test-scope",
testRootScope.toString(),
new Array(88).fill("1"),
"1",
imt,
@@ -148,59 +198,114 @@ describe("End to End Tests", function () {
undefined,
undefined,
forbiddenCountriesList,
(await user1.getAddress()).slice(2),
userIdentifierHash,
);
const vcAndDiscloseHubProof: VcAndDiscloseHubProof = {
// Set up verification config for testSelfVerificationRoot
const verificationConfigV2 = {
olderThanEnabled: true,
olderThan: "20",
forbiddenCountriesEnabled: true,
forbiddenCountriesListPacked: countriesListPacked,
forbiddenCountriesListPacked: countriesListPacked as [BigNumberish, BigNumberish, BigNumberish, BigNumberish],
ofacEnabled: [true, true, true] as [boolean, boolean, boolean],
vcAndDiscloseProof: vcAndDiscloseProof,
};
const result = await hub.verifyVcAndDisclose(vcAndDiscloseHubProof);
await testSelfVerificationRoot.setVerificationConfig(verificationConfigV2);
expect(result.identityCommitmentRoot).to.equal(
vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_MERKLE_ROOT_INDEX],
// Create V2 proof format and verify via testSelfVerificationRoot
const { proofData, userContextData: verifyUserContextData } = createV2ProofData(
vcAndDiscloseProof,
user1Address,
"test-user-data",
);
expect(result.revealedDataPacked).to.have.lengthOf(3);
expect(result.nullifier).to.equal(vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_NULLIFIER_INDEX]);
expect(result.attestationId).to.equal(
vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX],
// Reset test state before verification
await testSelfVerificationRoot.resetTestState();
// Verify the proof through V2 architecture
await testSelfVerificationRoot.connect(user1).verifySelfProof(proofData, verifyUserContextData);
// Check verification was successful
expect(await testSelfVerificationRoot.verificationSuccessful()).to.equal(true);
// Get the verification output and verify it
const lastOutput = await testSelfVerificationRoot.lastOutput();
expect(lastOutput).to.not.equal("0x");
// Verify attestationId matches both the expected bytes32 and the proof pubSignals
expect(lastOutput.attestationId).to.equal(attestationIdBytes32);
expect(lastOutput.attestationId).to.equal(
ethers.zeroPadValue(
ethers.toBeHex(vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]),
32,
),
);
expect(result.userIdentifier).to.equal(
vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX],
// Verify nullifier matches the proof pubSignals
expect(lastOutput.nullifier).to.equal(
vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_NULLIFIER_INDEX],
);
expect(result.scope).to.equal(vcAndDiscloseProof.pubSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_SCOPE_INDEX]);
for (let i = 0; i < 4; i++) {
expect(result.forbiddenCountriesListPacked[i]).to.equal(BigInt(countriesListPacked[i]));
}
// Verify userIdentifier is set
expect(lastOutput.userIdentifier).to.not.equal(0n);
// Verify olderThan value
expect(lastOutput.olderThan).to.equal(20n);
const tokenFactory = await ethers.getContractFactory("AirdropToken");
const token = await tokenFactory.connect(owner).deploy();
await token.waitForDeployment();
const airdropFactory = await ethers.getContractFactory("Airdrop");
const airdrop = await airdropFactory.connect(owner).deploy(
hub.target,
castFromScope("test-scope"),
ATTESTATION_ID.E_PASSPORT,
token.target,
true,
20,
// @ts-expect-error
true,
countriesListPacked as [BigNumberish, BigNumberish, BigNumberish, BigNumberish],
[true, true, true],
);
const airdrop = await airdropFactory.connect(owner).deploy(hub.target, "test-scope", token.target);
await airdrop.waitForDeployment();
// Set up verification config for the airdrop
const configTx = await hub.connect(owner).setVerificationConfigV2(verificationConfigV2);
const configReceipt = await configTx.wait();
const configId = configReceipt!.logs[0].topics[1];
// Set the config ID in the airdrop contract
await airdrop.connect(owner).setConfigId(configId);
await token.connect(owner).mint(airdrop.target, BigInt(1000000000000000000));
// Generate proof with the airdrop's actual scope
const airdropScope = await airdrop.scope();
// Calculate the user identifier hash for the airdrop proof
const airdropUserData = ethers.toUtf8Bytes("airdrop-user-data");
const airdropTempUserContextData = ethers.solidityPacked(
["bytes32", "bytes32", "bytes"],
[destChainId, ethers.zeroPadValue(user1Address, 32), airdropUserData],
);
const airdropUserIdentifierHash = calculateUserIdentifierHash(airdropTempUserContextData);
const airdropVcAndDiscloseProof = await generateVcAndDiscloseProof(
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
mockPassport,
airdropScope.toString(),
new Array(88).fill("1"),
"1",
imt,
"20",
undefined,
undefined,
undefined,
undefined,
forbiddenCountriesList,
airdropUserIdentifierHash,
);
await airdrop.connect(owner).openRegistration();
await airdrop.connect(user1).verifySelfProof(vcAndDiscloseProof);
// Create V2 proof format for verifySelfProof
const { proofData: airdropProofData, userContextData: airdropUserContextData } = createV2ProofData(
airdropVcAndDiscloseProof,
await user1.getAddress(),
);
await airdrop.connect(user1).verifySelfProof(airdropProofData, airdropUserContextData);
await airdrop.connect(owner).closeRegistration();
const tree = new BalanceTree([{ account: await user1.getAddress(), amount: BigInt(1000000000000000000) }]);
@@ -228,19 +333,13 @@ describe("End to End Tests", function () {
const isClaimed = await airdrop.claimed(await user1.getAddress());
expect(isClaimed).to.be.true;
const readableData = await hub.getReadableRevealedData(
[result.revealedDataPacked[0], result.revealedDataPacked[1], result.revealedDataPacked[2]],
["0", "1", "2", "3", "4", "5", "6", "7", "8"],
);
expect(readableData[0]).to.equal("FRA");
expect(readableData[1]).to.deep.equal(["ALPHONSE HUGHUES ALBERT", "DUPONT"]);
expect(readableData[2]).to.equal("15AA81234");
expect(readableData[3]).to.equal("FRA");
expect(readableData[4]).to.equal("31-01-94");
expect(readableData[5]).to.equal("M");
expect(readableData[6]).to.equal("31-10-40");
expect(readableData[7]).to.equal(20n);
expect(readableData[8]).to.equal(1n);
// Verify disclosed attributes from lastOutput
expect(lastOutput.issuingState).to.equal("FRA");
expect(lastOutput.idNumber).to.equal("15AA81234");
expect(lastOutput.nationality).to.equal("FRA");
expect(lastOutput.dateOfBirth).to.equal("31-01-94");
expect(lastOutput.gender).to.equal("M");
expect(lastOutput.expiryDate).to.equal("31-10-40");
expect(lastOutput.olderThan).to.equal(20n);
});
});

View File

@@ -11,8 +11,9 @@ import { BigNumberish } from "ethers";
import { generateRandomFieldElement, getStartOfDayTimestamp, splitHexFromBack } from "../utils/utils";
import { Formatter, CircuitAttributeHandler } from "../utils/formatter";
import { formatCountriesList, reverseBytes, reverseCountryBytes } from "@selfxyz/common/utils/circuits/formatInputs";
import { getPackedForbiddenCountries } from "@selfxyz/common/utils/sanctions";
import { getPackedForbiddenCountries } from "@selfxyz/common/utils/contracts/forbiddenCountries";
import { countries, Country3LetterCode } from "@selfxyz/common/constants/countries";
import { castFromScope } from "@selfxyz/common/utils/circuits/uuid";
import path from "path";
describe("VC and Disclose", () => {
@@ -100,7 +101,7 @@ describe("VC and Disclose", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
deployedActors.mockPassport,
"test-scope",
castFromScope("test-scope"),
new Array(88).fill("1"),
"1",
imt,
@@ -110,7 +111,7 @@ describe("VC and Disclose", () => {
undefined,
undefined,
forbiddenCountriesList,
(await deployedActors.user1.getAddress()).slice(2),
await deployedActors.user1.getAddress(),
);
snapshotId = await ethers.provider.send("evm_snapshot", []);
});
@@ -439,6 +440,7 @@ describe("VC and Disclose", () => {
const { hub, registry, owner, mockPassport } = deployedActors;
const hashFunction = (a: bigint, b: bigint) => poseidon2([a, b]);
const LeanIMT = await import("@openpassport/zk-kit-lean-imt").then((mod) => mod.LeanIMT);
const imt = new LeanIMT<bigint>(hashFunction);
imt.insert(BigInt(commitment));
@@ -448,7 +450,7 @@ describe("VC and Disclose", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
mockPassport,
"test-scope",
castFromScope("test-scope"),
new Array(88).fill("1"),
"1",
imt,
@@ -746,43 +748,60 @@ describe("VC and Disclose", () => {
it("should parse forbidden countries with CircuitAttributeHandler", async () => {
const { hub } = deployedActors;
const forbiddenCountriesListPacked = splitHexFromBack(
reverseCountryBytes(Formatter.bytesToHexString(new Uint8Array(formatCountriesList(forbiddenCountriesList)))),
);
const localForbiddenCountriesList = ["AFG", "ABC", "CBA"] as const;
const forbiddenCountriesListPacked = getPackedForbiddenCountries([...localForbiddenCountriesList]);
const readableForbiddenCountries = await hub.getReadableForbiddenCountries(forbiddenCountriesListPacked);
expect(readableForbiddenCountries[0]).to.equal(forbiddenCountriesList[0]);
expect(readableForbiddenCountries[1]).to.equal(forbiddenCountriesList[1]);
expect(readableForbiddenCountries[2]).to.equal(forbiddenCountriesList[2]);
expect(readableForbiddenCountries[0]).to.equal(localForbiddenCountriesList[0]);
expect(readableForbiddenCountries[1]).to.equal(localForbiddenCountriesList[1]);
expect(readableForbiddenCountries[2]).to.equal(localForbiddenCountriesList[2]);
});
it("should return maximum length of forbidden countries", async () => {
const { hub } = deployedActors;
const forbiddenCountriesList = ["AAA", "FRA", "CBA", "CBA", "CBA", "CBA", "CBA", "CBA", "CBA", "CBA"];
const forbiddenCountriesListPacked = splitHexFromBack(
reverseCountryBytes(Formatter.bytesToHexString(new Uint8Array(formatCountriesList(forbiddenCountriesList)))),
);
const localForbiddenCountriesList = [
"AAA",
"FRA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
] as const;
const forbiddenCountriesListPacked = getPackedForbiddenCountries([...localForbiddenCountriesList]);
const readableForbiddenCountries = await hub.getReadableForbiddenCountries(forbiddenCountriesListPacked);
expect(readableForbiddenCountries.length).to.equal(40);
expect(readableForbiddenCountries[0]).to.equal(forbiddenCountriesList[0]);
expect(readableForbiddenCountries[1]).to.equal(forbiddenCountriesList[1]);
expect(readableForbiddenCountries[2]).to.equal(forbiddenCountriesList[2]);
expect(readableForbiddenCountries[3]).to.equal(forbiddenCountriesList[3]);
expect(readableForbiddenCountries[4]).to.equal(forbiddenCountriesList[4]);
expect(readableForbiddenCountries[5]).to.equal(forbiddenCountriesList[5]);
expect(readableForbiddenCountries[6]).to.equal(forbiddenCountriesList[6]);
expect(readableForbiddenCountries[7]).to.equal(forbiddenCountriesList[7]);
expect(readableForbiddenCountries[8]).to.equal(forbiddenCountriesList[8]);
expect(readableForbiddenCountries[9]).to.equal(forbiddenCountriesList[9]);
expect(readableForbiddenCountries[0]).to.equal(localForbiddenCountriesList[0]);
expect(readableForbiddenCountries[1]).to.equal(localForbiddenCountriesList[1]);
expect(readableForbiddenCountries[2]).to.equal(localForbiddenCountriesList[2]);
expect(readableForbiddenCountries[3]).to.equal(localForbiddenCountriesList[3]);
expect(readableForbiddenCountries[4]).to.equal(localForbiddenCountriesList[4]);
expect(readableForbiddenCountries[5]).to.equal(localForbiddenCountriesList[5]);
expect(readableForbiddenCountries[6]).to.equal(localForbiddenCountriesList[6]);
expect(readableForbiddenCountries[7]).to.equal(localForbiddenCountriesList[7]);
expect(readableForbiddenCountries[8]).to.equal(localForbiddenCountriesList[8]);
expect(readableForbiddenCountries[9]).to.equal(localForbiddenCountriesList[9]);
});
it("should fail when getReadableForbiddenCountries is called by non-proxy", async () => {
const { hubImpl } = deployedActors;
const forbiddenCountriesList = ["AAA", "FRA", "CBA", "CBA", "CBA", "CBA", "CBA", "CBA", "CBA", "CBA"];
const forbiddenCountriesListPacked = splitHexFromBack(
reverseCountryBytes(Formatter.bytesToHexString(new Uint8Array(formatCountriesList(forbiddenCountriesList)))),
);
const localForbiddenCountriesList = [
"AAA",
"FRA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
"CBA",
] as const;
const forbiddenCountriesListPacked = getPackedForbiddenCountries([...localForbiddenCountriesList]);
await expect(hubImpl.getReadableForbiddenCountries(forbiddenCountriesListPacked)).to.be.revertedWithCustomError(
hubImpl,
"UUPSUnauthorizedCallContext",

View File

@@ -10,6 +10,7 @@ import { poseidon2 } from "poseidon-lite";
import { generateVcAndDiscloseProof, parseSolidityCalldata } from "../utils/generateProof";
import { Formatter } from "../utils/formatter";
import { formatCountriesList, reverseBytes } from "@selfxyz/common/utils/circuits/formatInputs";
import { stringToBigInt } from "@selfxyz/common/utils/scope";
import { VerifyAll } from "../../typechain-types";
import { getSMTs } from "../utils/generateProof";
import { Groth16Proof, PublicSignals, groth16 } from "snarkjs";
@@ -102,7 +103,7 @@ describe("VerifyAll", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
deployedActors.mockPassport,
"test-scope",
stringToBigInt("test-scope").toString(),
new Array(88).fill("1"),
"1",
imt,
@@ -112,7 +113,7 @@ describe("VerifyAll", () => {
undefined,
undefined,
forbiddenCountriesList,
(await deployedActors.user1.getAddress()).slice(2),
await deployedActors.user1.getAddress(),
);
snapshotId = await ethers.provider.send("evm_snapshot", []);
});
@@ -293,7 +294,7 @@ describe("VerifyAll", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
deployedActors.mockPassport,
"test-scope",
stringToBigInt("test-scope").toString(),
new Array(88).fill("1"),
"1",
imt,

View File

@@ -357,7 +357,7 @@ describe("Unit Tests for IdentityRegistry", () => {
await expect(registry.connect(user1).updateHub(newHubAddress)).to.be.revertedWithCustomError(
registry,
"OwnableUnauthorizedAccount",
"AccessControlUnauthorizedAccount",
);
});
@@ -498,7 +498,7 @@ describe("Unit Tests for IdentityRegistry", () => {
await expect(
registry.connect(user1).devAddIdentityCommitment(attestationId, nullifier, commitment),
).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount");
).to.be.revertedWithCustomError(registry, "AccessControlUnauthorizedAccount");
});
it("should not add commitment if caller is not proxy", async () => {
@@ -546,7 +546,7 @@ describe("Unit Tests for IdentityRegistry", () => {
const newCommitment = generateRandomFieldElement();
await expect(
registry.connect(user1).devUpdateCommitment(commitment, newCommitment, []),
).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount");
).to.be.revertedWithCustomError(registry, "AccessControlUnauthorizedAccount");
});
it("should not update commitment if caller is not proxy", async () => {
@@ -592,7 +592,7 @@ describe("Unit Tests for IdentityRegistry", () => {
await registry.devAddIdentityCommitment(attestationId, nullifier, commitment);
await expect(registry.connect(user1).devRemoveCommitment(commitment, [])).to.be.revertedWithCustomError(
registry,
"OwnableUnauthorizedAccount",
"AccessControlUnauthorizedAccount",
);
});
@@ -632,7 +632,7 @@ describe("Unit Tests for IdentityRegistry", () => {
const dscCommitment = generateRandomFieldElement();
await expect(registry.connect(user1).devAddDscKeyCommitment(dscCommitment)).to.be.revertedWithCustomError(
registry,
"OwnableUnauthorizedAccount",
"AccessControlUnauthorizedAccount",
);
});
@@ -673,7 +673,7 @@ describe("Unit Tests for IdentityRegistry", () => {
await registry.devAddDscKeyCommitment(dscCommitment);
await expect(
registry.connect(user1).devUpdateDscKeyCommitment(dscCommitment, newDscCommitment, []),
).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount");
).to.be.revertedWithCustomError(registry, "AccessControlUnauthorizedAccount");
});
it("should not update dsc key commitment if caller is not proxy", async () => {
@@ -711,7 +711,7 @@ describe("Unit Tests for IdentityRegistry", () => {
await registry.devAddDscKeyCommitment(dscCommitment);
await expect(registry.connect(user1).devRemoveDscKeyCommitment(dscCommitment, [])).to.be.revertedWithCustomError(
registry,
"OwnableUnauthorizedAccount",
"AccessControlUnauthorizedAccount",
);
});
@@ -751,7 +751,7 @@ describe("Unit Tests for IdentityRegistry", () => {
const nullifier = generateRandomFieldElement();
await expect(
registry.connect(user1).devChangeNullifierState(attestationId, nullifier, false),
).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount");
).to.be.revertedWithCustomError(registry, "AccessControlUnauthorizedAccount");
});
it("should not change nullifier state if caller is not proxy", async () => {
@@ -789,7 +789,7 @@ describe("Unit Tests for IdentityRegistry", () => {
const state = true;
await expect(
registry.connect(user1).devChangeDscKeyCommitmentState(dscCommitment, state),
).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount");
).to.be.revertedWithCustomError(registry, "AccessControlUnauthorizedAccount");
});
it("should not change dsc key commitment state if caller is not proxy", async () => {
@@ -915,7 +915,7 @@ describe("Unit Tests for IdentityRegistry", () => {
await expect(
registry.connect(user1).upgradeToAndCall(registryV2Implementation.target, "0x"),
).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount");
).to.be.revertedWithCustomError(registry, "AccessControlUnauthorizedAccount");
});
it("should not allow implementation contract to be initialized directly", async () => {

View File

@@ -149,9 +149,11 @@ describe("Aadhaar Registration test", function () {
});
it("should not fail if timestamp is within 20 minutes", async () => {
// Fix the AADHAAR_REGISTRATION_WINDOW that was incorrectly set to 0
await deployedActors.hub.setAadhaarRegistrationWindow(20);
const latestBlock = await ethers.provider.getBlock("latest");
const blockTimestamp = latestBlock!.timestamp;
const newAadhaarData = prepareAadhaarRegisterTestData(
privateKeyPem,
pubkeyPem,
@@ -161,8 +163,7 @@ describe("Aadhaar Registration test", function () {
"M",
"110051",
"WB",
//timestamp 10 minutes ago and converted to timestamp string
new Date(Date.now() - 10 * 60 * 1000).getTime().toString(),
(blockTimestamp - 10 * 60).toString(),
);
const newRegisterProof = await generateRegisterAadhaarProof(registerSecret, newAadhaarData.inputs);
@@ -171,9 +172,11 @@ describe("Aadhaar Registration test", function () {
});
it("should fail with InvalidUidaiTimestamp when UIDAI timestamp is not within 20 minutes of current time", async () => {
// Fix the AADHAAR_REGISTRATION_WINDOW that was incorrectly set to 0
await deployedActors.hub.setAadhaarRegistrationWindow(20);
const latestBlock = await ethers.provider.getBlock("latest");
const blockTimestamp = latestBlock!.timestamp;
const newAadhaarData = prepareAadhaarRegisterTestData(
privateKeyPem,
pubkeyPem,
@@ -183,8 +186,7 @@ describe("Aadhaar Registration test", function () {
"M",
"110051",
"WB",
//timestamp 30 minutes ago and converted to timestamp string
new Date(Date.now() - 30 * 60 * 1000).getTime().toString(),
(blockTimestamp - 30 * 60).toString(),
);
const newRegisterProof = await generateRegisterAadhaarProof(registerSecret, newAadhaarData.inputs);