import { expect } from "chai"; import { deploySystemFixtures } from "../utils/deployment"; import { DeployedActors } from "../utils/types"; import { ethers } from "hardhat"; import { RegisterVerifierId, DscVerifierId } from "@selfxyz/common/constants/constants"; describe("Unit Tests for IdentityVerificationHub", () => { let deployedActors: DeployedActors; let snapshotId: string; before(async () => { deployedActors = await deploySystemFixtures(); snapshotId = await ethers.provider.send("evm_snapshot", []); }); afterEach(async () => { await ethers.provider.send("evm_revert", [snapshotId]); snapshotId = await ethers.provider.send("evm_snapshot", []); }); describe("Initialization", () => { it("should initialize hub with correct parameters", async () => { const { hub, registry, vcAndDisclose, register, dsc } = deployedActors; // Check initial state expect(await hub.registry()).to.equal(registry.target); expect(await hub.vcAndDiscloseCircuitVerifier()).to.equal(vcAndDisclose.target); const registerId = RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096; const dscId = DscVerifierId.dsc_sha256_rsa_65537_4096; expect(await hub.sigTypeToRegisterCircuitVerifiers(registerId)).to.equal(register.target); expect(await hub.sigTypeToDscCircuitVerifiers(dscId)).to.equal(dsc.target); const filter = hub.filters.HubInitialized; const hubInitializedEvents = await hub.queryFilter(filter); expect(hubInitializedEvents.length).to.equal(1); const hubInitializedEvent = hubInitializedEvents[0]; expect(hubInitializedEvent.args.registry).to.equal(registry.target); expect(hubInitializedEvent.args.vcAndDiscloseCircuitVerifier).to.equal(vcAndDisclose.target); expect(hubInitializedEvent.args.registerCircuitVerifierIds).to.deep.equal([registerId]); expect(hubInitializedEvent.args.registerCircuitVerifiers).to.deep.equal([register.target]); expect(hubInitializedEvent.args.dscCircuitVerifierIds).to.deep.equal([dscId]); expect(hubInitializedEvent.args.dscCircuitVerifiers).to.deep.equal([dsc.target]); const initFilter = hub.filters.Initialized; const initEvents = await hub.queryFilter(initFilter); expect(initEvents.length).to.equal(1); const initEvent = initEvents[0]; expect(initEvent.args.version).to.equal(1); }); it("should not allow direct initialization of hub implementation", async () => { const { owner, registry, vcAndDisclose } = deployedActors; const HubFactory = await ethers.getContractFactory("IdentityVerificationHubImplV1", owner); const hubImpl = await HubFactory.deploy(); await expect( hubImpl.initialize(registry.target, vcAndDisclose.target, [], [], [], []), ).to.be.revertedWithCustomError(hubImpl, "InvalidInitialization"); }); it("should revert when register circuit verifier arrays length mismatch", async () => { const { owner, registry, vcAndDisclose } = deployedActors; const HubFactory = await ethers.getContractFactory("IdentityVerificationHubImplV1", owner); const hubImpl = await HubFactory.deploy(); await hubImpl.waitForDeployment(); const initializeData = hubImpl.interface.encodeFunctionData("initialize", [ registry.target, vcAndDisclose.target, [1], [], [], [], ]); const hubProxyFactory = await ethers.getContractFactory("IdentityVerificationHub", owner); await expect(hubProxyFactory.deploy(hubImpl.target, initializeData)).to.be.revertedWithCustomError( hubImpl, "LENGTH_MISMATCH", ); }); it("should revert when DSC circuit verifier arrays length mismatch", async () => { const { owner, registry, vcAndDisclose } = deployedActors; const HubFactory = await ethers.getContractFactory("IdentityVerificationHubImplV1", owner); const hubImpl = await HubFactory.deploy(); await hubImpl.waitForDeployment(); const initializeData = hubImpl.interface.encodeFunctionData("initialize", [ registry.target, vcAndDisclose.target, [], [], [1], [], ]); const hubProxyFactory = await ethers.getContractFactory("IdentityVerificationHub", owner); await expect(hubProxyFactory.deploy(hubImpl.target, initializeData)).to.be.revertedWithCustomError( hubImpl, "LENGTH_MISMATCH", ); }); it("should not allow initialization after initialized", async () => { const { hub, registry, vcAndDisclose } = deployedActors; await expect(hub.initialize(registry.target, vcAndDisclose.target, [], [], [], [])).to.be.revertedWithCustomError( hub, "InvalidInitialization", ); }); }); describe("Update functions", () => { it("should update registry address", async () => { const { hub, user1 } = deployedActors; const newRegistryAddress = await user1.getAddress(); await expect(hub.updateRegistry(newRegistryAddress)).to.emit(hub, "RegistryUpdated").withArgs(newRegistryAddress); expect(await hub.registry()).to.equal(newRegistryAddress); }); it("should not update registry address if caller is not owner", async () => { const { hub, user1 } = deployedActors; const newRegistryAddress = await user1.getAddress(); await expect(hub.connect(user1).updateRegistry(newRegistryAddress)).to.be.revertedWithCustomError( hub, "OwnableUnauthorizedAccount", ); }); it("should not update registry address if caller is not proxy", async () => { const { hubImpl, user1 } = deployedActors; const newRegistryAddress = await user1.getAddress(); await expect(hubImpl.updateRegistry(newRegistryAddress)).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); it("should update vc and disclose circuit verifier", async () => { const { hub, user1 } = deployedActors; const newVerifierAddress = await user1.getAddress(); await expect(hub.updateVcAndDiscloseCircuit(newVerifierAddress)) .to.emit(hub, "VcAndDiscloseCircuitUpdated") .withArgs(newVerifierAddress); expect(await hub.vcAndDiscloseCircuitVerifier()).to.equal(newVerifierAddress); }); it("should not update vc and disclose circuit verifier if caller is not owner", async () => { const { hub, user1 } = deployedActors; const newVerifierAddress = await user1.getAddress(); await expect(hub.connect(user1).updateVcAndDiscloseCircuit(newVerifierAddress)).to.be.revertedWithCustomError( hub, "OwnableUnauthorizedAccount", ); }); it("should not update vc and disclose circuit verifier if caller is not proxy", async () => { const { hubImpl, user1 } = deployedActors; const newVerifierAddress = await user1.getAddress(); await expect(hubImpl.updateVcAndDiscloseCircuit(newVerifierAddress)).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); it("should update register circuit verifier", async () => { const { hub, user1 } = deployedActors; const verifierId = RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096; const newVerifierAddress = await user1.getAddress(); await expect(hub.updateRegisterCircuitVerifier(verifierId, newVerifierAddress)) .to.emit(hub, "RegisterCircuitVerifierUpdated") .withArgs(verifierId, newVerifierAddress); expect(await hub.sigTypeToRegisterCircuitVerifiers(verifierId)).to.equal(newVerifierAddress); }); it("should not update register circuit verifier if caller is not owner", async () => { const { hub, user1 } = deployedActors; const verifierId = RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096; const newVerifierAddress = await user1.getAddress(); await expect( hub.connect(user1).updateRegisterCircuitVerifier(verifierId, newVerifierAddress), ).to.be.revertedWithCustomError(hub, "OwnableUnauthorizedAccount"); }); it("should not update register circuit verifier if caller is not proxy", async () => { const { hubImpl, user1 } = deployedActors; const verifierId = RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096; const newVerifierAddress = await user1.getAddress(); await expect(hubImpl.updateRegisterCircuitVerifier(verifierId, newVerifierAddress)).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); it("should update DSC verifier", async () => { const { hub, user1 } = deployedActors; const verifierId = DscVerifierId.dsc_sha256_rsa_65537_4096; const newVerifierAddress = await user1.getAddress(); await expect(hub.updateDscVerifier(verifierId, newVerifierAddress)) .to.emit(hub, "DscCircuitVerifierUpdated") .withArgs(verifierId, newVerifierAddress); expect(await hub.sigTypeToDscCircuitVerifiers(verifierId)).to.equal(newVerifierAddress); }); it("should not update DSC verifier if caller is not owner", async () => { const { hub, user1 } = deployedActors; const verifierId = DscVerifierId.dsc_sha256_rsa_65537_4096; const newVerifierAddress = await user1.getAddress(); await expect(hub.connect(user1).updateDscVerifier(verifierId, newVerifierAddress)).to.be.revertedWithCustomError( hub, "OwnableUnauthorizedAccount", ); }); it("should not update DSC verifier if caller is not proxy", async () => { const { hubImpl, user1 } = deployedActors; const verifierId = DscVerifierId.dsc_sha256_rsa_65537_4096; const newVerifierAddress = await user1.getAddress(); await expect(hubImpl.updateDscVerifier(verifierId, newVerifierAddress)).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); it("should batch update register circuit verifiers", async () => { const { hub, user1 } = deployedActors; const verifierIds = [1, 2]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect(hub.batchUpdateRegisterCircuitVerifiers(verifierIds, newVerifierAddresses)) .to.emit(hub, "RegisterCircuitVerifierUpdated") .withArgs(verifierIds[0], newVerifierAddresses[0]) .to.emit(hub, "RegisterCircuitVerifierUpdated") .withArgs(verifierIds[1], newVerifierAddresses[1]); for (let i = 0; i < verifierIds.length; i++) { expect(await hub.sigTypeToRegisterCircuitVerifiers(verifierIds[i])).to.equal(newVerifierAddresses[i]); } }); it("should not batch update register circuit verifiers if caller is not owner", async () => { const { hub, user1 } = deployedActors; const verifierIds = [1, 2]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect( hub.connect(user1).batchUpdateRegisterCircuitVerifiers(verifierIds, newVerifierAddresses), ).to.be.revertedWithCustomError(hub, "OwnableUnauthorizedAccount"); }); it("should not batch update register circuit verifiers if caller is not proxy", async () => { const { hubImpl, user1 } = deployedActors; const verifierIds = [1, 2]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect( hubImpl.batchUpdateRegisterCircuitVerifiers(verifierIds, newVerifierAddresses), ).to.be.revertedWithCustomError(hubImpl, "UUPSUnauthorizedCallContext"); }); it("should not batch update register verifiers if length is not the same", async () => { const { hub, user1 } = deployedActors; const verifierIds = [1]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect( hub.batchUpdateRegisterCircuitVerifiers(verifierIds, newVerifierAddresses), ).to.be.revertedWithCustomError(hub, "LENGTH_MISMATCH"); }); it("should batch update DSC circuit verifiers", async () => { const { hub, user1 } = deployedActors; const verifierIds = [1, 2]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect(hub.batchUpdateDscCircuitVerifiers(verifierIds, newVerifierAddresses)) .to.emit(hub, "DscCircuitVerifierUpdated") .withArgs(verifierIds[0], newVerifierAddresses[0]) .to.emit(hub, "DscCircuitVerifierUpdated") .withArgs(verifierIds[1], newVerifierAddresses[1]); for (let i = 0; i < verifierIds.length; i++) { expect(await hub.sigTypeToDscCircuitVerifiers(verifierIds[i])).to.equal(newVerifierAddresses[i]); } }); it("should not batch update DSC circuit verifiers if caller is not owner", async () => { const { hub, user1 } = deployedActors; const verifierIds = [1, 2]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect( hub.connect(user1).batchUpdateDscCircuitVerifiers(verifierIds, newVerifierAddresses), ).to.be.revertedWithCustomError(hub, "OwnableUnauthorizedAccount"); }); it("should not batch update DSC circuit verifiers if caller is not proxy", async () => { const { hubImpl, user1 } = deployedActors; const verifierIds = [1, 2]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect( hubImpl.batchUpdateDscCircuitVerifiers(verifierIds, newVerifierAddresses), ).to.be.revertedWithCustomError(hubImpl, "UUPSUnauthorizedCallContext"); }); it("should not batch update dsc verifiers if length is not the same", async () => { const { hub, user1 } = deployedActors; const verifierIds = [1]; const newVerifierAddresses = [await user1.getAddress(), await user1.getAddress()]; await expect(hub.batchUpdateDscCircuitVerifiers(verifierIds, newVerifierAddresses)).to.be.revertedWithCustomError( hub, "LENGTH_MISMATCH", ); }); }); describe("View functions", () => { it("should return correct registry address", async () => { const { hub, registry } = deployedActors; expect(await hub.registry()).to.equal(registry.target); }); it("should not return when view function is called by non-proxy", async () => { const { hubImpl } = deployedActors; await expect(hubImpl.registry()).to.be.revertedWithCustomError(hubImpl, "UUPSUnauthorizedCallContext"); }); it("should return correct vcAndDiscloseCircuitVerifier address", async () => { const { hub, vcAndDisclose } = deployedActors; expect(await hub.vcAndDiscloseCircuitVerifier()).to.equal(vcAndDisclose.target); }); it("should not return when view function is called by non-proxy", async () => { const { hubImpl } = deployedActors; await expect(hubImpl.vcAndDiscloseCircuitVerifier()).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); it("should return correct register circuit verifier address", async () => { const { hub, register } = deployedActors; const verifierId = RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096; expect(await hub.sigTypeToRegisterCircuitVerifiers(verifierId)).to.equal(register.target); }); it("should not return when view function is called by non-proxy", async () => { const { hubImpl } = deployedActors; await expect(hubImpl.sigTypeToRegisterCircuitVerifiers(1)).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); it("should return correct dsc circuit verifier address", async () => { const { hub, dsc } = deployedActors; const verifierId = DscVerifierId.dsc_sha256_rsa_65537_4096; expect(await hub.sigTypeToDscCircuitVerifiers(verifierId)).to.equal(dsc.target); }); it("should not return when view function is called by non-proxy", async () => { const { hubImpl } = deployedActors; await expect(hubImpl.sigTypeToDscCircuitVerifiers(1)).to.be.revertedWithCustomError( hubImpl, "UUPSUnauthorizedCallContext", ); }); }); describe("Upgradeabilitiy", () => { it("should preserve state after upgrade", async () => { const { hub, owner } = deployedActors; const registryAddressBefore = await hub.registry(); const vcAndDiscloseCircuitVerifierBefore = await hub.vcAndDiscloseCircuitVerifier(); const registerCircuitVerifierIdsBefore = await hub.sigTypeToRegisterCircuitVerifiers( RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096, ); const dscCircuitVerifierIdsBefore = await hub.sigTypeToDscCircuitVerifiers( DscVerifierId.dsc_sha256_rsa_65537_4096, ); const HubV2Factory = await ethers.getContractFactory("testUpgradedIdentityVerificationHubImplV1", owner); const hubV2Implementation = await HubV2Factory.deploy(); await hubV2Implementation.waitForDeployment(); const hubAsImpl = await ethers.getContractAt("testUpgradedIdentityVerificationHubImplV1", hub.target); await hubAsImpl .connect(owner) .upgradeToAndCall(hubV2Implementation.target, HubV2Factory.interface.encodeFunctionData("initialize", [true])); const hubV2 = await ethers.getContractAt("testUpgradedIdentityVerificationHubImplV1", hub.target); expect(await hubV2.isTest()).to.equal(true); expect(await hubV2.registry()).to.equal(registryAddressBefore); expect(await hubV2.vcAndDiscloseCircuitVerifier()).to.equal(vcAndDiscloseCircuitVerifierBefore); expect( await hubV2.sigTypeToRegisterCircuitVerifiers(RegisterVerifierId.register_sha256_sha256_sha256_rsa_65537_4096), ).to.equal(registerCircuitVerifierIdsBefore); expect(await hubV2.sigTypeToDscCircuitVerifiers(DscVerifierId.dsc_sha256_rsa_65537_4096)).to.equal( dscCircuitVerifierIdsBefore, ); const implementationSlot = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; const implementationAddress = await ethers.provider.getStorage(hub.target, implementationSlot); expect(ethers.zeroPadValue(implementationAddress, 32)).to.equal( ethers.zeroPadValue(hubV2Implementation.target.toString(), 32), ); }); it("should not allow non-proxy to upgrade implementation", async () => { const { hub, hubImpl, owner } = deployedActors; const HubV2Factory = await ethers.getContractFactory("testUpgradedIdentityVerificationHubImplV1", owner); const hubV2Implementation = await HubV2Factory.deploy(); await hubV2Implementation.waitForDeployment(); const hubAsImpl = await ethers.getContractAt("testUpgradedIdentityVerificationHubImplV1", hub.target); await expect( hubImpl .connect(owner) .upgradeToAndCall( hubV2Implementation.target, HubV2Factory.interface.encodeFunctionData("initialize", [true]), ), ).to.be.revertedWithCustomError(hubAsImpl, "UUPSUnauthorizedCallContext"); }); it("should not allow non-owner to upgrade implementation", async () => { const { hub, owner, user1 } = deployedActors; const HubV2Factory = await ethers.getContractFactory("testUpgradedIdentityVerificationHubImplV1", owner); const hubV2Implementation = await HubV2Factory.deploy(); await hubV2Implementation.waitForDeployment(); const hubAsImpl = await ethers.getContractAt("testUpgradedIdentityVerificationHubImplV1", hub.target); await expect( hubAsImpl .connect(user1) .upgradeToAndCall( hubV2Implementation.target, HubV2Factory.interface.encodeFunctionData("initialize", [true]), ), ).to.be.revertedWithCustomError(hubAsImpl, "OwnableUnauthorizedAccount"); }); it("should not allow implementation contract to be initialized directly", async () => { const { hub, owner } = deployedActors; const HubV2Factory = await ethers.getContractFactory("testUpgradedIdentityVerificationHubImplV1", owner); const hubV2Implementation = await HubV2Factory.deploy(); await hubV2Implementation.waitForDeployment(); await expect(hubV2Implementation.initialize(true)).to.be.revertedWithCustomError(hub, "InvalidInitialization"); }); it("should not allow direct calls to implementation contract", async () => { const { hub, owner } = deployedActors; const HubV2Factory = await ethers.getContractFactory("testUpgradedIdentityVerificationHubImplV1", owner); const hubV2Implementation = await HubV2Factory.deploy(); await hubV2Implementation.waitForDeployment(); await expect(hubV2Implementation.isTest()).to.be.revertedWithCustomError( hubV2Implementation, "UUPSUnauthorizedCallContext", ); }); }); });