@keep-network/tbtc-v2 0.1.1-dev.5 → 0.1.1-dev.52

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 (95) hide show
  1. package/README.adoc +12 -0
  2. package/artifacts/Bank.json +752 -0
  3. package/artifacts/Bridge.json +3307 -0
  4. package/artifacts/Deposit.json +117 -0
  5. package/artifacts/EcdsaDkgValidator.json +532 -0
  6. package/artifacts/EcdsaInactivity.json +156 -0
  7. package/artifacts/Fraud.json +154 -0
  8. package/artifacts/KeepRegistry.json +99 -0
  9. package/artifacts/KeepStake.json +286 -0
  10. package/artifacts/KeepToken.json +711 -0
  11. package/artifacts/KeepTokenStaking.json +483 -0
  12. package/artifacts/MovingFunds.json +160 -0
  13. package/artifacts/NuCypherStakingEscrow.json +256 -0
  14. package/artifacts/NuCypherToken.json +711 -0
  15. package/artifacts/RandomBeaconStub.json +141 -0
  16. package/artifacts/Redemption.json +161 -0
  17. package/artifacts/ReimbursementPool.json +509 -0
  18. package/artifacts/Relay.json +123 -0
  19. package/artifacts/SortitionPool.json +944 -0
  20. package/artifacts/Sweep.json +76 -0
  21. package/artifacts/T.json +1148 -0
  22. package/artifacts/TBTC.json +27 -26
  23. package/artifacts/TBTCToken.json +27 -26
  24. package/artifacts/TokenStaking.json +2288 -0
  25. package/artifacts/TokenholderGovernor.json +1795 -0
  26. package/artifacts/TokenholderTimelock.json +1058 -0
  27. package/artifacts/VendingMachine.json +30 -29
  28. package/artifacts/VendingMachineKeep.json +400 -0
  29. package/artifacts/VendingMachineNuCypher.json +400 -0
  30. package/artifacts/WalletRegistry.json +2709 -0
  31. package/artifacts/WalletRegistryGovernance.json +2364 -0
  32. package/artifacts/Wallets.json +186 -0
  33. package/artifacts/solcInputs/dcf2fc19e497270603c798faeb0a9ba9.json +218 -0
  34. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  35. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.json +2 -2
  36. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  37. package/build/contracts/bank/Bank.sol/Bank.json +20 -2
  38. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
  39. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
  40. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  41. package/build/contracts/bridge/Bridge.sol/Bridge.json +2114 -77
  42. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  43. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +172 -0
  44. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  45. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  46. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
  47. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
  48. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
  49. package/build/contracts/bridge/Fraud.sol/Fraud.json +86 -0
  50. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
  51. package/build/contracts/bridge/IRelay.sol/IRelay.json +37 -0
  52. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +4 -0
  53. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +81 -0
  54. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
  55. package/build/contracts/bridge/Redemption.sol/OutboundTx.json +10 -0
  56. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
  57. package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
  58. package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +4 -0
  59. package/build/contracts/bridge/Sweep.sol/Sweep.json +30 -0
  60. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  61. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
  62. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
  63. package/build/contracts/bridge/Wallets.sol/Wallets.json +112 -0
  64. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  65. package/build/contracts/token/TBTC.sol/TBTC.json +2 -2
  66. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  67. package/build/contracts/vault/IVault.sol/IVault.json +19 -1
  68. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  69. package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +36 -18
  70. package/contracts/GovernanceUtils.sol +1 -1
  71. package/contracts/bank/Bank.sol +34 -18
  72. package/contracts/bridge/BitcoinTx.sol +234 -0
  73. package/contracts/bridge/Bridge.sol +1298 -126
  74. package/contracts/bridge/BridgeState.sol +553 -0
  75. package/contracts/bridge/Deposit.sol +266 -0
  76. package/contracts/bridge/EcdsaLib.sol +45 -0
  77. package/contracts/bridge/Fraud.sol +504 -0
  78. package/contracts/bridge/IRelay.sol +28 -0
  79. package/contracts/bridge/MovingFunds.sol +511 -0
  80. package/contracts/bridge/Redemption.sol +846 -0
  81. package/contracts/bridge/Sweep.sol +514 -0
  82. package/contracts/bridge/VendingMachine.sol +1 -1
  83. package/contracts/bridge/Wallets.sol +552 -0
  84. package/contracts/token/TBTC.sol +1 -1
  85. package/contracts/vault/IVault.sol +32 -10
  86. package/contracts/vault/TBTCVault.sol +20 -2
  87. package/deploy/00_resolve_relay.ts +28 -0
  88. package/deploy/04_deploy_bank.ts +27 -0
  89. package/deploy/05_deploy_bridge.ts +64 -0
  90. package/deploy/06_bank_update_bridge.ts +19 -0
  91. package/deploy/07_transfer_ownership.ts +15 -0
  92. package/deploy/08_transfer_governance.ts +20 -0
  93. package/export.json +15194 -472
  94. package/package.json +27 -23
  95. package/artifacts/solcInputs/d71966212a658480bad5748ad85b1396.json +0 -116
