From 9c3116e5cda40ef2482be7954de88c9fc0a7d005 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 13 Mar 2018 19:30:12 -0400 Subject: [PATCH] Updated test unit to reflect changes in recovery contract --- Identity.md | 4 +- contracts/tests/TestContract.sol | 23 +++++++ test/friendsRecovery.js | 104 +++++++++++++------------------ utils/identityUtils.js | 16 +++++ 4 files changed, 83 insertions(+), 64 deletions(-) diff --git a/Identity.md b/Identity.md index b3a295a..163c5b9 100644 --- a/Identity.md +++ b/Identity.md @@ -102,8 +102,8 @@ Once updated, a `NewKernel` event is triggered. ### Upgrade an `IdentityKernel` instance When an identity instance needs to be upgraded, we can use the execute/approve process to upgrade it to an specific version. This upgrade process requires using `execute` to call two functions -- `updateRequestUpdatableInstance(address _newKernel)`. This will generate a pending request for upgrading an instance 30 days later and trigger an UpdateRequested event. -- `updateConfirmUpdatableInstance(address _newKernel)`. After 30 days pass, this function needs to be invoked to confirm the update process. Once the update process is completed a UpdateConfirmed event is triggered. +- `updateRequestUpdatableInstance(address _newKernel)`. This will generate a pending request for upgrading an instance `30 days` later and trigger an UpdateRequested event. +- `updateConfirmUpdatableInstance(address _newKernel)`. After `30 days` pass, this function needs to be invoked to confirm the update process. Once the update process is completed a UpdateConfirmed event is triggered. - An request for update can be cancelled if it hasn't been approved yet. This is done using the function `updateCancelUpdatableInstance()` with the same execute/approve process Kernel addresses could be obtained using the `getVersion` function of the `IdentityFactory` diff --git a/contracts/tests/TestContract.sol b/contracts/tests/TestContract.sol index 0ed094e..3074bda 100644 --- a/contracts/tests/TestContract.sol +++ b/contracts/tests/TestContract.sol @@ -8,4 +8,27 @@ contract TestContract { function test() public { TestFunctionExecuted(); } + + + /* + Helper function to be used in unit testing due to error in web3 + web3.utils.soliditySha3([1, 2, 3]) + Error: Autodetection of array types is not supported. + at _processSoliditySha3Args (node_modules/web3-utils/src/soliditySha3.js:176:15) + */ + function hash( + address identity, + bytes32 _revealedSecret, + address _dest, + bytes _data, + bytes32 _newSecret, + bytes32[] _newFriendsHashes) + external + pure + returns(bytes32) + { + return keccak256(identity, _revealedSecret, _dest, _data, _newSecret, _newFriendsHashes); + + } + } \ No newline at end of file diff --git a/test/friendsRecovery.js b/test/friendsRecovery.js index 2f47ddf..1958fa0 100644 --- a/test/friendsRecovery.js +++ b/test/friendsRecovery.js @@ -3,6 +3,7 @@ const idUtils = require("../utils/identityUtils"); const Identity = artifacts.require("./identity/Identity.sol"); const FriendsRecovery = artifacts.require("./identity/FriendsRecovery.sol"); +const TestContract = artifacts.require("./tests/TestContract.sol"); const web3Utils = require("web3-utils"); const web3EthAbi = require("web3-eth-abi"); @@ -20,23 +21,26 @@ contract('FriendsRecovery', function(accounts) { describe("FriendsRecovery()", () => { it("Execute a full recovery", async () => { + let testContractInstance = await TestContract.new({from: accounts[0]}); + let identity = await Identity.new({from: accounts[0]}) // A bytes32 string that represents some user data const secret = '0x0000000000000000000000000000000000000000000000000000000000123456'; - const hashedSecret = web3.sha3(secret, {encoding: 'hex'}); + const hashedSecret = web3Utils.soliditySha3(identity.address, secret); + - const newController = accounts[1]; + const newController = accounts[9]; let threshold = 3; let friendHashes = [ - web3Utils.soliditySha3(friends[0].address, secret), - web3Utils.soliditySha3(friends[2].address, secret), - web3Utils.soliditySha3(friends[3].address, secret), - web3Utils.soliditySha3(friends[1].address, secret), + web3Utils.soliditySha3(identity.address, secret, friends[0].address), + web3Utils.soliditySha3(identity.address, secret, friends[1].address), + web3Utils.soliditySha3(identity.address, secret, friends[2].address), + web3Utils.soliditySha3(identity.address, secret, friends[3].address), ]; - let recoveryContract = await FriendsRecovery.new(threshold, hashedSecret, friendHashes, {from: accounts[0]}); + let recoveryContract = await FriendsRecovery.new(identity.address, 600, threshold, hashedSecret, friendHashes, {from: accounts[0]}); // Setting up recovery contract for identity let tx1 = await identity.execute( @@ -45,14 +49,20 @@ contract('FriendsRecovery', function(accounts) { idUtils.encode.setupRecovery(recoveryContract.address), {from: accounts[0]} ); - //console.log(tx1.logs); - // RECOVER - const newControllerHash = web3Utils.soliditySha3(web3Utils.soliditySha3(secret), newController); - let message = web3Utils.soliditySha3(recoveryContract.address, newControllerHash); - let msgHash = ethUtils.hashPersonalMessage(ethUtils.toBuffer(message, 'hex')); - + const newSecret = '0x0000000000000000000000000000000000000000000000000000000000abcdef'; + const data = idUtils.encode.managerReset(newController); + const newHashedSecret = web3Utils.soliditySha3(identity.address, newSecret); + const newFriendHashes = [ + web3Utils.soliditySha3(accounts[3], newSecret), + web3Utils.soliditySha3(accounts[4], newSecret), + web3Utils.soliditySha3(accounts[5], newSecret) + ]; + + // Normaly we would use soliditySha3, but it doesn't like arrays + const hashedMessageToSign = await testContractInstance.hash.call(identity.address, secret, identity.address, data, newHashedSecret, newFriendHashes); + let msgHash = ethUtils.hashPersonalMessage(ethUtils.toBuffer(hashedMessageToSign, 'hex')); const friendSignatures = [ ethUtils.ecsign(msgHash, ethUtils.toBuffer(friends[0].private, 'hex')), ethUtils.ecsign(msgHash, ethUtils.toBuffer(friends[1].private, 'hex')), @@ -60,8 +70,8 @@ contract('FriendsRecovery', function(accounts) { ethUtils.ecsign(msgHash, ethUtils.toBuffer(friends[3].private, 'hex')) ]; - let nonce = await recoveryContract.recover.call( - newController, + let tx2 = await recoveryContract.approvePreSigned( + hashedMessageToSign, [ friendSignatures[0].v, friendSignatures[1].v, @@ -71,68 +81,38 @@ contract('FriendsRecovery', function(accounts) { '0x' + friendSignatures[0].r.toString('hex'), '0x' + friendSignatures[1].r.toString('hex'), '0x' + friendSignatures[2].r.toString('hex') - ], - {from: accounts[9]}); - - let tx2 = await recoveryContract.recover( - newController, - [ - friendSignatures[0].v, - friendSignatures[1].v, - friendSignatures[2].v ], - [ - '0x' + friendSignatures[0].r.toString('hex'), - '0x' + friendSignatures[1].r.toString('hex'), - '0x' + friendSignatures[2].r.toString('hex') - ], - {from: accounts[9]}); - - - - // REVEAL - const newSecret = '0x0000000000000000000000000000000000000000000000000000000000abcdef'; - const newHashedSecret = web3.sha3(secret, {encoding: 'hex'}); - - const newFriendHashes = [ - web3Utils.soliditySha3(accounts[3], newSecret), - web3Utils.soliditySha3(accounts[4], newSecret), - web3Utils.soliditySha3(accounts[5], newSecret) - ]; - - let tx3 = await recoveryContract.reveal( - nonce, - secret, - newHashedSecret, [ '0x' + friendSignatures[0].s.toString('hex'), '0x' + friendSignatures[1].s.toString('hex'), - '0x' + friendSignatures[2].s.toString('hex') - ], + '0x' + friendSignatures[2].s.toString('hex') + ], + {from: accounts[9]}); + + let tx3 = await recoveryContract.execute( + secret, + identity.address, + data, [ friends[0].address, friends[1].address, friends[2].address - ], newFriendHashes ); + ], + newHashedSecret, + newFriendHashes, + {from: accounts[5]}); - - const recoveryCompletedLog = tx3.logs[tx3.logs.length - 1]; - assert.strictEqual(recoveryCompletedLog.event, "RecoveryCompleted"); - - - // Execute something with new controller address - // In this case, adding new controller address key + await identity.processManagerReset(0, {from: accounts[0]}); - let tx4 = await recoveryContract.execute( - identity.address, - idUtils.encode.addKey(newController, idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS), - {from: newController}); + assert.equal( + await identity.getKeyPurpose(TestUtils.addressToBytes32(newController)), + idUtils.purposes.MANAGEMENT, + identity.address + ".getKeyPurpose(" + newController + ") is not MANAGEMENT_KEY") assert.equal( await identity.getKeyPurpose(TestUtils.addressToBytes32(newController)), idUtils.purposes.MANAGEMENT, identity.address+".getKeyPurpose("+newController+") is not correct") - }); }); diff --git a/utils/identityUtils.js b/utils/identityUtils.js index 5998092..7b52d62 100644 --- a/utils/identityUtils.js +++ b/utils/identityUtils.js @@ -103,6 +103,21 @@ const _setupRecovery = function(address){ }, [address]); } +const _managerReset = function(address){ + if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address)) + throw new Error('Address "'+ address +'" is not a valid Ethereum address.'); + + return web3EthAbi.encodeFunctionCall({ + name: 'managerReset', + type: 'function', + inputs: [{ + type: 'address', + name: '_newKey' + }] + }, [address]); +} + + const _updateUpdatableInstance = function(address){ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address)) throw new Error('Address "'+ address +'" is not a valid Ethereum address.'); @@ -167,6 +182,7 @@ module.exports = { removeKey: _removeKey, setMinimumApprovalsByKeyType: _setMinimumApprovalsByKeyType, setupRecovery: _setupRecovery, + managerReset: _managerReset, updateUpdatableInstance: _updateUpdatableInstance, updateRequestUpdatableInstance: _updateRequestUpdatableInstance, updateConfirmUpdatableInstance: _updateConfirmUpdatableInstance,