add eu id support (#527)

* add eu id support

* add ofac and disclosure euid support

* add contract support for euid cards

---------

Co-authored-by: motemotech <i.am.nicoshark@gmail.com>
This commit is contained in:
turnoffthiscomputer
2025-05-30 12:24:11 -04:00
committed by GitHub
parent 8351609046
commit 5d01e29bf4
42 changed files with 3976 additions and 160 deletions

View File

@@ -0,0 +1,937 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {CircuitConstants} from "./constants/CircuitConstants.sol";
import {AttestationId} from "./constants/AttestationId.sol";
import {Formatter} from "./libraries/Formatter.sol";
import {CircuitAttributeHandler} from "./libraries/CircuitAttributeHandler.sol";
import {IIdentityVerificationHubV2} from "./interfaces/IIdentityVerificationHubV2.sol";
import {IIdentityRegistryV1} from "./interfaces/IIdentityRegistryV1.sol";
import {IRegisterCircuitVerifier} from "./interfaces/IRegisterCircuitVerifier.sol";
import {IVcAndDiscloseCircuitVerifier} from "./interfaces/IVcAndDiscloseCircuitVerifier.sol";
import {IDscCircuitVerifier} from "./interfaces/IDscCircuitVerifier.sol";
import {ImplRoot} from "./upgradeable/ImplRoot.sol";
import {IIdentityRegistryIdCardV1} from "./interfaces/IIdentityRegistryIdCardV1.sol";
/**
* @notice ⚠️ CRITICAL STORAGE LAYOUT WARNING ⚠️
* =============================================
*
* This contract uses the UUPS upgradeable pattern which makes storage layout EXTREMELY SENSITIVE.
*
* 🚫 NEVER MODIFY OR REORDER existing storage variables
* 🚫 NEVER INSERT new variables between existing ones
* 🚫 NEVER CHANGE THE TYPE of existing variables
*
* ✅ New storage variables MUST be added in one of these two ways ONLY:
* 1. At the END of the storage layout
* 2. In a new V2 contract that inherits from this V1
*
* Examples of forbidden changes:
* - Changing uint256 to uint128
* - Changing bytes32 to bytes
* - Changing array type to mapping
*
* For more detailed information about forbidden changes, please refer to:
* https://docs.openzeppelin.com/upgrades-plugins/writing-upgradeable#modifying-your-contracts
*
* ⚠️ VIOLATION OF THESE RULES WILL CAUSE CATASTROPHIC STORAGE COLLISIONS IN FUTURE UPGRADES ⚠️
* =============================================
*/
/**
* @title IdentityVerificationHubStorageV2
* @notice Storage contract for IdentityVerificationHubImplV2.
* @dev Inherits from ImplRoot to include upgradeability functionality.
*/
abstract contract IdentityVerificationHubStorageV2 is
ImplRoot
{
// ====================================================
// Storage Variables
// ====================================================
/// @notice Address of the Identity Registry.
address internal _registry;
/// @notice Address of the VC and Disclose circuit verifier.
address internal _vcAndDiscloseCircuitVerifier;
/// @notice Mapping from signature type to register circuit verifier addresses.
mapping(uint256 => address) internal _sigTypeToRegisterCircuitVerifiers;
/// @notice Mapping from signature type to DSC circuit verifier addresses..
mapping(uint256 => address) internal _sigTypeToDscCircuitVerifiers;
/// @notice Address of the Identity Registry for Id Cards.
address internal _registryIdCard;
/// @notice Address of the VC and Disclose circuit verifier for Id Cards.
address internal _vcAndDiscloseCircuitVerifierIdCard;
/// @notice Mapping from signature type to register circuit verifier addresses for Id Cards.
mapping(uint256 => address) internal _sigTypeToRegisterCircuitVerifiersIdCard;
/// @notice Mapping from signature type to DSC circuit verifier addresses for Id Cards.
mapping(uint256 => address) internal _sigTypeToDscCircuitVerifiersIdCard;
}
/**
* @title IdentityVerificationHubImplV2
* @notice Implementation contract for the Identity Verification Hub.
* @dev Provides functions for registering commitments and verifying groth16 proofs and inclusion proofs.
*/
contract IdentityVerificationHubImplV2 is
IdentityVerificationHubStorageV2,
IIdentityVerificationHubV2
{
using Formatter for uint256;
uint256 constant MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH = 40;
// ====================================================
// Events
// ====================================================
/**
* @notice Emitted when the hub is initialized.
* @param registry The address of the registry.
* @param vcAndDiscloseCircuitVerifier The address of the VC and Disclose circuit verifier.
* @param registerCircuitVerifierIds Array of register circuit verifier ids.
* @param registerCircuitVerifiers Array of register circuit verifier addresses.
* @param dscCircuitVerifierIds Array of DSC circuit verifier ids.
* @param dscCircuitVerifiers Array of DSC circuit verifier addresses.
*/
event HubInitialized(
address registry,
address vcAndDiscloseCircuitVerifier,
uint256[] registerCircuitVerifierIds,
address[] registerCircuitVerifiers,
uint256[] dscCircuitVerifierIds,
address[] dscCircuitVerifiers,
address registryIdCard,
address vcAndDiscloseCircuitVerifierIdCard,
uint256[] registerCircuitVerifierIdsIdCard,
address[] registerCircuitVerifiersIdCard,
uint256[] dscCircuitVerifierIdsIdCard,
address[] dscCircuitVerifiersIdCard
);
/**
* @notice Emitted when the registry address is updated.
* @param registry The new registry address.
*/
event RegistryUpdated(address registry);
/**
* @notice Emitted when the VC and Disclose circuit verifier is updated.
* @param vcAndDiscloseCircuitVerifier The new VC and Disclose circuit verifier address.
*/
event VcAndDiscloseCircuitUpdated(address vcAndDiscloseCircuitVerifier);
/**
* @notice Emitted when a register circuit verifier is updated.
* @param typeId The signature type id.
* @param verifier The new verifier address for the register circuit.
*/
event RegisterCircuitVerifierUpdated(uint256 typeId, address verifier);
/**
* @notice Emitted when a DSC circuit verifier is updated.
* @param typeId The signature type id.
* @param verifier The new verifier address for the DSC circuit.
*/
event DscCircuitVerifierUpdated(uint256 typeId, address verifier);
// ====================================================
// Errors
// ====================================================
/// @notice Thrown when the lengths of provided arrays do not match.
/// @dev Used when initializing or updating arrays that must have equal length.
error LENGTH_MISMATCH();
/// @notice Thrown when no verifier is set for a given signature type.
/// @dev Indicates that the mapping lookup for the verifier returned the zero address.
error NO_VERIFIER_SET();
/// @notice Thrown when the current date in the proof is not within the valid range.
/// @dev Ensures that the provided proof's date is within one day of the expected start time.
error CURRENT_DATE_NOT_IN_VALID_RANGE();
/// @notice Thrown when the 'older than' attribute in the proof is invalid.
/// @dev The 'older than' value derived from the proof does not match the expected criteria.
error INVALID_OLDER_THAN();
/// @notice Thrown when the provided forbidden countries list is invalid.
/// @dev The forbidden countries list in the proof does not match the expected packed data.
error INVALID_FORBIDDEN_COUNTRIES();
/// @notice Thrown when the OFAC check fails.
/// @dev Indicates that the proof did not satisfy the required OFAC conditions.
error INVALID_OFAC();
/// @notice Thrown when the register circuit proof is invalid.
/// @dev The register circuit verifier did not validate the provided proof.
error INVALID_REGISTER_PROOF();
/// @notice Thrown when the DSC circuit proof is invalid.
/// @dev The DSC circuit verifier did not validate the provided proof.
error INVALID_DSC_PROOF();
/// @notice Thrown when the VC and Disclose proof is invalid.
/// @dev The VC and Disclose circuit verifier did not validate the provided proof.
error INVALID_VC_AND_DISCLOSE_PROOF();
/// @notice Thrown when the provided commitment root is invalid.
/// @dev Used in proofs to ensure that the commitment root matches the expected value in the registry.
error INVALID_COMMITMENT_ROOT();
/// @notice Thrown when the provided OFAC root is invalid.
/// @dev Indicates that the OFAC root from the proof does not match the expected OFAC root.
error INVALID_OFAC_ROOT();
/// @notice Thrown when the provided CSCA root is invalid.
/// @dev Indicates that the CSCA root from the DSC proof does not match the expected CSCA root.
error INVALID_CSCA_ROOT();
/// @notice Thrown when the revealed data type is invalid or not supported.
/// @dev Raised during the processing of revealed data if it does not match any supported type.
error INVALID_REVEALED_DATA_TYPE();
// ====================================================
// Constructor
// ====================================================
/**
* @notice Constructor that disables initializers.
* @dev Prevents direct initialization of the implementation contract.
*/
constructor() {
_disableInitializers();
}
// ====================================================
// Initializer
// ====================================================
/**
* @notice Initializes the hub implementation.
* @dev Sets the registry, VC and Disclose circuit verifier address, register circuit verifiers, and DSC circuit verifiers.
* @param registryAddress The address of the Identity Registry.
* @param vcAndDiscloseCircuitVerifierAddress The address of the VC and Disclose circuit verifier.
* @param registerCircuitVerifierIds Array of ids for register circuit verifiers.
* @param registerCircuitVerifierAddresses Array of addresses for register circuit verifiers.
* @param dscCircuitVerifierIds Array of ids for DSC circuit verifiers.
* @param dscCircuitVerifierAddresses Array of addresses for DSC circuit verifiers.
*/
function initialize(
address registryAddress,
address vcAndDiscloseCircuitVerifierAddress,
uint256[] memory registerCircuitVerifierIds,
address[] memory registerCircuitVerifierAddresses,
uint256[] memory dscCircuitVerifierIds,
address[] memory dscCircuitVerifierAddresses,
address registryIdCardAddress,
address vcAndDiscloseCircuitVerifierIdCardAddress,
uint256[] memory registerCircuitVerifierIdsIdCard,
address[] memory registerCircuitVerifiersIdCardAddresses,
uint256[] memory dscCircuitVerifierIdsIdCard,
address[] memory dscCircuitVerifiersIdCardAddresses
) external initializer {
__ImplRoot_init();
_registry = registryAddress;
_vcAndDiscloseCircuitVerifier = vcAndDiscloseCircuitVerifierAddress;
if (registerCircuitVerifierIds.length != registerCircuitVerifierAddresses.length) {
revert LENGTH_MISMATCH();
}
if (dscCircuitVerifierIds.length != dscCircuitVerifierAddresses.length) {
revert LENGTH_MISMATCH();
}
for (uint256 i = 0; i < registerCircuitVerifierIds.length; i++) {
_sigTypeToRegisterCircuitVerifiers[registerCircuitVerifierIds[i]] = registerCircuitVerifierAddresses[i];
}
for (uint256 i = 0; i < dscCircuitVerifierIds.length; i++) {
_sigTypeToDscCircuitVerifiers[dscCircuitVerifierIds[i]] = dscCircuitVerifierAddresses[i];
}
_registryIdCard = registryIdCardAddress;
_vcAndDiscloseCircuitVerifierIdCard = vcAndDiscloseCircuitVerifierIdCardAddress;
if (registerCircuitVerifierIdsIdCard.length != registerCircuitVerifiersIdCardAddresses.length) {
revert LENGTH_MISMATCH();
}
if (dscCircuitVerifierIdsIdCard.length != dscCircuitVerifiersIdCardAddresses.length) {
revert LENGTH_MISMATCH();
}
for (uint256 i = 0; i < registerCircuitVerifierIdsIdCard.length; i++) {
_sigTypeToRegisterCircuitVerifiersIdCard[registerCircuitVerifierIdsIdCard[i]] = registerCircuitVerifiersIdCardAddresses[i];
}
for (uint256 i = 0; i < dscCircuitVerifierIdsIdCard.length; i++) {
_sigTypeToDscCircuitVerifiersIdCard[dscCircuitVerifierIdsIdCard[i]] = dscCircuitVerifiersIdCardAddresses[i];
}
emit HubInitialized(
registryAddress,
vcAndDiscloseCircuitVerifierAddress,
registerCircuitVerifierIds,
registerCircuitVerifierAddresses,
dscCircuitVerifierIds,
dscCircuitVerifierAddresses,
registryIdCardAddress,
vcAndDiscloseCircuitVerifierIdCardAddress,
registerCircuitVerifierIdsIdCard,
registerCircuitVerifiersIdCardAddresses,
dscCircuitVerifierIdsIdCard,
dscCircuitVerifiersIdCardAddresses
);
}
// ====================================================
// External View Functions
// ====================================================
/**
* @notice Retrieves the registry address.
* @return The address of the Identity Registry.
*/
function registry()
external
virtual
onlyProxy
view
returns (address)
{
return _registry;
}
/**
* @notice Retrieves the VC and Disclose circuit verifier address.
* @return The address of the VC and Disclose circuit verifier.
*/
function vcAndDiscloseCircuitVerifier()
external
virtual
onlyProxy
view
returns (address)
{
return _vcAndDiscloseCircuitVerifier;
}
/**
* @notice Retrieves the register circuit verifier address for a given signature type.
* @param typeId The signature type identifier.
* @return The register circuit verifier address.
*/
function sigTypeToRegisterCircuitVerifiers(
uint256 typeId
)
external
virtual
onlyProxy
view
returns (address)
{
return _sigTypeToRegisterCircuitVerifiers[typeId];
}
/**
* @notice Retrieves the DSC circuit verifier address for a given signature type.
* @param typeId The signature type identifier.
* @return The DSC circuit verifier address.
*/
function sigTypeToDscCircuitVerifiers(
uint256 typeId
)
external
virtual
onlyProxy
view
returns (address)
{
return _sigTypeToDscCircuitVerifiers[typeId];
}
/**
* @notice Converts packed revealed data into a human-readable format.
* @dev Uses Formatter and CircuitAttributeHandler to decode the data.
* @param revealedDataPacked An array of three packed uint256 values.
* @param types An array of RevealedDataType indicating the types of data expected.
* @return A ReadableRevealedData struct containing the decoded data.
*/
function getReadableRevealedData(
uint256[3] memory revealedDataPacked,
RevealedDataType[] memory types
)
external
virtual
onlyProxy
view
returns (ReadableRevealedData memory)
{
bytes memory charcodes = Formatter.fieldElementsToBytes(
revealedDataPacked
);
ReadableRevealedData memory attrs;
for (uint256 i = 0; i < types.length; i++) {
RevealedDataType dataType = types[i];
if (dataType == RevealedDataType.ISSUING_STATE) {
attrs.issuingState = CircuitAttributeHandler.getIssuingState(charcodes);
} else if (dataType == RevealedDataType.NAME) {
attrs.name = CircuitAttributeHandler.getName(charcodes);
} else if (dataType == RevealedDataType.PASSPORT_NUMBER) {
attrs.passportNumber = CircuitAttributeHandler.getPassportNumber(charcodes);
} else if (dataType == RevealedDataType.NATIONALITY) {
attrs.nationality = CircuitAttributeHandler.getNationality(charcodes);
} else if (dataType == RevealedDataType.DATE_OF_BIRTH) {
attrs.dateOfBirth = CircuitAttributeHandler.getDateOfBirth(charcodes);
} else if (dataType == RevealedDataType.GENDER) {
attrs.gender = CircuitAttributeHandler.getGender(charcodes);
} else if (dataType == RevealedDataType.EXPIRY_DATE) {
attrs.expiryDate = CircuitAttributeHandler.getExpiryDate(charcodes);
} else if (dataType == RevealedDataType.OLDER_THAN) {
attrs.olderThan = CircuitAttributeHandler.getOlderThan(charcodes);
} else if (dataType == RevealedDataType.PASSPORT_NO_OFAC) {
attrs.passportNoOfac = CircuitAttributeHandler.getPassportNoOfac(charcodes);
} else if (dataType == RevealedDataType.NAME_AND_DOB_OFAC) {
attrs.nameAndDobOfac = CircuitAttributeHandler.getNameAndDobOfac(charcodes);
} else if (dataType == RevealedDataType.NAME_AND_YOB_OFAC) {
attrs.nameAndYobOfac = CircuitAttributeHandler.getNameAndYobOfac(charcodes);
}
}
return attrs;
}
/**
* @notice Extracts the forbidden countries list from packed data.
* @param forbiddenCountriesListPacked Packed data representing forbidden countries.
* @return An array of strings with a maximum length of MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH.
*/
function getReadableForbiddenCountries(
uint256[4] memory forbiddenCountriesListPacked
)
external
virtual
onlyProxy
view
returns (string[MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH] memory)
{
return Formatter.extractForbiddenCountriesFromPacked(forbiddenCountriesListPacked);
}
/**
* @notice Verifies the VC and Disclose proof.
* @dev Checks commitment roots, OFAC root, current date range, and other attributes depending on verification configuration.
* @param proof The VcAndDiscloseHubProof containing the proof data.
* @return result A VcAndDiscloseVerificationResult struct with the verification results.
*/
function verifyVcAndDisclose(
VcAndDiscloseHubProof memory proof
)
external
virtual
view
onlyProxy
returns (VcAndDiscloseVerificationResult memory)
{
VcAndDiscloseVerificationResult memory result;
result.identityCommitmentRoot = _verifyVcAndDiscloseProof(proof);
for (uint256 i = 0; i < 3; i++) {
result.revealedDataPacked[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX + i];
}
for (uint256 i = 0; i < 4; i++) {
result.forbiddenCountriesListPacked[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX + i];
}
result.nullifier = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX];
result.attestationId = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX];
result.userIdentifier = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX];
result.scope = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX];
return result;
}
// ====================================================
// External Functions - Registration
// ====================================================
/**
* @notice Registers a passport commitment using a register circuit proof.
* @dev Verifies the proof and then calls the Identity Registry to register the commitment.
* @param registerCircuitVerifierId The identifier for the register circuit verifier to use.
* @param registerCircuitProof The register circuit proof data.
*/
function registerPassportCommitment(
uint256 registerCircuitVerifierId,
IRegisterCircuitVerifier.RegisterCircuitProof memory registerCircuitProof
)
external
virtual
onlyProxy
{
_verifyPassportRegisterProof(registerCircuitVerifierId, registerCircuitProof);
IIdentityRegistryV1(_registry).registerCommitment(
AttestationId.E_PASSPORT,
registerCircuitProof.pubSignals[CircuitConstants.REGISTER_NULLIFIER_INDEX],
registerCircuitProof.pubSignals[CircuitConstants.REGISTER_COMMITMENT_INDEX]
);
}
/**
* @notice Registers a DSC key commitment using a DSC circuit proof.
* @dev Verifies the DSC proof and then calls the Identity Registry to register the dsc key commitment.
* @param dscCircuitVerifierId The identifier for the DSC circuit verifier to use.
* @param dscCircuitProof The DSC circuit proof data.
*/
function registerDscKeyCommitment(
uint256 dscCircuitVerifierId,
IDscCircuitVerifier.DscCircuitProof memory dscCircuitProof
)
external
virtual
onlyProxy
{
_verifyPassportDscProof(dscCircuitVerifierId, dscCircuitProof);
IIdentityRegistryV1(_registry).registerDscKeyCommitment(
dscCircuitProof.pubSignals[CircuitConstants.DSC_TREE_LEAF_INDEX]
);
}
/**
* @notice Registers an Id Card commitment using a register circuit proof.
* @dev Verifies the proof and then calls the Id Card Identity Registry to register the commitment.
* @param registerCircuitVerifierId The identifier for the register circuit verifier to use.
* @param registerCircuitProof The register circuit proof data.
*/
function registerIdCardCommitment(
uint256 registerCircuitVerifierId,
IRegisterCircuitVerifier.RegisterCircuitProof memory registerCircuitProof
)
external
onlyProxy
{
_verifyIdCardRegisterProof(registerCircuitVerifierId, registerCircuitProof);
IIdentityRegistryIdCardV1(_registryIdCard).registerCommitment(
AttestationId.ID_CARD,
registerCircuitProof.pubSignals[CircuitConstants.REGISTER_NULLIFIER_INDEX],
registerCircuitProof.pubSignals[CircuitConstants.REGISTER_COMMITMENT_INDEX]
);
}
/**
* @notice Registers an Id Card DSC key commitment using a DSC circuit proof.
* @dev Verifies the DSC proof and then calls the Id Card Identity Registry to register the dsc key commitment.
* @param dscCircuitVerifierId The identifier for the DSC circuit verifier to use.
* @param dscCircuitProof The DSC circuit proof data.
*/
function registerIdCardDscKeyCommitment(
uint256 dscCircuitVerifierId,
IDscCircuitVerifier.DscCircuitProof memory dscCircuitProof
)
external
onlyProxy
{
_verifyIdCardDscProof(dscCircuitVerifierId, dscCircuitProof);
IIdentityRegistryIdCardV1(_registryIdCard).registerDscKeyCommitment(
dscCircuitProof.pubSignals[CircuitConstants.DSC_TREE_LEAF_INDEX]
);
}
// ====================================================
// External Functions - Only Owner
// ====================================================
/**
* @notice Updates the registry address.
* @param registryAddress The new registry address.
*/
function updateRegistry(
address registryAddress
)
external
virtual
onlyProxy
onlyOwner
{
_registry = registryAddress;
emit RegistryUpdated(registryAddress);
}
/**
* @notice Updates the VC and Disclose circuit verifier address.
* @param vcAndDiscloseCircuitVerifierAddress The new VC and Disclose circuit verifier address.
*/
function updateVcAndDiscloseCircuit(
address vcAndDiscloseCircuitVerifierAddress
)
external
virtual
onlyProxy
onlyOwner
{
_vcAndDiscloseCircuitVerifier = vcAndDiscloseCircuitVerifierAddress;
emit VcAndDiscloseCircuitUpdated(vcAndDiscloseCircuitVerifierAddress);
}
/**
* @notice Updates the register circuit verifier for a specific signature type.
* @param typeId The signature type identifier.
* @param verifierAddress The new register circuit verifier address.
*/
function updateRegisterCircuitVerifier(
uint256 typeId,
address verifierAddress
)
external
virtual
onlyProxy
onlyOwner
{
_sigTypeToRegisterCircuitVerifiers[typeId] = verifierAddress;
emit RegisterCircuitVerifierUpdated(typeId, verifierAddress);
}
/**
* @notice Updates the DSC circuit verifier for a specific signature type.
* @param typeId The signature type identifier.
* @param verifierAddress The new DSC circuit verifier address.
*/
function updateDscVerifier(
uint256 typeId,
address verifierAddress
)
external
virtual
onlyProxy
onlyOwner
{
_sigTypeToDscCircuitVerifiers[typeId] = verifierAddress;
emit DscCircuitVerifierUpdated(typeId, verifierAddress);
}
/**
* @notice Batch updates register circuit verifiers.
* @param typeIds An array of signature type identifiers.
* @param verifierAddresses An array of new register circuit verifier addresses.
*/
function batchUpdateRegisterCircuitVerifiers(
uint256[] calldata typeIds,
address[] calldata verifierAddresses
)
external
virtual
onlyProxy
onlyOwner
{
if (typeIds.length != verifierAddresses.length) {
revert LENGTH_MISMATCH();
}
for (uint256 i = 0; i < typeIds.length; i++) {
_sigTypeToRegisterCircuitVerifiers[typeIds[i]] = verifierAddresses[i];
emit RegisterCircuitVerifierUpdated(typeIds[i], verifierAddresses[i]);
}
}
/**
* @notice Batch updates DSC circuit verifiers.
* @param typeIds An array of signature type identifiers.
* @param verifierAddresses An array of new DSC circuit verifier addresses.
*/
function batchUpdateDscCircuitVerifiers(
uint256[] calldata typeIds,
address[] calldata verifierAddresses
)
external
virtual
onlyProxy
onlyOwner
{
if (typeIds.length != verifierAddresses.length) {
revert LENGTH_MISMATCH();
}
for (uint256 i = 0; i < typeIds.length; i++) {
_sigTypeToDscCircuitVerifiers[typeIds[i]] = verifierAddresses[i];
emit DscCircuitVerifierUpdated(typeIds[i], verifierAddresses[i]);
}
}
// ====================================================
// External View Functions - Id Card
// ====================================================
/**
* @notice Retrieves the Id Card registry address.
* @return The address of the Id Card Identity Registry.
*/
function registryIdCard()
external
view
onlyProxy
returns (address)
{
return _registryIdCard;
}
/**
* @notice Retrieves the Id Card VC and Disclose circuit verifier address.
* @return The address of the Id Card VC and Disclose circuit verifier.
*/
function vcAndDiscloseCircuitVerifierIdCard()
external
view
onlyProxy
returns (address)
{
return _vcAndDiscloseCircuitVerifierIdCard;
}
/**
* @notice Retrieves the Id Card register circuit verifier address for a given signature type.
* @param typeId The signature type identifier.
* @return The Id Card register circuit verifier address.
*/
function sigTypeToRegisterCircuitVerifiersIdCard(
uint256 typeId
)
external
view
onlyProxy
returns (address)
{
return _sigTypeToRegisterCircuitVerifiersIdCard[typeId];
}
/**
* @notice Retrieves the Id Card DSC circuit verifier address for a given signature type.
* @param typeId The signature type identifier.
* @return The Id Card DSC circuit verifier address.
*/
function sigTypeToDscCircuitVerifiersIdCard(
uint256 typeId
)
external
view
onlyProxy
returns (address)
{
return _sigTypeToDscCircuitVerifiersIdCard[typeId];
}
// ====================================================
// Internal Functions
// ====================================================
/**
* @notice Verifies the VC and Disclose proof.
* @dev Checks commitment roots, OFAC root, current date range, and other attributes depending on verification configuration.
* @param proof The VcAndDiscloseHubProof containing the proof data.
* @return identityCommitmentRoot The verified identity commitment root from the proof.
*/
function _verifyVcAndDiscloseProof(
VcAndDiscloseHubProof memory proof
)
internal
view
returns (uint256 identityCommitmentRoot)
{
// verify identity commitment root
if (!IIdentityRegistryV1(_registry).checkIdentityCommitmentRoot(proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_MERKLE_ROOT_INDEX])) {
revert INVALID_COMMITMENT_ROOT();
}
// verify current date
uint[6] memory dateNum;
for (uint256 i = 0; i < 6; i++) {
dateNum[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_CURRENT_DATE_INDEX + i];
}
uint currentTimestamp = Formatter.proofDateToUnixTimestamp(dateNum);
if(
currentTimestamp < _getStartOfDayTimestamp() - 1 days + 1 ||
currentTimestamp > _getStartOfDayTimestamp() + 1 days - 1
) {
revert CURRENT_DATE_NOT_IN_VALID_RANGE();
}
// verify attributes
uint256[3] memory revealedDataPacked;
for (uint256 i = 0; i < 3; i++) {
revealedDataPacked[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX + i];
}
if (proof.olderThanEnabled) {
if (!CircuitAttributeHandler.compareOlderThan(Formatter.fieldElementsToBytes(revealedDataPacked), proof.olderThan)) {
revert INVALID_OLDER_THAN();
}
}
if (proof.ofacEnabled[0] || proof.ofacEnabled[1] || proof.ofacEnabled[2]) {
if (!CircuitAttributeHandler.compareOfac(
Formatter.fieldElementsToBytes(revealedDataPacked),
proof.ofacEnabled[0],
proof.ofacEnabled[1],
proof.ofacEnabled[2]
)) {
revert INVALID_OFAC();
}
if (!IIdentityRegistryV1(_registry).checkOfacRoots(
proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_PASSPORT_NO_SMT_ROOT_INDEX],
proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NAME_DOB_SMT_ROOT_INDEX],
proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NAME_YOB_SMT_ROOT_INDEX]
)) {
revert INVALID_OFAC_ROOT();
}
}
if (proof.forbiddenCountriesEnabled) {
for (uint256 i = 0; i < 4; i++) {
if (
proof.forbiddenCountriesListPacked[i] != proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX + i]
) {
revert INVALID_FORBIDDEN_COUNTRIES();
}
}
}
// verify the proof using the VC and Disclose circuit verifier
if (!IVcAndDiscloseCircuitVerifier(_vcAndDiscloseCircuitVerifier).verifyProof(proof.vcAndDiscloseProof.a, proof.vcAndDiscloseProof.b, proof.vcAndDiscloseProof.c, proof.vcAndDiscloseProof.pubSignals)) {
revert INVALID_VC_AND_DISCLOSE_PROOF();
}
return proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_MERKLE_ROOT_INDEX];
}
/**
* @notice Verifies the passport register circuit proof.
* @dev Uses the register circuit verifier specified by registerCircuitVerifierId.
* @param registerCircuitVerifierId The identifier for the register circuit verifier.
* @param registerCircuitProof The register circuit proof data.
*/
function _verifyPassportRegisterProof(
uint256 registerCircuitVerifierId,
IRegisterCircuitVerifier.RegisterCircuitProof memory registerCircuitProof
)
internal
view
{
address verifier = _sigTypeToRegisterCircuitVerifiers[registerCircuitVerifierId];
if (verifier == address(0)) {
revert NO_VERIFIER_SET();
}
if (!IIdentityRegistryV1(_registry).checkDscKeyCommitmentMerkleRoot(registerCircuitProof.pubSignals[CircuitConstants.REGISTER_MERKLE_ROOT_INDEX])) {
revert INVALID_COMMITMENT_ROOT();
}
if(!IRegisterCircuitVerifier(verifier).verifyProof(
registerCircuitProof.a,
registerCircuitProof.b,
registerCircuitProof.c,
registerCircuitProof.pubSignals
)) {
revert INVALID_REGISTER_PROOF();
}
}
/**
* @notice Verifies the passport DSC circuit proof.
* @dev Uses the DSC circuit verifier specified by dscCircuitVerifierId.
* @param dscCircuitVerifierId The identifier for the DSC circuit verifier.
* @param dscCircuitProof The DSC circuit proof data.
*/
function _verifyPassportDscProof(
uint256 dscCircuitVerifierId,
IDscCircuitVerifier.DscCircuitProof memory dscCircuitProof
)
internal
view
{
address verifier = _sigTypeToDscCircuitVerifiers[dscCircuitVerifierId];
if (verifier == address(0)) {
revert NO_VERIFIER_SET();
}
if (!IIdentityRegistryV1(_registry).checkCscaRoot(dscCircuitProof.pubSignals[CircuitConstants.DSC_CSCA_ROOT_INDEX])) {
revert INVALID_CSCA_ROOT();
}
if(!IDscCircuitVerifier(verifier).verifyProof(
dscCircuitProof.a,
dscCircuitProof.b,
dscCircuitProof.c,
dscCircuitProof.pubSignals
)) {
revert INVALID_DSC_PROOF();
}
}
/**
* @notice Verifies the Id Card register circuit proof.
* @dev Uses the register circuit verifier specified by registerCircuitVerifierId for Id Cards.
* @param registerCircuitVerifierId The identifier for the register circuit verifier.
* @param registerCircuitProof The register circuit proof data.
*/
function _verifyIdCardRegisterProof(
uint256 registerCircuitVerifierId,
IRegisterCircuitVerifier.RegisterCircuitProof memory registerCircuitProof
)
internal
view
{
address verifier = _sigTypeToRegisterCircuitVerifiersIdCard[registerCircuitVerifierId];
if (verifier == address(0)) {
revert NO_VERIFIER_SET();
}
if (!IIdentityRegistryIdCardV1(_registryIdCard).checkDscKeyCommitmentMerkleRoot(registerCircuitProof.pubSignals[CircuitConstants.REGISTER_MERKLE_ROOT_INDEX])) {
revert INVALID_COMMITMENT_ROOT();
}
if(!IRegisterCircuitVerifier(verifier).verifyProof(
registerCircuitProof.a,
registerCircuitProof.b,
registerCircuitProof.c,
registerCircuitProof.pubSignals
)) {
revert INVALID_REGISTER_PROOF();
}
}
/**
* @notice Verifies the Id Card DSC circuit proof.
* @dev Uses the DSC circuit verifier specified by dscCircuitVerifierId for Id Cards.
* @param dscCircuitVerifierId The identifier for the DSC circuit verifier.
* @param dscCircuitProof The DSC circuit proof data.
*/
function _verifyIdCardDscProof(
uint256 dscCircuitVerifierId,
IDscCircuitVerifier.DscCircuitProof memory dscCircuitProof
)
internal
view
{
address verifier = _sigTypeToDscCircuitVerifiersIdCard[dscCircuitVerifierId];
if (verifier == address(0)) {
revert NO_VERIFIER_SET();
}
if (!IIdentityRegistryIdCardV1(_registryIdCard).checkCscaRoot(dscCircuitProof.pubSignals[CircuitConstants.DSC_CSCA_ROOT_INDEX])) {
revert INVALID_CSCA_ROOT();
}
if(!IDscCircuitVerifier(verifier).verifyProof(
dscCircuitProof.a,
dscCircuitProof.b,
dscCircuitProof.c,
dscCircuitProof.pubSignals
)) {
revert INVALID_DSC_PROOF();
}
}
/**
* @notice Retrieves the timestamp for the start of the current day.
* @dev Calculated by subtracting the remainder of block.timestamp modulo 1 day.
* @return The Unix timestamp representing the start of the day.
*/
function _getStartOfDayTimestamp() internal view returns (uint256) {
return block.timestamp - (block.timestamp % 1 days);
}
}

