@keep-network/tbtc-v2 0.1.1-dev.41 → 0.1.1-dev.44

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.
Files changed (81) hide show
  1. package/artifacts/Bank.json +742 -0
  2. package/artifacts/Bridge.json +2914 -0
  3. package/artifacts/Deposit.json +117 -0
  4. package/artifacts/EcdsaDkgValidator.json +532 -0
  5. package/artifacts/EcdsaInactivity.json +156 -0
  6. package/artifacts/Fraud.json +153 -0
  7. package/artifacts/KeepRegistry.json +99 -0
  8. package/artifacts/KeepStake.json +286 -0
  9. package/artifacts/KeepToken.json +711 -0
  10. package/artifacts/KeepTokenStaking.json +483 -0
  11. package/artifacts/MovingFunds.json +137 -0
  12. package/artifacts/NuCypherStakingEscrow.json +256 -0
  13. package/artifacts/NuCypherToken.json +711 -0
  14. package/artifacts/RandomBeaconStub.json +141 -0
  15. package/artifacts/Redemption.json +161 -0
  16. package/artifacts/ReimbursementPool.json +509 -0
  17. package/artifacts/Relay.json +123 -0
  18. package/artifacts/SortitionPool.json +944 -0
  19. package/artifacts/Sweep.json +76 -0
  20. package/artifacts/T.json +1148 -0
  21. package/artifacts/TBTC.json +21 -21
  22. package/artifacts/TBTCToken.json +21 -21
  23. package/artifacts/TokenStaking.json +2288 -0
  24. package/artifacts/TokenholderGovernor.json +1795 -0
  25. package/artifacts/TokenholderTimelock.json +1058 -0
  26. package/artifacts/VendingMachine.json +24 -24
  27. package/artifacts/VendingMachineKeep.json +400 -0
  28. package/artifacts/VendingMachineNuCypher.json +400 -0
  29. package/artifacts/WalletRegistry.json +2709 -0
  30. package/artifacts/WalletRegistryGovernance.json +2364 -0
  31. package/artifacts/Wallets.json +186 -0
  32. package/artifacts/solcInputs/{e9b173393b9fd7287a0bfaa6d4eb4b71.json → bbe44823ec28554a9429cce5cafee035.json} +34 -34
  33. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  34. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  35. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  36. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +2 -2
  37. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  38. package/build/contracts/bridge/Bridge.sol/Bridge.json +535 -273
  39. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  40. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +147 -3
  41. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
  42. package/build/contracts/bridge/Deposit.sol/Deposit.json +2 -2
  43. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  44. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +2 -2
  45. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
  46. package/build/contracts/bridge/Fraud.sol/Fraud.json +5 -57
  47. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  48. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  49. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +41 -21
  50. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
  51. package/build/contracts/bridge/{Redeem.sol → Redemption.sol}/OutboundTx.json +3 -3
  52. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
  53. package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
  54. package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +1 -1
  55. package/build/contracts/bridge/Sweep.sol/Sweep.json +2 -2
  56. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  57. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  58. package/build/contracts/bridge/Wallets.sol/Wallets.json +12 -38
  59. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  60. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  61. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  62. package/contracts/bridge/BitcoinTx.sol +19 -26
  63. package/contracts/bridge/Bridge.sol +765 -524
  64. package/contracts/bridge/BridgeState.sol +312 -23
  65. package/contracts/bridge/Deposit.sol +25 -6
  66. package/contracts/bridge/EcdsaLib.sol +15 -0
  67. package/contracts/bridge/Fraud.sol +65 -33
  68. package/contracts/bridge/MovingFunds.sol +196 -10
  69. package/contracts/bridge/{Redeem.sol → Redemption.sol} +26 -29
  70. package/contracts/bridge/Sweep.sol +16 -12
  71. package/contracts/bridge/Wallets.sol +90 -153
  72. package/deploy/00_resolve_relay.ts +28 -0
  73. package/deploy/04_deploy_bank.ts +25 -0
  74. package/deploy/05_deploy_bridge.ts +60 -0
  75. package/deploy/06_bank_update_bridge.ts +19 -0
  76. package/deploy/07_transfer_ownership.ts +17 -0
  77. package/export.json +14797 -459
  78. package/package.json +2 -2
  79. package/build/contracts/bridge/Redeem.sol/OutboundTx.dbg.json +0 -4
  80. package/build/contracts/bridge/Redeem.sol/Redeem.dbg.json +0 -4
  81. package/build/contracts/bridge/Redeem.sol/Redeem.json +0 -110
