Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Governance based bridge contracts #3

Merged
merged 9 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# EditorConfig is awesome: http://EditorConfig.org

root = true

[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true

[*.ts]
indent_style = tab

[{package.json,.travis.yml}]
indent_style = space
indent_size = 4
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Dependencies
```bash
npm install
```

# To Deploy
Check `truffle.js` for networks and their ports
```bash
NETWORK=sokol npm run deploy
```

# To Flatten
```bash
npm run flatten
```
10 changes: 10 additions & 0 deletions contracts/BridgeDeploymentAddressStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity ^0.4.19;


contract BridgeDeploymentAddressStorage {
uint256 public deployedAtBlock;

function BridgeDeploymentAddressStorage() public {
deployedAtBlock = block.number;
}
}
104 changes: 55 additions & 49 deletions contracts/BridgeValidators.sol
Original file line number Diff line number Diff line change
@@ -1,52 +1,58 @@
pragma solidity ^0.4.18;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';

contract BridgeValidators is Ownable {

// Event created on validator gets added
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 {
require(_requiredValidators != 0);
require(_initialValidators.length >= _requiredValidators);
setRequiredValidators(_requiredValidators);
for (uint i = 0; i < _initialValidators.length; i++) {
require(!isValidator(_initialValidators[i]) && _initialValidators[i] != address(0));
addValidator(_initialValidators[i]);
}
validatorCount = _initialValidators.length;
}

function addValidator(address _validator) public onlyOwner {
assert(validators[_validator] != true);
validatorCount++;
validators[_validator] = true;
ValidatorAdded(_validator);
}

function removeValidator(address _validator) public onlyOwner {
require(validatorCount > requiredValidators);
validators[_validator] = false;
validatorCount--;
ValidatorRemoved(_validator);
}

function setRequiredValidators(uint8 _requiredValidators) public onlyOwner {
require(validatorCount >= _requiredValidators);
requiredValidators = _requiredValidators;
}

function isValidator(address _validator) public view returns(bool) {
return (validators[_validator] == true);
}

function onlyValidator(address _validator) public view returns(bool) {
return validators[_validator] == true;
}
import "zeppelin-solidity/contracts/ownership/Ownable.sol";
import "./IBridgeValidators.sol";


contract BridgeValidators is Ownable, IBridgeValidators {

// Event created on validator gets added
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;
for (uint i = 0; i < _initialValidators.length; i++) {
require(!isValidator(_initialValidators[i]) && _initialValidators[i] != address(0));
addValidator(_initialValidators[i]);
}
setRequiredValidators(_requiredValidators);
}

function addValidator(address _validator) public onlyOwner {
assert(validators[_validator] != true);
validatorCount++;
validators[_validator] = true;
ValidatorAdded(_validator);
}

function removeValidator(address _validator) public onlyOwner {
require(validatorCount > requiredValidators);
validators[_validator] = false;
validatorCount--;
ValidatorRemoved(_validator);
}

function setRequiredValidators(uint8 _requiredValidators) public onlyOwner {
require(validatorCount >= _requiredValidators);
requiredValidators = _requiredValidators;
}

function isValidator(address _validator) public view returns(bool) {
return validators[_validator] == true;
}

function requiredSignatures() public view returns(uint8) {
return requiredValidators;
}

function currentOwner() public view returns(address) {
return owner;
}
}
10 changes: 10 additions & 0 deletions contracts/ERC677.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity 0.4.19;
import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol";


contract ERC677 is ERC20 {
event Transfer(address indexed from, address indexed to, uint value, bytes data);

function transferAndCall(address, uint, bytes) public returns (bool);

}
6 changes: 6 additions & 0 deletions contracts/ERC677Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.4.19;


contract ERC677Receiver {
function onTokenTransfer(address _from, uint _value, bytes _data) external returns(bool);
}
182 changes: 117 additions & 65 deletions contracts/ForeignBridge.sol
Original file line number Diff line number Diff line change
@@ -1,75 +1,99 @@
pragma solidity 0.4.19;
import "./libraries/SafeMath.sol";
import "./libraries/Helpers.sol";
import "./libraries/Message.sol";
import "./libraries/MessageSigning.sol";
import "./BridgeValidators.sol";

contract ForeignBridge {
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 => uint8) num_deposits_signed;
mapping (bytes32 => bool) mintRequestsDone;
mapping (bytes32 => uint8) withdrawRequests;

/// Pending signatures and authorities who confirmed them
BridgeValidators public validatorsContract;
mapping (bytes32 => uint256) num_deposits_signed;
mapping (uint256 => uint256) totalSpentPerDay;

/// Token contract which the bridge has full ownership to burn and mint
address public token;
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, uint value);
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 _token,
address _validatorsContract
) public
{
require(_token != address(0) && _validatorsContract != address(0));
validatorsContract = BridgeValidators(_validatorsContract);
token = _token;
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;

/// Used to deposit money to the contract.
///
/// deposit recipient (bytes20)
/// deposit value (uint)
/// mainnet transaction hash (bytes32) // to avoid transaction duplication
function deposit(address recipient, uint256 value, bytes32 transactionHash) public {
require(validatorsContract.onlyValidator(msg.sender));
GasConsumptionLimitsUpdated(gasLimitDepositRelay, gasLimitWithdrawConfirm);
}

function setGasLimitWithdrawConfirm(uint256 gas) public onlyOwner {
gasLimitWithdrawConfirm = gas;

GasConsumptionLimitsUpdated(gasLimitDepositRelay, gasLimitWithdrawConfirm);
}

function deposit(address recipient, uint value, bytes32 transactionHash) public onlyValidator {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To not forget: create an issue to implement functionality that the bridge calls isValidator before sending a transaction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require(erc677token != address(0x0));

// Protection from misbehaing authority
bytes32 hash_msg = keccak256(recipient, value, transactionHash);
bytes32 hash_sender = keccak256(msg.sender, hash_msg);

// Prevents Duplicated deposits
// Duplicated deposits
require(!deposits_signed[hash_sender]);
deposits_signed[hash_sender] = true;

uint8 signed = num_deposits_signed[hash_msg] + 1;
uint256 signed = num_deposits_signed[hash_msg] + 1;
num_deposits_signed[hash_msg] = signed;

if (signed == requiredValidators) {
// token.mint(recipient, value);
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);
}

}

