@keep-network/tbtc-v2 0.1.1-dev.56 → 0.1.1-dev.59

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 +3 -3
  2. package/artifacts/Bridge.json +402 -177
  3. package/artifacts/Deposit.json +9 -9
  4. package/artifacts/DepositSweep.json +9 -9
  5. package/artifacts/EcdsaDkgValidator.json +1 -1
  6. package/artifacts/EcdsaInactivity.json +1 -1
  7. package/artifacts/Fraud.json +9 -9
  8. package/artifacts/KeepRegistry.json +1 -1
  9. package/artifacts/KeepStake.json +2 -2
  10. package/artifacts/KeepToken.json +2 -2
  11. package/artifacts/KeepTokenStaking.json +1 -1
  12. package/artifacts/MovingFunds.json +47 -11
  13. package/artifacts/NuCypherStakingEscrow.json +1 -1
  14. package/artifacts/NuCypherToken.json +2 -2
  15. package/artifacts/RandomBeaconStub.json +1 -1
  16. package/artifacts/Redemption.json +9 -9
  17. package/artifacts/ReimbursementPool.json +2 -2
  18. package/artifacts/Relay.json +9 -9
  19. package/artifacts/SortitionPool.json +2 -2
  20. package/artifacts/T.json +2 -2
  21. package/artifacts/TBTC.json +3 -3
  22. package/artifacts/TBTCToken.json +3 -3
  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 +10 -10
  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/{5dd2a7c685770548b7ea9ce25e179326.json → c86d08e5f2ce89fcf398a4658c796260.json} +6 -6
  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 +148 -39
  39. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  40. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +20 -2
  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/DepositSweep.sol/DepositSweep.dbg.json +1 -1
  44. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.json +2 -2
  45. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  46. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
  47. package/build/contracts/bridge/Fraud.sol/Fraud.json +2 -2
  48. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  49. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  50. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +27 -2
  51. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  52. package/build/contracts/bridge/Redemption.sol/OutboundTx.json +2 -2
  53. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  54. package/build/contracts/bridge/Redemption.sol/Redemption.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 +89 -4
  62. package/contracts/bridge/BridgeState.sol +54 -3
  63. package/contracts/bridge/Fraud.sol +5 -1
  64. package/contracts/bridge/MovingFunds.sol +135 -18
  65. package/contracts/bridge/Wallets.sol +2 -0
  66. package/export.json +235 -39
  67. package/package.json +1 -1
