@zentity/fhevm-contracts 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/abi/ComplianceRules.json +25 -16
- package/abi/CompliantERC20.json +30 -16
- package/abi/IdentityRegistry.json +496 -119
- package/contracts/compliance/ComplianceRules.sol +40 -189
- package/contracts/core/IdentityRegistry.sol +280 -232
- package/contracts/interfaces/IComplianceRules.sol +30 -0
- package/contracts/interfaces/IIdentityRegistry.sol +133 -159
- package/contracts/proxy/ERC1967Proxy.sol +6 -0
- package/contracts/test/MockFacilitator.sol +40 -0
- package/contracts/tokens/CompliantERC20.sol +28 -242
- package/deployments/hardhat/addresses.json +3 -8
- package/deployments/sepolia/ComplianceRules.json +91 -207
- package/deployments/sepolia/CompliantERC20.json +114 -293
- package/deployments/sepolia/IdentityRegistry.json +498 -742
- package/deployments/sepolia/IdentityRegistry_Implementation.json +1698 -0
- package/deployments/sepolia/IdentityRegistry_Proxy.json +192 -0
- package/deployments/sepolia/addresses.json +5 -6
- package/dist/index.d.ts +175 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -1
- package/package.json +1 -1
- package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
- package/typechain-types/@openzeppelin/contracts/access/Ownable2Step.ts +217 -0
- package/typechain-types/@openzeppelin/contracts/access/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts/index.ts +11 -0
- package/typechain-types/@openzeppelin/contracts/interfaces/IERC1967.ts +168 -0
- package/typechain-types/@openzeppelin/contracts/interfaces/IERC5267.ts +151 -0
- package/typechain-types/{contracts/tokens/CompliantERC20.sol/IComplianceChecker.ts → @openzeppelin/contracts/interfaces/draft-IERC1822.sol/IERC1822Proxiable.ts} +12 -17
- package/typechain-types/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/interfaces/index.ts +7 -0
- package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.ts +105 -0
- package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts/proxy/Proxy.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/IBeacon.ts +90 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/proxy/index.ts +8 -0
- package/typechain-types/@openzeppelin/contracts/utils/Address.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/Errors.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/Strings.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/cryptography/ECDSA.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/cryptography/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/utils/index.ts +10 -0
- package/typechain-types/@openzeppelin/contracts/utils/math/SafeCast.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/math/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.ts +251 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.ts +186 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/access/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/index.ts +9 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.ts +105 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.ts +196 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.ts +105 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.ts +184 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/cryptography/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/index.ts +6 -0
- package/typechain-types/@openzeppelin/index.ts +7 -0
- package/typechain-types/contracts/compliance/ComplianceRules.ts +18 -7
- package/typechain-types/contracts/core/IdentityRegistry.ts +493 -233
- package/typechain-types/contracts/index.ts +2 -0
- package/typechain-types/contracts/interfaces/IComplianceRules.ts +290 -0
- package/typechain-types/contracts/interfaces/IIdentityRegistry.ts +269 -341
- package/typechain-types/contracts/interfaces/index.ts +1 -0
- package/typechain-types/contracts/test/MockFacilitator.ts +187 -0
- package/typechain-types/contracts/test/index.ts +4 -0
- package/typechain-types/contracts/tokens/{CompliantERC20.sol/CompliantERC20.ts → CompliantERC20.ts} +19 -8
- package/typechain-types/contracts/tokens/index.ts +1 -2
- package/typechain-types/factories/@openzeppelin/contracts/access/Ownable2Step__factory.ts +138 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +96 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts/index.ts +7 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/IERC1967__factory.ts +67 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/IERC5267__factory.ts +71 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/IERC1822Proxiable__factory.ts +38 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/index.ts +6 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy__factory.ts +144 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils__factory.ts +105 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/Proxy__factory.ts +26 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/IBeacon__factory.ts +35 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/index.ts +6 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/Address__factory.ts +75 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/Errors__factory.ts +101 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/Strings__factory.ts +90 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/cryptography/ECDSA__factory.ts +91 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/cryptography/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/index.ts +8 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/math/SafeCast__factory.ts +118 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/math/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable__factory.ts +165 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable__factory.ts +122 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/index.ts +6 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable__factory.ts +48 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable__factory.ts +153 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable__factory.ts +48 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable__factory.ts +97 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/cryptography/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/index.ts +5 -0
- package/typechain-types/factories/contracts/compliance/ComplianceRules__factory.ts +26 -17
- package/typechain-types/factories/contracts/core/IdentityRegistry__factory.ts +493 -116
- package/typechain-types/factories/contracts/index.ts +1 -0
- package/typechain-types/factories/contracts/interfaces/IComplianceRules__factory.ts +193 -0
- package/typechain-types/factories/contracts/interfaces/IIdentityRegistry__factory.ts +236 -186
- package/typechain-types/factories/contracts/interfaces/index.ts +1 -0
- package/typechain-types/factories/contracts/test/MockFacilitator__factory.ts +194 -0
- package/typechain-types/factories/contracts/test/index.ts +4 -0
- package/typechain-types/factories/contracts/tokens/CompliantERC20__factory.ts +595 -0
- package/typechain-types/factories/contracts/tokens/index.ts +1 -1
- package/typechain-types/factories/index.ts +1 -0
- package/typechain-types/hardhat.d.ts +394 -16
- package/typechain-types/index.ts +48 -4
- package/deployments/hardhat/.chainId +0 -1
- package/typechain-types/contracts/tokens/CompliantERC20.sol/index.ts +0 -5
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/CompliantERC20__factory.ts +0 -581
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/IComplianceChecker__factory.ts +0 -44
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/index.ts +0 -5
|
@@ -3,229 +3,298 @@
|
|
|
3
3
|
pragma solidity ^0.8.27;
|
|
4
4
|
|
|
5
5
|
import {FHE, euint8, euint16, ebool, externalEuint8, externalEuint16, externalEbool} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {ZamaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
7
|
+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
8
|
+
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
9
|
+
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
|
10
|
+
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
|
11
|
+
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
7
12
|
import {IIdentityRegistry} from "../interfaces/IIdentityRegistry.sol";
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
/// @title IdentityRegistry
|
|
15
|
+
/// @author Gustavo Valverde
|
|
16
|
+
/// @notice On-chain encrypted identity registry with EIP-712 registrar permits,
|
|
17
|
+
/// per-attribute selective grants, and x402 compliance oracle surface.
|
|
18
|
+
/// @dev UUPS-upgradeable. User submits attestation tx with registrar-signed permit.
|
|
19
|
+
/// Compliance checks are composable by x402 facilitators and DeFi contracts
|
|
20
|
+
/// via the branch-free FHE.select() pattern.
|
|
21
|
+
contract IdentityRegistry is
|
|
22
|
+
IIdentityRegistry,
|
|
23
|
+
Initializable,
|
|
24
|
+
UUPSUpgradeable,
|
|
25
|
+
Ownable2StepUpgradeable,
|
|
26
|
+
EIP712Upgradeable
|
|
27
|
+
{
|
|
28
|
+
// ============ Constants ============
|
|
29
|
+
|
|
30
|
+
uint8 public constant ATTR_BIRTH_YEAR = 0x01;
|
|
31
|
+
uint8 public constant ATTR_COUNTRY = 0x02;
|
|
32
|
+
uint8 public constant ATTR_COMPLIANCE = 0x04;
|
|
33
|
+
uint8 public constant ATTR_BLACKLIST = 0x08;
|
|
34
|
+
uint8 public constant ATTR_ALL = 0x0F;
|
|
35
|
+
|
|
36
|
+
/// @dev EIP-712 typehash for the attestation permit
|
|
37
|
+
bytes32 public constant ATTEST_PERMIT_TYPEHASH = keccak256(
|
|
38
|
+
"AttestPermit(address user,uint8 birthYearOffset,uint16 countryCode,uint8 complianceLevel,bool isBlacklisted,bytes32 proofSetHash,uint32 policyVersion,uint256 nonce,uint256 deadline)"
|
|
39
|
+
);
|
|
40
|
+
|
|
30
41
|
// ============ Encrypted Identity Attributes ============
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
mapping(address user =>
|
|
43
|
+
mapping(address user => euint8 offset) private birthYearOffsets;
|
|
44
|
+
mapping(address user => euint16 code) private countryCodes;
|
|
45
|
+
mapping(address user => euint8 level) private complianceLevels;
|
|
46
|
+
mapping(address user => ebool status) private blacklistStatuses;
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
mapping(address user => euint16 countryCode) private countryCodes;
|
|
48
|
+
// ============ Attestation Metadata ============
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
mapping(address user =>
|
|
50
|
+
mapping(address user => uint256 id) public currentAttestationId;
|
|
51
|
+
mapping(address user => uint256 ts) public attestationTimestamp;
|
|
52
|
+
mapping(address user => bytes32 hash) public proofSetHashes;
|
|
53
|
+
mapping(address user => uint32 version) public policyVersions;
|
|
54
|
+
uint256 public latestAttestationId;
|
|
40
55
|
|
|
41
|
-
|
|
42
|
-
mapping(address user => ebool blacklisted) private isBlacklisted;
|
|
56
|
+
// ============ Access Control ============
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
mapping(address user => uint256
|
|
58
|
+
mapping(address registrar => bool authorized) public registrars;
|
|
59
|
+
mapping(address user => uint256 nonce) public nonces;
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
mapping(address user => uint256 attestationId) public currentAttestationId;
|
|
61
|
+
// ============ Per-Attribute Grants ============
|
|
49
62
|
|
|
50
|
-
/// @
|
|
51
|
-
mapping(
|
|
63
|
+
/// @dev keccak256(user, grantee) => attribute bitmask
|
|
64
|
+
mapping(bytes32 grantKey => uint8 mask) private attributeGrants;
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
struct AttestationMetadata {
|
|
55
|
-
uint256 timestamp;
|
|
56
|
-
uint256 revokedAt;
|
|
57
|
-
address registrar;
|
|
58
|
-
}
|
|
66
|
+
// ============ Verification Results ============
|
|
59
67
|
|
|
60
|
-
/// @notice Attestation history by user and attestation id
|
|
61
|
-
mapping(address user => mapping(uint256 attestationId => AttestationMetadata)) private attestationHistory;
|
|
62
|
-
|
|
63
|
-
/// @notice Store verification results for external queries
|
|
64
68
|
mapping(bytes32 key => ebool result) private verificationResults;
|
|
65
69
|
|
|
66
|
-
// ============
|
|
67
|
-
|
|
68
|
-
/// @notice Owner of the registry
|
|
69
|
-
address public owner;
|
|
70
|
-
/// @notice Pending owner for two-step ownership transfer
|
|
71
|
-
address public pendingOwner;
|
|
70
|
+
// ============ Upgrade Safety ============
|
|
72
71
|
|
|
73
|
-
/// @
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/// @notice Thrown when caller lacks permission for encrypted data
|
|
77
|
-
error AccessProhibited();
|
|
72
|
+
/// @dev Reserved storage gap for future upgrades
|
|
73
|
+
uint256[50] private __gap;
|
|
78
74
|
|
|
79
75
|
// ============ Modifiers ============
|
|
80
76
|
|
|
81
|
-
modifier
|
|
82
|
-
if (msg.sender
|
|
77
|
+
modifier onlyRegistrar() {
|
|
78
|
+
if (!registrars[msg.sender]) revert OnlyRegistrar();
|
|
83
79
|
_;
|
|
84
80
|
}
|
|
85
81
|
|
|
86
|
-
modifier
|
|
87
|
-
if (
|
|
82
|
+
modifier whenAttested(address user) {
|
|
83
|
+
if (currentAttestationId[user] == 0) revert NotAttested();
|
|
88
84
|
_;
|
|
89
85
|
}
|
|
90
86
|
|
|
91
|
-
// ============
|
|
87
|
+
// ============ Initializer ============
|
|
92
88
|
|
|
93
|
-
/// @
|
|
89
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
94
90
|
constructor() {
|
|
95
|
-
|
|
96
|
-
registrars[msg.sender] = true;
|
|
97
|
-
emit RegistrarAdded(msg.sender);
|
|
91
|
+
_disableInitializers();
|
|
98
92
|
}
|
|
99
93
|
|
|
100
|
-
|
|
94
|
+
/// @notice Initialize the registry (called once via proxy)
|
|
95
|
+
/// @param initialOwner Address that becomes owner and first registrar
|
|
96
|
+
function initialize(address initialOwner) external initializer {
|
|
97
|
+
if (initialOwner == address(0)) revert ZeroAddress();
|
|
101
98
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
__UUPSUpgradeable_init();
|
|
100
|
+
__Ownable2Step_init();
|
|
101
|
+
__Ownable_init(initialOwner);
|
|
102
|
+
__EIP712_init("ZentityIdentityRegistry", "2");
|
|
103
|
+
|
|
104
|
+
// Set up FHEVM coprocessor (replaces ZamaEthereumConfig constructor)
|
|
105
|
+
FHE.setCoprocessor(ZamaConfig.getEthereumCoprocessorConfig());
|
|
106
|
+
|
|
107
|
+
registrars[initialOwner] = true;
|
|
108
|
+
emit RegistrarUpdated(initialOwner, true);
|
|
106
109
|
}
|
|
107
110
|
|
|
111
|
+
// ============ Attestation with EIP-712 Permit ============
|
|
112
|
+
|
|
108
113
|
/// @inheritdoc IIdentityRegistry
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
function attestWithPermit(
|
|
115
|
+
AttestPermitData calldata permit,
|
|
116
|
+
externalEuint8 encBirthYearOffset,
|
|
117
|
+
externalEuint16 encCountryCode,
|
|
118
|
+
externalEuint8 encComplianceLevel,
|
|
119
|
+
externalEbool encIsBlacklisted,
|
|
120
|
+
bytes calldata inputProof
|
|
121
|
+
) external {
|
|
122
|
+
if (currentAttestationId[msg.sender] != 0) revert AlreadyAttested();
|
|
123
|
+
if (block.timestamp > permit.deadline) revert PermitExpired();
|
|
124
|
+
|
|
125
|
+
// Verify registrar signature over plaintext values
|
|
126
|
+
_verifyPermit(permit);
|
|
127
|
+
|
|
128
|
+
// Convert and store FHE-encrypted values
|
|
129
|
+
_storeEncryptedValues(encBirthYearOffset, encCountryCode, encComplianceLevel, encIsBlacklisted, inputProof);
|
|
130
|
+
|
|
131
|
+
// Store attestation metadata
|
|
132
|
+
latestAttestationId++;
|
|
133
|
+
currentAttestationId[msg.sender] = latestAttestationId;
|
|
134
|
+
attestationTimestamp[msg.sender] = block.timestamp;
|
|
135
|
+
proofSetHashes[msg.sender] = permit.proofSetHash;
|
|
136
|
+
policyVersions[msg.sender] = permit.policyVersion;
|
|
137
|
+
|
|
138
|
+
emit IdentityAttested(msg.sender);
|
|
112
139
|
}
|
|
113
140
|
|
|
114
|
-
|
|
141
|
+
/// @dev Verify the registrar's EIP-712 signature and increment nonce
|
|
142
|
+
function _verifyPermit(AttestPermitData calldata permit) internal {
|
|
143
|
+
uint256 currentNonce = nonces[msg.sender];
|
|
144
|
+
bytes32 structHash = keccak256(
|
|
145
|
+
abi.encode(
|
|
146
|
+
ATTEST_PERMIT_TYPEHASH,
|
|
147
|
+
msg.sender,
|
|
148
|
+
permit.birthYearOffset,
|
|
149
|
+
permit.countryCode,
|
|
150
|
+
permit.complianceLevel,
|
|
151
|
+
permit.isBlacklisted,
|
|
152
|
+
permit.proofSetHash,
|
|
153
|
+
permit.policyVersion,
|
|
154
|
+
currentNonce,
|
|
155
|
+
permit.deadline
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
address signer = ECDSA.recover(_hashTypedDataV4(structHash), permit.v, permit.r, permit.s);
|
|
160
|
+
if (!registrars[signer]) revert InvalidPermit();
|
|
161
|
+
|
|
162
|
+
nonces[msg.sender] = currentNonce + 1;
|
|
163
|
+
}
|
|
115
164
|
|
|
116
|
-
/// @
|
|
117
|
-
function
|
|
118
|
-
address user,
|
|
165
|
+
/// @dev Convert external encrypted inputs, store, and grant ACL permissions
|
|
166
|
+
function _storeEncryptedValues(
|
|
119
167
|
externalEuint8 encBirthYearOffset,
|
|
120
168
|
externalEuint16 encCountryCode,
|
|
121
169
|
externalEuint8 encComplianceLevel,
|
|
122
170
|
externalEbool encIsBlacklisted,
|
|
123
171
|
bytes calldata inputProof
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
FHE.
|
|
140
|
-
FHE.
|
|
141
|
-
FHE.
|
|
142
|
-
FHE.
|
|
143
|
-
|
|
144
|
-
// Grant user permission to their own data
|
|
145
|
-
FHE.allow(birthYear, user);
|
|
146
|
-
FHE.allow(country, user);
|
|
147
|
-
FHE.allow(compliance, user);
|
|
148
|
-
FHE.allow(blacklisted, user);
|
|
149
|
-
|
|
150
|
-
uint256 newAttestationId = latestAttestationId[user] + 1;
|
|
151
|
-
latestAttestationId[user] = newAttestationId;
|
|
152
|
-
currentAttestationId[user] = newAttestationId;
|
|
153
|
-
attestationHistory[user][newAttestationId] = AttestationMetadata({
|
|
154
|
-
timestamp: block.timestamp,
|
|
155
|
-
revokedAt: 0,
|
|
156
|
-
registrar: msg.sender
|
|
157
|
-
});
|
|
158
|
-
attestationTimestamp[user] = block.timestamp;
|
|
159
|
-
|
|
160
|
-
emit IdentityAttested(user, msg.sender);
|
|
161
|
-
emit IdentityAttestedDetailed(user, msg.sender, newAttestationId, block.timestamp);
|
|
172
|
+
) internal {
|
|
173
|
+
euint8 encByo = FHE.fromExternal(encBirthYearOffset, inputProof);
|
|
174
|
+
euint16 encCc = FHE.fromExternal(encCountryCode, inputProof);
|
|
175
|
+
euint8 encCl = FHE.fromExternal(encComplianceLevel, inputProof);
|
|
176
|
+
ebool encBl = FHE.fromExternal(encIsBlacklisted, inputProof);
|
|
177
|
+
|
|
178
|
+
birthYearOffsets[msg.sender] = encByo;
|
|
179
|
+
countryCodes[msg.sender] = encCc;
|
|
180
|
+
complianceLevels[msg.sender] = encCl;
|
|
181
|
+
blacklistStatuses[msg.sender] = encBl;
|
|
182
|
+
|
|
183
|
+
FHE.allowThis(encByo);
|
|
184
|
+
FHE.allowThis(encCc);
|
|
185
|
+
FHE.allowThis(encCl);
|
|
186
|
+
FHE.allowThis(encBl);
|
|
187
|
+
FHE.allow(encByo, msg.sender);
|
|
188
|
+
FHE.allow(encCc, msg.sender);
|
|
189
|
+
FHE.allow(encCl, msg.sender);
|
|
190
|
+
FHE.allow(encBl, msg.sender);
|
|
162
191
|
}
|
|
163
192
|
|
|
193
|
+
// ============ Bidirectional Revocation ============
|
|
194
|
+
|
|
164
195
|
/// @inheritdoc IIdentityRegistry
|
|
165
|
-
function revokeIdentity(
|
|
166
|
-
|
|
167
|
-
|
|
196
|
+
function revokeIdentity() external {
|
|
197
|
+
_revokeIdentity(msg.sender);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// @inheritdoc IIdentityRegistry
|
|
201
|
+
function revokeIdentityFor(address user) external onlyRegistrar {
|
|
202
|
+
_revokeIdentity(user);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function _revokeIdentity(address user) internal {
|
|
206
|
+
if (currentAttestationId[user] == 0) revert NotAttested();
|
|
168
207
|
|
|
169
|
-
//
|
|
208
|
+
// Zero out encrypted values (new ciphertext handles)
|
|
170
209
|
birthYearOffsets[user] = FHE.asEuint8(0);
|
|
171
210
|
countryCodes[user] = FHE.asEuint16(0);
|
|
172
211
|
complianceLevels[user] = FHE.asEuint8(0);
|
|
173
|
-
|
|
174
|
-
attestationTimestamp[user] = 0;
|
|
175
|
-
currentAttestationId[user] = 0;
|
|
212
|
+
blacklistStatuses[user] = FHE.asEbool(false);
|
|
176
213
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
214
|
+
// Clear metadata
|
|
215
|
+
currentAttestationId[user] = 0;
|
|
216
|
+
attestationTimestamp[user] = 0;
|
|
217
|
+
proofSetHashes[user] = bytes32(0);
|
|
218
|
+
policyVersions[user] = 0;
|
|
181
219
|
|
|
182
220
|
emit IdentityRevoked(user);
|
|
183
|
-
emit IdentityRevokedDetailed(user, msg.sender, attestationId, block.timestamp);
|
|
184
221
|
}
|
|
185
222
|
|
|
186
|
-
// ============
|
|
223
|
+
// ============ Per-Attribute Access Grants ============
|
|
187
224
|
|
|
188
225
|
/// @inheritdoc IIdentityRegistry
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
226
|
+
function grantAttributeAccess(
|
|
227
|
+
address grantee,
|
|
228
|
+
uint8 attributeMask,
|
|
229
|
+
Purpose purpose
|
|
230
|
+
) external whenAttested(msg.sender) {
|
|
231
|
+
if (grantee == address(0)) revert ZeroAddress();
|
|
232
|
+
|
|
233
|
+
bytes32 grantKey = keccak256(abi.encodePacked(msg.sender, grantee));
|
|
234
|
+
attributeGrants[grantKey] |= attributeMask;
|
|
235
|
+
|
|
236
|
+
// Grant FHE ACL per selected attribute
|
|
237
|
+
if (attributeMask & ATTR_BIRTH_YEAR != 0) {
|
|
238
|
+
FHE.allow(birthYearOffsets[msg.sender], grantee);
|
|
239
|
+
}
|
|
240
|
+
if (attributeMask & ATTR_COUNTRY != 0) {
|
|
241
|
+
FHE.allow(countryCodes[msg.sender], grantee);
|
|
242
|
+
}
|
|
243
|
+
if (attributeMask & ATTR_COMPLIANCE != 0) {
|
|
244
|
+
FHE.allow(complianceLevels[msg.sender], grantee);
|
|
245
|
+
}
|
|
246
|
+
if (attributeMask & ATTR_BLACKLIST != 0) {
|
|
247
|
+
FHE.allow(blacklistStatuses[msg.sender], grantee);
|
|
248
|
+
}
|
|
194
249
|
|
|
195
|
-
|
|
196
|
-
function getCountryCode(address user) external view returns (euint16) {
|
|
197
|
-
if (attestationTimestamp[user] == 0) revert NotAttested();
|
|
198
|
-
if (!FHE.isSenderAllowed(countryCodes[user])) revert AccessProhibited();
|
|
199
|
-
return countryCodes[user];
|
|
250
|
+
emit AttributeAccessGranted(msg.sender, grantee, attributeMask, purpose);
|
|
200
251
|
}
|
|
201
252
|
|
|
202
253
|
/// @inheritdoc IIdentityRegistry
|
|
203
|
-
function
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
254
|
+
function grantAccessTo(address grantee) external whenAttested(msg.sender) {
|
|
255
|
+
if (grantee == address(0)) revert ZeroAddress();
|
|
256
|
+
|
|
257
|
+
bytes32 grantKey = keccak256(abi.encodePacked(msg.sender, grantee));
|
|
258
|
+
attributeGrants[grantKey] = ATTR_ALL;
|
|
259
|
+
|
|
260
|
+
FHE.allow(birthYearOffsets[msg.sender], grantee);
|
|
261
|
+
FHE.allow(countryCodes[msg.sender], grantee);
|
|
262
|
+
FHE.allow(complianceLevels[msg.sender], grantee);
|
|
263
|
+
FHE.allow(blacklistStatuses[msg.sender], grantee);
|
|
264
|
+
|
|
265
|
+
emit AttributeAccessGranted(msg.sender, grantee, ATTR_ALL, Purpose.COMPLIANCE_CHECK);
|
|
207
266
|
}
|
|
208
267
|
|
|
268
|
+
// ============ Compliance Checks (x402 Oracle Surface) ============
|
|
269
|
+
|
|
209
270
|
/// @inheritdoc IIdentityRegistry
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
271
|
+
function checkCompliance(
|
|
272
|
+
address user,
|
|
273
|
+
uint8 minLevel
|
|
274
|
+
) external whenAttested(user) returns (ebool) {
|
|
275
|
+
euint8 encMinLevel = FHE.asEuint8(minLevel);
|
|
276
|
+
ebool meetsLevel = FHE.ge(complianceLevels[user], encMinLevel);
|
|
277
|
+
ebool notBlocked = FHE.not(blacklistStatuses[user]);
|
|
278
|
+
ebool result = FHE.and(meetsLevel, notBlocked);
|
|
279
|
+
|
|
280
|
+
// Store result and grant permissions
|
|
281
|
+
bytes32 key = keccak256(abi.encodePacked(user, "compliance", minLevel));
|
|
282
|
+
verificationResults[key] = result;
|
|
283
|
+
FHE.allowThis(result);
|
|
284
|
+
FHE.allow(result, msg.sender);
|
|
215
285
|
|
|
216
|
-
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
217
288
|
|
|
218
289
|
/// @inheritdoc IIdentityRegistry
|
|
219
|
-
function hasMinComplianceLevel(
|
|
220
|
-
|
|
221
|
-
|
|
290
|
+
function hasMinComplianceLevel(
|
|
291
|
+
address user,
|
|
292
|
+
uint8 minLevel
|
|
293
|
+
) external whenAttested(user) returns (ebool) {
|
|
222
294
|
ebool result = FHE.ge(complianceLevels[user], FHE.asEuint8(minLevel));
|
|
223
295
|
|
|
224
|
-
|
|
225
|
-
bytes32 key = keccak256(abi.encodePacked(user, uint8(0), uint256(minLevel)));
|
|
296
|
+
bytes32 key = keccak256(abi.encodePacked(user, "minLevel", minLevel));
|
|
226
297
|
verificationResults[key] = result;
|
|
227
|
-
|
|
228
|
-
// Grant caller permission to decrypt the result
|
|
229
298
|
FHE.allowThis(result);
|
|
230
299
|
FHE.allow(result, msg.sender);
|
|
231
300
|
|
|
@@ -233,16 +302,14 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
|
|
|
233
302
|
}
|
|
234
303
|
|
|
235
304
|
/// @inheritdoc IIdentityRegistry
|
|
236
|
-
function isFromCountry(
|
|
237
|
-
|
|
238
|
-
|
|
305
|
+
function isFromCountry(
|
|
306
|
+
address user,
|
|
307
|
+
uint16 country
|
|
308
|
+
) external whenAttested(user) returns (ebool) {
|
|
239
309
|
ebool result = FHE.eq(countryCodes[user], FHE.asEuint16(country));
|
|
240
310
|
|
|
241
|
-
|
|
242
|
-
bytes32 key = keccak256(abi.encodePacked(user, uint8(1), uint256(country)));
|
|
311
|
+
bytes32 key = keccak256(abi.encodePacked(user, "country", country));
|
|
243
312
|
verificationResults[key] = result;
|
|
244
|
-
|
|
245
|
-
// Grant caller permission to decrypt the result
|
|
246
313
|
FHE.allowThis(result);
|
|
247
314
|
FHE.allow(result, msg.sender);
|
|
248
315
|
|
|
@@ -250,103 +317,84 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
|
|
|
250
317
|
}
|
|
251
318
|
|
|
252
319
|
/// @inheritdoc IIdentityRegistry
|
|
253
|
-
function isNotBlacklisted(address user) external returns (ebool) {
|
|
254
|
-
|
|
255
|
-
if (!FHE.isSenderAllowed(isBlacklisted[user])) revert AccessProhibited();
|
|
256
|
-
ebool result = FHE.not(isBlacklisted[user]);
|
|
320
|
+
function isNotBlacklisted(address user) external whenAttested(user) returns (ebool) {
|
|
321
|
+
ebool result = FHE.not(blacklistStatuses[user]);
|
|
257
322
|
|
|
258
|
-
|
|
259
|
-
bytes32 key = keccak256(abi.encodePacked(user, uint8(2), uint256(0)));
|
|
323
|
+
bytes32 key = keccak256(abi.encodePacked(user, "notBlacklisted"));
|
|
260
324
|
verificationResults[key] = result;
|
|
261
|
-
|
|
262
|
-
// Grant caller permission to decrypt the result
|
|
263
325
|
FHE.allowThis(result);
|
|
264
326
|
FHE.allow(result, msg.sender);
|
|
265
327
|
|
|
266
328
|
return result;
|
|
267
329
|
}
|
|
268
330
|
|
|
269
|
-
// ============
|
|
331
|
+
// ============ Encrypted Attribute Getters ============
|
|
270
332
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
* @return Encrypted boolean result
|
|
276
|
-
*/
|
|
277
|
-
function getComplianceLevelResult(address user, uint8 minLevel) external view returns (ebool) {
|
|
278
|
-
bytes32 key = keccak256(abi.encodePacked(user, uint8(0), uint256(minLevel)));
|
|
279
|
-
ebool result = verificationResults[key];
|
|
280
|
-
if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
|
|
281
|
-
return result;
|
|
333
|
+
/// @inheritdoc IIdentityRegistry
|
|
334
|
+
function getBirthYearOffset(address user) external view whenAttested(user) returns (euint8) {
|
|
335
|
+
if (!FHE.isSenderAllowed(birthYearOffsets[user])) revert AccessProhibited();
|
|
336
|
+
return birthYearOffsets[user];
|
|
282
337
|
}
|
|
283
338
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
* @return Encrypted boolean result
|
|
289
|
-
*/
|
|
290
|
-
function getCountryResult(address user, uint16 country) external view returns (ebool) {
|
|
291
|
-
bytes32 key = keccak256(abi.encodePacked(user, uint8(1), uint256(country)));
|
|
292
|
-
ebool result = verificationResults[key];
|
|
293
|
-
if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
|
|
294
|
-
return result;
|
|
339
|
+
/// @inheritdoc IIdentityRegistry
|
|
340
|
+
function getCountryCode(address user) external view whenAttested(user) returns (euint16) {
|
|
341
|
+
if (!FHE.isSenderAllowed(countryCodes[user])) revert AccessProhibited();
|
|
342
|
+
return countryCodes[user];
|
|
295
343
|
}
|
|
296
344
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
*/
|
|
302
|
-
function getBlacklistResult(address user) external view returns (ebool) {
|
|
303
|
-
bytes32 key = keccak256(abi.encodePacked(user, uint8(2), uint256(0)));
|
|
304
|
-
ebool result = verificationResults[key];
|
|
305
|
-
if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
|
|
306
|
-
return result;
|
|
345
|
+
/// @inheritdoc IIdentityRegistry
|
|
346
|
+
function getComplianceLevel(address user) external view whenAttested(user) returns (euint8) {
|
|
347
|
+
if (!FHE.isSenderAllowed(complianceLevels[user])) revert AccessProhibited();
|
|
348
|
+
return complianceLevels[user];
|
|
307
349
|
}
|
|
308
350
|
|
|
309
|
-
// ============ Access Control ============
|
|
310
|
-
|
|
311
351
|
/// @inheritdoc IIdentityRegistry
|
|
312
|
-
function
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
emit OwnershipTransferStarted(owner, newOwner);
|
|
352
|
+
function getBlacklistStatus(address user) external view whenAttested(user) returns (ebool) {
|
|
353
|
+
if (!FHE.isSenderAllowed(blacklistStatuses[user])) revert AccessProhibited();
|
|
354
|
+
return blacklistStatuses[user];
|
|
316
355
|
}
|
|
317
356
|
|
|
357
|
+
// ============ Metadata Getters ============
|
|
358
|
+
|
|
318
359
|
/// @inheritdoc IIdentityRegistry
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
address previousOwner = owner;
|
|
322
|
-
owner = pendingOwner;
|
|
323
|
-
pendingOwner = address(0);
|
|
324
|
-
emit OwnershipTransferred(previousOwner, owner);
|
|
360
|
+
function isAttested(address user) external view returns (bool) {
|
|
361
|
+
return currentAttestationId[user] != 0;
|
|
325
362
|
}
|
|
326
363
|
|
|
327
364
|
/// @inheritdoc IIdentityRegistry
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
FHE.allow(birthYearOffsets[msg.sender], grantee);
|
|
332
|
-
FHE.allow(countryCodes[msg.sender], grantee);
|
|
333
|
-
FHE.allow(complianceLevels[msg.sender], grantee);
|
|
334
|
-
FHE.allow(isBlacklisted[msg.sender], grantee);
|
|
365
|
+
function getProofSetHash(address user) external view returns (bytes32) {
|
|
366
|
+
return proofSetHashes[user];
|
|
367
|
+
}
|
|
335
368
|
|
|
336
|
-
|
|
369
|
+
/// @inheritdoc IIdentityRegistry
|
|
370
|
+
function getPolicyVersion(address user) external view returns (uint32) {
|
|
371
|
+
return policyVersions[user];
|
|
337
372
|
}
|
|
338
373
|
|
|
339
374
|
/// @inheritdoc IIdentityRegistry
|
|
340
|
-
function
|
|
341
|
-
return
|
|
375
|
+
function getGrantedAttributes(address user, address grantee) external view returns (uint8) {
|
|
376
|
+
return attributeGrants[keccak256(abi.encodePacked(user, grantee))];
|
|
342
377
|
}
|
|
343
378
|
|
|
344
379
|
/// @inheritdoc IIdentityRegistry
|
|
345
|
-
function
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
380
|
+
function getVerificationResult(bytes32 key) external view returns (ebool) {
|
|
381
|
+
ebool result = verificationResults[key];
|
|
382
|
+
if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ============ Admin ============
|
|
387
|
+
|
|
388
|
+
/// @notice Add or remove a registrar
|
|
389
|
+
/// @param registrar Address to update
|
|
390
|
+
/// @param status true to add, false to remove
|
|
391
|
+
function setRegistrar(address registrar, bool status) external onlyOwner {
|
|
392
|
+
if (registrar == address(0)) revert ZeroAddress();
|
|
393
|
+
registrars[registrar] = status;
|
|
394
|
+
emit RegistrarUpdated(registrar, status);
|
|
351
395
|
}
|
|
396
|
+
|
|
397
|
+
// ============ UUPS ============
|
|
398
|
+
|
|
399
|
+
function _authorizeUpgrade(address) internal override onlyOwner {}
|
|
352
400
|
}
|