make contract sdk simpler (#514)

* make contract sdk simpler

* reduce root inputs

* delete convert function

* summarize our library

* update npm package

* update package version

* update attestation id

* add util function to get revealed data
This commit is contained in:
nicoshark
2025-04-17 22:57:02 +09:00
committed by GitHub
parent a3dc3bcfd1
commit 847b88d5ec
14 changed files with 10466 additions and 15261 deletions

View File

@@ -22,9 +22,9 @@ CIRCUITS=(
"dsc_sha512_ecdsa_brainpoolP512r1:21:false"
# RSA circuits
"dsc_sha1_rsa_65537_4096:21:true"
"dsc_sha1_rsa_65537_4096:21:false"
"dsc_sha256_rsa_65537_4096:21:true"
"dsc_sha512_rsa_65537_4096:21:true"
"dsc_sha512_rsa_65537_4096:21:false"
# RSA-PSS circuits
"dsc_sha256_rsapss_3_32_3072:22:false"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@ import {IVcAndDiscloseCircuitVerifier} from "../interfaces/IVcAndDiscloseCircuit
import {IIdentityVerificationHubV1} from "../interfaces/IIdentityVerificationHubV1.sol";
import {ISelfVerificationRoot} from "../interfaces/ISelfVerificationRoot.sol";
import {CircuitConstants} from "../constants/CircuitConstants.sol";
import {AttestationId} from "../constants/AttestationId.sol";
/**
* @title SelfVerificationRoot
@@ -22,8 +23,8 @@ abstract contract SelfVerificationRoot is ISelfVerificationRoot {
uint256 internal _scope;
/// @notice The attestation ID that proofs must match
/// @dev Used to validate that submitted proofs contain the correct attestation
uint256 internal _attestationId;
/// @dev Used to validate that submitted proofs is generated with allowed attestation IDs
mapping(uint256 => bool) internal _attestationIds;
/// @notice Configuration settings for the verification process
/// @dev Contains settings for age verification, country restrictions, and OFAC checks
@@ -33,6 +34,29 @@ abstract contract SelfVerificationRoot is ISelfVerificationRoot {
/// @dev Immutable reference used for proof verification
IIdentityVerificationHubV1 internal immutable _identityVerificationHub;
// ====================================================
// Circuit Constants
// ====================================================
// Make CircuitConstants available to inheriting contracts
uint256 internal constant REVEALED_DATA_PACKED_INDEX = CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX;
uint256 internal constant FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX = CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX;
uint256 internal constant NULLIFIER_INDEX = CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX;
uint256 internal constant ATTESTATION_ID_INDEX = CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX;
uint256 internal constant MERKLE_ROOT_INDEX = CircuitConstants.VC_AND_DISCLOSE_MERKLE_ROOT_INDEX;
uint256 internal constant CURRENT_DATE_INDEX = CircuitConstants.VC_AND_DISCLOSE_CURRENT_DATE_INDEX;
uint256 internal constant PASSPORT_NO_SMT_ROOT_INDEX = CircuitConstants.VC_AND_DISCLOSE_PASSPORT_NO_SMT_ROOT_INDEX;
uint256 internal constant NAME_DOB_SMT_ROOT_INDEX = CircuitConstants.VC_AND_DISCLOSE_NAME_DOB_SMT_ROOT_INDEX;
uint256 internal constant NAME_YOB_SMT_ROOT_INDEX = CircuitConstants.VC_AND_DISCLOSE_NAME_YOB_SMT_ROOT_INDEX;
uint256 internal constant SCOPE_INDEX = CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX;
uint256 internal constant USER_IDENTIFIER_INDEX = CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX;
// ====================================================
// Attestation ID
// ====================================================
bytes32 constant E_PASSPORT_ID = AttestationId.E_PASSPORT;
// ====================================================
// Errors
// ====================================================
@@ -49,31 +73,78 @@ abstract contract SelfVerificationRoot is ISelfVerificationRoot {
* @notice Initializes the SelfVerificationRoot contract.
* @param identityVerificationHub The address of the Identity Verification Hub.
* @param scope The expected proof scope for user registration.
* @param attestationId The expected attestation identifier required in proofs.
* @param olderThanEnabled Flag indicating if 'olderThan' verification is enabled.
* @param olderThan Value for 'olderThan' verification.
* @param forbiddenCountriesEnabled Flag indicating if forbidden countries verification is enabled.
* @param forbiddenCountriesListPacked Packed list of forbidden countries.
* @param ofacEnabled Array of flags indicating which OFAC checks are enabled. [passportNo, nameAndDob, nameAndYob]
* @param attestationIds The expected attestation identifiers required in proofs.
*/
constructor(
address identityVerificationHub,
uint256 scope,
uint256 attestationId,
bool olderThanEnabled,
uint256 olderThan,
bool forbiddenCountriesEnabled,
uint256[4] memory forbiddenCountriesListPacked,
bool[3] memory ofacEnabled
uint256[] memory attestationIds
) {
_identityVerificationHub = IIdentityVerificationHubV1(identityVerificationHub);
_scope = scope;
_attestationId = attestationId;
_verificationConfig.olderThanEnabled = olderThanEnabled;
_verificationConfig.olderThan = olderThan;
_verificationConfig.forbiddenCountriesEnabled = forbiddenCountriesEnabled;
_verificationConfig.forbiddenCountriesListPacked = forbiddenCountriesListPacked;
_verificationConfig.ofacEnabled = ofacEnabled;
for (uint256 i = 0; i < attestationIds.length; i++) {
_attestationIds[attestationIds[i]] = true;
}
}
/**
* @notice Updates the verification configuration
* @dev Used to set or update verification parameters after contract deployment
* @param verificationConfig The new verification configuration to apply
*/
function _setVerificationConfig(
ISelfVerificationRoot.VerificationConfig memory verificationConfig
) internal {
_verificationConfig = verificationConfig;
}
/**
* @notice Returns the current verification configuration
* @dev Used to retrieve the current verification settings
* @return Current verification configuration
*/
function _getVerificationConfig() internal view returns (ISelfVerificationRoot.VerificationConfig memory) {
return _verificationConfig;
}
/**
* @notice Updates the scope value
* @dev Used to change the expected scope for proofs
* @param newScope The new scope value to set
*/
function _setScope(uint256 newScope) internal {
_scope = newScope;
}
/**
* @notice Adds a new attestation ID to the allowed list
* @dev Used to add support for additional attestation types
* @param attestationId The attestation ID to add
*/
function _addAttestationId(uint256 attestationId) internal {
_attestationIds[attestationId] = true;
}
/**
* @notice Removes an attestation ID from the allowed list
* @dev Used to revoke support for specific attestation types
* @param attestationId The attestation ID to remove
*/
function _removeAttestationId(uint256 attestationId) internal {
_attestationIds[attestationId] = false;
}
/**
* @notice Helper function to get an array of revealed data values from proof signals
* @dev Returns an array of the three packed revealed data values
* @param pubSignals The proof's public signals
* @return revealedDataPacked Array of the three packed revealed data values
*/
function getRevealedDataPacked(uint256[21] memory pubSignals) internal pure returns (uint256[3] memory revealedDataPacked) {
revealedDataPacked[0] = pubSignals[REVEALED_DATA_PACKED_INDEX];
revealedDataPacked[1] = pubSignals[REVEALED_DATA_PACKED_INDEX + 1];
revealedDataPacked[2] = pubSignals[REVEALED_DATA_PACKED_INDEX + 2];
return revealedDataPacked;
}
/**
@@ -82,7 +153,7 @@ abstract contract SelfVerificationRoot is ISelfVerificationRoot {
* @param proof The proof data for verification and disclosure
*/
function verifySelfProof(
IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof
ISelfVerificationRoot.DiscloseCircuitProof memory proof
)
public
virtual
@@ -91,7 +162,7 @@ abstract contract SelfVerificationRoot is ISelfVerificationRoot {
revert InvalidScope();
}
if (_attestationId != proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]) {
if (!_attestationIds[proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]]) {
revert InvalidAttestationId();
}
@@ -102,9 +173,13 @@ abstract contract SelfVerificationRoot is ISelfVerificationRoot {
forbiddenCountriesEnabled: _verificationConfig.forbiddenCountriesEnabled,
forbiddenCountriesListPacked: _verificationConfig.forbiddenCountriesListPacked,
ofacEnabled: _verificationConfig.ofacEnabled,
vcAndDiscloseProof: proof
vcAndDiscloseProof: IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof({
a: proof.a,
b: proof.b,
c: proof.c,
pubSignals: proof.pubSignals
})
})
);
}
}

