diff --git a/truffle/contracts/Profile/ClaimHolder.sol b/truffle/contracts/Profile/ClaimHolder.sol new file mode 100644 index 0000000..a6b6bbb --- /dev/null +++ b/truffle/contracts/Profile/ClaimHolder.sol @@ -0,0 +1,110 @@ +pragma solidity ^0.5.11; + +import "./ERC735.sol"; +import "./KeyHolder.sol"; + +// **Warning!** This file is a protoype version of our work around ERC 725. +// This file is now out of date and **should not be used**. +// Our current identity contracts are here: +// https://github.com/OriginProtocol/origin/tree/master/origin-contracts/contracts/identity + +contract ClaimHolder is KeyHolder, ERC735 { + + mapping (bytes32 => Claim) claims; + mapping (uint256 => bytes32[]) claimsByType; + + function addClaim( + uint256 _claimType, + uint256 _scheme, + address _issuer, + bytes memory _signature, + bytes memory _data, + string memory _uri + ) + public + returns (bytes32 claimRequestId) + { + bytes32 claimId = keccak256(abi.encodePacked(_issuer, _claimType)); + + if (msg.sender != address(this)) { + require(keyHasPurpose(keccak256(abi.encodePacked(msg.sender)), 3), "Sender does not have claim signer key"); + } + + if (claims[claimId].issuer != _issuer) { + claimsByType[_claimType].push(claimId); + } + + claims[claimId].claimType = _claimType; + claims[claimId].scheme = _scheme; + claims[claimId].issuer = _issuer; + claims[claimId].signature = _signature; + claims[claimId].data = _data; + claims[claimId].uri = _uri; + + emit ClaimAdded( + claimId, + _claimType, + _scheme, + _issuer, + _signature, + _data, + _uri + ); + + return claimId; + } + + function removeClaim(bytes32 _claimId) public returns (bool success) { + if (msg.sender != address(this)) { + require(keyHasPurpose(keccak256(abi.encodePacked(msg.sender)), 1), "Sender does not have management key"); + } + + /* uint index; */ + /* (index, ) = claimsByType[claims[_claimId].claimType].indexOf(_claimId); + claimsByType[claims[_claimId].claimType].removeByIndex(index); */ + + emit ClaimRemoved( + _claimId, + claims[_claimId].claimType, + claims[_claimId].scheme, + claims[_claimId].issuer, + claims[_claimId].signature, + claims[_claimId].data, + claims[_claimId].uri + ); + + delete claims[_claimId]; + return true; + } + + function getClaim(bytes32 _claimId) + public + view + returns( + uint256 claimType, + uint256 scheme, + address issuer, + bytes memory signature, + bytes memory data, + string memory uri + ) + { + return ( + claims[_claimId].claimType, + claims[_claimId].scheme, + claims[_claimId].issuer, + claims[_claimId].signature, + claims[_claimId].data, + claims[_claimId].uri + ); + } + + function getClaimIdsByType(uint256 _claimType) + public + view + returns(bytes32[] memory claimIds) + { + return claimsByType[_claimType]; + } + +} diff --git a/truffle/contracts/Profile/ClaimVerifier.sol b/truffle/contracts/Profile/ClaimVerifier.sol new file mode 100644 index 0000000..0f6d1b5 --- /dev/null +++ b/truffle/contracts/Profile/ClaimVerifier.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.5.11; + +import "./ClaimHolder.sol"; + +// **Warning!** This file is a protoype version of our work around ERC 725. +// This file is now out of date and **should not be used**. +// Our current identity contracts are here: +// https://github.com/OriginProtocol/origin/tree/master/origin-contracts/contracts/identity + +contract ClaimVerifier { + + event ClaimValid(ClaimHolder _identity, uint256 claimType); + event ClaimInvalid(ClaimHolder _identity, uint256 claimType); + + ClaimHolder public trustedClaimHolder; + + constructor (address _trustedClaimHolder) public { + trustedClaimHolder = ClaimHolder(_trustedClaimHolder); + } + + function checkClaim(ClaimHolder _identity, uint256 claimType) + public + returns (bool claimValid) + { + if (claimIsValid(_identity, claimType)) { + emit ClaimValid(_identity, claimType); + return true; + } else { + emit ClaimInvalid(_identity, claimType); + return false; + } + } + + function claimIsValid(ClaimHolder _identity, uint256 claimType) + public + view + returns (bool claimValid) + { + uint256 foundClaimType; + uint256 scheme; + address issuer; + bytes memory sig; + bytes memory data; + + // Construct claimId (identifier + claim type) + bytes32 claimId = keccak256(abi.encodePacked(trustedClaimHolder, claimType)); + + // Fetch claim from user + ( foundClaimType, scheme, issuer, sig, data, ) = _identity.getClaim(claimId); + + bytes32 dataHash = keccak256(abi.encodePacked(_identity, claimType, data)); + 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.encodePacked(recovered)); + + // Does the trusted identifier have they key which signed the user's claim? + return trustedClaimHolder.keyHasPurpose(hashedAddr, 3); + } + + function getRecoveredAddress(bytes memory sig, bytes32 dataHash) + public + pure + returns (address addr) + { + bytes32 ra; + bytes32 sa; + uint8 va; + + // Check the signature length + if (sig.length != 65) { + return (address(0x0)); + } + + // Divide the signature in r, s and v variables + assembly { + ra := mload(add(sig, 32)) + sa := mload(add(sig, 64)) + va := byte(0, mload(add(sig, 96))) + } + + if (va < 27) { + va += 27; + } + + address recoveredAddress = ecrecover(dataHash, va, ra, sa); + + return (recoveredAddress); + } + +} diff --git a/truffle/contracts/Profile/ERC725.sol b/truffle/contracts/Profile/ERC725.sol new file mode 100644 index 0000000..76f2f66 --- /dev/null +++ b/truffle/contracts/Profile/ERC725.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.5.11; + +// **Warning!** This file is a protoype version of our work around ERC 725. +// This file is now out of date and **should not be used**. +// Our current identity contracts are here: +// https://github.com/OriginProtocol/origin/tree/master/origin-contracts/contracts/identity + +contract ERC725 { + + uint256 constant MANAGEMENT_KEY = 1; + uint256 constant ACTION_KEY = 2; + uint256 constant CLAIM_SIGNER_KEY = 3; + uint256 constant ENCRYPTION_KEY = 4; + + event KeyAdded(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType); + event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType); + event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data); + event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data); + event Approved(uint256 indexed executionId, bool approved); + + struct Key { + uint256 purpose; //e.g., MANAGEMENT_KEY = 1, ACTION_KEY = 2, etc. + uint256 keyType; // e.g. 1 = ECDSA, 2 = RSA, etc. + bytes32 key; + } + + function getKey(bytes32 _key) public view returns(uint256 purpose, uint256 keyType, bytes32 key); + function getKeyPurpose(bytes32 _key) public view returns(uint256 purpose); + function getKeysByPurpose(uint256 _purpose) public view returns(bytes32[] memory keys); + function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success); + function execute(address _to, uint256 _value, bytes memory _data) public returns (uint256 executionId); + function approve(uint256 _id, bool _approve) public returns (bool success); +} diff --git a/truffle/contracts/Profile/ERC735.sol b/truffle/contracts/Profile/ERC735.sol new file mode 100644 index 0000000..385d0fb --- /dev/null +++ b/truffle/contracts/Profile/ERC735.sol @@ -0,0 +1,43 @@ +pragma solidity ^0.5.11; + +// **Warning!** This file is a protoype version of our work around ERC 725. +// This file is now out of date and **should not be used**. +// Our current identity contracts are here: +// https://github.com/OriginProtocol/origin/tree/master/origin-contracts/contracts/identity + +contract ERC735 { + + event ClaimRequested(uint256 indexed claimRequestId, uint256 indexed claimType, uint256 scheme, + address indexed issuer, bytes signature, bytes data, string uri); + + event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, + uint256 signatureType, bytes32 signature, bytes claim, string uri); + + event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, + address indexed issuer, bytes signature, bytes data, string uri); + + event ClaimRemoved(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, + address indexed issuer, bytes signature, bytes data, string uri); + + event ClaimChanged(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, + address indexed issuer, bytes signature, bytes data, string uri); + + struct Claim { + uint256 claimType; + uint256 scheme; + address issuer; // msg.sender + bytes signature; // this.address + claimType + data + bytes data; + string uri; + } + + function getClaim(bytes32 _claimId) public view returns(uint256 claimType, uint256 scheme, + address issuer, bytes memory signature, bytes memory data, string memory uri); + + function getClaimIdsByType(uint256 _claimType) public view returns(bytes32[] memory claimIds); + + function addClaim(uint256 _claimType, uint256 _scheme, address issuer, bytes memory _signature, + bytes memory _data, string memory _uri) public returns (bytes32 claimRequestId); + + function removeClaim(bytes32 _claimId) public returns (bool success); +} diff --git a/truffle/contracts/Profile/KeyHolder.sol b/truffle/contracts/Profile/KeyHolder.sol new file mode 100644 index 0000000..1e22287 --- /dev/null +++ b/truffle/contracts/Profile/KeyHolder.sol @@ -0,0 +1,164 @@ +pragma solidity ^0.5.11; + +import "./ERC725.sol"; + +// **Warning!** This file is a protoype version of our work around ERC 725. +// This file is now out of date and **should not be used**. +// Our current identity contracts are here: +// https://github.com/OriginProtocol/origin/tree/master/origin-contracts/contracts/identity + +contract KeyHolder is ERC725 { + + uint256 executionNonce; + + struct Execution { + address to; + uint256 value; + bytes data; + bool approved; + bool executed; + } + + mapping (bytes32 => Key) keys; + mapping (uint256 => bytes32[]) keysByPurpose; + mapping (uint256 => Execution) executions; + + event ExecutionFailed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data); + + constructor() public { + bytes32 _key = keccak256(abi.encodePacked(msg.sender)); + keys[_key].key = _key; + keys[_key].purpose = 1; + keys[_key].keyType = 1; + keysByPurpose[1].push(_key); + emit KeyAdded(_key, keys[_key].purpose, 1); + } + + function getKey(bytes32 _key) + public + view + returns(uint256 purpose, uint256 keyType, bytes32 key) + { + return (keys[_key].purpose, keys[_key].keyType, keys[_key].key); + } + + function getKeyPurpose(bytes32 _key) + public + view + returns(uint256 purpose) + { + return (keys[_key].purpose); + } + + function getKeysByPurpose(uint256 _purpose) + public + view + returns(bytes32[] memory _keys) + { + return keysByPurpose[_purpose]; + } + + function addKey(bytes32 _key, uint256 _purpose, uint256 _type) + public + returns (bool success) + { + require(keys[_key].key != _key, "Key already exists"); // Key should not already exist + if (msg.sender != address(this)) { + require(keyHasPurpose(keccak256(abi.encodePacked(msg.sender)), 1), + "Sender does not have management key"); // Sender has MANAGEMENT_KEY + } + + keys[_key].key = _key; + keys[_key].purpose = _purpose; + keys[_key].keyType = _type; + + keysByPurpose[_purpose].push(_key); + + emit KeyAdded(_key, _purpose, _type); + + return true; + } + + function approve(uint256 _id, bool _approve) + public + returns (bool success) + { + require(keyHasPurpose(keccak256(abi.encodePacked(msg.sender)), 2), "Sender does not have action key"); + + emit Approved(_id, _approve); + + if (_approve == true) { + executions[_id].approved = true; + (success, ) = executions[_id].to.call(executions[_id].data); + if (success) { + executions[_id].executed = true; + emit Executed( + _id, + executions[_id].to, + executions[_id].value, + executions[_id].data + ); + return true; + } else { + emit ExecutionFailed( + _id, + executions[_id].to, + executions[_id].value, + executions[_id].data + ); + return false; + } + } else { + executions[_id].approved = false; + } + return true; + } + + function execute(address _to, uint256 _value, bytes memory _data) + public + returns (uint256 executionId) + { + require(!executions[executionNonce].executed, "Already executed"); + executions[executionNonce].to = _to; + executions[executionNonce].value = _value; + executions[executionNonce].data = _data; + + emit ExecutionRequested(executionNonce, _to, _value, _data); + + if (keyHasPurpose(keccak256(abi.encodePacked(msg.sender)), 1) + || keyHasPurpose(keccak256(abi.encodePacked(msg.sender)), 2)) { + approve(executionNonce, true); + } + + executionNonce++; + return executionNonce-1; + } + + function removeKey(bytes32 _key) + public + returns (bool success) + { + require(keys[_key].key == _key, "No such key"); + emit KeyRemoved(keys[_key].key, keys[_key].purpose, keys[_key].keyType); + + /* uint index; + (index,) = keysByPurpose[keys[_key].purpose.indexOf(_key); + keysByPurpose[keys[_key].purpose.removeByIndex(index); */ + + delete keys[_key]; + + return true; + } + + function keyHasPurpose(bytes32 _key, uint256 _purpose) + public + view + returns(bool result) + { + bool isThere; + if (keys[_key].key == 0) return false; + isThere = keys[_key].purpose <= _purpose; + return isThere; + } + +} diff --git a/truffle/contracts/Profile/VERSION b/truffle/contracts/Profile/VERSION new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/truffle/contracts/Profile/VERSION @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file diff --git a/truffle/migrations/4_profile_contract.js b/truffle/migrations/4_profile_contract.js new file mode 100644 index 0000000..05ce1ad --- /dev/null +++ b/truffle/migrations/4_profile_contract.js @@ -0,0 +1,12 @@ +var ClaimHolder = artifacts.require("ClaimHolder"); +var ClaimVerifier = artifacts.require("ClaimVerifier"); + +module.exports = function(deployer) { + // Use deployer to state migration tasks. + deployer.deploy(ClaimHolder).then(() => { + //print the address of the trustedClaimHolder if needed + //console.log("ClaimHolder address:"+ClaimHolder.address); + deployer.deploy(ClaimVerifier(ClaimHolder.address)).then( + () => console.log("ClaimVerifier address"+ClaimVerifier.address)); + }) +}; \ No newline at end of file diff --git a/truffle/truffle.js b/truffle/truffle.js index 8e83f6f..58ef2f7 100644 --- a/truffle/truffle.js +++ b/truffle/truffle.js @@ -32,6 +32,14 @@ module.exports = { network_id: "*", gas: 0xfffffffffff, gasPrice: 0x01 + }, + rinkeby: { + host: "localhost", // Connect to geth on the specified + port: 8545, + //from: "0x0085f8e72391Ce4BB5ce47541C846d059399fA6c", // default address to use for any transaction Truffle makes during migrations + from: "0x15f0f6c86547cd816EBc9B75bc800cF720549123", + network_id: 4, + gas: 4612388 // Gas limit used for deploys } }, mocha: { diff --git a/truffle/yarn.lock b/truffle/yarn.lock index 0c2aa96..1e0355b 100644 --- a/truffle/yarn.lock +++ b/truffle/yarn.lock @@ -1079,7 +1079,7 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -define-properties@^1.1.2, define-properties@^1.1.3: +define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -1257,25 +1257,25 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: once "^1.4.0" es-abstract@^1.13.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.0.tgz#f59d9d44278ea8f90c8ff3de1552537c2fd739b4" - integrity sha512-lri42nNq1tIohUuwFBYEM3wKwcrcJa78jukGDdWsuaNxTtxBFGFkKUQ15nc9J+ipje4mhbQR6JwABb4VvawR3A== + version "1.16.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.3.tgz#52490d978f96ff9f89ec15b5cf244304a5bca161" + integrity sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw== dependencies: - es-to-primitive "^1.2.0" + es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.0" + has-symbols "^1.0.1" is-callable "^1.1.4" is-regex "^1.0.4" - object-inspect "^1.6.0" + object-inspect "^1.7.0" object-keys "^1.1.1" - string.prototype.trimleft "^2.0.0" - string.prototype.trimright "^2.0.0" + string.prototype.trimleft "^2.1.0" + string.prototype.trimright "^2.1.0" -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -1963,7 +1963,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -function-bind@^1.0.2, function-bind@^1.1.1: +function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -2225,6 +2225,11 @@ has-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + has-to-string-tag-x@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" @@ -3310,10 +3315,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -4528,21 +4533,21 @@ string.prototype.trim@^1.1.2: es-abstract "^1.13.0" function-bind "^1.1.1" -string.prototype.trimleft@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz#68b6aa8e162c6a80e76e3a8a0c2e747186e271ff" - integrity sha1-aLaqjhYsaoDnbjqKDC50cYbicf8= +string.prototype.trimleft@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== dependencies: - define-properties "^1.1.2" - function-bind "^1.0.2" + define-properties "^1.1.3" + function-bind "^1.1.1" -string.prototype.trimright@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz#ab4a56d802a01fbe7293e11e84f24dc8164661dd" - integrity sha1-q0pW2AKgH75yk+EehPJNyBZGYd0= +string.prototype.trimright@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== dependencies: - define-properties "^1.1.2" - function-bind "^1.0.2" + define-properties "^1.1.3" + function-bind "^1.1.1" string_decoder@~1.1.1: version "1.1.1"