Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add claim issuer upgradeable #97

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.2.2]

### Added
- Implemented a new contract `ClaimIssuerUpgradeable`, the ERC-1967 and ERC-1822 compliant upgradeable variant of ClaimIssuer.
- Implemented a new contract `ClaimIssuerProxy`, an ERC-1967 compliant proxy contract for upgradable claim issuers.

## [2.2.1]

### Changed
Expand Down
89 changes: 89 additions & 0 deletions contracts/ClaimIssuerUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "./interface/IClaimIssuer.sol";
import "./Identity.sol";

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract ClaimIssuerUpgradeable is IClaimIssuer, Identity, UUPSUpgradeable {
mapping (bytes => bool) public revokedClaims;

// solhint-disable-next-line no-empty-blocks
constructor(address initialManagementKey, bool _isLibrary) Identity(initialManagementKey, _isLibrary) { }

/**
* @dev See {IClaimIssuer-revokeClaimBySignature}.
*/
function revokeClaimBySignature(bytes calldata signature) external override delegatedOnly onlyManager {
require(!revokedClaims[signature], "Conflict: Claim already revoked");

revokedClaims[signature] = true;

emit ClaimRevoked(signature);
}

/**
* @dev See {IClaimIssuer-revokeClaim}.
*/
function revokeClaim(bytes32 _claimId, address _identity) external override delegatedOnly onlyManager returns(bool) {
uint256 foundClaimTopic;
uint256 scheme;
address issuer;
bytes memory sig;
bytes memory data;

( foundClaimTopic, scheme, issuer, sig, data, ) = Identity(_identity).getClaim(_claimId);

require(!revokedClaims[sig], "Conflict: Claim already revoked");

revokedClaims[sig] = true;
emit ClaimRevoked(sig);
return true;
}

/**
* @dev See {IClaimIssuer-isClaimValid}.
*/
function isClaimValid(
IIdentity _identity,
uint256 claimTopic,
bytes memory sig,
bytes memory data)
public override(Identity, IClaimIssuer) view returns (bool claimValid)
{
bytes32 dataHash = keccak256(abi.encode(_identity, claimTopic, data));
// Use abi.encodePacked to concatenate the message prefix and the message to sign.
bytes32 prefixedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash));

// Recover address of data signer
address recovered = getRecoveredAddress(sig, prefixedHash);

// Take hash of recovered address
bytes32 hashedAddr = keccak256(abi.encode(recovered));

// Does the trusted identifier have they key which signed the user's claim?
// && (isClaimRevoked(_claimId) == false)
if (keyHasPurpose(hashedAddr, 3) && (isClaimRevoked(sig) == false)) {
return true;
}

return false;
}

/**
* @dev See {IClaimIssuer-isClaimRevoked}.
*/
function isClaimRevoked(bytes memory _sig) public override view returns (bool) {
if (revokedClaims[_sig]) {
return true;
}

return false;
}

// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address /*newImplementation*/) internal override virtual {
require(keyHasPurpose(keccak256(abi.encode(msg.sender)), 42), "Caller is not authorized to upgrade");
}
}
94 changes: 94 additions & 0 deletions contracts/_testContracts/TestUpgradedClaimIssuer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "../Identity.sol";
import "../interface/IClaimIssuer.sol";

contract TestUpgradedClaimIssuer is IClaimIssuer, Identity, UUPSUpgradeable {
mapping (bytes => bool) public revokedClaims;

uint256 public newField;

// solhint-disable-next-line no-empty-blocks
constructor(address initialManagementKey, bool _isLibrary) Identity(initialManagementKey, _isLibrary) { }

function setNewField(uint256 _newField) external onlyManager {
newField = _newField;
}

/**
* @dev See {IClaimIssuer-revokeClaimBySignature}.
*/
function revokeClaimBySignature(bytes calldata signature) external override delegatedOnly onlyManager {
require(!revokedClaims[signature], "Conflict: Claim already revoked");

revokedClaims[signature] = true;

emit ClaimRevoked(signature);
}

/**
* @dev See {IClaimIssuer-revokeClaim}.
*/
function revokeClaim(bytes32 _claimId, address _identity) external override delegatedOnly onlyManager returns(bool) {
uint256 foundClaimTopic;
uint256 scheme;
address issuer;
bytes memory sig;
bytes memory data;

( foundClaimTopic, scheme, issuer, sig, data, ) = Identity(_identity).getClaim(_claimId);

require(!revokedClaims[sig], "Conflict: Claim already revoked");

revokedClaims[sig] = true;
emit ClaimRevoked(sig);
return true;
}

/**
* @dev See {IClaimIssuer-isClaimValid}.
*/
function isClaimValid(
IIdentity _identity,
uint256 claimTopic,
bytes memory sig,
bytes memory data)
public override(Identity, IClaimIssuer) view returns (bool claimValid)
{
bytes32 dataHash = keccak256(abi.encode(_identity, claimTopic, data));
// Use abi.encodePacked to concatenate the message prefix and the message to sign.
bytes32 prefixedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash));

// Recover address of data signer
address recovered = getRecoveredAddress(sig, prefixedHash);

// Take hash of recovered address
bytes32 hashedAddr = keccak256(abi.encode(recovered));

// Does the trusted identifier have they key which signed the user's claim?
// && (isClaimRevoked(_claimId) == false)
if (keyHasPurpose(hashedAddr, 3) && (isClaimRevoked(sig) == false)) {
return true;
}

return false;
}

/**
* @dev See {IClaimIssuer-isClaimRevoked}.
*/
function isClaimRevoked(bytes memory _sig) public override view returns (bool) {
if (revokedClaims[_sig]) {
return true;
}

return false;
}

// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address /*newImplementation*/) internal override virtual {
require(keyHasPurpose(keccak256(abi.encode(msg.sender)), 42), "Caller is not authorized to upgrade");
}
}
9 changes: 9 additions & 0 deletions contracts/proxy/ClaimIssuerProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.17;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract ClaimIssuerProxy is ERC1967Proxy {
// solhint-disable-next-line no-empty-blocks
constructor(address implementation, bytes memory _data) ERC1967Proxy(implementation, _data) { }
}
2 changes: 1 addition & 1 deletion contracts/storage/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ contract Structs {
* Specification: Keys are cryptographic public keys, or contract addresses associated with this identity.
* The structure should be as follows:
* key: A public key owned by this identity
* purposes: uint256[] Array of the key purposes, like 1 = MANAGEMENT, 2 = EXECUTION
* purposes: uint256[] Array of the key purposes, like 1 = MANAGEMENT, 2 = EXECUTION, 42 = UPGRADE
* keyType: The type of key used, which would be a uint256 for different key types. e.g. 1 = ECDSA, 2 = RSA, etc.
* key: bytes32 The public key. // Its the Keccak256 hash of the key
*/
Expand Down
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "@nomicfoundation/hardhat-toolbox";
import { HardhatUserConfig } from "hardhat/config";
import 'solidity-coverage';
import '@openzeppelin/hardhat-upgrades';
import "@nomiclabs/hardhat-solhint";

import "./tasks/add-claim.task";
Expand Down
Loading
Loading