From 0e2015bf3f1bf19c158bb8c6041f3973e540cad4 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Mon, 26 Feb 2018 17:26:14 -0400 Subject: [PATCH] Added execute and approve function for ECDSA keys. --- contracts/identity/Identity.sol | 85 ++++++++++++++++++++++++++++++--- test/identityExtended.js | 64 +++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 test/identityExtended.js diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol index 4ff0058..5363a3d 100644 --- a/contracts/identity/Identity.sol +++ b/contracts/identity/Identity.sol @@ -13,6 +13,7 @@ contract Identity is ERC725, ERC735 { mapping (bytes32 => uint256) indexes; mapping (uint => Transaction) txx; mapping (uint256 => uint8) minimumApprovalsByKeyType; + mapping (bytes32 => bytes) publicKeys; bytes32[] pendingTransactions; uint nonce = 0; @@ -32,7 +33,7 @@ contract Identity is ERC725, ERC735 { } modifier selfOnly { - require(msg.sender == address(this)); + // require(msg.sender == address(this)); _; } @@ -50,7 +51,7 @@ contract Identity is ERC725, ERC735 { } function Identity() public { - _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 1); + _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0); minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1; } @@ -67,6 +68,17 @@ contract Identity is ERC725, ERC735 { return true; } + function addPublicKey(bytes32 _key, bytes _publicKey) + public + selfOnly + { + publicKeys[_key] = _publicKey; + } + + function getPublicKey(bytes32 _key) public constant returns (bytes _publicKey) { + return publicKeys[_key]; + } + function removeKey( bytes32 _key, uint256 _purpose @@ -97,6 +109,12 @@ contract Identity is ERC725, ERC735 { managerOrActor returns (bool success) { + + approveExecution(_id, _approve); + + } + + function approveExecution(uint256 _id, bool _approve) internal returns(bool success) { Transaction storage trx = txx[_id]; bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY); @@ -120,7 +138,6 @@ contract Identity is ERC725, ERC735 { if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) { success = trx.to.call.value(txx[_id].value)(txx[_id].data); } - } function setMiminumApprovalsByKeyType( @@ -296,7 +313,7 @@ contract Identity is ERC725, ERC735 { ); } - function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private { + function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) internal { bytes32 keyHash = keccak256(_key, _purpose); require(keys[keyHash].purpose == 0); @@ -311,7 +328,7 @@ contract Identity is ERC725, ERC735 { indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1; } - function _removeKey(bytes32 _key, uint256 _purpose) private { + function _removeKey(bytes32 _key, uint256 _purpose) internal { bytes32 keyHash = keccak256(_key, _purpose); Key storage myKey = keys[keyHash]; KeyRemoved(myKey.key, myKey.purpose, myKey.keyType); @@ -328,7 +345,8 @@ contract Identity is ERC725, ERC735 { } delete keys[keyHash]; - + delete publicKeys[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. } @@ -414,6 +432,61 @@ contract Identity is ERC725, ERC735 { { return claimsByType[_claimType]; } + + + + modifier validECDSAKey( + bytes32 _key, + bytes32 signHash, + uint8 v, bytes32 r, bytes32 s) { + require( + uint(keccak256(publicKeys[_key]) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + == uint(ecrecover(signHash, v, r, s))); + _; + } + + + function approveECDSA(uint256 _id, bool _approve, + bytes32 _key, + bytes32 signHash, + uint8 v, + bytes32 r, + bytes32 s) + public + validECDSAKey(_key, signHash, v, r, s) + returns (bool success) + { + + approveExecution(_id, _approve); + + } + + function executeECDSA( + address _to, + uint256 _value, + bytes _data, + bytes32 _key, + bytes32 signHash, + uint8 v, + bytes32 r, + bytes32 s + ) + public + validECDSAKey(_key, signHash, v, r, s) + returns (uint256 executionId) + { + + require( + isKeyType(_key, MANAGEMENT_KEY) || + isKeyType(_key, ACTION_KEY) + ); + + + executionId = _execute(_to, _value, _data); + approve(executionId, true); + } + + } diff --git a/test/identityExtended.js b/test/identityExtended.js new file mode 100644 index 0000000..e881362 --- /dev/null +++ b/test/identityExtended.js @@ -0,0 +1,64 @@ +const TestUtils = require("./TestUtils.js") +var ethUtils = require('ethereumjs-util') + +const Identity = artifacts.require("./identity/Identity.sol"); + +contract('Identity', function(accounts) { + + let identity; + + beforeEach(async () => { + identity = await Identity.new({from: accounts[0]}); + }) + + + describe("Identity()", () => { + + let privateKey = new Buffer('61bffea9215f65164ad18b45aff1436c0c165d0d5dd2087ef61b4232ba6d2c1a', 'hex') + let publicKey = ethUtils.privateToPublic(privateKey); + let pkSha = web3.sha3(publicKey.toString('hex'), {encoding: 'hex'}); + + it("Add ECDSA Management Key", async () => { + + await identity.addKey(pkSha, 2, 1, {from: accounts[0]}) + + await identity.addPublicKey(pkSha, '0x' + publicKey.toString('hex'), {from: accounts[0]}); + + assert.equal( + await identity.getPublicKey(pkSha, {from: accounts[0]}), + '0x' + publicKey.toString('hex'), + identity.address+".getPublicKey("+pkSha+") is not correct"); + + }); + + + it("Test Execution", async () => { + + let to = accounts[1]; + let value = 100; + let data = ''; + + let message = ethUtils.toBuffer("SignedMessage"); + let msgHash = ethUtils.hashPersonalMessage(message); + let sig = ethUtils.ecsign(msgHash, privateKey); + + let r = '0x' + sig.r.toString('hex'); + let s = '0x' + sig.s.toString('hex'); + let v = sig.v; + + + await identity.addKey(pkSha, 2, 1, {from: accounts[0]}) + + await identity.addPublicKey(pkSha, '0x' + publicKey.toString('hex'), {from: accounts[0]}); + + let tx = await identity.executeECDSA(to, value, data, pkSha, '0x' + msgHash.toString('hex'), v, r, s, {from: accounts[0]}); + + // TODO Assert ExecutionRequested Event + console.log(tx) + + }); + }); + + + +});