View File

@@ -5,10 +5,7 @@ import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeE
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {SelfVerificationRoot} from "../abstract/SelfVerificationRoot.sol";
import {ISelfVerificationRoot} from "../interfaces/ISelfVerificationRoot.sol";
import {IIdentityVerificationHubV1} from "../interfaces/IIdentityVerificationHubV1.sol";
import {IVcAndDiscloseCircuitVerifier} from "../interfaces/IVcAndDiscloseCircuitVerifier.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {CircuitConstants} from "../constants/CircuitConstants.sol";
/**
* @title Airdrop (Experimental)
@@ -77,6 +74,12 @@ contract Airdrop is SelfVerificationRoot, Ownable {
event ClaimClose();
event UserIdentifierRegistered(uint256 indexed registeredUserIdentifier, uint256 indexed nullifier);
/// @notice Emitted when the scope is updated.
event ScopeUpdated(uint256 newScope);
/// @notice Emitted when a new attestation ID is added.
event AttestationIdAdded(uint256 attestationId);
/// @notice Emitted when an attestation ID is removed.
event AttestationIdRemoved(uint256 attestationId);
// ====================================================
// Constructor
@@ -88,34 +91,19 @@ contract Airdrop is SelfVerificationRoot, Ownable {
* and sets the ERC20 token to be distributed.
* @param _identityVerificationHub The address of the Identity Verification Hub.
* @param _scope The expected proof scope for user registration.
* @param _attestationId The expected attestation identifier required in proofs.
* @param _attestationIds The expected attestation identifiers required in proofs.
* @param _token The address of the ERC20 token for airdrop.
* @param _olderThanEnabled Flag indicating if 'olderThan' verification is enabled.
* @param _olderThan Value for 'olderThan' verification.
* @param _forbiddenCountriesEnabled Flag indicating if forbidden countries verification is enabled.
* @param _forbiddenCountriesListPacked Packed list of forbidden countries.
* @param _ofacEnabled Array of flags indicating which OFAC checks are enabled. [passportNo, nameAndDob, nameAndYob]
*/
constructor(
address _identityVerificationHub,
uint256 _scope,
uint256 _attestationId,
address _token,
bool _olderThanEnabled,
uint256 _olderThan,
bool _forbiddenCountriesEnabled,
uint256[4] memory _forbiddenCountriesListPacked,
bool[3] memory _ofacEnabled
uint256[] memory _attestationIds,
address _token
)
SelfVerificationRoot(
_identityVerificationHub,
_scope,
_attestationId,
_olderThanEnabled,
_olderThan,
_forbiddenCountriesEnabled,
_forbiddenCountriesListPacked,
_ofacEnabled
_attestationIds
)
Ownable(_msgSender())
{
@@ -143,7 +131,37 @@ contract Airdrop is SelfVerificationRoot, Ownable {
function setVerificationConfig(
ISelfVerificationRoot.VerificationConfig memory newVerificationConfig
) external onlyOwner {
_verificationConfig = newVerificationConfig;
_setVerificationConfig(newVerificationConfig);
}
/**
* @notice Updates the scope used for verification.
* @dev Only callable by the contract owner.
* @param newScope The new scope to set.
*/
function setScope(uint256 newScope) external onlyOwner {
_setScope(newScope);
emit ScopeUpdated(newScope);
}
/**
* @notice Adds a new attestation ID to the allowed list.
* @dev Only callable by the contract owner.
* @param attestationId The attestation ID to add.
*/
function addAttestationId(uint256 attestationId) external onlyOwner {
_addAttestationId(attestationId);
emit AttestationIdAdded(attestationId);
}
/**
* @notice Removes an attestation ID from the allowed list.
* @dev Only callable by the contract owner.
* @param attestationId The attestation ID to remove.
*/
function removeAttestationId(uint256 attestationId) external onlyOwner {
_removeAttestationId(attestationId);
emit AttestationIdRemoved(attestationId);
}
/**
@@ -188,7 +206,7 @@ contract Airdrop is SelfVerificationRoot, Ownable {
* @param proof The VC and Disclose proof data used to verify and register the user.
*/
function verifySelfProof(
IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof
ISelfVerificationRoot.DiscloseCircuitProof memory proof
)
public
override
@@ -197,38 +215,21 @@ contract Airdrop is SelfVerificationRoot, Ownable {
if (!isRegistrationOpen) {
revert RegistrationNotOpen();
}
if (_scope != proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX]) {
revert InvalidScope();
}
if (_attestationId != proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]) {
revert InvalidAttestationId();
}
if (_nullifiers[proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX]] != 0) {
if (_nullifiers[proof.pubSignals[NULLIFIER_INDEX]] != 0) {
revert RegisteredNullifier();
}
if (proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX] == 0) {
if (proof.pubSignals[USER_IDENTIFIER_INDEX] == 0) {
revert InvalidUserIdentifier();
}
IIdentityVerificationHubV1.VcAndDiscloseVerificationResult memory result = _identityVerificationHub.verifyVcAndDisclose(
IIdentityVerificationHubV1.VcAndDiscloseHubProof({
olderThanEnabled: _verificationConfig.olderThanEnabled,
olderThan: _verificationConfig.olderThan,
forbiddenCountriesEnabled: _verificationConfig.forbiddenCountriesEnabled,
forbiddenCountriesListPacked: _verificationConfig.forbiddenCountriesListPacked,
ofacEnabled: _verificationConfig.ofacEnabled,
vcAndDiscloseProof: proof
})
);
super.verifySelfProof(proof);
_nullifiers[result.nullifier] = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX];
_registeredUserIdentifiers[proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX]] = true;
_nullifiers[proof.pubSignals[NULLIFIER_INDEX]] = proof.pubSignals[USER_IDENTIFIER_INDEX];
_registeredUserIdentifiers[proof.pubSignals[USER_IDENTIFIER_INDEX]] = true;
emit UserIdentifierRegistered(proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX], result.nullifier);
emit UserIdentifierRegistered(proof.pubSignals[USER_IDENTIFIER_INDEX], proof.pubSignals[NULLIFIER_INDEX]);
}
/**
@@ -240,11 +241,12 @@ contract Airdrop is SelfVerificationRoot, Ownable {
}
/**
* @notice Retrieves the expected attestation identifier.
* @return The attestation identifier.
* @notice Checks if the specified attestation ID is allowed.
* @param attestationId The attestation ID to check.
* @return True if the attestation ID is allowed, false otherwise.
*/
function getAttestationId() external view returns (uint256) {
return _attestationId;
function isAttestationIdAllowed(uint256 attestationId) external view returns (bool) {
return _attestationIds[attestationId];
}
/**
@@ -261,7 +263,7 @@ contract Airdrop is SelfVerificationRoot, Ownable {
* @return The verification configuration used for registration.
*/
function getVerificationConfig() external view returns (ISelfVerificationRoot.VerificationConfig memory) {
return _verificationConfig;
return _getVerificationConfig();
}
/**

View File

@@ -0,0 +1,216 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {SelfVerificationRoot} from "../abstract/SelfVerificationRoot.sol";
import {ISelfVerificationRoot} from "../interfaces/ISelfVerificationRoot.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SelfCircuitLibrary} from "../libraries/SelfCircuitLibrary.sol";
/**
* @title SelfPassportERC721
* @notice This contract issues ERC721 tokens based on verified passport credentials using self's verification infrastructure
* @dev Inherits from SelfVerificationRoot for verification logic and ERC721 for NFT functionality
*/
contract SelfPassportERC721 is SelfVerificationRoot, ERC721, Ownable {
using SelfCircuitLibrary for uint256[3];
// ====================================================
// Storage Variables
// ====================================================
/// @notice Counter for token IDs
uint256 private _tokenIdCounter;
/// @notice Mapping from token ID to passport attributes
mapping(uint256 => SelfCircuitLibrary.PassportData) private _passportAttributes;
/// @notice Mapping to track used nullifiers
mapping(uint256 => bool) private _usedNullifiers;
// ====================================================
// Events
// ====================================================
event PassportNFTMinted(
uint256 indexed tokenId,
address indexed owner,
SelfCircuitLibrary.PassportData attributes
);
/// @notice Emitted when the scope is updated
event ScopeUpdated(uint256 newScope);
/// @notice Emitted when a new attestation ID is added
event AttestationIdAdded(uint256 attestationId);
/// @notice Emitted when an attestation ID is removed
event AttestationIdRemoved(uint256 attestationId);
// ====================================================
// Errors
// ====================================================
error NullifierAlreadyUsed();
error RegistrationNotOpen();
error InvalidUserIdentifier();
// ====================================================
// Constructor
// ====================================================
/**
* @notice Constructor for the SelfPassportERC721 contract
* @param identityVerificationHub The address of the Identity Verification Hub
* @param scope The expected proof scope for user registration
* @param attestationIds The expected attestation identifiers required in proofs
* @param name The name of the NFT collection
* @param symbol The symbol of the NFT collection
*/
constructor(
address identityVerificationHub,
uint256 scope,
uint256[] memory attestationIds,
string memory name,
string memory symbol
)
SelfVerificationRoot(identityVerificationHub, scope, attestationIds)
ERC721(name, symbol)
Ownable(_msgSender())
{}
// ====================================================
// External/Public Functions
// ====================================================
/**
* @notice Updates the scope used for verification
* @dev Only callable by the contract owner
* @param newScope The new scope to set
*/
function setScope(uint256 newScope) external onlyOwner {
_setScope(newScope);
emit ScopeUpdated(newScope);
}
/**
* @notice Adds a new attestation ID to the allowed list
* @dev Only callable by the contract owner
* @param attestationId The attestation ID to add
*/
function addAttestationId(uint256 attestationId) external onlyOwner {
_addAttestationId(attestationId);
emit AttestationIdAdded(attestationId);
}
/**
* @notice Removes an attestation ID from the allowed list
* @dev Only callable by the contract owner
* @param attestationId The attestation ID to remove
*/
function removeAttestationId(uint256 attestationId) external onlyOwner {
_removeAttestationId(attestationId);
emit AttestationIdRemoved(attestationId);
}
/**
* @notice Updates the verification configuration
* @dev Only callable by the contract owner
* @param verificationConfig The new verification configuration
*/
function setVerificationConfig(
ISelfVerificationRoot.VerificationConfig memory verificationConfig
) external onlyOwner {
_setVerificationConfig(verificationConfig);
}
/**
* @notice Verifies a self-proof and mints an NFT with passport attributes
* @param proof The VC and Disclose proof data used to verify and register the user
*/
function verifySelfProof(
ISelfVerificationRoot.DiscloseCircuitProof memory proof
) public override {
if (_usedNullifiers[proof.pubSignals[NULLIFIER_INDEX]]) {
revert NullifierAlreadyUsed();
}
if (proof.pubSignals[USER_IDENTIFIER_INDEX] == 0) {
revert InvalidUserIdentifier();
}
// Verify the proof using the parent contract's logic
super.verifySelfProof(proof);
// Extract passport attributes from the revealed data using the utility function
uint256[3] memory revealedDataPacked = getRevealedDataPacked(proof.pubSignals);
// Extract passport data using SelfCircuitLibrary
SelfCircuitLibrary.PassportData memory attributes = SelfCircuitLibrary.extractPassportData(revealedDataPacked);
// Mint NFT
uint256 tokenId = _tokenIdCounter++;
_mint(msg.sender, tokenId);
_passportAttributes[tokenId] = attributes;
_usedNullifiers[proof.pubSignals[NULLIFIER_INDEX]] = true;
emit PassportNFTMinted(tokenId, msg.sender, attributes);
}
/**
* @notice Get passport attributes for a specific token ID
* @param tokenId The token ID to query
* @return The passport attributes associated with the token
*/
function getPassportAttributes(uint256 tokenId) external view returns (SelfCircuitLibrary.PassportData memory) {
require(_exists(tokenId), "Token does not exist");
return _passportAttributes[tokenId];
}
/**
* @notice Check if a nullifier has been used
* @param nullifier The nullifier to check
* @return True if the nullifier has been used, false otherwise
*/
function isNullifierUsed(uint256 nullifier) external view returns (bool) {
return _usedNullifiers[nullifier];
}
/**
* @notice Check if the specified attestation ID is allowed
* @param attestationId The attestation ID to check
* @return True if the attestation ID is allowed, false otherwise
*/
function isAttestationIdAllowed(uint256 attestationId) external view returns (bool) {
return _attestationIds[attestationId];
}
/**
* @notice Get the current scope value
* @return The current scope value
*/
function getScope() external view returns (uint256) {
return _scope;
}
/**
* @notice Get the current verification configuration
* @return The current verification configuration
*/
function getVerificationConfig() external view returns (ISelfVerificationRoot.VerificationConfig memory) {
return _getVerificationConfig();
}
// ====================================================
// Internal Functions
// ====================================================
/**
* @notice Check if a token exists
* @param tokenId The token ID to check
* @return True if the token exists, false otherwise
*/
function _exists(uint256 tokenId) internal view returns (bool) {
return _ownerOf(tokenId) != address(0);
}
}

View File

@@ -26,8 +26,8 @@ interface IIdentityVerificationHubV1 {
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 status.
NAME_AND_YOB_OFAC // The name and year of birth 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.
}
/**

View File

@@ -12,9 +12,20 @@ interface ISelfVerificationRoot {
uint256[4] forbiddenCountriesListPacked;
bool[3] ofacEnabled;
}
struct DiscloseCircuitProof {
uint256[2] a;
uint256[2][2] b;
uint256[2] c;
uint256[21] pubSignals;
}
/**
* @notice Verifies a self-proof
* @param proof The proof data for verification and disclosure
*/
function verifySelfProof(
IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof
DiscloseCircuitProof memory proof
) external;
}