View File

@@ -14,4 +14,5 @@ library AttestationId {
* Here it is hardcoded as bytes32(uint256(1)) for demonstration purposes.
*/
bytes32 constant E_PASSPORT = bytes32(uint256(1));
bytes32 constant ID_CARD = bytes32(uint256(2));
}

View File

@@ -0,0 +1,164 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title IIdentityRegistryV1
* @notice Interface for the Identity Registry v1.
* @dev This interface exposes only the external functions accessible by regular callers,
* i.e. functions that are not owner-restricted.
*/
interface IIdentityRegistryIdCardV1 {
/**
* @notice Retrieves the address of the registered identity verification hub.
* @return The address of the hub.
*/
function hub() external view returns (address);
/**
* @notice Checks if a specific nullifier is already registered for the given attestation.
* @param attestationId The attestation identifier.
* @param nullifier The nullifier to check.
* @return True if the nullifier is registered; otherwise, false.
*/
function nullifiers(
bytes32 attestationId,
uint256 nullifier
) external view returns (bool);
/**
* @notice Checks whether a DSC key commitment is registered.
* @param commitment The DSC key commitment to check.
* @return True if the commitment is registered, false otherwise.
*/
function isRegisteredDscKeyCommitment(
uint256 commitment
) external view returns (bool);
/**
* @notice Retrieves the timestamp at which a given Merkle tree root was created.
* @param root The Merkle tree root.
* @return The creation timestamp for the provided root.
*/
function rootTimestamps(uint256 root) external view returns (uint256);
/**
* @notice Checks if the identity commitment Merkle tree contains the specified root.
* @param root The Merkle tree root to check.
* @return True if the root exists in the tree, false otherwise.
*/
function checkIdentityCommitmentRoot(
uint256 root
) external view returns (bool);
/**
* @notice Retrieves the total number of identity commitments in the Merkle tree.
* @return The size (i.e., count) of the identity commitment Merkle tree.
*/
function getIdentityCommitmentMerkleTreeSize() external view returns (uint256);
/**
* @notice Retrieves the current Merkle root of the identity commitments.
* @return The current identity commitment Merkle root.
*/
function getIdentityCommitmentMerkleRoot() external view returns (uint256);
/**
* @notice Retrieves the index of a specific identity commitment in the Merkle tree.
* @param commitment The identity commitment to locate.
* @return The index position of the provided commitment.
*/
function getIdentityCommitmentIndex(
uint256 commitment
) external view returns (uint256);
/**
* @notice Retrieves the current name and date of birth OFAC root.
* @return The current name and date of birth OFAC root value.
*/
function getNameAndDobOfacRoot() external view returns (uint256);
/**
* @notice Retrieves the current name and year of birth OFAC root.
* @return The current name and year of birth OFAC root value.
*/
function getNameAndYobOfacRoot() external view returns (uint256);
/**
* @notice Checks if the provided OFAC roots match the stored OFAC roots.
* @param nameAndDobRoot The name and date of birth OFAC root to verify.
* @param nameAndYobRoot The name and year of birth OFAC root to verify.
* @return True if all provided roots match the stored values, false otherwise.
*/
function checkOfacRoots(
uint256 nameAndDobRoot,
uint256 nameAndYobRoot
) external view returns (bool);
/**
* @notice Retrieves the current CSCA root.
* @return The current CSCA root value.
*/
function getCscaRoot() external view returns (uint256);
/**
* @notice Checks if the provided CSCA root matches the stored CSCA root.
* @param root The CSCA root to verify.
* @return True if the given root equals the stored CSCA root, otherwise false.
*/
function checkCscaRoot(
uint256 root
) external view returns (bool);
/**
* @notice Retrieves the current Merkle root of the DSC key commitments.
* @return The current DSC key commitment Merkle root.
*/
function getDscKeyCommitmentMerkleRoot() external view returns (uint256);
/**
* @notice Checks if the provided root matches the DSC key commitment Merkle root.
* @param root The root to check.
* @return True if it matches the current root, false otherwise.
*/
function checkDscKeyCommitmentMerkleRoot(
uint256 root
) external view returns (bool);
/**
* @notice Retrieves the total number of DSC key commitments in the Merkle tree.
* @return The DSC key commitment Merkle tree size.
*/
function getDscKeyCommitmentTreeSize() external view returns (uint256);
/**
* @notice Retrieves the index of a specific DSC key commitment in the Merkle tree.
* @param commitment The DSC key commitment to locate.
* @return The index of the provided commitment.
*/
function getDscKeyCommitmentIndex(
uint256 commitment
) external view returns (uint256);
/**
* @notice Registers a new identity commitment.
* @dev Must be called by the identity verification hub. Reverts if the nullifier has already been used.
* @param attestationId The attestation identifier associated with the commitment.
* @param nullifier A unique nullifier to prevent double registration.
* @param commitment The identity commitment to register.
*/
function registerCommitment(
bytes32 attestationId,
uint256 nullifier,
uint256 commitment
) external;
/**
* @notice Registers a new DSC key commitment.
* @dev Must be called by the identity verification hub. Reverts if the DSC key commitment is already registered.
* @param dscCommitment The DSC key commitment to register.
*/
function registerDscKeyCommitment(
uint256 dscCommitment
) external;
}

