-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathIdentityContractLib.sol
182 lines (149 loc) · 8.51 KB
/
IdentityContractLib.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;
import "./../dependencies/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import "./Commons.sol";
import "./ClaimCommons.sol";
import "./ClaimVerifier.sol";
import "./IdentityContract.sol";
/**
* This library contains functionality that concerns the IdentityContract.
*/
library IdentityContractLib {
// Structs related to ERC-1155
struct PerishableValue {
uint256 value;
uint64 expiryDate;
}
// Structs ERC-735
struct Claim {
uint256 topic;
uint256 scheme;
address issuer; // msg.sender
bytes signature; // this.address + topic + data
bytes data;
string uri;
}
// Events ERC-725 (partially)
event ContractCreated(address indexed contractAddress);
// Events ERC-735 (partially)
event ClaimAdded(uint256 indexed claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimRemoved(uint256 indexed claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimChanged(uint256 indexed claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
function execute(uint256 _operationType, address _to, uint256 _value, bytes memory _data) public {
if(_operationType == 0) {
(bool success, ) = _to.call{value: _value}(_data);
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
return;
}
if(_operationType == 1) {
// Copy calldata to memory so it can easily be accessed via assembly.
bytes memory dataMemory = _data;
address newContract;
assembly {
newContract := create(0, add(dataMemory, 0x20), mload(dataMemory))
}
emit ContractCreated(newContract);
return;
}
require(false, "Unknown _operationType.");
}
function execute(address owner, uint256 _executionNonce, uint256 _operationType, address _to, uint256 _value, bytes calldata _data, bytes calldata _signature) external {
// address(this) needs to be part of the struct so that the tx cannot be replayed to a different IDC owned by the same EOA.
address signer = ECDSA.recover(keccak256(abi.encodePacked(_operationType, _to, _value, _data, address(this), _executionNonce)), _signature);
require(signer == owner, "invalid signature / wrong signer / wrong nonce.");
execute(_operationType, _to, _value, _data);
}
function addClaim(mapping (uint256 => Claim) storage claims, mapping (uint256 => uint256[]) storage topics2ClaimIds,
mapping (uint256 => bool) storage burnedClaimIds, IdentityContract marketAuthority, uint256 _topic, uint256 _scheme, address _issuer,
bytes memory _signature, bytes memory _data, string memory _uri) public returns (uint256 __claimRequestId) {
// Make sure that claim is correct if the topic is in the relevant range.
if(_topic >= 10000 && _topic <= 11000) {
ClaimCommons.ClaimType claimType = ClaimCommons.topic2ClaimType(_topic);
require(ClaimVerifier.validateClaim(marketAuthority, claimType, address(this), _topic, _scheme, _issuer, _signature, _data), "Invalid claim.");
}
__claimRequestId = getClaimId(_issuer, _topic);
// Check for burned claim IDs.
if(burnedClaimIds[__claimRequestId])
require(false, "Claim id burned.");
// Emit and modify before adding to save gas.
if(keccak256(claims[__claimRequestId].signature) != keccak256(new bytes(32))) { // Claim existence check since signature cannot be 0.
emit ClaimAdded(__claimRequestId, _topic, _scheme, _issuer, _signature, _data, _uri);
topics2ClaimIds[_topic].push(__claimRequestId);
} else {
// Make sure that only issuer or holder can change claims
require(msg.sender == address(this) || msg.sender == _issuer, "Only issuer or holder can change claims.");
emit ClaimChanged(__claimRequestId, _topic, _scheme, _issuer, _signature, _data, _uri);
}
claims[__claimRequestId] = Claim(_topic, _scheme, _issuer, _signature, _data, _uri);
}
function removeClaim(address owner, mapping (uint256 => Claim) storage claims, mapping (uint256 => uint256[]) storage topics2ClaimIds,
mapping (uint256 => bool) storage burnedClaimIds, uint256 _claimId) public returns (bool __success) {
require(msg.sender == owner || msg.sender == claims[_claimId].issuer, "Only issuer or holder can remove claims.");
// Emit event and store burned signature before deleting to save gas for copy.
IdentityContractLib.Claim storage claim = claims[_claimId];
emit ClaimRemoved(_claimId, claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
burnedClaimIds[_claimId] = true; // Make sure that this same claim cannot be added again.
// Delete entries of helper directories.
// Locate entry in topics2ClaimIds.
uint32 positionInArray = 0;
while(positionInArray < topics2ClaimIds[claim.topic].length && _claimId != topics2ClaimIds[claim.topic][positionInArray]) {
positionInArray++;
}
// Make sure that the element has actually been found.
require(positionInArray < topics2ClaimIds[claim.topic].length, "Claim element has not been found.");
// Swap the last element in for it.
topics2ClaimIds[claim.topic][positionInArray] = topics2ClaimIds[claim.topic][topics2ClaimIds[claim.topic].length - 1];
// Delete the (now duplicated) last entry by shrinking the array.
topics2ClaimIds[claim.topic].pop();
// Delete the actual directory entry.
claim.topic = 0;
claim.scheme = 0;
claim.issuer = address(0);
claim.signature = "";
claim.data = "";
claim.uri = "";
return true;
}
function burnClaimId(mapping (uint256 => bool) storage burnedClaimIds, uint256 _topic) public {
burnedClaimIds[getClaimId(msg.sender, _topic)] = true;
}
function reinstateClaimId(mapping (uint256 => bool) storage burnedClaimIds, uint256 _topic) public {
burnedClaimIds[getClaimId(msg.sender, _topic)] = false;
}
/**
* Only consumes reception approval when handling forwards. Fails iff granted reception approval does not match.
*/
function consumeReceptionApproval(mapping (address => mapping (uint256 => mapping(address => IdentityContractLib.PerishableValue))) storage receptionApproval,
uint64 _balancePeriod, uint256 _id, address _from, uint256 _value) public {
address energyToken = msg.sender;
// In the case of forwards, require an exact match; in the case of certificates anything
// not exceeding the limit is permissible.
if(isCertificate(_id)) {
require(receptionApproval[energyToken][_id][_from].value >= _value, "Approval for token value too low.");
receptionApproval[energyToken][_id][_from].value -= _value;
} else {
require(receptionApproval[energyToken][_id][_from].value == _value, "Approval for token value does not match.");
receptionApproval[energyToken][_id][_from].value = 0;
}
require(receptionApproval[energyToken][_id][_from].expiryDate >= _balancePeriod, "Approval for token reception is expired.");
}
// ########################
// # Internal functions
// ########################
function getClaimId(address _issuer, uint256 _topic) internal pure returns (uint256 __claimRequestId) {
// TODO: Addition or concatenation?
bytes memory preimageIssuer = abi.encodePacked(_issuer);
bytes memory preimageTopic = abi.encodePacked(_topic);
return uint256(keccak256(abi.encodePacked(preimageIssuer, preimageTopic)));
}
function isCertificate(uint256 _id) internal pure returns (bool) {
return (_id & 0xff00000000000000000000000000000000000000000000000000000000000000) == 0x0400000000000000000000000000000000000000000000000000000000000000;
}
}