make contracts upgradeable

This commit is contained in:
Roman Storm 2018-03-13 23:21:54 -07:00
parent 5eaa480b23
commit fb28fd067c
15 changed files with 1156 additions and 536 deletions

View File

@ -1,10 +0,0 @@
pragma solidity ^0.4.19;
contract BridgeDeploymentAddressStorage {
uint256 public deployedAtBlock;
function BridgeDeploymentAddressStorage() public {
deployedAtBlock = block.number;
}
}

View File

@ -1,165 +0,0 @@
pragma solidity 0.4.19;
import "./libraries/SafeMath.sol";
import "./libraries/Helpers.sol";
import "./libraries/Message.sol";
import "./IBridgeValidators.sol";
import "./Validatable.sol";
import "./BridgeDeploymentAddressStorage.sol";
import "./IBurnableMintableERC677Token.sol";
import "./ERC677Receiver.sol";
contract ForeignBridge is ERC677Receiver, Validatable, BridgeDeploymentAddressStorage {
using SafeMath for uint256;
uint256 public gasLimitDepositRelay;
uint256 public gasLimitWithdrawConfirm;
uint256 homeGasPrice = 1000000000 wei;
uint256 public foreignDailyLimit;
mapping (bytes32 => bytes) messages;
mapping (bytes32 => bytes) signatures;
mapping (bytes32 => bool) messages_signed;
mapping (bytes32 => uint256) num_messages_signed;
mapping (bytes32 => bool) deposits_signed;
mapping (bytes32 => uint256) num_deposits_signed;
mapping (uint256 => uint256) totalSpentPerDay;
IBurnableMintableERC677Token public erc677token;
/// triggered when relay of deposit from HomeBridge is complete
event Deposit(address recipient, uint value);
/// Event created on money withdraw.
event Withdraw(address recipient, uint256 value, uint256 homeGasPrice);
/// Collected signatures which should be relayed to home chain.
event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash);
event GasConsumptionLimitsUpdated(uint256 gasLimitDepositRelay, uint256 gasLimitWithdrawConfirm);
event SignedForDeposit(address indexed signer, bytes32 message);
event SignedForWithdraw(address indexed signer, bytes32 message);
event DailyLimit(uint256 newLimit);
function ForeignBridge(
address _validatorContract,
address _erc677token,
uint256 _foreignDailyLimit
) public Validatable(_validatorContract) {
require(_foreignDailyLimit > 0);
erc677token = IBurnableMintableERC677Token(_erc677token);
foreignDailyLimit = _foreignDailyLimit;
}
function setGasLimitDepositRelay(uint256 _gas) public onlyOwner {
gasLimitDepositRelay = _gas;
GasConsumptionLimitsUpdated(gasLimitDepositRelay, gasLimitWithdrawConfirm);
}
function setGasLimitWithdrawConfirm(uint256 gas) public onlyOwner {
gasLimitWithdrawConfirm = gas;
GasConsumptionLimitsUpdated(gasLimitDepositRelay, gasLimitWithdrawConfirm);
}
function deposit(address recipient, uint value, bytes32 transactionHash) public onlyValidator {
require(erc677token != address(0x0));
// Protection from misbehaing authority
bytes32 hash_msg = keccak256(recipient, value, transactionHash);
bytes32 hash_sender = keccak256(msg.sender, hash_msg);
// Duplicated deposits
require(!deposits_signed[hash_sender]);
deposits_signed[hash_sender] = true;
uint256 signed = num_deposits_signed[hash_msg] + 1;
num_deposits_signed[hash_msg] = signed;
SignedForDeposit(msg.sender, transactionHash);
if (signed == validatorContract.requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
erc677token.mint(recipient, value);
Deposit(recipient, value);
}
}
function onTokenTransfer(address _from, uint256 _value, bytes _data) external returns(bool) {
require(erc677token != address(0x0));
require(msg.sender == address(erc677token));
require(withinLimit(_value));
totalSpentPerDay[getCurrentDay()] = totalSpentPerDay[getCurrentDay()].add(_value);
erc677token.burn(_value);
Withdraw(_from, _value, homeGasPrice);
return true;
}
/// Should be used as sync tool
///
/// Message is a message that should be relayed to main chain once authorities sign it.
///
/// for withdraw message contains:
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature(bytes signature, bytes message) public onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(msg.sender == MessageSigning.recoverAddressFromSignedMessage(signature, message));
require(message.length == 116);
bytes32 hash = keccak256(message);
bytes32 hash_sender = keccak256(msg.sender, hash);
uint signed = num_messages_signed[hash_sender] + 1;
if (signed > 1) {
// Duplicated signatures
require(!messages_signed[hash_sender]);
}
else {
// check if it will really reduce gas usage in case of the second transaction
// with the same hash
messages[hash] = message;
}
messages_signed[hash_sender] = true;
bytes32 sign_idx = keccak256(hash, (signed-1));
signatures[sign_idx] = signature;
num_messages_signed[hash_sender] = signed;
// TODO: this may cause troubles if requiredSignatures len is changed
SignedForWithdraw(msg.sender, hash);
if (signed == validatorContract.requiredSignatures()) {
CollectedSignatures(msg.sender, hash);
}
}
function signature(bytes32 hash, uint index) public view returns (bytes) {
bytes32 sign_idx = keccak256(hash, index);
return signatures[sign_idx];
}
/// Get message
function message(bytes32 hash) public view returns (bytes) {
return messages[hash];
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function setDailyLimit(uint256 _foreignDailyLimit) public onlyOwner {
require(_foreignDailyLimit > 0);
foreignDailyLimit = _foreignDailyLimit;
DailyLimit(foreignDailyLimit);
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay[getCurrentDay()].add(_amount);
return foreignDailyLimit >= nextLimit;
}
}

View File

