Skip to content
This repository has been archived by the owner on Apr 15, 2020. It is now read-only.

Feature/146382715 metatx controller #38

Closed
wants to merge 58 commits into from
Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
5dfc653
IdentitManager which creates identities and acts as a controller
pelle Apr 17, 2017
32380cd
Adding of multiple devices as well as a change by recoveryKey
pelle Apr 17, 2017
f4ec8d6
tests for removal of owners
pelle Apr 17, 2017
048611e
tests for changeRecovery
pelle Apr 17, 2017
4a6da21
only allow owners who have been around for more than a day to modify …
pelle Apr 21, 2017
d289ce1
Reorganizing stuff based on latest changes in repo
pelle Apr 22, 2017
9d4fb31
added a general purpose rate limiter
pelle May 18, 2017
b5f1771
add a rate limiter to IdentityManager
pelle May 18, 2017
09ab55c
Also limit owners added by owners
pelle May 19, 2017
875ed99
Add registerIdentity function for registering an existing proxy
pelle May 19, 2017
ce9ae06
beginning of reorganizing tests
pelle May 19, 2017
03a40ff
set explicit 0.4.8 pragma
pelle May 24, 2017
03a546d
Configurable time locks
pelle May 24, 2017
9f8767e
Pushing refactored tests. Still failing.
pelle May 24, 2017
61097de
Fix time lock calculations and rate limiter
pelle May 25, 2017
eca4abc
Add remaining tests
pelle May 25, 2017
3ef76aa
Add function to migrate IdentityManager.
coder5876 Jun 5, 2017
e581692
Migration: added events, fixed bug, and cleaned up
naterush Jun 7, 2017
fd749c0
Migration Tests
naterush Jun 8, 2017
4f84f01
Stopped recoveryKey from ever equaling zero.
naterush Jun 8, 2017
ef6112b
Fixed ref to recovery quorum in indes.js
oed May 31, 2017
be34616
Bumped version
oed May 31, 2017
998175a
Add eth-lightwallet to packages
naterush Jun 15, 2017
6f07f58
Add meta-tx contracts
naterush Jun 15, 2017
3dfb959
Add migration and update index
naterush Jun 15, 2017
d71120d
Add basic relay tests
naterush Jun 15, 2017
cc35bc5
Merge branch 'develop' into feature/146382715-metatx-controller
naterush Jun 15, 2017
3b24d00
Add blockTimeout, basic test
naterush Jun 19, 2017
ed9d56b
Merge branch 'feature/146382715-metatx-controller' of https://github.…
naterush Jun 19, 2017
f97f41d
Change tests to async await, add tests
naterush Jun 25, 2017
47888c3
Merge branch 'develop' into feature/146382715-metatx-controller
naterush Jun 25, 2017
3310162
Merge branch 'feature/146382715-metatx-controller' of https://github.…
naterush Jun 25, 2017
4f788db
Change compiler version
naterush Jun 26, 2017
30a4e24
Add TestRegistry to gitignore
naterush Jun 26, 2017
2c25882
Reduce idenManager overheadon non-meta-tx
naterush Jun 26, 2017
c0db8fa
update build
naterush Jun 26, 2017
f33af80
update IdentityManager tests
naterush Jun 26, 2017
36feb7b
Remove regular IdentityManager, update build, migrations
naterush Jun 26, 2017
962f2e6
Jk, update index, migrations now
naterush Jun 26, 2017
8fa6cb2
update tests
naterush Jun 26, 2017
c410450
update yarn
naterush Jun 26, 2017
3b99c9f
update txRelay tests
naterush Jun 26, 2017
a8f254a
updating for circleci
naterush Jun 26, 2017
e6f57d9
simplify forwarding, update tests
naterush Jun 26, 2017
bb2c113
remove old tests
naterush Jun 26, 2017
194f4cf
update comments for MetaIdentityManager
naterush Jun 26, 2017
33b36fe
update meta-tx tests:
naterush Jun 27, 2017
6279ae8
minor tests update, add rateLimited tests
naterush Jun 28, 2017
99a625d
add optimizations for ~2000 gas per meta-tx
naterush Jun 29, 2017
197c885
simplify assembly, edit comment, update tests
naterush Jul 1, 2017
dff6e6f
Fixed syntax error
oed Jul 6, 2017
573d34c
change MetaIdentity -> Identity. Create -> create
naterush Jul 7, 2017
96e4d88
update artifacts
naterush Jul 7, 2017
7b6ed37
update solc, truffle, tests
naterush Jul 10, 2017
cda3b71
Merge branch 'develop' into feature/146382715-metatx-controller
naterush Jul 10, 2017
4a95c63
update dependencies, small linting fixes.
naterush Jul 11, 2017
6010aca
udpate comments
naterush Jul 11, 2017
0f15d2c
Added isOwner and isRecovery functions to IdentityManager
oed Jul 19, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions contracts/IdentityManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
pragma solidity 0.4.8;
import "./Proxy.sol";

