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

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/f1a50b67569d88ee54efa3e22c6b484e.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 +1743 -64
  14. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  15. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +10 -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/Frauds.sol/Frauds.dbg.json +4 -0
  21. package/build/contracts/bridge/Frauds.sol/Frauds.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 +48 -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 +110 -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 +138 -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 +980 -123
  46. package/contracts/bridge/BridgeState.sol +172 -0
  47. package/contracts/bridge/Deposit.sol +247 -0
  48. package/contracts/bridge/EcdsaLib.sol +30 -0
  49. package/contracts/bridge/Frauds.sol +529 -0
  50. package/contracts/bridge/IRelay.sol +28 -0
  51. package/contracts/bridge/MovingFunds.sol +280 -0
  52. package/contracts/bridge/Redeem.sol +849 -0
  53. package/contracts/bridge/Sweep.sol +510 -0
  54. package/contracts/bridge/VendingMachine.sol +1 -1
  55. package/contracts/bridge/Wallets.sol +591 -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,529 @@
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 {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
19
+ import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
20
+ import {CheckBitcoinSigs} from "@keep-network/bitcoin-spv-sol/contracts/CheckBitcoinSigs.sol";
21
+ import "./BitcoinTx.sol";
22
+ import "./EcdsaLib.sol";
23
+ import "./Bridge.sol";
24
+
25
+ library Frauds {
26
+ using BytesLib for bytes;
27
+ using BTCUtils for bytes;
28
+ using BTCUtils for uint32;
29
+ using EcdsaLib for bytes;
30
+
31
+ struct Data {
32
+ /// The amount of stake slashed from each member of a wallet for a fraud.
33
+ uint256 slashingAmount;
34
+ /// The percentage of the notifier reward from the staking contract
35
+ /// the notifier of a fraud receives. The value is in the range [0, 100].
36
+ uint256 notifierRewardMultiplier;
37
+ /// The amount of time the wallet has to defeat a fraud challenge.
38
+ uint256 challengeDefeatTimeout;
39
+ /// The amount of ETH in wei the party challenging the wallet for fraud
40
+ /// needs to deposit.
41
+ uint256 challengeDepositAmount;
42
+ /// Collection of all submitted fraud challenges indexed by challenge
43
+ /// key built as keccak256(walletPublicKey|sighash).
44
+ mapping(uint256 => FraudChallenge) challenges;
45
+ }
46
+
47
+ struct FraudChallenge {
48
+ // The address of the party challenging the wallet.
49
+ address challenger;
50
+ // The amount of ETH the challenger deposited.
51
+ uint256 depositAmount;
52
+ // The timestamp the challenge was submitted at.
53
+ uint32 reportedAt;
54
+ // The flag indicating whether the challenge has been resolved.
55
+ bool resolved;
56
+ }
57
+
58
+ event FraudSlashingAmountUpdated(uint256 newFraudSlashingAmount);
59
+
60
+ event FraudNotifierRewardMultiplierUpdated(
61
+ uint256 newFraudNotifierRewardMultiplier
62
+ );
63
+
64
+ event FraudChallengeDefeatTimeoutUpdated(
65
+ uint256 newFraudChallengeDefeatTimeout
66
+ );
67
+
68
+ event FraudChallengeDepositAmountUpdated(
69
+ uint256 newFraudChallengeDepositAmount
70
+ );
71
+
72
+ event FraudChallengeSubmitted(
73
+ bytes20 walletPublicKeyHash,
74
+ bytes32 sighash,
75
+ uint8 v,
76
+ bytes32 r,
77
+ bytes32 s
78
+ );
79
+
80
+ event FraudChallengeDefeated(bytes20 walletPublicKeyHash, bytes32 sighash);
81
+
82
+ event FraudChallengeDefeatTimedOut(
83
+ bytes20 walletPublicKeyHash,
84
+ bytes32 sighash
85
+ );
86
+
87
+ /// @notice Submits a fraud challenge indicating that a UTXO being under
88
+ /// wallet control was unlocked by the wallet but was not used
89
+ /// according to the protocol rules. That means the wallet signed
90
+ /// a transaction input pointing to that UTXO and there is a unique
91
+ /// sighash and signature pair associated with that input. This
92
+ /// function uses those parameters to create a fraud accusation that
93
+ /// proves a given transaction input unlocking the given UTXO was
94
+ /// actually signed by the wallet. This function cannot determine
95
+ /// whether the transaction was actually broadcast and the input was
96
+ /// consumed in a fraudulent way so it just opens a challenge period
97
+ /// during which the wallet can defeat the challenge by submitting
98
+ /// proof of a transaction that consumes the given input according
99
+ /// to protocol rules. To prevent spurious allegations, the caller
100
+ /// must deposit ETH that is returned back upon justified fraud
101
+ /// challenge or confiscated otherwise
102
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
103
+ /// and unprefixed format (64 bytes)
104
+ /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
105
+ // HASH160 over the compressed ECDSA public key) of the wallet
106
+ /// @param sighash The hash that was used to produce the ECDSA signature
107
+ /// that is the subject of the fraud claim. This hash is constructed
108
+ /// by applying double SHA-256 over a serialized subset of the
109
+ /// transaction. The exact subset used as hash preimage depends on
110
+ /// the transaction input the signature is produced for. See BIP-143
111
+ /// for reference
112
+ /// @param signature Bitcoin signature in the R/S/V format
113
+ /// @dev Requirements:
114
+ /// - The challenger must send appropriate amount of ETH used as
115
+ /// fraud challenge deposit
116
+ /// - The signature (represented by r, s and v) must be generated by
117
+ /// the wallet behind `walletPubKey` during signing of `sighash`
118
+ /// - Wallet can be challenged for the given signature only once
119
+ function submitChallenge(
120
+ Data storage self,
121
+ bytes calldata walletPublicKey,
122
+ bytes20 walletPubKeyHash,
123
+ bytes32 sighash,
124
+ BitcoinTx.RSVSignature calldata signature
125
+ ) external {
126
+ require(
127
+ msg.value >= self.challengeDepositAmount,
128
+ "The amount of ETH deposited is too low"
129
+ );
130
+
131
+ require(
132
+ CheckBitcoinSigs.checkSig(
133
+ walletPublicKey,
134
+ sighash,
135
+ signature.v,
136
+ signature.r,
137
+ signature.s
138
+ ),
139
+ "Signature verification failure"
140
+ );
141
+
142
+ uint256 challengeKey = uint256(
143
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
144
+ );
145
+
146
+ FraudChallenge storage challenge = self.challenges[challengeKey];
147
+ require(challenge.reportedAt == 0, "Fraud challenge already exists");
148
+
149
+ challenge.challenger = msg.sender;
150
+ challenge.depositAmount = msg.value;
151
+ /* solhint-disable-next-line not-rely-on-time */
152
+ challenge.reportedAt = uint32(block.timestamp);
153
+ challenge.resolved = false;
154
+
155
+ emit FraudChallengeSubmitted(
156
+ walletPubKeyHash,
157
+ sighash,
158
+ signature.v,
159
+ signature.r,
160
+ signature.s
161
+ );
162
+ }
163
+
164
+ /// @notice Unwraps the fraud challenge by verifying the given challenge
165
+ /// and returns the UTXO key extracted from the preimage.
166
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
167
+ /// and unprefixed format (64 bytes)
168
+ /// @param preimage The preimage which produces sighash used to generate the
169
+ /// ECDSA signature that is the subject of the fraud claim. It is a
170
+ /// serialized subset of the transaction. The exact subset used as
171
+ /// the preimage depends on the transaction input the signature is
172
+ /// produced for. See BIP-143 for reference
173
+ /// @param witness Flag indicating whether the preimage was produced for a
174
+ /// witness input. True for witness, false for non-witness input.
175
+ /// @return utxoKey UTXO key that identifies spent input.
176
+ /// @dev Requirements:
177
+ /// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
178
+ /// must identify an open fraud challenge
179
+ /// - the preimage must be a valid preimage of a transaction generated
180
+ /// according to the protocol rules and already proved in the Bridge
181
+ function unwrapChallenge(
182
+ Data storage self,
183
+ bytes calldata walletPublicKey,
184
+ bytes calldata preimage,
185
+ bool witness
186
+ ) external returns (uint256 utxoKey) {
187
+ bytes32 sighash = preimage.hash256();
188
+
189
+ uint256 challengeKey = uint256(
190
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
191
+ );
192
+
193
+ FraudChallenge storage challenge = self.challenges[challengeKey];
194
+
195
+ require(challenge.reportedAt > 0, "Fraud challenge does not exist");
196
+ require(
197
+ !challenge.resolved,
198
+ "Fraud challenge has already been resolved"
199
+ );
200
+
201
+ // Ensure SIGHASH_ALL type was used during signing, which is represented
202
+ // by type value `1`.
203
+ require(extractSighashType(preimage) == 1, "Wrong sighash type");
204
+
205
+ return
206
+ witness
207
+ ? extractUtxoKeyFromWitnessPreimage(preimage)
208
+ : extractUtxoKeyFromNonWitnessPreimage(preimage);
209
+ }
210
+
211
+ /// @notice Finalizes fraud challenge defeat by marking a pending fraud
212
+ /// challenge against the wallet as resolved and sending the ether
213
+ /// deposited by the challenger to the treasury.
214
+ /// In order to finalize the challenge defeat the same
215
+ /// `walletPublicKey` must be provided as was used in the fraud
216
+ /// challenge. Additionally a preimage must be provided which was
217
+ /// used to calculate the sighash during input signing.
218
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
219
+ /// and unprefixed format (64 bytes)
220
+ /// @param preimage The preimage which produces sighash used to generate the
221
+ /// ECDSA signature that is the subject of the fraud claim. It is a
222
+ /// serialized subset of the transaction. The exact subset used as
223
+ /// the preimage depends on the transaction input the signature is
224
+ /// produced for. See BIP-143 for reference
225
+ /// @param treasury Treasury associated with the Bridge
226
+ /// @dev Requirements:
227
+ /// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
228
+ /// must identify an open fraud challenge
229
+ /// - the preimage must be a valid preimage of a transaction generated
230
+ /// according to the protocol rules and already proved in the Bridge
231
+ /// - before a defeat attempt is made the transaction that spends the
232
+ /// given UTXO must be proven in the Bridge
233
+ function defeatChallenge(
234
+ Data storage self,
235
+ bytes calldata walletPublicKey,
236
+ bytes calldata preimage,
237
+ address treasury
238
+ ) external {
239
+ bytes32 sighash = preimage.hash256();
240
+
241
+ uint256 challengeKey = uint256(
242
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
243
+ );
244
+
245
+ FraudChallenge storage challenge = self.challenges[challengeKey];
246
+
247
+ // Mark the challenge as resolved as it was successfully defeated
248
+ challenge.resolved = true;
249
+
250
+ // Send the ether deposited by the challenger to the treasury
251
+ /* solhint-disable avoid-low-level-calls */
252
+ // slither-disable-next-line low-level-calls
253
+ treasury.call{gas: 100000, value: challenge.depositAmount}("");
254
+ /* solhint-enable avoid-low-level-calls */
255
+
256
+ bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
257
+ walletPublicKey.slice32(0),
258
+ walletPublicKey.slice32(32)
259
+ );
260
+ bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
261
+
262
+ emit FraudChallengeDefeated(walletPubKeyHash, sighash);
263
+ }
264
+
265
+ /// @notice Notifies about defeat timeout for the given fraud challenge.
266
+ /// Can be called only if there was a fraud challenge identified by
267
+ /// the provided `walletPublicKey` and `sighash` and it was not
268
+ /// defeated on time. The amount of time that needs to pass after a
269
+ /// fraud challenge is reported is indicated by the
270
+ /// `challengeDefeatTimeout`. After a successful fraud challenge
271
+ /// defeat timeout notification the fraud challenge is marked as
272
+ /// resolved, the stake of each operator is slashed, the ether
273
+ /// deposited is returned to the challenger and the challenger is
274
+ /// rewarded.
275
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
276
+ /// and unprefixed format (64 bytes)
277
+ /// @param sighash The hash that was used to produce the ECDSA signature
278
+ /// that is the subject of the fraud claim. This hash is constructed
279
+ /// by applying double SHA-256 over a serialized subset of the
280
+ /// transaction. The exact subset used as hash preimage depends on
281
+ /// the transaction input the signature is produced for. See BIP-143
282
+ /// for reference
283
+ /// @dev Requirements:
284
+ /// - `walletPublicKey` and `sighash` must identify an open fraud
285
+ /// challenge
286
+ /// - the amount of time indicated by `challengeDefeatTimeout` must pass
287
+ /// after the challenge was reported
288
+ function notifyChallengeDefeatTimeout(
289
+ Data storage self,
290
+ bytes calldata walletPublicKey,
291
+ bytes32 sighash
292
+ ) external {
293
+ uint256 challengeKey = uint256(
294
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
295
+ );
296
+
297
+ FraudChallenge storage challenge = self.challenges[challengeKey];
298
+ require(challenge.reportedAt > 0, "Fraud challenge does not exist");
299
+ require(
300
+ !challenge.resolved,
301
+ "Fraud challenge has already been resolved"
302
+ );
303
+ require(
304
+ /* solhint-disable-next-line not-rely-on-time */
305
+ block.timestamp >=
306
+ challenge.reportedAt + self.challengeDefeatTimeout,
307
+ "Fraud challenge defeat period did not time out yet"
308
+ );
309
+
310
+ // TODO: Call notifyFraud from Wallets library
311
+ // TODO: Reward the challenger
312
+
313
+ challenge.resolved = true;
314
+
315
+ // Return the ether deposited by the challenger
316
+ /* solhint-disable avoid-low-level-calls */
317
+ // slither-disable-next-line low-level-calls
318
+ challenge.challenger.call{gas: 100000, value: challenge.depositAmount}(
319
+ ""
320
+ );
321
+ /* solhint-enable avoid-low-level-calls */
322
+
323
+ bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
324
+ walletPublicKey.slice32(0),
325
+ walletPublicKey.slice32(32)
326
+ );
327
+ bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
328
+
329
+ emit FraudChallengeDefeatTimedOut(walletPubKeyHash, sighash);
330
+ }
331
+
332
+ /// @notice Sets the new value for the `slashingAmount` parameter.
333
+ /// @param _newSlashingAmount the new value for `slashingAmount`
334
+ function setSlashingAmount(Data storage self, uint256 _newSlashingAmount)
335
+ external
336
+ {
337
+ self.slashingAmount = _newSlashingAmount;
338
+ emit FraudSlashingAmountUpdated(_newSlashingAmount);
339
+ }
340
+
341
+ /// @notice Sets the new value for the `notifierRewardMultiplier` parameter.
342
+ /// @param _newNotifierRewardMultiplier the new value for `notifierRewardMultiplier`
343
+ /// @dev The value of `notifierRewardMultiplier` must be <= 100.
344
+ function setNotifierRewardMultiplier(
345
+ Data storage self,
346
+ uint256 _newNotifierRewardMultiplier
347
+ ) external {
348
+ require(
349
+ _newNotifierRewardMultiplier <= 100,
350
+ "Fraud notifier reward multiplier must be <= 100"
351
+ );
352
+ self.notifierRewardMultiplier = _newNotifierRewardMultiplier;
353
+ emit FraudNotifierRewardMultiplierUpdated(_newNotifierRewardMultiplier);
354
+ }
355
+
356
+ /// @notice Sets the new value for the `challengeDefeatTimeout` parameter.
357
+ /// @param _newChallengeDefeatTimeout the new value for `challengeDefeatTimeout`
358
+ /// @dev The value of `challengeDefeatTimeout` must be > 0.
359
+ function setChallengeDefeatTimeout(
360
+ Data storage self,
361
+ uint256 _newChallengeDefeatTimeout
362
+ ) external {
363
+ require(
364
+ _newChallengeDefeatTimeout > 0,
365
+ "Fraud challenge defeat timeout must be > 0"
366
+ );
367
+ self.challengeDefeatTimeout = _newChallengeDefeatTimeout;
368
+ emit FraudChallengeDefeatTimeoutUpdated(_newChallengeDefeatTimeout);
369
+ }
370
+
371
+ /// @notice Sets the new value for the `challengeDepositAmount` parameter.
372
+ /// @param _newChallengeDepositAmount the new value for `challengeDepositAmount`
373
+ /// @dev The value of `challengeDepositAmount` must be > 0.
374
+ function setChallengeDepositAmount(
375
+ Data storage self,
376
+ uint256 _newChallengeDepositAmount
377
+ ) external {
378
+ require(
379
+ _newChallengeDepositAmount > 0,
380
+ "Fraud challenge deposit amount must be > 0"
381
+ );
382
+ self.challengeDepositAmount = _newChallengeDepositAmount;
383
+ emit FraudChallengeDepositAmountUpdated(_newChallengeDepositAmount);
384
+ }
385
+
386
+ /// @notice Extracts the UTXO keys from the given preimage used during
387
+ /// signing of a witness input.
388
+ /// @param preimage The preimage which produces sighash used to generate the
389
+ /// ECDSA signature that is the subject of the fraud claim. It is a
390
+ /// serialized subset of the transaction. The exact subset used as
391
+ /// the preimage depends on the transaction input the signature is
392
+ /// produced for. See BIP-143 for reference
393
+ /// @return utxoKey UTXO key that identifies spent input.
394
+ function extractUtxoKeyFromWitnessPreimage(bytes calldata preimage)
395
+ internal
396
+ pure
397
+ returns (uint256 utxoKey)
398
+ {
399
+ // The expected structure of the preimage created during signing of a
400
+ // witness input:
401
+ // - transaction version (4 bytes)
402
+ // - hash of previous outpoints of all inputs (32 bytes)
403
+ // - hash of sequences of all inputs (32 bytes)
404
+ // - outpoint (hash + index) of the input being signed (36 bytes)
405
+ // - the unlocking script of the input (variable length)
406
+ // - value of the outpoint (8 bytes)
407
+ // - sequence of the input being signed (4 bytes)
408
+ // - hash of all outputs (32 bytes)
409
+ // - transaction locktime (4 bytes)
410
+ // - sighash type (4 bytes)
411
+
412
+ // See Bitcoin's BIP-143 for reference:
413
+ // https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki.
414
+
415
+ // The outpoint (hash and index) is located at the constant offset of
416
+ // 68 (4 + 32 + 32).
417
+ bytes32 outpointTxHash = preimage.extractInputTxIdLeAt(68);
418
+ uint32 outpointIndex = BTCUtils.reverseUint32(
419
+ uint32(preimage.extractTxIndexLeAt(68))
420
+ );
421
+
422
+ return
423
+ uint256(keccak256(abi.encodePacked(outpointTxHash, outpointIndex)));
424
+ }
425
+
426
+ /// @notice Extracts the UTXO key from the given preimage used during
427
+ /// signing of a non-witness input.
428
+ /// @param preimage The preimage which produces sighash used to generate the
429
+ /// ECDSA signature that is the subject of the fraud claim. It is a
430
+ /// serialized subset of the transaction. The exact subset used as
431
+ /// the preimage depends on the transaction input the signature is
432
+ /// produced for. See BIP-143 for reference
433
+ /// @return utxoKey UTXO key that identifies spent input.
434
+ function extractUtxoKeyFromNonWitnessPreimage(bytes calldata preimage)
435
+ internal
436
+ pure
437
+ returns (uint256 utxoKey)
438
+ {
439
+ // The expected structure of the preimage created during signing of a
440
+ // non-witness input:
441
+ // - transaction version (4 bytes)
442
+ // - number of inputs written as compactSize uint (1 byte, 3 bytes,
443
+ // 5 bytes or 9 bytes)
444
+ // - for each input
445
+ // - outpoint (hash and index) (36 bytes)
446
+ // - unlocking script for the input being signed (variable length)
447
+ // or `00` for all other inputs (1 byte)
448
+ // - input sequence (4 bytes)
449
+ // - number of outputs written as compactSize uint (1 byte, 3 bytes,
450
+ // 5 bytes or 9 bytes)
451
+ // - outputs (variable length)
452
+ // - transaction locktime (4 bytes)
453
+ // - sighash type (4 bytes)
454
+
455
+ // See example for reference:
456
+ // https://en.bitcoin.it/wiki/OP_CHECKSIG#Code_samples_and_raw_dumps.
457
+
458
+ // The input data begins at the constant offset of 4 (the first 4 bytes
459
+ // are for the transaction version).
460
+ (uint256 inputsCompactSizeUintLength, uint256 inputsCount) = preimage
461
+ .parseVarIntAt(4);
462
+
463
+ // To determine the first input starting index, we must jump 4 bytes
464
+ // over the transaction version length and the compactSize uint which
465
+ // prepends the input vector. One byte must be added because
466
+ // `BtcUtils.parseVarInt` does not include compactSize uint tag in the
467
+ // returned length.
468
+ //
469
+ // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`
470
+ // returns `0`, so we jump over one byte of compactSize uint.
471
+ //
472
+ // For >= 253 && <= 0xffff there is `0xfd` tag,
473
+ // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no
474
+ // tag byte included) so we need to jump over 1+2 bytes of
475
+ // compactSize uint.
476
+ //
477
+ // Please refer `BTCUtils` library and compactSize uint
478
+ // docs in `BitcoinTx` library for more details.
479
+ uint256 inputStartingIndex = 4 + 1 + inputsCompactSizeUintLength;
480
+
481
+ for (uint256 i = 0; i < inputsCount; i++) {
482
+ uint256 inputLength = preimage.determineInputLengthAt(
483
+ inputStartingIndex
484
+ );
485
+
486
+ (, uint256 scriptSigLength) = preimage.extractScriptSigLenAt(
487
+ inputStartingIndex
488
+ );
489
+
490
+ if (scriptSigLength > 0) {
491
+ // The input this preimage was generated for was found.
492
+ // All the other inputs in the preimage are marked with a null
493
+ // scriptSig ("00") which has length of 1.
494
+ bytes32 outpointTxHash = preimage.extractInputTxIdLeAt(
495
+ inputStartingIndex
496
+ );
497
+ uint32 outpointIndex = BTCUtils.reverseUint32(
498
+ uint32(preimage.extractTxIndexLeAt(inputStartingIndex))
499
+ );
500
+
501
+ utxoKey = uint256(
502
+ keccak256(abi.encodePacked(outpointTxHash, outpointIndex))
503
+ );
504
+
505
+ break;
506
+ }
507
+
508
+ inputStartingIndex += inputLength;
509
+ }
510
+
511
+ return utxoKey;
512
+ }
513
+
514
+ /// @notice Extracts the sighash type from the given preimage.
515
+ /// @param preimage Serialized subset of the transaction. See BIP-143 for
516
+ /// reference
517
+ /// @dev Sighash type is stored as the last 4 bytes in the preimage (little
518
+ /// endian).
519
+ /// @return sighashType Sighash type as a 32-bit integer.
520
+ function extractSighashType(bytes calldata preimage)
521
+ internal
522
+ pure
523
+ returns (uint32 sighashType)
524
+ {
525
+ bytes4 sighashTypeBytes = preimage.slice4(preimage.length - 4);
526
+ uint32 sighashTypeLE = uint32(sighashTypeBytes);
527
+ return sighashTypeLE.reverseUint32();
528
+ }
529
+ }
@@ -0,0 +1,28 @@
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
+ /// @title Interface for the Bitcoin relay
19
+ /// @notice Contains only the methods needed by tBTC v2. The Bitcoin relay
20
+ /// provides the difficulty of the previous and current epoch. One
21
+ /// difficulty epoch spans 2016 blocks.
22
+ interface IRelay {
23
+ /// @notice Returns the difficulty of the current epoch.
24
+ function getCurrentEpochDifficulty() external view returns (uint256);
25
+
26
+ /// @notice Returns the difficulty of the previous epoch.
27
+ function getPrevEpochDifficulty() external view returns (uint256);
28
+ }