Skip to content

Commit c42610a

Browse files
authored
Version 2.2.0
* ✨(Identity) Add isClaimValid for self-attested claims * 🔖(2.2.0) Update version and CHANGELOG * 🔖(2.2.0) Update version and CHANGELOG * ✨(factory) Add getter for authority
1 parent 3f3b07a commit c42610a

13 files changed

+119
-50
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.2.0]
8+
9+
### Added
10+
- Identities are now required to implement the standardized `function isClaimValid(IIdentity _identity, uint256
11+
claimTopic, bytes calldata sig, bytes calldata data) external view returns (bool)`, used for self-attested claims
12+
(`_identity` is the address of the Identity contract).
13+
- Implemented the `isClaimValid` function in the `Identity` contract.
14+
- IdFactory now implements the `implementationAuthority()` getter.
15+
716
## [2.1.0]
817

918
### Added

contracts/ClaimIssuer.sol

+1-35
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ contract ClaimIssuer is IClaimIssuer, Identity {
4848
uint256 claimTopic,
4949
bytes memory sig,
5050
bytes memory data)
51-
external override view returns (bool claimValid)
51+
public override(Identity, IClaimIssuer) view returns (bool claimValid)
5252
{
5353
bytes32 dataHash = keccak256(abi.encode(_identity, claimTopic, data));
5454
// Use abi.encodePacked to concatenate the message prefix and the message to sign.
@@ -79,38 +79,4 @@ contract ClaimIssuer is IClaimIssuer, Identity {
7979

8080
return false;
8181
}
82-
83-
/**
84-
* @dev See {IClaimIssuer-getRecoveredAddress}.
85-
*/
86-
function getRecoveredAddress(bytes memory sig, bytes32 dataHash)
87-
public override
88-
pure
89-
returns (address addr)
90-
{
91-
bytes32 ra;
92-
bytes32 sa;
93-
uint8 va;
94-
95-
// Check the signature length
96-
if (sig.length != 65) {
97-
return address(0);
98-
}
99-
100-
// Divide the signature in r, s and v variables
101-
// solhint-disable-next-line no-inline-assembly
102-
assembly {
103-
ra := mload(add(sig, 32))
104-
sa := mload(add(sig, 64))
105-
va := byte(0, mload(add(sig, 96)))
106-
}
107-
108-
if (va < 27) {
109-
va += 27;
110-
}
111-
112-
address recoveredAddress = ecrecover(dataHash, va, ra, sa);
113-
114-
return (recoveredAddress);
115-
}
11682
}

contracts/Identity.sol

+73
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,79 @@ contract Identity is Storage, IIdentity, Version {
492492
return false;
493493
}
494494

495+
/**
496+
* @dev Checks if a claim is valid. Claims issued by the identity are self-attested claims. They do not have a
497+
* built-in revocation mechanism and are considered valid as long as their signature is valid and they are still
498+
* stored by the identity contract.
499+
* @param _identity the identity contract related to the claim
500+
* @param claimTopic the claim topic of the claim
501+
* @param sig the signature of the claim
502+
* @param data the data field of the claim
503+
* @return claimValid true if the claim is valid, false otherwise
504+
*/
505+
function isClaimValid(
506+
IIdentity _identity,
507+
uint256 claimTopic,
508+
bytes memory sig,
509+
bytes memory data)
510+
public override virtual view returns (bool claimValid)
511+
{
512+
bytes32 dataHash = keccak256(abi.encode(_identity, claimTopic, data));
513+
// Use abi.encodePacked to concatenate the message prefix and the message to sign.
514+
bytes32 prefixedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash));
515+
516+
// Recover address of data signer
517+
address recovered = getRecoveredAddress(sig, prefixedHash);
518+
519+
// Take hash of recovered address
520+
bytes32 hashedAddr = keccak256(abi.encode(recovered));
521+
522+
// Does the trusted identifier have they key which signed the user's claim?
523+
// && (isClaimRevoked(_claimId) == false)
524+
if (keyHasPurpose(hashedAddr, 3)) {
525+
return true;
526+
}
527+
528+
return false;
529+
}
530+
531+
/**
532+
* @dev returns the address that signed the given data
533+
* @param sig the signature of the data
534+
* @param dataHash the data that was signed
535+
* returns the address that signed dataHash and created the signature sig
536+
*/
537+
function getRecoveredAddress(bytes memory sig, bytes32 dataHash)
538+
public
539+
pure
540+
returns (address addr)
541+
{
542+
bytes32 ra;
543+
bytes32 sa;
544+
uint8 va;
545+
546+
// Check the signature length
547+
if (sig.length != 65) {
548+
return address(0);
549+
}
550+
551+
// Divide the signature in r, s and v variables
552+
// solhint-disable-next-line no-inline-assembly
553+
assembly {
554+
ra := mload(add(sig, 32))
555+
sa := mload(add(sig, 64))
556+
va := byte(0, mload(add(sig, 96)))
557+
}
558+
559+
if (va < 27) {
560+
va += 27;
561+
}
562+
563+
address recoveredAddress = ecrecover(dataHash, va, ra, sa);
564+
565+
return (recoveredAddress);
566+
}
567+
495568
/**
496569
* @notice Initializer internal function for the Identity contract.
497570
*

contracts/factory/IIdFactory.sol

+5
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,9 @@ interface IIdFactory {
139139
* @param _salt the salt used for deployment
140140
*/
141141
function isSaltTaken(string calldata _salt) external view returns (bool);
142+
143+
/**
144+
* @dev getter for the implementation authority used by this factory.
145+
*/
146+
function implementationAuthority() external view returns (address);
142147
}

