@keep-network/tbtc-v2 0.1.1-dev.40 → 0.1.1-dev.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/TBTC.json +3 -3
- package/artifacts/TBTCToken.json +3 -3
- package/artifacts/VendingMachine.json +10 -10
- package/artifacts/solcInputs/{f1a50b67569d88ee54efa3e22c6b484e.json → f2c15d3cf1bd9566483f595c5ed30ccc.json} +25 -25
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +2 -2
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.json +191 -331
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
- package/build/contracts/bridge/BridgeState.sol/BridgeState.json +35 -3
- package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
- package/build/contracts/bridge/Deposit.sol/Deposit.json +2 -2
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +2 -2
- package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
- package/build/contracts/bridge/Fraud.sol/Fraud.json +86 -0
- package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +4 -22
- package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
- package/build/contracts/bridge/{Redeem.sol → Redemption.sol}/OutboundTx.json +3 -3
- package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
- package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
- package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +1 -1
- package/build/contracts/bridge/Sweep.sol/Sweep.json +2 -2
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.json +2 -47
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/contracts/bridge/BitcoinTx.sol +19 -26
- package/contracts/bridge/Bridge.sol +476 -534
- package/contracts/bridge/BridgeState.sol +190 -129
- package/contracts/bridge/Deposit.sol +23 -4
- package/contracts/bridge/EcdsaLib.sol +15 -0
- package/contracts/bridge/{Frauds.sol → Fraud.sol} +75 -146
- package/contracts/bridge/MovingFunds.sol +15 -9
- package/contracts/bridge/{Redeem.sol → Redemption.sol} +19 -17
- package/contracts/bridge/Sweep.sol +16 -9
- package/contracts/bridge/Wallets.sol +40 -121
- package/package.json +1 -1
- package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +0 -4
- package/build/contracts/bridge/Frauds.sol/Frauds.json +0 -138
- package/build/contracts/bridge/Redeem.sol/OutboundTx.dbg.json +0 -4
- package/build/contracts/bridge/Redeem.sol/Redeem.dbg.json +0 -4
- package/build/contracts/bridge/Redeem.sol/Redeem.json +0 -110
|
@@ -18,32 +18,35 @@ pragma solidity ^0.8.9;
|
|
|
18
18
|
import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
|
|
19
19
|
import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
|
|
20
20
|
import {CheckBitcoinSigs} from "@keep-network/bitcoin-spv-sol/contracts/CheckBitcoinSigs.sol";
|
|
21
|
+
|
|
21
22
|
import "./BitcoinTx.sol";
|
|
22
23
|
import "./EcdsaLib.sol";
|
|
23
|
-
import "./
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
import "./BridgeState.sol";
|
|
25
|
+
import "./Wallets.sol";
|
|
26
|
+
|
|
27
|
+
/// @title Bridge fraud
|
|
28
|
+
/// @notice The library handles the logic for challenging Bridge wallets that
|
|
29
|
+
/// committed fraud.
|
|
30
|
+
/// @dev Anyone can submit a fraud challenge indicating that a UTXO being under
|
|
31
|
+
/// the wallet control was unlocked by the wallet but was not used
|
|
32
|
+
/// according to the protocol rules. That means the wallet signed
|
|
33
|
+
/// a transaction input pointing to that UTXO and there is a unique
|
|
34
|
+
/// sighash and signature pair associated with that input.
|
|
35
|
+
///
|
|
36
|
+
/// In order to defeat the challenge, the same wallet public key and
|
|
37
|
+
/// signature must be provided as were used to calculate the sighash during
|
|
38
|
+
/// the challenge. The wallet provides the preimage which produces sighash
|
|
39
|
+
/// used to generate the ECDSA signature that is the subject of the fraud
|
|
40
|
+
/// claim. The fraud challenge defeat attempt will only succeed if the
|
|
41
|
+
/// inputs in the preimage are considered honestly spent by the wallet.
|
|
42
|
+
/// Therefore the transaction spending the UTXO must be proven in the
|
|
43
|
+
/// Bridge before a challenge defeat is called.
|
|
44
|
+
library Fraud {
|
|
26
45
|
using BytesLib for bytes;
|
|
27
46
|
using BTCUtils for bytes;
|
|
28
47
|
using BTCUtils for uint32;
|
|
29
48
|
using EcdsaLib for bytes;
|
|
30
49
|
|
|
31
|
-
struct Data {
|
|
32
|
-
/// The amount of stake slashed from each member of a wallet for a fraud.
|
|
33
|
-
uint256 slashingAmount;
|
|
34
|
-
/// The percentage of the notifier reward from the staking contract
|
|
35
|
-
/// the notifier of a fraud receives. The value is in the range [0, 100].
|
|
36
|
-
uint256 notifierRewardMultiplier;
|
|
37
|
-
/// The amount of time the wallet has to defeat a fraud challenge.
|
|
38
|
-
uint256 challengeDefeatTimeout;
|
|
39
|
-
/// The amount of ETH in wei the party challenging the wallet for fraud
|
|
40
|
-
/// needs to deposit.
|
|
41
|
-
uint256 challengeDepositAmount;
|
|
42
|
-
/// Collection of all submitted fraud challenges indexed by challenge
|
|
43
|
-
/// key built as keccak256(walletPublicKey|sighash).
|
|
44
|
-
mapping(uint256 => FraudChallenge) challenges;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
50
|
struct FraudChallenge {
|
|
48
51
|
// The address of the party challenging the wallet.
|
|
49
52
|
address challenger;
|
|
@@ -55,20 +58,6 @@ library Frauds {
|
|
|
55
58
|
bool resolved;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
event FraudSlashingAmountUpdated(uint256 newFraudSlashingAmount);
|
|
59
|
-
|
|
60
|
-
event FraudNotifierRewardMultiplierUpdated(
|
|
61
|
-
uint256 newFraudNotifierRewardMultiplier
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
event FraudChallengeDefeatTimeoutUpdated(
|
|
65
|
-
uint256 newFraudChallengeDefeatTimeout
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
event FraudChallengeDepositAmountUpdated(
|
|
69
|
-
uint256 newFraudChallengeDepositAmount
|
|
70
|
-
);
|
|
71
|
-
|
|
72
61
|
event FraudChallengeSubmitted(
|
|
73
62
|
bytes20 walletPublicKeyHash,
|
|
74
63
|
bytes32 sighash,
|
|
@@ -101,8 +90,6 @@ library Frauds {
|
|
|
101
90
|
/// challenge or confiscated otherwise
|
|
102
91
|
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
103
92
|
/// and unprefixed format (64 bytes)
|
|
104
|
-
/// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
|
|
105
|
-
// HASH160 over the compressed ECDSA public key) of the wallet
|
|
106
93
|
/// @param sighash The hash that was used to produce the ECDSA signature
|
|
107
94
|
/// that is the subject of the fraud claim. This hash is constructed
|
|
108
95
|
/// by applying double SHA-256 over a serialized subset of the
|
|
@@ -111,20 +98,21 @@ library Frauds {
|
|
|
111
98
|
/// for reference
|
|
112
99
|
/// @param signature Bitcoin signature in the R/S/V format
|
|
113
100
|
/// @dev Requirements:
|
|
101
|
+
/// - Wallet behind `walletPublicKey` must be in `Live` or `MovingFunds`
|
|
102
|
+
/// state
|
|
114
103
|
/// - The challenger must send appropriate amount of ETH used as
|
|
115
104
|
/// fraud challenge deposit
|
|
116
105
|
/// - The signature (represented by r, s and v) must be generated by
|
|
117
|
-
/// the wallet behind `
|
|
106
|
+
/// the wallet behind `walletPublicKey` during signing of `sighash`
|
|
118
107
|
/// - Wallet can be challenged for the given signature only once
|
|
119
|
-
function
|
|
120
|
-
|
|
108
|
+
function submitFraudChallenge(
|
|
109
|
+
BridgeState.Storage storage self,
|
|
121
110
|
bytes calldata walletPublicKey,
|
|
122
|
-
bytes20 walletPubKeyHash,
|
|
123
111
|
bytes32 sighash,
|
|
124
112
|
BitcoinTx.RSVSignature calldata signature
|
|
125
113
|
) external {
|
|
126
114
|
require(
|
|
127
|
-
msg.value >= self.
|
|
115
|
+
msg.value >= self.fraudChallengeDepositAmount,
|
|
128
116
|
"The amount of ETH deposited is too low"
|
|
129
117
|
);
|
|
130
118
|
|
|
@@ -139,11 +127,27 @@ library Frauds {
|
|
|
139
127
|
"Signature verification failure"
|
|
140
128
|
);
|
|
141
129
|
|
|
130
|
+
bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
|
|
131
|
+
walletPublicKey.slice32(0),
|
|
132
|
+
walletPublicKey.slice32(32)
|
|
133
|
+
);
|
|
134
|
+
bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
|
|
135
|
+
|
|
136
|
+
Wallets.Wallet storage wallet = self.registeredWallets[
|
|
137
|
+
walletPubKeyHash
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
require(
|
|
141
|
+
wallet.state == Wallets.WalletState.Live ||
|
|
142
|
+
wallet.state == Wallets.WalletState.MovingFunds,
|
|
143
|
+
"Wallet is neither in Live nor MovingFunds state"
|
|
144
|
+
);
|
|
145
|
+
|
|
142
146
|
uint256 challengeKey = uint256(
|
|
143
147
|
keccak256(abi.encodePacked(walletPublicKey, sighash))
|
|
144
148
|
);
|
|
145
149
|
|
|
146
|
-
FraudChallenge storage challenge = self.
|
|
150
|
+
FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
|
|
147
151
|
require(challenge.reportedAt == 0, "Fraud challenge already exists");
|
|
148
152
|
|
|
149
153
|
challenge.challenger = msg.sender;
|
|
@@ -161,8 +165,18 @@ library Frauds {
|
|
|
161
165
|
);
|
|
162
166
|
}
|
|
163
167
|
|
|
164
|
-
/// @notice
|
|
165
|
-
///
|
|
168
|
+
/// @notice Allows to defeat a pending fraud challenge against a wallet if
|
|
169
|
+
/// the transaction that spends the UTXO follows the protocol rules.
|
|
170
|
+
/// In order to defeat the challenge the same `walletPublicKey` and
|
|
171
|
+
/// signature (represented by `r`, `s` and `v`) must be provided as
|
|
172
|
+
/// were used to calculate the sighash during input signing.
|
|
173
|
+
/// The fraud challenge defeat attempt will only succeed if the
|
|
174
|
+
/// inputs in the preimage are considered honestly spent by the
|
|
175
|
+
/// wallet. Therefore the transaction spending the UTXO must be
|
|
176
|
+
/// proven in the Bridge before a challenge defeat is called.
|
|
177
|
+
/// If successfully defeated, the fraud challenge is marked as
|
|
178
|
+
/// resolved and the amount of ether deposited by the challenger is
|
|
179
|
+
/// sent to the treasury.
|
|
166
180
|
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
167
181
|
/// and unprefixed format (64 bytes)
|
|
168
182
|
/// @param preimage The preimage which produces sighash used to generate the
|
|
@@ -172,25 +186,26 @@ library Frauds {
|
|
|
172
186
|
/// produced for. See BIP-143 for reference
|
|
173
187
|
/// @param witness Flag indicating whether the preimage was produced for a
|
|
174
188
|
/// witness input. True for witness, false for non-witness input.
|
|
175
|
-
/// @return utxoKey UTXO key that identifies spent input.
|
|
176
189
|
/// @dev Requirements:
|
|
177
190
|
/// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
|
|
178
191
|
/// must identify an open fraud challenge
|
|
179
192
|
/// - the preimage must be a valid preimage of a transaction generated
|
|
180
193
|
/// according to the protocol rules and already proved in the Bridge
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
/// - before a defeat attempt is made the transaction that spends the
|
|
195
|
+
/// given UTXO must be proven in the Bridge
|
|
196
|
+
function defeatFraudChallenge(
|
|
197
|
+
BridgeState.Storage storage self,
|
|
183
198
|
bytes calldata walletPublicKey,
|
|
184
199
|
bytes calldata preimage,
|
|
185
200
|
bool witness
|
|
186
|
-
) external
|
|
201
|
+
) external {
|
|
187
202
|
bytes32 sighash = preimage.hash256();
|
|
188
203
|
|
|
189
204
|
uint256 challengeKey = uint256(
|
|
190
205
|
keccak256(abi.encodePacked(walletPublicKey, sighash))
|
|
191
206
|
);
|
|
192
207
|
|
|
193
|
-
FraudChallenge storage challenge = self.
|
|
208
|
+
FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
|
|
194
209
|
|
|
195
210
|
require(challenge.reportedAt > 0, "Fraud challenge does not exist");
|
|
196
211
|
require(
|
|
@@ -202,55 +217,23 @@ library Frauds {
|
|
|
202
217
|
// by type value `1`.
|
|
203
218
|
require(extractSighashType(preimage) == 1, "Wrong sighash type");
|
|
204
219
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
: extractUtxoKeyFromNonWitnessPreimage(preimage);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/// @notice Finalizes fraud challenge defeat by marking a pending fraud
|
|
212
|
-
/// challenge against the wallet as resolved and sending the ether
|
|
213
|
-
/// deposited by the challenger to the treasury.
|
|
214
|
-
/// In order to finalize the challenge defeat the same
|
|
215
|
-
/// `walletPublicKey` must be provided as was used in the fraud
|
|
216
|
-
/// challenge. Additionally a preimage must be provided which was
|
|
217
|
-
/// used to calculate the sighash during input signing.
|
|
218
|
-
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
219
|
-
/// and unprefixed format (64 bytes)
|
|
220
|
-
/// @param preimage The preimage which produces sighash used to generate the
|
|
221
|
-
/// ECDSA signature that is the subject of the fraud claim. It is a
|
|
222
|
-
/// serialized subset of the transaction. The exact subset used as
|
|
223
|
-
/// the preimage depends on the transaction input the signature is
|
|
224
|
-
/// produced for. See BIP-143 for reference
|
|
225
|
-
/// @param treasury Treasury associated with the Bridge
|
|
226
|
-
/// @dev Requirements:
|
|
227
|
-
/// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
|
|
228
|
-
/// must identify an open fraud challenge
|
|
229
|
-
/// - the preimage must be a valid preimage of a transaction generated
|
|
230
|
-
/// according to the protocol rules and already proved in the Bridge
|
|
231
|
-
/// - before a defeat attempt is made the transaction that spends the
|
|
232
|
-
/// given UTXO must be proven in the Bridge
|
|
233
|
-
function defeatChallenge(
|
|
234
|
-
Data storage self,
|
|
235
|
-
bytes calldata walletPublicKey,
|
|
236
|
-
bytes calldata preimage,
|
|
237
|
-
address treasury
|
|
238
|
-
) external {
|
|
239
|
-
bytes32 sighash = preimage.hash256();
|
|
220
|
+
uint256 utxoKey = witness
|
|
221
|
+
? extractUtxoKeyFromWitnessPreimage(preimage)
|
|
222
|
+
: extractUtxoKeyFromNonWitnessPreimage(preimage);
|
|
240
223
|
|
|
241
|
-
|
|
242
|
-
|
|
224
|
+
// Check that the UTXO key identifies a correctly spent UTXO.
|
|
225
|
+
require(
|
|
226
|
+
self.deposits[utxoKey].sweptAt > 0 || self.spentMainUTXOs[utxoKey],
|
|
227
|
+
"Spent UTXO not found among correctly spent UTXOs"
|
|
243
228
|
);
|
|
244
229
|
|
|
245
|
-
FraudChallenge storage challenge = self.challenges[challengeKey];
|
|
246
|
-
|
|
247
230
|
// Mark the challenge as resolved as it was successfully defeated
|
|
248
231
|
challenge.resolved = true;
|
|
249
232
|
|
|
250
233
|
// Send the ether deposited by the challenger to the treasury
|
|
251
234
|
/* solhint-disable avoid-low-level-calls */
|
|
252
235
|
// slither-disable-next-line low-level-calls
|
|
253
|
-
treasury.call{gas: 100000, value: challenge.depositAmount}("");
|
|
236
|
+
self.treasury.call{gas: 100000, value: challenge.depositAmount}("");
|
|
254
237
|
/* solhint-enable avoid-low-level-calls */
|
|
255
238
|
|
|
256
239
|
bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
|
|
@@ -285,8 +268,8 @@ library Frauds {
|
|
|
285
268
|
/// challenge
|
|
286
269
|
/// - the amount of time indicated by `challengeDefeatTimeout` must pass
|
|
287
270
|
/// after the challenge was reported
|
|
288
|
-
function
|
|
289
|
-
|
|
271
|
+
function notifyFraudChallengeDefeatTimeout(
|
|
272
|
+
BridgeState.Storage storage self,
|
|
290
273
|
bytes calldata walletPublicKey,
|
|
291
274
|
bytes32 sighash
|
|
292
275
|
) external {
|
|
@@ -294,7 +277,7 @@ library Frauds {
|
|
|
294
277
|
keccak256(abi.encodePacked(walletPublicKey, sighash))
|
|
295
278
|
);
|
|
296
279
|
|
|
297
|
-
FraudChallenge storage challenge = self.
|
|
280
|
+
FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
|
|
298
281
|
require(challenge.reportedAt > 0, "Fraud challenge does not exist");
|
|
299
282
|
require(
|
|
300
283
|
!challenge.resolved,
|
|
@@ -303,7 +286,7 @@ library Frauds {
|
|
|
303
286
|
require(
|
|
304
287
|
/* solhint-disable-next-line not-rely-on-time */
|
|
305
288
|
block.timestamp >=
|
|
306
|
-
challenge.reportedAt + self.
|
|
289
|
+
challenge.reportedAt + self.fraudChallengeDefeatTimeout,
|
|
307
290
|
"Fraud challenge defeat period did not time out yet"
|
|
308
291
|
);
|
|
309
292
|
|
|
@@ -329,60 +312,6 @@ library Frauds {
|
|
|
329
312
|
emit FraudChallengeDefeatTimedOut(walletPubKeyHash, sighash);
|
|
330
313
|
}
|
|
331
314
|
|
|
332
|
-
/// @notice Sets the new value for the `slashingAmount` parameter.
|
|
333
|
-
/// @param _newSlashingAmount the new value for `slashingAmount`
|
|
334
|
-
function setSlashingAmount(Data storage self, uint256 _newSlashingAmount)
|
|
335
|
-
external
|
|
336
|
-
{
|
|
337
|
-
self.slashingAmount = _newSlashingAmount;
|
|
338
|
-
emit FraudSlashingAmountUpdated(_newSlashingAmount);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/// @notice Sets the new value for the `notifierRewardMultiplier` parameter.
|
|
342
|
-
/// @param _newNotifierRewardMultiplier the new value for `notifierRewardMultiplier`
|
|
343
|
-
/// @dev The value of `notifierRewardMultiplier` must be <= 100.
|
|
344
|
-
function setNotifierRewardMultiplier(
|
|
345
|
-
Data storage self,
|
|
346
|
-
uint256 _newNotifierRewardMultiplier
|
|
347
|
-
) external {
|
|
348
|
-
require(
|
|
349
|
-
_newNotifierRewardMultiplier <= 100,
|
|
350
|
-
"Fraud notifier reward multiplier must be <= 100"
|
|
351
|
-
);
|
|
352
|
-
self.notifierRewardMultiplier = _newNotifierRewardMultiplier;
|
|
353
|
-
emit FraudNotifierRewardMultiplierUpdated(_newNotifierRewardMultiplier);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/// @notice Sets the new value for the `challengeDefeatTimeout` parameter.
|
|
357
|
-
/// @param _newChallengeDefeatTimeout the new value for `challengeDefeatTimeout`
|
|
358
|
-
/// @dev The value of `challengeDefeatTimeout` must be > 0.
|
|
359
|
-
function setChallengeDefeatTimeout(
|
|
360
|
-
Data storage self,
|
|
361
|
-
uint256 _newChallengeDefeatTimeout
|
|
362
|
-
) external {
|
|
363
|
-
require(
|
|
364
|
-
_newChallengeDefeatTimeout > 0,
|
|
365
|
-
"Fraud challenge defeat timeout must be > 0"
|
|
366
|
-
);
|
|
367
|
-
self.challengeDefeatTimeout = _newChallengeDefeatTimeout;
|
|
368
|
-
emit FraudChallengeDefeatTimeoutUpdated(_newChallengeDefeatTimeout);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/// @notice Sets the new value for the `challengeDepositAmount` parameter.
|
|
372
|
-
/// @param _newChallengeDepositAmount the new value for `challengeDepositAmount`
|
|
373
|
-
/// @dev The value of `challengeDepositAmount` must be > 0.
|
|
374
|
-
function setChallengeDepositAmount(
|
|
375
|
-
Data storage self,
|
|
376
|
-
uint256 _newChallengeDepositAmount
|
|
377
|
-
) external {
|
|
378
|
-
require(
|
|
379
|
-
_newChallengeDepositAmount > 0,
|
|
380
|
-
"Fraud challenge deposit amount must be > 0"
|
|
381
|
-
);
|
|
382
|
-
self.challengeDepositAmount = _newChallengeDepositAmount;
|
|
383
|
-
emit FraudChallengeDepositAmountUpdated(_newChallengeDepositAmount);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
315
|
/// @notice Extracts the UTXO keys from the given preimage used during
|
|
387
316
|
/// signing of a witness input.
|
|
388
317
|
/// @param preimage The preimage which produces sighash used to generate the
|
|
@@ -20,11 +20,20 @@ import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
|
|
|
20
20
|
|
|
21
21
|
import "./BitcoinTx.sol";
|
|
22
22
|
import "./BridgeState.sol";
|
|
23
|
-
import "./
|
|
23
|
+
import "./Redemption.sol";
|
|
24
24
|
|
|
25
|
+
/// @title Moving Bridge wallet funds
|
|
26
|
+
/// @notice The library handles the logic for moving Bitcoin between Bridge
|
|
27
|
+
/// wallets.
|
|
28
|
+
/// @dev A wallet that failed a heartbeat, did not process requested redemption
|
|
29
|
+
/// on time, or qualifies to be closed, begins the procedure of moving
|
|
30
|
+
/// funds to other wallets in the Bridge. The wallet needs to commit to
|
|
31
|
+
/// which other Live wallets it is moving the funds to and then, provide an
|
|
32
|
+
/// SPV proof of moving funds to the previously committed wallets.
|
|
25
33
|
library MovingFunds {
|
|
26
34
|
using BridgeState for BridgeState.Storage;
|
|
27
|
-
using Wallets for
|
|
35
|
+
using Wallets for BridgeState.Storage;
|
|
36
|
+
using BitcoinTx for BridgeState.Storage;
|
|
28
37
|
|
|
29
38
|
using BTCUtils for bytes;
|
|
30
39
|
using BytesLib for bytes;
|
|
@@ -81,7 +90,6 @@ library MovingFunds {
|
|
|
81
90
|
/// to `movingFundsTxMaxTotalFee` governable parameter.
|
|
82
91
|
function submitMovingFundsProof(
|
|
83
92
|
BridgeState.Storage storage self,
|
|
84
|
-
Wallets.Data storage wallets,
|
|
85
93
|
BitcoinTx.Info calldata movingFundsTx,
|
|
86
94
|
BitcoinTx.Proof calldata movingFundsProof,
|
|
87
95
|
BitcoinTx.UTXO calldata mainUtxo,
|
|
@@ -91,17 +99,15 @@ library MovingFunds {
|
|
|
91
99
|
// can assume the transaction happened on Bitcoin chain and has
|
|
92
100
|
// a sufficient number of confirmations as determined by
|
|
93
101
|
// `txProofDifficultyFactor` constant.
|
|
94
|
-
bytes32 movingFundsTxHash =
|
|
102
|
+
bytes32 movingFundsTxHash = self.validateProof(
|
|
95
103
|
movingFundsTx,
|
|
96
|
-
movingFundsProof
|
|
97
|
-
self.proofDifficultyContext()
|
|
104
|
+
movingFundsProof
|
|
98
105
|
);
|
|
99
106
|
|
|
100
107
|
// Process the moving funds transaction input. Specifically, check if
|
|
101
108
|
// it refers to the expected wallet's main UTXO.
|
|
102
109
|
OutboundTx.processWalletOutboundTxInput(
|
|
103
110
|
self,
|
|
104
|
-
wallets,
|
|
105
111
|
movingFundsTx.inputVector,
|
|
106
112
|
mainUtxo,
|
|
107
113
|
walletPubKeyHash
|
|
@@ -118,7 +124,7 @@ library MovingFunds {
|
|
|
118
124
|
"Transaction fee is too high"
|
|
119
125
|
);
|
|
120
126
|
|
|
121
|
-
|
|
127
|
+
self.notifyWalletFundsMoved(walletPubKeyHash, targetWalletsHash);
|
|
122
128
|
|
|
123
129
|
emit MovingFundsCompleted(walletPubKeyHash, movingFundsTxHash);
|
|
124
130
|
}
|
|
@@ -139,7 +145,7 @@ library MovingFunds {
|
|
|
139
145
|
/// - The total outputs value must be evenly divided over all outputs.
|
|
140
146
|
function processMovingFundsTxOutputs(bytes memory movingFundsTxOutputVector)
|
|
141
147
|
internal
|
|
142
|
-
|
|
148
|
+
pure
|
|
143
149
|
returns (bytes32 targetWalletsHash, uint256 outputsTotalValue)
|
|
144
150
|
{
|
|
145
151
|
// Determining the total number of Bitcoin transaction outputs in
|
|
@@ -47,13 +47,12 @@ library OutboundTx {
|
|
|
47
47
|
/// performed the outbound transaction.
|
|
48
48
|
function processWalletOutboundTxInput(
|
|
49
49
|
BridgeState.Storage storage self,
|
|
50
|
-
Wallets.Data storage wallets,
|
|
51
50
|
bytes memory walletOutboundTxInputVector,
|
|
52
51
|
BitcoinTx.UTXO calldata mainUtxo,
|
|
53
52
|
bytes20 walletPubKeyHash
|
|
54
53
|
) internal {
|
|
55
54
|
// Assert that main UTXO for passed wallet exists in storage.
|
|
56
|
-
bytes32 mainUtxoHash =
|
|
55
|
+
bytes32 mainUtxoHash = self
|
|
57
56
|
.registeredWallets[walletPubKeyHash]
|
|
58
57
|
.mainUtxoHash;
|
|
59
58
|
require(mainUtxoHash != bytes32(0), "No main UTXO for given wallet");
|
|
@@ -141,10 +140,18 @@ library OutboundTx {
|
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
|
|
144
|
-
|
|
145
|
-
library
|
|
143
|
+
/// @title Bridge redemption
|
|
144
|
+
/// @notice The library handles the logic for redeeming Bitcoin balances from
|
|
145
|
+
/// the Bridge.
|
|
146
|
+
/// @dev To initiate a redemption, a user with a Bank balance supplies
|
|
147
|
+
/// a Bitcoin address. Then, the system calculates the redemption fee, and
|
|
148
|
+
/// releases balance to the provided Bitcoin address. Just like in case of
|
|
149
|
+
/// sweeps of revealed deposits, redemption requests are processed in
|
|
150
|
+
/// batches and require SPV proof to be submitted to the Bridge.
|
|
151
|
+
library Redemption {
|
|
146
152
|
using BridgeState for BridgeState.Storage;
|
|
147
|
-
using Wallets for
|
|
153
|
+
using Wallets for BridgeState.Storage;
|
|
154
|
+
using BitcoinTx for BridgeState.Storage;
|
|
148
155
|
|
|
149
156
|
using BTCUtils for bytes;
|
|
150
157
|
using BytesLib for bytes;
|
|
@@ -248,13 +255,12 @@ library Redeem {
|
|
|
248
255
|
/// contract can spend the given `amount`.
|
|
249
256
|
function requestRedemption(
|
|
250
257
|
BridgeState.Storage storage self,
|
|
251
|
-
Wallets.Data storage wallets,
|
|
252
258
|
bytes20 walletPubKeyHash,
|
|
253
259
|
BitcoinTx.UTXO calldata mainUtxo,
|
|
254
260
|
bytes calldata redeemerOutputScript,
|
|
255
261
|
uint64 amount
|
|
256
262
|
) external {
|
|
257
|
-
Wallets.Wallet storage wallet =
|
|
263
|
+
Wallets.Wallet storage wallet = self.registeredWallets[
|
|
258
264
|
walletPubKeyHash
|
|
259
265
|
];
|
|
260
266
|
|
|
@@ -419,7 +425,6 @@ library Redeem {
|
|
|
419
425
|
/// is identified, that check is omitted in further iterations.
|
|
420
426
|
function submitRedemptionProof(
|
|
421
427
|
BridgeState.Storage storage self,
|
|
422
|
-
Wallets.Data storage wallets,
|
|
423
428
|
BitcoinTx.Info calldata redemptionTx,
|
|
424
429
|
BitcoinTx.Proof calldata redemptionProof,
|
|
425
430
|
BitcoinTx.UTXO calldata mainUtxo,
|
|
@@ -433,23 +438,21 @@ library Redeem {
|
|
|
433
438
|
// can assume the transaction happened on Bitcoin chain and has
|
|
434
439
|
// a sufficient number of confirmations as determined by
|
|
435
440
|
// `txProofDifficultyFactor` constant.
|
|
436
|
-
bytes32 redemptionTxHash =
|
|
441
|
+
bytes32 redemptionTxHash = self.validateProof(
|
|
437
442
|
redemptionTx,
|
|
438
|
-
redemptionProof
|
|
439
|
-
self.proofDifficultyContext()
|
|
443
|
+
redemptionProof
|
|
440
444
|
);
|
|
441
445
|
|
|
442
446
|
// Process the redemption transaction input. Specifically, check if it
|
|
443
447
|
// refers to the expected wallet's main UTXO.
|
|
444
448
|
OutboundTx.processWalletOutboundTxInput(
|
|
445
449
|
self,
|
|
446
|
-
wallets,
|
|
447
450
|
redemptionTx.inputVector,
|
|
448
451
|
mainUtxo,
|
|
449
452
|
walletPubKeyHash
|
|
450
453
|
);
|
|
451
454
|
|
|
452
|
-
Wallets.Wallet storage wallet =
|
|
455
|
+
Wallets.Wallet storage wallet = self.registeredWallets[
|
|
453
456
|
walletPubKeyHash
|
|
454
457
|
];
|
|
455
458
|
|
|
@@ -789,14 +792,13 @@ library Redeem {
|
|
|
789
792
|
/// timed-out).
|
|
790
793
|
function notifyRedemptionTimeout(
|
|
791
794
|
BridgeState.Storage storage self,
|
|
792
|
-
Wallets.Data storage wallets,
|
|
793
795
|
bytes20 walletPubKeyHash,
|
|
794
796
|
bytes calldata redeemerOutputScript
|
|
795
797
|
) external {
|
|
796
798
|
uint256 redemptionKey = uint256(
|
|
797
799
|
keccak256(abi.encodePacked(walletPubKeyHash, redeemerOutputScript))
|
|
798
800
|
);
|
|
799
|
-
|
|
801
|
+
Redemption.RedemptionRequest memory request = self.pendingRedemptions[
|
|
800
802
|
redemptionKey
|
|
801
803
|
];
|
|
802
804
|
|
|
@@ -808,7 +810,7 @@ library Redeem {
|
|
|
808
810
|
);
|
|
809
811
|
|
|
810
812
|
// Update the wallet's pending redemptions value
|
|
811
|
-
Wallets.Wallet storage wallet =
|
|
813
|
+
Wallets.Wallet storage wallet = self.registeredWallets[
|
|
812
814
|
walletPubKeyHash
|
|
813
815
|
];
|
|
814
816
|
wallet.pendingRedemptionsValue -=
|
|
@@ -838,7 +840,7 @@ library Redeem {
|
|
|
838
840
|
wallet.state == Wallets.WalletState.MovingFunds
|
|
839
841
|
) {
|
|
840
842
|
// Propagate timeout consequences to the wallet
|
|
841
|
-
|
|
843
|
+
self.notifyWalletTimedOutRedemption(walletPubKeyHash);
|
|
842
844
|
}
|
|
843
845
|
|
|
844
846
|
emit RedemptionTimedOut(walletPubKeyHash, redeemerOutputScript);
|
|
@@ -23,8 +23,19 @@ import "./Wallets.sol";
|
|
|
23
23
|
|
|
24
24
|
import "../bank/Bank.sol";
|
|
25
25
|
|
|
26
|
+
/// @title Bridge deposit sweep
|
|
27
|
+
/// @notice The library handles the logic for sweeping transactions revealed to
|
|
28
|
+
/// the Bridge
|
|
29
|
+
/// @dev Bridge active wallet periodically signs a transaction that unlocks all
|
|
30
|
+
/// of the valid, revealed deposits above the dust threshold, combines them
|
|
31
|
+
/// into a single UTXO with the existing main wallet UTXO, and relocks
|
|
32
|
+
/// those transactions without a 30-day refund clause to the same wallet.
|
|
33
|
+
/// This has two main effects: it consolidates the UTXO set and it disables
|
|
34
|
+
/// the refund. Balances of depositors in the Bank are increased when the
|
|
35
|
+
/// SPV sweep proof is submitted to the Bridge.
|
|
26
36
|
library Sweep {
|
|
27
37
|
using BridgeState for BridgeState.Storage;
|
|
38
|
+
using BitcoinTx for BridgeState.Storage;
|
|
28
39
|
|
|
29
40
|
using BTCUtils for bytes;
|
|
30
41
|
|
|
@@ -94,7 +105,6 @@ library Sweep {
|
|
|
94
105
|
/// If there is no main UTXO, this parameter is ignored.
|
|
95
106
|
function submitSweepProof(
|
|
96
107
|
BridgeState.Storage storage self,
|
|
97
|
-
Wallets.Data storage wallets,
|
|
98
108
|
BitcoinTx.Info calldata sweepTx,
|
|
99
109
|
BitcoinTx.Proof calldata sweepProof,
|
|
100
110
|
BitcoinTx.UTXO calldata mainUtxo
|
|
@@ -106,11 +116,7 @@ library Sweep {
|
|
|
106
116
|
// can assume the transaction happened on Bitcoin chain and has
|
|
107
117
|
// a sufficient number of confirmations as determined by
|
|
108
118
|
// `txProofDifficultyFactor` constant.
|
|
109
|
-
bytes32 sweepTxHash =
|
|
110
|
-
sweepTx,
|
|
111
|
-
sweepProof,
|
|
112
|
-
self.proofDifficultyContext()
|
|
113
|
-
);
|
|
119
|
+
bytes32 sweepTxHash = self.validateProof(sweepTx, sweepProof);
|
|
114
120
|
|
|
115
121
|
// Process sweep transaction output and extract its target wallet
|
|
116
122
|
// public key hash and value.
|
|
@@ -122,7 +128,7 @@ library Sweep {
|
|
|
122
128
|
(
|
|
123
129
|
Wallets.Wallet storage wallet,
|
|
124
130
|
BitcoinTx.UTXO memory resolvedMainUtxo
|
|
125
|
-
) = resolveSweepingWallet(
|
|
131
|
+
) = resolveSweepingWallet(self, walletPubKeyHash, mainUtxo);
|
|
126
132
|
|
|
127
133
|
// Process sweep transaction inputs and extract all information needed
|
|
128
134
|
// to perform deposit bookkeeping.
|
|
@@ -208,17 +214,18 @@ library Sweep {
|
|
|
208
214
|
/// - If the main UTXO of the sweeping wallet exists in the storage,
|
|
209
215
|
/// the passed `mainUTXO` parameter must be equal to the stored one.
|
|
210
216
|
function resolveSweepingWallet(
|
|
211
|
-
|
|
217
|
+
BridgeState.Storage storage self,
|
|
212
218
|
bytes20 walletPubKeyHash,
|
|
213
219
|
BitcoinTx.UTXO calldata mainUtxo
|
|
214
220
|
)
|
|
215
221
|
internal
|
|
222
|
+
view
|
|
216
223
|
returns (
|
|
217
224
|
Wallets.Wallet storage wallet,
|
|
218
225
|
BitcoinTx.UTXO memory resolvedMainUtxo
|
|
219
226
|
)
|
|
220
227
|
{
|
|
221
|
-
wallet =
|
|
228
|
+
wallet = self.registeredWallets[walletPubKeyHash];
|
|
222
229
|
|
|
223
230
|
Wallets.WalletState walletState = wallet.state;
|
|
224
231
|
require(
|