Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/solidity/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,13 @@ PRIVATE_KEY=your_private_key_here # Required for Sepolia, not needed for Anvil
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/your_infura_key_here # Required for Sepolia, not needed for Anvil

# API Keys
ETHERSCAN_API_KEY=your_etherscan_api_key_here # Optional: Only needed for contract verification on Sepolia
ETHERSCAN_API_KEY=your_etherscan_api_key_here # Optional: Only needed for contract verification on Sepolia

# Root registry address
ROOT_REGISTRY_ADDRESS=0xB6bF4a45D5Ed1363C45BD0e4cbaDCcd48F8D3FaB

# Root verifier admin address
ROOT_VERIFIER_ADMIN_ADDRESS=0x1111111111111111111111111111111111111111

# Root verifier guardian address
ROOT_VERIFIER_GUARDIAN_ADDRESS=0x2222222222222222222222222222222222222222
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ contract Deploy is Script {
console.log("Outer (13 subproofs) verifier deployed at:", verifierAddresses[9]);

// Add verifiers to ZKPassportVerifier
console.log("Adding verifiers to ZKPassportVerifier...");
zkPassportVerifier.addVerifiers(vkeyHashes, verifierAddresses);
console.log("Verifiers added to ZKPassportVerifier");
console.log("Adding sub-verifiers to ZKPassportVerifier...");
zkPassportVerifier.addSubVerifiers(vkeyHashes, verifierAddresses);
console.log("Sub-verifiers added to ZKPassportVerifier");

// Stop broadcasting transactions
vm.stopBroadcast();
Expand Down
6 changes: 3 additions & 3 deletions src/solidity/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ contract Deploy is Script {
console.log("ZKPassportVerifier deployed at:", address(zkPassportVerifier));

// Add verifiers to ZKPassportVerifier
console.log("Adding verifiers to ZKPassportVerifier...");
zkPassportVerifier.addVerifiers(vkeyHashes, verifierAddresses);
console.log("Verifiers added to ZKPassportVerifier");
console.log("Adding sub-verifiers to ZKPassportVerifier...");
zkPassportVerifier.addSubVerifiers(vkeyHashes, verifierAddresses);
console.log("Sub-verifiers added to ZKPassportVerifier");

// Stop broadcasting transactions
vm.stopBroadcast();
Expand Down
10 changes: 5 additions & 5 deletions src/solidity/script/DeployWithExistingVerifiers.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ contract Deploy is Script {
ZKPassportVerifier zkPassportVerifier = new ZKPassportVerifier(rootRegistry);
console.log("ZKPassportVerifier deployed at:", address(zkPassportVerifier));

// Add verifiers to ZKPassportVerifier
console.log("Adding verifiers to ZKPassportVerifier...");
zkPassportVerifier.addVerifiers(vkeyHashes, verifierAddresses);
console.log("Verifiers added to ZKPassportVerifier");
// Add sub-verifiers to ZKPassportVerifier
console.log("Adding sub-verifiers to ZKPassportVerifier...");
zkPassportVerifier.addSubVerifiers(vkeyHashes, verifierAddresses);
console.log("Sub-verifiers added to ZKPassportVerifier");

// Stop broadcasting transactions
vm.stopBroadcast();

// Create JSON for verifiers
// Create JSON for sub-verifiers
string memory verifiers = "verifiers";
vm.serializeAddress(verifiers, "outer_count_4", verifierAddresses[0]);
vm.serializeAddress(verifiers, "outer_count_5", verifierAddresses[1]);
Expand Down
28 changes: 16 additions & 12 deletions src/solidity/src/SampleContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ pragma solidity >=0.8.21;

import {DateUtils} from "../src/DateUtils.sol";
import {StringUtils} from "../src/StringUtils.sol";
import {DisclosedData} from "../src/Types.sol";
import {ZKPassportVerifier, ProofType, ProofVerificationParams, DisclosedData} from "../src/ZKPassportVerifier.sol";
import {DisclosedData, ProofVerificationParams} from "../src/Types.sol";
import {ZKPassportVerifier} from "../src/ZKPassportVerifier.sol";
import {ZKPassportRootVerifier} from "../src/ZKPassportRootVerifier.sol";
import {console} from "forge-std/console.sol";

contract SampleContract {
address public admin;
ZKPassportVerifier public zkPassportVerifier;

uint256 public constant ZKPASSPORT_VERIFIER_VERSION = 1;
ZKPassportRootVerifier public zkPassportRootVerifier;
// Unique Identifier => whether it was verified or not
mapping(bytes32 => bool) public isVerified;
// Unique Identifier => nationality
Expand All @@ -34,8 +35,8 @@ contract SampleContract {
_;
}

function setZKPassportVerifier(address _zkPassportVerifier) public onlyAdmin {
zkPassportVerifier = ZKPassportVerifier(_zkPassportVerifier);
function setZKPassportVerifier(address _verifier) public onlyAdmin {
zkPassportRootVerifier = ZKPassportRootVerifier(_verifier);
}

function setDomain(string calldata _domain) public onlyAdmin {
Expand All @@ -59,25 +60,28 @@ contract SampleContract {
// and the SDK will tell you which one they have
bool isIDCard
) public returns (bytes32) {
(bool verified, bytes32 uniqueIdentifier) = zkPassportVerifier.verifyProof(params);
// Get the current verifier from the ZKPassportRootVerifier
ZKPassportVerifier verifier = ZKPassportVerifier(zkPassportRootVerifier.getVerifier(ZKPASSPORT_VERIFIER_VERSION));
// Verify the proof
(bool verified, bytes32 uniqueIdentifier) = verifier.verifyProof(params);
require(verified, "Proof is invalid");
require(!isVerified[uniqueIdentifier], "User already verified");
// Check the proof was generated using your domain name (scope) and the subscope
// you specified
require(
zkPassportVerifier.verifyScopes(params.proofVerificationData.publicInputs, validDomain, validScope),
verifier.verifyScopes(params.proofVerificationData.publicInputs, validDomain, validScope),
"Invalid domain or scope"
);
require(zkPassportVerifier.isAgeAboveOrEqual(18, params.commitments), "Age is not 18+");
DisclosedData memory disclosedData = zkPassportVerifier.getDisclosedData(
require(verifier.isAgeAboveOrEqual(18, params.commitments), "Age is not 18+");
DisclosedData memory disclosedData = verifier.getDisclosedData(
params.commitments,
isIDCard
isIDCard
);
string[] memory nationalityExclusionList = new string[](3);
nationalityExclusionList[0] = "ESP";
nationalityExclusionList[1] = "ITA";
nationalityExclusionList[2] = "PRT";
require(zkPassportVerifier.isNationalityOut(nationalityExclusionList, params.commitments), "Nationality is part of the exclusion list");
require(verifier.isNationalityOut(nationalityExclusionList, params.commitments), "Nationality is part of the exclusion list");

// If all good, mark the user as verified
isVerified[uniqueIdentifier] = true;
Expand Down
8 changes: 7 additions & 1 deletion src/solidity/src/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
// Copyright 2025 ZKPassport
pragma solidity >=0.8.21;

interface IZKPassportVerifier {
function verifyProof(
ProofVerificationParams calldata params
) external view returns (bool valid, bytes32 uniqueIdentifier);
}

enum ProofType {
DISCLOSE,
AGE,
Expand Down Expand Up @@ -99,4 +105,4 @@ struct BoundData {
address senderAddress;
uint256 chainId;
string customData;
}
}
136 changes: 136 additions & 0 deletions src/solidity/src/ZKPassportRootVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 ZKPassport
pragma solidity >=0.8.21;

import {IZKPassportVerifier, ProofVerificationParams} from "./Types.sol";

contract ZKPassportRootVerifier {
address public admin;
address public guardian;
bool public paused;

mapping(uint256 => IZKPassportVerifier) public verifiers;

// Events
event AdminUpdated(address indexed oldAdmin, address indexed newAdmin);
event GuardianUpdated(address indexed oldGuardian, address indexed newGuardian);
event VerifierAdded(uint256 indexed version, address indexed verifier);
event VerifierRemoved(uint256 indexed version, address indexed verifier);
event VerifierUpdated(uint256 indexed version, address indexed oldVerifier, address indexed newVerifier);
event PausedStatusChanged(bool paused);

/**
* @dev Constructor
* @param _admin The address of the admin
* @param _guardian The address of the guardian
* @param _version The initial version number for the verifier
* @param _verifier The address of the ZKPassport verifier implementation
*/
constructor(address _admin, address _guardian, uint256 _version, address _verifier) {
require(_admin != address(0), "Admin cannot be zero address");
require(_verifier != address(0), "Verifier cannot be zero address");
require(_version > 0, "Version must be greater than 0");
admin = _admin;
guardian = _guardian;
verifiers[_version] = IZKPassportVerifier(_verifier);
emit VerifierAdded(_version, _verifier);
}

/**
* @notice Verifies a ZKPassport proof using a specific verifier version
* @param version The version number of the verifier to use
* @param params The proof verification parameters
* @return valid True if the proof is valid or false otherwise
* @return uniqueIdentifier The unique identifier associated with the ID used to generate the proof
*/
function verify(
uint256 version,
ProofVerificationParams calldata params
) external view whenNotPaused returns (bool valid, bytes32 uniqueIdentifier) {
IZKPassportVerifier verifier = verifiers[version];
require(address(verifier) != address(0), "Verifier not found for version");
(valid, uniqueIdentifier) = verifier.verifyProof(params);
return (valid, uniqueIdentifier);
}

modifier onlyAdmin() {
require(msg.sender == admin, "Not authorized: admin only");
_;
}

modifier onlyGuardian() {
require(msg.sender == guardian, "Not authorized: guardian only");
_;
}

modifier whenNotPaused() {
require(!paused, "Contract is paused");
_;
}

function transferAdmin(address newAdmin) external onlyAdmin {
require(newAdmin != address(0), "Admin cannot be zero address");
address oldAdmin = admin;
admin = newAdmin;
emit AdminUpdated(oldAdmin, newAdmin);
}

function setGuardian(address newGuardian) external onlyAdmin {
address oldGuardian = guardian;
guardian = newGuardian;
emit GuardianUpdated(oldGuardian, newGuardian);
}

/**
* @notice Gets the verifier address for a specific version
* @param version The version number
* @return The verifier address for the given version
*/
function getVerifier(uint256 version) external view returns (address) {
return address(verifiers[version]);
}

/**
* @notice Adds a verifier for a specific version
* @param version The version number
* @param newVerifier The address of the verifier
*/
function addVerifier(uint256 version, address newVerifier) external onlyAdmin {
require(newVerifier != address(0), "Verifier cannot be zero address");
require(version > 0, "Version must be greater than 0");
require((address)(verifiers[version]) == address(0), "Verifier already exists for version");
verifiers[version] = IZKPassportVerifier(newVerifier);
emit VerifierAdded(version, newVerifier);
}

/**
* @notice Removes a verifier for a specific version
* @param version The version number
*/
function removeVerifier(uint256 version) external onlyAdmin {
require(version > 0, "Version must be greater than 0");
address verifierAddress = address(verifiers[version]);
require(verifierAddress != address(0), "Verifier not found for version");
delete verifiers[version];
emit VerifierRemoved(version, verifierAddress);
}

/**
* @notice Updates a verifier for a specific version
* @param version The version number
* @param newVerifier The address of the verifier
*/
function updateVerifier(uint256 version, address newVerifier) external onlyAdmin {
require(version > 0, "Version must be greater than 0");
require(newVerifier != address(0), "Verifier cannot be zero address");
require((address)(verifiers[version]) != address(0), "Verifier not found for version");
address oldVerifier = address(verifiers[version]);
verifiers[version] = IZKPassportVerifier(newVerifier);
emit VerifierUpdated(version, oldVerifier, newVerifier);
}

function setPaused(bool _paused) external onlyGuardian {
paused = _paused;
emit PausedStatusChanged(_paused);
}
}
24 changes: 12 additions & 12 deletions src/solidity/src/ZKPassportVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract ZKPassportVerifier {
bool public paused;

// Mapping from vkey hash of each Outer Circuit to its Ultra Honk Verifier address
mapping(bytes32 => address) public vkeyHashToVerifier;
mapping(bytes32 => address) public vkeyHashToSubVerifier;

// Maybe make this immutable as this should most likely not change
IRootRegistry public rootRegistry;
Expand All @@ -28,8 +28,8 @@ contract ZKPassportVerifier {
event AdminUpdated(address indexed oldAdmin, address indexed newAdmin);
event PausedStatusChanged(bool paused);
event ZKPassportVerifierDeployed(address indexed admin, uint256 timestamp);
event VerifierAdded(bytes32 indexed vkeyHash, address indexed verifier);
event VerifierRemoved(bytes32 indexed vkeyHash);
event SubVerifierAdded(bytes32 indexed vkeyHash, address indexed verifier);
event SubVerifierRemoved(bytes32 indexed vkeyHash);
event CertificateRegistryRootAdded(bytes32 indexed certificateRegistryRoot);
event CertificateRegistryRootRemoved(bytes32 indexed certificateRegistryRoot);
event SanctionsTreesRootUpdates(bytes32 indexed _sanctionsTreesRoot);
Expand Down Expand Up @@ -66,20 +66,20 @@ contract ZKPassportVerifier {
emit PausedStatusChanged(_paused);
}

function addVerifiers(
function addSubVerifiers(
bytes32[] calldata vkeyHashes,
address[] calldata verifiers
) external onlyAdmin {
for (uint256 i = 0; i < vkeyHashes.length; i++) {
vkeyHashToVerifier[vkeyHashes[i]] = verifiers[i];
emit VerifierAdded(vkeyHashes[i], verifiers[i]);
vkeyHashToSubVerifier[vkeyHashes[i]] = verifiers[i];
emit SubVerifierAdded(vkeyHashes[i], verifiers[i]);
}
}

function removeVerifiers(bytes32[] calldata vkeyHashes) external onlyAdmin {
function removeSubVerifiers(bytes32[] calldata vkeyHashes) external onlyAdmin {
for (uint256 i = 0; i < vkeyHashes.length; i++) {
delete vkeyHashToVerifier[vkeyHashes[i]];
emit VerifierRemoved(vkeyHashes[i]);
delete vkeyHashToSubVerifier[vkeyHashes[i]];
emit SubVerifierRemoved(vkeyHashes[i]);
}
}

Expand All @@ -102,7 +102,7 @@ contract ZKPassportVerifier {
* @param isIDCard Whether the proof is an ID card
* @return disclosedData The data disclosed by the proof
*/
function getDisclosedData(
function getDisclosedData(
Commitments calldata commitments,
bool isIDCard
) public pure returns (DisclosedData memory disclosedData) {
Expand Down Expand Up @@ -589,7 +589,7 @@ contract ZKPassportVerifier {
}

function _getVerifier(bytes32 vkeyHash) internal view returns (address) {
address verifier = vkeyHashToVerifier[vkeyHash];
address verifier = vkeyHashToSubVerifier[vkeyHash];
require(verifier != address(0), "Verifier not found");
return verifier;
}
Expand Down Expand Up @@ -665,7 +665,7 @@ contract ZKPassportVerifier {
|| params.serviceConfig.devMode,
"Mock proofs are only allowed in dev mode"
);

// For now, only non-salted nullifiers are supported
// but salted nullifiers can be used in dev mode
// They will be later once a proper registration mechanism is implemented
Expand Down
Loading