-
Notifications
You must be signed in to change notification settings - Fork 228
/
U_ForeignBridge.sol
292 lines (234 loc) · 11.1 KB
/
U_ForeignBridge.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
pragma solidity 0.4.23;
import "../libraries/SafeMath.sol";
import "../libraries/Message.sol";
import "./U_BasicBridge.sol";
import "../IBurnableMintableERC677Token.sol";
import "../ERC677Receiver.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol";
contract ForeignBridge is ERC677Receiver, BasicBridge {
using SafeMath for uint256;
/// triggered when relay of deposit from HomeBridge is complete
event Deposit(address recipient, uint value, bytes32 transactionHash);
/// Event created on money withdraw.
event Withdraw(address recipient, uint256 value, uint256 homeGasPrice);
/// Collected signatures which should be relayed to home chain.
event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash, uint256 NumberOfCollectedSignatures);
event GasConsumptionLimitsUpdated(uint256 gasLimitDepositRelay, uint256 gasLimitWithdrawConfirm);
event SignedForDeposit(address indexed signer, bytes32 transactionHash);
event SignedForWithdraw(address indexed signer, bytes32 messageHash);
event DailyLimit(uint256 newLimit);
function initialize(
address _validatorContract,
address _erc677token,
uint256 _foreignDailyLimit,
uint256 _maxPerTx,
uint256 _minPerTx,
uint256 _foreignGasPrice,
uint256 _requiredBlockConfirmations
) public returns(bool) {
require(!isInitialized());
require(_validatorContract != address(0));
require(_minPerTx > 0 && _maxPerTx > _minPerTx && _foreignDailyLimit > _maxPerTx);
require(_foreignGasPrice > 0);
addressStorage[keccak256("validatorContract")] = _validatorContract;
setErc677token(_erc677token);
uintStorage[keccak256("foreignDailyLimit")] = _foreignDailyLimit;
uintStorage[keccak256("deployedAtBlock")] = block.number;
uintStorage[keccak256("maxPerTx")] = _maxPerTx;
uintStorage[keccak256("minPerTx")] = _minPerTx;
uintStorage[keccak256("gasPrice")] = _foreignGasPrice;
uintStorage[keccak256("requiredBlockConfirmations")] = _requiredBlockConfirmations;
setInitialize(true);
return isInitialized();
}
function onTokenTransfer(address _from, uint256 _value, bytes /*_data*/) external returns(bool) {
require(msg.sender == address(erc677token()));
require(withinLimit(_value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value));
erc677token().burn(_value);
emit Withdraw(_from, _value, gasPriceForCompensationAtHomeSide());
return true;
}
function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < foreignDailyLimit());
uintStorage[keccak256("maxPerTx")] = _maxPerTx;
}
function setMinPerTx(uint256 _minPerTx) external onlyOwner {
require(_minPerTx < foreignDailyLimit() && _minPerTx < maxPerTx());
uintStorage[keccak256("minPerTx")] = _minPerTx;
}
function claimTokens(address _token, address _to) external onlyOwner {
require(_to != address(0));
if (_token == address(0)) {
_to.transfer(address(this).balance);
return;
}
ERC20Basic token = ERC20Basic(_token);
uint256 balance = token.balanceOf(this);
require(token.transfer(_to, balance));
}
function claimTokensFromErc677(address _token, address _to) external onlyOwner {
erc677token().claimTokens(_token, _to);
}
function minPerTx() public view returns(uint256) {
return uintStorage[keccak256("minPerTx")];
}
function maxPerTx() public view returns(uint256) {
return uintStorage[keccak256("maxPerTx")];
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256("totalSpentPerDay", _day)];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256("deployedAtBlock")];
}
function gasLimitDepositRelay() public view returns(uint256) {
return uintStorage[keccak256("gasLimitDepositRelay")];
}
function gasLimitWithdrawConfirm() public view returns(uint256) {
return uintStorage[keccak256("gasLimitWithdrawConfirm")];
}
function foreignDailyLimit() public view returns(uint256) {
return uintStorage[keccak256("foreignDailyLimit")];
}
function erc677token() public view returns(IBurnableMintableERC677Token) {
return IBurnableMintableERC677Token(addressStorage[keccak256("erc677token")]);
}
function setGasLimits(uint256 _gasLimitDepositRelay, uint256 _gasLimitWithdrawConfirm) external onlyOwner {
uintStorage[keccak256("gasLimitDepositRelay")] = _gasLimitDepositRelay;
uintStorage[keccak256("gasLimitWithdrawConfirm")] = _gasLimitWithdrawConfirm;
emit GasConsumptionLimitsUpdated(gasLimitDepositRelay(), gasLimitWithdrawConfirm());
}
function deposit(address recipient, uint256 value, bytes32 transactionHash) external onlyValidator {
bytes32 hashMsg = keccak256(recipient, value, transactionHash);
bytes32 hashSender = keccak256(msg.sender, hashMsg);
// Duplicated deposits
require(!depositsSigned(hashSender));
setDepositsSigned(hashSender, true);
uint256 signed = numDepositsSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
setNumDepositsSigned(hashMsg, signed);
emit SignedForDeposit(msg.sender, transactionHash);
if (signed >= validatorContract().requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
setNumDepositsSigned(hashMsg, markAsProcessed(signed));
erc677token().mint(recipient, value);
emit Deposit(recipient, value, transactionHash);
}
}
/// 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) external onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(Message.isMessageValid(message));
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message));
bytes32 hashMsg = keccak256(message);
bytes32 hashSender = keccak256(msg.sender, hashMsg);
uint256 signed = numMessagesSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
} else {
setMessages(hashMsg, message);
}
setMessagesSigned(hashSender, true);
bytes32 signIdx = keccak256(hashMsg, (signed-1));
setSignatures(signIdx, signature);
setNumMessagesSigned(hashMsg, signed);
emit SignedForWithdraw(msg.sender, hashMsg);
uint256 reqSigs = validatorContract().requiredSignatures();
if (signed >= reqSigs) {
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
emit CollectedSignatures(msg.sender, hashMsg, reqSigs);
}
}
function gasPriceForCompensationAtHomeSide() public pure returns(uint256) {
return 1000000000 wei;
}
function isAlreadyProcessed(uint256 _number) public pure returns(bool) {
return _number & 2**255 == 2**255;
}
function signature(bytes32 _hash, uint256 _index) public view returns (bytes) {
bytes32 signIdx = keccak256(_hash, _index);
return signatures(signIdx);
}
/// Get message
function message(bytes32 _hash) public view returns (bytes) {
return messages(_hash);
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function setForeignDailyLimit(uint256 _foreignDailyLimit) public onlyOwner {
uintStorage[keccak256("foreignDailyLimit")] = _foreignDailyLimit;
emit DailyLimit(_foreignDailyLimit);
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return foreignDailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256("isInitialized")];
}
function messages(bytes32 _hash) private view returns(bytes) {
return bytesStorage[keccak256("messages", _hash)];
}
function setMessages(bytes32 _hash, bytes _message) private {
bytesStorage[keccak256("messages", _hash)] = _message;
}
function signatures(bytes32 _hash) private view returns(bytes) {
return bytesStorage[keccak256("signatures", _hash)];
}
function setSignatures(bytes32 _hash, bytes _signature) private {
bytesStorage[keccak256("signatures", _hash)] = _signature;
}
function messagesSigned(bytes32 _message) public view returns(bool) {
return boolStorage[keccak256("messagesSigned", _message)];
}
function depositsSigned(bytes32 _deposit) public view returns(bool) {
return boolStorage[keccak256("depositsSigned", _deposit)];
}
function markAsProcessed(uint256 _v) private pure returns(uint256) {
return _v | 2 ** 255;
}
function numMessagesSigned(bytes32 _message) private view returns(uint256) {
return uintStorage[keccak256("numMessagesSigned", _message)];
}
function numDepositsSigned(bytes32 _deposit) private view returns(uint256) {
return uintStorage[keccak256("numDepositsSigned", _deposit)];
}
function setMessagesSigned(bytes32 _hash, bool _status) private {
boolStorage[keccak256("messagesSigned", _hash)] = _status;
}
function setDepositsSigned(bytes32 _deposit, bool _status) private {
boolStorage[keccak256("depositsSigned", _deposit)] = _status;
}
function setNumMessagesSigned(bytes32 _message, uint256 _number) private {
uintStorage[keccak256("numMessagesSigned", _message)] = _number;
}
function setNumDepositsSigned(bytes32 _deposit, uint256 _number) private {
uintStorage[keccak256("numDepositsSigned", _deposit)] = _number;
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) private {
uintStorage[keccak256("totalSpentPerDay", _day)] = _value;
}
function setErc677token(address _token) private {
require(_token != address(0));
addressStorage[keccak256("erc677token")] = _token;
}
function setInitialize(bool _status) private {
boolStorage[keccak256("isInitialized")] = _status;
}
}