contracts/factory/IdFactory.sol

+7
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,13 @@ contract IdFactory is IIdFactory, Ownable {
215215
return _tokenFactories[_factory];
216216
}
217217

218+
/**
219+
* @dev See {IdFactory-implementationAuthority}.
220+
*/
221+
function implementationAuthority() public override view returns (address) {
222+
return _implementationAuthority;
223+
}
224+
218225
// deploy function with create2 opcode call
219226
// returns the address of the contract created
220227
function _deploy(string memory salt, bytes memory bytecode) private returns (address) {

contracts/interface/IClaimIssuer.sol

-8
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,4 @@ interface IClaimIssuer is IIdentity {
4848
bytes calldata sig,
4949
bytes calldata data)
5050
external view returns (bool);
51-
52-
/**
53-
* @dev returns the address that signed the given data
54-
* @param sig the signature of the data
55-
* @param dataHash the data that was signed
56-
* returns the address that signed dataHash and created the signature sig
57-
*/
58-
function getRecoveredAddress(bytes calldata sig, bytes32 dataHash) external pure returns (address);
5951
}

contracts/interface/IIdentity.sol

+16-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,19 @@ import "./IERC734.sol";
55
import "./IERC735.sol";
66

77
// solhint-disable-next-line no-empty-blocks
8-
interface IIdentity is IERC734, IERC735 {}
8+
interface IIdentity is IERC734, IERC735 {
9+
/**
10+
* @dev Checks if a claim is valid.
11+
* @param _identity the identity contract related to the claim
12+
* @param claimTopic the claim topic of the claim
13+
* @param sig the signature of the claim
14+
* @param data the data field of the claim
15+
* @return claimValid true if the claim is valid, false otherwise
16+
*/
17+
function isClaimValid(
18+
IIdentity _identity,
19+
uint256 claimTopic,
20+
bytes calldata sig,
21+
bytes calldata data)
22+
external view returns (bool);
23+
}

contracts/version/Version.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract Version {
1010
* @dev Returns the string of the current version.
1111
*/
1212
function version() external pure returns (string memory) {
13-
// version 2.0.1
14-
return "2.0.1";
13+
// version 2.2.0
14+
return "2.2.0";
1515
}
1616
}

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@onchain-id/solidity",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "EVM solidity smart contracts for Blockchain OnchainID identities.",
55
"files": [
66
"artifacts",

test/gateway/gateway.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ describe('Gateway', () => {
824824

825825
const tx = await gateway.connect(deployerWallet).callFactory(new ethers.utils.Interface(['function addTokenFactory(address)']).encodeFunctionData('addTokenFactory', [bobWallet.address]));
826826

827-
expect(tx).to.emit(gateway, "TokenFactoryAdded").withArgs(bobWallet.address);
827+
expect(tx).to.emit(identityFactory, "TokenFactoryAdded").withArgs(bobWallet.address);
828828
});
829829
});
830830
});

test/identities/claims.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe('Identity', () => {
2525

2626
const tx = await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
2727
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
28+
await expect(aliceIdentity.isClaimValid(claim.identity, claim.topic, claim.signature, claim.data)).to.eventually.equal(false);
2829
});
2930
});
3031

@@ -64,6 +65,7 @@ describe('Identity', () => {
6465
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
6566
await expect(tx).to.emit(aliceIdentity, 'Approved');
6667
await expect(tx).to.emit(aliceIdentity, 'Executed');
68+
await expect(aliceIdentity.isClaimValid(claim.identity, claim.topic, claim.signature, claim.data)).to.eventually.equal(true);
6769
});
6870
});
6971

test/identities/init.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ describe('Identity', () => {
3636
it('should return the version of the implementation', async () => {
3737
const {identityImplementation} = await loadFixture(deployIdentityFixture);
3838

39-
expect(await identityImplementation.version()).to.equal('2.0.1');
39+
expect(await identityImplementation.version()).to.equal('2.2.0');
4040
});
4141
});

0 commit comments

Comments
 (0)