This repository has been archived by the owner on Sep 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 80
/
bridge.sol
296 lines (249 loc) · 9.65 KB
/
bridge.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
pragma solidity ^0.4.15;
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;
}
}
/// Library used only to test Signer library via rpc calls
library SignerTest {
function signer (bytes signature, bytes message) constant returns (address) {
return Signer.signer(signature, message);
}
}
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);
}
bytes memory s = new bytes(i);
for (uint j = 0; j < i; j++) {
s[j] = reversed[i - j - 1];
}
str = string(s);
}
}
library Signer {
function signer (bytes signature, bytes message) internal returns (address) {
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);
}
}
contract HomeBridge {
using Authorities for address[];
/// Number of authorities signatures required to withdraw the money.
///
/// Must be lesser than number of authorities.
uint public requiredSignatures;
/// Contract authorities.
address[] public authorities;
/// Used foreign transaction hashes.
mapping (bytes32 => bool) withdraws;
/// Event created on money deposit.
event Deposit (address recipient, uint value);
/// Event created on money withdraw.
event Withdraw (address recipient, uint value);
/// Multisig authority validation
modifier allAuthorities (uint8[] v, bytes32[] r, bytes32[] s, bytes message) {
var hash = Signer.hash(message);
var used = new address[](requiredSignatures);
require(requiredSignatures <= v.length);
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));
used[i] = a;
}
_;
}
/// Constructor.
function HomeBridge (uint n, address[] a) {
require(n != 0);
require(n <= a.length);
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)
/// foreign 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(add(message, 20))
value := mload(add(message, 52))
hash := mload(add(message, 84))
}
// Duplicated withdraw
require(!withdraws[hash]);
// Order of operations below is critical to avoid TheDAO-like bug
withdraws[hash] = true;
recipient.transfer(value);
Withdraw(recipient, value);
}
}
contract ForeignBridge {
using Authorities for address[];
struct SignaturesCollection {
/// Signed message.
bytes message;
/// Authorities who signed the message.
address[] signed;
/// Signaturs
bytes[] signatures;
}
/// Number of authorities signatures required to withdraw the money.
///
/// Must be lesser than number of authorities.
uint public requiredSignatures;
uint public withdrawRelayGasCostPerAuthority;
/// Contract authorities.
address[] public authorities;
/// Ether balances
mapping (address => uint) public 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 recipient, uint value);
/// Event created on money withdraw.
event Withdraw(address recipient, uint value);
/// Event created on money transfer
event Transfer(address from, address to, uint value);
/// Collected signatures which should be relayed to home chain.
event CollectedSignatures(address authority, bytes32 messageHash);
/// Constructor.
function ForeignBridge(
uint requiredSignaturesParam,
address[] authoritiesParam,
uint withdrawRelayGasCostPerAuthorityParam
) {
require(requiredSignaturesParam != 0);
require(requiredSignaturesParam <= authoritiesParam.length);
requiredSignatures = requiredSignaturesParam;
authorities = authoritiesParam;
withdrawRelayGasCostPerAuthority = withdrawRelayGasCostPerAuthorityParam;
}
/// Multisig authority validation
modifier onlyAuthority () {
require(authorities.contains(msg.sender));
_;
}
/// 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 transactionHash) onlyAuthority() {
// Protection from misbehaing authority
var hash = sha3(recipient, value, transactionHash);
// Duplicated deposits
require(!deposits[hash].contains(msg.sender));
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 (externalTransfer) {
transferToHomeChain(recipient, value);
} else {
transferOnChain(recipient, value);
}
}
/// Used to transfer money to another account on the same chain
function transferOnChain(address recipient, uint value) {
require(balances[msg.sender] >= value);
// fails if value == 0, or if there is an overflow
require(balances[recipient] + value > balances[recipient]);
balances[msg.sender] -= value;
balances[recipient] += value;
Transfer(msg.sender, recipient, value);
}
/// Used to transfer money to an account on the home chain.
/// transfer will get relayed by authorities.
function transferToHomeChain(address recipient, uint value) {
// note that this is higher than the actual relay cost.
// except when requiredSignatures = authorities.length.
// the actual relay cost is:
// withdrawRelayGasCostPerAuthority * requiredSignatures
// we can't use that though since we have and at this point there's no way
// of knowing which authorities will do the relay.
var withdrawRelayWeiCostPerAuthority = withdrawRelayGasCostPerAuthority * tx.gasprice;
var totalRelayWeiCost = withdrawRelayWeiCostPerAuthority * authorities.length;
var amountToWithdraw = value + totalRelayWeiCost;
require(balances[msg.sender] >= amountToWithdraw);
// fails if value == 0, or if there is an overflow
require(balances[recipient] + value > balances[recipient]);
balances[msg.sender] -= amountToWithdraw;
for (uint i = 0; i < authorities.length; i++) {
balances[authorities[i]] += withdrawRelayWeiCostPerAuthority;
}
Withdraw(recipient, value);
}
/// Should be used as sync tool
///
/// Message is a message that should be relayed to main chain once authorities sign it.
///
/// for withdraw message contains:
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature (bytes signature, bytes message) onlyAuthority() {
// Validate submited signatures
require(Signer.signer(signature, message) == msg.sender);
// Valid withdraw message must have 84 bytes
require(message.length == 84);
var hash = sha3(message);
// Duplicated signatures
require(!signatures[hash].signed.contains(msg.sender));
signatures[hash].message = message;
signatures[hash].signed.push(msg.sender);
signatures[hash].signatures.push(signature);
// TODO: this may cause troubles if requriedSignatures len is changed
if (signatures[hash].signed.length == requiredSignatures) {
CollectedSignatures(msg.sender, hash);
}
}
/// Get signature
function signature (bytes32 hash, uint index) constant returns (bytes) {
return signatures[hash].signatures[index];
}
/// Get message
function message (bytes32 hash) constant returns (bytes) {
return signatures[hash].message;
}
}