@keep-network/tbtc-v2 0.1.1-dev.32 → 0.1.1-dev.35

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 (32) hide show
  1. package/artifacts/TBTC.json +3 -3
  2. package/artifacts/TBTCToken.json +3 -3
  3. package/artifacts/VendingMachine.json +10 -10
  4. package/artifacts/solcInputs/{590fbe282bf8b630b15f2da419b402f1.json → 922339b8aca537314dc3d35162317588.json} +14 -2
  5. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  6. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  7. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  8. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  9. package/build/contracts/bridge/Bridge.sol/Bridge.json +141 -150
  10. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  11. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +10 -0
  12. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  13. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  14. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  15. package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +1 -1
  16. package/build/contracts/bridge/Frauds.sol/Frauds.json +2 -2
  17. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
  18. package/build/contracts/bridge/{Bridge.sol → IRelay.sol}/IRelay.json +1 -1
  19. package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +4 -0
  20. package/build/contracts/bridge/Sweep.sol/Sweep.json +48 -0
  21. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  22. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  23. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  24. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  25. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  26. package/contracts/bridge/Bridge.sol +116 -686
  27. package/contracts/bridge/BridgeState.sol +104 -0
  28. package/contracts/bridge/Deposit.sol +247 -0
  29. package/contracts/bridge/IRelay.sol +28 -0
  30. package/contracts/bridge/Sweep.sol +510 -0
  31. package/package.json +1 -1
  32. package/build/contracts/bridge/Bridge.sol/IRelay.dbg.json +0 -4
@@ -19,25 +19,19 @@ import "@openzeppelin/contracts/access/Ownable.sol";
19
19
 
20
20
  import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
21
21
  import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
22
+
22
23
  import {IWalletOwner as EcdsaWalletOwner} from "@keep-network/ecdsa/contracts/api/IWalletOwner.sol";
23
24
 
24
- import "../bank/Bank.sol";
25
+ import "./IRelay.sol";
26
+ import "./BridgeState.sol";
27
+ import "./Deposit.sol";
28
+ import "./Sweep.sol";
25
29
  import "./BitcoinTx.sol";
26
30
  import "./EcdsaLib.sol";
27
31
  import "./Wallets.sol";
28
32
  import "./Frauds.sol";
29
33
 
30
- /// @title Interface for the Bitcoin relay
31
- /// @notice Contains only the methods needed by tBTC v2. The Bitcoin relay
32
- /// provides the difficulty of the previous and current epoch. One
33
- /// difficulty epoch spans 2016 blocks.
34
- interface IRelay {
35
- /// @notice Returns the difficulty of the current epoch.
36
- function getCurrentEpochDifficulty() external view returns (uint256);
37
-
38
- /// @notice Returns the difficulty of the previous epoch.
39
- function getPrevEpochDifficulty() external view returns (uint256);
40
- }
34
+ import "../bank/Bank.sol";
41
35
 
42
36
  /// @title Bitcoin Bridge
43
37
  /// @notice Bridge manages BTC deposit and redemption flow and is increasing and