@@ -110,6 +110,17 @@ library BridgeState {
110
110
  // if per single redemption. `movedFundsSweepTxMaxTotalFee` is a total
111
111
  // fee for the entire transaction.
112
112
  uint64 movedFundsSweepTxMaxTotalFee;
113
+ // Time after which the moved funds sweep process can be reported as
114
+ // timed out. It is counted from the moment when the recipient wallet
115
+ // was requested to sweep the received funds. Value in seconds.
116
+ uint32 movedFundsSweepTimeout;
117
+ // The amount of stake slashed from each member of a wallet for a moved
118
+ // funds sweep timeout.
119
+ uint96 movedFundsSweepTimeoutSlashingAmount;
120
+ // The percentage of the notifier reward from the staking contract
121
+ // the notifier of a moved funds sweep timeout receives. The value is
122
+ // in the range [0, 100].
123
+ uint256 movedFundsSweepTimeoutNotifierRewardMultiplier;
113
124
  // Collection of all moved funds sweep requests indexed by
114
125
  // `keccak256(movingFundsTxHash | movingFundsOutputIndex)`.
115
126
  // The `movingFundsTxHash` is `bytes32` (ordered as in Bitcoin
@@ -263,7 +274,10 @@ library BridgeState {
263
274
  uint32 movingFundsTimeout,
264
275
  uint96 movingFundsTimeoutSlashingAmount,
265
276
  uint256 movingFundsTimeoutNotifierRewardMultiplier,
266
- uint64 movedFundsSweepTxMaxTotalFee
277
+ uint64 movedFundsSweepTxMaxTotalFee,
278
+ uint32 movedFundsSweepTimeout,
279
+ uint96 movedFundsSweepTimeoutSlashingAmount,
280
+ uint256 movedFundsSweepTimeoutNotifierRewardMultiplier
267
281
  );
268
282
 
269
283
  event WalletParametersUpdated(
@@ -466,6 +480,19 @@ library BridgeState {
466
480
  /// of the total BTC transaction fee that is acceptable in a single
467
481
  /// moved funds sweep transaction. This is a _total_ max fee for the
468
482
  /// entire moved funds sweep transaction.
483
+ /// @param _movedFundsSweepTimeout New value of the moved funds sweep
484
+ /// timeout in seconds. It is the time after which the moved funds
485
+ /// sweep process can be reported as timed out. It is counted from
486
+ /// the moment when the wallet was requested to sweep the received
487
+ /// funds.
488
+ /// @param _movedFundsSweepTimeoutSlashingAmount New value of the moved
489
+ /// funds sweep timeout slashing amount in T, it is the amount
490
+ /// slashed from each wallet member for moved funds sweep timeout
491
+ /// @param _movedFundsSweepTimeoutNotifierRewardMultiplier New value of
492
+ /// the moved funds sweep timeout notifier reward multiplier as
493
+ /// percentage, it determines the percentage of the notifier reward
494
+ /// from the staking contact the notifier of a moved funds sweep
495
+ /// timeout receives. The value must be in the range [0, 100]
469
496
  /// @dev Requirements:
470
497
  /// - Moving funds transaction max total fee must be greater than zero
471
498
  /// - Moving funds dust threshold must be greater than zero
@@ -473,6 +500,9 @@ library BridgeState {
473
500
  /// - Moving funds timeout notifier reward multiplier must be in the
474
501
  /// range [0, 100]
475
502
  /// - Moved funds sweep transaction max total fee must be greater than zero
503
+ /// - Moved funds sweep timeout must be greater than zero
504
+ /// - Moved funds sweep timeout notifier reward multiplier must be in the
505
+ /// range [0, 100]
476
506
  function updateMovingFundsParameters(
477
507
  Storage storage self,
478
508
  uint64 _movingFundsTxMaxTotalFee,
@@ -480,7 +510,10 @@ library BridgeState {
480
510
  uint32 _movingFundsTimeout,
481
511
  uint96 _movingFundsTimeoutSlashingAmount,
482
512
  uint256 _movingFundsTimeoutNotifierRewardMultiplier,
483
- uint64 _movedFundsSweepTxMaxTotalFee
513
+ uint64 _movedFundsSweepTxMaxTotalFee,
514
+ uint32 _movedFundsSweepTimeout,
515
+ uint96 _movedFundsSweepTimeoutSlashingAmount,
516
+ uint256 _movedFundsSweepTimeoutNotifierRewardMultiplier
484
517
  ) internal {
485
518
  require(
486
519
  _movingFundsTxMaxTotalFee > 0,
@@ -507,6 +540,16 @@ library BridgeState {
507
540
  "Moved funds sweep transaction max total fee must be greater than zero"
508
541
  );
509
542
 
543
+ require(
544
+ _movedFundsSweepTimeout > 0,
545
+ "Moved funds sweep timeout must be greater than zero"
546
+ );
547
+
548
+ require(
549
+ _movedFundsSweepTimeoutNotifierRewardMultiplier <= 100,
550
+ "Moved funds sweep timeout notifier reward multiplier must be in the range [0, 100]"
551
+ );
552
+
510
553
  self.movingFundsTxMaxTotalFee = _movingFundsTxMaxTotalFee;
511
554
  self.movingFundsDustThreshold = _movingFundsDustThreshold;
512
555
  self.movingFundsTimeout = _movingFundsTimeout;
@@ -515,6 +558,11 @@ library BridgeState {
515
558
  self
516
559
  .movingFundsTimeoutNotifierRewardMultiplier = _movingFundsTimeoutNotifierRewardMultiplier;
517
560
  self.movedFundsSweepTxMaxTotalFee = _movedFundsSweepTxMaxTotalFee;
561
+ self.movedFundsSweepTimeout = _movedFundsSweepTimeout;
562
+ self
563
+ .movedFundsSweepTimeoutSlashingAmount = _movedFundsSweepTimeoutSlashingAmount;
564
+ self
565
+ .movedFundsSweepTimeoutNotifierRewardMultiplier = _movedFundsSweepTimeoutNotifierRewardMultiplier;
518
566
 
519
567
  emit MovingFundsParametersUpdated(
520
568
  _movingFundsTxMaxTotalFee,
@@ -522,7 +570,10 @@ library BridgeState {
522
570
  _movingFundsTimeout,
523
571
  _movingFundsTimeoutSlashingAmount,
524
572
  _movingFundsTimeoutNotifierRewardMultiplier,
525
- _movedFundsSweepTxMaxTotalFee
573
+ _movedFundsSweepTxMaxTotalFee,
574
+ _movedFundsSweepTimeout,
575
+ _movedFundsSweepTimeoutSlashingAmount,
576
+ _movedFundsSweepTimeoutNotifierRewardMultiplier
526
577
  );
527
578
  }
528
579
 
@@ -22,6 +22,7 @@ import {CheckBitcoinSigs} from "@keep-network/bitcoin-spv-sol/contracts/CheckBit
22
22
  import "./BitcoinTx.sol";
23
23
  import "./EcdsaLib.sol";
24
24
  import "./BridgeState.sol";
25
+ import "./MovingFunds.sol";
25
26
  import "./Wallets.sol";
26
27
 
27
28
  /// @title Bridge fraud
@@ -226,7 +227,10 @@ library Fraud {
226
227
 
227
228
  // Check that the UTXO key identifies a correctly spent UTXO.
228
229
  require(
229
- self.deposits[utxoKey].sweptAt > 0 || self.spentMainUTXOs[utxoKey],
230
+ self.deposits[utxoKey].sweptAt > 0 ||
231
+ self.spentMainUTXOs[utxoKey] ||
232
+ self.movedFundsSweepRequests[utxoKey].state ==
233
+ MovingFunds.MovedFundsSweepRequestState.Processed,
230
234
  "Spent UTXO not found among correctly spent UTXOs"
231
235
  );
232
236
 
@@ -58,6 +58,19 @@ library MovingFunds {
58
58
  bytes movingFundsTxOutputVector;
59
59
  }
60
60
 
61
+ /// @notice Represents moved funds sweep request state.
62
+ enum MovedFundsSweepRequestState {
63
+ /// @dev The request is unknown to the Bridge.
64
+ Unknown,
65
+ /// @dev Request is pending and can become either processed or timed out.
66
+ Pending,
67
+ /// @dev Request was processed by the target wallet.
68
+ Processed,
69
+ /// @dev Request was not processed in the given time window and
70
+ /// the timeout was reported.
71
+ TimedOut
72
+ }
73
+
61
74
  /// @notice Represents a moved funds sweep request. The request is
62
75
  /// registered in `submitMovingFundsProof` where we know funds
63
76
  /// have been moved to the target wallet and the only step left is
@@ -70,8 +83,8 @@ library MovingFunds {
70
83
  uint64 value;
71
84
  // UNIX timestamp the request was created at.
72
85
  uint32 createdAt;
73
- // UNIX timestamp the funds were swept at.
74
- uint32 sweptAt;
86
+ // The current state of the request.
87
+ MovedFundsSweepRequestState state;
75
88
  }
76
89
 
77
90
  event MovingFundsCommitmentSubmitted(
@@ -91,6 +104,12 @@ library MovingFunds {
91
104
 
92
105
  event MovedFundsSwept(bytes20 walletPubKeyHash, bytes32 sweepTxHash);
93
106
 
107
+ event MovedFundsSweepTimedOut(
108
+ bytes20 walletPubKeyHash,
109
+ bytes32 movingFundsTxHash,
110
+ uint32 movingFundsTxOutputIndex
111
+ );
112
+
94
113
  /// @notice Submits the moving funds target wallets commitment.
95
114
  /// Once all requirements are met, that function registers the
96
115
  /// target wallets commitment and opens the way for moving funds
@@ -107,6 +126,7 @@ library MovingFunds {
107
126
  /// @dev Requirements:
108
127
  /// - The source wallet must be in the MovingFunds state
109
128
  /// - The source wallet must not have pending redemption requests
129
+ /// - The source wallet must not have pending moved funds sweep requests
110
130
  /// - The source wallet must not have submitted its commitment already
111
131
  /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
112
132
  /// be exactly the same as the hash stored under `membersIdsHash`
@@ -152,6 +172,11 @@ library MovingFunds {
152
172
  "Source wallet must handle all pending redemptions first"
153
173
  );
154
174
 
175
+ require(
176
+ wallet.pendingMovedFundsSweepRequestsCount == 0,
177
+ "Source wallet must handle all pending moved funds sweep requests first"
178
+ );
179
+
155
180
  require(
156
181
  wallet.movingFundsTargetWalletsCommitmentHash == bytes32(0),
157
182
  "Target wallets commitment already submitted"
@@ -414,8 +439,13 @@ library MovingFunds {
414
439
  outputsValues[outputIndex],
415
440
  /* solhint-disable-next-line not-rely-on-time */
416
441
  uint32(block.timestamp),
417
- 0
442
+ MovedFundsSweepRequestState.Pending
418
443
  );
444
+ // We added a new moved funds sweep request for the target wallet
445
+ // so we must increment their request counter.
446
+ self
447
+ .registeredWallets[targetWalletPubKeyHash]
448
+ .pendingMovedFundsSweepRequestsCount++;
419
449
 
420
450
  // Make the `outputStartingIndex` pointing to the next output by
421
451
  // increasing it by current output's length.
@@ -560,7 +590,7 @@ library MovingFunds {
560
590
  /// correspond to appropriate Bitcoin transaction fields to produce
561
591
  /// a provable transaction hash.
562
592
  /// - The `sweepTx` should represent a Bitcoin transaction with
563
- /// the first input pointing to a wallet's sweep request and,
593
+ /// the first input pointing to a wallet's sweep Pending request and,
564
594
  /// optionally, the second input pointing to the wallet's main UTXO,
565
595
  /// if the sweeping wallet has a main UTXO set. There should be only
566
596
  /// one output locking funds on the sweeping wallet 20-byte public
@@ -723,8 +753,8 @@ library MovingFunds {
723
753
  /// @notice Processes the Bitcoin moved funds sweep transaction input vector.
724
754
  /// It extracts the first input and tries to match it with one of
725
755
  /// the moved funds sweep requests targeting the sweeping wallet.
726
- /// If the sweep request is found and not yet processed, this
727
- /// function marks it as processed. If the sweeping wallet has a
756
+ /// If the sweep request is an existing Pending request, this
757
+ /// function marks it as Processed. If the sweeping wallet has a
728
758
  /// main UTXO, this function extracts the second input, makes sure
729
759
  /// it refers to the wallet main UTXO, and marks that main UTXO as
730
760
  /// correctly spent.
@@ -741,9 +771,8 @@ library MovingFunds {
741
771
  /// - The input vector must consist of one mandatory and one optional
742
772
  /// input.
743
773
  /// - The mandatory input must be the first input in the vector
744
- /// - The mandatory input must point to a known moved funds sweep
745
- /// request that is not processed yet and is targeted to the sweeping
746
- /// wallet
774
+ /// - The mandatory input must point to a Pending moved funds sweep
775
+ /// request that is targeted to the sweeping wallet
747
776
  /// - The optional output must be the second input in the vector
748
777
  /// - The optional input is required if the sweeping wallet has a
749
778
  /// main UTXO (i.e. the `mainUtxo` is not zeroed). In that case,
@@ -817,10 +846,10 @@ library MovingFunds {
817
846
  )
818
847
  ];
819
848
 
820
- // The sweep request must exist, must be not processed yet, and must
821
- // belong to the sweeping wallet.
822
- require(sweepRequest.createdAt != 0, "Sweep request does not exist");
823
- require(sweepRequest.sweptAt == 0, "Sweep request already processed");
849
+ require(
850
+ sweepRequest.state == MovedFundsSweepRequestState.Pending,
851
+ "Sweep request must be in Pending state"
852
+ );
824
853
  // We must check if the wallet extracted from the moved funds sweep
825
854
  // transaction output is truly the owner of the sweep request connected
826
855
  // with the swept UTXO. This is needed to prevent a case when a wallet
@@ -833,13 +862,12 @@ library MovingFunds {
833
862
  // If the validation passed, the sweep request must be marked as
834
863
  // processed and its value should be counted into the total inputs
835
864
  // value sum.
836
- /* solhint-disable-next-line not-rely-on-time */
837
- sweepRequest.sweptAt = uint32(block.timestamp);
865
+ sweepRequest.state = MovedFundsSweepRequestState.Processed;
838
866
  inputsTotalValue += sweepRequest.value;
839
867
 
840
- // TODO: Decrease the sweep request count for the sweeping wallet.
841
- // That will be handled in the PR that will block moving
842
- // funds commitments for wallets with pending sweep requests.
868
+ self
869
+ .registeredWallets[walletPubKeyHash]
870
+ .pendingMovedFundsSweepRequestsCount--;
843
871
 
844
872
  // If the main UTXO for the sweeping wallet exists, it must be processed.
845
873
  if (mainUtxo.txHash != bytes32(0)) {
@@ -914,4 +942,93 @@ library MovingFunds {
914
942
 
915
943
  return (outpointTxHash, outpointIndex, inputLength);
916
944
  }
945
+
946
+ /// @notice Notifies about a timed out moved funds sweep process. If the
947
+ /// wallet is not terminated yet, that function terminates
948
+ /// the wallet and slashes signing group members as a result.
949
+ /// Marks the given sweep request as TimedOut.
950
+ /// @param movingFundsTxHash 32-byte hash of the moving funds transaction
951
+ /// that caused the sweep request to be created
952
+ /// @param movingFundsTxOutputIndex Index of the moving funds transaction
953
+ /// output that is subject of the sweep request.
954
+ /// @param walletMembersIDs Identifiers of the wallet signing group members
955
+ /// @dev Requirements:
956
+ /// - The moved funds sweep request must be in the Pending state
957
+ /// - The moved funds sweep timeout must be actually exceeded
958
+ /// - The wallet must be either in the Live or MovingFunds or
959
+ /// Terminated state
960
+ /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
961
+ /// be exactly the same as the hash stored under `membersIdsHash`
962
+ /// for the given `walletID`. Those IDs are not directly stored
963
+ /// in the contract for gas efficiency purposes but they can be
964
+ /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`
965
+ /// events of the `WalletRegistry` contract
966
+ function notifyMovedFundsSweepTimeout(
967
+ BridgeState.Storage storage self,
968
+ bytes32 movingFundsTxHash,
969
+ uint32 movingFundsTxOutputIndex,
970
+ uint32[] calldata walletMembersIDs
971
+ ) external {
972
+ MovedFundsSweepRequest storage sweepRequest = self
973
+ .movedFundsSweepRequests[
974
+ uint256(
975
+ keccak256(
976
+ abi.encodePacked(
977
+ movingFundsTxHash,
978
+ movingFundsTxOutputIndex
979
+ )
980
+ )
981
+ )
982
+ ];
983
+
984
+ require(
985
+ sweepRequest.state == MovedFundsSweepRequestState.Pending,
986
+ "Sweep request must be in Pending state"
987
+ );
988
+
989
+ require(
990
+ /* solhint-disable-next-line not-rely-on-time */
991
+ block.timestamp >
992
+ sweepRequest.createdAt + self.movedFundsSweepTimeout,
993
+ "Sweep request has not timed out yet"
994
+ );
995
+
996
+ bytes20 walletPubKeyHash = sweepRequest.walletPubKeyHash;
997
+ Wallets.Wallet storage wallet = self.registeredWallets[
998
+ walletPubKeyHash
999
+ ];
1000
+ Wallets.WalletState walletState = wallet.state;
1001
+
1002
+ require(
1003
+ walletState == Wallets.WalletState.Live ||
1004
+ walletState == Wallets.WalletState.MovingFunds ||
1005
+ walletState == Wallets.WalletState.Terminated,
1006
+ "ECDSA wallet must be in Live or MovingFunds or Terminated state"
1007
+ );
1008
+
1009
+ sweepRequest.state = MovedFundsSweepRequestState.TimedOut;
1010
+ wallet.pendingMovedFundsSweepRequestsCount--;
1011
+
1012
+ if (
1013
+ walletState == Wallets.WalletState.Live ||
1014
+ walletState == Wallets.WalletState.MovingFunds
1015
+ ) {
1016
+ self.terminateWallet(walletPubKeyHash);
1017
+
1018
+ self.ecdsaWalletRegistry.seize(
1019
+ self.movedFundsSweepTimeoutSlashingAmount,
1020
+ self.movedFundsSweepTimeoutNotifierRewardMultiplier,
1021
+ msg.sender,
1022
+ wallet.ecdsaWalletID,
1023
+ walletMembersIDs
1024
+ );
1025
+ }
1026
+
1027
+ // slither-disable-next-line reentrancy-events
1028
+ emit MovedFundsSweepTimedOut(
1029
+ walletPubKeyHash,
1030
+ movingFundsTxHash,
1031
+ movingFundsTxOutputIndex
1032
+ );
1033
+ }
917
1034
  }
@@ -78,6 +78,8 @@ library Wallets {
78
78
  // UNIX timestamp indicating the moment the wallet's closing period
79
79
  // started.
80
80
  uint32 closingStartedAt;
81
+ // Total count of pending moved funds sweep requests targeting this wallet.
82
+ uint32 pendingMovedFundsSweepRequestsCount;
81
83
  // Current state of the wallet.
82
84
  WalletState state;
83
85
  // Moving funds target wallet commitment submitted by the wallet. It