contract IdentityManager {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naterush @oed As I mentioned we need a new contract here. This may actually be a good candidate for inheritance as with the design here the only thing different is the constructor, onlyAuthorized and checkMessageData. That would also ensure that changes to the normal IdentityManager would get reflected here automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can rework this over the weekend. For a name, I think MetaIdentityManager works well.

Also, you mean just define a "ManagerInterface," right? If we do inheritance, I'm not sure we can just add modifiers on to functions (aka, would have to redefine functions anyway).

I do think it would make sense to make sure they share the same interface, though. Let me know what you think @pelle

uint adminTimeLock;
uint userTimeLock;
uint adminRate;

event IdentityCreated(
address indexed identity,
address indexed creator,
address owner,
address indexed recoveryKey);

event OwnerAdded(
address indexed identity,
address indexed owner,
address instigator);

event OwnerRemoved(
address indexed identity,
address indexed owner,
address instigator);

event RecoveryChanged(
address indexed identity,
address indexed recoveryKey,
address instigator);

event MigrationInitiated(
address indexed identity,
address indexed newIdManager,
address instigator);

event MigrationCanceled(
address indexed identity,
address indexed newIdManager,
address instigator);

event MigrationFinalized(
address indexed identity,
address indexed newIdManager,
address instigator);

mapping(address => mapping(address => uint)) owners;
mapping(address => address) recoveryKeys;
mapping(address => mapping(address => uint)) limiter;
mapping(address => uint) migrationInitiated;
mapping(address => address) migrationNewAddress;

modifier onlyOwner(address identity) {
if (owners[identity][msg.sender] > 0 && (owners[identity][msg.sender] + userTimeLock) <= now ) _ ;
else throw;
}

modifier onlyOlderOwner(address identity) {
if (owners[identity][msg.sender] > 0 && (owners[identity][msg.sender] + adminTimeLock) <= now) _ ;
else throw;
}

modifier onlyRecovery(address identity) {
if (recoveryKeys[identity] == msg.sender) _ ;
else throw;
}

modifier rateLimited(Proxy identity) {
if (limiter[identity][msg.sender] < (now - adminRate)) {
limiter[identity][msg.sender] = now;
_ ;
} else throw;
}

// Instantiate IdentityManager with the following limits:
// - userTimeLock - Time before new owner can control proxy
// - adminTimeLock - Time before new owner can add/remove owners
// - adminRate - Time period used for rate limiting a given key for admin functionality
function IdentityManager(uint _userTimeLock, uint _adminTimeLock, uint _adminRate) {
adminTimeLock = _adminTimeLock;
userTimeLock = _userTimeLock;
adminRate = _adminRate;
}

// Factory function
// gas 289,311
function CreateIdentity(address owner, address recoveryKey) {
if (recoveryKey == address(0)) throw;
Proxy identity = new Proxy();
owners[identity][owner] = now - adminTimeLock; // This is to ensure original owner has full power from day one
recoveryKeys[identity] = recoveryKey;
IdentityCreated(identity, msg.sender, owner, recoveryKey);
}

// An identity Proxy can use this to register itself with the IdentityManager
// Note they also have to change the owner of the Proxy over to this, but after calling this
function registerIdentity(address owner, address recoveryKey) {
if (recoveryKey == address(0)) throw;
if (owners[msg.sender][owner] > 0 || recoveryKeys[msg.sender] > 0 ) throw; // Deny any funny business
owners[msg.sender][owner] = now - adminTimeLock; // This is to ensure original owner has full power from day one
recoveryKeys[msg.sender] = recoveryKey;
IdentityCreated(msg.sender, msg.sender, owner, recoveryKey);
}

// Primary forward function
function forwardTo(Proxy identity, address destination, uint value, bytes data) onlyOwner(identity) {
identity.forward(destination, value, data);
}

// an owner can add a new device instantly
function addOwner(Proxy identity, address newOwner) onlyOlderOwner(identity) rateLimited(identity) {
owners[identity][newOwner] = now;
OwnerAdded(identity, newOwner, msg.sender);
}

// a recovery key owner can add a new device with 1 days wait time
function addOwnerForRecovery(Proxy identity, address newOwner) onlyRecovery(identity) rateLimited(identity) {
if (owners[identity][newOwner] > 0) throw;
owners[identity][newOwner] = now;
OwnerAdded(identity, newOwner, msg.sender);
}

// an owner can remove another owner instantly
function removeOwner(Proxy identity, address owner) onlyOlderOwner(identity) rateLimited(identity) {
owners[identity][owner] = 0;
OwnerRemoved(identity, owner, msg.sender);
}

// an owner can add change the recoverykey whenever they want to
function changeRecovery(Proxy identity, address recoveryKey) onlyOlderOwner(identity) rateLimited(identity) {
if (recoveryKey == address(0)) throw;
recoveryKeys[identity] = recoveryKey;
RecoveryChanged(identity, recoveryKey, msg.sender);
}

// an owner can migrate away to a new IdentityManager
function initiateMigration(Proxy identity, address newIdManager) onlyOlderOwner(identity) {
migrationInitiated[identity] = now;
migrationNewAddress[identity] = newIdManager;
MigrationInitiated(identity, newIdManager, msg.sender);
}

// any owner can cancel a migration
function cancelMigration(Proxy identity) onlyOwner(identity) {
address canceledManager = migrationNewAddress[identity];
migrationInitiated[identity] = 0;
migrationNewAddress[identity] = 0;
MigrationCanceled(identity, canceledManager, msg.sender);
}

// owner needs to finalize migration once adminTimeLock time has passed
// WARNING: before transfering to a new address, make sure this address is "ready to recieve" the proxy.
// Not doing so risks the proxy becoming stuck.
function finalizeMigration(Proxy identity) onlyOlderOwner(identity) {
if (migrationInitiated[identity] > 0 && migrationInitiated[identity] + adminTimeLock < now) {
address newIdManager = migrationNewAddress[identity];
migrationInitiated[identity] = 0;
migrationNewAddress[identity] = 0;
identity.transfer(newIdManager);
MigrationFinalized(identity, newIdManager, msg.sender);
}
}
}