View File

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

View File

@@ -0,0 +1,223 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Formatter} from "./Formatter.sol";
import {CircuitAttributeHandler} from "./CircuitAttributeHandler.sol";
/**
* @title SelfCircuitLibrary
* @notice A user-friendly wrapper library for handling passport verification and formatting
* @dev This library provides simplified interfaces for common operations using CircuitAttributeHandler and Formatter
*/
library SelfCircuitLibrary {
/**
* @notice Represents passport attributes in a more user-friendly format
*/
struct PassportData {
string issuingState;
string[] name; // [firstName, lastName]
string passportNumber;
string nationality;
string dateOfBirth;
string gender;
string expiryDate;
uint256 olderThan;
bool passportNoOfac;
bool nameAndDobOfac;
bool nameAndYobOfac;
}
/**
* @notice Extracts and formats passport data from packed revealed data
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return PassportData struct containing all formatted passport attributes
*/
function extractPassportData(uint256[3] memory revealedDataPacked) internal pure returns (PassportData memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return PassportData({
issuingState: CircuitAttributeHandler.getIssuingState(charcodes),
name: CircuitAttributeHandler.getName(charcodes),
passportNumber: CircuitAttributeHandler.getPassportNumber(charcodes),
nationality: CircuitAttributeHandler.getNationality(charcodes),
dateOfBirth: CircuitAttributeHandler.getDateOfBirth(charcodes),
gender: CircuitAttributeHandler.getGender(charcodes),
expiryDate: CircuitAttributeHandler.getExpiryDate(charcodes),
olderThan: CircuitAttributeHandler.getOlderThan(charcodes),
passportNoOfac: CircuitAttributeHandler.getPassportNoOfac(charcodes) == 1,
nameAndDobOfac: CircuitAttributeHandler.getNameAndDobOfac(charcodes) == 1,
nameAndYobOfac: CircuitAttributeHandler.getNameAndYobOfac(charcodes) == 1
});
}
/**
* @notice Retrieves the issuing state from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string The issuing state
*/
function getIssuingState(uint256[3] memory revealedDataPacked) internal pure returns (string memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getIssuingState(charcodes);
}
/**
* @notice Retrieves and formats the name from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string[] Array containing [firstName, lastName]
*/
function getName(uint256[3] memory revealedDataPacked) internal pure returns (string[] memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getName(charcodes);
}
/**
* @notice Retrieves the passport number from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string The passport number
*/
function getPassportNumber(uint256[3] memory revealedDataPacked) internal pure returns (string memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getPassportNumber(charcodes);
}
/**
* @notice Retrieves the nationality from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string The nationality
*/
function getNationality(uint256[3] memory revealedDataPacked) internal pure returns (string memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getNationality(charcodes);
}
/**
* @notice Retrieves and formats the date of birth from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string The formatted date of birth
*/
function getDateOfBirth(uint256[3] memory revealedDataPacked) internal pure returns (string memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getDateOfBirth(charcodes);
}
/**
* @notice Retrieves the gender from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string The gender
*/
function getGender(uint256[3] memory revealedDataPacked) internal pure returns (string memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getGender(charcodes);
}
/**
* @notice Retrieves and formats the passport expiry date from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return string The formatted passport expiry date
*/
function getExpiryDate(uint256[3] memory revealedDataPacked) internal pure returns (string memory) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getExpiryDate(charcodes);
}
/**
* @notice Retrieves the 'older than' age attribute from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return uint256 The extracted age
*/
function getOlderThan(uint256[3] memory revealedDataPacked) internal pure returns (uint256) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getOlderThan(charcodes);
}
/**
* @notice Retrieves the passport number OFAC status from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return bool True if passport number is not on OFAC list
*/
function getPassportNoOfac(uint256[3] memory revealedDataPacked) internal pure returns (bool) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getPassportNoOfac(charcodes) == 1;
}
/**
* @notice Retrieves the name and date of birth OFAC status from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return bool True if name and DOB are not on OFAC list
*/
function getNameAndDobOfac(uint256[3] memory revealedDataPacked) internal pure returns (bool) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getNameAndDobOfac(charcodes) == 1;
}
/**
* @notice Retrieves the name and year of birth OFAC status from the encoded attribute byte array
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @return bool True if name and YOB are not on OFAC list
*/
function getNameAndYobOfac(uint256[3] memory revealedDataPacked) internal pure returns (bool) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.getNameAndYobOfac(charcodes) == 1;
}
/**
* @notice Checks if a passport holder meets age requirements
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @param minimumAge The minimum age requirement
* @return bool True if the passport holder is at least minimumAge years old
*/
function compareOlderThan(uint256[3] memory revealedDataPacked, uint256 minimumAge) internal pure returns (bool) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.compareOlderThan(charcodes, minimumAge);
}
/**
* @notice Performs OFAC checks with simplified interface
* @param revealedDataPacked An array of three packed uint256 values containing the passport data
* @param checkPassportNo Whether to check passport number against OFAC
* @param checkNameAndDob Whether to check name and date of birth against OFAC
* @param checkNameAndYob Whether to check name and year of birth against OFAC
* @return bool True if all enabled checks pass
*/
function compareOfac(
uint256[3] memory revealedDataPacked,
bool checkPassportNo,
bool checkNameAndDob,
bool checkNameAndYob
) internal pure returns (bool) {
bytes memory charcodes = Formatter.fieldElementsToBytes(revealedDataPacked);
return CircuitAttributeHandler.compareOfac(
charcodes,
checkPassportNo,
checkNameAndDob,
checkNameAndYob
);
}
/**
* @notice Converts a date string to Unix timestamp
* @param dateString Date string in YYMMDD format
* @return uint256 Unix timestamp
*/
function dateToTimestamp(string memory dateString) internal pure returns (uint256) {
return Formatter.dateToUnixTimestamp(dateString);
}
/**
* @notice Formats a date string from YYMMDD to DD-MM-YY
* @param dateString Date string in YYMMDD format
* @return string Formatted date string in DD-MM-YY format
*/
function formatDate(string memory dateString) internal pure returns (string memory) {
return Formatter.formatDate(dateString);
}
/**
* @notice Formats a full name string into first name and last name
* @param fullName Full name string in "lastName<<firstName" format
* @return string[] Array containing [firstName, lastName]
*/
function formatName(string memory fullName) internal pure returns (string[] memory) {
return Formatter.formatName(fullName);
}
}