@@ -64,84 +58,15 @@ interface IRelay {
64
58
  /// Examples of such operations are main UTXO or pending redemptions
65
59
  /// value updates.
66
60
  contract Bridge is Ownable, EcdsaWalletOwner {
67
- using BTCUtils for bytes;
68
- using BTCUtils for uint256;
69
- using BytesLib for bytes;
61
+ using BridgeState for BridgeState.Storage;
62
+ using Deposit for BridgeState.Storage;
63
+ using Sweep for BridgeState.Storage;
70
64
  using Frauds for Frauds.Data;
71
65
  using Wallets for Wallets.Data;
72
66
 
73
- /// @notice Represents data which must be revealed by the depositor during
74
- /// deposit reveal.
75
- struct RevealInfo {
76
- // Index of the funding output belonging to the funding transaction.
77
- uint32 fundingOutputIndex;
78
- // Ethereum depositor address.
79
- address depositor;
80
- // The blinding factor as 8 bytes. Byte endianness doesn't matter
81
- // as this factor is not interpreted as uint.
82
- bytes8 blindingFactor;
83
- // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
84
- // of the deposit's wallet hashed in the HASH160 Bitcoin opcode style.
85
- bytes20 walletPubKeyHash;
86
- // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
87
- // that can be used to make the deposit refund after the refund
88
- // locktime passes. Hashed in the HASH160 Bitcoin opcode style.
89
- bytes20 refundPubKeyHash;
90
- // The refund locktime (4-byte LE). Interpreted according to locktime
91
- // parsing rules described in:
92
- // https://developer.bitcoin.org/devguide/transactions.html#locktime-and-sequence-number
93
- // and used with OP_CHECKLOCKTIMEVERIFY opcode as described in:
94
- // https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
95
- bytes4 refundLocktime;
96
- // Address of the Bank vault to which the deposit is routed to.
97
- // Optional, can be 0x0. The vault must be trusted by the Bridge.
98
- address vault;
99
- }
100
-
101
- /// @notice Represents tBTC deposit data.
102
- struct DepositRequest {
103
- // Ethereum depositor address.
104
- address depositor;
105
- // Deposit amount in satoshi.
106
- uint64 amount;
107
- // UNIX timestamp the deposit was revealed at.
108
- uint32 revealedAt;
109
- // Address of the Bank vault the deposit is routed to.
110
- // Optional, can be 0x0.
111
- address vault;
112
- // Treasury TBTC fee in satoshi at the moment of deposit reveal.
113
- uint64 treasuryFee;
114
- // UNIX timestamp the deposit was swept at. Note this is not the
115
- // time when the deposit was swept on the Bitcoin chain but actually
116
- // the time when the sweep proof was delivered to the Ethereum chain.
117
- uint32 sweptAt;
118
- }
119
-
120
- /// @notice Represents an outcome of the sweep Bitcoin transaction
121
- /// inputs processing.
122
- struct SweepTxInputsInfo {
123
- // Sum of all inputs values i.e. all deposits and main UTXO value,
124
- // if present.
125
- uint256 inputsTotalValue;
126
- // Addresses of depositors who performed processed deposits. Ordered in
127
- // the same order as deposits inputs in the input vector. Size of this
128
- // array is either equal to the number of inputs (main UTXO doesn't
129
- // exist) or less by one (main UTXO exists and is pointed by one of
130
- // the inputs).
131
- address[] depositors;
132
- // Amounts of deposits corresponding to processed deposits. Ordered in
133
- // the same order as deposits inputs in the input vector. Size of this
134
- // array is either equal to the number of inputs (main UTXO doesn't
135
- // exist) or less by one (main UTXO exists and is pointed by one of
136
- // the inputs).
137
- uint256[] depositedAmounts;
138
- // Values of the treasury fee corresponding to processed deposits.
139
- // Ordered in the same order as deposits inputs in the input vector.
140
- // Size of this array is either equal to the number of inputs (main
141
- // UTXO doesn't exist) or less by one (main UTXO exists and is pointed
142
- // by one of the inputs).
143
- uint256[] treasuryFees;
144
- }
67
+ using BTCUtils for bytes;
68
+ using BTCUtils for uint256;
69
+ using BytesLib for bytes;
145
70
 
146
71
  /// @notice Represents a redemption request.
147
72
  struct RedemptionRequest {
@@ -176,48 +101,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
176
101
  uint64 changeValue;
177
102
  }
178
103
 
179
- /// @notice The number of confirmations on the Bitcoin chain required to
180
- /// successfully evaluate an SPV proof.
181
- uint256 public immutable txProofDifficultyFactor;
182
-
183
- /// TODO: Revisit whether it should be governable or not.
184
- /// @notice Address of the Bank this Bridge belongs to.
185
- Bank public immutable bank;
186
-
187
- /// TODO: Make it governable.
188
- /// @notice Handle to the Bitcoin relay.
189
- IRelay public immutable relay;
190
-
191
- /// TODO: Revisit whether it should be governable or not.
192
- /// @notice Address where the redemptions treasury fees will be sent to.
193
- /// Treasury takes part in the operators rewarding process.
194
- address public immutable treasury;
195
-
196
- /// TODO: Make it governable.
197
- /// @notice The minimal amount that can be requested for deposit.
198
- /// Value of this parameter must take into account the value of
199
- /// `depositTreasuryFeeDivisor` and `depositTxMaxFee`
200
- /// parameters in order to make requests that can incur the
201
- /// treasury and transaction fee and still satisfy the depositor.
202
- uint64 public depositDustThreshold;
203
-
204
- /// TODO: Make it governable.
205
- /// @notice Divisor used to compute the treasury fee taken from each
206
- /// deposit and transferred to the treasury upon sweep proof
207
- /// submission. That fee is computed as follows:
208
- /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
209
- /// For example, if the treasury fee needs to be 2% of each deposit,
210
- /// the `depositTreasuryFeeDivisor` should be set to `50`
211
- /// because `1/50 = 0.02 = 2%`.
212
- uint64 public depositTreasuryFeeDivisor;
213
-
214
- /// TODO: Make it governable.
215
- /// @notice Maximum amount of BTC transaction fee that can be incurred by
216
- /// each swept deposit being part of the given sweep
217
- /// transaction. If the maximum BTC transaction fee is exceeded,
218
- /// such transaction is considered a fraud.
219
- /// @dev This is a per-deposit input max fee for the sweep transaction.
220
- uint64 public depositTxMaxFee;
104
+ BridgeState.Storage internal self;
221
105
 
222
106
  /// TODO: Make it governable.
223
107
  /// @notice The minimal amount that can be requested for redemption.
@@ -262,31 +146,6 @@ contract Bridge is Ownable, EcdsaWalletOwner {
262
146
  /// for the entire transaction.
263
147
  uint64 public movingFundsTxMaxTotalFee;
264
148
 
265
- /// @notice Indicates if the vault with the given address is trusted or not.
266
- /// Depositors can route their revealed deposits only to trusted
267
- /// vaults and have trusted vaults notified about new deposits as
268
- /// soon as these deposits get swept. Vaults not trusted by the
269
- /// Bridge can still be used by Bank balance owners on their own
270
- /// responsibility - anyone can approve their Bank balance to any
271
- /// address.
272
- mapping(address => bool) public isVaultTrusted;
273
-
274
- /// @notice Collection of all revealed deposits indexed by
275
- /// keccak256(fundingTxHash | fundingOutputIndex).
276
- /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
277
- /// and fundingOutputIndex an uint32. This mapping may contain valid
278
- /// and invalid deposits and the wallet is responsible for
279
- /// validating them before attempting to execute a sweep.
280
- mapping(uint256 => DepositRequest) public deposits;
281
-
282
- /// @notice Collection of main UTXOs that are honestly spent indexed by
283
- /// keccak256(fundingTxHash | fundingOutputIndex). The fundingTxHash
284
- /// is bytes32 (ordered as in Bitcoin internally) and
285
- /// fundingOutputIndex an uint32. A main UTXO is considered honestly
286
- /// spent if it was used as an input of a transaction that have been
287
- /// proven in the Bridge.
288
- mapping(uint256 => bool) public spentMainUTXOs;
289
-
290
149
  /// @notice Collection of all pending redemption requests indexed by
291
150
  /// redemption key built as
292
151
  /// keccak256(walletPubKeyHash | redeemerOutputScript). The
@@ -433,20 +292,20 @@ contract Bridge is Ownable, EcdsaWalletOwner {
433
292
  uint256 _txProofDifficultyFactor
434
293
  ) {
435
294
  require(_bank != address(0), "Bank address cannot be zero");
436
- bank = Bank(_bank);
295
+ self.bank = Bank(_bank);
437
296
 
438
297
  require(_relay != address(0), "Relay address cannot be zero");
439
- relay = IRelay(_relay);
298
+ self.relay = IRelay(_relay);
440
299
 
441
300
  require(_treasury != address(0), "Treasury address cannot be zero");
442
- treasury = _treasury;
301
+ self.treasury = _treasury;
443
302
 
444
- txProofDifficultyFactor = _txProofDifficultyFactor;
303
+ self.txProofDifficultyFactor = _txProofDifficultyFactor;
445
304
 
446
305
  // TODO: Revisit initial values.
447
- depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
448
- depositTxMaxFee = 10000; // 10000 satoshi
449
- depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
306
+ self.depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
307
+ self.depositTxMaxFee = 10000; // 10000 satoshi
308
+ self.depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
450
309
  redemptionDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
451
310
  redemptionTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
452
311
  redemptionTxMaxFee = 10000; // 10000 satoshi
@@ -522,7 +381,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
522
381
  /// @param isTrusted flag indicating whether the vault is trusted or not
523
382
  /// @dev Can only be called by the Governance.
524
383
  function setVaultStatus(address vault, bool isTrusted) external onlyOwner {
525
- isVaultTrusted[vault] = isTrusted;
384
+ self.isVaultTrusted[vault] = isTrusted;
526
385
  emit VaultStatusUpdated(vault, isTrusted);
527
386
  }
528
387
 
@@ -625,22 +484,6 @@ contract Bridge is Ownable, EcdsaWalletOwner {
625
484
  return wallets.activeWalletPubKeyHash;
626
485
  }
627
486
 
628
- /// @notice Determines the current Bitcoin SPV proof difficulty context.
629
- /// @return proofDifficulty Bitcoin proof difficulty context.
630
- function proofDifficultyContext()
631
- internal
632
- view
633
- returns (BitcoinTx.ProofDifficulty memory proofDifficulty)
634
- {
635
- proofDifficulty.currentEpochDifficulty = relay
636
- .getCurrentEpochDifficulty();
637
- proofDifficulty.previousEpochDifficulty = relay
638
- .getPrevEpochDifficulty();
639
- proofDifficulty.difficultyFactor = txProofDifficultyFactor;
640
-
641
- return proofDifficulty;
642
- }
643
-
644
487
  /// @notice Used by the depositor to reveal information about their P2(W)SH
645
488
  /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain
646
489
  /// wallet listens for revealed deposit events and may decide to
@@ -677,124 +520,9 @@ contract Bridge is Ownable, EcdsaWalletOwner {
677
520
  /// deposit script unlocks to receive their BTC back.
678
521
  function revealDeposit(
679
522
  BitcoinTx.Info calldata fundingTx,
680
- RevealInfo calldata reveal
523
+ Deposit.RevealInfo calldata reveal
681
524
  ) external {
682
- require(
683
- wallets.registeredWallets[reveal.walletPubKeyHash].state ==
684
- Wallets.WalletState.Live,
685
- "Wallet is not in Live state"
686
- );
687
-
688
- require(
689
- reveal.vault == address(0) || isVaultTrusted[reveal.vault],
690
- "Vault is not trusted"
691
- );
692
-
693
- // TODO: Should we enforce a specific locktime at contract level?
694
-
695
- bytes memory expectedScript = abi.encodePacked(
696
- hex"14", // Byte length of depositor Ethereum address.
697
- reveal.depositor,
698
- hex"75", // OP_DROP
699
- hex"08", // Byte length of blinding factor value.
700
- reveal.blindingFactor,
701
- hex"75", // OP_DROP
702
- hex"76", // OP_DUP
703
- hex"a9", // OP_HASH160
704
- hex"14", // Byte length of a compressed Bitcoin public key hash.
705
- reveal.walletPubKeyHash,
706
- hex"87", // OP_EQUAL
707
- hex"63", // OP_IF
708
- hex"ac", // OP_CHECKSIG
709
- hex"67", // OP_ELSE
710
- hex"76", // OP_DUP
711
- hex"a9", // OP_HASH160
712
- hex"14", // Byte length of a compressed Bitcoin public key hash.
713
- reveal.refundPubKeyHash,
714
- hex"88", // OP_EQUALVERIFY
715
- hex"04", // Byte length of refund locktime value.
716
- reveal.refundLocktime,
717
- hex"b1", // OP_CHECKLOCKTIMEVERIFY
718
- hex"75", // OP_DROP
719
- hex"ac", // OP_CHECKSIG
720
- hex"68" // OP_ENDIF
721
- );
722
-
723
- bytes memory fundingOutput = fundingTx
724
- .outputVector
725
- .extractOutputAtIndex(reveal.fundingOutputIndex);
726
- bytes memory fundingOutputHash = fundingOutput.extractHash();
727
-
728
- if (fundingOutputHash.length == 20) {
729
- // A 20-byte output hash is used by P2SH. That hash is constructed
730
- // by applying OP_HASH160 on the locking script. A 20-byte output
731
- // hash is used as well by P2PKH and P2WPKH (OP_HASH160 on the
732
- // public key). However, since we compare the actual output hash
733
- // with an expected locking script hash, this check will succeed only
734
- // for P2SH transaction type with expected script hash value. For
735
- // P2PKH and P2WPKH, it will fail on the output hash comparison with
736
- // the expected locking script hash.
737
- require(
738
- fundingOutputHash.slice20(0) == expectedScript.hash160View(),
739
- "Wrong 20-byte script hash"
740
- );
741
- } else if (fundingOutputHash.length == 32) {
742
- // A 32-byte output hash is used by P2WSH. That hash is constructed
743
- // by applying OP_SHA256 on the locking script.
744
- require(
745
- fundingOutputHash.toBytes32() == sha256(expectedScript),
746
- "Wrong 32-byte script hash"
747
- );
748
- } else {
749
- revert("Wrong script hash length");
750
- }
751
-
752
- // Resulting TX hash is in native Bitcoin little-endian format.
753
- bytes32 fundingTxHash = abi
754
- .encodePacked(
755
- fundingTx.version,
756
- fundingTx.inputVector,
757
- fundingTx.outputVector,
758
- fundingTx.locktime
759
- )
760
- .hash256View();
761
-
762
- DepositRequest storage deposit = deposits[
763
- uint256(
764
- keccak256(
765
- abi.encodePacked(fundingTxHash, reveal.fundingOutputIndex)
766
- )
767
- )
768
- ];
769
- require(deposit.revealedAt == 0, "Deposit already revealed");
770
-
771
- uint64 fundingOutputAmount = fundingOutput.extractValue();
772
-
773
- require(
774
- fundingOutputAmount >= depositDustThreshold,
775
- "Deposit amount too small"
776
- );
777
-
778
- deposit.amount = fundingOutputAmount;
779
- deposit.depositor = reveal.depositor;
780
- /* solhint-disable-next-line not-rely-on-time */
781
- deposit.revealedAt = uint32(block.timestamp);
782
- deposit.vault = reveal.vault;
783
- deposit.treasuryFee = depositTreasuryFeeDivisor > 0
784
- ? fundingOutputAmount / depositTreasuryFeeDivisor
785
- : 0;
786
-
787
- emit DepositRevealed(
788
- fundingTxHash,
789
- reveal.fundingOutputIndex,
790
- reveal.depositor,
791
- fundingOutputAmount,
792
- reveal.blindingFactor,
793
- reveal.walletPubKeyHash,
794
- reveal.refundPubKeyHash,
795
- reveal.refundLocktime,
796
- reveal.vault
797
- );
525
+ self.revealDeposit(wallets, fundingTx, reveal);
798
526
  }
799
527
 
800
528
  /// @notice Used by the wallet to prove the BTC deposit sweep transaction
@@ -838,388 +566,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
838
566
  BitcoinTx.Proof calldata sweepProof,
839
567
  BitcoinTx.UTXO calldata mainUtxo
840
568
  ) external {
841
- // TODO: Fail early if the function call gets frontrunned. See discussion:
842
- // https://github.com/keep-network/tbtc-v2/pull/106#discussion_r801745204
843
-
844
- // The actual transaction proof is performed here. After that point, we
845
- // can assume the transaction happened on Bitcoin chain and has
846
- // a sufficient number of confirmations as determined by
847
- // `txProofDifficultyFactor` constant.
848
- bytes32 sweepTxHash = BitcoinTx.validateProof(
849
- sweepTx,
850
- sweepProof,
851
- proofDifficultyContext()
852
- );
853
-
854
- // Process sweep transaction output and extract its target wallet
855
- // public key hash and value.
856
- (
857
- bytes20 walletPubKeyHash,
858
- uint64 sweepTxOutputValue
859
- ) = processSweepTxOutput(sweepTx.outputVector);
860
-
861
- Wallets.Wallet storage wallet = wallets.registeredWallets[
862
- walletPubKeyHash
863
- ];
864
-
865
- Wallets.WalletState walletState = wallet.state;
866
- require(
867
- walletState == Wallets.WalletState.Live ||
868
- walletState == Wallets.WalletState.MovingFunds,
869
- "Wallet must be in Live or MovingFunds state"
870
- );
871
-
872
- // Check if the main UTXO for given wallet exists. If so, validate
873
- // passed main UTXO data against the stored hash and use them for
874
- // further processing. If no main UTXO exists, use empty data.
875
- BitcoinTx.UTXO memory resolvedMainUtxo = BitcoinTx.UTXO(
876
- bytes32(0),
877
- 0,
878
- 0
879
- );
880
- bytes32 mainUtxoHash = wallet.mainUtxoHash;
881
- if (mainUtxoHash != bytes32(0)) {
882
- require(
883
- keccak256(
884
- abi.encodePacked(
885
- mainUtxo.txHash,
886
- mainUtxo.txOutputIndex,
887
- mainUtxo.txOutputValue
888
- )
889
- ) == mainUtxoHash,
890
- "Invalid main UTXO data"
891
- );
892
- resolvedMainUtxo = mainUtxo;
893
- }
894
-
895
- // Process sweep transaction inputs and extract all information needed
896
- // to perform deposit bookkeeping.
897
- SweepTxInputsInfo memory inputsInfo = processSweepTxInputs(
898
- sweepTx.inputVector,
899
- resolvedMainUtxo
900
- );
901
-
902
- // Helper variable that will hold the sum of treasury fees paid by
903
- // all deposits.
904
- uint256 totalTreasuryFee = 0;
905
-
906
- // Determine the transaction fee that should be incurred by each deposit
907
- // and the indivisible remainder that should be additionally incurred
908
- // by the last deposit.
909
- (
910
- uint256 depositTxFee,
911
- uint256 depositTxFeeRemainder
912
- ) = sweepTxFeeDistribution(
913
- inputsInfo.inputsTotalValue,
914
- sweepTxOutputValue,
915
- inputsInfo.depositedAmounts.length
916
- );
917
-
918
- // Make sure the highest value of the deposit transaction fee does not
919
- // exceed the maximum value limited by the governable parameter.
920
- require(
921
- depositTxFee + depositTxFeeRemainder <= depositTxMaxFee,
922
- "Transaction fee is too high"
923
- );
924
-
925
- // Reduce each deposit amount by treasury fee and transaction fee.
926
- for (uint256 i = 0; i < inputsInfo.depositedAmounts.length; i++) {
927
- // The last deposit should incur the deposit transaction fee
928
- // remainder.
929
- uint256 depositTxFeeIncurred = i ==
930
- inputsInfo.depositedAmounts.length - 1
931
- ? depositTxFee + depositTxFeeRemainder
932
- : depositTxFee;
933
-
934
- // There is no need to check whether
935
- // `inputsInfo.depositedAmounts[i] - inputsInfo.treasuryFees[i] - txFee > 0`
936
- // since the `depositDustThreshold` should force that condition
937
- // to be always true.
938
- inputsInfo.depositedAmounts[i] =
939
- inputsInfo.depositedAmounts[i] -
940
- inputsInfo.treasuryFees[i] -
941
- depositTxFeeIncurred;
942
- totalTreasuryFee += inputsInfo.treasuryFees[i];
943
- }
944
-
945
- // Record this sweep data and assign them to the wallet public key hash
946
- // as new main UTXO. Transaction output index is always 0 as sweep
947
- // transaction always contains only one output.
948
- wallet.mainUtxoHash = keccak256(
949
- abi.encodePacked(sweepTxHash, uint32(0), sweepTxOutputValue)
950
- );
951
-
952
- emit DepositsSwept(walletPubKeyHash, sweepTxHash);
953
-
954
- // Update depositors balances in the Bank.
955
- bank.increaseBalances(
956
- inputsInfo.depositors,
957
- inputsInfo.depositedAmounts
958
- );
959
- // Pass the treasury fee to the treasury address.
960
- bank.increaseBalance(treasury, totalTreasuryFee);
961
-
962
- // TODO: Handle deposits having `vault` set.
963
- }
964
-
965
- /// @notice Processes the Bitcoin sweep transaction output vector by
966
- /// extracting the single output and using it to gain additional
967
- /// information required for further processing (e.g. value and
968
- /// wallet public key hash).
969
- /// @param sweepTxOutputVector Bitcoin sweep transaction output vector.
970
- /// This function assumes vector's structure is valid so it must be
971
- /// validated using e.g. `BTCUtils.validateVout` function before
972
- /// it is passed here
973
- /// @return walletPubKeyHash 20-byte wallet public key hash.
974
- /// @return value 8-byte sweep transaction output value.
975
- function processSweepTxOutput(bytes memory sweepTxOutputVector)
976
- internal
977
- pure
978
- returns (bytes20 walletPubKeyHash, uint64 value)
979
- {
980
- // To determine the total number of sweep transaction outputs, we need to
981
- // parse the compactSize uint (VarInt) the output vector is prepended by.
982
- // That compactSize uint encodes the number of vector elements using the
983
- // format presented in:
984
- // https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers
985
- // We don't need asserting the compactSize uint is parseable since it
986
- // was already checked during `validateVout` validation.
987
- // See `BitcoinTx.outputVector` docs for more details.
988
- (, uint256 outputsCount) = sweepTxOutputVector.parseVarInt();
989
- require(
990
- outputsCount == 1,
991
- "Sweep transaction must have a single output"
992
- );
993
-
994
- bytes memory output = sweepTxOutputVector.extractOutputAtIndex(0);
995
- value = output.extractValue();
996
- bytes memory walletPubKeyHashBytes = output.extractHash();
997
- // The sweep transaction output should always be P2PKH or P2WPKH.
998
- // In both cases, the wallet public key hash should be 20 bytes length.
999
- require(
1000
- walletPubKeyHashBytes.length == 20,
1001
- "Wallet public key hash should have 20 bytes"
1002
- );
1003
- /* solhint-disable-next-line no-inline-assembly */
1004
- assembly {
1005
- walletPubKeyHash := mload(add(walletPubKeyHashBytes, 32))
1006
- }
1007
-
1008
- return (walletPubKeyHash, value);
1009
- }
1010
-
1011
- /// @notice Processes the Bitcoin sweep transaction input vector. It
1012
- /// extracts each input and tries to obtain associated deposit or
1013
- /// main UTXO data, depending on the input type. Reverts
1014
- /// if one of the inputs cannot be recognized as a pointer to a
1015
- /// revealed deposit or expected main UTXO.
1016
- /// This function also marks each processed deposit as swept.
1017
- /// @param sweepTxInputVector Bitcoin sweep transaction input vector.
1018
- /// This function assumes vector's structure is valid so it must be
1019
- /// validated using e.g. `BTCUtils.validateVin` function before
1020
- /// it is passed here
1021
- /// @param mainUtxo Data of the wallet's main UTXO. If no main UTXO
1022
- /// exists for the given the wallet, this parameter's fields should
1023
- /// be zeroed to bypass the main UTXO validation
1024
- /// @return info Outcomes of the processing.
1025
- function processSweepTxInputs(
1026
- bytes memory sweepTxInputVector,
1027
- BitcoinTx.UTXO memory mainUtxo
1028
- ) internal returns (SweepTxInputsInfo memory info) {
1029
- // If the passed `mainUtxo` parameter's values are zeroed, the main UTXO
1030
- // for the given wallet doesn't exist and it is not expected to be
1031
- // included in the sweep transaction input vector.
1032
- bool mainUtxoExpected = mainUtxo.txHash != bytes32(0);
1033
- bool mainUtxoFound = false;
1034
-
1035
- // Determining the total number of sweep transaction inputs in the same
1036
- // way as for number of outputs. See `BitcoinTx.inputVector` docs for
1037
- // more details.
1038
- (
1039
- uint256 inputsCompactSizeUintLength,
1040
- uint256 inputsCount
1041
- ) = sweepTxInputVector.parseVarInt();
1042
-
1043
- // To determine the first input starting index, we must jump over
1044
- // the compactSize uint which prepends the input vector. One byte
1045
- // must be added because `BtcUtils.parseVarInt` does not include
1046
- // compactSize uint tag in the returned length.
1047
- //
1048
- // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`
1049
- // returns `0`, so we jump over one byte of compactSize uint.
1050
- //
1051
- // For >= 253 && <= 0xffff there is `0xfd` tag,
1052
- // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no
1053
- // tag byte included) so we need to jump over 1+2 bytes of
1054
- // compactSize uint.
1055
- //
1056
- // Please refer `BTCUtils` library and compactSize uint
1057
- // docs in `BitcoinTx` library for more details.
1058
- uint256 inputStartingIndex = 1 + inputsCompactSizeUintLength;
1059
-
1060
- // Determine the swept deposits count. If main UTXO is NOT expected,
1061
- // all inputs should be deposits. If main UTXO is expected, one input
1062
- // should point to that main UTXO.
1063
- info.depositors = new address[](
1064
- !mainUtxoExpected ? inputsCount : inputsCount - 1
1065
- );
1066
- info.depositedAmounts = new uint256[](info.depositors.length);
1067
- info.treasuryFees = new uint256[](info.depositors.length);
1068
-
1069
- // Initialize helper variables.
1070
- uint256 processedDepositsCount = 0;
1071
-
1072
- // Inputs processing loop.
1073
- for (uint256 i = 0; i < inputsCount; i++) {
1074
- (
1075
- bytes32 outpointTxHash,
1076
- uint32 outpointIndex,
1077
- uint256 inputLength
1078
- ) = parseTxInputAt(sweepTxInputVector, inputStartingIndex);
1079
-
1080
- DepositRequest storage deposit = deposits[
1081
- uint256(
1082
- keccak256(abi.encodePacked(outpointTxHash, outpointIndex))
1083
- )
1084
- ];
1085
-
1086
- if (deposit.revealedAt != 0) {
1087
- // If we entered here, that means the input was identified as
1088
- // a revealed deposit.
1089
- require(deposit.sweptAt == 0, "Deposit already swept");
1090
-
1091
- if (processedDepositsCount == info.depositors.length) {
1092
- // If this condition is true, that means a deposit input
1093
- // took place of an expected main UTXO input.
1094
- // In other words, there is no expected main UTXO
1095
- // input and all inputs come from valid, revealed deposits.
1096
- revert(
1097
- "Expected main UTXO not present in sweep transaction inputs"
1098
- );
1099
- }
1100
-
1101
- /* solhint-disable-next-line not-rely-on-time */
1102
- deposit.sweptAt = uint32(block.timestamp);
1103
-
1104
- info.depositors[processedDepositsCount] = deposit.depositor;
1105
- info.depositedAmounts[processedDepositsCount] = deposit.amount;
1106
- info.inputsTotalValue += info.depositedAmounts[
1107
- processedDepositsCount
1108
- ];
1109
- info.treasuryFees[processedDepositsCount] = deposit.treasuryFee;
1110
-
1111
- processedDepositsCount++;
1112
- } else if (
1113
- mainUtxoExpected != mainUtxoFound &&
1114
- mainUtxo.txHash == outpointTxHash
1115
- ) {
1116
- // If we entered here, that means the input was identified as
1117
- // the expected main UTXO.
1118
- info.inputsTotalValue += mainUtxo.txOutputValue;
1119
- mainUtxoFound = true;
1120
-
1121
- // Main UTXO used as an input, mark it as spent.
1122
- spentMainUTXOs[
1123
- uint256(
1124
- keccak256(
1125
- abi.encodePacked(outpointTxHash, outpointIndex)
1126
- )
1127
- )
1128
- ] = true;
1129
- } else {
1130
- revert("Unknown input type");
1131
- }
1132
-
1133
- // Make the `inputStartingIndex` pointing to the next input by
1134
- // increasing it by current input's length.
1135
- inputStartingIndex += inputLength;
1136
- }
1137
-
1138
- // Construction of the input processing loop guarantees that:
1139
- // `processedDepositsCount == info.depositors.length == info.depositedAmounts.length`
1140
- // is always true at this point. We just use the first variable
1141
- // to assert the total count of swept deposit is bigger than zero.
1142
- require(
1143
- processedDepositsCount > 0,
1144
- "Sweep transaction must process at least one deposit"
1145
- );
1146
-
1147
- // Assert the main UTXO was used as one of current sweep's inputs if
1148
- // it was actually expected.
1149
- require(
1150
- mainUtxoExpected == mainUtxoFound,
1151
- "Expected main UTXO not present in sweep transaction inputs"
1152
- );
1153
-
1154
- return info;
1155
- }
1156
-
1157
- /// @notice Parses a Bitcoin transaction input starting at the given index.
1158
- /// @param inputVector Bitcoin transaction input vector
1159
- /// @param inputStartingIndex Index the given input starts at
1160
- /// @return outpointTxHash 32-byte hash of the Bitcoin transaction which is
1161
- /// pointed in the given input's outpoint.
1162
- /// @return outpointIndex 4-byte index of the Bitcoin transaction output
1163
- /// which is pointed in the given input's outpoint.
1164
- /// @return inputLength Byte length of the given input.
1165
- /// @dev This function assumes vector's structure is valid so it must be
1166
- /// validated using e.g. `BTCUtils.validateVin` function before it
1167
- /// is passed here.
1168
- function parseTxInputAt(
1169
- bytes memory inputVector,
1170
- uint256 inputStartingIndex
1171
- )
1172
- internal
1173
- pure
1174
- returns (
1175
- bytes32 outpointTxHash,
1176
- uint32 outpointIndex,
1177
- uint256 inputLength
1178
- )
1179
- {
1180
- outpointTxHash = inputVector.extractInputTxIdLeAt(inputStartingIndex);
1181
-
1182
- outpointIndex = BTCUtils.reverseUint32(
1183
- uint32(inputVector.extractTxIndexLeAt(inputStartingIndex))
1184
- );
1185
-
1186
- inputLength = inputVector.determineInputLengthAt(inputStartingIndex);
1187
-
1188
- return (outpointTxHash, outpointIndex, inputLength);
1189
- }
1190
-
1191
- /// @notice Determines the distribution of the sweep transaction fee
1192
- /// over swept deposits.
1193
- /// @param sweepTxInputsTotalValue Total value of all sweep transaction inputs.
1194
- /// @param sweepTxOutputValue Value of the sweep transaction output.
1195
- /// @param depositsCount Count of the deposits swept by the sweep transaction.
1196
- /// @return depositTxFee Transaction fee per deposit determined by evenly
1197
- /// spreading the divisible part of the sweep transaction fee
1198
- /// over all deposits.
1199
- /// @return depositTxFeeRemainder The indivisible part of the sweep
1200
- /// transaction fee than cannot be distributed over all deposits.
1201
- /// @dev It is up to the caller to decide how the remainder should be
1202
- /// counted in. This function only computes its value.
1203
- function sweepTxFeeDistribution(
1204
- uint256 sweepTxInputsTotalValue,
1205
- uint256 sweepTxOutputValue,
1206
- uint256 depositsCount
1207
- )
1208
- internal
1209
- pure
1210
- returns (uint256 depositTxFee, uint256 depositTxFeeRemainder)
1211
- {
1212
- // The sweep transaction fee is just the difference between inputs
1213
- // amounts sum and the output amount.
1214
- uint256 sweepTxFee = sweepTxInputsTotalValue - sweepTxOutputValue;
1215
- // Compute the indivisible remainder that remains after dividing the
1216
- // sweep transaction fee over all deposits evenly.
1217
- depositTxFeeRemainder = sweepTxFee % depositsCount;
1218
- // Compute the transaction fee per deposit by dividing the sweep
1219
- // transaction fee (reduced by the remainder) by the number of deposits.
1220
- depositTxFee = (sweepTxFee - depositTxFeeRemainder) / depositsCount;
1221
-
1222
- return (depositTxFee, depositTxFeeRemainder);
569
+ self.submitSweepProof(wallets, sweepTx, sweepProof, mainUtxo);
1223
570
  }
1224
571
 
1225
572
  /// @notice Submits a fraud challenge indicating that a UTXO being under
@@ -1324,11 +671,11 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1324
671
 
1325
672
  // Check that the UTXO key identifies a correctly spent UTXO.
1326
673
  require(
1327
- deposits[utxoKey].sweptAt > 0 || spentMainUTXOs[utxoKey],
674
+ self.deposits[utxoKey].sweptAt > 0 || self.spentMainUTXOs[utxoKey],
1328
675
  "Spent UTXO not found among correctly spent UTXOs"
1329
676
  );
1330
677
 
1331
- frauds.defeatChallenge(walletPublicKey, preimage, treasury);
678
+ frauds.defeatChallenge(walletPublicKey, preimage, self.treasury);
1332
679
  }
1333
680
 
1334
681
  /// @notice Notifies about defeat timeout for the given fraud challenge.
@@ -1550,7 +897,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1550
897
  txMaxFee
1551
898
  );
1552
899
 
1553
- bank.transferBalanceFrom(msg.sender, address(this), amount);
900
+ self.bank.transferBalanceFrom(msg.sender, address(this), amount);
1554
901
  }
1555
902
 
1556
903
  /// @notice Used by the wallet to prove the BTC redemption transaction
@@ -1614,7 +961,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1614
961
  bytes32 redemptionTxHash = BitcoinTx.validateProof(
1615
962
  redemptionTx,
1616
963
  redemptionProof,
1617
- proofDifficultyContext()
964
+ self.proofDifficultyContext()
1618
965
  );
1619
966
 
1620
967
  // Process the redemption transaction input. Specifically, check if it
@@ -1664,8 +1011,8 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1664
1011
 
1665
1012
  emit RedemptionsCompleted(walletPubKeyHash, redemptionTxHash);
1666
1013
 
1667
- bank.decreaseBalance(outputsInfo.totalBurnableValue);
1668
- bank.transferBalance(treasury, outputsInfo.totalTreasuryFee);
1014
+ self.bank.decreaseBalance(outputsInfo.totalBurnableValue);
1015
+ self.bank.transferBalance(self.treasury, outputsInfo.totalTreasuryFee);
1669
1016
  }
