/** * Source Code first verified at https://etherscan.io on Wednesday, March 20, 2019 (UTC) */ pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } contract PauserRole { using Roles for Roles.Role; event PauserAdded(address indexed account); event PauserRemoved(address indexed account); Roles.Role private _pausers; constructor () internal { _addPauser(msg.sender); } modifier onlyPauser() { require(isPauser(msg.sender)); _; } function isPauser(address account) public view returns (bool) { return _pausers.has(account); } function addPauser(address account) public onlyPauser { _addPauser(account); } function renouncePauser() public { _removePauser(msg.sender); } function _addPauser(address account) internal { _pausers.add(account); emit PauserAdded(account); } function _removePauser(address account) internal { _pausers.remove(account); emit PauserRemoved(account); } } /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is PauserRole { event Paused(address account); event Unpaused(address account); bool private _paused; constructor () internal { _paused = false; } /** * @return true if the contract is paused, false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!_paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(_paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyPauser whenNotPaused { _paused = true; emit Paused(msg.sender); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyPauser whenPaused { _paused = false; emit Unpaused(msg.sender); } } /** @title ProofBox. */ contract ProofBox is Ownable, Pausable { struct Device { uint index; address deviceOwner; address txOriginator; } mapping (bytes32 => Device) private deviceMap; mapping (address => bool) public authorized; bytes32[] public deviceIds; event deviceCreated(bytes32 indexed deviceId, address indexed deviceOwner); event txnCreated(bytes32 indexed deviceId, address indexed txnOriginator); event deviceProof(bytes32 indexed deviceId, address indexed deviceOwner); event deviceTransfer(bytes32 indexed deviceId, address indexed fromOwner, address indexed toOwner); event deviceMessage(bytes32 indexed deviceId, address indexed deviceOwner, address indexed txnOriginator, string messageToWrite); event deviceDestruct(bytes32 indexed deviceId, address indexed deviceOwner); event ipfsHashtoAddress(bytes32 indexed deviceId, address indexed ownerAddress, string ipfskey); /** @dev Checks to see if device exist * @param _deviceId ID of the device. * @return isIndeed True if the device ID exists. */ function isDeviceId(bytes32 _deviceId) public view returns(bool isIndeed) { if(deviceIds.length == 0) return false; return (deviceIds[deviceMap[_deviceId].index] == _deviceId); } /** @dev returns the index of stored deviceID * @param _deviceId ID of the device. * @return _index index of the device. */ function getDeviceId(bytes32 _deviceId) public view deviceIdExist(_deviceId) returns(uint _index) { return deviceMap[_deviceId].index; } /** @dev returns address of device owner * @param _deviceId ID of the device. * @return deviceOwner device owner's address */ function getOwnerByDevice(bytes32 _deviceId) public view returns (address deviceOwner){ return deviceMap[_deviceId].deviceOwner; } /** @dev returns up to 10 devices for the device owner * @return _deviceIds device ID's of the owner */ function getDevicesByOwner(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public view returns(bytes32[10] memory _deviceIds) { address signer = ecrecover(_message, _v, _r, _s); uint numDevices; bytes32[10] memory devicesByOwner; for(uint i = 0; i < deviceIds.length; i++) { if(addressEqual(deviceMap[deviceIds[i]].deviceOwner,signer)) { devicesByOwner[numDevices] = deviceIds[i]; if (numDevices == 10) { break; } numDevices++; } } return devicesByOwner; } /** @dev returns up to 10 transactions of device owner * @return _deviceIds device ID's of the msg.sender transactions */ function getDevicesByTxn(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public view returns(bytes32[10] memory _deviceIds) { address signer = ecrecover(_message, _v, _r, _s); uint numDevices; bytes32[10] memory devicesByTxOriginator; for(uint i = 0; i < deviceIds.length; i++) { if(addressEqual(deviceMap[deviceIds[i]].txOriginator,signer)) { devicesByTxOriginator[numDevices] = deviceIds[i]; if (numDevices == 10) { break; } numDevices++; } } return devicesByTxOriginator; } modifier deviceIdExist(bytes32 _deviceId){ require(isDeviceId(_deviceId)); _; } modifier deviceIdNotExist(bytes32 _deviceId){ require(!isDeviceId(_deviceId)); _; } modifier authorizedUser() { require(authorized[msg.sender] == true); _; } constructor() public { authorized[msg.sender]=true; } /** @dev when a new device ID is registered by a proxy owner by sending device owner signature * @param _deviceId ID of the device. * @return index of stored device */ function registerProof (bytes32 _deviceId, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() authorizedUser() deviceIdNotExist(_deviceId) returns(uint index) { address signer = ecrecover(_message, _v, _r, _s); deviceMap[_deviceId].deviceOwner = signer; deviceMap[_deviceId].txOriginator = signer; deviceMap[_deviceId].index = deviceIds.push(_deviceId)-1; emit deviceCreated(_deviceId, signer); return deviceIds.length-1; } /** @dev returns true if delete is successful * @param _deviceId ID of the device. * @return bool delete */ function destructProof(bytes32 _deviceId, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() authorizedUser() deviceIdExist(_deviceId) returns(bool success) { address signer = ecrecover(_message, _v, _r, _s); require(deviceMap[_deviceId].deviceOwner == signer); uint rowToDelete = deviceMap[_deviceId].index; bytes32 keyToMove = deviceIds[deviceIds.length-1]; deviceIds[rowToDelete] = keyToMove; deviceMap[keyToMove].index = rowToDelete; deviceIds.length--; emit deviceDestruct(_deviceId, signer); return true; } /** @dev returns request transfer of device * @param _deviceId ID of the device. * @return index of stored device */ function requestTransfer(bytes32 _deviceId, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() deviceIdExist(_deviceId) authorizedUser() returns(uint index) { address signer = ecrecover(_message, _v, _r, _s); deviceMap[_deviceId].txOriginator=signer; emit txnCreated(_deviceId, signer); return deviceMap[_deviceId].index; } /** @dev returns approve transfer of device * @param _deviceId ID of the device. * @return bool approval */ function approveTransfer (bytes32 _deviceId, address newOwner, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() deviceIdExist(_deviceId) authorizedUser() returns(bool) { address signer = ecrecover(_message, _v, _r, _s); require(deviceMap[_deviceId].deviceOwner == signer); require(deviceMap[_deviceId].txOriginator == newOwner); deviceMap[_deviceId].deviceOwner=newOwner; emit deviceTransfer(_deviceId, signer, deviceMap[_deviceId].deviceOwner); return true; } /** @dev returns write message success * @param _deviceId ID of the device. * @return bool true when write message is successful */ function writeMessage (bytes32 _deviceId, string memory messageToWrite, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() deviceIdExist(_deviceId) authorizedUser() returns(bool) { address signer = ecrecover(_message, _v, _r, _s); require(deviceMap[_deviceId].deviceOwner == signer); emit deviceMessage(_deviceId, deviceMap[_deviceId].deviceOwner, signer, messageToWrite); return true; } /** @dev returns request proof of device * @param _deviceId ID of the device. * @return _index info of that device */ function requestProof(bytes32 _deviceId, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() deviceIdExist(_deviceId) authorizedUser() returns(uint _index) { address signer = ecrecover(_message, _v, _r, _s); deviceMap[_deviceId].txOriginator=signer; emit txnCreated(_deviceId, signer); return deviceMap[_deviceId].index; } /** @dev returns approve proof of device * @param _deviceId ID of the device. * @return bool - approval */ function approveProof(bytes32 _deviceId, bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) public whenNotPaused() deviceIdExist(_deviceId) authorizedUser() returns(bool) { address signer = ecrecover(_message, _v, _r, _s); deviceMap[_deviceId].txOriginator=signer; require(deviceMap[_deviceId].deviceOwner == signer); emit deviceProof(_deviceId, signer); return true; } /** @dev updates IPFS hash into device owner public address * @param ipfskey - ipfs hash for attachment. */ function emitipfskey(bytes32 _deviceId, address ownerAddress, string memory ipfskey) public whenNotPaused() deviceIdExist(_deviceId) authorizedUser() { emit ipfsHashtoAddress(_deviceId, ownerAddress, ipfskey); } /** @dev Updates Authorization status of an address for executing functions * on this contract * @param target Address that will be authorized or not authorized * @param isAuthorized New authorization status of address */ function changeAuthStatus(address target, bool isAuthorized) public whenNotPaused() onlyOwner() { authorized[target] = isAuthorized; } /** @dev Updates Authorization status of an address for executing functions * on this contract * @param targets Address that will be authorized or not authorized in bulk * @param isAuthorized New registration status of address */ function changeAuthStatuses(address[] memory targets, bool isAuthorized) public whenNotPaused() onlyOwner() { for (uint i = 0; i < targets.length; i++) { changeAuthStatus(targets[i], isAuthorized); } } /* NOTE: We explicitly do not define a fallback function, because there are no ethers received by any funtion on this contract */ //Helper Functions /** @dev compares two String equal or not * @param a first string, b second string. * @return bool true if match */ function bytesEqual(bytes32 a, bytes32 b) private pure returns (bool) { return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); } /** @dev compares two address equal or not * @param a first address, b second address. * @return bool true if match */ function addressEqual(address a, address b) private pure returns (bool) { return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); } }