@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,278 @@
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 "./Redeem.sol";
24
+
25
+ library MovingFunds {
26
+ using BridgeState for BridgeState.Storage;
27
+ using Wallets for BridgeState.Storage;
28
+
29
+ using BTCUtils for bytes;
30
+ using BytesLib for bytes;
31
+
32
+ event MovingFundsCompleted(
33
+ bytes20 walletPubKeyHash,
34
+ bytes32 movingFundsTxHash
35
+ );
36
+
37
+ /// @notice Used by the wallet to prove the BTC moving funds transaction
38
+ /// and to make the necessary state changes. Moving funds is only
39
+ /// accepted if it satisfies SPV proof.
40
+ ///
41
+ /// The function validates the moving funds transaction structure
42
+ /// by checking if it actually spends the main UTXO of the declared
43
+ /// wallet and locks the value on the pre-committed target wallets
44
+ /// using a reasonable transaction fee. If all preconditions are
45
+ /// met, this functions closes the source wallet.
46
+ ///
47
+ /// It is possible to prove the given moving funds transaction only
48
+ /// one time.
49
+ /// @param movingFundsTx Bitcoin moving funds transaction data
50
+ /// @param movingFundsProof Bitcoin moving funds proof data
51
+ /// @param mainUtxo Data of the wallet's main UTXO, as currently known on
52
+ /// the Ethereum chain
53
+ /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
54
+ /// HASH160 over the compressed ECDSA public key) of the wallet
55
+ /// which performed the moving funds transaction
56
+ /// @dev Requirements:
57
+ /// - `movingFundsTx` components must match the expected structure. See
58
+ /// `BitcoinTx.Info` docs for reference. Their values must exactly
59
+ /// correspond to appropriate Bitcoin transaction fields to produce
60
+ /// a provable transaction hash.
61
+ /// - The `movingFundsTx` should represent a Bitcoin transaction with
62
+ /// exactly 1 input that refers to the wallet's main UTXO. That
63
+ /// transaction should have 1..n outputs corresponding to the
64
+ /// pre-committed target wallets. Outputs must be ordered in the
65
+ /// same way as their corresponding target wallets are ordered
66
+ /// within the target wallets commitment.
67
+ /// - `movingFundsProof` components must match the expected structure.
68
+ /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`
69
+ /// field must contain a valid number of block headers, not less
70
+ /// than the `txProofDifficultyFactor` contract constant.
71
+ /// - `mainUtxo` components must point to the recent main UTXO
72
+ /// of the given wallet, as currently known on the Ethereum chain.
73
+ /// Additionally, the recent main UTXO on Ethereum must be set.
74
+ /// - `walletPubKeyHash` must be connected with the main UTXO used
75
+ /// as transaction single input.
76
+ /// - The wallet that `walletPubKeyHash` points to must be in the
77
+ /// MovingFunds state.
78
+ /// - The target wallets commitment must be submitted by the wallet
79
+ /// that `walletPubKeyHash` points to.
80
+ /// - The total Bitcoin transaction fee must be lesser or equal
81
+ /// to `movingFundsTxMaxTotalFee` governable parameter.
82
+ function submitMovingFundsProof(
83
+ BridgeState.Storage storage self,
84
+ BitcoinTx.Info calldata movingFundsTx,
85
+ BitcoinTx.Proof calldata movingFundsProof,
86
+ BitcoinTx.UTXO calldata mainUtxo,
87
+ bytes20 walletPubKeyHash
88
+ ) external {
89
+ // The actual transaction proof is performed here. After that point, we
90
+ // can assume the transaction happened on Bitcoin chain and has
91
+ // a sufficient number of confirmations as determined by
92
+ // `txProofDifficultyFactor` constant.
93
+ bytes32 movingFundsTxHash = BitcoinTx.validateProof(
94
+ movingFundsTx,
95
+ movingFundsProof,
96
+ self.proofDifficultyContext()
97
+ );
98
+
99
+ // Process the moving funds transaction input. Specifically, check if
100
+ // it refers to the expected wallet's main UTXO.
101
+ OutboundTx.processWalletOutboundTxInput(
102
+ self,
103
+ movingFundsTx.inputVector,
104
+ mainUtxo,
105
+ walletPubKeyHash
106
+ );
107
+
108
+ (
109
+ bytes32 targetWalletsHash,
110
+ uint256 outputsTotalValue
111
+ ) = processMovingFundsTxOutputs(movingFundsTx.outputVector);
112
+
113
+ require(
114
+ mainUtxo.txOutputValue - outputsTotalValue <=
115
+ self.movingFundsTxMaxTotalFee,
116
+ "Transaction fee is too high"
117
+ );
118
+
119
+ self.notifyWalletFundsMoved(walletPubKeyHash, targetWalletsHash);
120
+
121
+ emit MovingFundsCompleted(walletPubKeyHash, movingFundsTxHash);
122
+ }
123
+
124
+ /// @notice Processes the moving funds Bitcoin transaction output vector
125
+ /// and extracts information required for further processing.
126
+ /// @param movingFundsTxOutputVector Bitcoin moving funds transaction output
127
+ /// vector. This function assumes vector's structure is valid so it
128
+ /// must be validated using e.g. `BTCUtils.validateVout` function
129
+ /// before it is passed here
130
+ /// @return targetWalletsHash keccak256 hash over the list of actual
131
+ /// target wallets used in the transaction.
132
+ /// @return outputsTotalValue Sum of all outputs values.
133
+ /// @dev Requirements:
134
+ /// - The `movingFundsTxOutputVector` must be parseable, i.e. must
135
+ /// be validated by the caller as stated in their parameter doc.
136
+ /// - Each output must refer to a 20-byte public key hash.
137
+ /// - The total outputs value must be evenly divided over all outputs.
138
+ function processMovingFundsTxOutputs(bytes memory movingFundsTxOutputVector)
139
+ internal
140
+ view
141
+ returns (bytes32 targetWalletsHash, uint256 outputsTotalValue)
142
+ {
143
+ // Determining the total number of Bitcoin transaction outputs in
144
+ // the same way as for number of inputs. See `BitcoinTx.outputVector`
145
+ // docs for more details.
146
+ (
147
+ uint256 outputsCompactSizeUintLength,
148
+ uint256 outputsCount
149
+ ) = movingFundsTxOutputVector.parseVarInt();
150
+
151
+ // To determine the first output starting index, we must jump over
152
+ // the compactSize uint which prepends the output vector. One byte
153
+ // must be added because `BtcUtils.parseVarInt` does not include
154
+ // compactSize uint tag in the returned length.
155
+ //
156
+ // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`
157
+ // returns `0`, so we jump over one byte of compactSize uint.
158
+ //
159
+ // For >= 253 && <= 0xffff there is `0xfd` tag,
160
+ // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no
161
+ // tag byte included) so we need to jump over 1+2 bytes of
162
+ // compactSize uint.
163
+ //
164
+ // Please refer `BTCUtils` library and compactSize uint
165
+ // docs in `BitcoinTx` library for more details.
166
+ uint256 outputStartingIndex = 1 + outputsCompactSizeUintLength;
167
+
168
+ bytes20[] memory targetWallets = new bytes20[](outputsCount);
169
+ uint64[] memory outputsValues = new uint64[](outputsCount);
170
+
171
+ // Outputs processing loop.
172
+ for (uint256 i = 0; i < outputsCount; i++) {
173
+ uint256 outputLength = movingFundsTxOutputVector
174
+ .determineOutputLengthAt(outputStartingIndex);
175
+
176
+ bytes memory output = movingFundsTxOutputVector.slice(
177
+ outputStartingIndex,
178
+ outputLength
179
+ );
180
+
181
+ // Extract the output script payload.
182
+ bytes memory targetWalletPubKeyHashBytes = output.extractHash();
183
+ // Output script payload must refer to a known wallet public key
184
+ // hash which is always 20-byte.
185
+ require(
186
+ targetWalletPubKeyHashBytes.length == 20,
187
+ "Target wallet public key hash must have 20 bytes"
188
+ );
189
+
190
+ bytes20 targetWalletPubKeyHash = targetWalletPubKeyHashBytes
191
+ .slice20(0);
192
+
193
+ // The next step is making sure that the 20-byte public key hash
194
+ // is actually used in the right context of a P2PKH or P2WPKH
195
+ // output. To do so, we must extract the full script from the output
196
+ // and compare with the expected P2PKH and P2WPKH scripts
197
+ // referring to that 20-byte public key hash. The output consists
198
+ // of an 8-byte value and a variable length script. To extract the
199
+ // script we slice the output starting from 9th byte until the end.
200
+ bytes32 outputScriptKeccak = keccak256(
201
+ output.slice(8, output.length - 8)
202
+ );
203
+ // Build the expected P2PKH script which has the following byte
204
+ // format: <0x1976a914> <20-byte PKH> <0x88ac>. According to
205
+ // https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
206
+ // - 0x19: Byte length of the entire script
207
+ // - 0x76: OP_DUP
208
+ // - 0xa9: OP_HASH160
209
+ // - 0x14: Byte length of the public key hash
210
+ // - 0x88: OP_EQUALVERIFY
211
+ // - 0xac: OP_CHECKSIG
212
+ // which matches the P2PKH structure as per:
213
+ // https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash
214
+ bytes32 targetWalletP2PKHScriptKeccak = keccak256(
215
+ abi.encodePacked(
216
+ hex"1976a914",
217
+ targetWalletPubKeyHash,
218
+ hex"88ac"
219
+ )
220
+ );
221
+ // Build the expected P2WPKH script which has the following format:
222
+ // <0x160014> <20-byte PKH>. According to
223
+ // https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
224
+ // - 0x16: Byte length of the entire script
225
+ // - 0x00: OP_0
226
+ // - 0x14: Byte length of the public key hash
227
+ // which matches the P2WPKH structure as per:
228
+ // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH
229
+ bytes32 targetWalletP2WPKHScriptKeccak = keccak256(
230
+ abi.encodePacked(hex"160014", targetWalletPubKeyHash)
231
+ );
232
+ // Make sure the actual output script matches either the P2PKH
233
+ // or P2WPKH format.
234
+ require(
235
+ outputScriptKeccak == targetWalletP2PKHScriptKeccak ||
236
+ outputScriptKeccak == targetWalletP2WPKHScriptKeccak,
237
+ "Output must be P2PKH or P2WPKH"
238
+ );
239
+
240
+ // Add the wallet public key hash to the list that will be used
241
+ // to build the result list hash. There is no need to check if
242
+ // given output is a change here because the actual target wallet
243
+ // list must be exactly the same as the pre-committed target wallet
244
+ // list which is guaranteed to be valid.
245
+ targetWallets[i] = targetWalletPubKeyHash;
246
+
247
+ // Extract the value from given output.
248
+ outputsValues[i] = output.extractValue();
249
+ outputsTotalValue += outputsValues[i];
250
+
251
+ // Make the `outputStartingIndex` pointing to the next output by
252
+ // increasing it by current output's length.
253
+ outputStartingIndex += outputLength;
254
+ }
255
+
256
+ // Compute the indivisible remainder that remains after dividing the
257
+ // outputs total value over all outputs evenly.
258
+ uint256 outputsTotalValueRemainder = outputsTotalValue % outputsCount;
259
+ // Compute the minimum allowed output value by dividing the outputs
260
+ // total value (reduced by the remainder) by the number of outputs.
261
+ uint256 minOutputValue = (outputsTotalValue -
262
+ outputsTotalValueRemainder) / outputsCount;
263
+ // Maximum possible value is the minimum value with the remainder included.
264
+ uint256 maxOutputValue = minOutputValue + outputsTotalValueRemainder;
265
+
266
+ for (uint256 i = 0; i < outputsCount; i++) {
267
+ require(
268
+ minOutputValue <= outputsValues[i] &&
269
+ outputsValues[i] <= maxOutputValue,
270
+ "Transaction amount is not distributed evenly"
271
+ );
272
+ }
273
+
274
+ targetWalletsHash = keccak256(abi.encodePacked(targetWallets));
275
+
276
+ return (targetWalletsHash, outputsTotalValue);
277
+ }
278
+ }