2018-01-04 06:42:21 -08:00
|
|
|
|
pragma solidity ^0.4.17;
|
2017-06-13 02:18:03 -07:00
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
|
2018-01-13 05:41:27 -08:00
|
|
|
|
/// general helpers.
|
|
|
|
|
/// `internal` so they get compiled into contracts using them.
|
2018-01-12 07:34:34 -08:00
|
|
|
|
library Helpers {
|
2018-01-13 05:41:27 -08:00
|
|
|
|
/// returns whether `array` contains `value`.
|
2018-01-12 07:17:34 -08:00
|
|
|
|
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
|
|
|
|
|
for (uint i = 0; i < array.length; i++) {
|
|
|
|
|
if (array[i] == value) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-15 02:00:30 -08:00
|
|
|
|
// returns the digits of `inputValue` as a string.
|
|
|
|
|
// example: `uintToString(12345678)` returns `"12345678"`
|
2018-01-15 02:00:04 -08:00
|
|
|
|
function uintToString(uint inputValue) internal pure returns (string) {
|
2018-01-15 01:47:50 -08:00
|
|
|
|
// figure out the length of the resulting string
|
|
|
|
|
uint length = 0;
|
|
|
|
|
uint currentValue = inputValue;
|
|
|
|
|
do {
|
|
|
|
|
length++;
|
|
|
|
|
currentValue /= 10;
|
|
|
|
|
} while (currentValue != 0);
|
|
|
|
|
// allocate enough memory
|
|
|
|
|
bytes memory result = new bytes(length);
|
|
|
|
|
// construct the string backwards
|
|
|
|
|
uint i = length - 1;
|
|
|
|
|
currentValue = inputValue;
|
|
|
|
|
do {
|
|
|
|
|
result[i--] = byte(48 + currentValue % 10);
|
|
|
|
|
currentValue /= 10;
|
|
|
|
|
} while (currentValue != 0);
|
|
|
|
|
return string(result);
|
2017-09-04 03:49:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
|
2018-01-15 01:30:52 -08:00
|
|
|
|
/// Library used only to test Helpers library via rpc calls
|
|
|
|
|
library HelpersTest {
|
|
|
|
|
function addressArrayContains(address[] array, address value) public pure returns (bool) {
|
|
|
|
|
return Helpers.addressArrayContains(array, value);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-15 02:00:04 -08:00
|
|
|
|
function uintToString(uint256 inputValue) public pure returns (string str) {
|
|
|
|
|
return Helpers.uintToString(inputValue);
|
2018-01-15 01:30:52 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-01-13 05:41:27 -08:00
|
|
|
|
// helpers for message signing.
|
|
|
|
|
// `internal` so they get compiled into contracts using them.
|
2018-01-12 07:42:09 -08:00
|
|
|
|
library MessageSigning {
|
2018-01-12 07:46:00 -08:00
|
|
|
|
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
|
2017-09-04 03:49:05 -07:00
|
|
|
|
require(signature.length == 65);
|
|
|
|
|
bytes32 r;
|
|
|
|
|
bytes32 s;
|
|
|
|
|
bytes1 v;
|
2017-12-13 06:35:00 -08:00
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
2017-09-04 03:49:05 -07:00
|
|
|
|
assembly {
|
|
|
|
|
r := mload(add(signature, 0x20))
|
|
|
|
|
s := mload(add(signature, 0x40))
|
|
|
|
|
v := mload(add(signature, 0x60))
|
|
|
|
|
}
|
2018-01-12 07:48:39 -08:00
|
|
|
|
return ecrecover(hashMessage(message), uint8(v), r, s);
|
2017-09-04 03:49:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-12 07:48:39 -08:00
|
|
|
|
function hashMessage(bytes message) internal pure returns (bytes32) {
|
2017-09-04 03:49:05 -07:00
|
|
|
|
bytes memory prefix = "\x19Ethereum Signed Message:\n";
|
2018-01-15 02:00:04 -08:00
|
|
|
|
return keccak256(prefix, Helpers.uintToString(message.length), message);
|
2017-09-04 03:49:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
|
2018-01-12 07:53:29 -08:00
|
|
|
|
/// Library used only to test MessageSigning library via rpc calls
|
|
|
|
|
library MessageSigningTest {
|
|
|
|
|
function recoverAddressFromSignedMessage(bytes signature, bytes message) public pure returns (address) {
|
|
|
|
|
return MessageSigning.recoverAddressFromSignedMessage(signature, message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-13 05:41:12 -08:00
|
|
|
|
|
2018-01-12 08:28:02 -08:00
|
|
|
|
library Message {
|
|
|
|
|
// layout of message :: bytes:
|
|
|
|
|
// offset 0: 32 bytes :: uint (little endian) - message length
|
|
|
|
|
// offset 32: 20 bytes :: address - recipient address
|
|
|
|
|
// offset 52: 32 bytes :: uint (little endian) - value
|
|
|
|
|
// offset 84: 32 bytes :: bytes32 - transaction hash
|
|
|
|
|
|
|
|
|
|
// bytes 1 to 32 are 0 because message length is stored as little endian.
|
|
|
|
|
// mload always reads 32 bytes.
|
|
|
|
|
// so we can and have to start reading recipient at offset 20 instead of 32.
|
|
|
|
|
// if we were to read at 32 the address would contain part of value and be corrupted.
|
|
|
|
|
// when reading from offset 20 mload will read 12 zero bytes followed
|
|
|
|
|
// by the 20 recipient address bytes and correctly convert it into an address.
|
|
|
|
|
// this saves some storage/gas over the alternative solution
|
|
|
|
|
// which is padding address to 32 bytes and reading recipient at offset 32.
|
|
|
|
|
// for more details see discussion in:
|
|
|
|
|
// https://github.com/paritytech/parity-bridge/issues/61
|
|
|
|
|
|
|
|
|
|
function getRecipient(bytes message) internal pure returns (address) {
|
|
|
|
|
address recipient;
|
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
|
|
|
assembly {
|
|
|
|
|
recipient := mload(add(message, 20))
|
|
|
|
|
}
|
|
|
|
|
return recipient;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getValue(bytes message) internal pure returns (uint) {
|
|
|
|
|
uint value;
|
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
|
|
|
assembly {
|
|
|
|
|
value := mload(add(message, 52))
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTransactionHash(bytes message) internal pure returns (bytes32) {
|
|
|
|
|
bytes32 hash;
|
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
|
|
|
assembly {
|
|
|
|
|
hash := mload(add(message, 84))
|
|
|
|
|
}
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Library used only to test Message library via rpc calls
|
|
|
|
|
library MessageTest {
|
|
|
|
|
function getRecipient(bytes message) public pure returns (address) {
|
2018-01-13 05:41:12 -08:00
|
|
|
|
return Message.getRecipient(message);
|
2018-01-12 08:28:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getValue(bytes message) public pure returns (uint) {
|
2018-01-13 05:41:12 -08:00
|
|
|
|
return Message.getValue(message);
|
2018-01-12 08:28:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTransactionHash(bytes message) public pure returns (bytes32) {
|
2018-01-13 05:41:12 -08:00
|
|
|
|
return Message.getTransactionHash(message);
|
2018-01-12 08:28:02 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-12 07:53:29 -08:00
|
|
|
|
|
2018-01-13 05:41:12 -08:00
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
|
contract HomeBridge {
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Number of authorities signatures required to withdraw the money.
|
|
|
|
|
///
|
|
|
|
|
/// Must be lesser than number of authorities.
|
2017-09-01 06:11:20 -07:00
|
|
|
|
uint public requiredSignatures;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2018-01-04 06:14:34 -08:00
|
|
|
|
/// The gas cost of calling `HomeBridge.withdraw`.
|
|
|
|
|
///
|
|
|
|
|
/// Is subtracted from `value` on withdraw.
|
|
|
|
|
/// recipient pays the relaying authority for withdraw.
|
|
|
|
|
/// this shuts down attacks that exhaust authorities funds on home chain.
|
|
|
|
|
uint public estimatedGasCostOfWithdraw;
|
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Contract authorities.
|
2017-09-01 06:11:20 -07:00
|
|
|
|
address[] public authorities;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
|
/// Used foreign transaction hashes.
|
2017-06-13 02:18:03 -07:00
|
|
|
|
mapping (bytes32 => bool) withdraws;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Event created on money deposit.
|
2017-08-21 08:32:37 -07:00
|
|
|
|
event Deposit (address recipient, uint value);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Event created on money withdraw.
|
2017-08-21 08:32:37 -07:00
|
|
|
|
event Withdraw (address recipient, uint value);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Multisig authority validation
|
2018-01-11 05:20:48 -08:00
|
|
|
|
modifier allAuthorities(uint8[] v, bytes32[] r, bytes32[] s, bytes message) {
|
2018-01-12 07:48:39 -08:00
|
|
|
|
var hash = MessageSigning.hashMessage(message);
|
2017-06-13 02:18:03 -07:00
|
|
|
|
var used = new address[](requiredSignatures);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-08-22 04:01:16 -07:00
|
|
|
|
require(requiredSignatures <= v.length);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
for (uint i = 0; i < requiredSignatures; i++) {
|
|
|
|
|
var a = ecrecover(hash, v[i], r[i], s[i]);
|
2018-01-12 07:34:34 -08:00
|
|
|
|
require(Helpers.addressArrayContains(authorities, a));
|
|
|
|
|
require(!Helpers.addressArrayContains(used, a));
|
2017-06-13 02:18:03 -07:00
|
|
|
|
used[i] = a;
|
|
|
|
|
}
|
|
|
|
|
_;
|
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Constructor.
|
2018-01-11 05:20:48 -08:00
|
|
|
|
function HomeBridge(
|
2018-01-04 04:59:02 -08:00
|
|
|
|
uint requiredSignaturesParam,
|
2018-01-04 06:14:34 -08:00
|
|
|
|
address[] authoritiesParam,
|
|
|
|
|
uint estimatedGasCostOfWithdrawParam
|
2018-01-04 06:29:54 -08:00
|
|
|
|
) public
|
|
|
|
|
{
|
2018-01-04 04:59:02 -08:00
|
|
|
|
require(requiredSignaturesParam != 0);
|
|
|
|
|
require(requiredSignaturesParam <= authoritiesParam.length);
|
|
|
|
|
requiredSignatures = requiredSignaturesParam;
|
|
|
|
|
authorities = authoritiesParam;
|
2018-01-04 06:30:10 -08:00
|
|
|
|
estimatedGasCostOfWithdraw = estimatedGasCostOfWithdrawParam;
|
2017-06-13 02:18:03 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Should be used to deposit money.
|
2017-12-13 06:28:44 -08:00
|
|
|
|
function () public payable {
|
2017-06-13 02:18:03 -07:00
|
|
|
|
Deposit(msg.sender, msg.value);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-05 07:31:23 -08:00
|
|
|
|
/// to be called by authorities to check
|
|
|
|
|
/// whether they withdraw message should be relayed or whether it
|
|
|
|
|
/// is too low to cover the cost of calling withdraw and can be ignored
|
2018-01-08 02:42:33 -08:00
|
|
|
|
function isMessageValueSufficientToCoverRelay(bytes message) public view returns (bool) {
|
2018-01-12 08:28:02 -08:00
|
|
|
|
return Message.getValue(message) > getWithdrawRelayCost();
|
2018-01-05 07:31:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-05 07:30:47 -08:00
|
|
|
|
/// an upper bound to the cost of relaying a withdraw by calling HomeBridge.withdraw
|
|
|
|
|
function getWithdrawRelayCost() public view returns (uint) {
|
|
|
|
|
return estimatedGasCostOfWithdraw * tx.gasprice;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-14 08:54:49 -08:00
|
|
|
|
/// Used to withdraw money from the contract.
|
2017-06-13 02:18:03 -07:00
|
|
|
|
///
|
|
|
|
|
/// message contains:
|
|
|
|
|
/// withdrawal recipient (bytes20)
|
|
|
|
|
/// withdrawal value (uint)
|
2017-10-10 02:02:46 -07:00
|
|
|
|
/// foreign transaction hash (bytes32) // to avoid transaction duplication
|
2017-12-14 08:54:49 -08:00
|
|
|
|
///
|
2018-01-04 06:15:02 -08:00
|
|
|
|
/// NOTE that anyone can call withdraw provided they have the message and required signatures!
|
2018-01-11 05:20:48 -08:00
|
|
|
|
function withdraw(uint8[] v, bytes32[] r, bytes32[] s, bytes message) public allAuthorities(v, r, s, message) {
|
2017-12-14 08:54:49 -08:00
|
|
|
|
require(message.length == 84);
|
2018-01-12 08:28:02 -08:00
|
|
|
|
address recipient = Message.getRecipient(message);
|
|
|
|
|
uint value = Message.getValue(message);
|
|
|
|
|
bytes32 hash = Message.getTransactionHash(message);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2018-01-04 06:42:36 -08:00
|
|
|
|
// The following two statements guard against reentry into this function.
|
|
|
|
|
// Duplicated withdraw or reentry.
|
2017-08-22 04:01:16 -07:00
|
|
|
|
require(!withdraws[hash]);
|
2018-01-04 05:14:29 -08:00
|
|
|
|
// Order of operations below is critical to avoid TheDAO-like re-entry bug
|
2017-06-13 02:18:03 -07:00
|
|
|
|
withdraws[hash] = true;
|
2018-01-04 06:42:36 -08:00
|
|
|
|
|
|
|
|
|
// this fails if `value` is not even enough to cover the relay cost.
|
|
|
|
|
// Authorities simply IGNORE withdraws where `value` can’t relay cost.
|
|
|
|
|
// Think of it as `value` getting burned entirely on the relay with no value left to pay out the recipient.
|
2018-01-08 02:42:33 -08:00
|
|
|
|
require(isMessageValueSufficientToCoverRelay(message));
|
2018-01-05 07:31:46 -08:00
|
|
|
|
|
|
|
|
|
uint estimatedWeiCostOfWithdraw = getWithdrawRelayCost();
|
2018-01-04 06:42:36 -08:00
|
|
|
|
|
|
|
|
|
// charge recipient for relay cost
|
|
|
|
|
uint valueRemainingAfterSubtractingCost = value - estimatedWeiCostOfWithdraw;
|
|
|
|
|
|
|
|
|
|
// pay out recipient
|
|
|
|
|
recipient.transfer(valueRemainingAfterSubtractingCost);
|
|
|
|
|
|
|
|
|
|
// refund relay cost to relaying authority
|
|
|
|
|
msg.sender.transfer(estimatedWeiCostOfWithdraw);
|
|
|
|
|
|
|
|
|
|
Withdraw(recipient, valueRemainingAfterSubtractingCost);
|
2017-06-13 02:18:03 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
|
contract ForeignBridge {
|
2018-01-17 07:22:57 -08:00
|
|
|
|
// following is the part of ForeignBridge that implements an ERC20 token
|
|
|
|
|
// ERC20 spec: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
|
|
|
|
|
|
|
|
|
|
uint public totalSupply;
|
|
|
|
|
|
2018-01-23 06:36:59 -08:00
|
|
|
|
string public name = "ForeignBridge";
|
2018-01-23 04:29:32 -08:00
|
|
|
|
|
2018-01-17 07:22:57 -08:00
|
|
|
|
/// maps addresses to their token balances
|
|
|
|
|
mapping (address => uint) public balances;
|
|
|
|
|
|
|
|
|
|
// Owner of account approves the transfer of an amount by another account
|
|
|
|
|
mapping(address => mapping (address => uint)) allowed;
|
|
|
|
|
|
|
|
|
|
/// Event created on money transfer
|
|
|
|
|
event Transfer(address indexed from, address indexed to, uint tokens);
|
|
|
|
|
|
|
|
|
|
function balanceOf(address tokenOwner) public view returns (uint) {
|
|
|
|
|
return balances[tokenOwner];
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-23 03:14:25 -08:00
|
|
|
|
/// Transfer `value` to `recipient` on this `foreign` chain.
|
|
|
|
|
///
|
|
|
|
|
/// does not affect `home` chain. does not do a relay.
|
2018-01-23 04:45:17 -08:00
|
|
|
|
/// note that as specificed in ERC20 this doesn't fail if tokens == 0
|
2018-01-23 03:14:25 -08:00
|
|
|
|
function transfer(address recipient, uint tokens) public returns (bool) {
|
|
|
|
|
require(balances[msg.sender] >= tokens);
|
|
|
|
|
// fails if there is an overflow
|
|
|
|
|
require(balances[recipient] + tokens >= balances[recipient]);
|
2018-01-17 07:22:57 -08:00
|
|
|
|
|
2018-01-23 03:14:25 -08:00
|
|
|
|
balances[msg.sender] -= tokens;
|
|
|
|
|
balances[recipient] += tokens;
|
|
|
|
|
Transfer(msg.sender, recipient, tokens);
|
2018-01-17 07:22:57 -08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-23 04:29:50 -08:00
|
|
|
|
// following is the part of ForeignBridge that is concerned
|
|
|
|
|
// with the part of the ERC20 standard responsible for giving others spending rights
|
|
|
|
|
// and spending others tokens
|
2018-01-23 03:14:25 -08:00
|
|
|
|
|
|
|
|
|
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
|
|
|
|
|
|
2018-01-17 07:22:57 -08:00
|
|
|
|
// Allow `spender` to withdraw from your account, multiple times, up to the `tokens` amount.
|
|
|
|
|
// If this function is called again it overwrites the current allowance with _value.
|
|
|
|
|
function approve(address spender, uint tokens) public returns (bool) {
|
|
|
|
|
allowed[msg.sender][spender] = tokens;
|
|
|
|
|
Approval(msg.sender, spender, tokens);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns how much `spender` is allowed to spend of `owner`s tokens
|
|
|
|
|
function allowance(address owner, address spender) public view returns (uint256) {
|
|
|
|
|
return allowed[owner][spender];
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-23 03:14:25 -08:00
|
|
|
|
function transferFrom(address from, address to, uint tokens) public returns (bool) {
|
|
|
|
|
// `from` has enough tokens
|
|
|
|
|
require(balances[from] >= tokens);
|
|
|
|
|
// `sender` is allowed to move `tokens` from `from`
|
|
|
|
|
require(allowed[from][msg.sender] >= tokens);
|
2018-01-17 07:22:57 -08:00
|
|
|
|
|
2018-01-23 03:14:25 -08:00
|
|
|
|
balances[to] += tokens;
|
|
|
|
|
balances[from] -= tokens;
|
|
|
|
|
allowed[from][msg.sender] -= tokens;
|
|
|
|
|
|
|
|
|
|
Transfer(from, to, tokens);
|
2018-01-17 07:22:57 -08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-23 04:29:50 -08:00
|
|
|
|
// following is the part of ForeignBridge that is
|
|
|
|
|
// no longer part of ERC20 and is concerned with
|
2018-01-23 03:14:25 -08:00
|
|
|
|
// with moving tokens from and to HomeBridge
|
2018-01-17 07:22:57 -08:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
struct SignaturesCollection {
|
|
|
|
|
/// Signed message.
|
|
|
|
|
bytes message;
|
2017-08-22 04:47:01 -07:00
|
|
|
|
/// Authorities who signed the message.
|
2017-06-13 02:18:03 -07:00
|
|
|
|
address[] signed;
|
2018-01-17 06:26:49 -08:00
|
|
|
|
/// Signatures
|
2017-08-22 04:47:01 -07:00
|
|
|
|
bytes[] signatures;
|
2017-06-13 02:18:03 -07:00
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Number of authorities signatures required to withdraw the money.
|
|
|
|
|
///
|
2018-01-17 06:29:08 -08:00
|
|
|
|
/// Must be less than number of authorities.
|
2017-09-01 06:44:36 -07:00
|
|
|
|
uint public requiredSignatures;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Contract authorities.
|
2017-09-01 06:44:36 -07:00
|
|
|
|
address[] public authorities;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Pending deposits and authorities who confirmed them
|
|
|
|
|
mapping (bytes32 => address[]) deposits;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Pending signatures and authorities who confirmed them
|
|
|
|
|
mapping (bytes32 => SignaturesCollection) signatures;
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2018-01-17 06:29:51 -08:00
|
|
|
|
/// triggered when relay of deposit from HomeBridge is complete
|
2017-08-21 08:32:37 -07:00
|
|
|
|
event Deposit(address recipient, uint value);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Event created on money withdraw.
|
2017-08-21 08:32:37 -07:00
|
|
|
|
event Withdraw(address recipient, uint value);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
|
/// Collected signatures which should be relayed to home chain.
|
2018-01-17 06:30:59 -08:00
|
|
|
|
event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2018-01-04 05:12:03 -08:00
|
|
|
|
function ForeignBridge(
|
2018-01-17 07:22:57 -08:00
|
|
|
|
uint _requiredSignatures,
|
|
|
|
|
address[] _authorities
|
2018-01-04 06:29:54 -08:00
|
|
|
|
) public
|
|
|
|
|
{
|
2018-01-17 07:22:57 -08:00
|
|
|
|
require(_requiredSignatures != 0);
|
|
|
|
|
require(_requiredSignatures <= _authorities.length);
|
|
|
|
|
requiredSignatures = _requiredSignatures;
|
|
|
|
|
authorities = _authorities;
|
2018-01-17 02:16:08 -08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-17 07:22:01 -08:00
|
|
|
|
/// require that sender is an authority
|
2018-01-11 05:20:48 -08:00
|
|
|
|
modifier onlyAuthority() {
|
2018-01-12 07:34:34 -08:00
|
|
|
|
require(Helpers.addressArrayContains(authorities, msg.sender));
|
2017-06-13 02:18:03 -07:00
|
|
|
|
_;
|
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Used to deposit money to the contract.
|
|
|
|
|
///
|
|
|
|
|
/// deposit recipient (bytes20)
|
|
|
|
|
/// deposit value (uint)
|
|
|
|
|
/// mainnet transaction hash (bytes32) // to avoid transaction duplication
|
2018-01-11 05:20:48 -08:00
|
|
|
|
function deposit(address recipient, uint value, bytes32 transactionHash) public onlyAuthority() {
|
2018-01-17 07:22:17 -08:00
|
|
|
|
// Protection from misbehaving authority
|
2017-12-13 06:30:13 -08:00
|
|
|
|
var hash = keccak256(recipient, value, transactionHash);
|
2017-09-01 07:26:06 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
// Duplicated deposits
|
2018-01-12 07:34:34 -08:00
|
|
|
|
require(!Helpers.addressArrayContains(deposits[hash], msg.sender));
|
2017-06-13 02:18:03 -07:00
|
|
|
|
|
|
|
|
|
deposits[hash].push(msg.sender);
|
2018-01-17 02:11:32 -08:00
|
|
|
|
// TODO: this may cause troubles if requiredSignatures len is changed
|
2017-06-13 02:18:03 -07:00
|
|
|
|
if (deposits[hash].length == requiredSignatures) {
|
|
|
|
|
balances[recipient] += value;
|
2018-01-23 03:14:25 -08:00
|
|
|
|
// mints tokens
|
2018-01-17 02:10:51 -08:00
|
|
|
|
totalSupply += value;
|
2018-01-23 06:34:16 -08:00
|
|
|
|
// ERC20 specifies: a token contract which creates new tokens
|
|
|
|
|
// SHOULD trigger a Transfer event with the _from address
|
|
|
|
|
// set to 0x0 when tokens are created.
|
|
|
|
|
Transfer(0x0, recipient, value);
|
2017-06-13 02:18:03 -07:00
|
|
|
|
Deposit(recipient, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2018-01-11 05:15:51 -08:00
|
|
|
|
/// 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, uint value) public {
|
2017-08-22 04:01:16 -07:00
|
|
|
|
require(balances[msg.sender] >= value);
|
2018-01-23 06:27:10 -08:00
|
|
|
|
// don't allow 0 value transfers to home
|
2018-01-23 05:07:06 -08:00
|
|
|
|
require(value > 0);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
balances[msg.sender] -= value;
|
2018-01-23 03:14:25 -08:00
|
|
|
|
// burns tokens
|
2018-01-17 02:10:51 -08:00
|
|
|
|
totalSupply -= value;
|
2018-01-23 06:35:17 -08:00
|
|
|
|
// in line with the transfer event from `0x0` on token creation
|
|
|
|
|
// recommended by ERC20 (see implementation of `deposit` above)
|
|
|
|
|
// we trigger a Transfer event to `0x0` on token destruction
|
|
|
|
|
Transfer(msg.sender, 0x0, value);
|
2018-01-11 05:15:51 -08:00
|
|
|
|
Withdraw(recipient, value);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Should be used as sync tool
|
2017-08-25 07:27:55 -07:00
|
|
|
|
///
|
2017-06-13 02:18:03 -07:00
|
|
|
|
/// Message is a message that should be relayed to main chain once authorities sign it.
|
2017-08-25 07:27:55 -07:00
|
|
|
|
///
|
2017-08-23 10:09:51 -07:00
|
|
|
|
/// for withdraw message contains:
|
|
|
|
|
/// withdrawal recipient (bytes20)
|
|
|
|
|
/// withdrawal value (uint)
|
2017-10-10 02:02:46 -07:00
|
|
|
|
/// foreign transaction hash (bytes32) // to avoid transaction duplication
|
2018-01-11 05:20:48 -08:00
|
|
|
|
function submitSignature(bytes signature, bytes message) public onlyAuthority() {
|
2017-09-04 03:49:05 -07:00
|
|
|
|
// Validate submited signatures
|
2018-01-12 07:46:00 -08:00
|
|
|
|
require(MessageSigning.recoverAddressFromSignedMessage(signature, message) == msg.sender);
|
2017-09-04 03:49:05 -07:00
|
|
|
|
|
2017-09-04 03:50:43 -07:00
|
|
|
|
// Valid withdraw message must have 84 bytes
|
|
|
|
|
require(message.length == 84);
|
2017-12-13 06:30:13 -08:00
|
|
|
|
var hash = keccak256(message);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
|
// Duplicated signatures
|
2018-01-12 07:34:34 -08:00
|
|
|
|
require(!Helpers.addressArrayContains(signatures[hash].signed, msg.sender));
|
2017-06-13 02:18:03 -07:00
|
|
|
|
signatures[hash].message = message;
|
|
|
|
|
signatures[hash].signed.push(msg.sender);
|
2017-08-22 04:47:01 -07:00
|
|
|
|
signatures[hash].signatures.push(signature);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-12-14 08:54:49 -08:00
|
|
|
|
// TODO: this may cause troubles if requiredSignatures len is changed
|
2017-06-13 02:18:03 -07:00
|
|
|
|
if (signatures[hash].signed.length == requiredSignatures) {
|
2017-08-22 04:53:40 -07:00
|
|
|
|
CollectedSignatures(msg.sender, hash);
|
2017-06-13 02:18:03 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
|
|
|
|
/// Get signature
|
2018-01-11 05:20:48 -08:00
|
|
|
|
function signature(bytes32 hash, uint index) public view returns (bytes) {
|
2017-08-22 04:47:01 -07:00
|
|
|
|
return signatures[hash].signatures[index];
|
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
|
2017-08-22 04:53:40 -07:00
|
|
|
|
/// Get message
|
2018-01-11 05:20:48 -08:00
|
|
|
|
function message(bytes32 hash) public view returns (bytes) {
|
2017-08-22 04:53:40 -07:00
|
|
|
|
return signatures[hash].message;
|
|
|
|
|
}
|
2017-08-21 08:32:37 -07:00
|
|
|
|
}
|