View File

@@ -1,25 +1,20 @@
{
"name": "@openpassport/contracts",
"name": "@selfxyz/contracts",
"version": "0.0.7",
"repository": {
"type": "git",
"url": "https://github.com/zk-passport/openpassport"
"url": "https://github.com/selfxyz/self"
},
"license": "MIT",
"author": "motemotech <i.am.nicoshark@gmail.com>",
"files": [
"contracts/constants/*",
"contracts/interfaces/*",
"contracts/constants/*",
"contracts/libraries/*",
"contracts/proxy/*",
"contracts/registry/*",
"contracts/verifiers/*",
"contracts/IdentityVerificationHub.sol",
"contracts/IdentityVerificationHubImplV1.sol",
"contracts/utils/PCR0Manager.sol"
"contracts/abstract/*"
],
"scripts": {
"compile": "npx hardhat clean && npx hardhat compile",
"build": "npx hardhat clean && npx hardhat compile",
"deploy:all:localhost": "npm run deploy:verifiers:localhost && npm run deploy:registry:localhost && npm run deploy:hub:localhost",
"deploy:hub:localhost": "npx hardhat ignition deploy ignition/modules/deployHub.ts --network localhost --verify",
"deploy:registry:localhost": "npx hardhat ignition deploy ignition/modules/deployRegistry.ts --network localhost --verify",

View File

@@ -63,12 +63,13 @@ const errorSignatures = [
'INVALID_REVEALED_DATA_TYPE()',
'HUB_NOT_SET()',
'ONLY_HUB_CAN_ACCESS()',
'REGISTERED_COMMITMENT()'
'REGISTERED_COMMITMENT()',
'RegisteredNullifier()'
];
errorSignatures.forEach(sig => {
// Pls input the error code
const errorCode = '0x9003ac4d';
const errorCode = '0x22cbc6a2';
const selector = ethers.id(sig).slice(0, 10);
if (selector === errorCode) {
console.log(`Found matching error: ${sig}`);

View File

@@ -13,6 +13,7 @@ import BalanceTree from "../utils/example/balance-tree";
import { castFromScope } from "../../../common/src/utils/circuits/uuid";
import { formatCountriesList, reverseBytes } from '../../../common/src/utils/circuits/formatInputs';
import { Formatter } from "../utils/formatter";
import { hashEndpointWithScope } from "../../../common/src/utils/scope";
describe("Airdrop", () => {
let deployedActors: DeployedActors;
@@ -27,12 +28,14 @@ describe("Airdrop", () => {
let nullifier: any;
let forbiddenCountriesList: any;
let countriesListPacked: any;
let attestationIds: any[];
before(async () => {
deployedActors = await deploySystemFixtures();
registerSecret = generateRandomFieldElement();
nullifier = generateRandomFieldElement();
attestationIds = [BigInt(ATTESTATION_ID.E_PASSPORT)];
commitment = generateCommitment(registerSecret, ATTESTATION_ID.E_PASSPORT, deployedActors.mockPassport);
forbiddenCountriesList = ['AAA', 'ABC', 'CBA'];
@@ -45,7 +48,7 @@ describe("Airdrop", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
deployedActors.mockPassport,
"test-airdrop",
hashEndpointWithScope("https://test.com", "test-scope"),
new Array(88).fill("1"),
"1",
imt,
@@ -73,17 +76,21 @@ describe("Airdrop", () => {
const airdropFactory = await ethers.getContractFactory("Airdrop");
airdrop = await airdropFactory.connect(deployedActors.owner).deploy(
deployedActors.hub.target,
castFromScope("test-airdrop"),
ATTESTATION_ID.E_PASSPORT,
token.target,
true,
20,
true,
countriesListPacked,
[true, true, true],
hashEndpointWithScope("https://test.com", "test-scope"),
attestationIds,
token.target
);
await airdrop.waitForDeployment();
const verificationConfig = {
olderThanEnabled: true,
olderThan: 20,
forbiddenCountriesEnabled: true,
forbiddenCountriesListPacked: countriesListPacked,
ofacEnabled: [true, true, true] as [boolean, boolean, boolean]
};
await airdrop.connect(deployedActors.owner).setVerificationConfig(verificationConfig);
const mintAmount = ethers.parseEther("424242424242");
await token.mint(airdrop.target, mintAmount);
@@ -234,11 +241,17 @@ describe("Airdrop", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
deployedActors.mockPassport,
"test-airdrop-invalid",
hashEndpointWithScope("https://test.com", "test-scope-invalid"),
new Array(88).fill("1"),
"1",
imt,
"20",
undefined,
undefined,
undefined,
undefined,
forbiddenCountriesList,
(await deployedActors.user1.getAddress()).slice(2)
);
await airdrop.connect(owner).openRegistration();
@@ -275,11 +288,17 @@ describe("Airdrop", () => {
registerSecret,
BigInt(ATTESTATION_ID.INVALID_ATTESTATION_ID).toString(),
deployedActors.mockPassport,
"test-airdrop",
hashEndpointWithScope("https://test.com", "test-scope"),
new Array(88).fill("1"),
"1",
invalidImt,
"20",
undefined,
undefined,
undefined,
undefined,
forbiddenCountriesList,
(await deployedActors.user1.getAddress()).slice(2)
);
await airdrop.connect(owner).openRegistration();
@@ -294,13 +313,15 @@ describe("Airdrop", () => {
registerSecret,
BigInt(ATTESTATION_ID.E_PASSPORT).toString(),
deployedActors.mockPassport,
"test-airdrop",
hashEndpointWithScope("https://test.com", "test-scope"),
new Array(88).fill("1"),
"1",
imt,
"20",
undefined,
undefined,
undefined,
undefined,
forbiddenCountriesList,
"0000000000000000000000000000000000000000"
);
@@ -316,17 +337,21 @@ describe("Airdrop", () => {
const airdropFactory = await ethers.getContractFactory("Airdrop");
const newAirdrop = await airdropFactory.connect(owner).deploy(
hub.target,
castFromScope("test-airdrop"),
ATTESTATION_ID.E_PASSPORT,
token.target,
true,
20,
true,
countriesListPacked,
[true, true, true],
hashEndpointWithScope("https://test.com", "test-scope"),
attestationIds,
token.target
);
await newAirdrop.waitForDeployment();
const verificationConfig = {
olderThanEnabled: true,
olderThan: 20,
forbiddenCountriesEnabled: true,
forbiddenCountriesListPacked: countriesListPacked,
ofacEnabled: [true, true, true] as [boolean, boolean, boolean]
};
await newAirdrop.connect(owner).setVerificationConfig(verificationConfig);
await newAirdrop.connect(owner).openRegistration();
await expect(newAirdrop.connect(user1).verifySelfProof(vcAndDiscloseProof))
.to.not.be.reverted;
@@ -334,12 +359,15 @@ describe("Airdrop", () => {
it("should return correct scope", async () => {
const scope = await airdrop.getScope();
expect(scope).to.equal(castFromScope("test-airdrop"));
expect(scope).to.equal(hashEndpointWithScope("https://test.com", "test-scope"));
});
it("should return correct attestation id", async () => {
const attestationId = await airdrop.getAttestationId();
expect(attestationId).to.equal(ATTESTATION_ID.E_PASSPORT);
it("should check if attestation ID is allowed", async () => {
const isAllowed = await airdrop.isAttestationIdAllowed(ATTESTATION_ID.E_PASSPORT);
expect(isAllowed).to.be.true;
const isNotAllowed = await airdrop.isAttestationIdAllowed(999999); // Some random ID not in the list
expect(isNotAllowed).to.be.false;
});
it("should return correct merkle root", async () => {
@@ -511,7 +539,7 @@ describe("Airdrop", () => {
olderThan: 25,
forbiddenCountriesEnabled: false,
forbiddenCountriesListPacked: countriesListPacked,
ofacEnabled: [false, false, false]
ofacEnabled: [false, false, false] as [boolean, boolean, boolean]
};
await airdrop.connect(owner).setVerificationConfig(newVerificationConfig);
@@ -533,7 +561,7 @@ describe("Airdrop", () => {
olderThan: 25,
forbiddenCountriesEnabled: false,
forbiddenCountriesListPacked: countriesListPacked,
ofacEnabled: [false, false, false]
ofacEnabled: [false, false, false] as [boolean, boolean, boolean]
};
await expect(airdrop.connect(user1).setVerificationConfig(newVerificationConfig))
@@ -552,4 +580,76 @@ describe("Airdrop", () => {
expect(config.ofacEnabled).to.deep.equal([true, true, true]);
});
it("should able to update scope by owner", async () => {
const { owner } = deployedActors;
const newScope = hashEndpointWithScope("https://newtest.com", "new-test-scope");
await airdrop.connect(owner).setScope(newScope);
const scope = await airdrop.getScope();
expect(scope).to.equal(newScope);
// Verify event was emitted
const filter = airdrop.filters.ScopeUpdated();
const events = await airdrop.queryFilter(filter);
const lastEvent = events[events.length - 1];
expect(lastEvent.args.newScope).to.equal(newScope);
});
it("should not be able to update scope by non-owner", async () => {
const { user1 } = deployedActors;
const newScope = hashEndpointWithScope("https://newtest.com", "new-test-scope");
await expect(airdrop.connect(user1).setScope(newScope))
.to.be.revertedWithCustomError(airdrop, "OwnableUnauthorizedAccount")
.withArgs(await user1.getAddress());
});
it("should able to add attestation ID by owner", async () => {
const { owner } = deployedActors;
const newAttestationId = 999; // Some new ID
await airdrop.connect(owner).addAttestationId(newAttestationId);
const isAllowed = await airdrop.isAttestationIdAllowed(newAttestationId);
expect(isAllowed).to.be.true;
// Verify event was emitted
const filter = airdrop.filters.AttestationIdAdded();
const events = await airdrop.queryFilter(filter);
const lastEvent = events[events.length - 1];
expect(lastEvent.args.attestationId).to.equal(newAttestationId);
});
it("should not be able to add attestation ID by non-owner", async () => {
const { user1 } = deployedActors;
const newAttestationId = 888; // Some new ID
await expect(airdrop.connect(user1).addAttestationId(newAttestationId))
.to.be.revertedWithCustomError(airdrop, "OwnableUnauthorizedAccount")
.withArgs(await user1.getAddress());
});
it("should able to remove attestation ID by owner", async () => {
const { owner } = deployedActors;
const attestationIdToRemove = ATTESTATION_ID.E_PASSPORT;
await airdrop.connect(owner).removeAttestationId(attestationIdToRemove);
const isAllowed = await airdrop.isAttestationIdAllowed(attestationIdToRemove);
expect(isAllowed).to.be.false;
// Verify event was emitted
const filter = airdrop.filters.AttestationIdRemoved();
const events = await airdrop.queryFilter(filter);
const lastEvent = events[events.length - 1];
expect(lastEvent.args.attestationId).to.equal(attestationIdToRemove);
});
it("should not be able to remove attestation ID by non-owner", async () => {
const { user1 } = deployedActors;
const attestationIdToRemove = ATTESTATION_ID.E_PASSPORT;
await expect(airdrop.connect(user1).removeAttestationId(attestationIdToRemove))
.to.be.revertedWithCustomError(airdrop, "OwnableUnauthorizedAccount")
.withArgs(await user1.getAddress());
});
});

File diff suppressed because it is too large Load Diff