From 555e58b5d9142f8f36e19d9eff0c567b55660ae8 Mon Sep 17 00:00:00 2001 From: mirathewhite Date: Wed, 8 Aug 2018 14:28:14 -0400 Subject: [PATCH 1/2] checkDiff validates Etherscan source code compared to local files. --- validate/FiatTokenProxy.etherscan | 329 ++++++++++++++++ validate/FiatTokenV1.etherscan | 606 ++++++++++++++++++++++++++++++ validate/README.checkDiff.md | 53 +++ validate/contractDiff.js | 118 ++++++ 4 files changed, 1106 insertions(+) create mode 100644 validate/FiatTokenProxy.etherscan create mode 100644 validate/FiatTokenV1.etherscan create mode 100644 validate/README.checkDiff.md create mode 100644 validate/contractDiff.js diff --git a/validate/FiatTokenProxy.etherscan b/validate/FiatTokenProxy.etherscan new file mode 100644 index 000000000..10c84dded --- /dev/null +++ b/validate/FiatTokenProxy.etherscan @@ -0,0 +1,329 @@ +pragma solidity ^0.4.24; + +// File: zos-lib/contracts/upgradeability/Proxy.sol + +/** + * @title Proxy + * @dev Implements delegation of calls to other contracts, with proper + * forwarding of return values and bubbling of failures. + * It defines a fallback function that delegates all calls to the address + * returned by the abstract _implementation() internal function. + */ +contract Proxy { + /** + * @dev Fallback function. + * Implemented entirely in `_fallback`. + */ + function () payable external { + _fallback(); + } + + /** + * @return The Address of the implementation. + */ + function _implementation() internal view returns (address); + + /** + * @dev Delegates execution to an implementation contract. + * This is a low level function that doesn't return to its internal call site. + * It will return to the external caller whatever the implementation returns. + * @param implementation Address to delegate. + */ + function _delegate(address implementation) internal { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize) + + switch result + // delegatecall returns 0 on error. + case 0 { revert(0, returndatasize) } + default { return(0, returndatasize) } + } + } + + /** + * @dev Function that is run as the first thing in the fallback function. + * Can be redefined in derived contracts to add functionality. + * Redefinitions must call super._willFallback(). + */ + function _willFallback() internal { + } + + /** + * @dev fallback implementation. + * Extracted to enable manual triggering. + */ + function _fallback() internal { + _willFallback(); + _delegate(_implementation()); + } +} + +// File: openzeppelin-solidity/contracts/AddressUtils.sol + +/** + * Utility library of inline functions on addresses + */ +library AddressUtils { + + /** + * Returns whether the target address is a contract + * @dev This function will return false if invoked during the constructor of a contract, + * as the code is not actually created until after the constructor finishes. + * @param addr address to check + * @return whether the target address is a contract + */ + function isContract(address addr) internal view returns (bool) { + uint256 size; + // XXX Currently there is no better way to check if there is a contract in an address + // than to check the size of the code at that address. + // See https://ethereum.stackexchange.com/a/14016/36603 + // for more details about how this works. + // TODO Check this again before the Serenity release, because all addresses will be + // contracts then. + // solium-disable-next-line security/no-inline-assembly + assembly { size := extcodesize(addr) } + return size > 0; + } + +} + +// File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol + +/** + * @title UpgradeabilityProxy + * @dev This contract implements a proxy that allows to change the + * implementation address to which it will delegate. + * Such a change is called an implementation upgrade. + */ +contract UpgradeabilityProxy is Proxy { + /** + * @dev Emitted when the implementation is upgraded. + * @param implementation Address of the new implementation. + */ + event Upgraded(address implementation); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is + * validated in the constructor. + */ + bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; + + /** + * @dev Contract constructor. + * @param _implementation Address of the initial implementation. + */ + constructor(address _implementation) public { + assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation")); + + _setImplementation(_implementation); + } + + /** + * @dev Returns the current implementation. + * @return Address of the current implementation + */ + function _implementation() internal view returns (address impl) { + bytes32 slot = IMPLEMENTATION_SLOT; + assembly { + impl := sload(slot) + } + } + + /** + * @dev Upgrades the proxy to a new implementation. + * @param newImplementation Address of the new implementation. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation address of the proxy. + * @param newImplementation Address of the new implementation. + */ + function _setImplementation(address newImplementation) private { + require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); + + bytes32 slot = IMPLEMENTATION_SLOT; + + assembly { + sstore(slot, newImplementation) + } + } +} + +// File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol + +/** + * @title AdminUpgradeabilityProxy + * @dev This contract combines an upgradeability proxy with an authorization + * mechanism for administrative tasks. + * All external functions in this contract must be guarded by the + * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity + * feature proposal that would enable this to be done automatically. + */ +contract AdminUpgradeabilityProxy is UpgradeabilityProxy { + /** + * @dev Emitted when the administration has been transferred. + * @param previousAdmin Address of the previous admin. + * @param newAdmin Address of the new admin. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is + * validated in the constructor. + */ + bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b; + + /** + * @dev Modifier to check whether the `msg.sender` is the admin. + * If it is, it will run the function. Otherwise, it will delegate the call + * to the implementation. + */ + modifier ifAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + /** + * Contract constructor. + * It sets the `msg.sender` as the proxy administrator. + * @param _implementation address of the initial implementation. + */ + constructor(address _implementation) UpgradeabilityProxy(_implementation) public { + assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin")); + + _setAdmin(msg.sender); + } + + /** + * @return The address of the proxy admin. + */ + function admin() external view ifAdmin returns (address) { + return _admin(); + } + + /** + * @return The address of the implementation. + */ + function implementation() external view ifAdmin returns (address) { + return _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * Only the current admin can call this function. + * @param newAdmin Address to transfer proxy administration to. + */ + function changeAdmin(address newAdmin) external ifAdmin { + require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); + emit AdminChanged(_admin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev Upgrade the backing implementation of the proxy. + * Only the admin can call this function. + * @param newImplementation Address of the new implementation. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the backing implementation of the proxy and call a function + * on the new implementation. + * This is useful to initialize the proxied contract. + * @param newImplementation Address of the new implementation. + * @param data Data to send as msg.data in the low level call. + * It should include the signature and the parameters of the function to be + * called, as described in + * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding. + */ + function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin { + _upgradeTo(newImplementation); + require(address(this).call.value(msg.value)(data)); + } + + /** + * @return The admin slot. + */ + function _admin() internal view returns (address adm) { + bytes32 slot = ADMIN_SLOT; + assembly { + adm := sload(slot) + } + } + + /** + * @dev Sets the address of the proxy admin. + * @param newAdmin Address of the new proxy admin. + */ + function _setAdmin(address newAdmin) internal { + bytes32 slot = ADMIN_SLOT; + + assembly { + sstore(slot, newAdmin) + } + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal { + require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); + super._willFallback(); + } +} + +// File: contracts/FiatTokenProxy.sol + +/** +* Copyright CENTRE SECZ 2018 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is furnished to +* do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.24; + + +/** + * @title FiatTokenProxy + * @dev This contract proxies FiatToken calls and enables FiatToken upgrades +*/ +contract FiatTokenProxy is AdminUpgradeabilityProxy { + constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) { + } +} \ No newline at end of file diff --git a/validate/FiatTokenV1.etherscan b/validate/FiatTokenV1.etherscan new file mode 100644 index 000000000..d9c496db4 --- /dev/null +++ b/validate/FiatTokenV1.etherscan @@ -0,0 +1,606 @@ +pragma solidity ^0.4.24; + +// File: contracts/Ownable.sol + +/** +* Copyright CENTRE SECZ 2018 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is furnished to +* do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.24; + +/** + * @title Ownable + * @dev The Ownable contract from https://github.com/zeppelinos/labs/blob/master/upgradeability_ownership/contracts/ownership/Ownable.sol + * branch: master commit: 3887ab77b8adafba4a26ace002f3a684c1a3388b modified to: + * 1) Add emit prefix to OwnershipTransferred event (7/13/18) + * 2) Replace constructor with constructor syntax (7/13/18) + * 3) consolidate OwnableStorage into this contract + */ +contract Ownable { + + // Owner of the contract + address private _owner; + + /** + * @dev Event to show ownership has been transferred + * @param previousOwner representing the address of the previous owner + * @param newOwner representing the address of the new owner + */ + event OwnershipTransferred(address previousOwner, address newOwner); + + /** + * @dev The constructor sets the original owner of the contract to the sender account. + */ + constructor() public { + setOwner(msg.sender); + } + + /** + * @dev Tells the address of the owner + * @return the address of the owner + */ + function owner() public view returns (address) { + return _owner; + } + + /** + * @dev Sets a new owner address + */ + function setOwner(address newOwner) internal { + _owner = newOwner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner()); + _; + } + + /** + * @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 { + require(newOwner != address(0)); + emit OwnershipTransferred(owner(), newOwner); + setOwner(newOwner); + } +} + +// File: contracts/Blacklistable.sol + +/** +* Copyright CENTRE SECZ 2018 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is furnished to +* do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.24; + + +/** + * @title Blacklistable Token + * @dev Allows accounts to be blacklisted by a "blacklister" role +*/ +contract Blacklistable is Ownable { + + address public blacklister; + mapping(address => bool) internal blacklisted; + + event Blacklisted(address indexed _account); + event UnBlacklisted(address indexed _account); + event BlacklisterChanged(address indexed newBlacklister); + + /** + * @dev Throws if called by any account other than the blacklister + */ + modifier onlyBlacklister() { + require(msg.sender == blacklister); + _; + } + + /** + * @dev Throws if argument account is blacklisted + * @param _account The address to check + */ + modifier notBlacklisted(address _account) { + require(blacklisted[_account] == false); + _; + } + + /** + * @dev Checks if account is blacklisted + * @param _account The address to check + */ + function isBlacklisted(address _account) public view returns (bool) { + return blacklisted[_account]; + } + + /** + * @dev Adds account to blacklist + * @param _account The address to blacklist + */ + function blacklist(address _account) public onlyBlacklister { + blacklisted[_account] = true; + emit Blacklisted(_account); + } + + /** + * @dev Removes account from blacklist + * @param _account The address to remove from the blacklist + */ + function unBlacklist(address _account) public onlyBlacklister { + blacklisted[_account] = false; + emit UnBlacklisted(_account); + } + + function updateBlacklister(address _newBlacklister) public onlyOwner { + require(_newBlacklister != address(0)); + blacklister = _newBlacklister; + emit BlacklisterChanged(blacklister); + } +} + +// File: contracts/Pausable.sol + +/** +* Copyright CENTRE SECZ 2018 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is furnished to +* do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.24; + + +/** + * @title Pausable + * @dev Base contract which allows children to implement an emergency stop mechanism. + * Based on openzeppelin tag v1.10.0 commit: feb665136c0dae9912e08397c1a21c4af3651ef3 + * Modifications: + * 1) Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018) + * 2) Removed whenNotPause/whenPaused from pause/unpause (6/14/2018) + * 3) Removed whenPaused (6/14/2018) + * 4) Switches ownable library to use zeppelinos (7/12/18) + * 5) Remove constructor (7/13/18) + */ +contract Pausable is Ownable { + event Pause(); + event Unpause(); + event PauserChanged(address indexed newAddress); + + + address public pauser; + bool public paused = false; + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + */ + modifier whenNotPaused() { + require(!paused); + _; + } + + /** + * @dev throws if called by any account other than the pauser + */ + modifier onlyPauser() { + require(msg.sender == pauser); + _; + } + + /** + * @dev called by the owner to pause, triggers stopped state + */ + function pause() onlyPauser public { + paused = true; + emit Pause(); + } + + /** + * @dev called by the owner to unpause, returns to normal state + */ + function unpause() onlyPauser public { + paused = false; + emit Unpause(); + } + + /** + * @dev update the pauser role + */ + function updatePauser(address _newPauser) onlyOwner public { + require(_newPauser != address(0)); + pauser = _newPauser; + emit PauserChanged(pauser); + } + +} + +// File: openzeppelin-solidity/contracts/math/SafeMath.sol + +/** + * @title SafeMath + * @dev Math operations with safety checks that throw on error + */ +library SafeMath { + + /** + * @dev Multiplies two numbers, throws on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { + // Gas optimization: this is cheaper than asserting 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + c = a * b; + assert(c / a == b); + return c; + } + + /** + * @dev Integer division of two numbers, truncating the quotient. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // assert(b > 0); // Solidity automatically throws when dividing by 0 + // uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + return a / b; + } + + /** + * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + assert(b <= a); + return a - b; + } + + /** + * @dev Adds two numbers, throws on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256 c) { + c = a + b; + assert(c >= a); + return c; + } +} + +// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol + +/** + * @title ERC20Basic + * @dev Simpler version of ERC20 interface + * See https://github.com/ethereum/EIPs/issues/179 + */ +contract ERC20Basic { + function totalSupply() public view returns (uint256); + function balanceOf(address who) public view returns (uint256); + function transfer(address to, uint256 value) public returns (bool); + event Transfer(address indexed from, address indexed to, uint256 value); +} + +// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20 is ERC20Basic { + function allowance(address owner, address spender) + public view returns (uint256); + + function transferFrom(address from, address to, uint256 value) + public returns (bool); + + function approve(address spender, uint256 value) public returns (bool); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); +} + +// File: contracts/FiatTokenV1.sol + +/** +* Copyright CENTRE SECZ 2018 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is furnished to +* do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.24; + + + + + + +/** + * @title FiatToken + * @dev ERC20 Token backed by fiat reserves + */ +contract FiatTokenV1 is Ownable, ERC20, Pausable, Blacklistable { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + string public currency; + address public masterMinter; + bool internal initialized; + + mapping(address => uint256) internal balances; + mapping(address => mapping(address => uint256)) internal allowed; + uint256 internal totalSupply_ = 0; + mapping(address => bool) internal minters; + mapping(address => uint256) internal minterAllowed; + + event Mint(address indexed minter, address indexed to, uint256 amount); + event Burn(address indexed burner, uint256 amount); + event MinterConfigured(address indexed minter, uint256 minterAllowedAmount); + event MinterRemoved(address indexed oldMinter); + event MasterMinterChanged(address indexed newMasterMinter); + + function initialize( + string _name, + string _symbol, + string _currency, + uint8 _decimals, + address _masterMinter, + address _pauser, + address _blacklister, + address _owner + ) public { + require(!initialized); + require(_masterMinter != address(0)); + require(_pauser != address(0)); + require(_blacklister != address(0)); + require(_owner != address(0)); + + name = _name; + symbol = _symbol; + currency = _currency; + decimals = _decimals; + masterMinter = _masterMinter; + pauser = _pauser; + blacklister = _blacklister; + setOwner(_owner); + initialized = true; + } + + /** + * @dev Throws if called by any account other than a minter + */ + modifier onlyMinters() { + require(minters[msg.sender] == true); + _; + } + + /** + * @dev Function to mint tokens + * @param _to The address that will receive the minted tokens. + * @param _amount The amount of tokens to mint. Must be less than or equal to the minterAllowance of the caller. + * @return A boolean that indicates if the operation was successful. + */ + function mint(address _to, uint256 _amount) whenNotPaused onlyMinters notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) { + require(_to != address(0)); + require(_amount > 0); + + uint256 mintingAllowedAmount = minterAllowed[msg.sender]; + require(_amount <= mintingAllowedAmount); + + totalSupply_ = totalSupply_.add(_amount); + balances[_to] = balances[_to].add(_amount); + minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount); + emit Mint(msg.sender, _to, _amount); + emit Transfer(0x0, _to, _amount); + return true; + } + + /** + * @dev Throws if called by any account other than the masterMinter + */ + modifier onlyMasterMinter() { + require(msg.sender == masterMinter); + _; + } + + /** + * @dev Get minter allowance for an account + * @param minter The address of the minter + */ + function minterAllowance(address minter) public view returns (uint256) { + return minterAllowed[minter]; + } + + /** + * @dev Checks if account is a minter + * @param account The address to check + */ + function isMinter(address account) public view returns (bool) { + return minters[account]; + } + + /** + * @dev Get allowed amount for an account + * @param owner address The account owner + * @param spender address The account spender + */ + function allowance(address owner, address spender) public view returns (uint256) { + return allowed[owner][spender]; + } + + /** + * @dev Get totalSupply of token + */ + function totalSupply() public view returns (uint256) { + return totalSupply_; + } + + /** + * @dev Get token balance of an account + * @param account address The account + */ + function balanceOf(address account) public view returns (uint256) { + return balances[account]; + } + + /** + * @dev Adds blacklisted check to approve + * @return True if the operation was successful. + */ + function approve(address _spender, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_spender) public returns (bool) { + allowed[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + return true; + } + + /** + * @dev Transfer tokens from one address to another. + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @return bool success + */ + function transferFrom(address _from, address _to, uint256 _value) whenNotPaused notBlacklisted(_to) notBlacklisted(msg.sender) notBlacklisted(_from) public returns (bool) { + require(_to != address(0)); + require(_value <= balances[_from]); + require(_value <= allowed[_from][msg.sender]); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + emit Transfer(_from, _to, _value); + return true; + } + + /** + * @dev transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + * @return bool success + */ + function transfer(address _to, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) { + require(_to != address(0)); + require(_value <= balances[msg.sender]); + + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + emit Transfer(msg.sender, _to, _value); + return true; + } + + /** + * @dev Function to add/update a new minter + * @param minter The address of the minter + * @param minterAllowedAmount The minting amount allowed for the minter + * @return True if the operation was successful. + */ + function configureMinter(address minter, uint256 minterAllowedAmount) whenNotPaused onlyMasterMinter public returns (bool) { + minters[minter] = true; + minterAllowed[minter] = minterAllowedAmount; + emit MinterConfigured(minter, minterAllowedAmount); + return true; + } + + /** + * @dev Function to remove a minter + * @param minter The address of the minter to remove + * @return True if the operation was successful. + */ + function removeMinter(address minter) onlyMasterMinter public returns (bool) { + minters[minter] = false; + minterAllowed[minter] = 0; + emit MinterRemoved(minter); + return true; + } + + /** + * @dev allows a minter to burn some of its own tokens + * Validates that caller is a minter and that sender is not blacklisted + * amount is less than or equal to the minter's account balance + * @param _amount uint256 the amount of tokens to be burned + */ + function burn(uint256 _amount) whenNotPaused onlyMinters notBlacklisted(msg.sender) public { + uint256 balance = balances[msg.sender]; + require(_amount > 0); + require(balance >= _amount); + + totalSupply_ = totalSupply_.sub(_amount); + balances[msg.sender] = balance.sub(_amount); + emit Burn(msg.sender, _amount); + emit Transfer(msg.sender, address(0), _amount); + } + + function updateMasterMinter(address _newMasterMinter) onlyOwner public { + require(_newMasterMinter != address(0)); + masterMinter = _newMasterMinter; + emit MasterMinterChanged(masterMinter); + } +} \ No newline at end of file diff --git a/validate/README.checkDiff.md b/validate/README.checkDiff.md new file mode 100644 index 000000000..8c8ab3472 --- /dev/null +++ b/validate/README.checkDiff.md @@ -0,0 +1,53 @@ +# Overview +The `checkDiff` script compares source code uploaded to Etherscan to source +code on the local machine. + +The souce code in Etherscan is a concatenation of several files. The script reads +comments inside the files to determine which local files were used, then it reads the +file and reconstructs the expected source code. + +The script displays a `diff` between the actual and expected code. The difference +should fall in the following categories: + +1. Comments / whitespace +2. The local code has `import` statements not included in Etherscan because the files were +explictly concatenated into a master file. +3. The local code has extra `pragma solidity ^0.4.24` statements at the head of each file, +while Etherscan should have just one at the top. + +At the end of the run, `checkDiff` will output which files it was able to process. Success +means that the Etherscan file could be read AND all the included files could be read. +Validity should be determined by actually looking at the diffs. + +# Running checkDiff.js +To run the script, type + +`node validate/contractDiff.js ... ` + +Where the `filename` is the location of source code downloaded from Etherscan. +Copies of `FiatTokenProxy` and `FiatTokenV1` are included for testing purposes. +You can test them: + +`node validate/contractDiff.js validate/FiatTokenProxy.etherscan validate/FiatTokenV1.etherscan` + +# Finding code on Etherscan +1. Go to [https://etherscan.io](https://etherscan.io) +2. Enter the contract address in the search bar. +3. Click on the `Code` tab. + +# Uploading to Etherscan +When uploading source code to Etherscan, you will need to combine several file. +Before the start of an included file, add the comment: + +`// File: ` + +For example, to include `contracts/Ownable.sol`, add the comment + +`// File: contracts/Ownable.sol` + +For Open Zeppelin files, omit the `node_modules` directory. For example: + +`// File: openzeppelin-solidity/contracts/math/SafeMath.sol` + +`// File: zos-lib/contracts/upgradeability/Proxy.sol` + diff --git a/validate/contractDiff.js b/validate/contractDiff.js new file mode 100644 index 000000000..2540ae4d2 --- /dev/null +++ b/validate/contractDiff.js @@ -0,0 +1,118 @@ +var fs = require('fs'); +const chalk = require("chalk"); +const diff = require("diff"); + +function readFileSync(filename) { + return fs.readFileSync(filename, "utf8"); +} + +function writeFileSync(filename, text) { + fs.writeFileSync(filename, text); +} + +function getFilenamesFromCode(code) { + // find all lines with prefix "// File:" + var filenames = code.match(/\/\/\s*File:\s*\S+\n/gi); + for (var i = 0; i < filenames.length; i++) { + // remove prefix "// File: ". + filenames[i] = filenames[i].replace(/\/\/\s*File:\s*/i, ""); + filenames[i] = filenames[i].replace(/\s+/i, ""); + + // directory openzeppelin-solidity is inside node_modules + if(filenames[i].match(/openzeppelin-solidity/)){ + filenames[i] = "node_modules/" + filenames[i] + } + + // directory zos-lib is inside node_modules + if(filenames[i].match(/zos-lib/)){ + filenames[i] = "node_modules/" + filenames[i] + } + + // add ./ prefix + filenames[i] = "./" + filenames[i]; + console.log(filenames[i]); + } + return filenames; +} + +function createCodeFile(filenames) { + var code = ""; + for (var i = 0; i < filenames.length; i++) { + try { + console.log("Reading file " + filenames[i]); + } catch (err) { + console.log("Could not read file " + filenames[i]); + return ""; + } + code = code + readFileSync(filenames[i]) + "\n"; + } + return code; +} + +function diffText(code1, code2) { + const diffOutput = diff.diffTrimmedLines(code1, code2); + for (var i = 0; i < diffOutput.length; i++) { + var diffLine = diffOutput[i]; + if (diffLine.added) { + process.stdout.write(chalk.green(`+ ${diffLine.value}`)); + } else if (diffLine.removed) { + process.stdout.write(chalk.red(`- ${diffLine.value}`)); + } + } +} + +function removeExtraComments(code) { + var modified = code.replace(/\/\/\s*File:\s*\S+\n/ig, ""); + return modified; +} + +function validate(filename) { + var code = readFileSync(filename); + + var filenames = getFilenamesFromCode(code); + var expectedCode = createCodeFile(filenames); + + code = removeExtraComments(code); + diffText(code, expectedCode); +} + +function printUsage() { + console.log("node contractDiff .... "); +} + +function main() { + if(process.argv.length < 3) { + printUsage(); + return; + } + + var fail = 0; + var total = process.argv.length -2; + var goodFiles = ""; + var badFiles = ""; + + for(var i=2; i Date: Fri, 10 Aug 2018 10:03:05 -0400 Subject: [PATCH 2/2] fixed typos and output formatting --- ...EADME.checkDiff.md => README.contractDiff.md} | 4 ++-- validate/contractDiff.js | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) rename validate/{README.checkDiff.md => README.contractDiff.md} (93%) diff --git a/validate/README.checkDiff.md b/validate/README.contractDiff.md similarity index 93% rename from validate/README.checkDiff.md rename to validate/README.contractDiff.md index 8c8ab3472..9655d5938 100644 --- a/validate/README.checkDiff.md +++ b/validate/README.contractDiff.md @@ -2,7 +2,7 @@ The `checkDiff` script compares source code uploaded to Etherscan to source code on the local machine. -The souce code in Etherscan is a concatenation of several files. The script reads +The source code in Etherscan is a concatenation of several files. The script reads comments inside the files to determine which local files were used, then it reads the file and reconstructs the expected source code. @@ -11,7 +11,7 @@ should fall in the following categories: 1. Comments / whitespace 2. The local code has `import` statements not included in Etherscan because the files were -explictly concatenated into a master file. +explicitly concatenated into a master file. 3. The local code has extra `pragma solidity ^0.4.24` statements at the head of each file, while Etherscan should have just one at the top. diff --git a/validate/contractDiff.js b/validate/contractDiff.js index 2540ae4d2..d0daf2949 100644 --- a/validate/contractDiff.js +++ b/validate/contractDiff.js @@ -105,13 +105,15 @@ function main() { } } - console.log(); - console.log() - process.stdout.write(chalk.green("Successfully processed " + (total-fail) + " files.\n")); - process.stdout.write(chalk.green(goodFiles)); - process.stdout.write(chalk.red("\n\nFailed to process " + fail + " files.\n")); - process.stdout.write(chalk.red(badFiles)); - console.log(); + if(total - fail > 0) { + process.stdout.write(chalk.green("\n\nSuccessfully processed " + (total-fail) + " files.\n")); + process.stdout.write(chalk.green(goodFiles)); + } + + if(fail > 0) { + process.stdout.write(chalk.red("\n\nFailed to process " + fail + " files.\n")); + process.stdout.write(chalk.red(badFiles + "\n")); + } } main();