/// Transfer `value` from `msg.sender`s local balance (on `foreign` chain) to `recipient` on `home` chain.
///
/// immediately decreases `msg.sender`s local balance.
/// emits a `Withdraw` event which will be picked up by the bridge authorities.
/// bridge authorities will then sign off (by calling `submitSignature`) on a message containing `value`,
/// `recipient` and the `hash` of the transaction on `foreign` containing the `Withdraw` event.
/// once `requiredSignatures` are collected a `CollectedSignatures` event will be emitted.
/// an authority will pick up `CollectedSignatures` an call `HomeBridge.withdraw`
/// which transfers `value - relayCost` to `recipient` completing the transfer.
function transferHomeViaRelay(address recipient, uint256 value) public {
Withdraw(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
Expand All @@ -80,34 +104,62 @@ contract ForeignBridge {
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature(bytes signature, bytes message) public {
require(validatorsContract.onlyValidator(msg.sender));
// Validate submited signatures
require(MessageSigning.recoverAddressFromSignedMessage(signature, message) == msg.sender);
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));

// Valid withdraw message must have 84 bytes
require(message.length == 84);
var hash = keccak256(message);
require(message.length == 116);
bytes32 hash = keccak256(message);
bytes32 hash_sender = keccak256(msg.sender, hash);

// Duplicated signatures
// require(!Helpers.addressArrayContains(signatures[hash].signed, msg.sender));
// signatures[hash].message = message;
// signatures[hash].signed.push(msg.sender);
// signatures[hash].signatures.push(signature);
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
// if (signatures[hash].signed.length == requiredSignatures) {
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 signature
// function signature(bytes32 hash, uint index) public view returns (bytes) {
// return signatures[hash].signatures[index];
// }
/// 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;
}

// /// Get message
// function message(bytes32 hash) public view returns (bytes) {
// return signatures[hash].message;
// }
}
}
Loading