@ -1,75 +0,0 @@
pragma solidity ^0.4.19;
import "./libraries/SafeMath.sol";
import "./libraries/Helpers.sol";
import "./libraries/Message.sol";
import "./IBridgeValidators.sol";
import "./Validatable.sol";
import "./BridgeDeploymentAddressStorage.sol";
contract HomeBridge is Validatable, BridgeDeploymentAddressStorage {
using SafeMath for uint256;
uint256 public gasLimitWithdrawRelay;
uint256 public homeDailyLimit;
mapping (uint256 => uint256) totalSpentPerDay;
mapping (bytes32 => bool) withdraws;
event GasConsumptionLimitsUpdated(uint256 gas);
event Deposit (address recipient, uint256 value);
event Withdraw (address recipient, uint256 value);
event DailyLimit(uint256 newLimit);
function HomeBridge (
address _validatorContract,
uint256 _homeDailyLimit
) public Validatable(_validatorContract) {
require(_homeDailyLimit > 0);
homeDailyLimit = _homeDailyLimit;
DailyLimit(homeDailyLimit);
}
/// Should be used to deposit money.
function () public payable {
require(msg.value > 0);
require(withinLimit(msg.value));
totalSpentPerDay[getCurrentDay()] = totalSpentPerDay[getCurrentDay()].add(msg.value);
Deposit(msg.sender, msg.value);
}
function setGasLimitWithdrawRelay(uint256 _gas) public onlyOwner {
gasLimitWithdrawRelay = _gas;
GasConsumptionLimitsUpdated(gasLimitWithdrawRelay);
}
function withdraw(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) public {
require(message.length == 116);
require(Helpers.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract));
address recipient = Message.getRecipient(message);
uint256 value = Message.getValue(message);
bytes32 hash = Message.getTransactionHash(message);
require(!withdraws[hash]);
// Order of operations below is critical to avoid TheDAO-like re-entry bug
withdraws[hash] = true;
// pay out recipient
recipient.transfer(value);
Withdraw(recipient, value);
}
function setDailyLimit(uint256 _homeDailyLimit) public onlyOwner {
require(_homeDailyLimit > 0);
homeDailyLimit = _homeDailyLimit;
DailyLimit(homeDailyLimit);
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay[getCurrentDay()].add(_amount);
return homeDailyLimit >= nextLimit;
}
}

View File

@ -1,22 +0,0 @@
pragma solidity ^0.4.19;
import "./IBridgeValidators.sol";
contract Validatable {
IBridgeValidators public validatorContract;
modifier onlyValidator() {
require(validatorContract.isValidator(msg.sender));
_;
}
modifier onlyOwner() {
require(validatorContract.currentOwner() == msg.sender);
_;
}
function Validatable(address _validatorContract) public {
require(_validatorContract != address(0));
validatorContract = IBridgeValidators(_validatorContract);
}
}

View File

