@keep-network/tbtc-v2 0.1.1-dev.3 → 0.1.1-dev.30

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 (42) 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/087a7a27c8cd210b7c63e594263dfed9.json +197 -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 +43 -2
  10. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
  11. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +96 -0
  12. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  13. package/build/contracts/bridge/Bridge.sol/Bridge.json +1598 -71
  14. package/build/contracts/bridge/Bridge.sol/IRelay.dbg.json +4 -0
  15. package/build/contracts/bridge/Bridge.sol/IRelay.json +37 -0
  16. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
  17. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
  18. package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +4 -0
  19. package/build/contracts/bridge/Frauds.sol/Frauds.json +138 -0
  20. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  21. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
  22. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
  23. package/build/contracts/bridge/Wallets.sol/Wallets.json +138 -0
  24. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  25. package/build/contracts/token/TBTC.sol/TBTC.json +2 -2
  26. package/build/contracts/vault/IVault.sol/IVault.dbg.json +4 -0
  27. package/build/contracts/vault/IVault.sol/IVault.json +47 -0
  28. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  29. package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +38 -2
  30. package/contracts/GovernanceUtils.sol +1 -1
  31. package/contracts/bank/Bank.sol +69 -18
  32. package/contracts/bridge/BitcoinTx.sol +241 -0
  33. package/contracts/bridge/Bridge.sol +1956 -104
  34. package/contracts/bridge/EcdsaLib.sol +30 -0
  35. package/contracts/bridge/Frauds.sol +531 -0
  36. package/contracts/bridge/VendingMachine.sol +1 -1
  37. package/contracts/bridge/Wallets.sol +521 -0
  38. package/contracts/token/TBTC.sol +1 -1
  39. package/contracts/vault/IVault.sol +60 -0
  40. package/contracts/vault/TBTCVault.sol +50 -8
  41. package/package.json +28 -24
  42. package/artifacts/solcInputs/cebfa5efa019cb9c8c5e23e38703b883.json +0 -113
@@ -12,10 +12,13 @@
12
12
  // ▐████▌ ▐████▌
13
13
  // ▐████▌ ▐████▌
14
14
  // ▐████▌ ▐████▌
15
- pragma solidity 0.8.4;
15
+
16
+ pragma solidity ^0.8.9;
16
17
 
17
18
  import "@openzeppelin/contracts/access/Ownable.sol";
18
19
 
20
+ import "../vault/IVault.sol";
21
+
19
22
  /// @title Bitcoin Bank
20
23
  /// @notice Bank is a central component tracking Bitcoin balances. Balances can
21
24
  /// be transferred between holders and holders can approve their
@@ -111,6 +114,21 @@ contract Bank is Ownable {
111
114
  _approveBalance(msg.sender, spender, amount);
112
115
  }
113
116
 
117
+ /// @notice Sets `amount` as the allowance of a smart contract `vault` over
118
+ /// the caller's balance and calls the vault via
119
+ /// `receiveBalanceApproval`.
120
+ /// @dev If the `amount` is set to `type(uint256).max` then the logic in
121
+ /// `receiveBalanceApproval` or later call to `transferBalanceFrom` by
122
+ /// the vault will not reduce an allowance. Beware that changing an
123
+ /// allowance with this function brings the risk that vault may use
124
+ /// both the old and the new allowance by unfortunate transaction
125
+ /// ordering. Please use `increaseBalanceAllowance` and
126
+ /// `decreaseBalanceAllowance` to eliminate the risk.
127
+ function approveBalanceAndCall(address vault, uint256 amount) external {
128
+ _approveBalance(msg.sender, vault, amount);
129
+ IVault(vault).receiveBalanceApproval(msg.sender, amount);
130
+ }
131
+
114
132
  /// @notice Atomically increases the balance allowance granted to `spender`
115
133
  /// by the caller by the given `addedValue`.
116
134
  function increaseBalanceAllowance(address spender, uint256 addedValue)
@@ -204,23 +222,22 @@ contract Bank is Ownable {
204
222
  );
205
223
  require(v == 27 || v == 28, "Invalid signature 'v' value");
206
224
 
