2018-02-21 21:17:38 +00:00
|
|
|
pragma solidity ^0.4.17;
|
|
|
|
|
|
|
|
import "./ERC725.sol";
|
|
|
|
import "./ERC735.sol";
|
|
|
|
|
|
|
|
|
|
|
|
contract Identity is ERC725, ERC735 {
|
|
|
|
|
|
|
|
mapping (bytes32 => Key) keys;
|
|
|
|
mapping (bytes32 => Claim) claims;
|
|
|
|
mapping (uint256 => bytes32[]) keysByPurpose;
|
|
|
|
mapping (uint256 => bytes32[]) claimsByType;
|
|
|
|
mapping (bytes32 => uint256) indexes;
|
|
|
|
mapping (uint => Transaction) txx;
|
2018-02-22 18:58:25 +00:00
|
|
|
mapping (uint => Action) actionCatalogByTrx;
|
|
|
|
mapping (uint => bytes32) claimCatalogByTrx;
|
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
mapping (uint256 => uint8) minimumApprovalsByKeyType;
|
2018-02-22 18:58:25 +00:00
|
|
|
mapping (bytes32 => Claim) pendingClaims;
|
2018-02-21 21:17:38 +00:00
|
|
|
uint nonce = 0;
|
|
|
|
|
2018-02-22 18:58:25 +00:00
|
|
|
struct Action {
|
2018-02-21 21:17:38 +00:00
|
|
|
address to;
|
|
|
|
uint value;
|
|
|
|
bytes data;
|
2018-02-22 18:58:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8 constant ACTION_TRANSACTION = 1;
|
|
|
|
uint8 constant CLAIM_TRANSACTION = 2;
|
|
|
|
|
|
|
|
struct Transaction {
|
|
|
|
uint8 trxType;
|
2018-02-21 21:17:38 +00:00
|
|
|
uint nonce;
|
|
|
|
uint approverCount;
|
|
|
|
mapping(uint256 => uint8) approvalsByKeyType;
|
|
|
|
mapping(bytes32 => bool) approvals;
|
2018-02-22 18:58:25 +00:00
|
|
|
bool executed;
|
2018-02-21 21:17:38 +00:00
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
modifier managerOnly {
|
2018-02-21 22:45:25 +00:00
|
|
|
require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
|
2018-02-21 21:17:38 +00:00
|
|
|
_;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:16:06 +00:00
|
|
|
modifier selfOnly {
|
|
|
|
require(msg.sender == address(this));
|
2018-02-21 21:17:38 +00:00
|
|
|
_;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
modifier actorOnly {
|
2018-02-21 22:45:25 +00:00
|
|
|
require(isKeyType(bytes32(msg.sender), ACTION_KEY));
|
2018-02-21 21:17:38 +00:00
|
|
|
_;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
modifier claimSignerOnly {
|
2018-02-21 22:45:25 +00:00
|
|
|
require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
|
2018-02-21 21:17:38 +00:00
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
|
|
|
modifier managerOrActor {
|
2018-02-22 08:05:10 +00:00
|
|
|
require(
|
|
|
|
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
|
|
|
|
isKeyType(bytes32(msg.sender), ACTION_KEY)
|
|
|
|
);
|
2018-02-21 21:17:38 +00:00
|
|
|
_;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
function Identity() public {
|
|
|
|
_addKey(bytes32(msg.sender), MANAGEMENT_KEY, 1);
|
|
|
|
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function addKey(
|
|
|
|
bytes32 _key,
|
|
|
|
uint256 _purpose,
|
|
|
|
uint256 _type
|
|
|
|
)
|
|
|
|
public
|
2018-02-22 08:16:06 +00:00
|
|
|
selfOnly
|
2018-02-22 08:05:10 +00:00
|
|
|
returns (bool success)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
_addKey(_key, _purpose, _type);
|
|
|
|
return true;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function removeKey(
|
|
|
|
bytes32 _key,
|
|
|
|
uint256 _purpose
|
|
|
|
)
|
|
|
|
public
|
2018-02-22 08:16:06 +00:00
|
|
|
selfOnly
|
2018-02-22 08:05:10 +00:00
|
|
|
returns (bool success)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
_removeKey(_key, _purpose);
|
|
|
|
return true;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function execute(
|
|
|
|
address _to,
|
|
|
|
uint256 _value,
|
|
|
|
bytes _data
|
|
|
|
)
|
2018-02-21 21:17:38 +00:00
|
|
|
public
|
|
|
|
managerOrActor
|
|
|
|
returns (uint256 executionId)
|
|
|
|
{
|
2018-02-22 18:58:25 +00:00
|
|
|
executionId = createTransaction(ACTION_TRANSACTION);
|
2018-02-21 21:17:38 +00:00
|
|
|
ExecutionRequested(executionId, _to, _value, _data);
|
2018-02-22 18:58:25 +00:00
|
|
|
|
|
|
|
actionCatalogByTrx[nonce] = Action({to: _to, value: _value, data: _data});
|
2018-02-22 08:19:38 +00:00
|
|
|
approve(executionId, true);
|
2018-02-21 21:17:38 +00:00
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
function approve(uint256 _id, bool _approve)
|
|
|
|
public
|
|
|
|
managerOrActor
|
|
|
|
returns (bool success)
|
|
|
|
{
|
|
|
|
Transaction storage trx = txx[_id];
|
2018-02-22 18:58:25 +00:00
|
|
|
require(trx.executed == false);
|
2018-02-21 21:17:38 +00:00
|
|
|
|
|
|
|
bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
|
2018-02-22 08:05:10 +00:00
|
|
|
bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
|
2018-02-21 21:17:38 +00:00
|
|
|
|
|
|
|
uint8 approvalCount;
|
|
|
|
uint256 requiredKeyType;
|
2018-02-22 18:58:25 +00:00
|
|
|
if (trx.trxType == ACTION_KEY) {
|
|
|
|
Action memory action = actionCatalogByTrx[_id];
|
|
|
|
if (action.to == address(this)) {
|
|
|
|
requiredKeyType = MANAGEMENT_KEY;
|
|
|
|
if (keys[managerKeyHash].purpose == MANAGEMENT_KEY)
|
|
|
|
approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
|
|
|
|
} else {
|
|
|
|
requiredKeyType = ACTION_KEY;
|
|
|
|
if (keys[managerKeyHash].purpose == ACTION_KEY)
|
|
|
|
approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
|
|
|
|
trx.executed = true;
|
|
|
|
Executed(_id, action.to, action.value, action.data);
|
|
|
|
success = action.to.call.value(action.value)(action.data);
|
2018-02-21 21:17:38 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-02-22 18:58:25 +00:00
|
|
|
// It is a claim
|
|
|
|
bytes32 claimHash = claimCatalogByTrx[_id];
|
|
|
|
if (keys[managerKeyHash].purpose == MANAGEMENT_KEY)
|
|
|
|
approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
|
|
|
|
|
|
|
|
if (approvalCount >= minimumApprovalsByKeyType[MANAGEMENT_KEY]) {
|
|
|
|
Claim memory c = pendingClaims[claimHash];
|
|
|
|
claims[claimHash] = Claim(
|
|
|
|
{
|
|
|
|
claimType: c.claimType,
|
|
|
|
scheme: c.scheme,
|
|
|
|
issuer: c.issuer,
|
|
|
|
signature: c.signature,
|
|
|
|
data: c.data,
|
|
|
|
uri: c.uri
|
|
|
|
});
|
|
|
|
|
|
|
|
indexes[claimHash] = claimsByType[c.claimType].length;
|
|
|
|
claimsByType[c.claimType].push(claimHash);
|
|
|
|
ClaimAdded(claimHash, c.claimType, c.scheme, c.issuer, c.signature, c.data, c.uri);
|
|
|
|
trx.executed = true;
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-22 18:58:25 +00:00
|
|
|
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:16:06 +00:00
|
|
|
function setMiminumApprovalsByKeyType(
|
|
|
|
uint256 _type,
|
|
|
|
uint8 _minimumApprovals
|
|
|
|
)
|
|
|
|
public
|
|
|
|
selfOnly
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
minimumApprovalsByKeyType[_type] = _minimumApprovals;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function _calculateApprovals(
|
|
|
|
bytes32 _keyHash,
|
|
|
|
uint256 _keyType,
|
|
|
|
bool _approve,
|
|
|
|
Transaction storage trx
|
|
|
|
)
|
2018-02-21 21:17:38 +00:00
|
|
|
private
|
|
|
|
returns (uint8 approvalCount)
|
|
|
|
{
|
2018-02-22 08:05:10 +00:00
|
|
|
if (trx.approvals[_keyHash] == false && _approve) {
|
2018-02-21 21:17:38 +00:00
|
|
|
trx.approvalsByKeyType[_keyType]++;
|
|
|
|
} else if (trx.approvals[_keyHash] == true && !_approve && trx.approverCount > 0) {
|
|
|
|
trx.approvalsByKeyType[_keyType]--;
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
trx.approvals[_keyHash] = _approve;
|
|
|
|
trx.approverCount++;
|
|
|
|
return trx.approvalsByKeyType[_keyType];
|
|
|
|
}
|
2018-02-22 18:58:25 +00:00
|
|
|
|
|
|
|
function createTransaction(uint8 _trxType) private returns (uint256 id){
|
|
|
|
id = nonce;
|
|
|
|
txx[nonce] = Transaction(
|
|
|
|
{
|
|
|
|
nonce: nonce,
|
|
|
|
trxType: _trxType,
|
|
|
|
approverCount: 0,
|
|
|
|
executed: false
|
|
|
|
});
|
|
|
|
nonce++;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTransactionIdByClaim(bytes32 _claimHash) public view returns (uint256 id) {
|
|
|
|
return indexes[_claimHash];
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function addClaim(
|
|
|
|
uint256 _claimType,
|
|
|
|
uint256 _scheme,
|
|
|
|
address _issuer,
|
|
|
|
bytes _signature,
|
|
|
|
bytes _data,
|
|
|
|
string _uri
|
|
|
|
)
|
2018-02-21 21:17:38 +00:00
|
|
|
public
|
|
|
|
claimSignerOnly
|
|
|
|
returns (bytes32 claimRequestId)
|
|
|
|
{
|
|
|
|
|
|
|
|
bytes32 claimHash = keccak256(_issuer, _claimType);
|
|
|
|
|
|
|
|
claimRequestId = claimHash;
|
|
|
|
|
2018-02-22 18:58:25 +00:00
|
|
|
uint256 executionId = createTransaction(CLAIM_TRANSACTION);
|
|
|
|
claimCatalogByTrx[executionId] = claimHash;
|
|
|
|
indexes[claimHash] = executionId;
|
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
if (claims[claimHash].claimType > 0) {
|
2018-02-22 18:58:25 +00:00
|
|
|
// Claim existed, needs approval
|
2018-02-22 08:05:10 +00:00
|
|
|
ClaimChanged(
|
|
|
|
claimRequestId,
|
|
|
|
_claimType,
|
|
|
|
_scheme,
|
|
|
|
_issuer,
|
|
|
|
_signature,
|
|
|
|
_data,
|
|
|
|
_uri);
|
2018-02-21 21:17:38 +00:00
|
|
|
} else {
|
2018-02-22 08:05:10 +00:00
|
|
|
ClaimRequested(
|
|
|
|
claimRequestId,
|
|
|
|
_claimType,
|
|
|
|
_scheme,
|
|
|
|
_issuer,
|
|
|
|
_signature,
|
|
|
|
_data,
|
|
|
|
_uri);
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
|
2018-02-22 18:58:25 +00:00
|
|
|
pendingClaims[claimHash] = Claim(
|
2018-02-21 21:17:38 +00:00
|
|
|
{
|
|
|
|
claimType: _claimType,
|
|
|
|
scheme: _scheme,
|
|
|
|
issuer: _issuer,
|
|
|
|
signature: _signature,
|
|
|
|
data: _data,
|
|
|
|
uri: _uri
|
|
|
|
}
|
2018-02-22 18:58:25 +00:00
|
|
|
);
|
2018-02-21 21:17:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function removeClaim(bytes32 _claimId) public returns (bool success) {
|
|
|
|
Claim memory c = claims[_claimId];
|
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
require(
|
|
|
|
msg.sender == c.issuer ||
|
|
|
|
msg.sender == address(this) ||
|
|
|
|
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
|
|
|
|
);
|
2018-02-21 21:17:38 +00:00
|
|
|
|
|
|
|
uint claimIdTypePos = indexes[_claimId];
|
|
|
|
delete indexes[_claimId];
|
|
|
|
bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
|
|
|
|
bytes32 replacer = claimsTypeArr[claimsTypeArr.length-1];
|
|
|
|
claimsTypeArr[claimIdTypePos] = replacer;
|
|
|
|
indexes[replacer] = claimIdTypePos;
|
|
|
|
delete claims[_claimId];
|
|
|
|
claimsTypeArr.length--;
|
|
|
|
return true;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
|
|
|
|
bytes32 keyHash = keccak256(_key, _purpose);
|
|
|
|
|
|
|
|
require(keys[keyHash].purpose == 0);
|
2018-02-22 08:05:10 +00:00
|
|
|
require(
|
|
|
|
_purpose == MANAGEMENT_KEY ||
|
|
|
|
_purpose == ACTION_KEY ||
|
|
|
|
_purpose == CLAIM_SIGNER_KEY ||
|
|
|
|
_purpose == ENCRYPTION_KEY
|
|
|
|
);
|
2018-02-21 21:17:38 +00:00
|
|
|
KeyAdded(_key, _purpose, _type);
|
|
|
|
keys[keyHash] = Key(_purpose, _type, _key);
|
|
|
|
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
function _removeKey(bytes32 _key, uint256 _purpose) private {
|
|
|
|
bytes32 keyHash = keccak256(_key, _purpose);
|
|
|
|
Key storage myKey = keys[keyHash];
|
|
|
|
KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
|
|
|
|
|
|
|
|
uint index = indexes[keyHash];
|
|
|
|
delete indexes[keyHash];
|
|
|
|
bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
|
|
|
|
keysByPurpose[_purpose][index] = replacer;
|
2018-02-22 08:05:10 +00:00
|
|
|
indexes[keccak256(replacer, _purpose)] = index;
|
2018-02-21 21:17:38 +00:00
|
|
|
keysByPurpose[_purpose].length--;
|
|
|
|
|
|
|
|
if (_purpose == MANAGEMENT_KEY) {
|
|
|
|
require(keysByPurpose[MANAGEMENT_KEY].length >= 1);
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 21:17:38 +00:00
|
|
|
delete keys[keyHash];
|
|
|
|
|
|
|
|
// MUST only be done by keys of purpose 1, or the identity itself.
|
|
|
|
// TODO If its the identity itself, the approval process will determine its approval.
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function getKey(
|
|
|
|
bytes32 _key,
|
|
|
|
uint256 _purpose
|
|
|
|
)
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns(uint256 purpose, uint256 keyType, bytes32 key)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
Key storage myKey = keys[keccak256(_key, _purpose)];
|
|
|
|
return (myKey.purpose, myKey.keyType, myKey.key);
|
|
|
|
}
|
2018-02-21 22:45:25 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function isKeyType(bytes32 _key, uint256 _type)
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns (bool)
|
|
|
|
{
|
2018-02-21 22:45:25 +00:00
|
|
|
return keys[keccak256(_key, _type)].purpose == _type;
|
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function getKeyPurpose(bytes32 _key)
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns(uint256[] purpose)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
|
|
|
|
uint256[] memory purposeHolder = new uint256[](4);
|
|
|
|
uint8 counter = 0;
|
|
|
|
|
2018-02-21 22:45:25 +00:00
|
|
|
if (isKeyType(_key, MANAGEMENT_KEY)) {
|
2018-02-21 21:17:38 +00:00
|
|
|
purposeHolder[counter] = MANAGEMENT_KEY;
|
|
|
|
counter++;
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
|
2018-02-21 22:45:25 +00:00
|
|
|
if (isKeyType(_key, ACTION_KEY)) {
|
2018-02-21 21:17:38 +00:00
|
|
|
purposeHolder[counter] = ACTION_KEY;
|
|
|
|
counter++;
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
|
2018-02-21 22:45:25 +00:00
|
|
|
if (isKeyType(_key, CLAIM_SIGNER_KEY)) {
|
2018-02-21 21:17:38 +00:00
|
|
|
purposeHolder[counter] = CLAIM_SIGNER_KEY;
|
|
|
|
counter++;
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
|
2018-02-21 22:45:25 +00:00
|
|
|
if (isKeyType(_key, ENCRYPTION_KEY)) {
|
2018-02-21 21:17:38 +00:00
|
|
|
purposeHolder[counter] = ENCRYPTION_KEY;
|
|
|
|
counter++;
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
|
|
|
|
uint256[] memory result = new uint256[](counter);
|
2018-02-22 08:05:10 +00:00
|
|
|
for (uint8 i = 0; i < counter; i++) {
|
2018-02-21 21:17:38 +00:00
|
|
|
result[i] = purposeHolder[i];
|
2018-02-22 08:05:10 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function getKeysByPurpose(uint256 _purpose)
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns(bytes32[] keys)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
return keysByPurpose[_purpose];
|
|
|
|
}
|
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function getClaim(bytes32 _claimId)
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
Claim memory _claim = claims[_claimId];
|
|
|
|
return (_claim.claimType, _claim.scheme, _claim.issuer, _claim.signature, _claim.data, _claim.uri);
|
|
|
|
}
|
|
|
|
|
2018-02-22 08:05:10 +00:00
|
|
|
function getClaimIdsByType(uint256 _claimType)
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns(bytes32[] claimIds)
|
|
|
|
{
|
2018-02-21 21:17:38 +00:00
|
|
|
return claimsByType[_claimType];
|
2018-01-09 01:00:07 +00:00
|
|
|
}
|
2018-02-21 21:17:38 +00:00
|
|
|
}
|
2018-01-09 01:00:07 +00:00
|
|
|
|
|
|
|
|