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

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 (26) 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 → 7e4974a220c8c12bc2c31c8235131416.json} +8 -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 +100 -85
  10. package/build/contracts/bridge/Bridge.sol/IRelay.dbg.json +1 -1
  11. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  12. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +10 -0
  13. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  14. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  15. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  16. package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +1 -1
  17. package/build/contracts/bridge/Frauds.sol/Frauds.json +2 -2
  18. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  19. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  20. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  21. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  22. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  23. package/contracts/bridge/Bridge.sol +62 -205
  24. package/contracts/bridge/BridgeState.sol +54 -0
  25. package/contracts/bridge/Deposit.sol +247 -0
  26. package/package.json +1 -1
@@ -22,6 +22,7 @@ import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
22
22
  import {IWalletOwner as EcdsaWalletOwner} from "@keep-network/ecdsa/contracts/api/IWalletOwner.sol";
23
23
 
24
24
  import "../bank/Bank.sol";
25
+ import "./BridgeState.sol";
25
26
  import "./BitcoinTx.sol";
26
27
  import "./EcdsaLib.sol";
27
28
  import "./Wallets.sol";
@@ -64,58 +65,14 @@ interface IRelay {
64
65
  /// Examples of such operations are main UTXO or pending redemptions
65
66
  /// value updates.
66
67
  contract Bridge is Ownable, EcdsaWalletOwner {
67
- using BTCUtils for bytes;
68
- using BTCUtils for uint256;
69
- using BytesLib for bytes;
68
+ using BridgeState for BridgeState.Storage;
69
+ using Deposit for BridgeState.Storage;
70
70
  using Frauds for Frauds.Data;
71
71
  using Wallets for Wallets.Data;
72
72
 
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
- }
73
+ using BTCUtils for bytes;
74
+ using BTCUtils for uint256;
75
+ using BytesLib for bytes;
119
76
 
120
77
  /// @notice Represents an outcome of the sweep Bitcoin transaction
121
78
  /// inputs processing.
@@ -193,23 +150,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
193
150
  /// Treasury takes part in the operators rewarding process.
194
151
  address public immutable treasury;
195
152
 
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;
153
+ BridgeState.Storage internal self;
213
154
 
214
155
  /// TODO: Make it governable.
215
156
  /// @notice Maximum amount of BTC transaction fee that can be incurred by
@@ -262,23 +203,6 @@ contract Bridge is Ownable, EcdsaWalletOwner {
262
203
  /// for the entire transaction.
263
204
  uint64 public movingFundsTxMaxTotalFee;
264
205
 
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
206
  /// @notice Collection of main UTXOs that are honestly spent indexed by
283
207
  /// keccak256(fundingTxHash | fundingOutputIndex). The fundingTxHash
284
208
  /// is bytes32 (ordered as in Bitcoin internally) and
@@ -444,9 +368,9 @@ contract Bridge is Ownable, EcdsaWalletOwner {
444
368
  txProofDifficultyFactor = _txProofDifficultyFactor;
445
369
 
446
370
  // TODO: Revisit initial values.
447
- depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
371
+ self.depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
448
372
  depositTxMaxFee = 10000; // 10000 satoshi
449
- depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
373
+ self.depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
450
374
  redemptionDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
451
375
  redemptionTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
452
376
  redemptionTxMaxFee = 10000; // 10000 satoshi
@@ -522,7 +446,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
522
446
  /// @param isTrusted flag indicating whether the vault is trusted or not
523
447
  /// @dev Can only be called by the Governance.
524
448
  function setVaultStatus(address vault, bool isTrusted) external onlyOwner {
525
- isVaultTrusted[vault] = isTrusted;
449
+ self.isVaultTrusted[vault] = isTrusted;
526
450
  emit VaultStatusUpdated(vault, isTrusted);
527
451
  }
528
452
 
@@ -677,124 +601,9 @@ contract Bridge is Ownable, EcdsaWalletOwner {
677
601
  /// deposit script unlocks to receive their BTC back.
678
602
  function revealDeposit(
679
603
  BitcoinTx.Info calldata fundingTx,
680
- RevealInfo calldata reveal
604
+ Deposit.RevealInfo calldata reveal
681
605
  ) 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
- );
606
+ self.revealDeposit(wallets, fundingTx, reveal);
798
607
  }
799
608
 
800
609
  /// @notice Used by the wallet to prove the BTC deposit sweep transaction
@@ -1077,7 +886,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1077
886
  uint256 inputLength
1078
887
  ) = parseTxInputAt(sweepTxInputVector, inputStartingIndex);
1079
888
 
1080
- DepositRequest storage deposit = deposits[
889
+ Deposit.Request storage deposit = self.deposits[
1081
890
  uint256(
1082
891
  keccak256(abi.encodePacked(outpointTxHash, outpointIndex))
1083
892
  )
@@ -1324,7 +1133,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
1324
1133
 
1325
1134
  // Check that the UTXO key identifies a correctly spent UTXO.
1326
1135
  require(
1327
- deposits[utxoKey].sweptAt > 0 || spentMainUTXOs[utxoKey],
1136
+ self.deposits[utxoKey].sweptAt > 0 || spentMainUTXOs[utxoKey],
1328
1137
  "Spent UTXO not found among correctly spent UTXOs"
1329
1138
  );
1330
1139
 
@@ -2288,4 +2097,52 @@ contract Bridge is Ownable, EcdsaWalletOwner {
2288
2097
 
2289
2098
  return (targetWalletsHash, outputsTotalValue);
2290
2099
  }
2100
+
2101
+ /// @notice Returns the current values of Bridge deposit parameters.
2102
+ /// @return depositDustThreshold The minimal amount that can be requested
2103
+ /// to deposit. Value of this parameter must take into account the
2104
+ /// value of `depositTreasuryFeeDivisor` and `depositTxMaxFee`
2105
+ /// parameters in order to make requests that can incur the
2106
+ /// treasury and transaction fee and still satisfy the depositor.
2107
+ /// @return depositTreasuryFeeDivisor Divisor used to compute the treasury
2108
+ /// fee taken from each deposit and transferred to the treasury upon
2109
+ /// sweep proof submission. That fee is computed as follows:
2110
+ /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
2111
+ /// For example, if the treasury fee needs to be 2% of each deposit,
2112
+ /// the `depositTreasuryFeeDivisor` should be set to `50`
2113
+ /// because `1/50 = 0.02 = 2%`.
2114
+ function depositParameters()
2115
+ external
2116
+ view
2117
+ returns (uint64 depositDustThreshold, uint64 depositTreasuryFeeDivisor)
2118
+ {
2119
+ depositDustThreshold = self.depositDustThreshold;
2120
+ depositTreasuryFeeDivisor = self.depositTreasuryFeeDivisor;
2121
+ }
2122
+
2123
+ /// @notice Indicates if the vault with the given address is trusted or not.
2124
+ /// Depositors can route their revealed deposits only to trusted
2125
+ /// vaults and have trusted vaults notified about new deposits as
2126
+ /// soon as these deposits get swept. Vaults not trusted by the
2127
+ /// Bridge can still be used by Bank balance owners on their own
2128
+ /// responsibility - anyone can approve their Bank balance to any
2129
+ /// address.
2130
+ function isVaultTrusted(address vault) external view returns (bool) {
2131
+ return self.isVaultTrusted[vault];
2132
+ }
2133
+
2134
+ /// @notice Collection of all revealed deposits indexed by
2135
+ /// keccak256(fundingTxHash | fundingOutputIndex).
2136
+ /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
2137
+ /// and fundingOutputIndex an uint32. This mapping may contain valid
2138
+ /// and invalid deposits and the wallet is responsible for
2139
+ /// validating them before attempting to execute a sweep.
2140
+ function deposits(uint256 depositKey)
2141
+ external
2142
+ view
2143
+ returns (Deposit.Request memory)
2144
+ {
2145
+ // TODO: rename to getDeposit?
2146
+ return self.deposits[depositKey];
2147
+ }
2291
2148
  }
@@ -0,0 +1,54 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ // ██████████████ ▐████▌ ██████████████
4
+ // ██████████████ ▐████▌ ██████████████
5
+ // ▐████▌ ▐████▌
6
+ // ▐████▌ ▐████▌
7
+ // ██████████████ ▐████▌ ██████████████
8
+ // ██████████████ ▐████▌ ██████████████
9
+ // ▐████▌ ▐████▌
10
+ // ▐████▌ ▐████▌
11
+ // ▐████▌ ▐████▌
12
+ // ▐████▌ ▐████▌
13
+ // ▐████▌ ▐████▌
14
+ // ▐████▌ ▐████▌
15
+
16
+ pragma solidity ^0.8.9;
17
+
18
+ import "./Deposit.sol";
19
+
20
+ library BridgeState {
21
+ struct Storage {
22
+ /// TODO: Make it governable.
23
+ /// @notice The minimal amount that can be requested to deposit.
24
+ /// Value of this parameter must take into account the value of
25
+ /// `depositTreasuryFeeDivisor` and `depositTxMaxFee`
26
+ /// parameters in order to make requests that can incur the
27
+ /// treasury and transaction fee and still satisfy the depositor.
28
+ uint64 depositDustThreshold;
29
+ /// TODO: Make it governable.
30
+ /// @notice Divisor used to compute the treasury fee taken from each
31
+ /// deposit and transferred to the treasury upon sweep proof
32
+ /// submission. That fee is computed as follows:
33
+ /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
34
+ /// For example, if the treasury fee needs to be 2% of each deposit,
35
+ /// the `depositTreasuryFeeDivisor` should be set to `50`
36
+ /// because `1/50 = 0.02 = 2%`.
37
+ uint64 depositTreasuryFeeDivisor;
38
+ /// @notice Collection of all revealed deposits indexed by
39
+ /// keccak256(fundingTxHash | fundingOutputIndex).
40
+ /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
41
+ /// and fundingOutputIndex an uint32. This mapping may contain valid
42
+ /// and invalid deposits and the wallet is responsible for
43
+ /// validating them before attempting to execute a sweep.
44
+ mapping(uint256 => Deposit.Request) deposits;
45
+ /// @notice Indicates if the vault with the given address is trusted or not.
46
+ /// Depositors can route their revealed deposits only to trusted
47
+ /// vaults and have trusted vaults notified about new deposits as
48
+ /// soon as these deposits get swept. Vaults not trusted by the
49
+ /// Bridge can still be used by Bank balance owners on their own
50
+ /// responsibility - anyone can approve their Bank balance to any
51
+ /// address.
52
+ mapping(address => bool) isVaultTrusted;
53
+ }
54
+ }
@@ -0,0 +1,247 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ // ██████████████ ▐████▌ ██████████████
4
+ // ██████████████ ▐████▌ ██████████████
5
+ // ▐████▌ ▐████▌
6
+ // ▐████▌ ▐████▌
7
+ // ██████████████ ▐████▌ ██████████████
8
+ // ██████████████ ▐████▌ ██████████████
9
+ // ▐████▌ ▐████▌
10
+ // ▐████▌ ▐████▌
11
+ // ▐████▌ ▐████▌
12
+ // ▐████▌ ▐████▌
13
+ // ▐████▌ ▐████▌
14
+ // ▐████▌ ▐████▌
15
+
16
+ pragma solidity ^0.8.9;
17
+
18
+ import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
19
+ import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
20
+
21
+ import "./BitcoinTx.sol";
22
+ import "./BridgeState.sol";
23
+ import "./Wallets.sol";
24
+
25
+ library Deposit {
26
+ using Wallets for Wallets.Data;
27
+
28
+ using BTCUtils for bytes;
29
+ using BytesLib for bytes;
30
+
31
+ /// @notice Represents data which must be revealed by the depositor during
32
+ /// deposit reveal.
33
+ struct RevealInfo {
34
+ // Index of the funding output belonging to the funding transaction.
35
+ uint32 fundingOutputIndex;
36
+ // Ethereum depositor address.
37
+ address depositor;
38
+ // The blinding factor as 8 bytes. Byte endianness doesn't matter
39
+ // as this factor is not interpreted as uint.
40
+ bytes8 blindingFactor;
41
+ // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
42
+ // of the deposit's wallet hashed in the HASH160 Bitcoin opcode style.
43
+ bytes20 walletPubKeyHash;
44
+ // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
45
+ // that can be used to make the deposit refund after the refund
46
+ // locktime passes. Hashed in the HASH160 Bitcoin opcode style.
47
+ bytes20 refundPubKeyHash;
48
+ // The refund locktime (4-byte LE). Interpreted according to locktime
49
+ // parsing rules described in:
50
+ // https://developer.bitcoin.org/devguide/transactions.html#locktime-and-sequence-number
51
+ // and used with OP_CHECKLOCKTIMEVERIFY opcode as described in:
52
+ // https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
53
+ bytes4 refundLocktime;
54
+ // Address of the Bank vault to which the deposit is routed to.
55
+ // Optional, can be 0x0. The vault must be trusted by the Bridge.
56
+ address vault;
57
+ }
58
+
59
+ /// @notice Represents tBTC deposit request data.
60
+ struct Request {
61
+ // Ethereum depositor address.
62
+ address depositor;
63
+ // Deposit amount in satoshi.
64
+ uint64 amount;
65
+ // UNIX timestamp the deposit was revealed at.
66
+ uint32 revealedAt;
67
+ // Address of the Bank vault the deposit is routed to.
68
+ // Optional, can be 0x0.
69
+ address vault;
70
+ // Treasury TBTC fee in satoshi at the moment of deposit reveal.
71
+ uint64 treasuryFee;
72
+ // UNIX timestamp the deposit was swept at. Note this is not the
73
+ // time when the deposit was swept on the Bitcoin chain but actually
74
+ // the time when the sweep proof was delivered to the Ethereum chain.
75
+ uint32 sweptAt;
76
+ }
77
+
78
+ event DepositRevealed(
79
+ bytes32 fundingTxHash,
80
+ uint32 fundingOutputIndex,
81
+ address depositor,
82
+ uint64 amount,
83
+ bytes8 blindingFactor,
84
+ bytes20 walletPubKeyHash,
85
+ bytes20 refundPubKeyHash,
86
+ bytes4 refundLocktime,
87
+ address vault
88
+ );
89
+
90
+ /// @notice Used by the depositor to reveal information about their P2(W)SH
91
+ /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain
92
+ /// wallet listens for revealed deposit events and may decide to
93
+ /// include the revealed deposit in the next executed sweep.
94
+ /// Information about the Bitcoin deposit can be revealed before or
95
+ /// after the Bitcoin transaction with P2(W)SH deposit is mined on
96
+ /// the Bitcoin chain. Worth noting, the gas cost of this function
97
+ /// scales with the number of P2(W)SH transaction inputs and
98
+ /// outputs. The deposit may be routed to one of the trusted vaults.
99
+ /// When a deposit is routed to a vault, vault gets notified when
100
+ /// the deposit gets swept and it may execute the appropriate action.
101
+ /// @param fundingTx Bitcoin funding transaction data, see `BitcoinTx.Info`
102
+ /// @param reveal Deposit reveal data, see `RevealInfo struct
103
+ /// @dev Requirements:
104
+ /// - `reveal.walletPubKeyHash` must identify a `Live` wallet
105
+ /// - `reveal.vault` must be 0x0 or point to a trusted vault
106
+ /// - `reveal.fundingOutputIndex` must point to the actual P2(W)SH
107
+ /// output of the BTC deposit transaction
108
+ /// - `reveal.depositor` must be the Ethereum address used in the
109
+ /// P2(W)SH BTC deposit transaction,
110
+ /// - `reveal.blindingFactor` must be the blinding factor used in the
111
+ /// P2(W)SH BTC deposit transaction,
112
+ /// - `reveal.walletPubKeyHash` must be the wallet pub key hash used in
113
+ /// the P2(W)SH BTC deposit transaction,
114
+ /// - `reveal.refundPubKeyHash` must be the refund pub key hash used in
115
+ /// the P2(W)SH BTC deposit transaction,
116
+ /// - `reveal.refundLocktime` must be the refund locktime used in the
117
+ /// P2(W)SH BTC deposit transaction,
118
+ /// - BTC deposit for the given `fundingTxHash`, `fundingOutputIndex`
119
+ /// can be revealed only one time.
120
+ ///
121
+ /// If any of these requirements is not met, the wallet _must_ refuse
122
+ /// to sweep the deposit and the depositor has to wait until the
123
+ /// deposit script unlocks to receive their BTC back.
124
+ function revealDeposit(
125
+ BridgeState.Storage storage self,
126
+ Wallets.Data storage wallets,
127
+ BitcoinTx.Info calldata fundingTx,
128
+ RevealInfo calldata reveal
129
+ ) external {
130
+ require(
131
+ wallets.registeredWallets[reveal.walletPubKeyHash].state ==
132
+ Wallets.WalletState.Live,
133
+ "Wallet is not in Live state"
134
+ );
135
+
136
+ require(
137
+ reveal.vault == address(0) || self.isVaultTrusted[reveal.vault],
138
+ "Vault is not trusted"
139
+ );
140
+
141
+ // TODO: Should we enforce a specific locktime at contract level?
142
+
143
+ bytes memory expectedScript = abi.encodePacked(
144
+ hex"14", // Byte length of depositor Ethereum address.
145
+ reveal.depositor,
146
+ hex"75", // OP_DROP
147
+ hex"08", // Byte length of blinding factor value.
148
+ reveal.blindingFactor,
149
+ hex"75", // OP_DROP
150
+ hex"76", // OP_DUP
151
+ hex"a9", // OP_HASH160
152
+ hex"14", // Byte length of a compressed Bitcoin public key hash.
153
+ reveal.walletPubKeyHash,
154
+ hex"87", // OP_EQUAL
155
+ hex"63", // OP_IF
156
+ hex"ac", // OP_CHECKSIG
157
+ hex"67", // OP_ELSE
158
+ hex"76", // OP_DUP
159
+ hex"a9", // OP_HASH160
160
+ hex"14", // Byte length of a compressed Bitcoin public key hash.
161
+ reveal.refundPubKeyHash,
162
+ hex"88", // OP_EQUALVERIFY
163
+ hex"04", // Byte length of refund locktime value.
164
+ reveal.refundLocktime,
165
+ hex"b1", // OP_CHECKLOCKTIMEVERIFY
166
+ hex"75", // OP_DROP
167
+ hex"ac", // OP_CHECKSIG
168
+ hex"68" // OP_ENDIF
169
+ );
170
+
171
+ bytes memory fundingOutput = fundingTx
172
+ .outputVector
173
+ .extractOutputAtIndex(reveal.fundingOutputIndex);
174
+ bytes memory fundingOutputHash = fundingOutput.extractHash();
175
+
176
+ if (fundingOutputHash.length == 20) {
177
+ // A 20-byte output hash is used by P2SH. That hash is constructed
178
+ // by applying OP_HASH160 on the locking script. A 20-byte output
179
+ // hash is used as well by P2PKH and P2WPKH (OP_HASH160 on the
180
+ // public key). However, since we compare the actual output hash
181
+ // with an expected locking script hash, this check will succeed only
182
+ // for P2SH transaction type with expected script hash value. For
183
+ // P2PKH and P2WPKH, it will fail on the output hash comparison with
184
+ // the expected locking script hash.
185
+ require(
186
+ fundingOutputHash.slice20(0) == expectedScript.hash160View(),
187
+ "Wrong 20-byte script hash"
188
+ );
189
+ } else if (fundingOutputHash.length == 32) {
190
+ // A 32-byte output hash is used by P2WSH. That hash is constructed
191
+ // by applying OP_SHA256 on the locking script.
192
+ require(
193
+ fundingOutputHash.toBytes32() == sha256(expectedScript),
194
+ "Wrong 32-byte script hash"
195
+ );
196
+ } else {
197
+ revert("Wrong script hash length");
198
+ }
199
+
200
+ // Resulting TX hash is in native Bitcoin little-endian format.
201
+ bytes32 fundingTxHash = abi
202
+ .encodePacked(
203
+ fundingTx.version,
204
+ fundingTx.inputVector,
205
+ fundingTx.outputVector,
206
+ fundingTx.locktime
207
+ )
208
+ .hash256View();
209
+
210
+ Request storage deposit = self.deposits[
211
+ uint256(
212
+ keccak256(
213
+ abi.encodePacked(fundingTxHash, reveal.fundingOutputIndex)
214
+ )
215
+ )
216
+ ];
217
+ require(deposit.revealedAt == 0, "Deposit already revealed");
218
+
219
+ uint64 fundingOutputAmount = fundingOutput.extractValue();
220
+
221
+ require(
222
+ fundingOutputAmount >= self.depositDustThreshold,
223
+ "Deposit amount too small"
224
+ );
225
+
226
+ deposit.amount = fundingOutputAmount;
227
+ deposit.depositor = reveal.depositor;
228
+ /* solhint-disable-next-line not-rely-on-time */
229
+ deposit.revealedAt = uint32(block.timestamp);
230
+ deposit.vault = reveal.vault;
231
+ deposit.treasuryFee = self.depositTreasuryFeeDivisor > 0
232
+ ? fundingOutputAmount / self.depositTreasuryFeeDivisor
233
+ : 0;
234
+
235
+ emit DepositRevealed(
236
+ fundingTxHash,
237
+ reveal.fundingOutputIndex,
238
+ reveal.depositor,
239
+ fundingOutputAmount,
240
+ reveal.blindingFactor,
241
+ reveal.walletPubKeyHash,
242
+ reveal.refundPubKeyHash,
243
+ reveal.refundLocktime,
244
+ reveal.vault
245
+ );
246
+ }
247
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keep-network/tbtc-v2",
3
- "version": "0.1.1-dev.32+main.87f7a68d24fa6c31f061522279c60e7fd0860c48",
3
+ "version": "0.1.1-dev.33+main.d11bd402d3ccb9e33f4f43209c7fd03def8ccba1",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "artifacts/",