Files

284 lines
12 KiB
Solidity

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../interface/IClaimIssuer.sol";
contract Verifier is Ownable {
/// @dev All topics of claims required to pass verification.
uint256[] public requiredClaimTopics;
/// @dev Array containing all TrustedIssuers identity contract address allowed to issue claims required.
IClaimIssuer[] public trustedIssuers;
/// @dev Mapping between a trusted issuer address and the topics of claims they are trusted for.
mapping(address => uint256[]) public trustedIssuerClaimTopics;
/// @dev Mapping between a claim topic and the trusted issuers trusted for it.
mapping(uint256 => IClaimIssuer[]) public claimTopicsToTrustedIssuers;
/**
* this event is emitted when a claim topic has been added to the requirement list
* the event is emitted by the 'addClaimTopic' function
* `claimTopic` is the required claim topic added
*/
event ClaimTopicAdded(uint256 indexed claimTopic);
/**
* this event is emitted when a claim topic has been removed from the requirement list
* the event is emitted by the 'removeClaimTopic' function
* `claimTopic` is the required claim removed
*/
event ClaimTopicRemoved(uint256 indexed claimTopic);
/**
* this event is emitted when an issuer is added to the trusted list.
* the event is emitted by the addTrustedIssuer function
* `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract
* `claimTopics` is the set of claims that the trusted issuer is allowed to emit
*/
event TrustedIssuerAdded(IClaimIssuer indexed trustedIssuer, uint256[] claimTopics);
/**
* this event is emitted when an issuer is removed from the trusted list.
* the event is emitted by the removeTrustedIssuer function
* `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract
*/
event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer);
/**
* this event is emitted when the set of claim topics is changed for a given trusted issuer.
* the event is emitted by the updateIssuerClaimTopics function
* `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract
* `claimTopics` is the set of claims that the trusted issuer is allowed to emit
*/
event ClaimTopicsUpdated(IClaimIssuer indexed trustedIssuer, uint256[] claimTopics);
modifier onlyVerifiedSender() {
require(verify(_msgSender()), "sender is not verified");
_;
}
/**
* @dev See {IClaimTopicsRegistry-removeClaimTopic}.
*/
function addClaimTopic(uint256 claimTopic) public onlyOwner {
uint256 length = requiredClaimTopics.length;
require(length < 15, "cannot require more than 15 topics");
for (uint256 i = 0; i < length; i++) {
require(requiredClaimTopics[i] != claimTopic, "claimTopic already exists");
}
requiredClaimTopics.push(claimTopic);
emit ClaimTopicAdded(claimTopic);
}
/**
* @dev See {IClaimTopicsRegistry-getClaimTopics}.
*/
function removeClaimTopic(uint256 claimTopic) public onlyOwner {
uint256 length = requiredClaimTopics.length;
for (uint256 i = 0; i < length; i++) {
if (requiredClaimTopics[i] == claimTopic) {
requiredClaimTopics[i] = requiredClaimTopics[length - 1];
requiredClaimTopics.pop();
emit ClaimTopicRemoved(claimTopic);
break;
}
}
}
/**
* @dev See {ITrustedIssuersRegistry-addTrustedIssuer}.
*/
function addTrustedIssuer(IClaimIssuer trustedIssuer, uint256[] calldata claimTopics) public onlyOwner {
require(address(trustedIssuer) != address(0), "invalid argument - zero address");
require(trustedIssuerClaimTopics[address(trustedIssuer)].length == 0, "trusted Issuer already exists");
require(claimTopics.length > 0, "trusted claim topics cannot be empty");
require(claimTopics.length <= 15, "cannot have more than 15 claim topics");
require(trustedIssuers.length < 50, "cannot have more than 50 trusted issuers");
trustedIssuers.push(trustedIssuer);
trustedIssuerClaimTopics[address(trustedIssuer)] = claimTopics;
for (uint256 i = 0; i < claimTopics.length; i++) {
claimTopicsToTrustedIssuers[claimTopics[i]].push(trustedIssuer);
}
emit TrustedIssuerAdded(trustedIssuer, claimTopics);
}
/**
* @dev See {ITrustedIssuersRegistry-removeTrustedIssuer}.
*/
function removeTrustedIssuer(IClaimIssuer trustedIssuer) public onlyOwner {
require(address(trustedIssuer) != address(0), "invalid argument - zero address");
require(trustedIssuerClaimTopics[address(trustedIssuer)].length != 0, "NOT a trusted issuer");
uint256 length = trustedIssuers.length;
for (uint256 i = 0; i < length; i++) {
if (trustedIssuers[i] == trustedIssuer) {
trustedIssuers[i] = trustedIssuers[length - 1];
trustedIssuers.pop();
break;
}
}
for (
uint256 claimTopicIndex = 0;
claimTopicIndex < trustedIssuerClaimTopics[address(trustedIssuer)].length;
claimTopicIndex++) {
uint256 claimTopic = trustedIssuerClaimTopics[address(trustedIssuer)][claimTopicIndex];
uint256 topicsLength = claimTopicsToTrustedIssuers[claimTopic].length;
for (uint256 i = 0; i < topicsLength; i++) {
if (claimTopicsToTrustedIssuers[claimTopic][i] == trustedIssuer) {
claimTopicsToTrustedIssuers[claimTopic][i] =
claimTopicsToTrustedIssuers[claimTopic][topicsLength - 1];
claimTopicsToTrustedIssuers[claimTopic].pop();
break;
}
}
}
delete trustedIssuerClaimTopics[address(trustedIssuer)];
emit TrustedIssuerRemoved(trustedIssuer);
}
/**
* @dev See {ITrustedIssuersRegistry-updateIssuerClaimTopics}.
*/
function updateIssuerClaimTopics(IClaimIssuer trustedIssuer, uint256[] calldata newClaimTopics) public onlyOwner {
require(address(trustedIssuer) != address(0), "invalid argument - zero address");
require(trustedIssuerClaimTopics[address(trustedIssuer)].length != 0, "NOT a trusted issuer");
require(newClaimTopics.length <= 15, "cannot have more than 15 claim topics");
require(newClaimTopics.length > 0, "claim topics cannot be empty");
for (uint256 i = 0; i < trustedIssuerClaimTopics[address(trustedIssuer)].length; i++) {
uint256 claimTopic = trustedIssuerClaimTopics[address(trustedIssuer)][i];
uint256 topicsLength = claimTopicsToTrustedIssuers[claimTopic].length;
for (uint256 j = 0; j < topicsLength; j++) {
if (claimTopicsToTrustedIssuers[claimTopic][j] == trustedIssuer) {
claimTopicsToTrustedIssuers[claimTopic][j] =
claimTopicsToTrustedIssuers[claimTopic][topicsLength - 1];
claimTopicsToTrustedIssuers[claimTopic].pop();
break;
}
}
}
trustedIssuerClaimTopics[address(trustedIssuer)] = newClaimTopics;
for (uint256 i = 0; i < newClaimTopics.length; i++) {
claimTopicsToTrustedIssuers[newClaimTopics[i]].push(trustedIssuer);
}
emit ClaimTopicsUpdated(trustedIssuer, newClaimTopics);
}
/**
* @dev See {ITrustedIssuersRegistry-getTrustedIssuers}.
*/
function getTrustedIssuers() public view returns (IClaimIssuer[] memory) {
return trustedIssuers;
}
/**
* @dev See {ITrustedIssuersRegistry-getTrustedIssuersForClaimTopic}.
*/
function getTrustedIssuersForClaimTopic(uint256 claimTopic) public view returns (IClaimIssuer[] memory) {
return claimTopicsToTrustedIssuers[claimTopic];
}
/**
* @dev See {ITrustedIssuersRegistry-isTrustedIssuer}.
*/
function isTrustedIssuer(address issuer) public view returns (bool) {
if(trustedIssuerClaimTopics[issuer].length > 0) {
return true;
}
return false;
}
/**
* @dev See {ITrustedIssuersRegistry-getTrustedIssuerClaimTopics}.
*/
function getTrustedIssuerClaimTopics(IClaimIssuer trustedIssuer) public view returns (uint256[] memory) {
require(trustedIssuerClaimTopics[address(trustedIssuer)].length != 0, "trusted Issuer doesn\'t exist");
return trustedIssuerClaimTopics[address(trustedIssuer)];
}
/**
* @dev See {ITrustedIssuersRegistry-hasClaimTopic}.
*/
function hasClaimTopic(address issuer, uint256 claimTopic) public view returns (bool) {
uint256[] memory claimTopics = trustedIssuerClaimTopics[issuer];
uint256 length = claimTopics.length;
for (uint256 i = 0; i < length; i++) {
if (claimTopics[i] == claimTopic) {
return true;
}
}
return false;
}
function isClaimTopicRequired(uint256 claimTopic) public view returns (bool) {
uint256 length = requiredClaimTopics.length;
for (uint256 i = 0; i < length; i++) {
if (requiredClaimTopics[i] == claimTopic) {
return true;
}
}
return false;
}
/**
* @dev Verify an identity (ONCHAINID) by checking if the identity has at least one valid claim from a trusted
* issuer for each required claim topic. Returns true if the identity is compliant, false otherwise.
*/
function verify(address identity) public view returns(bool isVerified) {
if (requiredClaimTopics.length == 0) {
return true;
}
uint256 foundClaimTopic;
uint256 scheme;
address issuer;
bytes memory sig;
bytes memory data;
uint256 claimTopic;
for (claimTopic = 0; claimTopic < requiredClaimTopics.length; claimTopic++) {
IClaimIssuer[] memory trustedIssuersForClaimTopic =
this.getTrustedIssuersForClaimTopic(requiredClaimTopics[claimTopic]);
if (trustedIssuersForClaimTopic.length == 0) {
return false;
}
bytes32[] memory claimIds = new bytes32[](trustedIssuersForClaimTopic.length);
for (uint256 i = 0; i < trustedIssuersForClaimTopic.length; i++) {
claimIds[i] = keccak256(abi.encode(trustedIssuersForClaimTopic[i], requiredClaimTopics[claimTopic]));
}
for (uint256 j = 0; j < claimIds.length; j++) {
(foundClaimTopic, scheme, issuer, sig, data, ) = IIdentity(identity).getClaim(claimIds[j]);
if (foundClaimTopic == requiredClaimTopics[claimTopic]) {
try IClaimIssuer(issuer).isClaimValid(IIdentity(identity), requiredClaimTopics[claimTopic], sig,
data) returns(bool _validity) {
if (
_validity
) {
j = claimIds.length;
}
if (!_validity && j == (claimIds.length - 1)) {
return false;
}
} catch {
if (j == (claimIds.length - 1)) {
return false;
}
}
} else if (j == (claimIds.length - 1)) {
return false;
}
}
}
return true;
}
}