@keep-network/tbtc-v2 0.1.1-dev.4 → 0.1.1-dev.42

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 (60) hide show
  1. package/README.adoc +12 -0
  2. package/artifacts/TBTC.json +19 -18
  3. package/artifacts/TBTCToken.json +19 -18
  4. package/artifacts/VendingMachine.json +20 -19
  5. package/artifacts/solcInputs/002940e9cc8128f6629e90620c66cba5.json +215 -0
  6. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  7. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.json +2 -2
  8. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  9. package/build/contracts/bank/Bank.sol/Bank.json +20 -2
  10. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
  11. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
  12. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  13. package/build/contracts/bridge/Bridge.sol/Bridge.json +1664 -63
  14. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  15. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +42 -0
  16. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  17. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  18. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
  19. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
  20. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
  21. package/build/contracts/bridge/Fraud.sol/Fraud.json +138 -0
  22. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
  23. package/build/contracts/bridge/IRelay.sol/IRelay.json +37 -0
  24. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +4 -0
  25. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +30 -0
  26. package/build/contracts/bridge/Redeem.sol/OutboundTx.dbg.json +4 -0
  27. package/build/contracts/bridge/Redeem.sol/OutboundTx.json +10 -0
  28. package/build/contracts/bridge/Redeem.sol/Redeem.dbg.json +4 -0
  29. package/build/contracts/bridge/Redeem.sol/Redeem.json +92 -0
  30. package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +4 -0
  31. package/build/contracts/bridge/Sweep.sol/Sweep.json +30 -0
  32. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  33. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
  34. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
  35. package/build/contracts/bridge/Wallets.sol/Wallets.json +93 -0
  36. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  37. package/build/contracts/token/TBTC.sol/TBTC.json +2 -2
  38. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  39. package/build/contracts/vault/IVault.sol/IVault.json +19 -1
  40. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  41. package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +36 -18
  42. package/contracts/GovernanceUtils.sol +1 -1
  43. package/contracts/bank/Bank.sol +34 -18
  44. package/contracts/bridge/BitcoinTx.sol +241 -0
  45. package/contracts/bridge/Bridge.sol +942 -123
  46. package/contracts/bridge/BridgeState.sol +251 -0
  47. package/contracts/bridge/Deposit.sol +244 -0
  48. package/contracts/bridge/EcdsaLib.sol +30 -0
  49. package/contracts/bridge/Fraud.sol +455 -0
  50. package/contracts/bridge/IRelay.sol +28 -0
  51. package/contracts/bridge/MovingFunds.sol +278 -0
  52. package/contracts/bridge/Redeem.sol +844 -0
  53. package/contracts/bridge/Sweep.sol +509 -0
  54. package/contracts/bridge/VendingMachine.sol +1 -1
  55. package/contracts/bridge/Wallets.sol +510 -0
  56. package/contracts/token/TBTC.sol +1 -1
  57. package/contracts/vault/IVault.sol +32 -10
  58. package/contracts/vault/TBTCVault.sol +20 -2
  59. package/package.json +28 -24
  60. package/artifacts/solcInputs/d71966212a658480bad5748ad85b1396.json +0 -116
