all files / contracts/ MetaIdentityManager.sol

97.5% Statements 39/40
50% Branches 1/2
100% Functions 21/21
100% Lines 50/50
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223                                                                                                          147× 146×       26× 18×       101× 85×       19× 18×       86× 72× 72×       80× 79×                 48× 48× 48× 48×               70×   70× 70×                                 14×                 42× 42×                 16× 15× 15×                                                 12× 12× 12×                                     81× 81×             46×       103×            
pragma solidity 0.4.15;
import "./Proxy.sol";
 
 
contract MetaIdentityManager {
    uint adminTimeLock;
    uint userTimeLock;
    uint adminRate;
    address relay;
 
    event LogIdentityCreated(
        address indexed identity,
        address indexed creator,
        address owner,
        address indexed recoveryKey);
 
    event LogOwnerAdded(
        address indexed identity,
        address indexed owner,
        address instigator);
 
    event LogOwnerRemoved(
        address indexed identity,
        address indexed owner,
        address instigator);
 
    event LogRecoveryChanged(
        address indexed identity,
        address indexed recoveryKey,
        address instigator);
 
    event LogMigrationInitiated(
        address indexed identity,
        address indexed newIdManager,
        address instigator);
 
    event LogMigrationCanceled(
        address indexed identity,
        address indexed newIdManager,
        address instigator);
 
    event LogMigrationFinalized(
        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) public migrationInitiated;
    mapping(address => address) public migrationNewAddress;
 
    modifier onlyAuthorized() {
        require(msg.sender == relay || checkMessageData(msg.sender));
        _;
    }
 
    modifier onlyOwner(address identity, address sender) {
        require(isOwner(identity, sender));
        _;
    }
 
    modifier onlyOlderOwner(address identity, address sender) {
        require(isOlderOwner(identity, sender));
        _;
    }
 
    modifier onlyRecovery(address identity, address sender) {
        require(recoveryKeys[identity] == sender);
        _;
    }
 
    modifier rateLimited(Proxy identity, address sender) {
        require(limiter[identity][sender] < (now - adminRate));
        limiter[identity][sender] = now;
        _;
    }
 
    modifier validAddress(address addr) { //protects against some weird attacks
        require(addr != address(0));
        _;
    }
 
    /// @dev Contract constructor sets initial timelocks and meta-tx relay address
    /// @param _userTimeLock Time before new owner added by recovery can control proxy
    /// @param _adminTimeLock Time before new owner can add/remove owners
    /// @param _adminRate Time period used for rate limiting a given key for admin functionality
    /// @param _relayAddress Address of meta transaction relay contract
    function MetaIdentityManager(uint _userTimeLock, uint _adminTimeLock, uint _adminRate, address _relayAddress) {
        adminTimeLock = _adminTimeLock;
        userTimeLock = _userTimeLock;
        adminRate = _adminRate;
        relay = _relayAddress;
    }
 
    /// @dev Creates a new proxy contract for an owner and recovery
    /// @param owner Key who can use this contract to control proxy. Given full power
    /// @param recoveryKey Key of recovery network or address from seed to recovery proxy
    /// Gas cost of ~300,000
    function createIdentity(address owner, address recoveryKey) public validAddress(recoveryKey) {
        Proxy identity = new Proxy();
        owners[identity][owner] = now - adminTimeLock; // This is to ensure original owner has full power from day one
        recoveryKeys[identity] = recoveryKey;
        LogIdentityCreated(identity, msg.sender, owner,  recoveryKey);
    }
 
    /// @dev Allows a user to transfer control of existing proxy to this contract. Must come through proxy
    /// @param owner Key who can use this contract to control proxy. Given full power
    /// @param recoveryKey Key of recovery network or address from seed to recovery proxy
    /// Note: User must change owner of proxy to this contract after calling this
    function registerIdentity(address owner, address recoveryKey) public validAddress(recoveryKey) {
        require(recoveryKeys[msg.sender] == 0); // Deny any funny business
        owners[msg.sender][owner] = now - adminTimeLock; // Owner has full power from day one
        recoveryKeys[msg.sender] = recoveryKey;
        LogIdentityCreated(msg.sender, msg.sender, owner, recoveryKey);
    }
 
    /// @dev Allows a user to forward a call through their proxy.
    function forwardTo(address sender, Proxy identity, address destination, uint value, bytes data) public
        onlyAuthorized
        onlyOwner(identity, sender)
    {
        identity.forward(destination, value, data);
    }
 
    /// @dev Allows an olderOwner to add a new owner instantly
    function addOwner(address sender, Proxy identity, address newOwner) public
        onlyAuthorized
        onlyOlderOwner(identity, sender)
        rateLimited(identity, sender)
    {
        owners[identity][newOwner] = now - userTimeLock;
        LogOwnerAdded(identity, newOwner, sender);
    }
 
    /// @dev Allows a recoveryKey to add a new owner with userTimeLock waiting time
    function addOwnerFromRecovery(address sender, Proxy identity, address newOwner) public
        onlyAuthorized
        onlyRecovery(identity, sender)
        rateLimited(identity, sender)
    {
        require(!isOwner(identity, newOwner));
        owners[identity][newOwner] = now;
        LogOwnerAdded(identity, newOwner, sender);
    }
 
    /// @dev Allows an owner to remove another owner instantly
    function removeOwner(address sender, Proxy identity, address owner) public
        onlyAuthorized
        onlyOlderOwner(identity, sender)
        rateLimited(identity, sender)
    {
        delete owners[identity][owner];
        LogOwnerRemoved(identity, owner, sender);
    }
 
    /// @dev Allows an owner to change the recoveryKey instantly
    function changeRecovery(address sender, Proxy identity, address recoveryKey) public
        onlyAuthorized
        onlyOlderOwner(identity, sender)
        rateLimited(identity, sender)
        validAddress(recoveryKey)
    {
        recoveryKeys[identity] = recoveryKey;
        LogRecoveryChanged(identity, recoveryKey, sender);
    }
 
    /// @dev Allows an owner to begin process of transfering proxy to new IdentityManager
    function initiateMigration(address sender, Proxy identity, address newIdManager) public
        onlyAuthorized
        onlyOlderOwner(identity, sender)
    {
        migrationInitiated[identity] = now;
        migrationNewAddress[identity] = newIdManager;
        LogMigrationInitiated(identity, newIdManager, sender);
    }
 
    /// @dev Allows an owner to cancel the process of transfering proxy to new IdentityManager
    function cancelMigration(address sender, Proxy identity) public
        onlyAuthorized
        onlyOwner(identity, sender)
    {
        address canceledManager = migrationNewAddress[identity];
        delete migrationInitiated[identity];
        delete migrationNewAddress[identity];
        LogMigrationCanceled(identity, canceledManager, sender);
    }
 
    /// @dev Allows an owner to finalize and completly transfer proxy to new IdentityManager
    /// Note: 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) onlyAuthorized onlyOlderOwner(identity, sender) {
        require(migrationInitiated[identity] != 0 && migrationInitiated[identity] + adminTimeLock < now);
        address newIdManager = migrationNewAddress[identity];
        delete migrationInitiated[identity];
        delete migrationNewAddress[identity];
        identity.transfer(newIdManager);
        LogMigrationFinalized(identity, newIdManager, sender);
    }
 
    //Checks that address a is the first input in msg.data.
    //Has very minimal gas overhead.
    function checkMessageData(address a) internal constant returns (bool t) {
        Iif (msg.data.length < 36) return false;
        assembly {
            let mask := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
            t := eq(a, and(mask, calldataload(4)))
        }
    }
 
    function isOwner(address identity, address owner) public constant returns (bool) {
        return (owners[identity][owner] > 0 && (owners[identity][owner] + userTimeLock) <= now);
    }
 
    function isOlderOwner(address identity, address owner) public constant returns (bool) {
        return (owners[identity][owner] > 0 && (owners[identity][owner] + adminTimeLock) <= now);
    }
 
    function isRecovery(address identity, address recoveryKey) public constant returns (bool) {
        return recoveryKeys[identity] == recoveryKey;
    }
}