@keep-network/tbtc-v2 0.1.1-dev.50 → 0.1.1-dev.53

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 (67) hide show
  1. package/artifacts/Bank.json +6 -6
  2. package/artifacts/Bridge.json +235 -141
  3. package/artifacts/Deposit.json +9 -9
  4. package/artifacts/EcdsaDkgValidator.json +1 -1
  5. package/artifacts/EcdsaInactivity.json +1 -1
  6. package/artifacts/Fraud.json +13 -12
  7. package/artifacts/KeepRegistry.json +1 -1
  8. package/artifacts/KeepStake.json +2 -2
  9. package/artifacts/KeepToken.json +2 -2
  10. package/artifacts/KeepTokenStaking.json +1 -1
  11. package/artifacts/MovingFunds.json +9 -9
  12. package/artifacts/NuCypherStakingEscrow.json +1 -1
  13. package/artifacts/NuCypherToken.json +2 -2
  14. package/artifacts/RandomBeaconStub.json +1 -1
  15. package/artifacts/Redemption.json +14 -13
  16. package/artifacts/ReimbursementPool.json +2 -2
  17. package/artifacts/Relay.json +11 -11
  18. package/artifacts/SortitionPool.json +2 -2
  19. package/artifacts/Sweep.json +7 -7
  20. package/artifacts/T.json +2 -2
  21. package/artifacts/TBTC.json +6 -6
  22. package/artifacts/TBTCToken.json +6 -6
  23. package/artifacts/TokenStaking.json +1 -1
  24. package/artifacts/TokenholderGovernor.json +9 -9
  25. package/artifacts/TokenholderTimelock.json +8 -8
  26. package/artifacts/VendingMachine.json +13 -13
  27. package/artifacts/VendingMachineKeep.json +1 -1
  28. package/artifacts/VendingMachineNuCypher.json +1 -1
  29. package/artifacts/WalletRegistry.json +2 -2
  30. package/artifacts/WalletRegistryGovernance.json +2 -2
  31. package/artifacts/Wallets.json +9 -9
  32. package/artifacts/solcInputs/{b0c3ed0992bd570aaaee717425c37538.json → fa22a04615b4037761340d27e55c86ee.json} +5 -5
  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 +104 -46
  39. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  40. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +24 -6
  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/Fraud.sol/Fraud.dbg.json +1 -1
  45. package/build/contracts/bridge/Fraud.sol/Fraud.json +2 -2
  46. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  47. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  48. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +2 -2
  49. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  50. package/build/contracts/bridge/Redemption.sol/OutboundTx.json +2 -2
  51. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  52. package/build/contracts/bridge/Redemption.sol/Redemption.json +2 -2
  53. package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +1 -1
  54. package/build/contracts/bridge/Sweep.sol/Sweep.json +2 -2
  55. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  56. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  57. package/build/contracts/bridge/Wallets.sol/Wallets.json +2 -2
  58. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  59. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  60. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  61. package/contracts/bridge/Bridge.sol +111 -43
  62. package/contracts/bridge/BridgeState.sol +72 -26
  63. package/contracts/bridge/Fraud.sol +21 -5
  64. package/contracts/bridge/Redemption.sol +33 -11
  65. package/contracts/bridge/Wallets.sol +9 -12
  66. package/export.json +70 -12
  67. package/package.json +1 -1
@@ -170,7 +170,9 @@ contract Bridge is Governable, EcdsaWalletOwner {
170
170
  uint64 redemptionDustThreshold,
171
171
  uint64 redemptionTreasuryFeeDivisor,
172
172
  uint64 redemptionTxMaxFee,
173
- uint256 redemptionTimeout
173
+ uint256 redemptionTimeout,
174
+ uint96 redemptionTimeoutSlashingAmount,
175
+ uint256 redemptionTimeoutNotifierRewardMultiplier
174
176
  );
175
177
 