@@ -0,0 +1,251 @@
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 "./IRelay.sol";
19
+ import "./Deposit.sol";
20
+ import "./Redeem.sol";
21
+ import "./Fraud.sol";
22
+ import "./Wallets.sol";
23
+
24
+ import "../bank/Bank.sol";
25
+
26
+ library BridgeState {
27
+ // TODO: Make parameters governable
28
+ struct Storage {
29
+ // Address of the Bank the Bridge belongs to.
30
+ Bank bank;
31
+ // Bitcoin relay providing the current Bitcoin network difficulty.
32
+ IRelay relay;
33
+ // ECDSA Wallet Registry contract handle.
34
+ EcdsaWalletRegistry ecdsaWalletRegistry;
35
+ // The number of confirmations on the Bitcoin chain required to
36
+ // successfully evaluate an SPV proof.
37
+ uint256 txProofDifficultyFactor;
38
+ // Address where the deposit and redemption treasury fees will be sent
39
+ // to. Treasury takes part in the operators rewarding process.
40
+ address treasury;
41
+ // The minimal amount that can be requested to deposit.
42
+ // Value of this parameter must take into account the value of
43
+ // `depositTreasuryFeeDivisor` and `depositTxMaxFee` parameters in order
44
+ // to make requests that can incur the treasury and transaction fee and
45
+ // still satisfy the depositor.
46
+ uint64 depositDustThreshold;
47
+ // Divisor used to compute the treasury fee taken from each deposit and
48
+ // transferred to the treasury upon sweep proof submission. That fee is
49
+ // computed as follows:
50
+ // `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
51
+ // For example, if the treasury fee needs to be 2% of each deposit,
52
+ // the `depositTreasuryFeeDivisor` should be set to `50` because
53
+ // `1/50 = 0.02 = 2%`.
54
+ uint64 depositTreasuryFeeDivisor;
55
+ // Maximum amount of BTC transaction fee that can be incurred by each
56
+ // swept deposit being part of the given sweep transaction. If the
57
+ // maximum BTC transaction fee is exceeded, such transaction is
58
+ // considered a fraud.
59
+ //
60
+ // This is a per-deposit input max fee for the sweep transaction.
61
+ uint64 depositTxMaxFee;
62
+ // Collection of all revealed deposits indexed by
63
+ // `keccak256(fundingTxHash | fundingOutputIndex)`.
64
+ // The `fundingTxHash` is `bytes32` (ordered as in Bitcoin internally)
65
+ // and `fundingOutputIndex` an `uint32`. This mapping may contain valid
66
+ // and invalid deposits and the wallet is responsible for validating
67
+ // them before attempting to execute a sweep.
68
+ mapping(uint256 => Deposit.DepositRequest) deposits;
69
+ // Indicates if the vault with the given address is trusted or not.
70
+ // Depositors can route their revealed deposits only to trusted vaults
71
+ // and have trusted vaults notified about new deposits as soon as these
72
+ // deposits get swept. Vaults not trusted by the Bridge can still be
73
+ // used by Bank balance owners on their own responsibility - anyone can
74
+ // approve their Bank balance to any address.
75
+ mapping(address => bool) isVaultTrusted;
76
+ // Maximum amount of the total BTC transaction fee that is acceptable in
77
+ // a single moving funds transaction.
78
+ //
79
+ // This is a TOTAL max fee for the moving funds transaction. Note
80
+ // that `depositTxMaxFee` is per single deposit and `redemptionTxMaxFee`
81
+ // if per single redemption. `movingFundsTxMaxTotalFee` is a total
82
+ // fee for the entire transaction.
83
+ uint64 movingFundsTxMaxTotalFee;
84
+ // The minimal amount that can be requested for redemption.
85
+ // Value of this parameter must take into account the value of
86
+ // `redemptionTreasuryFeeDivisor` and `redemptionTxMaxFee`
87
+ // parameters in order to make requests that can incur the
88
+ // treasury and transaction fee and still satisfy the redeemer.
89
+ uint64 redemptionDustThreshold;
90
+ // Divisor used to compute the treasury fee taken from each
91
+ // redemption request and transferred to the treasury upon
92
+ // successful request finalization. That fee is computed as follows:
93
+ // `treasuryFee = requestedAmount / redemptionTreasuryFeeDivisor`
94
+ // For example, if the treasury fee needs to be 2% of each
95
+ // redemption request, the `redemptionTreasuryFeeDivisor` should
96
+ // be set to `50` because `1/50 = 0.02 = 2%`.
97
+ uint64 redemptionTreasuryFeeDivisor;
98
+ // Maximum amount of BTC transaction fee that can be incurred by
99
+ // each redemption request being part of the given redemption
100
+ // transaction. If the maximum BTC transaction fee is exceeded, such
101
+ // transaction is considered a fraud.
102
+ //
103
+ // This is a per-redemption output max fee for the redemption
104
+ // transaction.
105
+ uint64 redemptionTxMaxFee;
106
+ // Time after which the redemption request can be reported as
107
+ // timed out. It is counted from the moment when the redemption
108
+ // request was created via `requestRedemption` call. Reported
109
+ // timed out requests are cancelled and locked TBTC is returned
110
+ // to the redeemer in full amount.
111
+ uint256 redemptionTimeout;
112
+ // Collection of all pending redemption requests indexed by
113
+ // redemption key built as
114
+ // `keccak256(walletPubKeyHash | redeemerOutputScript)`.
115
+ // The `walletPubKeyHash` is the 20-byte wallet's public key hash
116
+ // (computed using Bitcoin HASH160 over the compressed ECDSA
117
+ // public key) and `redeemerOutputScript` is a Bitcoin script
118
+ // (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock
119
+ // redeemed BTC as requested by the redeemer. Requests are added
120
+ // to this mapping by the `requestRedemption` method (duplicates
121
+ // not allowed) and are removed by one of the following methods:
122
+ // - `submitRedemptionProof` in case the request was handled
123
+ // successfully
124
+ // - `notifyRedemptionTimeout` in case the request was reported
125
+ // to be timed out
126
+ mapping(uint256 => Redeem.RedemptionRequest) pendingRedemptions;
127
+ // Collection of all timed out redemptions requests indexed by
128
+ // redemption key built as
129
+ // `keccak256(walletPubKeyHash | redeemerOutputScript)`. The
130
+ // `walletPubKeyHash` is the 20-byte wallet's public key hash
131
+ // (computed using Bitcoin HASH160 over the compressed ECDSA
132
+ // public key) and `redeemerOutputScript` is the Bitcoin script
133
+ // (P2PKH, P2WPKH, P2SH or P2WSH) that is involved in the timed
134
+ // out request. Timed out requests are stored in this mapping to
135
+ // avoid slashing the wallets multiple times for the same timeout.
136
+ // Only one method can add to this mapping:
137
+ // - `notifyRedemptionTimeout` which puts the redemption key to this
138
+ // mapping basing on a timed out request stored previously in
139
+ // `pendingRedemptions` mapping.
140
+ mapping(uint256 => Redeem.RedemptionRequest) timedOutRedemptions;
141
+ // The amount of stake slashed from each member of a wallet for a fraud.
142
+ uint256 fraudSlashingAmount;
143
+ // The percentage of the notifier reward from the staking contract
144
+ // the notifier of a fraud receives. The value is in the range [0, 100].
145
+ uint256 fraudNotifierRewardMultiplier;
146
+ // The amount of time the wallet has to defeat a fraud challenge.
147
+ uint256 fraudChallengeDefeatTimeout;
148
+ // The amount of ETH in wei the party challenging the wallet for fraud
149
+ // needs to deposit.
150
+ uint256 fraudChallengeDepositAmount;
151
+ // Collection of all submitted fraud challenges indexed by challenge
152
+ // key built as `keccak256(walletPublicKey|sighash)`.
153
+ mapping(uint256 => Fraud.FraudChallenge) fraudChallenges;
154
+ // Collection of main UTXOs that are honestly spent indexed by
155
+ // `keccak256(fundingTxHash | fundingOutputIndex)`. The `fundingTxHash`
156
+ // is `bytes32` (ordered as in Bitcoin internally) and
157
+ // `fundingOutputIndex` an `uint32`. A main UTXO is considered honestly
158
+ // spent if it was used as an input of a transaction that have been
159
+ // proven in the Bridge.
160
+ mapping(uint256 => bool) spentMainUTXOs;
161
+ // Determines how frequently a new wallet creation can be requested.
162
+ // Value in seconds.
163
+ uint32 walletCreationPeriod;
164
+ // The minimum BTC threshold in satoshi that is used to decide about
165
+ // wallet creation or closing.
166
+ uint64 walletMinBtcBalance;
167
+ // The maximum BTC threshold in satoshi that is used to decide about
168
+ // wallet creation.
169
+ uint64 walletMaxBtcBalance;
170
+ // The maximum age of a wallet in seconds, after which the wallet
171
+ // moving funds process can be requested.
172
+ uint32 walletMaxAge;
173
+ // 20-byte wallet public key hash being reference to the currently
174
+ // active wallet. Can be unset to the zero value under certain
175
+ // circumstances.
176
+ bytes20 activeWalletPubKeyHash;
177
+ // Maps the 20-byte wallet public key hash (computed using Bitcoin
178
+ // HASH160 over the compressed ECDSA public key) to the basic wallet
179
+ // information like state and pending redemptions value.
180
+ mapping(bytes20 => Wallets.Wallet) registeredWallets;
181
+ }
182
+
183
+ event WalletParametersUpdated(
184
+ uint32 walletCreationPeriod,
185
+ uint64 walletMinBtcBalance,
186
+ uint64 walletMaxBtcBalance,
187
+ uint32 walletMaxAge
188
+ );
189
+
190
+ /// @notice Updates parameters of wallets.
191
+ /// @param _walletCreationPeriod New value of the wallet creation period in
192
+ /// seconds, determines how frequently a new wallet creation can be
193
+ /// requested
194
+ /// @param _walletMinBtcBalance New value of the wallet minimum BTC balance
195
+ /// in sathoshis, used to decide about wallet creation or closing
196
+ /// @param _walletMaxBtcBalance New value of the wallet maximum BTC balance
197
+ /// in sathoshis, used to decide about wallet creation
198
+ /// @param _walletMaxAge New value of the wallet maximum age in seconds,
199
+ /// indicates the maximum age of a wallet in seconds, after which
200
+ /// the wallet moving funds process can be requested
201
+ /// @dev Requirements:
202
+ /// - Wallet minimum BTC balance must be greater than zero
203
+ /// - Wallet maximum BTC balance must be greater than the wallet
204
+ /// minimum BTC balance
205
+ function updateWalletParameters(
206
+ Storage storage self,
207
+ uint32 _walletCreationPeriod,
208
+ uint64 _walletMinBtcBalance,
209
+ uint64 _walletMaxBtcBalance,
210
+ uint32 _walletMaxAge
211
+ ) internal {
212
+ require(
213
+ _walletMinBtcBalance > 0,
214
+ "Wallet minimum BTC balance must be greater than zero"
215
+ );
216
+ require(
217
+ _walletMaxBtcBalance > _walletMinBtcBalance,
218
+ "Wallet maximum BTC balance must be greater than the minimum"
219
+ );
220
+
221
+ self.walletCreationPeriod = _walletCreationPeriod;
222
+ self.walletMinBtcBalance = _walletMinBtcBalance;
223
+ self.walletMaxBtcBalance = _walletMaxBtcBalance;
224
+ self.walletMaxAge = _walletMaxAge;
225
+
226
+ emit WalletParametersUpdated(
227
+ _walletCreationPeriod,
228
+ _walletMinBtcBalance,
229
+ _walletMaxBtcBalance,
230
+ _walletMaxAge
231
+ );
232
+ }
233
+
234
+ // TODO: Is it the right place for this function? Should we move it to Bridge?
235
+ /// @notice Determines the current Bitcoin SPV proof difficulty context.
236
+ /// @return proofDifficulty Bitcoin proof difficulty context.
237
+ function proofDifficultyContext(Storage storage self)
238
+ internal
239
+ view
240
+ returns (BitcoinTx.ProofDifficulty memory proofDifficulty)
241
+ {
242
+ IRelay relay = self.relay;
243
+ proofDifficulty.currentEpochDifficulty = relay
244
+ .getCurrentEpochDifficulty();
245
+ proofDifficulty.previousEpochDifficulty = relay
246
+ .getPrevEpochDifficulty();
247
+ proofDifficulty.difficultyFactor = self.txProofDifficultyFactor;
248
+
249
+ return proofDifficulty;
250
+ }
251
+ }
@@ -0,0 +1,244 @@
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 BTCUtils for bytes;
27
+ using BytesLib for bytes;
28
+
29
+ /// @notice Represents data which must be revealed by the depositor during
30
+ /// deposit reveal.
31
+ struct DepositRevealInfo {
32
+ // Index of the funding output belonging to the funding transaction.
33
+ uint32 fundingOutputIndex;
34
+ // Ethereum depositor address.
35
+ address depositor;
36
+ // The blinding factor as 8 bytes. Byte endianness doesn't matter
37
+ // as this factor is not interpreted as uint.
38
+ bytes8 blindingFactor;
39
+ // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
40
+ // of the deposit's wallet hashed in the HASH160 Bitcoin opcode style.
41
+ bytes20 walletPubKeyHash;
42
+ // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
43
+ // that can be used to make the deposit refund after the refund
44
+ // locktime passes. Hashed in the HASH160 Bitcoin opcode style.
45
+ bytes20 refundPubKeyHash;
46
+ // The refund locktime (4-byte LE). Interpreted according to locktime
47
+ // parsing rules described in:
48
+ // https://developer.bitcoin.org/devguide/transactions.html#locktime-and-sequence-number
49
+ // and used with OP_CHECKLOCKTIMEVERIFY opcode as described in:
50
+ // https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
51
+ bytes4 refundLocktime;
52
+ // Address of the Bank vault to which the deposit is routed to.
53
+ // Optional, can be 0x0. The vault must be trusted by the Bridge.
54
+ address vault;
55
+ }
56
+
57
+ /// @notice Represents tBTC deposit request data.
58
+ struct DepositRequest {
59
+ // Ethereum depositor address.
60
+ address depositor;
61
+ // Deposit amount in satoshi.
62
+ uint64 amount;
63
+ // UNIX timestamp the deposit was revealed at.
64
+ uint32 revealedAt;
65
+ // Address of the Bank vault the deposit is routed to.
66
+ // Optional, can be 0x0.
67
+ address vault;
68
+ // Treasury TBTC fee in satoshi at the moment of deposit reveal.
69
+ uint64 treasuryFee;
70
+ // UNIX timestamp the deposit was swept at. Note this is not the
71
+ // time when the deposit was swept on the Bitcoin chain but actually
72
+ // the time when the sweep proof was delivered to the Ethereum chain.
73
+ uint32 sweptAt;
74
+ }
75
+
76
+ event DepositRevealed(
77
+ bytes32 fundingTxHash,
78
+ uint32 fundingOutputIndex,
79
+ address depositor,
80
+ uint64 amount,
81
+ bytes8 blindingFactor,
82
+ bytes20 walletPubKeyHash,
83
+ bytes20 refundPubKeyHash,
84
+ bytes4 refundLocktime,
85
+ address vault
86
+ );
87
+
88
+ /// @notice Used by the depositor to reveal information about their P2(W)SH
89
+ /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain
90
+ /// wallet listens for revealed deposit events and may decide to
91
+ /// include the revealed deposit in the next executed sweep.
92
+ /// Information about the Bitcoin deposit can be revealed before or
93
+ /// after the Bitcoin transaction with P2(W)SH deposit is mined on
94
+ /// the Bitcoin chain. Worth noting, the gas cost of this function
95
+ /// scales with the number of P2(W)SH transaction inputs and
96
+ /// outputs. The deposit may be routed to one of the trusted vaults.
97
+ /// When a deposit is routed to a vault, vault gets notified when
98
+ /// the deposit gets swept and it may execute the appropriate action.
99
+ /// @param fundingTx Bitcoin funding transaction data, see `BitcoinTx.Info`
100
+ /// @param reveal Deposit reveal data, see `RevealInfo struct
101
+ /// @dev Requirements:
102
+ /// - `reveal.walletPubKeyHash` must identify a `Live` wallet
103
+ /// - `reveal.vault` must be 0x0 or point to a trusted vault
104
+ /// - `reveal.fundingOutputIndex` must point to the actual P2(W)SH
105
+ /// output of the BTC deposit transaction
106
+ /// - `reveal.depositor` must be the Ethereum address used in the
107
+ /// P2(W)SH BTC deposit transaction,
108
+ /// - `reveal.blindingFactor` must be the blinding factor used in the
109
+ /// P2(W)SH BTC deposit transaction,
110
+ /// - `reveal.walletPubKeyHash` must be the wallet pub key hash used in
111
+ /// the P2(W)SH BTC deposit transaction,
112
+ /// - `reveal.refundPubKeyHash` must be the refund pub key hash used in
113
+ /// the P2(W)SH BTC deposit transaction,
114
+ /// - `reveal.refundLocktime` must be the refund locktime used in the
115
+ /// P2(W)SH BTC deposit transaction,
116
+ /// - BTC deposit for the given `fundingTxHash`, `fundingOutputIndex`
117
+ /// can be revealed only one time.
118
+ ///
119
+ /// If any of these requirements is not met, the wallet _must_ refuse
120
+ /// to sweep the deposit and the depositor has to wait until the
121
+ /// deposit script unlocks to receive their BTC back.
122
+ function revealDeposit(
123
+ BridgeState.Storage storage self,
124
+ BitcoinTx.Info calldata fundingTx,
125
+ DepositRevealInfo calldata reveal
126
+ ) external {
127
+ require(
128
+ self.registeredWallets[reveal.walletPubKeyHash].state ==
129
+ Wallets.WalletState.Live,
130
+ "Wallet is not in Live state"
131
+ );
132
+
133
+ require(
134
+ reveal.vault == address(0) || self.isVaultTrusted[reveal.vault],
135
+ "Vault is not trusted"
136
+ );
137
+
138
+ // TODO: Should we enforce a specific locktime at contract level?
139
+
140
+ bytes memory expectedScript = abi.encodePacked(
141
+ hex"14", // Byte length of depositor Ethereum address.
142
+ reveal.depositor,
143
+ hex"75", // OP_DROP
144
+ hex"08", // Byte length of blinding factor value.
145
+ reveal.blindingFactor,
146
+ hex"75", // OP_DROP
147
+ hex"76", // OP_DUP
148
+ hex"a9", // OP_HASH160
149
+ hex"14", // Byte length of a compressed Bitcoin public key hash.
150
+ reveal.walletPubKeyHash,
151
+ hex"87", // OP_EQUAL
152
+ hex"63", // OP_IF
153
+ hex"ac", // OP_CHECKSIG
154
+ hex"67", // OP_ELSE
155
+ hex"76", // OP_DUP
156
+ hex"a9", // OP_HASH160
157
+ hex"14", // Byte length of a compressed Bitcoin public key hash.
158
+ reveal.refundPubKeyHash,
159
+ hex"88", // OP_EQUALVERIFY
160
+ hex"04", // Byte length of refund locktime value.
161
+ reveal.refundLocktime,
162
+ hex"b1", // OP_CHECKLOCKTIMEVERIFY
163
+ hex"75", // OP_DROP
164
+ hex"ac", // OP_CHECKSIG
165
+ hex"68" // OP_ENDIF
166
+ );
167
+
168
+ bytes memory fundingOutput = fundingTx
169
+ .outputVector
170
+ .extractOutputAtIndex(reveal.fundingOutputIndex);
171
+ bytes memory fundingOutputHash = fundingOutput.extractHash();
172
+
173
+ if (fundingOutputHash.length == 20) {
174
+ // A 20-byte output hash is used by P2SH. That hash is constructed
175
+ // by applying OP_HASH160 on the locking script. A 20-byte output
176
+ // hash is used as well by P2PKH and P2WPKH (OP_HASH160 on the
177
+ // public key). However, since we compare the actual output hash
178
+ // with an expected locking script hash, this check will succeed only
179
+ // for P2SH transaction type with expected script hash value. For
180
+ // P2PKH and P2WPKH, it will fail on the output hash comparison with
181
+ // the expected locking script hash.
182
+ require(
183
+ fundingOutputHash.slice20(0) == expectedScript.hash160View(),
184
+ "Wrong 20-byte script hash"
185
+ );
186
+ } else if (fundingOutputHash.length == 32) {
187
+ // A 32-byte output hash is used by P2WSH. That hash is constructed
188
+ // by applying OP_SHA256 on the locking script.
189
+ require(
190
+ fundingOutputHash.toBytes32() == sha256(expectedScript),
191
+ "Wrong 32-byte script hash"
192
+ );
193
+ } else {
194
+ revert("Wrong script hash length");
195
+ }
196
+
197
+ // Resulting TX hash is in native Bitcoin little-endian format.
198
+ bytes32 fundingTxHash = abi
199
+ .encodePacked(
200
+ fundingTx.version,
201
+ fundingTx.inputVector,
202
+ fundingTx.outputVector,
203
+ fundingTx.locktime
204
+ )
205
+ .hash256View();
206
+
207
+ DepositRequest storage deposit = self.deposits[
208
+ uint256(
209
+ keccak256(
210
+ abi.encodePacked(fundingTxHash, reveal.fundingOutputIndex)
211
+ )
212
+ )
213
+ ];
214
+ require(deposit.revealedAt == 0, "Deposit already revealed");
215
+
216
+ uint64 fundingOutputAmount = fundingOutput.extractValue();
217
+
218
+ require(
219
+ fundingOutputAmount >= self.depositDustThreshold,
220
+ "Deposit amount too small"
221
+ );
222
+
223
+ deposit.amount = fundingOutputAmount;
224
+ deposit.depositor = reveal.depositor;
225
+ /* solhint-disable-next-line not-rely-on-time */
226
+ deposit.revealedAt = uint32(block.timestamp);
227
+ deposit.vault = reveal.vault;
228
+ deposit.treasuryFee = self.depositTreasuryFeeDivisor > 0
229
+ ? fundingOutputAmount / self.depositTreasuryFeeDivisor
230
+ : 0;
231
+
232
+ emit DepositRevealed(
233
+ fundingTxHash,
234
+ reveal.fundingOutputIndex,
235
+ reveal.depositor,
236
+ fundingOutputAmount,
237
+ reveal.blindingFactor,
238
+ reveal.walletPubKeyHash,
239
+ reveal.refundPubKeyHash,
240
+ reveal.refundLocktime,
241
+ reveal.vault
242
+ );
243
+ }
244
+ }
@@ -0,0 +1,30 @@
1
+ pragma solidity ^0.8.9;
2
+
3
+ import "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
4
+
5
+ library EcdsaLib {
6
+ using BytesLib for bytes;
7
+
8
+ /// @notice Converts public key X and Y coordinates (32-byte each) to a
9
+ /// compressed public key (33-byte). Compressed public key is X
10
+ /// coordinate prefixed with `02` or `03` based on the Y coordinate parity.
11
+ /// It is expected that the uncompressed public key is stripped
12
+ /// (i.e. it is not prefixed with `04`).
13
+ /// @param x Wallet's public key's X coordinate.
14
+ /// @param y Wallet's public key's Y coordinate.
15
+ /// @return Compressed public key (33-byte), prefixed with `02` or `03`.
16
+ function compressPublicKey(bytes32 x, bytes32 y)
17
+ internal
18
+ pure
19
+ returns (bytes memory)
20
+ {
21
+ bytes1 prefix;
22
+ if (uint256(y) % 2 == 0) {
23
+ prefix = hex"02";
24
+ } else {
25
+ prefix = hex"03";
26
+ }
27
+
28
+ return bytes.concat(prefix, x);
29
+ }
30
+ }