View File

@@ -0,0 +1,255 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IRegisterCircuitVerifier} from "./IRegisterCircuitVerifier.sol";
import {IDscCircuitVerifier} from "./IDscCircuitVerifier.sol";
import {IVcAndDiscloseCircuitVerifier} from "./IVcAndDiscloseCircuitVerifier.sol";
import {CircuitConstants} from "../constants/CircuitConstants.sol";
/**
* @title IIdentityVerificationHubV2
* @notice Interface for the Identity Verification Hub for verifying zero-knowledge proofs using VC and Disclose circuits.
* @dev Defines data structures and external functions for verifying proofs and recovering human-readable data.
*/
interface IIdentityVerificationHubV2 {
/**
* @notice Enum representing types of data that may be revealed.
*/
enum RevealedDataType {
ISSUING_STATE, // The issuing state of the passport.
NAME, // The full name of the passport holder.
PASSPORT_NUMBER, // The passport number.
NATIONALITY, // The nationality.
DATE_OF_BIRTH, // The date of birth.
GENDER, // The gender.
EXPIRY_DATE, // The passport expiry date.
OLDER_THAN, // The "older than" age verification value.
PASSPORT_NO_OFAC, // The passport number OFAC status.
NAME_AND_DOB_OFAC, // The name and date of birth OFAC verification result.
NAME_AND_YOB_OFAC // The name and year of birth OFAC verification result.
}
/**
* @notice Structure representing the verification result of a VC and Disclose proof.
* @param attestationId The attestation identifier from the proof.
* @param scope The scope of the verification.
* @param userIdentifier Unique identifier for the user.
* @param nullifier A value used to prevent double registration.
* @param identityCommitmentRoot The root of the identity commitment.
* @param revealedDataPacked Packed revealed data.
* @param forbiddenCountriesListPacked Packed forbidden countries list.
*/
struct VcAndDiscloseVerificationResult {
uint256 attestationId;
uint256 scope;
uint256 userIdentifier;
uint256 nullifier;
uint256 identityCommitmentRoot;
uint256[3] revealedDataPacked;
uint256[4] forbiddenCountriesListPacked;
}
/**
* @notice Structure representing human-readable revealed data after unpacking.
* @param issuingState The issuing state as a string.
* @param name Array of strings representing the passport holder's name.
* @param passportNumber The passport number.
* @param nationality The nationality.
* @param dateOfBirth Formatted date of birth.
* @param gender The gender.
* @param expiryDate Formatted expiration date.
* @param olderThan The verified "older than" age.
* @param passportNoOfac The passport number OFAC verification result.
* @param nameAndDobOfac The name and date of birth OFAC verification result.
* @param nameAndYobOfac The name and year of birth OFAC verification result.
*/
struct ReadableRevealedData {
string issuingState;
string[] name;
string passportNumber;
string nationality;
string dateOfBirth;
string gender;
string expiryDate;
uint256 olderThan;
uint256 passportNoOfac;
uint256 nameAndDobOfac;
uint256 nameAndYobOfac;
}
/**
* @notice Structure representing a hub proof for VC and Disclose verification.
* @param olderThanEnabled Flag indicating if the 'olderThan' check is required.
* @param olderThan Threshold age for verification.
* @param forbiddenCountriesEnabled Flag indicating if forbidden countries verification is required.
* @param forbiddenCountriesListPacked Packed forbidden countries list.
* @param ofacEnabled Array of flags indicating which OFAC checks are enabled. [passportNo, nameAndDob, nameAndYob]
* @param vcAndDiscloseProof The underlying VC and Disclose proof.
*/
struct VcAndDiscloseHubProof {
bool olderThanEnabled;
uint256 olderThan;
bool forbiddenCountriesEnabled;
uint256[4] forbiddenCountriesListPacked;
bool[3] ofacEnabled;
IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof vcAndDiscloseProof;
}
/**
* @notice Verifies a VC and Disclose proof.
* @dev Checks the provided proof against verification configuration and returns key result data.
* @param proof The hub proof containing configuration flags and the underlying VC and Disclose proof.
* @return result The verification result including attestationId, scope, userIdentifier, nullifier, identityCommitmentRoot, revealed data, and forbidden countries list.
*/
function verifyVcAndDisclose(
VcAndDiscloseHubProof memory proof
)
external
view
returns (VcAndDiscloseVerificationResult memory result);
/**
* @notice Converts packed revealed data into a human-readable format.
* @dev Uses an array of RevealedDataType to determine which attributes to extract from the packed data.
* @param revealedDataPacked An array of three uint256 containing the packed data.
* @param types An array of RevealedDataType indicating the order of attributes.
* @return readableData The decoded and formatted revealed data.
*/
function getReadableRevealedData(
uint256[3] memory revealedDataPacked,
RevealedDataType[] memory types
)
external
view
returns (ReadableRevealedData memory readableData);
/**
* @notice Retrieves a human-readable list of forbidden countries.
* @dev Converts the packed forbidden countries list into a fixed-size array of strings.
* @param forbiddenCountriesListPacked The packed representation of forbidden countries.
* @return forbiddenCountries A fixed-size array (length defined by CircuitConstants.MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH) of strings representing forbidden countries.
*/
function getReadableForbiddenCountries(
uint256[4] memory forbiddenCountriesListPacked
)
external
view
returns (string[40] memory forbiddenCountries);
/**
* @notice Registers a passport commitment using a register circuit proof.
* @dev Verifies the register circuit proof before registering the passport commitment.
* @param registerCircuitVerifierId The identifier for the register circuit verifier to be used.
* @param registerCircuitProof The proof data for the register circuit.
*/
function registerPassportCommitment(
uint256 registerCircuitVerifierId,
IRegisterCircuitVerifier.RegisterCircuitProof memory registerCircuitProof
)
external;
/**
* @notice Registers an Id Card commitment using a register circuit proof.
* @dev Verifies the register circuit proof before registering the Id Card commitment.
* @param registerCircuitVerifierId The identifier for the register circuit verifier to be used.
* @param registerCircuitProof The proof data for the register circuit.
*/
function registerIdCardCommitment(
uint256 registerCircuitVerifierId,
IRegisterCircuitVerifier.RegisterCircuitProof memory registerCircuitProof
)
external;
/**
* @notice Registers a DSC key commitment using a DSC circuit proof.
* @dev Verifies the DSC circuit proof before registering the DSC key commitment.
* @param dscCircuitVerifierId The identifier for the DSC circuit verifier to be used.
* @param dscCircuitProof The proof data for the DSC circuit.
*/
function registerDscKeyCommitment(
uint256 dscCircuitVerifierId,
IDscCircuitVerifier.DscCircuitProof memory dscCircuitProof
)
external;
/**
* @notice Registers an Id Card DSC key commitment using a DSC circuit proof.
* @dev Verifies the DSC proof and then calls the Id Card Identity Registry to register the dsc key commitment.
* @param dscCircuitVerifierId The identifier for the DSC circuit verifier to use.
* @param dscCircuitProof The DSC circuit proof data.
*/
function registerIdCardDscKeyCommitment(
uint256 dscCircuitVerifierId,
IDscCircuitVerifier.DscCircuitProof memory dscCircuitProof
)
external;
/**
* @notice Returns the address of the Identity Registry.
* @return registryAddr The address of the Identity Registry contract.
*/
function registry() external view returns (address registryAddr);
/**
* @notice Returns the address of the Identity Registry for Id Cards.
* @return registryIdCardAddr The address of the Identity Registry for Id Cards.
*/
function registryIdCard() external view returns (address registryIdCardAddr);
/**
* @notice Returns the address of the VC and Disclose circuit verifier.
* @return verifierAddr The address of the VC and Disclose circuit verifier.
*/
function vcAndDiscloseCircuitVerifier() external view returns (address verifierAddr);
/**
* @notice Retrieves the register circuit verifier for a given signature type.
* @param typeId The signature type identifier.
* @return verifier The address of the register circuit verifier.
*/
function sigTypeToRegisterCircuitVerifiers(
uint256 typeId
)
external
view
returns (address verifier);
/**
* @notice Retrieves the DSC circuit verifier for a given signature type.
* @param typeId The signature type identifier.
* @return verifier The address of the DSC circuit verifier.
*/
function sigTypeToDscCircuitVerifiers(
uint256 typeId
)
external
view
returns (address verifier);
/**
* @notice Retrieves the register circuit verifier for a given signature type for Id Cards.
* @param typeId The signature type identifier.
* @return verifier The address of the register circuit verifier.
*/
function sigTypeToRegisterCircuitVerifiersIdCard(
uint256 typeId
)
external
view
returns (address verifier);
/**
* @notice Retrieves the DSC circuit verifier for a given signature type for Id Cards.
* @param typeId The signature type identifier.
* @return verifier The address of the DSC circuit verifier.
*/
function sigTypeToDscCircuitVerifiersIdCard(
uint256 typeId
)
external
view
returns (address verifier);
}

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
pragma solidity 0.8.28;
import {CircuitConstants} from "../constants/CircuitConstants.sol";
import {Formatter} from "./Formatter.sol";

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
pragma solidity 0.8.28;
/**
* @title Formatter Library

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
pragma solidity 0.8.28;
import {Formatter} from "./Formatter.sol";
import {CircuitAttributeHandler} from "./CircuitAttributeHandler.sol";

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ProxyRoot} from "../upgradeable/ProxyRoot.sol";
/**
* @title IdentityRegistry
* @notice Acts as an upgradeable proxy for the identity registry.
* @dev Inherits from ProxyRoot to delegate calls to an implementation contract.
* The constructor initializes the proxy using the provided implementation address and initialization data.
*/
contract IdentityRegistryIdCard is ProxyRoot {
/**
* @notice Creates a new instance of the IdentityRegistry proxy.
* @param _logic The address of the initial implementation contract that contains the registry logic.
* @param _data The initialization data passed to the implementation during deployment.
*/
constructor(address _logic, bytes memory _data) ProxyRoot(_logic, _data) {}
}

