@keep-network/tbtc-v2 0.1.1-dev.5 → 0.1.1-dev.50
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.
- package/README.adoc +12 -0
- package/artifacts/Bank.json +752 -0
- package/artifacts/Bridge.json +3271 -0
- package/artifacts/Deposit.json +117 -0
- package/artifacts/EcdsaDkgValidator.json +532 -0
- package/artifacts/EcdsaInactivity.json +156 -0
- package/artifacts/Fraud.json +153 -0
- package/artifacts/KeepRegistry.json +99 -0
- package/artifacts/KeepStake.json +286 -0
- package/artifacts/KeepToken.json +711 -0
- package/artifacts/KeepTokenStaking.json +483 -0
- package/artifacts/MovingFunds.json +160 -0
- package/artifacts/NuCypherStakingEscrow.json +256 -0
- package/artifacts/NuCypherToken.json +711 -0
- package/artifacts/RandomBeaconStub.json +141 -0
- package/artifacts/Redemption.json +161 -0
- package/artifacts/ReimbursementPool.json +509 -0
- package/artifacts/Relay.json +123 -0
- package/artifacts/SortitionPool.json +944 -0
- package/artifacts/Sweep.json +76 -0
- package/artifacts/T.json +1148 -0
- package/artifacts/TBTC.json +27 -26
- package/artifacts/TBTCToken.json +27 -26
- package/artifacts/TokenStaking.json +2288 -0
- package/artifacts/TokenholderGovernor.json +1795 -0
- package/artifacts/TokenholderTimelock.json +1058 -0
- package/artifacts/VendingMachine.json +30 -29
- package/artifacts/VendingMachineKeep.json +400 -0
- package/artifacts/VendingMachineNuCypher.json +400 -0
- package/artifacts/WalletRegistry.json +2709 -0
- package/artifacts/WalletRegistryGovernance.json +2364 -0
- package/artifacts/Wallets.json +186 -0
- package/artifacts/solcInputs/b0c3ed0992bd570aaaee717425c37538.json +218 -0
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.json +2 -2
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.json +20 -2
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.json +2093 -77
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
- package/build/contracts/bridge/BridgeState.sol/BridgeState.json +166 -0
- package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
- package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
- package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
- package/build/contracts/bridge/Fraud.sol/Fraud.json +86 -0
- package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
- package/build/contracts/bridge/IRelay.sol/IRelay.json +37 -0
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +4 -0
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +81 -0
- package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
- package/build/contracts/bridge/Redemption.sol/OutboundTx.json +10 -0
- package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
- package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
- package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +4 -0
- package/build/contracts/bridge/Sweep.sol/Sweep.json +30 -0
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
- package/build/contracts/bridge/Wallets.sol/Wallets.json +112 -0
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/token/TBTC.sol/TBTC.json +2 -2
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.json +19 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +36 -18
- package/contracts/GovernanceUtils.sol +1 -1
- package/contracts/bank/Bank.sol +34 -18
- package/contracts/bridge/BitcoinTx.sol +234 -0
- package/contracts/bridge/Bridge.sol +1274 -126
- package/contracts/bridge/BridgeState.sol +538 -0
- package/contracts/bridge/Deposit.sol +266 -0
- package/contracts/bridge/EcdsaLib.sol +45 -0
- package/contracts/bridge/Fraud.sol +488 -0
- package/contracts/bridge/IRelay.sol +28 -0
- package/contracts/bridge/MovingFunds.sol +511 -0
- package/contracts/bridge/Redemption.sol +846 -0
- package/contracts/bridge/Sweep.sol +514 -0
- package/contracts/bridge/VendingMachine.sol +1 -1
- package/contracts/bridge/Wallets.sol +551 -0
- package/contracts/token/TBTC.sol +1 -1
- package/contracts/vault/IVault.sol +32 -10
- package/contracts/vault/TBTCVault.sol +20 -2
- package/deploy/00_resolve_relay.ts +28 -0
- package/deploy/04_deploy_bank.ts +27 -0
- package/deploy/05_deploy_bridge.ts +64 -0
- package/deploy/06_bank_update_bridge.ts +19 -0
- package/deploy/07_transfer_ownership.ts +15 -0
- package/deploy/08_transfer_governance.ts +20 -0
- package/export.json +15173 -472
- package/package.json +27 -23
- package/artifacts/solcInputs/d71966212a658480bad5748ad85b1396.json +0 -116
|
@@ -0,0 +1,488 @@
|
|
|
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 sighash The hash that was used to produce the ECDSA signature
|
|
264
|
+
/// that is the subject of the fraud claim. This hash is constructed
|
|
265
|
+
/// by applying double SHA-256 over a serialized subset of the
|
|
266
|
+
/// transaction. The exact subset used as hash preimage depends on
|
|
267
|
+
/// the transaction input the signature is produced for. See BIP-143
|
|
268
|
+
/// for reference
|
|
269
|
+
/// @dev Requirements:
|
|
270
|
+
/// - The wallet must be in the Live or MovingFunds or Closing or
|
|
271
|
+
/// Terminated state
|
|
272
|
+
/// - The `walletPublicKey` and `sighash` must identify an open fraud
|
|
273
|
+
/// challenge
|
|
274
|
+
/// - The amount of time indicated by `challengeDefeatTimeout` must pass
|
|
275
|
+
/// after the challenge was reported
|
|
276
|
+
function notifyFraudChallengeDefeatTimeout(
|
|
277
|
+
BridgeState.Storage storage self,
|
|
278
|
+
bytes calldata walletPublicKey,
|
|
279
|
+
bytes32 sighash
|
|
280
|
+
) external {
|
|
281
|
+
uint256 challengeKey = uint256(
|
|
282
|
+
keccak256(abi.encodePacked(walletPublicKey, sighash))
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
FraudChallenge storage challenge = self.fraudChallenges[challengeKey];
|
|
286
|
+
|
|
287
|
+
require(challenge.reportedAt > 0, "Fraud challenge does not exist");
|
|
288
|
+
|
|
289
|
+
require(
|
|
290
|
+
!challenge.resolved,
|
|
291
|
+
"Fraud challenge has already been resolved"
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
require(
|
|
295
|
+
/* solhint-disable-next-line not-rely-on-time */
|
|
296
|
+
block.timestamp >=
|
|
297
|
+
challenge.reportedAt + self.fraudChallengeDefeatTimeout,
|
|
298
|
+
"Fraud challenge defeat period did not time out yet"
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
challenge.resolved = true;
|
|
302
|
+
// Return the ether deposited by the challenger
|
|
303
|
+
/* solhint-disable avoid-low-level-calls */
|
|
304
|
+
// slither-disable-next-line low-level-calls,unchecked-lowlevel
|
|
305
|
+
challenge.challenger.call{gas: 100000, value: challenge.depositAmount}(
|
|
306
|
+
""
|
|
307
|
+
);
|
|
308
|
+
/* solhint-enable avoid-low-level-calls */
|
|
309
|
+
|
|
310
|
+
bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
|
|
311
|
+
walletPublicKey.slice32(0),
|
|
312
|
+
walletPublicKey.slice32(32)
|
|
313
|
+
);
|
|
314
|
+
bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
|
|
315
|
+
Wallets.WalletState walletState = self
|
|
316
|
+
.registeredWallets[walletPubKeyHash]
|
|
317
|
+
.state;
|
|
318
|
+
|
|
319
|
+
if (
|
|
320
|
+
walletState == Wallets.WalletState.Live ||
|
|
321
|
+
walletState == Wallets.WalletState.MovingFunds ||
|
|
322
|
+
walletState == Wallets.WalletState.Closing
|
|
323
|
+
) {
|
|
324
|
+
self.terminateWallet(walletPubKeyHash);
|
|
325
|
+
|
|
326
|
+
// TODO: Perform slashing of the wallet operators, reward the
|
|
327
|
+
// challenger, and add unit tests for that.
|
|
328
|
+
} else if (walletState == Wallets.WalletState.Terminated) {
|
|
329
|
+
// This is a special case when the wallet was already terminated
|
|
330
|
+
// due to a previous deliberate protocol violation. In that
|
|
331
|
+
// case, this function should be still callable for other fraud
|
|
332
|
+
// challenges timeouts in order to let the challenger unlock its
|
|
333
|
+
// ETH deposit back. However, the wallet termination logic is
|
|
334
|
+
// not called and the challenger is not rewarded.
|
|
335
|
+
} else {
|
|
336
|
+
revert(
|
|
337
|
+
"Wallet must be in Live or MovingFunds or Closing or Terminated state"
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// slither-disable-next-line reentrancy-events
|
|
342
|
+
emit FraudChallengeDefeatTimedOut(walletPubKeyHash, sighash);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// @notice Extracts the UTXO keys from the given preimage used during
|
|
346
|
+
/// signing of a witness input.
|
|
347
|
+
/// @param preimage The preimage which produces sighash used to generate the
|
|
348
|
+
/// ECDSA signature that is the subject of the fraud claim. It is a
|
|
349
|
+
/// serialized subset of the transaction. The exact subset used as
|
|
350
|
+
/// the preimage depends on the transaction input the signature is
|
|
351
|
+
/// produced for. See BIP-143 for reference
|
|
352
|
+
/// @return utxoKey UTXO key that identifies spent input.
|
|
353
|
+
function extractUtxoKeyFromWitnessPreimage(bytes calldata preimage)
|
|
354
|
+
internal
|
|
355
|
+
pure
|
|
356
|
+
returns (uint256 utxoKey)
|
|
357
|
+
{
|
|
358
|
+
// The expected structure of the preimage created during signing of a
|
|
359
|
+
// witness input:
|
|
360
|
+
// - transaction version (4 bytes)
|
|
361
|
+
// - hash of previous outpoints of all inputs (32 bytes)
|
|
362
|
+
// - hash of sequences of all inputs (32 bytes)
|
|
363
|
+
// - outpoint (hash + index) of the input being signed (36 bytes)
|
|
364
|
+
// - the unlocking script of the input (variable length)
|
|
365
|
+
// - value of the outpoint (8 bytes)
|
|
366
|
+
// - sequence of the input being signed (4 bytes)
|
|
367
|
+
// - hash of all outputs (32 bytes)
|
|
368
|
+
// - transaction locktime (4 bytes)
|
|
369
|
+
// - sighash type (4 bytes)
|
|
370
|
+
|
|
371
|
+
// See Bitcoin's BIP-143 for reference:
|
|
372
|
+
// https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki.
|
|
373
|
+
|
|
374
|
+
// The outpoint (hash and index) is located at the constant offset of
|
|
375
|
+
// 68 (4 + 32 + 32).
|
|
376
|
+
bytes32 outpointTxHash = preimage.extractInputTxIdLeAt(68);
|
|
377
|
+
uint32 outpointIndex = BTCUtils.reverseUint32(
|
|
378
|
+
uint32(preimage.extractTxIndexLeAt(68))
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
return
|
|
382
|
+
uint256(keccak256(abi.encodePacked(outpointTxHash, outpointIndex)));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/// @notice Extracts the UTXO key from the given preimage used during
|
|
386
|
+
/// signing of a non-witness input.
|
|
387
|
+
/// @param preimage The preimage which produces sighash used to generate the
|
|
388
|
+
/// ECDSA signature that is the subject of the fraud claim. It is a
|
|
389
|
+
/// serialized subset of the transaction. The exact subset used as
|
|
390
|
+
/// the preimage depends on the transaction input the signature is
|
|
391
|
+
/// produced for. See BIP-143 for reference
|
|
392
|
+
/// @return utxoKey UTXO key that identifies spent input.
|
|
393
|
+
function extractUtxoKeyFromNonWitnessPreimage(bytes calldata preimage)
|
|
394
|
+
internal
|
|
395
|
+
pure
|
|
396
|
+
returns (uint256 utxoKey)
|
|
397
|
+
{
|
|
398
|
+
// The expected structure of the preimage created during signing of a
|
|
399
|
+
// non-witness input:
|
|
400
|
+
// - transaction version (4 bytes)
|
|
401
|
+
// - number of inputs written as compactSize uint (1 byte, 3 bytes,
|
|
402
|
+
// 5 bytes or 9 bytes)
|
|
403
|
+
// - for each input
|
|
404
|
+
// - outpoint (hash and index) (36 bytes)
|
|
405
|
+
// - unlocking script for the input being signed (variable length)
|
|
406
|
+
// or `00` for all other inputs (1 byte)
|
|
407
|
+
// - input sequence (4 bytes)
|
|
408
|
+
// - number of outputs written as compactSize uint (1 byte, 3 bytes,
|
|
409
|
+
// 5 bytes or 9 bytes)
|
|
410
|
+
// - outputs (variable length)
|
|
411
|
+
// - transaction locktime (4 bytes)
|
|
412
|
+
// - sighash type (4 bytes)
|
|
413
|
+
|
|
414
|
+
// See example for reference:
|
|
415
|
+
// https://en.bitcoin.it/wiki/OP_CHECKSIG#Code_samples_and_raw_dumps.
|
|
416
|
+
|
|
417
|
+
// The input data begins at the constant offset of 4 (the first 4 bytes
|
|
418
|
+
// are for the transaction version).
|
|
419
|
+
(uint256 inputsCompactSizeUintLength, uint256 inputsCount) = preimage
|
|
420
|
+
.parseVarIntAt(4);
|
|
421
|
+
|
|
422
|
+
// To determine the first input starting index, we must jump 4 bytes
|
|
423
|
+
// over the transaction version length and the compactSize uint which
|
|
424
|
+
// prepends the input vector. One byte must be added because
|
|
425
|
+
// `BtcUtils.parseVarInt` does not include compactSize uint tag in the
|
|
426
|
+
// returned length.
|
|
427
|
+
//
|
|
428
|
+
// For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`
|
|
429
|
+
// returns `0`, so we jump over one byte of compactSize uint.
|
|
430
|
+
//
|
|
431
|
+
// For >= 253 && <= 0xffff there is `0xfd` tag,
|
|
432
|
+
// `BTCUtils.determineVarIntDataLengthAt` returns `2` (no
|
|
433
|
+
// tag byte included) so we need to jump over 1+2 bytes of
|
|
434
|
+
// compactSize uint.
|
|
435
|
+
//
|
|
436
|
+
// Please refer `BTCUtils` library and compactSize uint
|
|
437
|
+
// docs in `BitcoinTx` library for more details.
|
|
438
|
+
uint256 inputStartingIndex = 4 + 1 + inputsCompactSizeUintLength;
|
|
439
|
+
|
|
440
|
+
for (uint256 i = 0; i < inputsCount; i++) {
|
|
441
|
+
uint256 inputLength = preimage.determineInputLengthAt(
|
|
442
|
+
inputStartingIndex
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
(, uint256 scriptSigLength) = preimage.extractScriptSigLenAt(
|
|
446
|
+
inputStartingIndex
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (scriptSigLength > 0) {
|
|
450
|
+
// The input this preimage was generated for was found.
|
|
451
|
+
// All the other inputs in the preimage are marked with a null
|
|
452
|
+
// scriptSig ("00") which has length of 1.
|
|
453
|
+
bytes32 outpointTxHash = preimage.extractInputTxIdLeAt(
|
|
454
|
+
inputStartingIndex
|
|
455
|
+
);
|
|
456
|
+
uint32 outpointIndex = BTCUtils.reverseUint32(
|
|
457
|
+
uint32(preimage.extractTxIndexLeAt(inputStartingIndex))
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
utxoKey = uint256(
|
|
461
|
+
keccak256(abi.encodePacked(outpointTxHash, outpointIndex))
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
inputStartingIndex += inputLength;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return utxoKey;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/// @notice Extracts the sighash type from the given preimage.
|
|
474
|
+
/// @param preimage Serialized subset of the transaction. See BIP-143 for
|
|
475
|
+
/// reference
|
|
476
|
+
/// @dev Sighash type is stored as the last 4 bytes in the preimage (little
|
|
477
|
+
/// endian).
|
|
478
|
+
/// @return sighashType Sighash type as a 32-bit integer.
|
|
479
|
+
function extractSighashType(bytes calldata preimage)
|
|
480
|
+
internal
|
|
481
|
+
pure
|
|
482
|
+
returns (uint32 sighashType)
|
|
483
|
+
{
|
|
484
|
+
bytes4 sighashTypeBytes = preimage.slice4(preimage.length - 4);
|
|
485
|
+
uint32 sighashTypeLE = uint32(sighashTypeBytes);
|
|
486
|
+
return sighashTypeLE.reverseUint32();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
@@ -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
|
+
}
|