-
Notifications
You must be signed in to change notification settings - Fork 68
Feature/146382715 metatx controller #38
Changes from 27 commits
5dfc653
32380cd
f4ec8d6
048611e
4a6da21
d289ce1
9d4fb31
b5f1771
09ab55c
875ed99
ce9ae06
03a40ff
03a546d
9f8767e
61097de
eca4abc
3ef76aa
e581692
fd749c0
4f84f01
ef6112b
be34616
998175a
6f07f58
3dfb959
d71120d
cc35bc5
3b24d00
ed9d56b
f97f41d
47888c3
3310162
4f788db
30a4e24
2c25882
c0db8fa
f33af80
36feb7b
962f2e6
8fa6cb2
c410450
3b99c9f
a8f254a
e6f57d9
bb2c113
194f4cf
33b36fe
6279ae8
99a625d
197c885
dff6e6f
573d34c
96e4d88
7b6ed37
cda3b71
4a95c63
6010aca
0f15d2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
pragma solidity 0.4.8; | ||
import "./Proxy.sol"; | ||
|
||
contract IdentityManager { | ||
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); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
pragma solidity ^0.4.8; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use |
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't it be There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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
andcheckMessageData
. That would also ensure that changes to the normal IdentityManager would get reflected here automatically.There was a problem hiding this comment.
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