@@ -15,23 +15,28 @@
15
15
 
16
16
  pragma solidity ^0.8.9;
17
17
 
18
+ import {IWalletRegistry as EcdsaWalletRegistry} from "@keep-network/ecdsa/contracts/api/IWalletRegistry.sol";
19
+
18
20
  import "./IRelay.sol";
19
21
  import "./Deposit.sol";
20
- import "./Redeem.sol";
22
+ import "./Redemption.sol";
21
23
  import "./Fraud.sol";
24
+ import "./Wallets.sol";
22
25
 
23
26
  import "../bank/Bank.sol";
24
27
 
25
28
  library BridgeState {
26
29
  // TODO: Make parameters governable
27
30
  struct Storage {
28
- // The number of confirmations on the Bitcoin chain required to
29
- // successfully evaluate an SPV proof.
30
- uint256 txProofDifficultyFactor;
31
- // Address of the Bank this Bridge belongs to.
31
+ // Address of the Bank the Bridge belongs to.
32
32
  Bank bank;
33
33
  // Bitcoin relay providing the current Bitcoin network difficulty.
34
34
  IRelay relay;
35
+ // ECDSA Wallet Registry contract handle.
36
+ EcdsaWalletRegistry ecdsaWalletRegistry;
37
+ // The number of confirmations on the Bitcoin chain required to
38
+ // successfully evaluate an SPV proof.
39
+ uint256 txProofDifficultyFactor;
35
40
  // Address where the deposit and redemption treasury fees will be sent
36
41
  // to. Treasury takes part in the operators rewarding process.
37
42
  address treasury;
@@ -78,6 +83,11 @@ library BridgeState {
78
83
  // if per single redemption. `movingFundsTxMaxTotalFee` is a total
79
84
  // fee for the entire transaction.
80
85
  uint64 movingFundsTxMaxTotalFee;
86
+ // Time after which the moving funds process can be reported as
87
+ // timed out. It is counted from the moment when the wallet
88
+ // was requested to move their funds and switched to the MovingFunds
89
+ // state. Value in seconds.
90
+ uint32 movingFundsTimeout;
81
91
  // The minimal amount that can be requested for redemption.
82
92
  // Value of this parameter must take into account the value of
83
93
  // `redemptionTreasuryFeeDivisor` and `redemptionTxMaxFee`
@@ -120,7 +130,7 @@ library BridgeState {
120
130
  // successfully
121
131
  // - `notifyRedemptionTimeout` in case the request was reported
122
132
  // to be timed out
123
- mapping(uint256 => Redeem.RedemptionRequest) pendingRedemptions;
133
+ mapping(uint256 => Redemption.RedemptionRequest) pendingRedemptions;
124
134
  // Collection of all timed out redemptions requests indexed by
125
135
  // redemption key built as
126
136
  // `keccak256(walletPubKeyHash | redeemerOutputScript)`. The
@@ -134,7 +144,7 @@ library BridgeState {
134
144
  // - `notifyRedemptionTimeout` which puts the redemption key to this
135
145
  // mapping basing on a timed out request stored previously in
136
146
  // `pendingRedemptions` mapping.
137
- mapping(uint256 => Redeem.RedemptionRequest) timedOutRedemptions;
147
+ mapping(uint256 => Redemption.RedemptionRequest) timedOutRedemptions;
138
148
  // The amount of stake slashed from each member of a wallet for a fraud.
139
149
  uint256 fraudSlashingAmount;
140
150
  // The percentage of the notifier reward from the staking contract
@@ -155,23 +165,302 @@ library BridgeState {
155
165
  // spent if it was used as an input of a transaction that have been
156
166
  // proven in the Bridge.
157
167
  mapping(uint256 => bool) spentMainUTXOs;
168
+ // Determines how frequently a new wallet creation can be requested.
169
+ // Value in seconds.
170
+ uint32 walletCreationPeriod;
171
+ // The minimum BTC threshold in satoshi that is used to decide about
172
+ // wallet creation or closing.
173
+ uint64 walletMinBtcBalance;
174
+ // The maximum BTC threshold in satoshi that is used to decide about
175
+ // wallet creation.
176
+ uint64 walletMaxBtcBalance;
177
+ // The maximum age of a wallet in seconds, after which the wallet
178
+ // moving funds process can be requested.
179
+ uint32 walletMaxAge;
180
+ // 20-byte wallet public key hash being reference to the currently
181
+ // active wallet. Can be unset to the zero value under certain
182
+ // circumstances.
183
+ bytes20 activeWalletPubKeyHash;
184
+ // The current number of wallets in the Live state.
185
+ uint32 liveWalletsCount;
186
+ // The maximum BTC amount in satoshi than can be transferred to a single
187
+ // target wallet during the moving funds process.
188
+ uint64 walletMaxBtcTransfer;
189
+ // Maps the 20-byte wallet public key hash (computed using Bitcoin
190
+ // HASH160 over the compressed ECDSA public key) to the basic wallet
191
+ // information like state and pending redemptions value.
192
+ mapping(bytes20 => Wallets.Wallet) registeredWallets;
193
+ }
194
+
195
+ event DepositParametersUpdated(
196
+ uint64 depositDustThreshold,
197
+ uint64 depositTreasuryFeeDivisor,
198
+ uint64 depositTxMaxFee
199
+ );
200
+
201
+ event RedemptionParametersUpdated(
202
+ uint64 redemptionDustThreshold,
203
+ uint64 redemptionTreasuryFeeDivisor,
204
+ uint64 redemptionTxMaxFee,
205
+ uint256 redemptionTimeout
206
+ );
207
+
208
+ event MovingFundsParametersUpdated(
209
+ uint64 movingFundsTxMaxTotalFee,
210
+ uint32 movingFundsTimeout
211
+ );
212
+
213
+ event WalletParametersUpdated(
214
+ uint32 walletCreationPeriod,
215
+ uint64 walletMinBtcBalance,
216
+ uint64 walletMaxBtcBalance,
217
+ uint32 walletMaxAge,
218
+ uint64 walletMaxBtcTransfer
219
+ );
220
+
221
+ event FraudParametersUpdated(
222
+ uint256 fraudSlashingAmount,
223
+ uint256 fraudNotifierRewardMultiplier,
224
+ uint256 fraudChallengeDefeatTimeout,
225
+ uint256 fraudChallengeDepositAmount
226
+ );
227
+
228
+ /// @notice Updates parameters of deposits.
229
+ /// @param _depositDustThreshold New value of the deposit dust threshold in
230
+ /// satoshis. It is the minimal amount that can be requested to
231
+ //// deposit. Value of this parameter must take into account the value
232
+ /// of `depositTreasuryFeeDivisor` and `depositTxMaxFee` parameters
233
+ /// in order to make requests that can incur the treasury and
234
+ /// transaction fee and still satisfy the depositor
235
+ /// @param _depositTreasuryFeeDivisor New value of the treasury fee divisor.
236
+ /// It is the divisor used to compute the treasury fee taken from
237
+ /// each deposit and transferred to the treasury upon sweep proof
238
+ /// submission. That fee is computed as follows:
239
+ /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
240
+ /// For example, if the treasury fee needs to be 2% of each deposit,
241
+ /// the `depositTreasuryFeeDivisor` should be set to `50`
242
+ /// because `1/50 = 0.02 = 2%`
243
+ /// @param _depositTxMaxFee New value of the deposit tx max fee in satoshis.
244
+ /// It is the maximum amount of BTC transaction fee that can
245
+ /// be incurred by each swept deposit being part of the given sweep
246
+ /// transaction. If the maximum BTC transaction fee is exceeded,
247
+ /// such transaction is considered a fraud
248
+ /// @dev Requirements:
249
+ /// - Deposit treasury fee divisor must be greater than zero
250
+ function updateDepositParameters(
251
+ Storage storage self,
252
+ uint64 _depositDustThreshold,
253
+ uint64 _depositTreasuryFeeDivisor,
254
+ uint64 _depositTxMaxFee
255
+ ) internal {
256
+ require(
257
+ _depositTreasuryFeeDivisor > 0,
258
+ "Deposit treasury fee divisor must be greater than zero"
259
+ );
260
+
261
+ self.depositDustThreshold = _depositDustThreshold;
262
+ self.depositTreasuryFeeDivisor = _depositTreasuryFeeDivisor;
263
+ self.depositTxMaxFee = _depositTxMaxFee;
264
+
265
+ emit DepositParametersUpdated(
266
+ _depositDustThreshold,
267
+ _depositTreasuryFeeDivisor,
268
+ _depositTxMaxFee
269
+ );
158
270
  }
159
271
 
160
- // TODO: Is it the right place for this function? Should we move it to Bridge?
161
- /// @notice Determines the current Bitcoin SPV proof difficulty context.
162
- /// @return proofDifficulty Bitcoin proof difficulty context.
163
- function proofDifficultyContext(Storage storage self)
164
- internal
165
- view
166
- returns (BitcoinTx.ProofDifficulty memory proofDifficulty)
167
- {
168
- IRelay relay = self.relay;
169
- proofDifficulty.currentEpochDifficulty = relay
170
- .getCurrentEpochDifficulty();
171
- proofDifficulty.previousEpochDifficulty = relay
172
- .getPrevEpochDifficulty();
173
- proofDifficulty.difficultyFactor = self.txProofDifficultyFactor;
174
-
175
- return proofDifficulty;
272
+ /// @notice Updates parameters of redemptions.
273
+ /// @param _redemptionDustThreshold New value of the redemption dust
274
+ /// threshold in satoshis. It is the minimal amount that can be
275
+ /// requested for redemption. Value of this parameter must take into
276
+ /// account the value of `redemptionTreasuryFeeDivisor` and
277
+ /// `redemptionTxMaxFee` parameters in order to make requests that
278
+ /// can incur the treasury and transaction fee and still satisfy the
279
+ /// redeemer.
280
+ /// @param _redemptionTreasuryFeeDivisor New value of the redemption
281
+ /// treasury fee divisor. It is the divisor used to compute the
282
+ /// treasury fee taken from each redemption request and transferred
283
+ /// to the treasury upon successful request finalization. That fee is
284
+ /// computed as follows:
285
+ /// `treasuryFee = requestedAmount / redemptionTreasuryFeeDivisor`
286
+ /// For example, if the treasury fee needs to be 2% of each
287
+ /// redemption request, the `redemptionTreasuryFeeDivisor` should
288
+ /// be set to `50` because `1/50 = 0.02 = 2%`.
289
+ /// @param _redemptionTxMaxFee New value of the redemption transaction max
290
+ /// fee in satoshis. It is the maximum amount of BTC transaction fee
291
+ /// that can be incurred by each redemption request being part of the
292
+ /// given redemption transaction. If the maximum BTC transaction fee
293
+ /// is exceeded, such transaction is considered a fraud.
294
+ /// @param _redemptionTimeout New value of the redemption timeout in seconds.
295
+ /// It is the time after which the redemption request can be reported
296
+ /// as timed out. It is counted from the moment when the redemption
297
+ /// request was created via `requestRedemption` call. Reported timed
298
+ /// out requests are cancelled and locked TBTC is returned to the
299
+ /// redeemer in full amount.
300
+ /// @dev Requirements:
301
+ /// - Redemption treasury fee divisor must be greater than zero
302
+ /// - Redemption timeout must be greater than zero
303
+ function updateRedemptionParameters(
304
+ Storage storage self,
305
+ uint64 _redemptionDustThreshold,
306
+ uint64 _redemptionTreasuryFeeDivisor,
307
+ uint64 _redemptionTxMaxFee,
308
+ uint256 _redemptionTimeout
309
+ ) internal {
310
+ require(
311
+ _redemptionTreasuryFeeDivisor > 0,
312
+ "Redemption treasury fee divisor must be greater than zero"
313
+ );
314
+
315
+ require(
316
+ _redemptionTimeout > 0,
317
+ "Redemption timeout must be greater than zero"
318
+ );
319
+
320
+ self.redemptionDustThreshold = _redemptionDustThreshold;
321
+ self.redemptionTreasuryFeeDivisor = _redemptionTreasuryFeeDivisor;
322
+ self.redemptionTxMaxFee = _redemptionTxMaxFee;
323
+ self.redemptionTimeout = _redemptionTimeout;
324
+
325
+ emit RedemptionParametersUpdated(
326
+ _redemptionDustThreshold,
327
+ _redemptionTreasuryFeeDivisor,
328
+ _redemptionTxMaxFee,
329
+ _redemptionTimeout
330
+ );
331
+ }
332
+
333
+ /// @notice Updates parameters of moving funds.
334
+ /// @param _movingFundsTxMaxTotalFee New value of the moving funds transaction
335
+ /// max total fee in satoshis. It is the maximum amount of the total
336
+ /// BTC transaction fee that is acceptable in a single moving funds
337
+ /// transaction. This is a _total_ max fee for the entire moving
338
+ /// funds transaction.
339
+ /// @param _movingFundsTimeout New value of the moving funds timeout in
340
+ /// seconds. It is the time after which the moving funds process can
341
+ /// be reported as timed out. It is counted from the moment when the
342
+ /// wallet was requested to move their funds and switched to the
343
+ /// MovingFunds state.
344
+ /// @dev Requirements:
345
+ /// - Moving funds timeout must be greater than zero
346
+ function updateMovingFundsParameters(
347
+ Storage storage self,
348
+ uint64 _movingFundsTxMaxTotalFee,
349
+ uint32 _movingFundsTimeout
350
+ ) internal {
351
+ require(
352
+ _movingFundsTimeout > 0,
353
+ "Moving funds timeout must be greater than zero"
354
+ );
355
+
356
+ self.movingFundsTxMaxTotalFee = _movingFundsTxMaxTotalFee;
357
+ self.movingFundsTimeout = _movingFundsTimeout;
358
+
359
+ emit MovingFundsParametersUpdated(
360
+ _movingFundsTxMaxTotalFee,
361
+ _movingFundsTimeout
362
+ );
363
+ }
364
+
365
+ /// @notice Updates parameters of wallets.
366
+ /// @param _walletCreationPeriod New value of the wallet creation period in
367
+ /// seconds, determines how frequently a new wallet creation can be
368
+ /// requested
369
+ /// @param _walletMinBtcBalance New value of the wallet minimum BTC balance
370
+ /// in satoshi, used to decide about wallet creation or closing
371
+ /// @param _walletMaxBtcBalance New value of the wallet maximum BTC balance
372
+ /// in satoshi, used to decide about wallet creation
373
+ /// @param _walletMaxAge New value of the wallet maximum age in seconds,
374
+ /// indicates the maximum age of a wallet in seconds, after which
375
+ /// the wallet moving funds process can be requested
376
+ /// @param _walletMaxBtcTransfer New value of the wallet maximum BTC transfer
377
+ /// in satoshi, determines the maximum amount that can be transferred
378
+ // to a single target wallet during the moving funds process
379
+ /// @dev Requirements:
380
+ /// - Wallet minimum BTC balance must be greater than zero
381
+ /// - Wallet maximum BTC balance must be greater than the wallet
382
+ /// minimum BTC balance
383
+ /// - Wallet maximum BTC transfer must be greater than zero
384
+ function updateWalletParameters(
385
+ Storage storage self,
386
+ uint32 _walletCreationPeriod,
387
+ uint64 _walletMinBtcBalance,
388
+ uint64 _walletMaxBtcBalance,
389
+ uint32 _walletMaxAge,
390
+ uint64 _walletMaxBtcTransfer
391
+ ) internal {
392
+ require(
393
+ _walletMinBtcBalance > 0,
394
+ "Wallet minimum BTC balance must be greater than zero"
395
+ );
396
+ require(
397
+ _walletMaxBtcBalance > _walletMinBtcBalance,
398
+ "Wallet maximum BTC balance must be greater than the minimum"
399
+ );
400
+ require(
401
+ _walletMaxBtcTransfer > 0,
402
+ "Wallet maximum BTC transfer must be greater than zero"
403
+ );
404
+
405
+ self.walletCreationPeriod = _walletCreationPeriod;
406
+ self.walletMinBtcBalance = _walletMinBtcBalance;
407
+ self.walletMaxBtcBalance = _walletMaxBtcBalance;
408
+ self.walletMaxAge = _walletMaxAge;
409
+ self.walletMaxBtcTransfer = _walletMaxBtcTransfer;
410
+
411
+ emit WalletParametersUpdated(
412
+ _walletCreationPeriod,
413
+ _walletMinBtcBalance,
414
+ _walletMaxBtcBalance,
415
+ _walletMaxAge,
416
+ _walletMaxBtcTransfer
417
+ );
418
+ }
419
+
420
+ /// @notice Updates parameters related to frauds.
421
+ /// @param _fraudSlashingAmount New value of the fraud slashing amount in T,
422
+ /// it is the amount slashed from each wallet member for committing
423
+ /// a fraud
424
+ /// @param _fraudNotifierRewardMultiplier New value of the fraud notifier
425
+ /// reward multiplier as percentage, it determines the percentage of
426
+ /// the notifier reward from the staking contact the notifier of
427
+ /// a fraud receives. The value must be in the range [0, 100]
428
+ /// @param _fraudChallengeDefeatTimeout New value of the challenge defeat
429
+ /// timeout in seconds, it is the amount of time the wallet has to
430
+ /// defeat a fraud challenge. The value must be greater than zero
431
+ /// @param _fraudChallengeDepositAmount New value of the fraud challenge
432
+ /// deposit amount in wei, it is the amount of ETH the party
433
+ /// challenging the wallet for fraud needs to deposit
434
+ /// @dev Requirements:
435
+ /// - Fraud notifier reward multiplier must be in the range [0, 100]
436
+ /// - Fraud challenge defeat timeout must be greater than 0
437
+ function updateFraudParameters(
438
+ Storage storage self,
439
+ uint256 _fraudSlashingAmount,
440
+ uint256 _fraudNotifierRewardMultiplier,
441
+ uint256 _fraudChallengeDefeatTimeout,
442
+ uint256 _fraudChallengeDepositAmount
443
+ ) internal {
444
+ require(
445
+ _fraudNotifierRewardMultiplier <= 100,
446
+ "Fraud notifier reward multiplier must be in the range [0, 100]"
447
+ );
448
+
449
+ require(
450
+ _fraudChallengeDefeatTimeout > 0,
451
+ "Fraud challenge defeat timeout must be greater than zero"
452
+ );
453
+
454
+ self.fraudSlashingAmount = _fraudSlashingAmount;
455
+ self.fraudNotifierRewardMultiplier = _fraudNotifierRewardMultiplier;
456
+ self.fraudChallengeDefeatTimeout = _fraudChallengeDefeatTimeout;
457
+ self.fraudChallengeDepositAmount = _fraudChallengeDepositAmount;
458
+
459
+ emit FraudParametersUpdated(
460
+ _fraudSlashingAmount,
461
+ _fraudNotifierRewardMultiplier,
462
+ _fraudChallengeDefeatTimeout,
463
+ _fraudChallengeDepositAmount
464
+ );
176
465
  }
177
466
  }
@@ -22,9 +22,29 @@ import "./BitcoinTx.sol";
22
22
  import "./BridgeState.sol";
23
23
  import "./Wallets.sol";
24
24
 
25
+ /// @title Bridge deposit
26
+ /// @notice The library handles the logic for revealing Bitcoin deposits to
27
+ /// the Bridge.
28
+ /// @dev The depositor puts together a P2SH or P2WSH address to deposit the
29
+ /// funds. This script is unique to each depositor and looks like this:
30
+ ///
31
+ /// ```
32
+ /// <depositorAddress> DROP
33
+ /// <blindingFactor> DROP
34
+ /// DUP HASH160 <walletPubKeyHash> EQUAL
35
+ /// IF
36
+ /// CHECKSIG
37
+ /// ELSE
38
+ /// DUP HASH160 <refundPubkeyHash> EQUALVERIFY
39
+ /// <refundLocktime> CHECKLOCKTIMEVERIFY DROP
40
+ /// CHECKSIG
41
+ /// ENDIF
42
+ /// ```
43
+ ///
44
+ /// Since each depositor has their own Ethereum address and their own
45
+ /// secret blinding factor, each depositor’s script is unique, and the hash
46
+ /// of each depositor’s script is unique.
25
47
  library Deposit {
26
- using Wallets for Wallets.Data;
27
-
28
48
  using BTCUtils for bytes;
29
49
  using BytesLib for bytes;
30
50
 
@@ -123,14 +143,13 @@ library Deposit {
123
143
  /// deposit script unlocks to receive their BTC back.
124
144
  function revealDeposit(
125
145
  BridgeState.Storage storage self,
126
- Wallets.Data storage wallets,
127
146
  BitcoinTx.Info calldata fundingTx,
128
147
  DepositRevealInfo calldata reveal
129
148
  ) external {
130
149
  require(
131
- wallets.registeredWallets[reveal.walletPubKeyHash].state ==
150
+ self.registeredWallets[reveal.walletPubKeyHash].state ==
132
151
  Wallets.WalletState.Live,
133
- "Wallet is not in Live state"
152
+ "Wallet must be in Live state"
134
153
  );
135
154
 
136
155
  require(
@@ -231,7 +250,7 @@ library Deposit {
231
250
  deposit.treasuryFee = self.depositTreasuryFeeDivisor > 0
232
251
  ? fundingOutputAmount / self.depositTreasuryFeeDivisor
233
252
  : 0;
234
-
253
+ // slither-disable-next-line reentrancy-events
235
254
  emit DepositRevealed(
236
255
  fundingTxHash,
237
256
  reveal.fundingOutputIndex,
@@ -1,3 +1,18 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ // ██████████████ ▐████▌ ██████████████
4
+ // ██████████████ ▐████▌ ██████████████
5
+ // ▐████▌ ▐████▌
6
+ // ▐████▌ ▐████▌
7
+ // ██████████████ ▐████▌ ██████████████
8
+ // ██████████████ ▐████▌ ██████████████
9
+ // ▐████▌ ▐████▌
10
+ // ▐████▌ ▐████▌
11
+ // ▐████▌ ▐████▌
12
+ // ▐████▌ ▐████▌
13
+ // ▐████▌ ▐████▌
14
+ // ▐████▌ ▐████▌
15
+
1
16
  pragma solidity ^0.8.9;
2
17
 
3
18
  import "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
@@ -24,7 +24,26 @@ import "./EcdsaLib.sol";
24
24
  import "./BridgeState.sol";
25
25
  import "./Wallets.sol";
26
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.
27
44
  library Fraud {
45
+ using Wallets for BridgeState.Storage;
46
+
28
47
  using BytesLib for bytes;
29
48
  using BTCUtils for bytes;
30
49
  using BTCUtils for uint32;
@@ -41,32 +60,18 @@ library Fraud {
41
60
  bool resolved;
42
61
  }
43
62
 
44
- event FraudSlashingAmountUpdated(uint256 newFraudSlashingAmount);
45
-
46
- event FraudNotifierRewardMultiplierUpdated(
47
- uint256 newFraudNotifierRewardMultiplier
48
- );
49
-
50
- event FraudChallengeDefeatTimeoutUpdated(
51
- uint256 newFraudChallengeDefeatTimeout
52
- );
53
-
54
- event FraudChallengeDepositAmountUpdated(
55
- uint256 newFraudChallengeDepositAmount
56
- );
57
-
58
63
  event FraudChallengeSubmitted(
59
- bytes20 walletPublicKeyHash,
64
+ bytes20 walletPubKeyHash,
60
65
  bytes32 sighash,
61
66
  uint8 v,
62
67
  bytes32 r,
63
68
  bytes32 s
64
69
  );
65
70
 
66
- event FraudChallengeDefeated(bytes20 walletPublicKeyHash, bytes32 sighash);
71
+ event FraudChallengeDefeated(bytes20 walletPubKeyHash, bytes32 sighash);
67
72
 
68
73
  event FraudChallengeDefeatTimedOut(
69
- bytes20 walletPublicKeyHash,
74
+ bytes20 walletPubKeyHash,
70
75
  bytes32 sighash
71
76
  );
72
77
 
@@ -95,8 +100,8 @@ library Fraud {
95
100
  /// for reference
96
101
  /// @param signature Bitcoin signature in the R/S/V format
97
102
  /// @dev Requirements:
98
- /// - Wallet behind `walletPublicKey` must be in `Live` or `MovingFunds`
99
- /// state
103
+ /// - Wallet behind `walletPublicKey` must be in Live or MovingFunds
104
+ /// or Closing state
100
105
  /// - The challenger must send appropriate amount of ETH used as
101
106
  /// fraud challenge deposit
102
107
  /// - The signature (represented by r, s and v) must be generated by
@@ -104,7 +109,6 @@ library Fraud {
104
109
  /// - Wallet can be challenged for the given signature only once
105
110
  function submitFraudChallenge(
106
111
  BridgeState.Storage storage self,
107
- Wallets.Data storage wallets,
108
112
  bytes calldata walletPublicKey,
109
113
  bytes32 sighash,
110
114
  BitcoinTx.RSVSignature calldata signature
@@ -131,14 +135,15 @@ library Fraud {
131
135
  );
132
136
  bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
133
137
 
134
- Wallets.Wallet storage wallet = wallets.registeredWallets[
138
+ Wallets.Wallet storage wallet = self.registeredWallets[
135
139
  walletPubKeyHash
136
140
  ];
137
141
 
138
142
  require(
139
143
  wallet.state == Wallets.WalletState.Live ||
140
- wallet.state == Wallets.WalletState.MovingFunds,
141
- "Wallet is neither in Live nor MovingFunds state"
144
+ wallet.state == Wallets.WalletState.MovingFunds ||
145
+ wallet.state == Wallets.WalletState.Closing,
146
+ "Wallet must be in Live or MovingFunds or Closing state"
142
147
  );
143
148
 
144
149
  uint256 challengeKey = uint256(
@@ -153,7 +158,7 @@ library Fraud {
153
158
  /* solhint-disable-next-line not-rely-on-time */
154
159
  challenge.reportedAt = uint32(block.timestamp);
155
160
  challenge.resolved = false;
156
-
161
+ // slither-disable-next-line reentrancy-events
157
162
  emit FraudChallengeSubmitted(
158
163
  walletPubKeyHash,
159
164
  sighash,
@@ -230,7 +235,7 @@ library Fraud {
230
235
 
231
236
  // Send the ether deposited by the challenger to the treasury
232
237
  /* solhint-disable avoid-low-level-calls */
233
- // slither-disable-next-line low-level-calls
238
+ // slither-disable-next-line low-level-calls,unchecked-lowlevel,arbitrary-send
234
239
  self.treasury.call{gas: 100000, value: challenge.depositAmount}("");
235
240
  /* solhint-enable avoid-low-level-calls */
236
241
 
@@ -239,7 +244,7 @@ library Fraud {
239
244
  walletPublicKey.slice32(32)
240
245
  );
241
246
  bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
242
-
247
+ // slither-disable-next-line reentrancy-events
243
248
  emit FraudChallengeDefeated(walletPubKeyHash, sighash);
244
249
  }
245
250
 
@@ -262,9 +267,11 @@ library Fraud {
262
267
  /// the transaction input the signature is produced for. See BIP-143
263
268
  /// for reference
264
269
  /// @dev Requirements:
265
- /// - `walletPublicKey` and `sighash` must identify an open fraud
270
+ /// - The wallet must be in the Live or MovingFunds or Closing or
271
+ /// Terminated state
272
+ /// - The `walletPublicKey` and `sighash` must identify an open fraud
266
273
  /// challenge
267
- /// - the amount of time indicated by `challengeDefeatTimeout` must pass
274
+ /// - The amount of time indicated by `challengeDefeatTimeout` must pass
268
275
  /// after the challenge was reported
269
276
  function notifyFraudChallengeDefeatTimeout(
270
277
  BridgeState.Storage storage self,
@@ -276,11 +283,14 @@ library Fraud {
276
283
  );
277
284
 
278
285
  FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
286
+
279
287
  require(challenge.reportedAt > 0, "Fraud challenge does not exist");
288
+
280
289
  require(
281
290
  !challenge.resolved,
282
291
  "Fraud challenge has already been resolved"
283
292
  );
293
+
284
294
  require(
285
295
  /* solhint-disable-next-line not-rely-on-time */
286
296
  block.timestamp >=
@@ -288,14 +298,10 @@ library Fraud {
288
298
  "Fraud challenge defeat period did not time out yet"
289
299
  );
290
300
 
291
- // TODO: Call notifyFraud from Wallets library
292
- // TODO: Reward the challenger
293
-
294
301
  challenge.resolved = true;
295
-
296
302
  // Return the ether deposited by the challenger
297
303
  /* solhint-disable avoid-low-level-calls */
298
- // slither-disable-next-line low-level-calls
304
+ // slither-disable-next-line low-level-calls,unchecked-lowlevel
299
305
  challenge.challenger.call{gas: 100000, value: challenge.depositAmount}(
300
306
  ""
301
307
  );
@@ -306,7 +312,33 @@ library Fraud {
306
312
  walletPublicKey.slice32(32)
307
313
  );
308
314
  bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
315
+ Wallets.WalletState walletState = self
316
+ .registeredWallets[walletPubKeyHash]
317
+ .state;
318
+
319
+ if (
320
+ walletState == Wallets.WalletState.Live ||
321
+ walletState == Wallets.WalletState.MovingFunds ||
322
+ walletState == Wallets.WalletState.Closing
323
+ ) {
324
+ self.terminateWallet(walletPubKeyHash);
325
+
326
+ // TODO: Perform slashing of the wallet operators, reward the
327
+ // challenger, and add unit tests for that.
328
+ } else if (walletState == Wallets.WalletState.Terminated) {
329
+ // This is a special case when the wallet was already terminated
330
+ // due to a previous deliberate protocol violation. In that
331
+ // case, this function should be still callable for other fraud
332
+ // challenges timeouts in order to let the challenger unlock its
333
+ // ETH deposit back. However, the wallet termination logic is
334
+ // not called and the challenger is not rewarded.
335
+ } else {
336
+ revert(
337
+ "Wallet must be in Live or MovingFunds or Closing or Terminated state"
338
+ );
339
+ }
309
340
 
341
+ // slither-disable-next-line reentrancy-events
310
342
  emit FraudChallengeDefeatTimedOut(walletPubKeyHash, sighash);
311
343
  }
312
344