View File

@@ -0,0 +1,779 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import { InternalLeanIMT, LeanIMTData } from "@zk-kit/imt.sol/internal/InternalLeanIMT.sol";
import {IIdentityRegistryIdCardV1} from "../interfaces/IIdentityRegistryIdCardV1.sol";
import {IIdentityVerificationHubV1} from "../interfaces/IIdentityVerificationHubV1.sol";
import {ImplRoot} from "../upgradeable/ImplRoot.sol";
/**
* @notice ⚠️ CRITICAL STORAGE LAYOUT WARNING ⚠️
* =============================================
*
* This contract uses the UUPS upgradeable pattern which makes storage layout EXTREMELY SENSITIVE.
*
* 🚫 NEVER MODIFY OR REORDER existing storage variables
* 🚫 NEVER INSERT new variables between existing ones
* 🚫 NEVER CHANGE THE TYPE of existing variables
*
* ✅ New storage variables MUST be added in one of these two ways ONLY:
* 1. At the END of the storage layout
* 2. In a new V2 contract that inherits from this V1
* ✅ It is safe to rename variables (e.g., changing 'variable' to 'oldVariable')
* as long as the type and order remain the same
*
* Examples of forbidden changes:
* - Changing uint256 to uint128
* - Changing bytes32 to bytes
* - Changing array type to mapping
*
* For more detailed information about forbidden changes, please refer to:
* https://docs.openzeppelin.com/upgrades-plugins/writing-upgradeable#modifying-your-contracts
*
* ⚠️ VIOLATION OF THESE RULES WILL CAUSE CATASTROPHIC STORAGE COLLISIONS IN FUTURE UPGRADES ⚠️
* =============================================
*/
/**
* @title IdentityRegistryStorageV1
* @dev Abstract contract for storage layout of IdentityRegistryImplV1.
* Inherits from ImplRoot to provide upgradeable functionality.
*/
abstract contract IdentityRegistryIdCardStorageV1 is
ImplRoot
{
// ====================================================
// Storage Variables
// ====================================================
/// @notice Address of the identity verification hub.
address internal _hub;
/// @notice Merkle tree data structure for identity commitments.
LeanIMTData internal _identityCommitmentIMT;
/// @notice Mapping from Merkle tree root to its creation timestamp.
mapping(uint256 => uint256) internal _rootTimestamps;
/// @notice Mapping from attestation ID and nullifier to a boolean indicating registration.
/// @dev Example: For passport, the attestation id is 1.
mapping(bytes32 => mapping(uint256 => bool)) internal _nullifiers;
/// @notice Merkle tree data structure for DSC key commitments.
LeanIMTData internal _dscKeyCommitmentIMT;
/// @notice Mapping to determine if a DSC key commitment is registered.
mapping(uint256 => bool) internal _isRegisteredDscKeyCommitment;
/// @notice Current name and date of birth OFAC root.
uint256 internal _nameAndDobOfacRoot;
/// @notice Current name and year of birth OFAC root.
uint256 internal _nameAndYobOfacRoot;
/// @notice Current CSCA root.
uint256 internal _cscaRoot;
}
/**
* @title IdentityRegistryImplV1
* @notice Provides functions to register and manage identity commitments using a Merkle tree structure.
* @dev Inherits from IdentityRegistryStorageV1 and implements IIdentityRegistryV1.
*/
contract IdentityRegistryIdCardImplV1 is
IdentityRegistryIdCardStorageV1,
IIdentityRegistryIdCardV1
{
using InternalLeanIMT for LeanIMTData;
// ====================================================
// Events
// ====================================================
/// @notice Emitted when the registry is initialized.
event RegistryInitialized(address hub);
/// @notice Emitted when the hub address is updated.
event HubUpdated(address hub);
/// @notice Emitted when the CSCA root is updated.
event CscaRootUpdated(uint256 cscaRoot);
/// @notice Emitted when the name and date of birth OFAC root is updated.
event NameAndDobOfacRootUpdated(uint256 nameAndDobOfacRoot);
/// @notice Emitted when the name and year of birth OFAC root is updated.
event NameAndYobOfacRootUpdated(uint256 nameAndYobOfacRoot);
/// @notice Emitted when an identity commitment is successfully registered.
event CommitmentRegistered(bytes32 indexed attestationId, uint256 indexed nullifier, uint256 indexed commitment, uint256 timestamp, uint256 imtRoot, uint256 imtIndex);
/// @notice Emitted when a DSC key commitment is successfully registered.
event DscKeyCommitmentRegistered(uint256 indexed commitment, uint256 timestamp, uint256 imtRoot, uint256 imtIndex);
/// @notice Emitted when a identity commitment is added by dev team.
event DevCommitmentRegistered(bytes32 indexed attestationId, uint256 indexed nullifier, uint256 indexed commitment, uint256 timestamp, uint256 imtRoot, uint256 imtIndex);
/// @notice Emitted when a identity commitment is updated by dev team.
event DevCommitmentUpdated(uint256 indexed oldLeaf, uint256 indexed newLeaf, uint256 imtRoot, uint256 timestamp);
/// @notice Emitted when a identity commitment is removed by dev team.
event DevCommitmentRemoved(uint256 indexed oldLeaf, uint256 imtRoot, uint256 timestamp);
/// @notice Emitted when a DSC key commitment is added by dev team.
event DevDscKeyCommitmentRegistered(uint256 indexed commitment, uint256 imtRoot, uint256 imtIndex);
/// @notice Emitted when a DSC key commitment is updated by dev team.
event DevDscKeyCommitmentUpdated(uint256 indexed oldLeaf, uint256 indexed newLeaf, uint256 imtRoot);
/// @notice Emitted when a DSC key commitment is removed by dev team.
event DevDscKeyCommitmentRemoved(uint256 indexed oldLeaf, uint256 imtRoot);
/// @notice Emitted when the state of a nullifier is changed by dev team.
event DevNullifierStateChanged(bytes32 indexed attestationId, uint256 indexed nullifier, bool state);
/// @notice Emitted when the state of a DSC key commitment is changed by dev team.
event DevDscKeyCommitmentStateChanged(uint256 indexed commitment, bool state);
// ====================================================
// Errors
// ====================================================
/// @notice Thrown when the hub is not set.
error HUB_NOT_SET();
/// @notice Thrown when a function is accessed by an address other than the designated hub.
error ONLY_HUB_CAN_ACCESS();
/// @notice Thrown when attempting to register a commitment that has already been registered.
error REGISTERED_COMMITMENT();
// ====================================================
// Modifiers
// ====================================================
/**
* @notice Modifier to restrict access to functions to only the hub.
* @dev Reverts if the hub is not set or if the caller is not the hub.
*/
modifier onlyHub() {
if (address(_hub) == address(0)) revert HUB_NOT_SET();
if (msg.sender != address(_hub)) revert ONLY_HUB_CAN_ACCESS();
_;
}
// ====================================================
// Constructor
// ====================================================
/**
* @notice Constructor that disables initializers.
* @dev Prevents direct initialization of the implementation contract.
*/
constructor() {
_disableInitializers();
}
// ====================================================
// Initializer
// ====================================================
/**
* @notice Initializes the registry implementation.
* @dev Sets the hub address and initializes the UUPS upgradeable feature.
* @param _hub The address of the identity verification hub.
*/
function initialize(
address _hub
)
external
initializer
{
__ImplRoot_init();
_hub = _hub;
emit RegistryInitialized(_hub);
}
// ====================================================
// External Functions - View & Checks
// ====================================================
/**
* @notice Retrieves the hub address.
* @return The current identity verification hub address.
*/
function hub()
external
virtual
onlyProxy
view
returns (address)
{
return _hub;
}
/**
* @notice Checks if a specific nullifier is registered for a given attestation.
* @param attestationId The attestation identifier.
* @param nullifier The nullifier to be checked.
* @return True if the nullifier has been registered, false otherwise.
*/
function nullifiers(
bytes32 attestationId,
uint256 nullifier
)
external
virtual
onlyProxy
view
returns (bool)
{
return _nullifiers[attestationId][nullifier];
}
/**
* @notice Checks if a DSC key commitment is registered.
* @param commitment The DSC key commitment.
* @return True if the DSC key commitment is registered, false otherwise.
*/
function isRegisteredDscKeyCommitment(
uint256 commitment
)
external
virtual
onlyProxy
view
returns (bool)
{
return _isRegisteredDscKeyCommitment[commitment];
}
/**
* @notice Retrieves the timestamp when a specific Merkle root was created.
* @param root The Merkle tree root.
* @return The timestamp corresponding to the given root.
*/
function rootTimestamps(
uint256 root
)
external
virtual
onlyProxy
view
returns (uint256)
{
return _rootTimestamps[root];
}
/**
* @notice Checks if the identity commitment Merkle tree contains the provided root.
* @param root The Merkle tree root.
* @return True if the root exists, false otherwise.
*/
function checkIdentityCommitmentRoot(
uint256 root
)
external
onlyProxy
view
returns (bool)
{
return _rootTimestamps[root] != 0;
}
/**
* @notice Retrieves the number of identity commitments in the Merkle tree.
* @return The size of the identity commitment Merkle tree.
*/
function getIdentityCommitmentMerkleTreeSize()
external
onlyProxy
view
returns (uint256)
{
return _identityCommitmentIMT.size;
}
/**
* @notice Retrieves the current Merkle root of the identity commitments.
* @return The current identity commitment Merkle root.
*/
function getIdentityCommitmentMerkleRoot()
external
onlyProxy
view
returns (uint256)
{
return _identityCommitmentIMT._root();
}
/**
* @notice Retrieves the index of a specific identity commitment in the Merkle tree.
* @param commitment The identity commitment to locate.
* @return The index of the provided commitment within the Merkle tree.
*/
function getIdentityCommitmentIndex(
uint256 commitment
)
external
onlyProxy
view
returns (uint256)
{
return _identityCommitmentIMT._indexOf(commitment);
}
/**
* @notice Retrieves the current name and date of birth OFAC root.
* @return The stored name and date of birth OFAC root.
*/
function getNameAndDobOfacRoot()
external
onlyProxy
view
returns (uint256)
{
return _nameAndDobOfacRoot;
}
/**
* @notice Retrieves the current name and year of birth OFAC root.
* @return The stored name and year of birth OFAC root.
*/
function getNameAndYobOfacRoot()
external
onlyProxy
view
returns (uint256)
{
return _nameAndYobOfacRoot;
}
/**
* @notice Validates whether the provided OFAC roots match the stored values.
* @param nameAndDobRoot The name and date of birth OFAC root to validate.
* @param nameAndYobRoot The name and year of birth OFAC root to validate.
* @return True if all provided roots match the stored values, false otherwise.
*/
function checkOfacRoots(
uint256 nameAndDobRoot,
uint256 nameAndYobRoot
)
external
onlyProxy
view
returns (bool)
{
return _nameAndDobOfacRoot == nameAndDobRoot
&& _nameAndYobOfacRoot == nameAndYobRoot;
}
/**
* @notice Retrieves the current CSCA root.
* @return The stored CSCA root.
*/
function getCscaRoot()
external
onlyProxy
view
returns (uint256)
{
return _cscaRoot;
}
/**
* @notice Validates whether the provided CSCA root matches the stored value.
* @param root The CSCA root to validate.
* @return True if the provided root is equal to the stored CSCA root, false otherwise.
*/
function checkCscaRoot(
uint256 root
)
external
onlyProxy
view
returns (bool)
{
return _cscaRoot == root;
}
/**
* @notice Retrieves the current Merkle root of the DSC key commitments.
* @return The current DSC key commitment Merkle root.
*/
function getDscKeyCommitmentMerkleRoot()
external
onlyProxy
view
returns (uint256)
{
return _dscKeyCommitmentIMT._root();
}
/**
* @notice Validates whether the provided root matches the DSC key commitment Merkle root.
* @param root The root to validate.
* @return True if the roots match, false otherwise.
*/
function checkDscKeyCommitmentMerkleRoot(
uint256 root
)
external
onlyProxy
view
returns (bool)
{
return _dscKeyCommitmentIMT._root() == root;
}
/**
* @notice Retrieves the number of DSC key commitments in the Merkle tree.
* @return The DSC key commitment Merkle tree size.
*/
function getDscKeyCommitmentTreeSize()
external
onlyProxy
view
returns (uint256)
{
return _dscKeyCommitmentIMT.size;
}
/**
* @notice Retrieves the index of a specific DSC key commitment in the Merkle tree.
* @param commitment The DSC key commitment to locate.
* @return The index of the provided commitment within the DSC key commitment Merkle tree.
*/
function getDscKeyCommitmentIndex(
uint256 commitment
)
external
onlyProxy
view
returns (uint256)
{
return _dscKeyCommitmentIMT._indexOf(commitment);
}
// ====================================================
// External Functions - Registration
// ====================================================
/**
* @notice Registers a new identity commitment.
* @dev Caller must be the hub. Reverts if the nullifier is already registered.
* @param attestationId The identifier for the attestation.
* @param nullifier The nullifier associated with the identity commitment.
* @param commitment The identity commitment to register.
*/
function registerCommitment(
bytes32 attestationId,
uint256 nullifier,
uint256 commitment
)
external
onlyProxy
onlyHub
{
if (_nullifiers[attestationId][nullifier]) revert REGISTERED_COMMITMENT();
_nullifiers[attestationId][nullifier] = true;
uint256 index = _identityCommitmentIMT.size;
uint256 imt_root = _addCommitment(_identityCommitmentIMT, commitment);
_rootTimestamps[imt_root] = block.timestamp;
emit CommitmentRegistered(attestationId, nullifier, commitment, block.timestamp, imt_root, index);
}
/**
* @notice Registers a new DSC key commitment.
* @dev Caller must be the hub. Reverts if the commitment has already been registered.
* @param dscCommitment The DSC key commitment to register.
*/
function registerDscKeyCommitment(
uint256 dscCommitment
)
external
onlyProxy
onlyHub
{
if (_isRegisteredDscKeyCommitment[dscCommitment]) revert REGISTERED_COMMITMENT();
_isRegisteredDscKeyCommitment[dscCommitment] = true;
uint256 index = _dscKeyCommitmentIMT.size;
uint256 imt_root = _addCommitment(_dscKeyCommitmentIMT, dscCommitment);
emit DscKeyCommitmentRegistered(dscCommitment, block.timestamp, imt_root, index);
}
// ====================================================
// External Functions - Only Owner
// ====================================================
/**
* @notice Updates the hub address.
* @dev Callable only via a proxy and restricted to the contract owner.
* @param newHubAddress The new address of the hub.
*/
function updateHub(
address newHubAddress
)
external
onlyProxy
onlyOwner
{
_hub = newHubAddress;
emit HubUpdated(newHubAddress);
}
/**
* @notice Updates the name and date of birth OFAC root.
* @dev Callable only via a proxy and restricted to the contract owner.
* @param newNameAndDobOfacRoot The new name and date of birth OFAC root value.
*/
function updateNameAndDobOfacRoot(
uint256 newNameAndDobOfacRoot
)
external
onlyProxy
onlyOwner
{
_nameAndDobOfacRoot = newNameAndDobOfacRoot;
emit NameAndDobOfacRootUpdated(newNameAndDobOfacRoot);
}
/**
* @notice Updates the name and year of birth OFAC root.
* @dev Callable only via a proxy and restricted to the contract owner.
* @param newNameAndYobOfacRoot The new name and year of birth OFAC root value.
*/
function updateNameAndYobOfacRoot(
uint256 newNameAndYobOfacRoot
)
external
onlyProxy
onlyOwner
{
_nameAndYobOfacRoot = newNameAndYobOfacRoot;
emit NameAndYobOfacRootUpdated(newNameAndYobOfacRoot);
}
/**
* @notice Updates the CSCA root.
* @dev Callable only via a proxy and restricted to the contract owner.
* @param newCscaRoot The new CSCA root value.
*/
function updateCscaRoot(
uint256 newCscaRoot
)
external
onlyProxy
onlyOwner
{
_cscaRoot = newCscaRoot;
emit CscaRootUpdated(newCscaRoot);
}
/**
* @notice (DEV) Force-adds an identity commitment.
* @dev Callable only by the owner for testing or administration.
* @param attestationId The identifier for the attestation.
* @param nullifier The nullifier associated with the identity commitment.
* @param commitment The identity commitment to add.
*/
function devAddIdentityCommitment(
bytes32 attestationId,
uint256 nullifier,
uint256 commitment
)
external
onlyProxy
onlyOwner
{
_nullifiers[attestationId][nullifier] = true;
uint256 imt_root = _addCommitment(_identityCommitmentIMT, commitment);
_rootTimestamps[imt_root] = block.timestamp;
uint256 index = _identityCommitmentIMT._indexOf(commitment);
emit DevCommitmentRegistered(attestationId, nullifier, commitment, block.timestamp, imt_root, index);
}
/**
* @notice (DEV) Updates an existing identity commitment.
* @dev Caller must be the owner. Provides sibling nodes for proof of position.
* @param oldLeaf The current identity commitment to update.
* @param newLeaf The new identity commitment.
* @param siblingNodes An array of sibling nodes for Merkle proof generation.
*/
function devUpdateCommitment(
uint256 oldLeaf,
uint256 newLeaf,
uint256[] calldata siblingNodes
)
external
onlyProxy
onlyOwner
{
uint256 imt_root = _updateCommitment(_identityCommitmentIMT, oldLeaf, newLeaf, siblingNodes);
_rootTimestamps[imt_root] = block.timestamp;
emit DevCommitmentUpdated(oldLeaf, newLeaf, imt_root, block.timestamp);
}
/**
* @notice (DEV) Removes an existing identity commitment.
* @dev Caller must be the owner. Provides sibling nodes for proof of position.
* @param oldLeaf The identity commitment to remove.
* @param siblingNodes An array of sibling nodes for Merkle proof generation.
*/
function devRemoveCommitment(
uint256 oldLeaf,
uint256[] calldata siblingNodes
)
external
onlyProxy
onlyOwner
{
uint256 imt_root = _removeCommitment(_identityCommitmentIMT, oldLeaf, siblingNodes);
_rootTimestamps[imt_root] = block.timestamp;
emit DevCommitmentRemoved(oldLeaf, imt_root, block.timestamp);
}
/**
* @notice (DEV) Force-adds a DSC key commitment.
* @dev Callable only by the owner for testing or administration.
* @param dscCommitment The DSC key commitment to add.
*/
function devAddDscKeyCommitment(
uint256 dscCommitment
)
external
onlyProxy
onlyOwner
{
_isRegisteredDscKeyCommitment[dscCommitment] = true;
uint256 imt_root = _addCommitment(_dscKeyCommitmentIMT, dscCommitment);
uint256 index = _dscKeyCommitmentIMT._indexOf(dscCommitment);
emit DevDscKeyCommitmentRegistered(dscCommitment, imt_root, index);
}
/**
* @notice (DEV) Updates an existing DSC key commitment.
* @dev Caller must be the owner. Provides sibling nodes for proof of position.
* @param oldLeaf The current DSC key commitment to update.
* @param newLeaf The new DSC key commitment.
* @param siblingNodes An array of sibling nodes for Merkle proof generation.
*/
function devUpdateDscKeyCommitment(
uint256 oldLeaf,
uint256 newLeaf,
uint256[] calldata siblingNodes
)
external
onlyProxy
onlyOwner
{
uint256 imt_root = _updateCommitment(_dscKeyCommitmentIMT, oldLeaf, newLeaf, siblingNodes);
emit DevDscKeyCommitmentUpdated(oldLeaf, newLeaf, imt_root);
}
/**
* @notice (DEV) Removes an existing DSC key commitment.
* @dev Caller must be the owner. Provides sibling nodes for proof of position.
* @param oldLeaf The DSC key commitment to remove.
* @param siblingNodes An array of sibling nodes for Merkle proof generation.
*/
function devRemoveDscKeyCommitment(
uint256 oldLeaf,
uint256[] calldata siblingNodes
)
external
onlyProxy
onlyOwner
{
uint256 imt_root = _removeCommitment(_dscKeyCommitmentIMT, oldLeaf, siblingNodes);
emit DevDscKeyCommitmentRemoved(oldLeaf, imt_root);
}
/**
* @notice (DEV) Changes the state of a nullifier.
* @dev Callable only by the owner.
* @param attestationId The attestation identifier.
* @param nullifier The nullifier whose state is to be updated.
* @param state The new state of the nullifier (true for registered, false for not registered).
*/
function devChangeNullifierState(
bytes32 attestationId,
uint256 nullifier,
bool state
)
external
onlyProxy
onlyOwner
{
_nullifiers[attestationId][nullifier] = state;
emit DevNullifierStateChanged(attestationId, nullifier, state);
}
/**
* @notice (DEV) Changes the registration state of a DSC key commitment.
* @dev Callable only by the owner.
* @param dscCommitment The DSC key commitment.
* @param state The new state of the DSC key commitment (true for registered, false for not registered).
*/
function devChangeDscKeyCommitmentState(
uint256 dscCommitment,
bool state
)
external
onlyProxy
onlyOwner
{
_isRegisteredDscKeyCommitment[dscCommitment] = state;
emit DevDscKeyCommitmentStateChanged(dscCommitment, state);
}
// ====================================================
// Internal Functions
// ====================================================
/**
* @notice Adds a commitment to the specified Merkle tree.
* @dev Inserts the commitment using the provided Merkle tree structure.
* @param imt The Merkle tree data structure.
* @param commitment The commitment to add.
* @return imt_root The new Merkle tree root after insertion.
*/
function _addCommitment(
LeanIMTData storage imt,
uint256 commitment
)
internal
returns(uint256 imt_root)
{
imt_root = imt._insert(commitment);
}
/**
* @notice Updates an existing commitment in the specified Merkle tree.
* @dev Uses sibling nodes to prove the commitment's position and update it.
* @param imt The Merkle tree data structure.
* @param oldLeaf The current commitment to update.
* @param newLeaf The new commitment.
* @param siblingNodes An array of sibling nodes for generating a valid proof.
* @return imt_root The new Merkle tree root after update.
*/
function _updateCommitment(
LeanIMTData storage imt,
uint256 oldLeaf,
uint256 newLeaf,
uint256[] calldata siblingNodes
)
internal
returns(uint256 imt_root)
{
imt_root = imt._update(oldLeaf, newLeaf, siblingNodes);
}
/**
* @notice Removes a commitment from the specified Merkle tree.
* @dev Uses sibling nodes to prove the commitment's position before removal.
* @param imt The Merkle tree data structure.
* @param oldLeaf The commitment to remove.
* @param siblingNodes An array of sibling nodes for generating a valid proof.
* @return imt_root The new Merkle tree root after removal.
*/
function _removeCommitment(
LeanIMTData storage imt,
uint256 oldLeaf,
uint256[] calldata siblingNodes
)
internal
returns(uint256 imt_root)
{
imt_root = imt._remove(oldLeaf, siblingNodes);
}
}