207
- bytes32 digest =
208
- keccak256(
209
- abi.encodePacked(
210
- "\x19\x01",
211
- DOMAIN_SEPARATOR(),
212
- keccak256(
213
- abi.encode(
214
- PERMIT_TYPEHASH,
215
- owner,
216
- spender,
217
- amount,
218
- nonce[owner]++,
219
- deadline
220
- )
225
+ bytes32 digest = keccak256(
226
+ abi.encodePacked(
227
+ "\x19\x01",
228
+ DOMAIN_SEPARATOR(),
229
+ keccak256(
230
+ abi.encode(
231
+ PERMIT_TYPEHASH,
232
+ owner,
233
+ spender,
234
+ amount,
235
+ nonce[owner]++,
236
+ deadline
221
237
  )
222
238
  )
223
- );
239
+ )
240
+ );
224
241
  address recoveredAddress = ecrecover(digest, v, r, s);
225
242
  require(
226
243
  recoveredAddress != address(0) && recoveredAddress == owner,
@@ -231,7 +248,8 @@ contract Bank is Ownable {
231
248
 
232
249
  /// @notice Increases balances of the provided `recipients` by the provided
233
250
  /// `amounts`. Can only be called by the Bridge.
234
- /// @dev This function fails if the lengths of the arrays are not the same.
251
+ /// @dev Requirements:
252
+ /// - length of `recipients` and `amounts` must be the same.
235
253
  function increaseBalances(
236
254
  address[] calldata recipients,
237
255
  uint256[] calldata amounts
@@ -254,6 +272,37 @@ contract Bank is Ownable {
254
272
  _increaseBalance(recipient, amount);
255
273
  }
256
274
 
275
+ /// @notice Increases the given smart contract `vault`'s balance and
276
+ /// notifies the `vault` contract. Called by the Bridge after
277
+ /// the deposits routed by depositors to that `vault` have been
278
+ /// swept by the Bridge. This way, the depositor does not have to
279
+ /// issue a separate transaction to the `vault` contract.
280
+ /// Can be called only by the Bridge.
281
+ /// @dev Requirements:
282
+ /// - `vault` must implement `IVault` interface,
283
+ /// - length of `depositors` and `depositedAmounts` must be the same.
284
+ /// @param vault Address of `IVault` recipient contract
285
+ /// @param depositors Addresses of depositors whose deposits have been swept
286
+ /// @param depositedAmounts Amounts deposited by individual depositors and
287
+ /// swept. The `vault`'s balance in the Bank will be increased by the
288
+ /// sum of all elements in this array.
289
+ function increaseBalanceAndCall(
290
+ address vault,
291
+ address[] calldata depositors,
292
+ uint256[] calldata depositedAmounts
293
+ ) external onlyBridge {
294
+ require(
295
+ depositors.length == depositedAmounts.length,
296
+ "Arrays must have the same length"
297
+ );
298
+ uint256 totalAmount = 0;
299
+ for (uint256 i = 0; i < depositedAmounts.length; i++) {
300
+ totalAmount += depositedAmounts[i];
301
+ }
302
+ _increaseBalance(vault, totalAmount);
303
+ IVault(vault).receiveBalanceIncrease(depositors, depositedAmounts);
304
+ }
305
+
257
306
  /// @notice Decreases caller's balance by the provided `amount`. There is no
258
307
  /// way to restore the balance so do not call this function unless
259
308
  /// you really know what you are doing!
@@ -307,7 +356,9 @@ contract Bank is Ownable {
307
356
 
308
357
  uint256 spenderBalance = balanceOf[spender];
309
358
  require(spenderBalance >= amount, "Transfer amount exceeds balance");
310
- unchecked {balanceOf[spender] = spenderBalance - amount;}
359
+ unchecked {
360
+ balanceOf[spender] = spenderBalance - amount;
361
+ }
311
362
  balanceOf[recipient] += amount;
312
363
  emit BalanceTransferred(spender, recipient, amount);
313
364
  }
@@ -0,0 +1,241 @@
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 {ValidateSPV} from "@keep-network/bitcoin-spv-sol/contracts/ValidateSPV.sol";
20
+
21
+ /// @title Bitcoin transaction
22
+ /// @notice Allows to reference Bitcoin raw transaction in Solidity.
23
+ /// @dev See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format
24
+ ///
25
+ /// Raw Bitcon transaction data:
26
+ ///
27
+ /// | Bytes | Name | BTC type | Description |
28
+ /// |--------|--------------|------------------------|---------------------------|
29
+ /// | 4 | version | int32_t (LE) | TX version number |
30
+ /// | varies | tx_in_count | compactSize uint (LE) | Number of TX inputs |
31
+ /// | varies | tx_in | txIn[] | TX inputs |
32
+ /// | varies | tx_out count | compactSize uint (LE) | Number of TX outputs |
33
+ /// | varies | tx_out | txOut[] | TX outputs |
34
+ /// | 4 | lock_time | uint32_t (LE) | Unix time or block number |
35
+ ///
36
+ //
37
+ /// Non-coinbase transaction input (txIn):
38
+ ///
39
+ /// | Bytes | Name | BTC type | Description |
40
+ /// |--------|------------------|------------------------|---------------------------------------------|
41
+ /// | 36 | previous_output | outpoint | The previous outpoint being spent |
42
+ /// | varies | script bytes | compactSize uint (LE) | The number of bytes in the signature script |
43
+ /// | varies | signature script | char[] | The signature script, empty for P2WSH |
44
+ /// | 4 | sequence | uint32_t (LE) | Sequence number |
45
+ ///
46
+ ///
47
+ /// The reference to transaction being spent (outpoint):
48
+ ///
49
+ /// | Bytes | Name | BTC type | Description |
50
+ /// |-------|-------|---------------|------------------------------------------|
51
+ /// | 32 | hash | char[32] | Hash of the transaction to spend |
52
+ /// | 4 | index | uint32_t (LE) | Index of the specific output from the TX |
53
+ ///
54
+ ///
55
+ /// Transaction output (txOut):
56
+ ///
57
+ /// | Bytes | Name | BTC type | Description |
58
+ /// |--------|-----------------|-----------------------|--------------------------------------|
59
+ /// | 8 | value | int64_t (LE) | Number of satoshis to spend |
60
+ /// | 1+ | pk_script_bytes | compactSize uint (LE) | Number of bytes in the pubkey script |
61
+ /// | varies | pk_script | char[] | Pubkey script |
62
+ ///
63
+ /// compactSize uint format:
64
+ ///
65
+ /// | Value | Bytes | Format |
66
+ /// |-----------------------------------------|-------|----------------------------------------------|
67
+ /// | >= 0 && <= 252 | 1 | uint8_t |
68
+ /// | >= 253 && <= 0xffff | 3 | 0xfd followed by the number as uint16_t (LE) |
69
+ /// | >= 0x10000 && <= 0xffffffff | 5 | 0xfe followed by the number as uint32_t (LE) |
70
+ /// | >= 0x100000000 && <= 0xffffffffffffffff | 9 | 0xff followed by the number as uint64_t (LE) |
71
+ ///
72
+ /// (*) compactSize uint is often references as VarInt)
73
+ ///
74
+ library BitcoinTx {
75
+ using BTCUtils for bytes;
76
+ using BTCUtils for uint256;
77
+ using ValidateSPV for bytes;
78
+ using ValidateSPV for bytes32;
79
+
80
+ /// @notice Represents Bitcoin transaction data.
81
+ struct Info {
82
+ /// @notice Bitcoin transaction version
83
+ /// @dev `version` from raw Bitcon transaction data.
84
+ /// Encoded as 4-bytes signed integer, little endian.
85
+ bytes4 version;
86
+ /// @notice All Bitcoin transaction inputs, prepended by the number of
87
+ /// transaction inputs.
88
+ /// @dev `tx_in_count | tx_in` from raw Bitcon transaction data.
89
+ ///
90
+ /// The number of transaction inputs encoded as compactSize
91
+ /// unsigned integer, little-endian.
92
+ ///
93
+ /// Note that some popular block explorers reverse the order of
94
+ /// bytes from `outpoint`'s `hash` and display it as big-endian.
95
+ /// Solidity code of Bridge expects hashes in little-endian, just
96
+ /// like they are represented in a raw Bitcoin transaction.
97
+ bytes inputVector;
98
+ /// @notice All Bitcoin transaction outputs prepended by the number of
99
+ /// transaction outputs.
100
+ /// @dev `tx_out_count | tx_out` from raw Bitcoin transaction data.
101
+ ///
102
+ /// The number of transaction outputs encoded as a compactSize
103
+ /// unsigned integer, little-endian.
104
+ bytes outputVector;
105
+ /// @notice Bitcoin transaction locktime.
106
+ ///
107
+ /// @dev `lock_time` from raw Bitcoin transaction data.
108
+ /// Encoded as 4-bytes unsigned integer, little endian.
109
+ bytes4 locktime;
110
+ }
111
+
112
+ /// @notice Represents data needed to perform a Bitcoin SPV proof.
113
+ struct Proof {
114
+ /// @notice The merkle proof of transaction inclusion in a block.
115
+ bytes merkleProof;
116
+ /// @notice Transaction index in the block (0-indexed).
117
+ uint256 txIndexInBlock;
118
+ /// @notice Single byte-string of 80-byte bitcoin headers,
119
+ /// lowest height first.
120
+ bytes bitcoinHeaders;
121
+ }
122
+
123
+ /// @notice Determines the difficulty context for a Bitcoin SPV proof.
124
+ struct ProofDifficulty {
125
+ /// @notice Difficulty of the current epoch.
126
+ uint256 currentEpochDifficulty;
127
+ /// @notice Difficulty of the previous epoch.
128
+ uint256 previousEpochDifficulty;
129
+ /// @notice The number of confirmations on the Bitcoin chain required
130
+ /// to successfully evaluate an SPV proof.
131
+ uint256 difficultyFactor;
132
+ }
133
+
134
+ /// @notice Represents info about an unspent transaction output.
135
+ struct UTXO {
136
+ /// @notice Hash of the transaction the output belongs to.
137
+ /// @dev Byte order corresponds to the Bitcoin internal byte order.
138
+ bytes32 txHash;
139
+ /// @notice Index of the transaction output (0-indexed).
140
+ uint32 txOutputIndex;
141
+ /// @notice Value of the transaction output.
142
+ uint64 txOutputValue;
143
+ }
144
+
145
+ /// @notice Represents Bitcoin signature in the R/S/V format.
146
+ struct RSVSignature {
147
+ /// @notice Signature r value.
148
+ bytes32 r;
149
+ /// @notice Signature s value.
150
+ bytes32 s;
151
+ /// @notice Signature recovery value.
152
+ uint8 v;
153
+ }
154
+
155
+ /// @notice Validates the SPV proof of the Bitcoin transaction.
156
+ /// Reverts in case the validation or proof verification fail.
157
+ /// @param txInfo Bitcoin transaction data
158
+ /// @param proof Bitcoin proof data
159
+ /// @param proofDifficulty Bitcoin proof difficulty context.
160
+ /// @return txHash Proven 32-byte transaction hash.
161
+ function validateProof(
162
+ Info calldata txInfo,
163
+ Proof calldata proof,
164
+ ProofDifficulty calldata proofDifficulty
165
+ ) external view returns (bytes32 txHash) {
166
+ require(
167
+ txInfo.inputVector.validateVin(),
168
+ "Invalid input vector provided"
169
+ );
170
+ require(
171
+ txInfo.outputVector.validateVout(),
172
+ "Invalid output vector provided"
173
+ );
174
+
175
+ txHash = abi
176
+ .encodePacked(
177
+ txInfo.version,
178
+ txInfo.inputVector,
179
+ txInfo.outputVector,
180
+ txInfo.locktime
181
+ )
182
+ .hash256View();
183
+
184
+ require(
185
+ txHash.prove(
186
+ proof.bitcoinHeaders.extractMerkleRootLE(),
187
+ proof.merkleProof,
188
+ proof.txIndexInBlock
189
+ ),
190
+ "Tx merkle proof is not valid for provided header and tx hash"
191
+ );
192
+
193
+ evaluateProofDifficulty(proof.bitcoinHeaders, proofDifficulty);
194
+
195
+ return txHash;
196
+ }
197
+
198
+ /// @notice Evaluates the given Bitcoin proof difficulty against the actual
199
+ /// Bitcoin chain difficulty provided by the relay oracle.
200
+ /// Reverts in case the evaluation fails.
201
+ /// @param bitcoinHeaders Bitcoin headers chain being part of the SPV
202
+ /// proof. Used to extract the observed proof difficulty
203
+ /// @param proofDifficulty Bitcoin proof difficulty context.
204
+ function evaluateProofDifficulty(
205
+ bytes memory bitcoinHeaders,
206
+ ProofDifficulty calldata proofDifficulty
207
+ ) internal view {
208
+ uint256 requestedDiff = 0;
209
+ uint256 firstHeaderDiff = bitcoinHeaders
210
+ .extractTarget()
211
+ .calculateDifficulty();
212
+
213
+ if (firstHeaderDiff == proofDifficulty.currentEpochDifficulty) {
214
+ requestedDiff = proofDifficulty.currentEpochDifficulty;
215
+ } else if (firstHeaderDiff == proofDifficulty.previousEpochDifficulty) {
216
+ requestedDiff = proofDifficulty.previousEpochDifficulty;
217
+ } else {
218
+ revert("Not at current or previous difficulty");
219
+ }
220
+
221
+ uint256 observedDiff = bitcoinHeaders.validateHeaderChain();
222
+
223
+ require(
224
+ observedDiff != ValidateSPV.getErrBadLength(),
225
+ "Invalid length of the headers chain"
226
+ );
227
+ require(
228
+ observedDiff != ValidateSPV.getErrInvalidChain(),
229
+ "Invalid headers chain"
230
+ );
231
+ require(
232
+ observedDiff != ValidateSPV.getErrLowWork(),
233
+ "Insufficient work in a header"
234
+ );
235
+
236
+ require(
237
+ observedDiff >= requestedDiff * proofDifficulty.difficultyFactor,
238
+ "Insufficient accumulated difficulty in header chain"
239
+ );
240
+ }
241
+ }