176
178
  event MovingFundsParametersUpdated(
@@ -181,15 +183,16 @@ contract Bridge is Governable, EcdsaWalletOwner {
181
183
 
182
184
  event WalletParametersUpdated(
183
185
  uint32 walletCreationPeriod,
184
- uint64 walletMinBtcBalance,
185
- uint64 walletMaxBtcBalance,
186
+ uint64 walletCreationMinBtcBalance,
187
+ uint64 walletCreationMaxBtcBalance,
188
+ uint64 walletClosureMinBtcBalance,
186
189
  uint32 walletMaxAge,
187
190
  uint64 walletMaxBtcTransfer,
188
191
  uint32 walletClosingPeriod
189
192
  );
190
193
 
191
194
  event FraudParametersUpdated(
192
- uint256 fraudSlashingAmount,
195
+ uint96 fraudSlashingAmount,
193
196
  uint256 fraudNotifierRewardMultiplier,
194
197
  uint256 fraudChallengeDefeatTimeout,
195
198
  uint256 fraudChallengeDepositAmount
@@ -227,6 +230,8 @@ contract Bridge is Governable, EcdsaWalletOwner {
227
230
  self.redemptionTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
228
231
  self.redemptionTxMaxFee = 10000; // 10000 satoshi
229
232
  self.redemptionTimeout = 172800; // 48 hours
233
+ self.redemptionTimeoutSlashingAmount = 10000 * 1e18; // 10000 T
234
+ self.redemptionTimeoutNotifierRewardMultiplier = 100; // 100%
230
235
  self.movingFundsTxMaxTotalFee = 10000; // 10000 satoshi
231
236
  self.movingFundsTimeout = 7 days;
232
237
  self.movingFundsDustThreshold = 20000; // 20000 satoshi
@@ -235,8 +240,9 @@ contract Bridge is Governable, EcdsaWalletOwner {
235
240
  self.fraudChallengeDefeatTimeout = 7 days;
236
241
  self.fraudChallengeDepositAmount = 2 ether;
237
242
  self.walletCreationPeriod = 1 weeks;
238
- self.walletMinBtcBalance = 1e8; // 1 BTC
239
- self.walletMaxBtcBalance = 10e8; // 10 BTC
243
+ self.walletCreationMinBtcBalance = 1e8; // 1 BTC
244
+ self.walletCreationMaxBtcBalance = 100e8; // 100 BTC
245
+ self.walletClosureMinBtcBalance = 5 * 1e7; // 0.5 BTC
240
246
  self.walletMaxAge = 26 weeks; // ~6 months
241
247
  self.walletMaxBtcTransfer = 10e8; // 10 BTC
242
248
  self.walletClosingPeriod = 40 days;
@@ -434,31 +440,47 @@ contract Bridge is Governable, EcdsaWalletOwner {
434
440
  /// with the given wallet, that has timed out. The redemption
435
441
  /// request is identified by the key built as
436
442
  /// `keccak256(walletPubKeyHash | redeemerOutputScript)`.
437
- /// The results of calling this function: the pending redemptions
438
- /// value for the wallet will be decreased by the requested amount
439
- /// (minus treasury fee), the tokens taken from the redeemer on
440
- /// redemption request will be returned to the redeemer, the request
441
- /// will be moved from pending redemptions to timed-out redemptions.
442
- /// If the state of the wallet is `Live` or `MovingFunds`, the
443
- /// wallet operators will be slashed.
444
- /// Additionally, if the state of wallet is `Live`, the wallet will
445
- /// be closed or marked as `MovingFunds` (depending on the presence
446
- /// or absence of the wallet's main UTXO) and the wallet will no
447
- /// longer be marked as the active wallet (if it was marked as such).
443
+ /// The results of calling this function:
444
+ /// - the pending redemptions value for the wallet will be decreased
445
+ /// by the requested amount (minus treasury fee),
446
+ /// - the tokens taken from the redeemer on redemption request will
447
+ /// be returned to the redeemer,
448
+ /// - the request will be moved from pending redemptions to
449
+ /// timed-out redemptions.
450
+ /// - if the state of the wallet is `Live` or `MovingFunds`, the
451
+ /// wallet operators will be slashed and the notifier will be
452
+ /// rewarded
453
+ /// - if the state of wallet is `Live`, the wallet will be closed or
454
+ /// marked as `MovingFunds` (depending on the presence or absence
455
+ /// of the wallet's main UTXO) and the wallet will no longer be
456
+ /// marked as the active wallet (if it was marked as such).
448
457
  /// @param walletPubKeyHash 20-byte public key hash of the wallet
458
+ /// @param walletMembersIDs Identifiers of the wallet signing group members
449
459
  /// @param redeemerOutputScript The redeemer's length-prefixed output
450
460
  /// script (P2PKH, P2WPKH, P2SH or P2WSH)
451
461
  /// @dev Requirements:
462
+ /// - The wallet must be in the Live or MovingFunds or Terminated state
452
463
  /// - The redemption request identified by `walletPubKeyHash` and
453
464
  /// `redeemerOutputScript` must exist
465
+ /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
466
+ /// be exactly the same as the hash stored under `membersIdsHash`
467
+ /// for the given `walletID`. Those IDs are not directly stored
468
+ /// in the contract for gas efficiency purposes but they can be
469
+ /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`
470
+ /// events.
454
471
  /// - The amount of time defined by `redemptionTimeout` must have
455
472
  /// passed since the redemption was requested (the request must be
456
473
  /// timed-out).
457
474
  function notifyRedemptionTimeout(
458
475
  bytes20 walletPubKeyHash,
476
+ uint32[] calldata walletMembersIDs,
459
477
  bytes calldata redeemerOutputScript
460
478
  ) external {
461
- self.notifyRedemptionTimeout(walletPubKeyHash, redeemerOutputScript);
479
+ self.notifyRedemptionTimeout(
480
+ walletPubKeyHash,
481
+ walletMembersIDs,
482
+ redeemerOutputScript
483
+ );
462
484
  }
463
485
 
464
486
  /// @notice Submits the moving funds target wallets commitment.
@@ -785,6 +807,7 @@ contract Bridge is Governable, EcdsaWalletOwner {
785
807
  /// rewarded.
786
808
  /// @param walletPublicKey The public key of the wallet in the uncompressed
787
809
  /// and unprefixed format (64 bytes)
810
+ /// @param walletMembersIDs Identifiers of the wallet signing group members
788
811
  /// @param sighash The hash that was used to produce the ECDSA signature
789
812
  /// that is the subject of the fraud claim. This hash is constructed
790
813
  /// by applying double SHA-256 over a serialized subset of the
@@ -792,15 +815,28 @@ contract Bridge is Governable, EcdsaWalletOwner {
792
815
  /// the transaction input the signature is produced for. See BIP-143
793
816
  /// for reference
794
817
  /// @dev Requirements:
795
- /// - `walletPublicKey`and `sighash` must identify an open fraud
818
+ /// - The wallet must be in the Live or MovingFunds or Closing or
819
+ /// Terminated state
820
+ /// - The `walletPublicKey` and `sighash` must identify an open fraud
796
821
  /// challenge
797
- /// - the amount of time indicated by `challengeDefeatTimeout` must
798
- /// pass after the challenge was reported
822
+ /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
823
+ /// be exactly the same as the hash stored under `membersIdsHash`
824
+ /// for the given `walletID`. Those IDs are not directly stored
825
+ /// in the contract for gas efficiency purposes but they can be
826
+ /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`
827
+ /// events.
828
+ /// - The amount of time indicated by `challengeDefeatTimeout` must pass
829
+ /// after the challenge was reported
799
830
  function notifyFraudChallengeDefeatTimeout(
800
831
  bytes calldata walletPublicKey,
832
+ uint32[] calldata walletMembersIDs,
801
833
  bytes32 sighash
802
834
  ) external {
803
- self.notifyFraudChallengeDefeatTimeout(walletPublicKey, sighash);
835
+ self.notifyFraudChallengeDefeatTimeout(
836
+ walletPublicKey,
837
+ walletMembersIDs,
838
+ sighash
839
+ );
804
840
  }
805
841
 
806
842
  /// @notice Allows the Governance to mark the given vault address as trusted
@@ -890,22 +926,36 @@ contract Bridge is Governable, EcdsaWalletOwner {
890
926
  /// request was created via `requestRedemption` call. Reported timed
891
927
  /// out requests are cancelled and locked TBTC is returned to the
892
928
  /// redeemer in full amount.
929
+ /// @param redemptionTimeoutSlashingAmount New value of the redemption
930
+ /// timeout slashing amount in T, it is the amount slashed from each
931
+ /// wallet member for redemption timeout
932
+ /// @param redemptionTimeoutNotifierRewardMultiplier New value of the
933
+ /// redemption timeout notifier reward multiplier as percentage,
934
+ /// it determines the percentage of the notifier reward from the
935
+ /// staking contact the notifier of a redemption timeout receives.
936
+ /// The value must be in the range [0, 100]
893
937
  /// @dev Requirements:
894
938
  /// - Redemption dust threshold must be greater than zero
895
939
  /// - Redemption treasury fee divisor must be greater than zero
896
940
  /// - Redemption transaction max fee must be greater than zero
897
941
  /// - Redemption timeout must be greater than zero
942
+ /// - Redemption timeout notifier reward multiplier must be in the
943
+ /// range [0, 100]
898
944
  function updateRedemptionParameters(
899
945
  uint64 redemptionDustThreshold,
900
946
  uint64 redemptionTreasuryFeeDivisor,
901
947
  uint64 redemptionTxMaxFee,
902
- uint256 redemptionTimeout
948
+ uint256 redemptionTimeout,
949
+ uint96 redemptionTimeoutSlashingAmount,
950
+ uint256 redemptionTimeoutNotifierRewardMultiplier
903
951
  ) external onlyGovernance {
904
952
  self.updateRedemptionParameters(
905
953
  redemptionDustThreshold,
906
954
  redemptionTreasuryFeeDivisor,
907
955
  redemptionTxMaxFee,
908
- redemptionTimeout
956
+ redemptionTimeout,
957
+ redemptionTimeoutSlashingAmount,
958
+ redemptionTimeoutNotifierRewardMultiplier
909
959
  );
910
960
  }
911
961
 
@@ -946,10 +996,12 @@ contract Bridge is Governable, EcdsaWalletOwner {
946
996
  /// @param walletCreationPeriod New value of the wallet creation period in
947
997
  /// seconds, determines how frequently a new wallet creation can be
948
998
  /// requested
949
- /// @param walletMinBtcBalance New value of the wallet minimum BTC balance
950
- /// in satoshi, used to decide about wallet creation or closing
951
- /// @param walletMaxBtcBalance New value of the wallet maximum BTC balance
952
- /// in satoshi, used to decide about wallet creation
999
+ /// @param walletCreationMinBtcBalance New value of the wallet minimum BTC
1000
+ /// balance in satoshi, used to decide about wallet creation
1001
+ /// @param walletCreationMaxBtcBalance New value of the wallet maximum BTC
1002
+ /// balance in satoshi, used to decide about wallet creation
1003
+ /// @param walletClosureMinBtcBalance New value of the wallet minimum BTC
1004
+ /// balance in satoshi, used to decide about wallet closure
953
1005
  /// @param walletMaxAge New value of the wallet maximum age in seconds,
954
1006
  /// indicates the maximum age of a wallet in seconds, after which
955
1007
  /// the wallet moving funds process can be requested
@@ -968,16 +1020,18 @@ contract Bridge is Governable, EcdsaWalletOwner {
968
1020
  /// - Wallet closing period must be greater than zero
969
1021
  function updateWalletParameters(
970
1022
  uint32 walletCreationPeriod,
971
- uint64 walletMinBtcBalance,
972
- uint64 walletMaxBtcBalance,
1023
+ uint64 walletCreationMinBtcBalance,
1024
+ uint64 walletCreationMaxBtcBalance,
1025
+ uint64 walletClosureMinBtcBalance,
973
1026
  uint32 walletMaxAge,
974
1027
  uint64 walletMaxBtcTransfer,
975
1028
  uint32 walletClosingPeriod
976
1029
  ) external onlyGovernance {
977
1030
  self.updateWalletParameters(
978
1031
  walletCreationPeriod,
979
- walletMinBtcBalance,
980
- walletMaxBtcBalance,
1032
+ walletCreationMinBtcBalance,
1033
+ walletCreationMaxBtcBalance,
1034
+ walletClosureMinBtcBalance,
981
1035
  walletMaxAge,
982
1036
  walletMaxBtcTransfer,
983
1037
  walletClosingPeriod
@@ -1002,7 +1056,7 @@ contract Bridge is Governable, EcdsaWalletOwner {
1002
1056
  /// - Fraud notifier reward multiplier must be in the range [0, 100]
1003
1057
  /// - Fraud challenge defeat timeout must be greater than 0
1004
1058
  function updateFraudParameters(
1005
- uint256 fraudSlashingAmount,
1059
+ uint96 fraudSlashingAmount,
1006
1060
  uint256 fraudNotifierRewardMultiplier,
1007
1061
  uint256 fraudChallengeDefeatTimeout,
1008
1062
  uint256 fraudChallengeDepositAmount
@@ -1185,6 +1239,11 @@ contract Bridge is Governable, EcdsaWalletOwner {
1185
1239
  /// redemption request was created via `requestRedemption` call.
1186
1240
  /// Reported timed out requests are cancelled and locked TBTC is
1187
1241
  /// returned to the redeemer in full amount.
1242
+ /// @return redemptionTimeoutSlashingAmount The amount of stake slashed
1243
+ /// from each member of a wallet for a redemption timeout.
1244
+ /// @return redemptionTimeoutNotifierRewardMultiplier The percentage of the
1245
+ /// notifier reward from the staking contract the notifier of a
1246
+ /// redemption timeout receives. The value is in the range [0, 100].
1188
1247
  function redemptionParameters()
1189
1248
  external
1190
1249
  view
@@ -1192,13 +1251,18 @@ contract Bridge is Governable, EcdsaWalletOwner {
1192
1251
  uint64 redemptionDustThreshold,
1193
1252
  uint64 redemptionTreasuryFeeDivisor,
1194
1253
  uint64 redemptionTxMaxFee,
1195
- uint256 redemptionTimeout
1254
+ uint256 redemptionTimeout,
1255
+ uint96 redemptionTimeoutSlashingAmount,
1256
+ uint256 redemptionTimeoutNotifierRewardMultiplier
1196
1257
  )
1197
1258
  {
1198
1259
  redemptionDustThreshold = self.redemptionDustThreshold;
1199
1260
  redemptionTreasuryFeeDivisor = self.redemptionTreasuryFeeDivisor;
1200
1261
  redemptionTxMaxFee = self.redemptionTxMaxFee;
1201
1262
  redemptionTimeout = self.redemptionTimeout;
1263
+ redemptionTimeoutSlashingAmount = self.redemptionTimeoutSlashingAmount;
1264
+ redemptionTimeoutNotifierRewardMultiplier = self
1265
+ .redemptionTimeoutNotifierRewardMultiplier;
1202
1266
  }
1203
1267
 
1204
1268
  /// @notice Returns the current values of Bridge moving funds between
@@ -1232,10 +1296,12 @@ contract Bridge is Governable, EcdsaWalletOwner {
1232
1296
 
1233
1297
  /// @return walletCreationPeriod Determines how frequently a new wallet
1234
1298
  /// creation can be requested. Value in seconds.
1235
- /// @return walletMinBtcBalance The minimum BTC threshold in satoshi that is
1236
- /// used to decide about wallet creation or closing.
1237
- /// @return walletMaxBtcBalance The maximum BTC threshold in satoshi that is
1238
- /// used to decide about wallet creation.
1299
+ /// @return walletCreationMinBtcBalance The minimum BTC threshold in satoshi
1300
+ /// that is used to decide about wallet creation.
1301
+ /// @return walletCreationMaxBtcBalance The maximum BTC threshold in satoshi
1302
+ /// that is used to decide about wallet creation.
1303
+ /// @return walletClosureMinBtcBalance The minimum BTC threshold in satoshi
1304
+ /// that is used to decide about wallet closure.
1239
1305
  /// @return walletMaxAge The maximum age of a wallet in seconds, after which
1240
1306
  /// the wallet moving funds process can be requested.
1241
1307
  /// @return walletMaxBtcTransfer The maximum BTC amount in satoshi than
@@ -1250,16 +1316,18 @@ contract Bridge is Governable, EcdsaWalletOwner {
1250
1316
  view
1251
1317
  returns (
1252
1318
  uint32 walletCreationPeriod,
1253
- uint64 walletMinBtcBalance,
1254
- uint64 walletMaxBtcBalance,
1319
+ uint64 walletCreationMinBtcBalance,
1320
+ uint64 walletCreationMaxBtcBalance,
1321
+ uint64 walletClosureMinBtcBalance,
1255
1322
  uint32 walletMaxAge,
1256
1323
  uint64 walletMaxBtcTransfer,
1257
1324
  uint32 walletClosingPeriod
1258
1325
  )
1259
1326
  {
1260
1327
  walletCreationPeriod = self.walletCreationPeriod;
1261
- walletMinBtcBalance = self.walletMinBtcBalance;
1262
- walletMaxBtcBalance = self.walletMaxBtcBalance;
1328
+ walletCreationMinBtcBalance = self.walletCreationMinBtcBalance;
1329
+ walletCreationMaxBtcBalance = self.walletCreationMaxBtcBalance;
1330
+ walletClosureMinBtcBalance = self.walletClosureMinBtcBalance;
1263
1331
  walletMaxAge = self.walletMaxAge;
1264
1332
  walletMaxBtcTransfer = self.walletMaxBtcTransfer;
1265
1333
  walletClosingPeriod = self.walletClosingPeriod;
@@ -1279,7 +1347,7 @@ contract Bridge is Governable, EcdsaWalletOwner {
1279
1347
  external
1280
1348
  view
1281
1349
  returns (
1282
- uint256 fraudSlashingAmount,
1350
+ uint96 fraudSlashingAmount,
1283
1351
  uint256 fraudNotifierRewardMultiplier,
1284
1352
  uint256 fraudChallengeDefeatTimeout,
1285
1353
  uint256 fraudChallengeDepositAmount
@@ -122,6 +122,13 @@ library BridgeState {
122
122
  // timed out requests are cancelled and locked TBTC is returned
123
123
  // to the redeemer in full amount.
124
124
  uint256 redemptionTimeout;
125
+ // The amount of stake slashed from each member of a wallet for a
126
+ // redemption timeout.
127
+ uint96 redemptionTimeoutSlashingAmount;
128
+ // The percentage of the notifier reward from the staking contract
129
+ // the notifier of a redemption timeout receives. The value is in the
130
+ // range [0, 100].
131
+ uint256 redemptionTimeoutNotifierRewardMultiplier;
125
132
  // Collection of all pending redemption requests indexed by
126
133
  // redemption key built as
127
134
  // `keccak256(walletPubKeyHash | redeemerOutputScript)`.
@@ -152,7 +159,7 @@ library BridgeState {
152
159
  // `pendingRedemptions` mapping.
153
160
  mapping(uint256 => Redemption.RedemptionRequest) timedOutRedemptions;
154
161
  // The amount of stake slashed from each member of a wallet for a fraud.
155
- uint256 fraudSlashingAmount;
162
+ uint96 fraudSlashingAmount;
156
163
  // The percentage of the notifier reward from the staking contract
157
164
  // the notifier of a fraud receives. The value is in the range [0, 100].
158
165
  uint256 fraudNotifierRewardMultiplier;
@@ -175,11 +182,20 @@ library BridgeState {
175
182
  // Value in seconds.
176
183
  uint32 walletCreationPeriod;
177
184
  // The minimum BTC threshold in satoshi that is used to decide about
178
- // wallet creation or closing.
179
- uint64 walletMinBtcBalance;
185
+ // wallet creation. Specifically, we allow for the creation of a new
186
+ // wallet if the active wallet is old enough and their amount of BTC
187
+ // is greater than or equal this threshold.
188
+ uint64 walletCreationMinBtcBalance;
180
189
  // The maximum BTC threshold in satoshi that is used to decide about
181
- // wallet creation.
182
- uint64 walletMaxBtcBalance;
190
+ // wallet creation. Specifically, we allow for the creation of a new
191
+ // wallet if the active wallet's amount of BTC is greater than or equal
192
+ // this threshold, regardless of the active wallet's age.
193
+ uint64 walletCreationMaxBtcBalance;
194
+ // The minimum BTC threshold in satoshi that is used to decide about
195
+ // wallet closing. Specifically, we allow for the closure of the given
196
+ // wallet if their amount of BTC is lesser than this threshold,
197
+ // regardless of the wallet's age.
198
+ uint64 walletClosureMinBtcBalance;
183
199
  // The maximum age of a wallet in seconds, after which the wallet
184
200
  // moving funds process can be requested.
185
201
  uint32 walletMaxAge;
@@ -213,7 +229,9 @@ library BridgeState {
213
229
  uint64 redemptionDustThreshold,
214
230
  uint64 redemptionTreasuryFeeDivisor,
215
231
  uint64 redemptionTxMaxFee,
216
- uint256 redemptionTimeout
232
+ uint256 redemptionTimeout,
233
+ uint96 redemptionTimeoutSlashingAmount,
234
+ uint256 redemptionTimeoutNotifierRewardMultiplier
217
235
  );
218
236
 
219
237
  event MovingFundsParametersUpdated(
@@ -224,15 +242,16 @@ library BridgeState {
224
242
 
225
243
  event WalletParametersUpdated(
226
244
  uint32 walletCreationPeriod,
227
- uint64 walletMinBtcBalance,
228
- uint64 walletMaxBtcBalance,
245
+ uint64 walletCreationMinBtcBalance,
246
+ uint64 walletCreationMaxBtcBalance,
247
+ uint64 walletClosureMinBtcBalance,
229
248
  uint32 walletMaxAge,
230
249
  uint64 walletMaxBtcTransfer,
231
250
  uint32 walletClosingPeriod
232
251
  );
233
252
 
234
253
  event FraudParametersUpdated(
235
- uint256 fraudSlashingAmount,
254
+ uint96 fraudSlashingAmount,
236
255
  uint256 fraudNotifierRewardMultiplier,
237
256
  uint256 fraudChallengeDefeatTimeout,
238
257
  uint256 fraudChallengeDepositAmount
@@ -324,17 +343,29 @@ library BridgeState {
324
343
  /// request was created via `requestRedemption` call. Reported timed
325
344
  /// out requests are cancelled and locked TBTC is returned to the
326
345
  /// redeemer in full amount.
346
+ /// @param _redemptionTimeoutSlashingAmount New value of the redemption
347
+ /// timeout slashing amount in T, it is the amount slashed from each
348
+ /// wallet member for redemption timeout
349
+ /// @param _redemptionTimeoutNotifierRewardMultiplier New value of the
350
+ /// redemption timeout notifier reward multiplier as percentage,
351
+ /// it determines the percentage of the notifier reward from the
352
+ /// staking contact the notifier of a redemption timeout receives.
353
+ /// The value must be in the range [0, 100]
327
354
  /// @dev Requirements:
328
355
  /// - Redemption dust threshold must be greater than zero
329
356
  /// - Redemption treasury fee divisor must be greater than zero
330
357
  /// - Redemption transaction max fee must be greater than zero
331
358
  /// - Redemption timeout must be greater than zero
359
+ /// - Redemption timeout notifier reward multiplier must be in the
360
+ /// range [0, 100]
332
361
  function updateRedemptionParameters(
333
362
  Storage storage self,
334
363
  uint64 _redemptionDustThreshold,
335
364
  uint64 _redemptionTreasuryFeeDivisor,
336
365
  uint64 _redemptionTxMaxFee,
337
- uint256 _redemptionTimeout
366
+ uint256 _redemptionTimeout,
367
+ uint96 _redemptionTimeoutSlashingAmount,
368
+ uint256 _redemptionTimeoutNotifierRewardMultiplier
338
369
  ) internal {
339
370
  require(
340
371
  _redemptionDustThreshold > 0,
@@ -356,16 +387,26 @@ library BridgeState {
356
387
  "Redemption timeout must be greater than zero"
357
388
  );
358
389
 
390
+ require(
391
+ _redemptionTimeoutNotifierRewardMultiplier <= 100,
392
+ "Redemption timeout notifier reward multiplier must be in the range [0, 100]"
393
+ );
394
+
359
395
  self.redemptionDustThreshold = _redemptionDustThreshold;
360
396
  self.redemptionTreasuryFeeDivisor = _redemptionTreasuryFeeDivisor;
361
397
  self.redemptionTxMaxFee = _redemptionTxMaxFee;
362
398
  self.redemptionTimeout = _redemptionTimeout;
399
+ self.redemptionTimeoutSlashingAmount = _redemptionTimeoutSlashingAmount;
400
+ self
401
+ .redemptionTimeoutNotifierRewardMultiplier = _redemptionTimeoutNotifierRewardMultiplier;
363
402
 
364
403
  emit RedemptionParametersUpdated(
365
404
  _redemptionDustThreshold,
366
405
  _redemptionTreasuryFeeDivisor,
367
406
  _redemptionTxMaxFee,
368
- _redemptionTimeout
407
+ _redemptionTimeout,
408
+ _redemptionTimeoutSlashingAmount,
409
+ _redemptionTimeoutNotifierRewardMultiplier
369
410
  );
370
411
  }
371
412
 
@@ -426,10 +467,12 @@ library BridgeState {
426
467
  /// @param _walletCreationPeriod New value of the wallet creation period in
427
468
  /// seconds, determines how frequently a new wallet creation can be
428
469
  /// requested
429
- /// @param _walletMinBtcBalance New value of the wallet minimum BTC balance
430
- /// in satoshi, used to decide about wallet creation or closing
431
- /// @param _walletMaxBtcBalance New value of the wallet maximum BTC balance
432
- /// in satoshi, used to decide about wallet creation
470
+ /// @param _walletCreationMinBtcBalance New value of the wallet minimum BTC
471
+ /// balance in satoshi, used to decide about wallet creation
472
+ /// @param _walletCreationMaxBtcBalance New value of the wallet maximum BTC
473
+ /// balance in satoshi, used to decide about wallet creation
474
+ /// @param _walletClosureMinBtcBalance New value of the wallet minimum BTC
475
+ /// balance in satoshi, used to decide about wallet closure
433
476
  /// @param _walletMaxAge New value of the wallet maximum age in seconds,
434
477
  /// indicates the maximum age of a wallet in seconds, after which
435
478
  /// the wallet moving funds process can be requested
@@ -449,19 +492,20 @@ library BridgeState {
449
492
  function updateWalletParameters(
450
493
  Storage storage self,
451
494
  uint32 _walletCreationPeriod,
452
- uint64 _walletMinBtcBalance,
453
- uint64 _walletMaxBtcBalance,
495
+ uint64 _walletCreationMinBtcBalance,
496
+ uint64 _walletCreationMaxBtcBalance,
497
+ uint64 _walletClosureMinBtcBalance,
454
498
  uint32 _walletMaxAge,
455
499
  uint64 _walletMaxBtcTransfer,
456
500
  uint32 _walletClosingPeriod
457
501
  ) internal {
458
502
  require(
459
- _walletMinBtcBalance > 0,
460
- "Wallet minimum BTC balance must be greater than zero"
503
+ _walletCreationMaxBtcBalance > _walletCreationMinBtcBalance,
504
+ "Wallet creation maximum BTC balance must be greater than the creation minimum BTC balance"
461
505
  );
462
506
  require(
463
- _walletMaxBtcBalance > _walletMinBtcBalance,
464
- "Wallet maximum BTC balance must be greater than the minimum"
507
+ _walletClosureMinBtcBalance > 0,
508
+ "Wallet closure minimum BTC balance must be greater than zero"
465
509
  );
466
510
  require(
467
511
  _walletMaxBtcTransfer > 0,
@@ -473,16 +517,18 @@ library BridgeState {
473
517
  );
474
518
 
475
519
  self.walletCreationPeriod = _walletCreationPeriod;
476
- self.walletMinBtcBalance = _walletMinBtcBalance;
477
- self.walletMaxBtcBalance = _walletMaxBtcBalance;
520
+ self.walletCreationMinBtcBalance = _walletCreationMinBtcBalance;
521
+ self.walletCreationMaxBtcBalance = _walletCreationMaxBtcBalance;
522
+ self.walletClosureMinBtcBalance = _walletClosureMinBtcBalance;
478
523
  self.walletMaxAge = _walletMaxAge;
479
524
  self.walletMaxBtcTransfer = _walletMaxBtcTransfer;
480
525
  self.walletClosingPeriod = _walletClosingPeriod;
481
526
 
482
527
  emit WalletParametersUpdated(
483
528
  _walletCreationPeriod,
484
- _walletMinBtcBalance,
485
- _walletMaxBtcBalance,
529
+ _walletCreationMinBtcBalance,
530
+ _walletCreationMaxBtcBalance,
531
+ _walletClosureMinBtcBalance,
486
532
  _walletMaxAge,
487
533
  _walletMaxBtcTransfer,
488
534
  _walletClosingPeriod
@@ -508,7 +554,7 @@ library BridgeState {
508
554
  /// - Fraud challenge defeat timeout must be greater than 0
509
555
  function updateFraudParameters(
510
556
  Storage storage self,
511
- uint256 _fraudSlashingAmount,
557
+ uint96 _fraudSlashingAmount,
512
558
  uint256 _fraudNotifierRewardMultiplier,
513
559
  uint256 _fraudChallengeDefeatTimeout,
514
560
  uint256 _fraudChallengeDepositAmount
@@ -260,6 +260,7 @@ library Fraud {
260
260
  /// rewarded.
261
261
  /// @param walletPublicKey The public key of the wallet in the uncompressed
262
262
  /// and unprefixed format (64 bytes)
263
+ /// @param walletMembersIDs Identifiers of the wallet signing group members
263
264
  /// @param sighash The hash that was used to produce the ECDSA signature
264
265
  /// that is the subject of the fraud claim. This hash is constructed
265
266
  /// by applying double SHA-256 over a serialized subset of the
@@ -271,11 +272,18 @@ library Fraud {
271
272
  /// Terminated state
272
273
  /// - The `walletPublicKey` and `sighash` must identify an open fraud
273
274
  /// challenge
275
+ /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
276
+ /// be exactly the same as the hash stored under `membersIdsHash`
277
+ /// for the given `walletID`. Those IDs are not directly stored
278
+ /// in the contract for gas efficiency purposes but they can be
279
+ /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`
280
+ /// events.
274
281
  /// - The amount of time indicated by `challengeDefeatTimeout` must pass
275
282
  /// after the challenge was reported
276
283
  function notifyFraudChallengeDefeatTimeout(
277
284
  BridgeState.Storage storage self,
278
285
  bytes calldata walletPublicKey,
286
+ uint32[] calldata walletMembersIDs,
279
287
  bytes32 sighash
280
288
  ) external {
281
289
  uint256 challengeKey = uint256(
@@ -312,9 +320,12 @@ library Fraud {
312
320
  walletPublicKey.slice32(32)
313
321
  );
314
322
  bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
315
- Wallets.WalletState walletState = self
316
- .registeredWallets[walletPubKeyHash]
317
- .state;
323
+
324
+ Wallets.Wallet storage wallet = self.registeredWallets[
325
+ walletPubKeyHash
326
+ ];
327
+
328
+ Wallets.WalletState walletState = wallet.state;
318
329
 
319
330
  if (
320
331
  walletState == Wallets.WalletState.Live ||
@@ -323,8 +334,13 @@ library Fraud {
323
334
  ) {
324
335
  self.terminateWallet(walletPubKeyHash);
325
336
 
326
- // TODO: Perform slashing of the wallet operators, reward the
327
- // challenger, and add unit tests for that.
337
+ self.ecdsaWalletRegistry.seize(
338
+ self.fraudSlashingAmount,
339
+ self.fraudNotifierRewardMultiplier,
340
+ challenge.challenger,
341
+ wallet.ecdsaWalletID,
342
+ walletMembersIDs
343
+ );
328
344
  } else if (walletState == Wallets.WalletState.Terminated) {
329
345
  // This is a special case when the wallet was already terminated
330
346
  // due to a previous deliberate protocol violation. In that