2017-09-01 06:11:20 -07:00
|
|
|
pragma solidity ^0.4.15;
|
2017-06-13 02:18:03 -07:00
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
library Authorities {
|
|
|
|
function contains (address[] self, address value) internal returns (bool) {
|
|
|
|
for (uint i = 0; i < self.length; i++) {
|
|
|
|
if (self[i] == value) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
2017-09-04 03:49:05 -07:00
|
|
|
/// Library used only to test Signer library via rpc calls
|
|
|
|
library SignerTest {
|
2017-12-13 06:28:44 -08:00
|
|
|
function signer (bytes signature, bytes message) public constant returns (address) {
|
2017-09-04 07:02:41 -07:00
|
|
|
return Signer.signer(signature, message);
|
2017-09-04 03:49:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
2017-09-04 03:49:05 -07:00
|
|
|
library Utils {
|
|
|
|
function toString (uint256 v) internal returns (string str) {
|
|
|
|
// it is used only for small numbers
|
|
|
|
bytes memory reversed = new bytes(8);
|
|
|
|
uint i = 0;
|
|
|
|
while (v != 0) {
|
|
|
|
uint remainder = v % 10;
|
|
|
|
v = v / 10;
|
|
|
|
reversed[i++] = byte(48 + remainder);
|
|
|
|
}
|
2017-09-04 03:50:43 -07:00
|
|
|
bytes memory s = new bytes(i);
|
|
|
|
for (uint j = 0; j < i; j++) {
|
|
|
|
s[j] = reversed[i - j - 1];
|
|
|
|
}
|
|
|
|
str = string(s);
|
2017-09-04 03:49:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
2017-09-04 03:49:05 -07:00
|
|
|
library Signer {
|
2017-09-04 07:02:41 -07:00
|
|
|
function signer (bytes signature, bytes message) internal returns (address) {
|
2017-09-04 03:49:05 -07:00
|
|
|
require(signature.length == 65);
|
|
|
|
bytes32 r;
|
|
|
|
bytes32 s;
|
|
|
|
bytes1 v;
|
|
|
|
assembly {
|
|
|
|
r := mload(add(signature, 0x20))
|
|
|
|
s := mload(add(signature, 0x40))
|
|
|
|
v := mload(add(signature, 0x60))
|
|
|
|
}
|
|
|
|
return ecrecover(hash(message), uint8(v), r, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
function hash (bytes message) internal returns (bytes32) {
|
|
|
|
bytes memory prefix = "\x19Ethereum Signed Message:\n";
|
|
|
|
return sha3(prefix, Utils.toString(message.length), message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
contract HomeBridge {
|
2017-06-13 02:18:03 -07:00
|
|
|
using Authorities for address[];
|
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.
|
|
|
|
///
|
|
|
|
/// 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
|
|
|
|
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
|
|
|
|
modifier allAuthorities (uint8[] v, bytes32[] r, bytes32[] s, bytes message) {
|
2017-09-04 03:49:05 -07:00
|
|
|
var hash = Signer.hash(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]);
|
2017-08-22 04:01:16 -07:00
|
|
|
require(authorities.contains(a));
|
|
|
|
require(!used.contains(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.
|
2017-12-13 06:28:44 -08:00
|
|
|
function HomeBridge (uint n, address[] a) public {
|
2017-09-04 03:50:43 -07:00
|
|
|
require(n != 0);
|
2017-09-01 06:11:20 -07:00
|
|
|
require(n <= a.length);
|
2017-06-13 02:18:03 -07:00
|
|
|
requiredSignatures = n;
|
|
|
|
authorities = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
///
|
|
|
|
/// NOTE that anyone can call withdraw provided they have the
|
|
|
|
/// message and required signatures!
|
2017-12-13 06:28:44 -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);
|
2017-06-13 02:18:03 -07:00
|
|
|
address recipient;
|
|
|
|
uint value;
|
|
|
|
bytes32 hash;
|
|
|
|
assembly {
|
2017-12-14 08:54:49 -08:00
|
|
|
// 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
|
|
|
|
|
|
|
|
// we require above that message length == 84.
|
|
|
|
// 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
|
|
|
|
recipient := mload(add(message, 20))
|
|
|
|
value := mload(add(message, 52))
|
|
|
|
hash := mload(add(message, 84))
|
2017-06-13 02:18:03 -07:00
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
// Duplicated withdraw
|
2017-08-22 04:01:16 -07:00
|
|
|
require(!withdraws[hash]);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
// Order of operations below is critical to avoid TheDAO-like bug
|
|
|
|
withdraws[hash] = true;
|
|
|
|
recipient.transfer(value);
|
|
|
|
Withdraw(recipient, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 06:20:56 -08:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
contract ForeignBridge {
|
2017-06-13 02:18:03 -07:00
|
|
|
using Authorities for address[];
|
2017-08-25 07:27:55 -07: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;
|
2017-08-22 04:47:01 -07:00
|
|
|
/// Signaturs
|
|
|
|
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.
|
|
|
|
///
|
|
|
|
/// Must be lesser 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
|
|
|
/// Ether balances
|
2017-09-01 06:44:36 -07:00
|
|
|
mapping (address => uint) public balances;
|
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
|
|
|
|
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
|
|
|
/// Event created on money transfer
|
2017-08-21 08:32:37 -07:00
|
|
|
event Transfer(address from, address to, 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.
|
2017-08-22 04:53:40 -07:00
|
|
|
event CollectedSignatures(address authority, bytes32 messageHash);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
/// Constructor.
|
2017-12-13 06:28:44 -08:00
|
|
|
function ForeignBridge(uint n, address[] a) public {
|
2017-09-04 03:50:43 -07:00
|
|
|
require(n != 0);
|
2017-09-01 06:44:36 -07:00
|
|
|
require(n <= a.length);
|
2017-06-13 02:18:03 -07:00
|
|
|
requiredSignatures = n;
|
|
|
|
authorities = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Multisig authority validation
|
|
|
|
modifier onlyAuthority () {
|
2017-08-22 04:01:16 -07:00
|
|
|
require(authorities.contains(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
|
2017-12-13 06:28:44 -08:00
|
|
|
function deposit (address recipient, uint value, bytes32 transactionHash) public onlyAuthority() {
|
2017-09-04 03:50:43 -07:00
|
|
|
// Protection from misbehaing authority
|
|
|
|
var hash = sha3(recipient, value, transactionHash);
|
2017-09-01 07:26:06 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
// Duplicated deposits
|
2017-08-22 04:01:16 -07:00
|
|
|
require(!deposits[hash].contains(msg.sender));
|
2017-06-13 02:18:03 -07:00
|
|
|
|
|
|
|
deposits[hash].push(msg.sender);
|
|
|
|
// TODO: this may cause troubles if requriedSignatures len is changed
|
|
|
|
if (deposits[hash].length == requiredSignatures) {
|
|
|
|
balances[recipient] += value;
|
|
|
|
Deposit(recipient, value);
|
|
|
|
}
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
/// Used to transfer money between accounts
|
|
|
|
function transfer (address recipient, uint value, bool externalTransfer) {
|
2017-08-22 04:01:16 -07:00
|
|
|
require(balances[msg.sender] >= value);
|
2017-10-23 22:23:17 -07:00
|
|
|
// fails if value == 0, or if there is an overflow
|
|
|
|
require(balances[recipient] + value > balances[recipient]);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
balances[msg.sender] -= value;
|
|
|
|
if (externalTransfer) {
|
|
|
|
Withdraw(recipient, value);
|
|
|
|
} else {
|
|
|
|
balances[recipient] += value;
|
|
|
|
Transfer(msg.sender, recipient, value);
|
|
|
|
}
|
|
|
|
}
|
2017-08-25 07:27:55 -07:00
|
|
|
|
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
|
2017-08-22 04:47:01 -07:00
|
|
|
function submitSignature (bytes signature, bytes message) onlyAuthority() {
|
2017-09-04 03:49:05 -07:00
|
|
|
// Validate submited signatures
|
2017-09-04 07:02:41 -07:00
|
|
|
require(Signer.signer(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-06-13 02:18:03 -07:00
|
|
|
var hash = sha3(message);
|
2017-08-25 07:27:55 -07:00
|
|
|
|
2017-06-13 02:18:03 -07:00
|
|
|
// Duplicated signatures
|
2017-08-22 04:01:16 -07:00
|
|
|
require(!signatures[hash].signed.contains(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
|
2017-12-13 06:28:44 -08:00
|
|
|
function signature (bytes32 hash, uint index) public constant 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
|
2017-12-13 06:28:44 -08:00
|
|
|
function message (bytes32 hash) public constant returns (bytes) {
|
2017-08-22 04:53:40 -07:00
|
|
|
return signatures[hash].message;
|
|
|
|
}
|
2017-08-21 08:32:37 -07:00
|
|
|
}
|