poa-bridge/contracts/bridge.sol

247 lines
7.5 KiB
Solidity
Raw Normal View History

2017-09-01 06:11:20 -07:00
pragma solidity ^0.4.15;
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-08-25 07:27:55 -07:00
2017-06-13 02:18:03 -07:00
function truncate (address[] storage self, uint len) internal {
for (uint i = len; i < self.length; i++) {
delete self[i];
}
self.length = len;
}
}
contract EthereumBridge {
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-06-13 02:18:03 -07:00
/// Used kovan transaction hashes.
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.
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.
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) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
var hash = sha3(prefix, message.length, message);
2017-06-13 02:18:03 -07:00
var used = new address[](requiredSignatures);
2017-08-25 07:27:55 -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]);
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.
function EthereumBridge (uint n, address[] a) {
2017-09-01 06:11:20 -07:00
require(n != 0);
require(n <= a.length);
2017-06-13 02:18:03 -07:00
requiredSignatures = n;
authorities = a;
}
/// Should be used to deposit money.
function () payable {
Deposit(msg.sender, msg.value);
}
/// Used to withdrawn money from the contract.
///
/// message contains:
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// kovan transaction hash (bytes32) // to avoid transaction duplication
function withdraw (uint8[] v, bytes32[] r, bytes32[] s, bytes message) allAuthorities(v, r, s, message) {
address recipient;
uint value;
bytes32 hash;
assembly {
recipient := mload(message)
value := mload(add(message, 0x32))
hash := mload(add(message, 0x64))
}
2017-08-25 07:27:55 -07:00
2017-06-13 02:18:03 -07:00
// Duplicated withdraw
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-08-25 07:27:55 -07:00
2017-06-13 02:18:03 -07:00
/// Used to elect new authorities.
///
// message contains:
// new requiredSignatures (uint)
// new number of authorities (uint)
// new authorities (bytes20)
function reelect (uint8[] v, bytes32[] r, bytes32[] s, bytes message) allAuthorities(v, r, s, message) {
uint newRequiredSignatures;
uint newAuthoritiesNumber;
address addressPtr;
2017-08-25 07:27:55 -07:00
2017-06-13 02:18:03 -07:00
assembly {
newRequiredSignatures := mload(message)
newAuthoritiesNumber := mload(add(message, 0x32))
}
require(newRequiredSignatures <= newAuthoritiesNumber);
2017-06-13 02:18:03 -07:00
authorities.truncate(newAuthoritiesNumber);
for (uint i = 0; i < newAuthoritiesNumber; i++) {
assembly {
let offset := add(0x64, mul(0x32, i))
addressPtr := mload(add(message, offset))
}
authorities[i] = addressPtr;
}
}
}
contract KovanBridge {
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.
uint requiredSignatures;
2017-08-25 07:27:55 -07:00
2017-06-13 02:18:03 -07:00
/// Contract authorities.
address[] authorities;
2017-08-25 07:27:55 -07:00
2017-06-13 02:18:03 -07:00
/// Ether balances
mapping (address => uint) 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.
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.
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
event Transfer(address from, address to, uint value);
2017-08-25 07:27:55 -07:00
2017-08-22 04:47:01 -07:00
/// Collected signatures which should be relayed to ethereum 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.
function KovanBridge(uint n, address[] a) {
require(requiredSignatures <= a.length);
2017-06-13 02:18:03 -07:00
requiredSignatures = n;
authorities = a;
}
/// Multisig authority validation
modifier onlyAuthority () {
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
function deposit (address recipient, uint value, bytes32 hash) onlyAuthority() {
// Duplicated deposits
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) {
require(balances[msg.sender] >= value);
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)
/// kovan transaction hash (bytes32) // to avoid transaction duplication
2017-08-22 04:47:01 -07:00
function submitSignature (bytes signature, bytes message) onlyAuthority() {
2017-08-28 03:28:21 -07:00
// Valid signature must have 65 bytes
require(signature.length == 65);
// 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
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-06-13 02:18:03 -07:00
// TODO: this may cause troubles if requriedSignatures len is changed
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-08-22 04:47:01 -07:00
function signature (bytes32 hash, uint index) constant returns (bytes) {
return signatures[hash].signatures[index];
}
2017-08-25 07:27:55 -07:00
2017-08-22 04:53:40 -07:00
/// Get message
function message (bytes32 hash) constant returns (bytes) {
return signatures[hash].message;
}
}