168 changes: 168 additions & 0 deletions contracts/MetaIdentityManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
pragma solidity ^0.4.8;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use 0.4.8 without the ^ so that we know which version of the compiler that the contracts are compiled with.

import "./Proxy.sol";

contract MetaIdentityManager {
uint adminTimeLock;
uint userTimeLock;
uint adminRate;
address relay;

event IdentityCreated(
address indexed identity,
address indexed creator,
address owner,
address indexed recoveryKey);

event OwnerAdded(
address indexed identity,
address indexed owner,
address instigator);

event OwnerRemoved(
address indexed identity,
address indexed owner,
address instigator);

event RecoveryChanged(
address indexed identity,
address indexed recoveryKey,
address instigator);

event MigrationInitiated(
address indexed identity,
address indexed newIdManager,
address instigator);

event MigrationCanceled(
address indexed identity,
address indexed newIdManager,
address instigator);

event MigrationFinalized(
address indexed identity,
address indexed newIdManager,
address instigator);

mapping(address => mapping(address => uint)) owners;
mapping(address => address) recoveryKeys;
mapping(address => mapping(address => uint)) limiter;
mapping(address => uint) migrationInitiated;
mapping(address => address) migrationNewAddress;

modifier onlyRelay() {
if (msg.sender == relay) _;
else throw;
}

modifier onlyOwner(address identity, address sender) {
if (owners[identity][sender] > 0 && (owners[identity][sender] + userTimeLock) <= now ) _ ;
else throw;
}

modifier onlyOlderOwner(address identity, address sender) {
if (owners[identity][sender] > 0 && (owners[identity][sender] + adminTimeLock) <= now) _ ;
else throw;
}

modifier onlyRecovery(address identity, address sender) {
if (recoveryKeys[identity] == sender) _ ;
else throw;
}

modifier rateLimited(Proxy identity, address sender) {
if (limiter[identity][sender] < (now - adminRate)) {
limiter[identity][sender] = now;
_ ;
} else throw;
}

// Instantiate IdentityManager with the following limits:
// - userTimeLock - Time before new owner can control proxy
// - adminTimeLock - Time before new owner can add/remove owners
// - adminRate - Time period used for rate limiting a given key for admin functionality
function MetaIdentityManager(uint _userTimeLock, uint _adminTimeLock, uint _adminRate, address relayAddress) {
adminTimeLock = _adminTimeLock;
userTimeLock = _userTimeLock;
adminRate = _adminRate;
relay = relayAddress;
}

// Factory function
// gas 289,311
function CreateIdentity(address owner, address recoveryKey) {
if (recoveryKey == address(0)) throw;
Proxy identity = new Proxy();
owners[identity][owner] = now - adminTimeLock; // This is to ensure original owner has full power from day one
recoveryKeys[identity] = recoveryKey;
IdentityCreated(identity, msg.sender, owner, recoveryKey);
}

// An identity Proxy can use this to register itself with the IdentityManager
// Note they also have to change the owner of the Proxy over to this, but after calling this
function registerIdentity(address owner, address recoveryKey) {
if (recoveryKey == address(0)) throw;
if (owners[msg.sender][owner] > 0 || recoveryKeys[msg.sender] > 0 ) throw; // Deny any funny business
owners[msg.sender][owner] = now - adminTimeLock; // This is to ensure original owner has full power from day one
recoveryKeys[msg.sender] = recoveryKey;
IdentityCreated(msg.sender, msg.sender, owner, recoveryKey);
}

// Primary forward function
function forwardTo(address sender, Proxy identity, address destination, uint value, bytes data) payable onlyRelay onlyOwner(identity, sender) {
identity.forward(destination, value, data);
}

// an owner can add a new device instantly
function addOwner(address sender, Proxy identity, address newOwner) onlyRelay onlyOlderOwner(identity, sender) rateLimited(identity, sender) {
owners[identity][newOwner] = now;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be now - userTimeLock if the new owner should be added instantly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - I missed this in my comment. I don't see a problem w/ adding new owner instantly. What do you think @oed ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think there is a problem with that.

OwnerAdded(identity, newOwner, sender);
}

// a recovery key owner can add a new device with 1 days wait time
function addOwnerForRecovery(address sender, Proxy identity, address newOwner) onlyRelay onlyRecovery(identity, sender) rateLimited(identity, sender) {
if (owners[identity][newOwner] > 0) throw;
owners[identity][newOwner] = now;
OwnerAdded(identity, newOwner, sender);
}

// an owner can remove another owner instantly
function removeOwner(address sender, Proxy identity, address owner) onlyRelay onlyOlderOwner(identity, sender) rateLimited(identity, sender) {
owners[identity][owner] = 0;
OwnerRemoved(identity, owner, sender);
}

// an owner can add change the recoverykey whenever they want to
function changeRecovery(address sender, Proxy identity, address recoveryKey) onlyRelay onlyOlderOwner(identity, sender) rateLimited(identity, sender) {
if (recoveryKey == address(0)) throw;
recoveryKeys[identity] = recoveryKey;
RecoveryChanged(identity, recoveryKey, sender);
}

// an owner can migrate away to a new IdentityManager
function initiateMigration(address sender, Proxy identity, address newIdManager) onlyRelay onlyOlderOwner(identity, sender) {
migrationInitiated[identity] = now;
migrationNewAddress[identity] = newIdManager;
MigrationInitiated(identity, newIdManager, sender);
}

// any owner can cancel a migration
function cancelMigration(address sender, Proxy identity) onlyRelay onlyOwner(identity, sender) {
address canceledManager = migrationNewAddress[identity];
migrationInitiated[identity] = 0;
migrationNewAddress[identity] = 0;
MigrationCanceled(identity, canceledManager, sender);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to put the event in the end of the function? If we put it before the delete we don't need to assign the canceledManager variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. Will update.

}

// owner needs to finalize migration once adminTimeLock time has passed
// WARNING: before transfering to a new address, make sure this address is "ready to recieve" the proxy.
// Not doing so risks the proxy becoming stuck.
function finalizeMigration(address sender, Proxy identity) onlyRelay onlyOlderOwner(identity, sender) {
if (migrationInitiated[identity] > 0 && migrationInitiated[identity] + adminTimeLock < now) {
address newIdManager = migrationNewAddress[identity];
migrationInitiated[identity] = 0;
migrationNewAddress[identity] = 0;
identity.transfer(newIdManager);
MigrationFinalized(identity, newIdManager, sender);
}
}
}
Loading