mirror of
https://github.com/selfxyz/self.git
synced 2026-01-07 22:04:03 -05:00
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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const config: HardhatUserConfig = {
|
||||
evmVersion: "cancun",
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 100000,
|
||||
runs: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user