Verification in the IdentityVerificationHub
# Verification in the IdentityVerificationHub
:::note
**Who is this for?** This page explains the internal verification flow of the on-chain IdentityVerificationHub contract — how proofs are decoded, routed, and verified. It's useful if you're building custom verification hooks or need to understand the on-chain data structures. For basic smart contract integration, see [Smart Contract Integration](/docs/self-pass/contracts/basic-integration/).
:::
The IdentityVerificationHub V2 is the core verification engine that processes zero-knowledge proofs and executes identity verification workflows.
## How the Hub Works
The Hub operates as a central verification coordinator that:
* **Receives Verification Requests** from contracts implementing `ISelfVerificationRoot`
* **Processes ZK Proofs** using specialized circuit verifiers
* **Applies Verification Rules** based on stored configurations
* **Returns Structured Results** to the calling contract
## Complete Verification Flow
### 1. Request Initiation
```solidity
// User contract calls Hub with proof data
function verifySelfProof(bytes calldata proofPayload, bytes calldata userContextData) external;
```
**What happens:**
#### TEE Proof Generation (Gas-Free for Users)
* **User Input**: User completes a supported document flow in the mobile app (passport, ID, Aadhaar, or KYC)
* **TEE Processing**: Trusted Execution Environment securely processes identity data
* **ZK Proof Creation**: TEE generates zero-knowledge proof without revealing raw identity data
#### Relayer (Sponsored Transactions)
* **Proof Relay**: Relayer receives ZK proof from TEE
* **Gas Sponsorship**: Relayer pays all transaction gas fees on behalf of user
* **Onchain Submission**: Relayer submits proof to user's contract via blockchain transaction
* **User Experience**: User gets verified identity without any crypto/gas requirements
#### Contract Processing
* Contract receives ZK proof from TEE/Relayer
* Calls Hub's `verifySelfProof` with proof + user context data
* Hub begins processing the verification request
### 2. Input Decoding & Context Processing
```solidity
// Hub internal: _decodeInput() and _decodeUserContextData()
(HubInputHeader memory header, bytes calldata proofData) = _decodeInput(baseVerificationInput);
(configId, destChainId, userIdentifier, remainingData) = _decodeUserContextData(userContextData);
```
**What happens:**
* **Header Extraction**: Gets contract version, scope, attestation ID
* **Context Parsing**: Extracts config ID, destination chain, user identifier
* **Data Preparation**: Prepares proof data for verification
### 3. Configuration Retrieval
```solidity
// Hub loads verification configuration by configId
VerificationConfigV2 memory config = $v2._v2VerificationConfigs[configId];
```
**What happens:**
* Hub looks up stored verification configuration using configId
* Configuration contains all verification rules (age, countries, OFAC, etc.)
* If config doesn't exist, verification fails
### 4. Document Type Detection & Routing
```solidity
// Based on attestationId in header
if (attestationId == AttestationId.E_PASSPORT) {
// Route to passport verification logic
} else if (attestationId == AttestationId.EU_ID_CARD) {
// Route to EU ID card verification logic
} else if (attestationId == AttestationId.AADHAAR) {
// Route to Aadhaar verification logic
} else if (attestationId == AttestationId.KYC) {
// Route to KYC verification logic
}
```
**What happens:**
* Hub identifies document type from attestation ID
* Routes to appropriate verification pipeline
* Uses document-specific circuit verifiers
### 5. Basic Verification (\_basicVerification)
**Purpose:** Validates the **cryptographic correctness** and **security** of the ZK proof itself.
```solidity
// Hub performs 4 core verification steps
bytes memory proofOutput = _basicVerification(
header,
vcAndDiscloseProof,
userContextData,
userIdentifier
);
```
**What happens (4 verification scopes):**
#### Scope 1: Contract Validation
* **Scope Check**: Ensures proof was generated for the correct contract
* **User Identifier Check**: Validates user identity consistency
#### Scope 2: Registry & Timestamp Validation
* **Root Check**: Validates against Merkle tree in identity registry
* **Current Date Check**: Ensures proof is within valid time window
#### Scope 3: Cryptographic Proof Verification
* **Groth16 Proof Verification**: Validates ZK proof using circuit verifier
* **Public Signals Validation**: Verifies proof inputs/outputs match expectations
#### Scope 4: Raw Data Extraction
* **Output Generation**: Creates `PassportOutput` or `EuIdOutput` with raw field data
* **Data Preparation**: Prepares extracted data for business logic verification
**Output:** Raw identity data (`PassportOutput`/`EuIdOutput`) ready for custom verification.
### 6. Custom Verification (CustomVerifier.customVerify)
**Purpose:** Applies **business logic rules** and validates **identity attributes** against configuration requirements.
```solidity
// Hub applies custom verification logic
GenericDiscloseOutputV2 memory output = CustomVerifier.customVerify(
header.attestationId,
config,
proofOutput
);
```
**What happens:**
* **Document Type Routing**: Routes to passport, ID card, Aadhaar, or KYC-specific verification
* **Business Rule Application**: Applies age, geographic, and sanctions requirements
* **Identity Data Extraction**: Converts raw data to structured, human-readable format
* **Final Validation**: Ensures all configuration requirements are met
**Output:** Structured identity data (`GenericDiscloseOutputV2`) with verification results.
#### Age Verification (olderThanEnabled)
```solidity
if (verificationConfig.olderThanEnabled) {
if (!CircuitAttributeHandlerV2.compareOlderThan(
attestationId,
passportOutput.revealedDataPacked,
verificationConfig.olderThan
)) {
revert InvalidOlderThan();
}
}
```
* Validates user meets minimum age requirement
* Uses circuit-extracted age data for verification
* Example: config requires 18+, user is 20 → ✅ passes
* Example: config requires 21+, user is 18 → ❌ fails
#### Geographic Restrictions (forbiddenCountriesEnabled)
```solidity
if (verificationConfig.forbiddenCountriesEnabled) {
for (uint256 i = 0; i < 4; i++) {
if (passportOutput.forbiddenCountriesListPacked[i] !=
verificationConfig.forbiddenCountriesListPacked[i]) {
revert InvalidForbiddenCountries();
}
}
}
```
* Validates forbidden countries list matches exactly
* Uses packed representation for gas efficiency (4 uint256 array)
* Order in config must match proof's forbidden countries list
#### OFAC Sanctions Verification
```solidity
if (verificationConfig.ofacEnabled[0] ||
verificationConfig.ofacEnabled[1] ||
verificationConfig.ofacEnabled[2]) {
if (!CircuitAttributeHandlerV2.compareOfac(
attestationId,
passportOutput.revealedDataPacked,
verificationConfig.ofacEnabled[0], // passport number
verificationConfig.ofacEnabled[1], // name + DOB
verificationConfig.ofacEnabled[2] // name + YOB
)) {
revert InvalidOfacCheck();
}
}
```
* **Mode 0**: OFAC check using passport number
* **Mode 1**: OFAC check using name + date of birth
* **Mode 2**: OFAC check using name + year of birth
* Each mode can be independently enabled/disabled
* Uses circuit-provided OFAC verification results
### 7. Output Formatting & Generation
```solidity
// Hub formats verification results into structured output
output = _formatVerificationOutput(header.contractVersion, genericDiscloseOutput);
```
**What happens:**
* Raw proof signals converted to human-readable data
* Structured identity information extracted
* Verification results (age, OFAC, etc.) included
### 8. Result Delivery
```solidity
// Hub calls back to the original contract
ISelfVerificationRoot(callingContract).onVerificationSuccess(
abi.encode(output),
userData
);
```
**What happens:**
* Hub calls `onVerificationSuccess` on the requesting contract
* Passes structured output + user-defined data
* Contract can then execute its custom business logic
## Data Structures
### VerificationConfigV2
```solidity
struct VerificationConfigV2 {
bool olderThanEnabled; // Enable age verification
uint256 olderThan; // Minimum age requirement
bool forbiddenCountriesEnabled; // Enable country restrictions
uint256[4] forbiddenCountriesListPacked; // Packed forbidden countries
bool[3] ofacEnabled; // OFAC verification modes
}
```
### GenericDiscloseOutputV2 (Verification Result)
```solidity
struct GenericDiscloseOutputV2 {
bytes32 attestationId; // E_PASSPORT, EU_ID_CARD, AADHAAR, or KYC
uint256 userIdentifier; // User's unique identifier
uint256 nullifier; // Anti-replay nullifier
uint256[4] forbiddenCountriesListPacked; // Forbidden countries used
// Disclosed identity information
string issuingState; // Document issuing country
string[] name; // [first, middle, last] names
string idNumber; // Passport/ID number
string nationality; // User's nationality
string dateOfBirth; // Birth date (DD-MM-YY)
string gender; // User's gender
string expiryDate; // Document expiry date
// Verification results
uint256 olderThan; // Verified minimum age
bool[3] ofac; // OFAC results [passport number, name+dob, name+yob]
}
```
## Key V2 Improvements
### Multi-Document Support
* Automatic routing for attestation types: E\_PASSPORT, EU\_ID\_CARD, AADHAAR and KYC
* Document-specific verification pipelines
* Unified interface for different document types
### Structured Output
* Rich `GenericDiscloseOutputV2` with pre-extracted attributes
* No more manual parsing of raw field elements
* Type-safe access to identity data
### Flexible Configuration Management
* Reusable `VerificationConfigV2` stored in Hub
* Create configurations without contract redeployment
* Use [Self Configuration Tools](https://tools.self.xyz/) to register configs
### Enhanced Security
* Separate verification logic for each document type
* Improved timestamp and replay protection
* Gas-optimized verification checks
The IdentityVerificationHub V2 is the core verification engine that processes zero-knowledge proofs and executes identity verification workflows.
How the Hub Works
The Hub operates as a central verification coordinator that:
- Receives Verification Requests from contracts implementing
ISelfVerificationRoot - Processes ZK Proofs using specialized circuit verifiers
- Applies Verification Rules based on stored configurations
- Returns Structured Results to the calling contract
Complete Verification Flow
1. Request Initiation
// User contract calls Hub with proof data
function verifySelfProof(bytes calldata proofPayload, bytes calldata userContextData) external;
What happens:
TEE Proof Generation (Gas-Free for Users)
- User Input: User completes a supported document flow in the mobile app (passport, ID, Aadhaar, or KYC)
- TEE Processing: Trusted Execution Environment securely processes identity data
- ZK Proof Creation: TEE generates zero-knowledge proof without revealing raw identity data
Relayer (Sponsored Transactions)
- Proof Relay: Relayer receives ZK proof from TEE
- Gas Sponsorship: Relayer pays all transaction gas fees on behalf of user
- Onchain Submission: Relayer submits proof to user’s contract via blockchain transaction
- User Experience: User gets verified identity without any crypto/gas requirements
Contract Processing
- Contract receives ZK proof from TEE/Relayer
- Calls Hub’s
verifySelfProofwith proof + user context data - Hub begins processing the verification request
2. Input Decoding & Context Processing
// Hub internal: _decodeInput() and _decodeUserContextData()
(HubInputHeader memory header, bytes calldata proofData) = _decodeInput(baseVerificationInput);
(configId, destChainId, userIdentifier, remainingData) = _decodeUserContextData(userContextData);
What happens:
- Header Extraction: Gets contract version, scope, attestation ID
- Context Parsing: Extracts config ID, destination chain, user identifier
- Data Preparation: Prepares proof data for verification
3. Configuration Retrieval
// Hub loads verification configuration by configId
VerificationConfigV2 memory config = $v2._v2VerificationConfigs[configId];
What happens:
- Hub looks up stored verification configuration using configId
- Configuration contains all verification rules (age, countries, OFAC, etc.)
- If config doesn’t exist, verification fails
4. Document Type Detection & Routing
// Based on attestationId in header
if (attestationId == AttestationId.E_PASSPORT) {
// Route to passport verification logic
} else if (attestationId == AttestationId.EU_ID_CARD) {
// Route to EU ID card verification logic
} else if (attestationId == AttestationId.AADHAAR) {
// Route to Aadhaar verification logic
} else if (attestationId == AttestationId.KYC) {
// Route to KYC verification logic
}
What happens:
- Hub identifies document type from attestation ID
- Routes to appropriate verification pipeline
- Uses document-specific circuit verifiers
5. Basic Verification (_basicVerification)
Purpose: Validates the cryptographic correctness and security of the ZK proof itself.
// Hub performs 4 core verification steps
bytes memory proofOutput = _basicVerification(
header,
vcAndDiscloseProof,
userContextData,
userIdentifier
);
What happens (4 verification scopes):
Scope 1: Contract Validation
- Scope Check: Ensures proof was generated for the correct contract
- User Identifier Check: Validates user identity consistency
Scope 2: Registry & Timestamp Validation
- Root Check: Validates against Merkle tree in identity registry
- Current Date Check: Ensures proof is within valid time window
Scope 3: Cryptographic Proof Verification
- Groth16 Proof Verification: Validates ZK proof using circuit verifier
- Public Signals Validation: Verifies proof inputs/outputs match expectations
Scope 4: Raw Data Extraction
- Output Generation: Creates
PassportOutputorEuIdOutputwith raw field data - Data Preparation: Prepares extracted data for business logic verification
Output: Raw identity data (PassportOutput/EuIdOutput) ready for custom verification.
6. Custom Verification (CustomVerifier.customVerify)
Purpose: Applies business logic rules and validates identity attributes against configuration requirements.
// Hub applies custom verification logic
GenericDiscloseOutputV2 memory output = CustomVerifier.customVerify(
header.attestationId,
config,
proofOutput
);
What happens:
- Document Type Routing: Routes to passport, ID card, Aadhaar, or KYC-specific verification
- Business Rule Application: Applies age, geographic, and sanctions requirements
- Identity Data Extraction: Converts raw data to structured, human-readable format
- Final Validation: Ensures all configuration requirements are met
Output: Structured identity data (GenericDiscloseOutputV2) with verification results.
Age Verification (olderThanEnabled)
if (verificationConfig.olderThanEnabled) {
if (!CircuitAttributeHandlerV2.compareOlderThan(
attestationId,
passportOutput.revealedDataPacked,
verificationConfig.olderThan
)) {
revert InvalidOlderThan();
}
}
- Validates user meets minimum age requirement
- Uses circuit-extracted age data for verification
- Example: config requires 18+, user is 20 → ✅ passes
- Example: config requires 21+, user is 18 → ❌ fails
Geographic Restrictions (forbiddenCountriesEnabled)
if (verificationConfig.forbiddenCountriesEnabled) {
for (uint256 i = 0; i < 4; i++) {
if (passportOutput.forbiddenCountriesListPacked[i] !=
verificationConfig.forbiddenCountriesListPacked[i]) {
revert InvalidForbiddenCountries();
}
}
}
- Validates forbidden countries list matches exactly
- Uses packed representation for gas efficiency (4 uint256 array)
- Order in config must match proof’s forbidden countries list
OFAC Sanctions Verification
if (verificationConfig.ofacEnabled[0] ||
verificationConfig.ofacEnabled[1] ||
verificationConfig.ofacEnabled[2]) {
if (!CircuitAttributeHandlerV2.compareOfac(
attestationId,
passportOutput.revealedDataPacked,
verificationConfig.ofacEnabled[0], // passport number
verificationConfig.ofacEnabled[1], // name + DOB
verificationConfig.ofacEnabled[2] // name + YOB
)) {
revert InvalidOfacCheck();
}
}
- Mode 0: OFAC check using passport number
- Mode 1: OFAC check using name + date of birth
- Mode 2: OFAC check using name + year of birth
- Each mode can be independently enabled/disabled
- Uses circuit-provided OFAC verification results
7. Output Formatting & Generation
// Hub formats verification results into structured output
output = _formatVerificationOutput(header.contractVersion, genericDiscloseOutput);
What happens:
- Raw proof signals converted to human-readable data
- Structured identity information extracted
- Verification results (age, OFAC, etc.) included
8. Result Delivery
// Hub calls back to the original contract
ISelfVerificationRoot(callingContract).onVerificationSuccess(
abi.encode(output),
userData
);
What happens:
- Hub calls
onVerificationSuccesson the requesting contract - Passes structured output + user-defined data
- Contract can then execute its custom business logic
Data Structures
VerificationConfigV2
struct VerificationConfigV2 {
bool olderThanEnabled; // Enable age verification
uint256 olderThan; // Minimum age requirement
bool forbiddenCountriesEnabled; // Enable country restrictions
uint256[4] forbiddenCountriesListPacked; // Packed forbidden countries
bool[3] ofacEnabled; // OFAC verification modes
}
GenericDiscloseOutputV2 (Verification Result)
struct GenericDiscloseOutputV2 {
bytes32 attestationId; // E_PASSPORT, EU_ID_CARD, AADHAAR, or KYC
uint256 userIdentifier; // User's unique identifier
uint256 nullifier; // Anti-replay nullifier
uint256[4] forbiddenCountriesListPacked; // Forbidden countries used
// Disclosed identity information
string issuingState; // Document issuing country
string[] name; // [first, middle, last] names
string idNumber; // Passport/ID number
string nationality; // User's nationality
string dateOfBirth; // Birth date (DD-MM-YY)
string gender; // User's gender
string expiryDate; // Document expiry date
// Verification results
uint256 olderThan; // Verified minimum age
bool[3] ofac; // OFAC results [passport number, name+dob, name+yob]
}
Key V2 Improvements
Multi-Document Support
- Automatic routing for attestation types: E_PASSPORT, EU_ID_CARD, AADHAAR and KYC
- Document-specific verification pipelines
- Unified interface for different document types
Structured Output
- Rich
GenericDiscloseOutputV2with pre-extracted attributes - No more manual parsing of raw field elements
- Type-safe access to identity data
Flexible Configuration Management
- Reusable
VerificationConfigV2stored in Hub - Create configurations without contract redeployment
- Use Self Configuration Tools to register configs
Enhanced Security
- Separate verification logic for each document type
- Improved timestamp and replay protection
- Gas-optimized verification checks