poa-bridge/contracts/bridge.sol

248 lines
7.2 KiB
Solidity

pragma solidity ^0.4.11;
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;
}
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[];
/// Number of authorities signatures required to withdraw the money.
///
/// Must be lesser than number of authorities.
uint requiredSignatures;
/// Contract authorities.
address[] authorities;
/// Used kovan transaction hashes.
mapping (bytes32 => bool) withdraws;
/// Event created on money deposit.
event Deposit (address, uint);
/// Event created on money withdraw.
event Withdraw (address, uint);
/// Multisig authority validation
modifier allAuthorities (uint8[] v, bytes32[] r, bytes32[] s, bytes message) {
var hash = sha3(message);
var used = new address[](requiredSignatures);
if (v.length < requiredSignatures) {
throw;
}
for (uint i = 0; i < requiredSignatures; i++) {
var a = ecrecover(hash, v[i], r[i], s[i]);
if (!authorities.contains(a) || used.contains(a)) {
throw;
}
used[i] = a;
}
_;
}
/// Constructor.
function EthereumBridge (uint n, address[] a) {
if (requiredSignatures > a.length) {
throw;
}
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))
}
// Duplicated withdraw
if (withdraws[hash]) {
throw;
}
// Order of operations below is critical to avoid TheDAO-like bug
withdraws[hash] = true;
recipient.transfer(value);
Withdraw(recipient, value);
}
/// 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;
assembly {
newRequiredSignatures := mload(message)
newAuthoritiesNumber := mload(add(message, 0x32))
}
if (newRequiredSignatures > newAuthoritiesNumber) {
throw;
}
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[];
struct SignaturesCollection {
/// Signed message.
bytes message;
/// Authorities who signed signature.
address[] signed;
uint8[] r;
bytes32[] s;
bytes32[] v;
}
/// Number of authorities signatures required to withdraw the money.
///
/// Must be lesser than number of authorities.
uint requiredSignatures;
/// Contract authorities.
address[] authorities;
/// Ether balances
mapping (address => uint) balances;
/// Pending deposits and authorities who confirmed them
mapping (bytes32 => address[]) deposits;
/// Pending signatures and authorities who confirmed them
mapping (bytes32 => SignaturesCollection) signatures;
/// Event created on money deposit.
event Deposit(address, uint);
/// Event created on money withdraw.
event Withdraw(address, uint);
/// Event created on money transfer
event Transfer(address from, address to, uint);
/// Collected signatures which should be relayed to ehtereum chain.
event CollectedSignatures(bytes message, uint8[] r, bytes32[] s, bytes32[] v);
/// Constructor.
function KovanBridge(uint n, address[] a) {
if (requiredSignatures > a.length) {
throw;
}
requiredSignatures = n;
authorities = a;
}
/// Multisig authority validation
modifier onlyAuthority () {
if (!authorities.contains(msg.sender)) {
throw;
}
_;
}
/// Used to deposit money to the contract.
///
/// message contains:
/// 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
if (deposits[hash].contains(msg.sender)) {
throw;
}
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);
}
}
/// Used to transfer money between accounts
function transfer (address recipient, uint value, bool externalTransfer) {
if (balances[msg.sender] < value) {
throw;
}
balances[msg.sender] -= value;
if (externalTransfer) {
Withdraw(recipient, value);
} else {
balances[recipient] += value;
Transfer(msg.sender, recipient, value);
}
}
/// Should be used as sync tool
///
/// Message is a message that should be relayed to main chain once authorities sign it.
function collectSignatures (uint8 r, bytes32 s, bytes32 v, bytes message) onlyAuthority() {
var hash = sha3(message);
// Duplicated signatures
if (signatures[hash].signed.contains(msg.sender)) {
throw;
}
signatures[hash].message = message;
signatures[hash].signed.push(msg.sender);
signatures[hash].r.push(r);
signatures[hash].s.push(s);
signatures[hash].v.push(v);
// TODO: this may cause troubles if requriedSignatures len is changed
if (signatures[hash].signed.length == requiredSignatures) {
CollectedSignatures(message, signatures[hash].r, signatures[hash].s, signatures[hash].v);
}
}
}