Skip to content

Commit

Permalink
Add recover contract for user recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Rudyak committed Jul 9, 2018
1 parent 86fc7b5 commit f5dd715
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ module.exports = {
skipFiles: [
'migration/Migrations.sol',
'helpers/BumpedUserBackend.sol',
'helpers/FailedUserMock.sol',
'helpers/Mock.sol',
'helpers/StorageManager.sol',
'helpers/UserMock.sol',
'helpers/StubRoles2Library.sol',
'helpers/UserProxyTester.sol',
]
Expand Down
36 changes: 36 additions & 0 deletions contracts/Recovery.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2017–2018, LaborX PTY
* Licensed under the AGPL Version 3 license.
*/

pragma solidity ^0.4.23;


import "solidity-shared-lib/contracts/Owned.sol";
import "solidity-roles-lib/contracts/Roles2LibraryAdapter.sol";
import "./UserInterface.sol";


contract Recovery is Roles2LibraryAdapter {

uint constant RECOVERY_SCOPE = 19000;

event UserRecovered(address prevUser, address newUser, UserInterface userContract);

constructor(address _roles2Library) Roles2LibraryAdapter(_roles2Library) public {}

function recoverUser(UserInterface _userContract, address _newAddress)
auth
public
returns (uint)
{
address prev = Owned(_userContract).contractOwner();
if (OK != _userContract.recoverUser(_newAddress)) {
revert("Cannot recover to a new address");
}

emit UserRecovered(prev, _newAddress, _userContract);
return OK;
}

}
26 changes: 26 additions & 0 deletions contracts/helpers/FailedUserMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright 2017–2018, LaborX PTY
* Licensed under the AGPL Version 3 license.
*/

pragma solidity ^0.4.18;


contract FailedUserMock {

uint constant USER_ERROR = 19001;

address public contractOwner;
uint public recoverUserCalls;

function recoverUser(address) external returns (uint) {
recoverUserCalls++;
return USER_ERROR;
}

function setContractOwner(address _newOwner) external returns (bool) {
contractOwner = _newOwner;
return true;
}

}
27 changes: 27 additions & 0 deletions contracts/helpers/UserMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright 2017–2018, LaborX PTY
* Licensed under the AGPL Version 3 license.
*/

pragma solidity ^0.4.18;


contract UserMock {

uint constant OK = 1;

address public contractOwner;
uint public recoverUserCalls;

function recoverUser(address _newAddress) external returns (uint) {
contractOwner = _newAddress;
recoverUserCalls++;
return OK;
}

function setContractOwner(address _newOwner) external returns (bool) {
contractOwner = _newOwner;
return true;
}

}
17 changes: 17 additions & 0 deletions test/multisig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict"

const MultiSig = artifacts.require("MultiSig")

contract("Multisig", accounts => {

// TODO: alesanro

before(async () => {

})

after(async () => {

})

})
115 changes: 115 additions & 0 deletions test/recovery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"use strict"

const Recovery = artifacts.require("Recovery")
const Roles2LibraryInterface = artifacts.require("Roles2LibraryInterface")
const Roles2Library = artifacts.require("StubRoles2Library")
const Storage = artifacts.require("Storage")
const StorageManager = artifacts.require("StorageManager")
const Mock = artifacts.require("Mock")
const UserMock = artifacts.require("UserMock")
const FailedUserMock = artifacts.require("FailedUserMock")

const Reverter = require('./helpers/reverter')
const eventsHelper = require('./helpers/eventsHelper')

contract('Recovery', function(accounts) {
const reverter = new Reverter(web3)

const users = {
contractOwner: accounts[0],
caller: accounts[1],
newUser: '0xffffffffffffffffffffffffffffffffffffffff',
prevUser: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
}

const contracts = {
mock: null,
userMock: null,
failedUserMock: null,
recovery: null,
rolesLibrary: null,
roles2LibraryInterface: web3.eth.contract(Roles2LibraryInterface.abi).at('0x0'),
}

let snapshotId

const assertExpectations = async (expected = 0, callsCount = null) => {
assert.equal(
(await contracts.mock.expectationsLeft()).toString(16),
expected.toString(16)
)

const expectationsCount = await contracts.mock.expectationsCount()
assert.equal(
(await contracts.mock.callsCount()).toString(16),
callsCount === null ? expectationsCount.toString(16) : callsCount.toString(16)
)
}

before('setup', async () => {
await reverter.promisifySnapshot()
snapshotId = reverter.snapshotId

contracts.storage = await Storage.new({ from: users.contractOwner, })
contracts.storageManager = await StorageManager.new({ from: users.contractOwner, })
await contracts.storage.setManager(contracts.storageManager.address, { from: users.contractOwner, })

contracts.rolesLibrary = await Roles2Library.new(contracts.storage.address, "RolesLib", { from: users.contractOwner, })
await contracts.storageManager.giveAccess(contracts.rolesLibrary.address, "RolesLib", { from: users.contractOwner, })
await contracts.rolesLibrary.setRootUser(users.contractOwner, true, { from: users.contractOwner, })

contracts.recovery = await Recovery.new(contracts.rolesLibrary.address, { from: users.contractOwner, })
contracts.mock = await Mock.new({ from: users.contractOwner, })
contracts.userMock = await UserMock.new({ from: users.contractOwner, })
contracts.failedUserMock = await FailedUserMock.new({ from: users.contractOwner, })

await contracts.userMock.setContractOwner(users.prevUser)
await contracts.failedUserMock.setContractOwner(users.prevUser)

await reverter.promisifySnapshot()
})

after(async () => {
await reverter.promisifyRevert(snapshotId)
})

afterEach('revert', async () => {
await reverter.promisifyRevert()
})

it('should check auth on user recovery', async () => {
await contracts.recovery.setRoles2Library(contracts.mock.address)
await contracts.mock.expect(
contracts.recovery.address,
0,
contracts.roles2LibraryInterface.canCall.getData(
users.caller,
contracts.recovery.address,
contracts.recovery.contract.recoverUser.getData(0, 0).slice(0, 10)
),
0
)
await contracts.recovery.recoverUser(contracts.userMock.address, users.newUser, { from: users.caller, })
await assertExpectations()
})

it('should recover users', async () => {
const tx = await contracts.recovery.recoverUser(contracts.userMock.address, users.newUser)
{
const event = (await eventsHelper.findEvent([contracts.recovery,], tx, "UserRecovered"))[0]
assert.isDefined(event)
assert.equal(event.args.prevUser, users.prevUser)
assert.equal(event.args.newUser, users.newUser)
assert.equal(event.args.userContract, contracts.userMock.address)
assert.notEqual(event.args.newUser, event.args.prevUser)
}

assert.equal((await contracts.userMock.recoverUserCalls.call()).toString(16), '1')
})

it('should THROW on user recovery when unable to recover', async () => {
await contracts.recovery.recoverUser(contracts.failedUserMock.address, users.newUser).then(assert.fail, () => true)
assert.equal((await contracts.failedUserMock.recoverUserCalls.call()).toString(16), '0')
})

})

0 comments on commit f5dd715

Please sign in to comment.