@ -38,7 +38,7 @@ library Helpers {
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view returns (bool) {
uint8 requiredSignatures = _validatorContract.requiredSignatures();
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = MessageSigning.hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);

View File

@ -11,15 +11,17 @@ contract BridgeValidators is IBridgeValidators, Ownable, OwnedUpgradeabilityStor
event ValidatorAdded (address validator);
event ValidatorRemoved (address validator);
function BridgeValidators(uint8 _requiredSignatures, address[] _initialValidators) public Ownable() {
function initialize(uint256 _requiredSignatures, address[] _initialValidators, address _owner) public {
require(!isInitialized());
setOwner(_owner);
require(_requiredSignatures != 0);
require(_initialValidators.length >= _requiredSignatures);
setValidatorCount(_initialValidators.length);
for (uint i = 0; i < _initialValidators.length; i++) {
require(!isValidator(_initialValidators[i]) && _initialValidators[i] != address(0));
addValidator(_initialValidators[i]);
}
setRequiredSignatures(_requiredSignatures);
setInitialize(true);
}
function addValidator(address _validator) public onlyOwner {
@ -57,6 +59,10 @@ contract BridgeValidators is IBridgeValidators, Ownable, OwnedUpgradeabilityStor
return validators(_validator) == true;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function setValidatorCount(uint256 _validatorCount) private {
uintStorage[keccak256("validatorCount")] = _validatorCount;
}
@ -64,4 +70,8 @@ contract BridgeValidators is IBridgeValidators, Ownable, OwnedUpgradeabilityStor
function setValidator(address _validator, bool _status) private {
boolStorage[keccak256("validators", _validator)] = _status;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}

View File

@ -0,0 +1,241 @@
pragma solidity ^0.4.19;
import "../libraries/SafeMath.sol";
import "../libraries/Helpers.sol";
import "../libraries/Message.sol";
// import "./IBridgeValidators.sol";
import "./U_Validatable.sol";
// import "./BridgeDeploymentAddressStorage.sol";
import "../IBurnableMintableERC677Token.sol";
import "../ERC677Receiver.sol";
contract ForeignBridge is ERC677Receiver, Validatable {
using SafeMath for uint256;
/// triggered when relay of deposit from HomeBridge is complete
event Deposit(address recipient, uint value);
/// Event created on money withdraw.
event Withdraw(address recipient, uint256 value, uint256 homeGasPrice);
/// Collected signatures which should be relayed to home chain.
event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash);
event GasConsumptionLimitsUpdated(uint256 gasLimitDepositRelay, uint256 gasLimitWithdrawConfirm);
event SignedForDeposit(address indexed signer, bytes32 message);
event SignedForWithdraw(address indexed signer, bytes32 message);
event DailyLimit(uint256 newLimit);
function initialize(
address _validatorContract,
address _erc677token,
uint256 _foreignDailyLimit
) public {
require(!isInitialized());
require(_validatorContract != address(0));
require(_foreignDailyLimit > 0);
addressStorage[keccak256("validatorContract")] = _validatorContract;
setErc677token(_erc677token);
setForeignDailyLimit(_foreignDailyLimit);
uintStorage[keccak256("deployedAtBlock")] = block.number;
setInitialize(true);
}
function onTokenTransfer(address _from, uint256 _value, bytes _data) external returns(bool) {
require(address(erc677token()) != address(0x0));
require(msg.sender == address(erc677token()));
require(withinLimit(_value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value));
erc677token().burn(_value);
Withdraw(_from, _value, homeGasPrice());
return true;
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256("totalSpentPerDay", _day)];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256("deployedAtBlock")];
}
function gasLimitDepositRelay() public view returns(uint256) {
return uintStorage[keccak256("gasLimitDepositRelay")];
}
function gasLimitWithdrawConfirm() public view returns(uint256) {
return uintStorage[keccak256("gasLimitWithdrawConfirm")];
}
function foreignDailyLimit() public view returns(uint256) {
return uintStorage[keccak256("foreignDailyLimit")];
}
function erc677token() public view returns(IBurnableMintableERC677Token) {
return IBurnableMintableERC677Token(addressStorage[keccak256("erc677token")]);
}
function setGasLimits(uint256 _gasLimitDepositRelay, uint256 _gasLimitWithdrawConfirm) public onlyOwner {
uintStorage[keccak256("gasLimitDepositRelay")] = _gasLimitDepositRelay;
uintStorage[keccak256("gasLimitWithdrawConfirm")] = _gasLimitWithdrawConfirm;
GasConsumptionLimitsUpdated(gasLimitDepositRelay(), gasLimitWithdrawConfirm());
}
function deposit(address recipient, uint value, bytes32 transactionHash) public onlyValidator {
require(address(erc677token()) != address(0x0));
// Protection from misbehaing authority
bytes32 hashMsg = keccak256(recipient, value, transactionHash);
bytes32 hashSender = keccak256(msg.sender, hashMsg);
// Duplicated deposits
require(!depositsSigned(hashSender));
setDepositsSigned(hashSender, true);
uint256 signed = numDepositsSigned(hashMsg).add(1);
setNumDepositsSigned(hashMsg, signed);
SignedForDeposit(msg.sender, transactionHash);
if (signed == validatorContract().requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
erc677token().mint(recipient, value);
Deposit(recipient, value);
}
}
/// Should be used as sync tool
///
/// Message is a message that should be relayed to main chain once authorities sign it.
///
/// for withdraw message contains:
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature(bytes signature, bytes message) public onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(msg.sender == MessageSigning.recoverAddressFromSignedMessage(signature, message));
require(message.length == 116);
bytes32 hash = keccak256(message);
bytes32 hashSender = keccak256(msg.sender, hash);
uint signed = numMessagesSigned(hashSender) + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
} else {
// check if it will really reduce gas usage in case of the second transaction
// with the same hash
setMessages(hash, message);
}
setMessagesSigned(hashSender, true);
bytes32 signIdx = keccak256(hash, (signed-1));
setSignatures(signIdx, signature);
setNumMessagesSigned(hashSender, signed);
SignedForWithdraw(msg.sender, hash);
if (signed == validatorContract().requiredSignatures()) {
CollectedSignatures(msg.sender, hash);
}
}
function signature(bytes32 _hash, uint256 _index) public view returns (bytes) {
bytes32 signIdx = keccak256(_hash, _index);
return signatures(signIdx);
}
/// Get message
function message(bytes32 _hash) public view returns (bytes) {
return messages(_hash);
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function setForeignDailyLimit(uint256 _foreignDailyLimit) public onlyOwner {
require(_foreignDailyLimit > 0);
uintStorage[keccak256("foreignDailyLimit")] = _foreignDailyLimit;
DailyLimit(_foreignDailyLimit);
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return foreignDailyLimit() >= nextLimit;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function homeGasPrice() internal view returns(uint256) {
return 1000000000 wei;
}
function messages(bytes32 _hash) private view returns(bytes) {
return bytesStorage[keccak256("messages", _hash)];
}
function setMessages(bytes32 _hash, bytes _message) private {
bytesStorage[keccak256("messages", _hash)] = _message;
}
function signatures(bytes32 _hash) private view returns(bytes) {
return bytesStorage[keccak256("signatures", _hash)];
}
function setSignatures(bytes32 _hash, bytes _signature) private {
bytesStorage[keccak256("signatures", _hash)] = _signature;
}
function messagesSigned(bytes32 _message) private view returns(bool) {
return boolStorage[keccak256("messagesSigned", _message)];
}
function depositsSigned(bytes32 _deposit) private view returns(bool) {
return boolStorage[keccak256("depositsSigned", _deposit)];
}
function numMessagesSigned(bytes32 _message) private view returns(uint256) {
return uintStorage[keccak256("numMessagesSigned", _message)];
}
function numDepositsSigned(bytes32 _deposit) private view returns(uint256) {
return uintStorage[keccak256("numDepositsSigned", _deposit)];
}
function setMessagesSigned(bytes32 _hash, bool _status) private {
boolStorage[keccak256("messagesSigned", _hash)] = _status;
}
function setDepositsSigned(bytes32 _deposit, bool _status) private {
boolStorage[keccak256("depositsSigned", _deposit)] = _status;
}
function setNumMessagesSigned(bytes32 _message, uint256 _number) private {
uintStorage[keccak256("numMessagesSigned", _message)] = _number;
}
function setNumDepositsSigned(bytes32 _deposit, uint256 _number) private {
uintStorage[keccak256("numDepositsSigned", _deposit)] = _number;
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) private {
uintStorage[keccak256("totalSpentPerDay", _day)] = _value;
}
function setErc677token(address _token) private {
require(_token != address(0));
addressStorage[keccak256("erc677token")] = _token;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}

View File

@ -0,0 +1,110 @@
pragma solidity ^0.4.19;
import "../libraries/SafeMath.sol";
import "../libraries/Helpers.sol";
import "../libraries/Message.sol";
// import "./IBridgeValidators.sol";
// import "./Validatable.sol";
// import "./BridgeDeploymentAddressStorage.sol";
import "./U_Validatable.sol";
import "../upgradeability/OwnedUpgradeabilityStorage.sol";
contract HomeBridge is OwnedUpgradeabilityStorage, Validatable {
using SafeMath for uint256;
event GasConsumptionLimitsUpdated(uint256 gas);
event Deposit (address recipient, uint256 value);
event Withdraw (address recipient, uint256 value);
event DailyLimit(uint256 newLimit);
function initialize (
address _validatorContract,
uint256 _homeDailyLimit
) public {
require(!isInitialized());
require(_validatorContract != address(0));
require(_homeDailyLimit > 0);
addressStorage[keccak256("validatorContract")] = _validatorContract;
uintStorage[keccak256("deployedAtBlock")] = block.number;
setHomeDailyLimit(_homeDailyLimit);
setInitialize(true);
}
function () public payable {
require(msg.value > 0);
require(withinLimit(msg.value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(msg.value));
Deposit(msg.sender, msg.value);
}
function gasLimitWithdrawRelay() public view returns(uint256) {
return uintStorage[keccak256("gasLimitWithdrawRelay")];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256("deployedAtBlock")];
}
function homeDailyLimit() public view returns(uint256) {
return uintStorage[keccak256("homeDailyLimit")];
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256("totalSpentPerDay", _day)];
}
function withdraws(bytes32 _withdraw) public view returns(bool) {
return boolStorage[keccak256("withdraws", _withdraw)];
}
function setGasLimitWithdrawRelay(uint256 _gas) public onlyOwner {
uintStorage[keccak256("gasLimitWithdrawRelay")] = _gas;
GasConsumptionLimitsUpdated(_gas);
}
function withdraw(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) public {
require(message.length == 116);
require(Helpers.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract()));
address recipient = Message.getRecipient(message);
uint256 value = Message.getValue(message);
bytes32 hash = Message.getTransactionHash(message);
require(!withdraws(hash));
// Order of operations below is critical to avoid TheDAO-like re-entry bug
setWithdraws(hash, true);
// pay out recipient
recipient.transfer(value);
Withdraw(recipient, value);
}
function setHomeDailyLimit(uint256 _homeDailyLimit) public onlyOwner {
uintStorage[keccak256("homeDailyLimit")] = _homeDailyLimit;
DailyLimit(_homeDailyLimit);
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return homeDailyLimit() >= nextLimit;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) private {
uintStorage[keccak256("totalSpentPerDay", _day)] = _value;
}
function setWithdraws(bytes32 _withdraw, bool _status) private {
boolStorage[keccak256("withdraws", _withdraw)] = _status;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}

View File

@ -0,0 +1,22 @@
pragma solidity ^0.4.19;
import "../IBridgeValidators.sol";
import "../upgradeability/OwnedUpgradeabilityStorage.sol";
contract Validatable is OwnedUpgradeabilityStorage {
function validatorContract() public view returns(IBridgeValidators) {
return IBridgeValidators(addressStorage[keccak256("validatorContract")]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
_;
}
modifier onlyOwner() {
require(validatorContract().owner() == msg.sender);
_;
}
}

View File

@ -1,107 +1,265 @@
pragma solidity ^0.4.19;
pragma solidity 0.4.19;
// File: contracts/IBridgeValidators.sol
interface IBridgeValidators {
function isValidator(address _validator) public view returns(bool);
function requiredSignatures() public view returns(uint8);
function currentOwner() public view returns(address);
function requiredSignatures() public view returns(uint256);
function owner() public view returns(address);
}
// File: zeppelin-solidity/contracts/ownership/Ownable.sol
// File: contracts/libraries/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) {
if (a == 0) {
return 0;
}
uint256 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 c;
}
/**
* @dev Substracts 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) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
// File: contracts/upgradeability/EternalStorage.sol
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
/**
* @title UpgradeabilityOwnerStorage
* @dev This contract keeps track of the upgradeability owner
*/
contract UpgradeabilityOwnerStorage {
// Owner of the contract
address private _upgradeabilityOwner;
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function upgradeabilityOwner() public view returns (address) {
return _upgradeabilityOwner;
}
/**
* @dev Sets the address of the owner
*/
function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
_upgradeabilityOwner = newUpgradeabilityOwner;
}
}
// File: contracts/upgradeability/UpgradeabilityStorage.sol
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Version name of the current implementation
string internal _version;
// Address of the current implementation
address internal _implementation;
/**
* @dev Tells the version name of the current implementation
* @return string representing the name of the current version
*/
function version() public view returns (string) {
return _version;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}
// File: contracts/upgradeability/OwnedUpgradeabilityStorage.sol
/**
* @title OwnedUpgradeabilityStorage
* @dev This is the storage necessary to perform upgradeable contracts.
* This means, required state variables for upgradeability purpose and eternal storage per se.
*/
contract OwnedUpgradeabilityStorage is UpgradeabilityOwnerStorage, UpgradeabilityStorage, EternalStorage {}
// File: contracts/upgradeable_contracts/Ownable.sol
// Roman Storm Multi Sender
// To Use this Dapp: https://poanetwork.github.io/multisender
pragma solidity 0.4.19;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable {
address public owner;
contract Ownable is EternalStorage {
/**
* @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 Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
_;
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[keccak256("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));
setOwner(newOwner);
}
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @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));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
OwnershipTransferred(owner(), newOwner);
addressStorage[keccak256("owner")] = newOwner;
}
}
// File: contracts/BridgeValidators.sol
// File: contracts/upgradeable_contracts/U_BridgeValidators.sol
contract BridgeValidators is Ownable, IBridgeValidators {
// Event created on validator gets added
contract BridgeValidators is IBridgeValidators, Ownable, OwnedUpgradeabilityStorage {
using SafeMath for uint256;
event ValidatorAdded (address validator);
event ValidatorRemoved (address validator);
uint8 requiredValidators = 0;
uint256 public validatorCount = 0;
mapping (address => bool) public validators;
function BridgeValidators(uint8 _requiredValidators, address[] _initialValidators) public Ownable() {
require(_requiredValidators != 0);
require(_initialValidators.length >= _requiredValidators);
validatorCount = _initialValidators.length;
function initialize(uint256 _requiredSignatures, address[] _initialValidators, address _owner) public {
require(!isInitialized());
setOwner(_owner);
require(_requiredSignatures != 0);
require(_initialValidators.length >= _requiredSignatures);
for (uint i = 0; i < _initialValidators.length; i++) {
require(!isValidator(_initialValidators[i]) && _initialValidators[i] != address(0));
addValidator(_initialValidators[i]);
}
setRequiredValidators(_requiredValidators);
setRequiredSignatures(_requiredSignatures);
setInitialize(true);
}
function addValidator(address _validator) public onlyOwner {
assert(validators[_validator] != true);
validatorCount++;
validators[_validator] = true;
function addValidator(address _validator) public onlyOwner {
assert(validators(_validator) != true);
setValidatorCount(validatorCount().add(1));
setValidator(_validator, true);
ValidatorAdded(_validator);
}
function removeValidator(address _validator) public onlyOwner {
require(validatorCount > requiredValidators);
validators[_validator] = false;
validatorCount--;
require(validatorCount() > requiredSignatures());
setValidator(_validator, false);
setValidatorCount(validatorCount().sub(1));
ValidatorRemoved(_validator);
}
function setRequiredValidators(uint8 _requiredValidators) public onlyOwner {
require(validatorCount >= _requiredValidators);
requiredValidators = _requiredValidators;
function setRequiredSignatures(uint256 _requiredSignatures) public onlyOwner {
require(validatorCount() >= _requiredSignatures);
uintStorage[keccak256("requiredSignatures")] = _requiredSignatures;
}
function requiredSignatures() public view returns(uint256) {
return uintStorage[keccak256("requiredSignatures")];
}
function validatorCount() public view returns(uint256) {
return uintStorage[keccak256("validatorCount")];
}
function validators(address _validator) public view returns(bool) {
return boolStorage[keccak256("validators", _validator)];
}
function isValidator(address _validator) public view returns(bool) {
return validators[_validator] == true;
return validators(_validator) == true;
}
function requiredSignatures() public view returns(uint8) {
return requiredValidators;
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function currentOwner() public view returns(address) {
return owner;
function setValidatorCount(uint256 _validatorCount) private {
uintStorage[keccak256("validatorCount")] = _validatorCount;
}
function setValidator(address _validator, bool _status) private {
boolStorage[keccak256("validators", _validator)] = _status;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}

View File

@ -1,29 +1,11 @@
pragma solidity 0.4.19;
// File: contracts/BridgeDeploymentAddressStorage.sol
contract BridgeDeploymentAddressStorage {
uint256 public deployedAtBlock;
function BridgeDeploymentAddressStorage() public {
deployedAtBlock = block.number;
}
}
// File: contracts/ERC677Receiver.sol
contract ERC677Receiver {
function onTokenTransfer(address _from, uint _value, bytes _data) external returns(bool);
}
// File: contracts/IBridgeValidators.sol
interface IBridgeValidators {
function isValidator(address _validator) public view returns(bool);
function requiredSignatures() public view returns(uint8);
function currentOwner() public view returns(address);
}
// File: zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
/**
@ -67,25 +49,12 @@ contract IBurnableMintableERC677Token is ERC677 {
function burn(uint256 _value) public;
}
// File: contracts/Validatable.sol
// File: contracts/IBridgeValidators.sol
contract Validatable {
IBridgeValidators public validatorContract;
modifier onlyValidator() {
require(validatorContract.isValidator(msg.sender));
_;
}
modifier onlyOwner() {
require(validatorContract.currentOwner() == msg.sender);
_;
}
function Validatable(address _validatorContract) public {
require(_validatorContract != address(0));
validatorContract = IBridgeValidators(_validatorContract);
}
interface IBridgeValidators {
function isValidator(address _validator) public view returns(bool);
function requiredSignatures() public view returns(uint256);
function owner() public view returns(address);
}
// File: contracts/libraries/Helpers.sol
@ -126,7 +95,7 @@ library Helpers {
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view returns (bool) {
uint8 requiredSignatures = _validatorContract.requiredSignatures();
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = MessageSigning.hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
@ -262,24 +231,119 @@ library SafeMath {
}
}
// File: contracts/ForeignBridge.sol
// File: contracts/upgradeability/EternalStorage.sol
contract ForeignBridge is ERC677Receiver, Validatable, BridgeDeploymentAddressStorage {
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
/**
* @title UpgradeabilityOwnerStorage
* @dev This contract keeps track of the upgradeability owner
*/
contract UpgradeabilityOwnerStorage {
// Owner of the contract
address private _upgradeabilityOwner;
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function upgradeabilityOwner() public view returns (address) {
return _upgradeabilityOwner;
}
/**
* @dev Sets the address of the owner
*/
function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
_upgradeabilityOwner = newUpgradeabilityOwner;
}
}
// File: contracts/upgradeability/UpgradeabilityStorage.sol
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Version name of the current implementation
string internal _version;
// Address of the current implementation
address internal _implementation;
/**
* @dev Tells the version name of the current implementation
* @return string representing the name of the current version
*/
function version() public view returns (string) {
return _version;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}
// File: contracts/upgradeability/OwnedUpgradeabilityStorage.sol
/**
* @title OwnedUpgradeabilityStorage
* @dev This is the storage necessary to perform upgradeable contracts.
* This means, required state variables for upgradeability purpose and eternal storage per se.
*/
contract OwnedUpgradeabilityStorage is UpgradeabilityOwnerStorage, UpgradeabilityStorage, EternalStorage {}
// File: contracts/upgradeable_contracts/U_Validatable.sol
contract Validatable is OwnedUpgradeabilityStorage {
function validatorContract() public view returns(IBridgeValidators) {
return IBridgeValidators(addressStorage[keccak256("validatorContract")]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
_;
}
modifier onlyOwner() {
require(validatorContract().owner() == msg.sender);
_;
}
}
// File: contracts/upgradeable_contracts/U_ForeignBridge.sol
// import "./IBridgeValidators.sol";
// import "./BridgeDeploymentAddressStorage.sol";
contract ForeignBridge is ERC677Receiver, Validatable {
using SafeMath for uint256;
uint256 public gasLimitDepositRelay;
uint256 public gasLimitWithdrawConfirm;
uint256 homeGasPrice = 1000000000 wei;
uint256 public foreignDailyLimit;
mapping (bytes32 => bytes) messages;
mapping (bytes32 => bytes) signatures;
mapping (bytes32 => bool) messages_signed;
mapping (bytes32 => uint256) num_messages_signed;
mapping (bytes32 => bool) deposits_signed;
mapping (bytes32 => uint256) num_deposits_signed;
mapping (uint256 => uint256) totalSpentPerDay;
IBurnableMintableERC677Token public erc677token;
/// triggered when relay of deposit from HomeBridge is complete
event Deposit(address recipient, uint value);
@ -295,63 +359,85 @@ contract ForeignBridge is ERC677Receiver, Validatable, BridgeDeploymentAddressSt
event SignedForWithdraw(address indexed signer, bytes32 message);
event DailyLimit(uint256 newLimit);
function ForeignBridge(
function initialize(
address _validatorContract,
address _erc677token,
uint256 _foreignDailyLimit
) public Validatable(_validatorContract) {
) public {
require(!isInitialized());
require(_validatorContract != address(0));
require(_foreignDailyLimit > 0);
erc677token = IBurnableMintableERC677Token(_erc677token);
foreignDailyLimit = _foreignDailyLimit;
}
function setGasLimitDepositRelay(uint256 _gas) public onlyOwner {
gasLimitDepositRelay = _gas;
GasConsumptionLimitsUpdated(gasLimitDepositRelay, gasLimitWithdrawConfirm);
}
function setGasLimitWithdrawConfirm(uint256 gas) public onlyOwner {
gasLimitWithdrawConfirm = gas;
GasConsumptionLimitsUpdated(gasLimitDepositRelay, gasLimitWithdrawConfirm);
}
function deposit(address recipient, uint value, bytes32 transactionHash) public onlyValidator {
require(erc677token != address(0x0));
// Protection from misbehaing authority
bytes32 hash_msg = keccak256(recipient, value, transactionHash);
bytes32 hash_sender = keccak256(msg.sender, hash_msg);
// Duplicated deposits
require(!deposits_signed[hash_sender]);
deposits_signed[hash_sender] = true;
uint256 signed = num_deposits_signed[hash_msg] + 1;
num_deposits_signed[hash_msg] = signed;
SignedForDeposit(msg.sender, transactionHash);
if (signed == validatorContract.requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
erc677token.mint(recipient, value);
Deposit(recipient, value);
}
addressStorage[keccak256("validatorContract")] = _validatorContract;
setErc677token(_erc677token);
setForeignDailyLimit(_foreignDailyLimit);
uintStorage[keccak256("deployedAtBlock")] = block.number;
setInitialize(true);
}
function onTokenTransfer(address _from, uint256 _value, bytes _data) external returns(bool) {
require(erc677token != address(0x0));
require(msg.sender == address(erc677token));
require(address(erc677token()) != address(0x0));
require(msg.sender == address(erc677token()));
require(withinLimit(_value));
totalSpentPerDay[getCurrentDay()] = totalSpentPerDay[getCurrentDay()].add(_value);
erc677token.burn(_value);
Withdraw(_from, _value, homeGasPrice);
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value));
erc677token().burn(_value);
Withdraw(_from, _value, homeGasPrice());
return true;
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256("totalSpentPerDay", _day)];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256("deployedAtBlock")];
}
function gasLimitDepositRelay() public view returns(uint256) {
return uintStorage[keccak256("gasLimitDepositRelay")];
}
function gasLimitWithdrawConfirm() public view returns(uint256) {
return uintStorage[keccak256("gasLimitWithdrawConfirm")];
}
function foreignDailyLimit() public view returns(uint256) {
return uintStorage[keccak256("foreignDailyLimit")];
}
function erc677token() public view returns(IBurnableMintableERC677Token) {
return IBurnableMintableERC677Token(addressStorage[keccak256("erc677token")]);
}
function setGasLimits(uint256 _gasLimitDepositRelay, uint256 _gasLimitWithdrawConfirm) public onlyOwner {
uintStorage[keccak256("gasLimitDepositRelay")] = _gasLimitDepositRelay;
uintStorage[keccak256("gasLimitWithdrawConfirm")] = _gasLimitWithdrawConfirm;
GasConsumptionLimitsUpdated(gasLimitDepositRelay(), gasLimitWithdrawConfirm());
}
function deposit(address recipient, uint value, bytes32 transactionHash) public onlyValidator {
require(address(erc677token()) != address(0x0));
// Protection from misbehaing authority
bytes32 hashMsg = keccak256(recipient, value, transactionHash);
bytes32 hashSender = keccak256(msg.sender, hashMsg);
// Duplicated deposits
require(!depositsSigned(hashSender));
setDepositsSigned(hashSender, true);
uint256 signed = numDepositsSigned(hashMsg).add(1);
setNumDepositsSigned(hashMsg, signed);
SignedForDeposit(msg.sender, transactionHash);
if (signed == validatorContract().requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
erc677token().mint(recipient, value);
Deposit(recipient, value);
}
}
/// Should be used as sync tool
///
/// Message is a message that should be relayed to main chain once authorities sign it.
@ -366,56 +452,123 @@ contract ForeignBridge is ERC677Receiver, Validatable, BridgeDeploymentAddressSt
require(message.length == 116);
bytes32 hash = keccak256(message);
bytes32 hash_sender = keccak256(msg.sender, hash);
bytes32 hashSender = keccak256(msg.sender, hash);
uint signed = num_messages_signed[hash_sender] + 1;
uint signed = numMessagesSigned(hashSender) + 1;
if (signed > 1) {
// Duplicated signatures
require(!messages_signed[hash_sender]);
}
else {
require(!messagesSigned(hashSender));
} else {
// check if it will really reduce gas usage in case of the second transaction
// with the same hash
messages[hash] = message;
setMessages(hash, message);
}
messages_signed[hash_sender] = true;
setMessagesSigned(hashSender, true);
bytes32 sign_idx = keccak256(hash, (signed-1));
signatures[sign_idx] = signature;
bytes32 signIdx = keccak256(hash, (signed-1));
setSignatures(signIdx, signature);
num_messages_signed[hash_sender] = signed;
setNumMessagesSigned(hashSender, signed);
// TODO: this may cause troubles if requiredSignatures len is changed
SignedForWithdraw(msg.sender, hash);
if (signed == validatorContract.requiredSignatures()) {
if (signed == validatorContract().requiredSignatures()) {
CollectedSignatures(msg.sender, hash);
}
}
function signature(bytes32 hash, uint index) public view returns (bytes) {
bytes32 sign_idx = keccak256(hash, index);
return signatures[sign_idx];
function signature(bytes32 _hash, uint256 _index) public view returns (bytes) {
bytes32 signIdx = keccak256(_hash, _index);
return signatures(signIdx);
}
/// Get message
function message(bytes32 hash) public view returns (bytes) {
return messages[hash];
function message(bytes32 _hash) public view returns (bytes) {
return messages(_hash);
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function setDailyLimit(uint256 _foreignDailyLimit) public onlyOwner {
function setForeignDailyLimit(uint256 _foreignDailyLimit) public onlyOwner {
require(_foreignDailyLimit > 0);
foreignDailyLimit = _foreignDailyLimit;
DailyLimit(foreignDailyLimit);
uintStorage[keccak256("foreignDailyLimit")] = _foreignDailyLimit;
DailyLimit(_foreignDailyLimit);
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay[getCurrentDay()].add(_amount);
return foreignDailyLimit >= nextLimit;
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return foreignDailyLimit() >= nextLimit;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function homeGasPrice() internal view returns(uint256) {
return 1000000000 wei;
}
function messages(bytes32 _hash) private view returns(bytes) {
return bytesStorage[keccak256("messages", _hash)];
}
function setMessages(bytes32 _hash, bytes _message) private {
bytesStorage[keccak256("messages", _hash)] = _message;
}
function signatures(bytes32 _hash) private view returns(bytes) {
return bytesStorage[keccak256("signatures", _hash)];
}
function setSignatures(bytes32 _hash, bytes _signature) private {
bytesStorage[keccak256("signatures", _hash)] = _signature;
}
function messagesSigned(bytes32 _message) private view returns(bool) {
return boolStorage[keccak256("messagesSigned", _message)];
}
function depositsSigned(bytes32 _deposit) private view returns(bool) {
return boolStorage[keccak256("depositsSigned", _deposit)];
}
function numMessagesSigned(bytes32 _message) private view returns(uint256) {
return uintStorage[keccak256("numMessagesSigned", _message)];
}
function numDepositsSigned(bytes32 _deposit) private view returns(uint256) {
return uintStorage[keccak256("numDepositsSigned", _deposit)];
}
function setMessagesSigned(bytes32 _hash, bool _status) private {
boolStorage[keccak256("messagesSigned", _hash)] = _status;
}
function setDepositsSigned(bytes32 _deposit, bool _status) private {
boolStorage[keccak256("depositsSigned", _deposit)] = _status;
}
function setNumMessagesSigned(bytes32 _message, uint256 _number) private {
uintStorage[keccak256("numMessagesSigned", _message)] = _number;
}
function setNumDepositsSigned(bytes32 _deposit, uint256 _number) private {
uintStorage[keccak256("numDepositsSigned", _deposit)] = _number;
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) private {
uintStorage[keccak256("totalSpentPerDay", _day)] = _value;
}
function setErc677token(address _token) private {
require(_token != address(0));
addressStorage[keccak256("erc677token")] = _token;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}

View File

@ -1,42 +1,11 @@
pragma solidity 0.4.19;
// File: contracts/BridgeDeploymentAddressStorage.sol
contract BridgeDeploymentAddressStorage {
uint256 public deployedAtBlock;
function BridgeDeploymentAddressStorage() public {
deployedAtBlock = block.number;
}
}
// File: contracts/IBridgeValidators.sol
interface IBridgeValidators {
function isValidator(address _validator) public view returns(bool);
function requiredSignatures() public view returns(uint8);
function currentOwner() public view returns(address);
}
// File: contracts/Validatable.sol
contract Validatable {
IBridgeValidators public validatorContract;
modifier onlyValidator() {
require(validatorContract.isValidator(msg.sender));
_;
}
modifier onlyOwner() {
require(validatorContract.currentOwner() == msg.sender);
_;
}
function Validatable(address _validatorContract) public {
require(_validatorContract != address(0));
validatorContract = IBridgeValidators(_validatorContract);
}
function requiredSignatures() public view returns(uint256);
function owner() public view returns(address);
}
// File: contracts/libraries/Helpers.sol
@ -77,7 +46,7 @@ library Helpers {
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view returns (bool) {
uint8 requiredSignatures = _validatorContract.requiredSignatures();
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = MessageSigning.hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
@ -213,52 +182,179 @@ library SafeMath {
}
}
// File: contracts/HomeBridge.sol
// File: contracts/upgradeability/EternalStorage.sol
contract HomeBridge is Validatable, BridgeDeploymentAddressStorage {
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
/**
* @title UpgradeabilityOwnerStorage
* @dev This contract keeps track of the upgradeability owner
*/
contract UpgradeabilityOwnerStorage {
// Owner of the contract
address private _upgradeabilityOwner;
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function upgradeabilityOwner() public view returns (address) {
return _upgradeabilityOwner;
}
/**
* @dev Sets the address of the owner
*/
function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
_upgradeabilityOwner = newUpgradeabilityOwner;
}
}
// File: contracts/upgradeability/UpgradeabilityStorage.sol
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Version name of the current implementation
string internal _version;
// Address of the current implementation
address internal _implementation;
/**
* @dev Tells the version name of the current implementation
* @return string representing the name of the current version
*/
function version() public view returns (string) {
return _version;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}
// File: contracts/upgradeability/OwnedUpgradeabilityStorage.sol
/**
* @title OwnedUpgradeabilityStorage
* @dev This is the storage necessary to perform upgradeable contracts.
* This means, required state variables for upgradeability purpose and eternal storage per se.
*/
contract OwnedUpgradeabilityStorage is UpgradeabilityOwnerStorage, UpgradeabilityStorage, EternalStorage {}
// File: contracts/upgradeable_contracts/U_Validatable.sol
contract Validatable is OwnedUpgradeabilityStorage {
function validatorContract() public view returns(IBridgeValidators) {
return IBridgeValidators(addressStorage[keccak256("validatorContract")]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
_;
}
modifier onlyOwner() {
require(validatorContract().owner() == msg.sender);
_;
}
}
// File: contracts/upgradeable_contracts/U_HomeBridge.sol
// import "./IBridgeValidators.sol";
// import "./Validatable.sol";
// import "./BridgeDeploymentAddressStorage.sol";
contract HomeBridge is OwnedUpgradeabilityStorage, Validatable {
using SafeMath for uint256;
uint256 public gasLimitWithdrawRelay;
uint256 public homeDailyLimit;
mapping (uint256 => uint256) totalSpentPerDay;
mapping (bytes32 => bool) withdraws;
event GasConsumptionLimitsUpdated(uint256 gas);
event Deposit (address recipient, uint256 value);
event Withdraw (address recipient, uint256 value);
event DailyLimit(uint256 newLimit);
function HomeBridge (
function initialize (
address _validatorContract,
uint256 _homeDailyLimit
) public Validatable(_validatorContract) {
) public {
require(!isInitialized());
require(_validatorContract != address(0));
require(_homeDailyLimit > 0);
homeDailyLimit = _homeDailyLimit;
DailyLimit(homeDailyLimit);
addressStorage[keccak256("validatorContract")] = _validatorContract;
uintStorage[keccak256("deployedAtBlock")] = block.number;
setHomeDailyLimit(_homeDailyLimit);
setInitialize(true);
}
/// Should be used to deposit money.
function () public payable {
require(msg.value > 0);
require(withinLimit(msg.value));
totalSpentPerDay[getCurrentDay()] = totalSpentPerDay[getCurrentDay()].add(msg.value);
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(msg.value));
Deposit(msg.sender, msg.value);
}
function gasLimitWithdrawRelay() public view returns(uint256) {
return uintStorage[keccak256("gasLimitWithdrawRelay")];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256("deployedAtBlock")];
}
function homeDailyLimit() public view returns(uint256) {
return uintStorage[keccak256("homeDailyLimit")];
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256("totalSpentPerDay", _day)];
}
function withdraws(bytes32 _withdraw) public view returns(bool) {
return boolStorage[keccak256("withdraws", _withdraw)];
}
function setGasLimitWithdrawRelay(uint256 _gas) public onlyOwner {
gasLimitWithdrawRelay = _gas;
GasConsumptionLimitsUpdated(gasLimitWithdrawRelay);
uintStorage[keccak256("gasLimitWithdrawRelay")] = _gas;
GasConsumptionLimitsUpdated(_gas);
}
function withdraw(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) public {
require(message.length == 116);
require(Helpers.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract));
require(Helpers.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract()));
address recipient = Message.getRecipient(message);
uint256 value = Message.getValue(message);
bytes32 hash = Message.getTransactionHash(message);
require(!withdraws[hash]);
require(!withdraws(hash));
// Order of operations below is critical to avoid TheDAO-like re-entry bug
withdraws[hash] = true;
setWithdraws(hash, true);
// pay out recipient
recipient.transfer(value);
@ -266,10 +362,9 @@ contract HomeBridge is Validatable, BridgeDeploymentAddressStorage {
Withdraw(recipient, value);
}
function setDailyLimit(uint256 _homeDailyLimit) public onlyOwner {
require(_homeDailyLimit > 0);
homeDailyLimit = _homeDailyLimit;
DailyLimit(homeDailyLimit);
function setHomeDailyLimit(uint256 _homeDailyLimit) public onlyOwner {
uintStorage[keccak256("homeDailyLimit")] = _homeDailyLimit;
DailyLimit(_homeDailyLimit);
}
function getCurrentDay() public view returns(uint256) {
@ -277,7 +372,23 @@ contract HomeBridge is Validatable, BridgeDeploymentAddressStorage {
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay[getCurrentDay()].add(_amount);
return homeDailyLimit >= nextLimit;
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return homeDailyLimit() >= nextLimit;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) private {
uintStorage[keccak256("totalSpentPerDay", _day)] = _value;
}
function setWithdraws(bytes32 _withdraw, bool _status) private {
boolStorage[keccak256("withdraws", _withdraw)] = _status;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
rm -rf flats/*
./node_modules/.bin/truffle-flattener contracts/ForeignBridge.sol > flats/ForeignBridge_flat.sol
./node_modules/.bin/truffle-flattener contracts/BridgeValidators.sol > flats/BridgeValidators_flat.sol
./node_modules/.bin/truffle-flattener contracts/HomeBridge.sol > flats/HomeBridge_flat.sol
./node_modules/.bin/truffle-flattener contracts/upgradeable_contracts/U_ForeignBridge.sol > flats/ForeignBridge_flat.sol
./node_modules/.bin/truffle-flattener contracts/upgradeable_contracts/U_BridgeValidators.sol > flats/BridgeValidators_flat.sol
./node_modules/.bin/truffle-flattener contracts/upgradeable_contracts/U_HomeBridge.sol > flats/HomeBridge_flat.sol
./node_modules/.bin/truffle-flattener contracts/POA20.sol > flats/POA20_flat.sol

View File

@ -4,22 +4,24 @@ const HomeBridge = artifacts.require("./HomeBridge.sol");
const ForeignBridge = artifacts.require("./ForeignBridge.sol");
module.exports = async function(deployer, network, accounts) {
let validators = ["0xb8988b690910913c97a090c3a6f80fad8b3a4683"]
const homeDailyLimit = '1000000000000000000' // 1 ether
const foreignDailyLimit = '1000000000000000000' // 1 ether
console.log('deploying token')
await deployer.deploy(POA20, "POA ERC20 on Foundation", "POA20", 18)
const erc677token = await POA20.deployed()
console.log('deploying validators')
await deployer.deploy(BridgeValidators, '1', validators);
const validatorContract = await BridgeValidators.deployed();
console.log('deploying home')
await deployer.deploy(HomeBridge, validatorContract.address, homeDailyLimit);
console.log('deploying ForeignBridge')
await deployer.deploy(ForeignBridge, validatorContract.address, erc677token.address, foreignDailyLimit);
const foreignBridge = await ForeignBridge.deployed();
if(process.env.DEPLOY_NORMAL === true){
let validators = ["0xb8988b690910913c97a090c3a6f80fad8b3a4683"]
const homeDailyLimit = '1000000000000000000' // 1 ether
const foreignDailyLimit = '1000000000000000000' // 1 ether
console.log('deploying token')
await deployer.deploy(POA20, "POA ERC20 on Foundation", "POA20", 18)
const erc677token = await POA20.deployed()
console.log('deploying validators')
await deployer.deploy(BridgeValidators, '1', validators);
const validatorContract = await BridgeValidators.deployed();
console.log('deploying home')
await deployer.deploy(HomeBridge, validatorContract.address, homeDailyLimit);
console.log('deploying ForeignBridge')
await deployer.deploy(ForeignBridge, validatorContract.address, erc677token.address, foreignDailyLimit);
const foreignBridge = await ForeignBridge.deployed();
await erc677token.transferOwnership(foreignBridge.address)
console.log('all is done')
await erc677token.transferOwnership(foreignBridge.address)
console.log('all is done')
}
};

View File

@ -0,0 +1,85 @@
const POA20 = artifacts.require("./POA20.sol");
const BridgeValidators = artifacts.require("./BridgeValidators.sol");
const HomeBridge = artifacts.require("./HomeBridge.sol");
const ForeignBridge = artifacts.require("./ForeignBridge.sol");
const EternalStorageProxy = artifacts.require('EternalStorageProxy')
module.exports = async function(deployer, network, accounts) {
const VALIDATORS = process.env.VALIDATORS ? process.env.VALIDATORS.split(" ") : ["0xb8988b690910913c97a090c3a6f80fad8b3a4683"];
const REQUIRED_NUMBER_OF_VALIDATORS = process.env.REQUIRED_VALIDATORS || VALIDATORS.length
const PROXY_OWNER = process.env.PROXY_OWNER || accounts[0];
const homeDailyLimit = process.env.HOME_LIMIT || '1000000000000000000' // 1 ether
const foreignDailyLimit = process.env.FOREIGN_LIMIT || '1000000000000000000' // 1 ether
console.log('storage for home validators')
await deployer.deploy(EternalStorageProxy, {from: PROXY_OWNER});
const storageBridgeValidators = await EternalStorageProxy.deployed()
console.log('deploying token')
await deployer.deploy(POA20, "POA ERC20 on Foundation", "POA20", 18)
const erc677token = await POA20.deployed()
console.log('deploying validators')
await deployer.deploy(BridgeValidators);
const validatorContract = await BridgeValidators.deployed();
console.log('hooking up eternal storage to BridgeValidators')
//truffle sucks, it uses web3 0.20, hence I need to work around in order to generate data param
var bridgeValidatorsWeb3 = web3.eth.contract(BridgeValidators.abi);
var bridgeValidatorsWeb3Instance = bridgeValidatorsWeb3.at(validatorContract.address);
var initializeDataValidators = bridgeValidatorsWeb3Instance.initialize.getData(REQUIRED_NUMBER_OF_VALIDATORS, VALIDATORS, PROXY_OWNER);
await storageBridgeValidators.upgradeTo('0', validatorContract.address, {from: PROXY_OWNER});
await web3.eth.sendTransaction({
from: PROXY_OWNER,
to: storageBridgeValidators.address,
data: initializeDataValidators,
value: 0,
gas: 4700000
})
console.log('deploying home storage on home network')
await deployer.deploy(EternalStorageProxy, {from: PROXY_OWNER});
const homeBridgeUpgradeable = await EternalStorageProxy.deployed()
await deployer.deploy(HomeBridge);
const homeBridgeImplementation = await HomeBridge.deployed();
var homeBridgeWeb3 = web3.eth.contract(HomeBridge.abi);
var homeBridgeWeb3Instance = homeBridgeWeb3.at(homeBridgeImplementation.address);
var initializeDataHome = homeBridgeWeb3Instance.initialize.getData(storageBridgeValidators.address, homeDailyLimit);
await homeBridgeUpgradeable.upgradeTo('0', homeBridgeImplementation.address, {from: PROXY_OWNER});
await web3.eth.sendTransaction({
from: PROXY_OWNER,
to: homeBridgeUpgradeable.address,
data: initializeDataHome,
value: 0,
gas: 4700000
})
console.log('deploying ForeignBridge')
await deployer.deploy(EternalStorageProxy, {from: PROXY_OWNER});
const foreignBridgeUpgradeable = await EternalStorageProxy.deployed()
await deployer.deploy(ForeignBridge);
const foreignBridgeImplementation = await ForeignBridge.deployed();
var foreignBridgeWeb3 = web3.eth.contract(ForeignBridge.abi);
var foreignBridgeWeb3Instance = foreignBridgeWeb3.at(foreignBridgeImplementation.address);
var initializeDataForeign = foreignBridgeWeb3Instance.initialize.getData(storageBridgeValidators.address, erc677token.address, foreignDailyLimit);
await foreignBridgeUpgradeable.upgradeTo('0', foreignBridgeImplementation.address, {from: PROXY_OWNER});
await web3.eth.sendTransaction({
from: PROXY_OWNER,
to: foreignBridgeUpgradeable.address,
data: initializeDataForeign,
value: 0,
gas: 4700000
})
// await deployer.deploy(ForeignBridge, validatorContract.address, erc677token.address, foreignDailyLimit);
// const foreignBridge = await ForeignBridge.deployed();
await erc677token.transferOwnership(foreignBridgeUpgradeable.address)
console.log('all is done', `
validators: ${VALIDATORS}
Owner: ${PROXY_OWNER}
Foreign Bridge: ${foreignBridgeUpgradeable.address}
Home Bridge: ${homeBridgeUpgradeable.address}
POA20: ${erc677token.address}`)
};