-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add recover contract for user recovery
- Loading branch information
Alex Rudyak
committed
Jul 9, 2018
1 parent
86fc7b5
commit f5dd715
Showing
6 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 () => { | ||
|
||
}) | ||
|
||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | ||
}) | ||
|
||
}) |