@@ -0,0 +1,504 @@
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
+
22
+ import "./BitcoinTx.sol";
23
+ import "./EcdsaLib.sol";
24
+ import "./BridgeState.sol";
25
+ import "./Wallets.sol";
26
+
27
+ /// @title Bridge fraud
28
+ /// @notice The library handles the logic for challenging Bridge wallets that
29
+ /// committed fraud.
30
+ /// @dev Anyone can submit a fraud challenge indicating that a UTXO being under
31
+ /// the wallet control was unlocked by the wallet but was not used
32
+ /// according to the protocol rules. That means the wallet signed
33
+ /// a transaction input pointing to that UTXO and there is a unique
34
+ /// sighash and signature pair associated with that input.
35
+ ///
36
+ /// In order to defeat the challenge, the same wallet public key and
37
+ /// signature must be provided as were used to calculate the sighash during
38
+ /// the challenge. The wallet provides the preimage which produces sighash
39
+ /// used to generate the ECDSA signature that is the subject of the fraud
40
+ /// claim. The fraud challenge defeat attempt will only succeed if the
41
+ /// inputs in the preimage are considered honestly spent by the wallet.
42
+ /// Therefore the transaction spending the UTXO must be proven in the
43
+ /// Bridge before a challenge defeat is called.
44
+ library Fraud {
45
+ using Wallets for BridgeState.Storage;
46
+
47
+ using BytesLib for bytes;
48
+ using BTCUtils for bytes;
49
+ using BTCUtils for uint32;
50
+ using EcdsaLib for bytes;
51
+
52
+ struct FraudChallenge {
53
+ // The address of the party challenging the wallet.
54
+ address challenger;
55
+ // The amount of ETH the challenger deposited.
56
+ uint256 depositAmount;
57
+ // The timestamp the challenge was submitted at.
58
+ uint32 reportedAt;
59
+ // The flag indicating whether the challenge has been resolved.
60
+ bool resolved;
61
+ }
62
+
63
+ event FraudChallengeSubmitted(
64
+ bytes20 walletPubKeyHash,
65
+ bytes32 sighash,
66
+ uint8 v,
67
+ bytes32 r,
68
+ bytes32 s
69
+ );
70
+
71
+ event FraudChallengeDefeated(bytes20 walletPubKeyHash, bytes32 sighash);
72
+
73
+ event FraudChallengeDefeatTimedOut(
74
+ bytes20 walletPubKeyHash,
75
+ bytes32 sighash
76
+ );
77
+
78
+ /// @notice Submits a fraud challenge indicating that a UTXO being under
79
+ /// wallet control was unlocked by the wallet but was not used
80
+ /// according to the protocol rules. That means the wallet signed
81
+ /// a transaction input pointing to that UTXO and there is a unique
82
+ /// sighash and signature pair associated with that input. This
83
+ /// function uses those parameters to create a fraud accusation that
84
+ /// proves a given transaction input unlocking the given UTXO was
85
+ /// actually signed by the wallet. This function cannot determine
86
+ /// whether the transaction was actually broadcast and the input was
87
+ /// consumed in a fraudulent way so it just opens a challenge period
88
+ /// during which the wallet can defeat the challenge by submitting
89
+ /// proof of a transaction that consumes the given input according
90
+ /// to protocol rules. To prevent spurious allegations, the caller
91
+ /// must deposit ETH that is returned back upon justified fraud
92
+ /// challenge or confiscated otherwise
93
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
94
+ /// and unprefixed format (64 bytes)
95
+ /// @param sighash The hash that was used to produce the ECDSA signature
96
+ /// that is the subject of the fraud claim. This hash is constructed
97
+ /// by applying double SHA-256 over a serialized subset of the
98
+ /// transaction. The exact subset used as hash preimage depends on
99
+ /// the transaction input the signature is produced for. See BIP-143
100
+ /// for reference
101
+ /// @param signature Bitcoin signature in the R/S/V format
102
+ /// @dev Requirements:
103
+ /// - Wallet behind `walletPublicKey` must be in Live or MovingFunds
104
+ /// or Closing state
105
+ /// - The challenger must send appropriate amount of ETH used as
106
+ /// fraud challenge deposit
107
+ /// - The signature (represented by r, s and v) must be generated by
108
+ /// the wallet behind `walletPublicKey` during signing of `sighash`
109
+ /// - Wallet can be challenged for the given signature only once
110
+ function submitFraudChallenge(
111
+ BridgeState.Storage storage self,
112
+ bytes calldata walletPublicKey,
113
+ bytes32 sighash,
114
+ BitcoinTx.RSVSignature calldata signature
115
+ ) external {
116
+ require(
117
+ msg.value >= self.fraudChallengeDepositAmount,
118
+ "The amount of ETH deposited is too low"
119
+ );
120
+
121
+ require(
122
+ CheckBitcoinSigs.checkSig(
123
+ walletPublicKey,
124
+ sighash,
125
+ signature.v,
126
+ signature.r,
127
+ signature.s
128
+ ),
129
+ "Signature verification failure"
130
+ );
131
+
132
+ bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
133
+ walletPublicKey.slice32(0),
134
+ walletPublicKey.slice32(32)
135
+ );
136
+ bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
137
+
138
+ Wallets.Wallet storage wallet = self.registeredWallets[
139
+ walletPubKeyHash
140
+ ];
141
+
142
+ require(
143
+ wallet.state == Wallets.WalletState.Live ||
144
+ wallet.state == Wallets.WalletState.MovingFunds ||
145
+ wallet.state == Wallets.WalletState.Closing,
146
+ "Wallet must be in Live or MovingFunds or Closing state"
147
+ );
148
+
149
+ uint256 challengeKey = uint256(
150
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
151
+ );
152
+
153
+ FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
154
+ require(challenge.reportedAt == 0, "Fraud challenge already exists");
155
+
156
+ challenge.challenger = msg.sender;
157
+ challenge.depositAmount = msg.value;
158
+ /* solhint-disable-next-line not-rely-on-time */
159
+ challenge.reportedAt = uint32(block.timestamp);
160
+ challenge.resolved = false;
161
+ // slither-disable-next-line reentrancy-events
162
+ emit FraudChallengeSubmitted(
163
+ walletPubKeyHash,
164
+ sighash,
165
+ signature.v,
166
+ signature.r,
167
+ signature.s
168
+ );
169
+ }
170
+
171
+ /// @notice Allows to defeat a pending fraud challenge against a wallet if
172
+ /// the transaction that spends the UTXO follows the protocol rules.
173
+ /// In order to defeat the challenge the same `walletPublicKey` and
174
+ /// signature (represented by `r`, `s` and `v`) must be provided as
175
+ /// were used to calculate the sighash during input signing.
176
+ /// The fraud challenge defeat attempt will only succeed if the
177
+ /// inputs in the preimage are considered honestly spent by the
178
+ /// wallet. Therefore the transaction spending the UTXO must be
179
+ /// proven in the Bridge before a challenge defeat is called.
180
+ /// If successfully defeated, the fraud challenge is marked as
181
+ /// resolved and the amount of ether deposited by the challenger is
182
+ /// sent to the treasury.
183
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
184
+ /// and unprefixed format (64 bytes)
185
+ /// @param preimage The preimage which produces sighash used to generate the
186
+ /// ECDSA signature that is the subject of the fraud claim. It is a
187
+ /// serialized subset of the transaction. The exact subset used as
188
+ /// the preimage depends on the transaction input the signature is
189
+ /// produced for. See BIP-143 for reference
190
+ /// @param witness Flag indicating whether the preimage was produced for a
191
+ /// witness input. True for witness, false for non-witness input.
192
+ /// @dev Requirements:
193
+ /// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
194
+ /// must identify an open fraud challenge
195
+ /// - the preimage must be a valid preimage of a transaction generated
196
+ /// according to the protocol rules and already proved in the Bridge
197
+ /// - before a defeat attempt is made the transaction that spends the
198
+ /// given UTXO must be proven in the Bridge
199
+ function defeatFraudChallenge(
200
+ BridgeState.Storage storage self,
201
+ bytes calldata walletPublicKey,
202
+ bytes calldata preimage,
203
+ bool witness
204
+ ) external {
205
+ bytes32 sighash = preimage.hash256();
206
+
207
+ uint256 challengeKey = uint256(
208
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
209
+ );
210
+
211
+ FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
212
+
213
+ require(challenge.reportedAt > 0, "Fraud challenge does not exist");
214
+ require(
215
+ !challenge.resolved,
216
+ "Fraud challenge has already been resolved"
217
+ );
218
+
219
+ // Ensure SIGHASH_ALL type was used during signing, which is represented
220
+ // by type value `1`.
221
+ require(extractSighashType(preimage) == 1, "Wrong sighash type");
222
+
223
+ uint256 utxoKey = witness
224
+ ? extractUtxoKeyFromWitnessPreimage(preimage)
225
+ : extractUtxoKeyFromNonWitnessPreimage(preimage);
226
+
227
+ // Check that the UTXO key identifies a correctly spent UTXO.
228
+ require(
229
+ self.deposits[utxoKey].sweptAt > 0 || self.spentMainUTXOs[utxoKey],
230
+ "Spent UTXO not found among correctly spent UTXOs"
231
+ );
232
+
233
+ // Mark the challenge as resolved as it was successfully defeated
234
+ challenge.resolved = true;
235
+
236
+ // Send the ether deposited by the challenger to the treasury
237
+ /* solhint-disable avoid-low-level-calls */
238
+ // slither-disable-next-line low-level-calls,unchecked-lowlevel,arbitrary-send
239
+ self.treasury.call{gas: 100000, value: challenge.depositAmount}("");
240
+ /* solhint-enable avoid-low-level-calls */
241
+
242
+ bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
243
+ walletPublicKey.slice32(0),
244
+ walletPublicKey.slice32(32)
245
+ );
246
+ bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
247
+ // slither-disable-next-line reentrancy-events
248
+ emit FraudChallengeDefeated(walletPubKeyHash, sighash);
249
+ }
250
+
251
+ /// @notice Notifies about defeat timeout for the given fraud challenge.
252
+ /// Can be called only if there was a fraud challenge identified by
253
+ /// the provided `walletPublicKey` and `sighash` and it was not
254
+ /// defeated on time. The amount of time that needs to pass after a
255
+ /// fraud challenge is reported is indicated by the
256
+ /// `challengeDefeatTimeout`. After a successful fraud challenge
257
+ /// defeat timeout notification the fraud challenge is marked as
258
+ /// resolved, the stake of each operator is slashed, the ether
259
+ /// deposited is returned to the challenger and the challenger is
260
+ /// rewarded.
261
+ /// @param walletPublicKey The public key of the wallet in the uncompressed
262
+ /// and unprefixed format (64 bytes)
263
+ /// @param walletMembersIDs Identifiers of the wallet signing group members
264
+ /// @param sighash The hash that was used to produce the ECDSA signature
265
+ /// that is the subject of the fraud claim. This hash is constructed
266
+ /// by applying double SHA-256 over a serialized subset of the
267
+ /// transaction. The exact subset used as hash preimage depends on
268
+ /// the transaction input the signature is produced for. See BIP-143
269
+ /// for reference
270
+ /// @dev Requirements:
271
+ /// - The wallet must be in the Live or MovingFunds or Closing or
272
+ /// Terminated state
273
+ /// - The `walletPublicKey` and `sighash` must identify an open fraud
274
+ /// challenge
275
+ /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
276
+ /// be exactly the same as the hash stored under `membersIdsHash`
277
+ /// for the given `walletID`. Those IDs are not directly stored
278
+ /// in the contract for gas efficiency purposes but they can be
279
+ /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`
280
+ /// events.
281
+ /// - The amount of time indicated by `challengeDefeatTimeout` must pass
282
+ /// after the challenge was reported
283
+ function notifyFraudChallengeDefeatTimeout(
284
+ BridgeState.Storage storage self,
285
+ bytes calldata walletPublicKey,
286
+ uint32[] calldata walletMembersIDs,
287
+ bytes32 sighash
288
+ ) external {
289
+ uint256 challengeKey = uint256(
290
+ keccak256(abi.encodePacked(walletPublicKey, sighash))
291
+ );
292
+
293
+ FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
294
+
295
+ require(challenge.reportedAt > 0, "Fraud challenge does not exist");
296
+
297
+ require(
298
+ !challenge.resolved,
299
+ "Fraud challenge has already been resolved"
300
+ );
301
+
302
+ require(
303
+ /* solhint-disable-next-line not-rely-on-time */
304
+ block.timestamp >=
305
+ challenge.reportedAt + self.fraudChallengeDefeatTimeout,
306
+ "Fraud challenge defeat period did not time out yet"
307
+ );
308
+
309
+ challenge.resolved = true;
310
+ // Return the ether deposited by the challenger
311
+ /* solhint-disable avoid-low-level-calls */
312
+ // slither-disable-next-line low-level-calls,unchecked-lowlevel
313
+ challenge.challenger.call{gas: 100000, value: challenge.depositAmount}(
314
+ ""
315
+ );
316
+ /* solhint-enable avoid-low-level-calls */
317
+
318
+ bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
319
+ walletPublicKey.slice32(0),
320
+ walletPublicKey.slice32(32)
321
+ );
322
+ bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
323
+
324
+ Wallets.Wallet storage wallet = self.registeredWallets[
325
+ walletPubKeyHash
326
+ ];
327
+
328
+ Wallets.WalletState walletState = wallet.state;
329
+
330
+ if (
331
+ walletState == Wallets.WalletState.Live ||
332
+ walletState == Wallets.WalletState.MovingFunds ||
333
+ walletState == Wallets.WalletState.Closing
334
+ ) {
335
+ self.terminateWallet(walletPubKeyHash);
336
+
337
+ self.ecdsaWalletRegistry.seize(
338
+ self.fraudSlashingAmount,
339
+ self.fraudNotifierRewardMultiplier,
340
+ challenge.challenger,
341
+ wallet.ecdsaWalletID,
342
+ walletMembersIDs
343
+ );
344
+ } else if (walletState == Wallets.WalletState.Terminated) {
345
+ // This is a special case when the wallet was already terminated
346
+ // due to a previous deliberate protocol violation. In that
347
+ // case, this function should be still callable for other fraud
348
+ // challenges timeouts in order to let the challenger unlock its
349
+ // ETH deposit back. However, the wallet termination logic is
350
+ // not called and the challenger is not rewarded.
351
+ } else {
352
+ revert(
353
+ "Wallet must be in Live or MovingFunds or Closing or Terminated state"
354
+ );
355
+ }
356
+
357
+ // slither-disable-next-line reentrancy-events
358
+ emit FraudChallengeDefeatTimedOut(walletPubKeyHash, sighash);
359
+ }
360
+
361
+ /// @notice Extracts the UTXO keys from the given preimage used during
362
+ /// signing of a witness input.
363
+ /// @param preimage The preimage which produces sighash used to generate the
364
+ /// ECDSA signature that is the subject of the fraud claim. It is a
365
+ /// serialized subset of the transaction. The exact subset used as
366
+ /// the preimage depends on the transaction input the signature is
367
+ /// produced for. See BIP-143 for reference
368
+ /// @return utxoKey UTXO key that identifies spent input.
369
+ function extractUtxoKeyFromWitnessPreimage(bytes calldata preimage)
370
+ internal
371
+ pure
372
+ returns (uint256 utxoKey)
373
+ {
374
+ // The expected structure of the preimage created during signing of a
375
+ // witness input:
376
+ // - transaction version (4 bytes)
377
+ // - hash of previous outpoints of all inputs (32 bytes)
378
+ // - hash of sequences of all inputs (32 bytes)
379
+ // - outpoint (hash + index) of the input being signed (36 bytes)
380
+ // - the unlocking script of the input (variable length)
381
+ // - value of the outpoint (8 bytes)
382
+ // - sequence of the input being signed (4 bytes)
383
+ // - hash of all outputs (32 bytes)
384
+ // - transaction locktime (4 bytes)
385
+ // - sighash type (4 bytes)
386
+
387
+ // See Bitcoin's BIP-143 for reference:
388
+ // https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki.
389
+
390
+ // The outpoint (hash and index) is located at the constant offset of
391
+ // 68 (4 + 32 + 32).
392
+ bytes32 outpointTxHash = preimage.extractInputTxIdLeAt(68);
393
+ uint32 outpointIndex = BTCUtils.reverseUint32(
394
+ uint32(preimage.extractTxIndexLeAt(68))
395
+ );
396
+
397
+ return
398
+ uint256(keccak256(abi.encodePacked(outpointTxHash, outpointIndex)));
399
+ }
400
+
401
+ /// @notice Extracts the UTXO key from the given preimage used during
402
+ /// signing of a non-witness input.
403
+ /// @param preimage The preimage which produces sighash used to generate the
404
+ /// ECDSA signature that is the subject of the fraud claim. It is a
405
+ /// serialized subset of the transaction. The exact subset used as
406
+ /// the preimage depends on the transaction input the signature is
407
+ /// produced for. See BIP-143 for reference
408
+ /// @return utxoKey UTXO key that identifies spent input.
409
+ function extractUtxoKeyFromNonWitnessPreimage(bytes calldata preimage)
410
+ internal
411
+ pure
412
+ returns (uint256 utxoKey)
413
+ {
414
+ // The expected structure of the preimage created during signing of a
415
+ // non-witness input:
416
+ // - transaction version (4 bytes)
417
+ // - number of inputs written as compactSize uint (1 byte, 3 bytes,
418
+ // 5 bytes or 9 bytes)
419
+ // - for each input
420
+ // - outpoint (hash and index) (36 bytes)
421
+ // - unlocking script for the input being signed (variable length)
422
+ // or `00` for all other inputs (1 byte)
423
+ // - input sequence (4 bytes)
424
+ // - number of outputs written as compactSize uint (1 byte, 3 bytes,
425
+ // 5 bytes or 9 bytes)
426
+ // - outputs (variable length)
427
+ // - transaction locktime (4 bytes)
428
+ // - sighash type (4 bytes)
429
+
430
+ // See example for reference:
431
+ // https://en.bitcoin.it/wiki/OP_CHECKSIG#Code_samples_and_raw_dumps.
432
+
433
+ // The input data begins at the constant offset of 4 (the first 4 bytes
434
+ // are for the transaction version).
435
+ (uint256 inputsCompactSizeUintLength, uint256 inputsCount) = preimage
436
+ .parseVarIntAt(4);
437
+
438
+ // To determine the first input starting index, we must jump 4 bytes
439
+ // over the transaction version length and the compactSize uint which
440
+ // prepends the input vector. One byte must be added because
441
+ // `BtcUtils.parseVarInt` does not include compactSize uint tag in the
442
+ // returned length.
443
+ //
444
+ // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`
445
+ // returns `0`, so we jump over one byte of compactSize uint.
446
+ //
447
+ // For >= 253 && <= 0xffff there is `0xfd` tag,
448
+ // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no
449
+ // tag byte included) so we need to jump over 1+2 bytes of
450
+ // compactSize uint.
451
+ //
452
+ // Please refer `BTCUtils` library and compactSize uint
453
+ // docs in `BitcoinTx` library for more details.
454
+ uint256 inputStartingIndex = 4 + 1 + inputsCompactSizeUintLength;
455
+
456
+ for (uint256 i = 0; i < inputsCount; i++) {
457
+ uint256 inputLength = preimage.determineInputLengthAt(
458
+ inputStartingIndex
459
+ );
460
+
461
+ (, uint256 scriptSigLength) = preimage.extractScriptSigLenAt(
462
+ inputStartingIndex
463
+ );
464
+
465
+ if (scriptSigLength > 0) {
466
+ // The input this preimage was generated for was found.
467
+ // All the other inputs in the preimage are marked with a null
468
+ // scriptSig ("00") which has length of 1.
469
+ bytes32 outpointTxHash = preimage.extractInputTxIdLeAt(
470
+ inputStartingIndex
471
+ );
472
+ uint32 outpointIndex = BTCUtils.reverseUint32(
473
+ uint32(preimage.extractTxIndexLeAt(inputStartingIndex))
474
+ );
475
+
476
+ utxoKey = uint256(
477
+ keccak256(abi.encodePacked(outpointTxHash, outpointIndex))
478
+ );
479
+
480
+ break;
481
+ }
482
+
483
+ inputStartingIndex += inputLength;
484
+ }
485
+
486
+ return utxoKey;
487
+ }
488
+
489
+ /// @notice Extracts the sighash type from the given preimage.
490
+ /// @param preimage Serialized subset of the transaction. See BIP-143 for
491
+ /// reference
492
+ /// @dev Sighash type is stored as the last 4 bytes in the preimage (little
493
+ /// endian).
494
+ /// @return sighashType Sighash type as a 32-bit integer.
495
+ function extractSighashType(bytes calldata preimage)
496
+ internal
497
+ pure
498
+ returns (uint32 sighashType)
499
+ {
500
+ bytes4 sighashTypeBytes = preimage.slice4(preimage.length - 4);
501
+ uint32 sighashTypeLE = uint32(sighashTypeBytes);
502
+ return sighashTypeLE.reverseUint32();
503
+ }
504
+ }
@@ -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
+ }