@keep-network/tbtc-v2 0.1.1-dev.21 → 0.1.1-dev.22

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.
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/e673cc02819030f1193719954ceaf337.json"
3
+ "buildInfo": "../../../build-info/62f38619f9d2a8c4872f2e3972501896.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/e673cc02819030f1193719954ceaf337.json"
3
+ "buildInfo": "../../../build-info/62f38619f9d2a8c4872f2e3972501896.json"
4
4
  }
@@ -15,6 +15,9 @@
15
15
 
16
16
  pragma solidity ^0.8.9;
17
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
+
18
21
  /// @title Bitcoin transaction
19
22
  /// @notice Allows to reference Bitcoin raw transaction in Solidity.
20
23
  /// @dev See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format
@@ -69,8 +72,12 @@ pragma solidity ^0.8.9;
69
72
  /// (*) compactSize uint is often references as VarInt)
70
73
  ///
71
74
  library BitcoinTx {
72
- /// @notice Represents Bitcoin transaction data for funding BTC deposit
73
- /// P2(W)SH transaction.
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.
74
81
  struct Info {
75
82
  /// @notice Bitcoin transaction version
76
83
  /// @dev `version` from raw Bitcon transaction data.
@@ -113,6 +120,17 @@ library BitcoinTx {
113
120
  bytes bitcoinHeaders;
114
121
  }
115
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
+
116
134
  /// @notice Represents info about an unspent transaction output.
117
135
  struct UTXO {
118
136
  /// @notice Hash of the transaction the output belongs to.
@@ -123,4 +141,91 @@ library BitcoinTx {
123
141
  /// @notice Value of the transaction output.
124
142
  uint64 txOutputValue;
125
143
  }
144
+
145
+ /// @notice Validates the SPV proof of the Bitcoin transaction.
146
+ /// Reverts in case the validation or proof verification fail.
147
+ /// @param txInfo Bitcoin transaction data
148
+ /// @param proof Bitcoin proof data
149
+ /// @param proofDifficulty Bitcoin proof difficulty context.
150
+ /// @return txHash Proven 32-byte transaction hash.
151
+ function validateProof(
152
+ Info calldata txInfo,
153
+ Proof calldata proof,
154
+ ProofDifficulty calldata proofDifficulty
155
+ ) external view returns (bytes32 txHash) {
156
+ require(
157
+ txInfo.inputVector.validateVin(),
158
+ "Invalid input vector provided"
159
+ );
160
+ require(
161
+ txInfo.outputVector.validateVout(),
162
+ "Invalid output vector provided"
163
+ );
164
+
165
+ txHash = abi
166
+ .encodePacked(
167
+ txInfo.version,
168
+ txInfo.inputVector,
169
+ txInfo.outputVector,
170
+ txInfo.locktime
171
+ )
172
+ .hash256View();
173
+
174
+ require(
175
+ txHash.prove(
176
+ proof.bitcoinHeaders.extractMerkleRootLE(),
177
+ proof.merkleProof,
178
+ proof.txIndexInBlock
179
+ ),
180
+ "Tx merkle proof is not valid for provided header and tx hash"
181
+ );
182
+
183
+ evaluateProofDifficulty(proof.bitcoinHeaders, proofDifficulty);
184
+
185
+ return txHash;
186
+ }
187
+
188
+ /// @notice Evaluates the given Bitcoin proof difficulty against the actual
189
+ /// Bitcoin chain difficulty provided by the relay oracle.
190
+ /// Reverts in case the evaluation fails.
191
+ /// @param bitcoinHeaders Bitcoin headers chain being part of the SPV
192
+ /// proof. Used to extract the observed proof difficulty
193
+ /// @param proofDifficulty Bitcoin proof difficulty context.
194
+ function evaluateProofDifficulty(
195
+ bytes memory bitcoinHeaders,
196
+ ProofDifficulty calldata proofDifficulty
197
+ ) internal view {
198
+ uint256 requestedDiff = 0;
199
+ uint256 firstHeaderDiff = bitcoinHeaders
200
+ .extractTarget()
201
+ .calculateDifficulty();
202
+
203
+ if (firstHeaderDiff == proofDifficulty.currentEpochDifficulty) {
204
+ requestedDiff = proofDifficulty.currentEpochDifficulty;
205
+ } else if (firstHeaderDiff == proofDifficulty.previousEpochDifficulty) {
206
+ requestedDiff = proofDifficulty.previousEpochDifficulty;
207
+ } else {
208
+ revert("Not at current or previous difficulty");
209
+ }
210
+
211
+ uint256 observedDiff = bitcoinHeaders.validateHeaderChain();
212
+
213
+ require(
214
+ observedDiff != ValidateSPV.getErrBadLength(),
215
+ "Invalid length of the headers chain"
216
+ );
217
+ require(
218
+ observedDiff != ValidateSPV.getErrInvalidChain(),
219
+ "Invalid headers chain"
220
+ );
221
+ require(
222
+ observedDiff != ValidateSPV.getErrLowWork(),
223
+ "Insufficient work in a header"
224
+ );
225
+
226
+ require(
227
+ observedDiff >= requestedDiff * proofDifficulty.difficultyFactor,
228
+ "Insufficient accumulated difficulty in header chain"
229
+ );
230
+ }
126
231
  }
@@ -19,7 +19,6 @@ import "@openzeppelin/contracts/access/Ownable.sol";
19
19
 
20
20
  import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
21
21
  import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
22
- import {ValidateSPV} from "@keep-network/bitcoin-spv-sol/contracts/ValidateSPV.sol";
23
22
 
24
23
  import "../bank/Bank.sol";
25
24
  import "./BitcoinTx.sol";
@@ -59,8 +58,6 @@ contract Bridge is Ownable {
59
58
  using BTCUtils for bytes;
60
59
  using BTCUtils for uint256;
61
60
  using BytesLib for bytes;
62
- using ValidateSPV for bytes;
63
- using ValidateSPV for bytes32;
64
61
 
65
62
  /// @notice Represents data which must be revealed by the depositor during
66
63
  /// deposit reveal.
@@ -413,6 +410,22 @@ contract Bridge is Ownable {
413
410
  emit VaultStatusUpdated(vault, isTrusted);
414
411
  }
415
412
 
413
+ /// @notice Determines the current Bitcoin SPV proof difficulty context.
414
+ /// @return proofDifficulty Bitcoin proof difficulty context.
415
+ function proofDifficultyContext()
416
+ internal
417
+ view
418
+ returns (BitcoinTx.ProofDifficulty memory proofDifficulty)
419
+ {
420
+ proofDifficulty.currentEpochDifficulty = relay
421
+ .getCurrentEpochDifficulty();
422
+ proofDifficulty.previousEpochDifficulty = relay
423
+ .getPrevEpochDifficulty();
424
+ proofDifficulty.difficultyFactor = txProofDifficultyFactor;
425
+
426
+ return proofDifficulty;
427
+ }
428
+
416
429
  /// @notice Used by the depositor to reveal information about their P2(W)SH
417
430
  /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain
418
431
  /// wallet listens for revealed deposit events and may decide to
@@ -608,7 +621,11 @@ contract Bridge is Ownable {
608
621
  // can assume the transaction happened on Bitcoin chain and has
609
622
  // a sufficient number of confirmations as determined by
610
623
  // `txProofDifficultyFactor` constant.
611
- bytes32 sweepTxHash = validateBitcoinTxProof(sweepTx, sweepProof);
624
+ bytes32 sweepTxHash = BitcoinTx.validateProof(
625
+ sweepTx,
626
+ sweepProof,
627
+ proofDifficultyContext()
628
+ );
612
629
 
613
630
  // Process sweep transaction output and extract its target wallet
614
631
  // public key hash and value.
@@ -712,103 +729,6 @@ contract Bridge is Ownable {
712
729
  // TODO: Handle deposits having `vault` set.
713
730
  }
714
731
 
715
- /// @notice Validates the SPV proof of the Bitcoin transaction.
716
- /// Reverts in case the validation or proof verification fail.
717
- /// @param txInfo Bitcoin transaction data
718
- /// @param proof Bitcoin proof data
719
- /// @return txHash Proven 32-byte transaction hash.
720
- function validateBitcoinTxProof(
721
- BitcoinTx.Info calldata txInfo,
722
- BitcoinTx.Proof calldata proof
723
- ) internal view returns (bytes32 txHash) {
724
- require(
725
- txInfo.inputVector.validateVin(),
726
- "Invalid input vector provided"
727
- );
728
- require(
729
- txInfo.outputVector.validateVout(),
730
- "Invalid output vector provided"
731
- );
732
-
733
- txHash = abi
734
- .encodePacked(
735
- txInfo.version,
736
- txInfo.inputVector,
737
- txInfo.outputVector,
738
- txInfo.locktime
739
- )
740
- .hash256View();
741
-
742
- checkProofFromTxHash(txHash, proof);
743
-
744
- return txHash;
745
- }
746
-
747
- /// @notice Checks the given Bitcoin transaction hash against the SPV proof.
748
- /// Reverts in case the check fails.
749
- /// @param txHash 32-byte hash of the checked Bitcoin transaction
750
- /// @param proof Bitcoin proof data
751
- function checkProofFromTxHash(
752
- bytes32 txHash,
753
- BitcoinTx.Proof calldata proof
754
- ) internal view {
755
- require(
756
- txHash.prove(
757
- proof.bitcoinHeaders.extractMerkleRootLE(),
758
- proof.merkleProof,
759
- proof.txIndexInBlock
760
- ),
761
- "Tx merkle proof is not valid for provided header and tx hash"
762
- );
763
-
764
- evaluateProofDifficulty(proof.bitcoinHeaders);
765
- }
766
-
767
- /// @notice Evaluates the given Bitcoin proof difficulty against the actual
768
- /// Bitcoin chain difficulty provided by the relay oracle.
769
- /// Reverts in case the evaluation fails.
770
- /// @param bitcoinHeaders Bitcoin headers chain being part of the SPV
771
- /// proof. Used to extract the observed proof difficulty
772
- function evaluateProofDifficulty(bytes memory bitcoinHeaders)
773
- internal
774
- view
775
- {
776
- uint256 requestedDiff = 0;
777
- uint256 currentDiff = relay.getCurrentEpochDifficulty();
778
- uint256 previousDiff = relay.getPrevEpochDifficulty();
779
- uint256 firstHeaderDiff = bitcoinHeaders
780
- .extractTarget()
781
- .calculateDifficulty();
782
-
783
- if (firstHeaderDiff == currentDiff) {
784
- requestedDiff = currentDiff;
785
- } else if (firstHeaderDiff == previousDiff) {
786
- requestedDiff = previousDiff;
787
- } else {
788
- revert("Not at current or previous difficulty");
789
- }
790
-
791
- uint256 observedDiff = bitcoinHeaders.validateHeaderChain();
792
-
793
- require(
794
- observedDiff != ValidateSPV.getErrBadLength(),
795
- "Invalid length of the headers chain"
796
- );
797
- require(
798
- observedDiff != ValidateSPV.getErrInvalidChain(),
799
- "Invalid headers chain"
800
- );
801
- require(
802
- observedDiff != ValidateSPV.getErrLowWork(),
803
- "Insufficient work in a header"
804
- );
805
-
806
- require(
807
- observedDiff >= requestedDiff * txProofDifficultyFactor,
808
- "Insufficient accumulated difficulty in header chain"
809
- );
810
- }
811
-
812
732
  /// @notice Processes the Bitcoin sweep transaction output vector by
813
733
  /// extracting the single output and using it to gain additional
814
734
  /// information required for further processing (e.g. value and
@@ -1271,9 +1191,10 @@ contract Bridge is Ownable {
1271
1191
  // can assume the transaction happened on Bitcoin chain and has
1272
1192
  // a sufficient number of confirmations as determined by
1273
1193
  // `txProofDifficultyFactor` constant.
1274
- bytes32 redemptionTxHash = validateBitcoinTxProof(
1194
+ bytes32 redemptionTxHash = BitcoinTx.validateProof(
1275
1195
  redemptionTx,
1276
- redemptionProof
1196
+ redemptionProof,
1197
+ proofDifficultyContext()
1277
1198
  );
1278
1199
 
1279
1200
  // Perform validation of the redemption transaction input. Specifically,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keep-network/tbtc-v2",
3
- "version": "0.1.1-dev.21+main.c2eacefd26c561583124537f501f8f4afcdee4d7",
3
+ "version": "0.1.1-dev.22+main.c25835616ce50ecd68c4421262fb4462cb716d0e",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "artifacts/",
@@ -49,6 +49,7 @@
49
49
  "ethereum-waffle": "^3.4.0",
50
50
  "ethers": "^5.4.7",
51
51
  "hardhat": "^2.6.4",
52
+ "hardhat-contract-sizer": "^2.5.0",
52
53
  "hardhat-deploy": "^0.8.11",
53
54
  "hardhat-gas-reporter": "^1.0.4",
54
55
  "prettier": "^2.5.1",