1670
1017
 
1671
1018
  /// @notice Checks whether an outbound Bitcoin transaction performed from
@@ -1721,7 +1068,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1721
1068
  );
1722
1069
 
1723
1070
  // Main UTXO used as an input, mark it as spent.
1724
- spentMainUTXOs[
1071
+ self.spentMainUTXOs[
1725
1072
  uint256(
1726
1073
  keccak256(
1727
1074
  abi.encodePacked(mainUtxo.txHash, mainUtxo.txOutputIndex)
@@ -2046,7 +1393,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
2046
1393
  emit RedemptionTimedOut(walletPubKeyHash, redeemerOutputScript);
2047
1394
 
2048
1395
  // Return the requested amount of tokens to the redeemer
2049
- bank.transferBalance(request.redeemer, request.requestedAmount);
1396
+ self.bank.transferBalance(request.redeemer, request.requestedAmount);
2050
1397
  }
2051
1398
 
2052
1399
  /// @notice Used by the wallet to prove the BTC moving funds transaction
@@ -2107,7 +1454,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
2107
1454
  bytes32 movingFundsTxHash = BitcoinTx.validateProof(
2108
1455
  movingFundsTx,
2109
1456
  movingFundsProof,
2110
- proofDifficultyContext()
1457
+ self.proofDifficultyContext()
2111
1458
  );
2112
1459
 
2113
1460
  // Process the moving funds transaction input. Specifically, check if
@@ -2288,4 +1635,87 @@ contract Bridge is Ownable, EcdsaWalletOwner {
2288
1635
 
2289
1636
  return (targetWalletsHash, outputsTotalValue);
2290
1637
  }
1638
+
1639
+ /// @return bank Address of the Bank the Bridge belongs to.
1640
+ /// @return relay Address of the Bitcoin relay providing the current Bitcoin
1641
+ /// network difficulty.
1642
+ function getContracts() external view returns (Bank bank, IRelay relay) {
1643
+ bank = self.bank;
1644
+ relay = self.relay;
1645
+ }
1646
+
1647
+ /// @notice Returns the current values of Bridge deposit parameters.
1648
+ /// @return depositDustThreshold The minimal amount that can be requested
1649
+ /// to deposit. Value of this parameter must take into account the
1650
+ /// value of `depositTreasuryFeeDivisor` and `depositTxMaxFee`
1651
+ /// parameters in order to make requests that can incur the
1652
+ /// treasury and transaction fee and still satisfy the depositor.
1653
+ /// @return depositTreasuryFeeDivisor Divisor used to compute the treasury
1654
+ /// fee taken from each deposit and transferred to the treasury upon
1655
+ /// sweep proof submission. That fee is computed as follows:
1656
+ /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
1657
+ /// For example, if the treasury fee needs to be 2% of each deposit,
1658
+ /// the `depositTreasuryFeeDivisor` should be set to `50`
1659
+ /// because `1/50 = 0.02 = 2%`.
1660
+ /// @return depositTxMaxFee Maximum amount of BTC transaction fee that can
1661
+ /// be incurred by each swept deposit being part of the given sweep
1662
+ /// transaction. If the maximum BTC transaction fee is exceeded,
1663
+ /// such transaction is considered a fraud.
1664
+ /// @return treasury Address where the deposit treasury fees will be
1665
+ /// sent to. Treasury takes part in the operators rewarding process.
1666
+ /// @return txProofDifficultyFactor The number of confirmations on the
1667
+ /// Bitcoin chain required to successfully evaluate an SPV proof.
1668
+ function depositParameters()
1669
+ external
1670
+ view
1671
+ returns (
1672
+ uint64 depositDustThreshold,
1673
+ uint64 depositTreasuryFeeDivisor,
1674
+ uint64 depositTxMaxFee,
1675
+ address treasury,
1676
+ uint256 txProofDifficultyFactor
1677
+ )
1678
+ {
1679
+ depositDustThreshold = self.depositDustThreshold;
1680
+ depositTreasuryFeeDivisor = self.depositTreasuryFeeDivisor;
1681
+ depositTxMaxFee = self.depositTxMaxFee;
1682
+ treasury = self.treasury;
1683
+ txProofDifficultyFactor = self.txProofDifficultyFactor;
1684
+ }
1685
+
1686
+ /// @notice Indicates if the vault with the given address is trusted or not.
1687
+ /// Depositors can route their revealed deposits only to trusted
1688
+ /// vaults and have trusted vaults notified about new deposits as
1689
+ /// soon as these deposits get swept. Vaults not trusted by the
1690
+ /// Bridge can still be used by Bank balance owners on their own
1691
+ /// responsibility - anyone can approve their Bank balance to any
1692
+ /// address.
1693
+ function isVaultTrusted(address vault) external view returns (bool) {
1694
+ return self.isVaultTrusted[vault];
1695
+ }
1696
+
1697
+ /// @notice Collection of all revealed deposits indexed by
1698
+ /// keccak256(fundingTxHash | fundingOutputIndex).
1699
+ /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
1700
+ /// and fundingOutputIndex an uint32. This mapping may contain valid
1701
+ /// and invalid deposits and the wallet is responsible for
1702
+ /// validating them before attempting to execute a sweep.
1703
+ function deposits(uint256 depositKey)
1704
+ external
1705
+ view
1706
+ returns (Deposit.Request memory)
1707
+ {
1708
+ // TODO: rename to getDeposit?
1709
+ return self.deposits[depositKey];
1710
+ }
1711
+
1712
+ /// @notice Collection of main UTXOs that are honestly spent indexed by
1713
+ /// keccak256(fundingTxHash | fundingOutputIndex). The fundingTxHash
1714
+ /// is bytes32 (ordered as in Bitcoin internally) and
1715
+ /// fundingOutputIndex an uint32. A main UTXO is considered honestly
1716
+ /// spent if it was used as an input of a transaction that have been
1717
+ /// proven in the Bridge.
1718
+ function spentMainUTXOs(uint256 utxoKey) external view returns (bool) {
1719
+ return self.spentMainUTXOs[utxoKey];
1720
+ }
2291
1721
  }