@keep-network/tbtc-v2 0.1.1-dev.24 → 0.1.1-dev.27
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/artifacts/TBTC.json +10 -10
- package/artifacts/TBTCToken.json +10 -10
- package/artifacts/VendingMachine.json +11 -11
- package/artifacts/solcInputs/{2676c70e1dffa939dbf0519ef3304b34.json → e103ea0f293e8ca60f7bd00f669fc831.json} +10 -4
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +2 -2
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.json +499 -18
- package/build/contracts/bridge/Bridge.sol/IRelay.dbg.json +1 -1
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
- package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +4 -0
- package/build/contracts/bridge/Frauds.sol/Frauds.json +138 -0
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.json +53 -2
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/contracts/bridge/BitcoinTx.sol +10 -0
- package/contracts/bridge/Bridge.sol +304 -88
- package/contracts/bridge/Frauds.sol +531 -0
- package/contracts/bridge/Wallets.sol +173 -5
- package/package.json +1 -1
|
@@ -41,10 +41,9 @@ library Wallets {
|
|
|
41
41
|
// The maximum BTC threshold in satoshi that is used to decide about
|
|
42
42
|
// wallet creation.
|
|
43
43
|
uint64 maxBtcBalance;
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
//
|
|
44
|
+
// The maximum age of a wallet in seconds, after which the wallet
|
|
45
|
+
// moving funds process can be requested.
|
|
46
|
+
uint32 maxAge;
|
|
48
47
|
// 20-byte wallet public key hash being reference to the currently
|
|
49
48
|
// active wallet. Can be unset to the zero value under certain
|
|
50
49
|
// circumstances.
|
|
@@ -91,6 +90,9 @@ library Wallets {
|
|
|
91
90
|
uint64 pendingRedemptionsValue;
|
|
92
91
|
// UNIX timestamp the wallet was created at.
|
|
93
92
|
uint32 createdAt;
|
|
93
|
+
// UNIX timestamp indicating the moment the wallet was requested to
|
|
94
|
+
// move their funds.
|
|
95
|
+
uint32 moveFundsRequestedAt;
|
|
94
96
|
// Current state of the wallet.
|
|
95
97
|
WalletState state;
|
|
96
98
|
}
|
|
@@ -102,6 +104,8 @@ library Wallets {
|
|
|
102
104
|
uint64 newMaxBtcBalance
|
|
103
105
|
);
|
|
104
106
|
|
|
107
|
+
event WalletMaxAgeUpdated(uint32 newMaxAge);
|
|
108
|
+
|
|
105
109
|
event NewWalletRequested();
|
|
106
110
|
|
|
107
111
|
event NewWalletRegistered(
|
|
@@ -109,6 +113,16 @@ library Wallets {
|
|
|
109
113
|
bytes20 indexed walletPubKeyHash
|
|
110
114
|
);
|
|
111
115
|
|
|
116
|
+
event WalletMovingFunds(
|
|
117
|
+
bytes32 indexed ecdsaWalletID,
|
|
118
|
+
bytes20 indexed walletPubKeyHash
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
event WalletClosed(
|
|
122
|
+
bytes32 indexed ecdsaWalletID,
|
|
123
|
+
bytes20 indexed walletPubKeyHash
|
|
124
|
+
);
|
|
125
|
+
|
|
112
126
|
event WalletTerminated(
|
|
113
127
|
bytes32 indexed ecdsaWalletID,
|
|
114
128
|
bytes20 indexed walletPubKeyHash
|
|
@@ -164,6 +178,14 @@ library Wallets {
|
|
|
164
178
|
emit WalletBtcBalanceRangeUpdated(minBtcBalance, maxBtcBalance);
|
|
165
179
|
}
|
|
166
180
|
|
|
181
|
+
/// @notice Sets the wallet maximum age.
|
|
182
|
+
/// @param maxAge New value of the wallet maximum age
|
|
183
|
+
function setMaxAge(Data storage self, uint32 maxAge) external {
|
|
184
|
+
self.maxAge = maxAge;
|
|
185
|
+
|
|
186
|
+
emit WalletMaxAgeUpdated(maxAge);
|
|
187
|
+
}
|
|
188
|
+
|
|
167
189
|
/// @notice Requests creation of a new wallet. This function just
|
|
168
190
|
/// forms a request and the creation process is performed
|
|
169
191
|
/// asynchronously. Outcome of that process should be delivered
|
|
@@ -305,6 +327,152 @@ library Wallets {
|
|
|
305
327
|
emit NewWalletRegistered(ecdsaWalletID, walletPubKeyHash);
|
|
306
328
|
}
|
|
307
329
|
|
|
330
|
+
/// @notice Handles a notification about a wallet heartbeat failure and
|
|
331
|
+
/// triggers the wallet moving funds process.
|
|
332
|
+
/// @param publicKeyX Wallet's public key's X coordinate.
|
|
333
|
+
/// @param publicKeyY Wallet's public key's Y coordinate.
|
|
334
|
+
/// @dev Requirements:
|
|
335
|
+
/// - The only caller authorized to call this function is `registry`
|
|
336
|
+
/// - Wallet must be in Live state
|
|
337
|
+
function notifyWalletHeartbeatFailed(
|
|
338
|
+
Data storage self,
|
|
339
|
+
bytes32 publicKeyX,
|
|
340
|
+
bytes32 publicKeyY
|
|
341
|
+
) external {
|
|
342
|
+
require(
|
|
343
|
+
msg.sender == address(self.registry),
|
|
344
|
+
"Caller is not the ECDSA Wallet Registry"
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
// Compress wallet's public key and calculate Bitcoin's hash160 of it.
|
|
348
|
+
bytes20 walletPubKeyHash = bytes20(
|
|
349
|
+
EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160()
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
require(
|
|
353
|
+
self.registeredWallets[walletPubKeyHash].state == WalletState.Live,
|
|
354
|
+
"ECDSA wallet must be in Live state"
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
moveFunds(self, walletPubKeyHash);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/// @notice Handles a notification about a wallet redemption timeout
|
|
361
|
+
/// and requests slashing of the wallet operators. Triggers the
|
|
362
|
+
/// wallet moving funds process only if the wallet is still in the
|
|
363
|
+
/// Live state. That means multiple action timeouts can be reported
|
|
364
|
+
/// for the same wallet but only the first report requests the
|
|
365
|
+
/// wallet to move their funds.
|
|
366
|
+
/// @param walletPubKeyHash 20-byte public key hash of the wallet
|
|
367
|
+
/// @dev Requirements:
|
|
368
|
+
/// - Wallet must be in Live or MovingFunds state
|
|
369
|
+
function notifyRedemptionTimedOut(
|
|
370
|
+
Data storage self,
|
|
371
|
+
bytes20 walletPubKeyHash
|
|
372
|
+
) external {
|
|
373
|
+
WalletState walletState = self
|
|
374
|
+
.registeredWallets[walletPubKeyHash]
|
|
375
|
+
.state;
|
|
376
|
+
|
|
377
|
+
require(
|
|
378
|
+
walletState == WalletState.Live ||
|
|
379
|
+
walletState == WalletState.MovingFunds,
|
|
380
|
+
"ECDSA wallet must be in Live or MovingFunds state"
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (walletState == WalletState.Live) {
|
|
384
|
+
moveFunds(self, walletPubKeyHash);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// TODO: Perform slashing of wallet operators.
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/// @notice Notifies that the wallet is either old enough or has too few
|
|
391
|
+
/// satoshis left and qualifies to be closed.
|
|
392
|
+
/// @param walletPubKeyHash 20-byte public key hash of the wallet
|
|
393
|
+
/// @param walletMainUtxo Data of the wallet's main UTXO, as currently
|
|
394
|
+
/// known on the Ethereum chain.
|
|
395
|
+
/// @dev Requirements:
|
|
396
|
+
/// - Wallet must not be set as the current active wallet
|
|
397
|
+
/// - Wallet must exceed the wallet maximum age OR the wallet BTC
|
|
398
|
+
/// balance must be lesser than the minimum threshold. If the latter
|
|
399
|
+
/// case is true, the `walletMainUtxo` components must point to the
|
|
400
|
+
/// recent main UTXO of the given wallet, as currently known on the
|
|
401
|
+
/// Ethereum chain. If the wallet has no main UTXO, this parameter
|
|
402
|
+
/// can be empty as it is ignored since the wallet balance is
|
|
403
|
+
/// assumed to be zero.
|
|
404
|
+
/// - Wallet must be in Live state
|
|
405
|
+
function notifyCloseableWallet(
|
|
406
|
+
Data storage self,
|
|
407
|
+
bytes20 walletPubKeyHash,
|
|
408
|
+
BitcoinTx.UTXO calldata walletMainUtxo
|
|
409
|
+
) external {
|
|
410
|
+
require(
|
|
411
|
+
self.activeWalletPubKeyHash != walletPubKeyHash,
|
|
412
|
+
"Active wallet cannot be considered closeable"
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
|
|
416
|
+
require(
|
|
417
|
+
wallet.state == WalletState.Live,
|
|
418
|
+
"ECDSA wallet must be in Live state"
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
/* solhint-disable-next-line not-rely-on-time */
|
|
422
|
+
bool walletOldEnough = block.timestamp >=
|
|
423
|
+
wallet.createdAt + self.maxAge;
|
|
424
|
+
|
|
425
|
+
require(
|
|
426
|
+
walletOldEnough ||
|
|
427
|
+
getWalletBtcBalance(self, walletPubKeyHash, walletMainUtxo) <
|
|
428
|
+
self.minBtcBalance,
|
|
429
|
+
"Wallet needs to be old enough or have too few satoshis"
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
moveFunds(self, walletPubKeyHash);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/// @notice Requests a wallet to move their funds. If the wallet balance
|
|
436
|
+
/// is zero, the wallet is closed immediately and the ECDSA
|
|
437
|
+
/// registry is notified about this fact. If the move funds
|
|
438
|
+
/// request refers to the current active wallet, such a wallet
|
|
439
|
+
/// is no longer considered active and the active wallet slot
|
|
440
|
+
/// is unset allowing to trigger a new wallet creation immediately.
|
|
441
|
+
/// @param walletPubKeyHash 20-byte public key hash of the wallet
|
|
442
|
+
/// @dev Requirements:
|
|
443
|
+
/// - The caller must make sure that the wallet is in the Live state
|
|
444
|
+
function moveFunds(Data storage self, bytes20 walletPubKeyHash) internal {
|
|
445
|
+
Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
|
|
446
|
+
|
|
447
|
+
if (wallet.mainUtxoHash == bytes32(0)) {
|
|
448
|
+
// If the wallet has no main UTXO, that means its BTC balance
|
|
449
|
+
// is zero and it should be closed immediately.
|
|
450
|
+
wallet.state = WalletState.Closed;
|
|
451
|
+
|
|
452
|
+
emit WalletClosed(wallet.ecdsaWalletID, walletPubKeyHash);
|
|
453
|
+
|
|
454
|
+
self.registry.closeWallet(wallet.ecdsaWalletID);
|
|
455
|
+
} else {
|
|
456
|
+
// Otherwise, initialize the moving funds process.
|
|
457
|
+
wallet.state = WalletState.MovingFunds;
|
|
458
|
+
/* solhint-disable-next-line not-rely-on-time */
|
|
459
|
+
wallet.moveFundsRequestedAt = uint32(block.timestamp);
|
|
460
|
+
|
|
461
|
+
emit WalletMovingFunds(wallet.ecdsaWalletID, walletPubKeyHash);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (self.activeWalletPubKeyHash == walletPubKeyHash) {
|
|
465
|
+
// If the move funds request refers to the current active wallet,
|
|
466
|
+
// unset the active wallet and make the wallet creation process
|
|
467
|
+
// possible in order to get a new healthy active wallet.
|
|
468
|
+
delete self.activeWalletPubKeyHash;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// TODO: Implement functions that will be called upon moving funds process
|
|
473
|
+
// end. Remember the moving funds process ends up with a successful
|
|
474
|
+
// proof or a timeout.
|
|
475
|
+
|
|
308
476
|
/// @notice Reports about a fraud committed by the given wallet. This
|
|
309
477
|
/// function performs slashing and wallet termination in reaction
|
|
310
478
|
/// to a proven fraud and it should only be called when the fraud
|
|
@@ -347,6 +515,6 @@ library Wallets {
|
|
|
347
515
|
delete self.activeWalletPubKeyHash;
|
|
348
516
|
}
|
|
349
517
|
|
|
350
|
-
self.registry.closeWallet(
|
|
518
|
+
self.registry.closeWallet(wallet.ecdsaWalletID);
|
|
351
519
|
}
|
|
352
520
|
}
|