@keep-network/tbtc-v2 0.1.1-dev.30 → 0.1.1-dev.33
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 +3 -3
- package/artifacts/TBTCToken.json +3 -3
- package/artifacts/VendingMachine.json +10 -10
- package/artifacts/solcInputs/{087a7a27c8cd210b7c63e594263dfed9.json → 7e4974a220c8c12bc2c31c8235131416.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/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.json +234 -82
- package/build/contracts/bridge/Bridge.sol/IRelay.dbg.json +1 -1
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
- package/build/contracts/bridge/BridgeState.sol/BridgeState.json +10 -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 +1 -1
- package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +1 -1
- package/build/contracts/bridge/Frauds.sol/Frauds.json +2 -2
- 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 +2 -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/Bridge.sol +556 -421
- package/contracts/bridge/BridgeState.sol +54 -0
- package/contracts/bridge/Deposit.sol +247 -0
- package/contracts/bridge/Frauds.sol +5 -7
- package/contracts/bridge/Wallets.sol +89 -19
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
|
|
|
22
22
|
import {IWalletOwner as EcdsaWalletOwner} from "@keep-network/ecdsa/contracts/api/IWalletOwner.sol";
|
|
23
23
|
|
|
24
24
|
import "../bank/Bank.sol";
|
|
25
|
+
import "./BridgeState.sol";
|
|
25
26
|
import "./BitcoinTx.sol";
|
|
26
27
|
import "./EcdsaLib.sol";
|
|
27
28
|
import "./Wallets.sol";
|
|
@@ -58,59 +59,20 @@ interface IRelay {
|
|
|
58
59
|
/// wallet informs the Bridge about the sweep increasing appropriate
|
|
59
60
|
/// balances in the Bank.
|
|
60
61
|
/// @dev Bridge is an upgradeable component of the Bank.
|
|
62
|
+
///
|
|
63
|
+
/// TODO: All wallets-related operations that are currently done directly
|
|
64
|
+
/// by the Bridge can be probably delegated to the Wallets library.
|
|
65
|
+
/// Examples of such operations are main UTXO or pending redemptions
|
|
66
|
+
/// value updates.
|
|
61
67
|
contract Bridge is Ownable, EcdsaWalletOwner {
|
|
62
|
-
using
|
|
63
|
-
using
|
|
64
|
-
using BytesLib for bytes;
|
|
68
|
+
using BridgeState for BridgeState.Storage;
|
|
69
|
+
using Deposit for BridgeState.Storage;
|
|
65
70
|
using Frauds for Frauds.Data;
|
|
66
71
|
using Wallets for Wallets.Data;
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// Index of the funding output belonging to the funding transaction.
|
|
72
|
-
uint32 fundingOutputIndex;
|
|
73
|
-
// Ethereum depositor address.
|
|
74
|
-
address depositor;
|
|
75
|
-
// The blinding factor as 8 bytes. Byte endianness doesn't matter
|
|
76
|
-
// as this factor is not interpreted as uint.
|
|
77
|
-
bytes8 blindingFactor;
|
|
78
|
-
// The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
|
|
79
|
-
// of the deposit's wallet hashed in the HASH160 Bitcoin opcode style.
|
|
80
|
-
bytes20 walletPubKeyHash;
|
|
81
|
-
// The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
|
|
82
|
-
// that can be used to make the deposit refund after the refund
|
|
83
|
-
// locktime passes. Hashed in the HASH160 Bitcoin opcode style.
|
|
84
|
-
bytes20 refundPubKeyHash;
|
|
85
|
-
// The refund locktime (4-byte LE). Interpreted according to locktime
|
|
86
|
-
// parsing rules described in:
|
|
87
|
-
// https://developer.bitcoin.org/devguide/transactions.html#locktime-and-sequence-number
|
|
88
|
-
// and used with OP_CHECKLOCKTIMEVERIFY opcode as described in:
|
|
89
|
-
// https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
|
|
90
|
-
bytes4 refundLocktime;
|
|
91
|
-
// Address of the Bank vault to which the deposit is routed to.
|
|
92
|
-
// Optional, can be 0x0. The vault must be trusted by the Bridge.
|
|
93
|
-
address vault;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/// @notice Represents tBTC deposit data.
|
|
97
|
-
struct DepositRequest {
|
|
98
|
-
// Ethereum depositor address.
|
|
99
|
-
address depositor;
|
|
100
|
-
// Deposit amount in satoshi.
|
|
101
|
-
uint64 amount;
|
|
102
|
-
// UNIX timestamp the deposit was revealed at.
|
|
103
|
-
uint32 revealedAt;
|
|
104
|
-
// Address of the Bank vault the deposit is routed to.
|
|
105
|
-
// Optional, can be 0x0.
|
|
106
|
-
address vault;
|
|
107
|
-
// Treasury TBTC fee in satoshi at the moment of deposit reveal.
|
|
108
|
-
uint64 treasuryFee;
|
|
109
|
-
// UNIX timestamp the deposit was swept at. Note this is not the
|
|
110
|
-
// time when the deposit was swept on the Bitcoin chain but actually
|
|
111
|
-
// the time when the sweep proof was delivered to the Ethereum chain.
|
|
112
|
-
uint32 sweptAt;
|
|
113
|
-
}
|
|
73
|
+
using BTCUtils for bytes;
|
|
74
|
+
using BTCUtils for uint256;
|
|
75
|
+
using BytesLib for bytes;
|
|
114
76
|
|
|
115
77
|
/// @notice Represents an outcome of the sweep Bitcoin transaction
|
|
116
78
|
/// inputs processing.
|
|
@@ -188,29 +150,14 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
188
150
|
/// Treasury takes part in the operators rewarding process.
|
|
189
151
|
address public immutable treasury;
|
|
190
152
|
|
|
191
|
-
|
|
192
|
-
/// @notice The minimal amount that can be requested for deposit.
|
|
193
|
-
/// Value of this parameter must take into account the value of
|
|
194
|
-
/// `depositTreasuryFeeDivisor` and `depositTxMaxFee`
|
|
195
|
-
/// parameters in order to make requests that can incur the
|
|
196
|
-
/// treasury and transaction fee and still satisfy the depositor.
|
|
197
|
-
uint64 public depositDustThreshold;
|
|
198
|
-
|
|
199
|
-
/// TODO: Make it governable.
|
|
200
|
-
/// @notice Divisor used to compute the treasury fee taken from each
|
|
201
|
-
/// deposit and transferred to the treasury upon sweep proof
|
|
202
|
-
/// submission. That fee is computed as follows:
|
|
203
|
-
/// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
|
|
204
|
-
/// For example, if the treasury fee needs to be 2% of each deposit,
|
|
205
|
-
/// the `depositTreasuryFeeDivisor` should be set to `50`
|
|
206
|
-
/// because `1/50 = 0.02 = 2%`.
|
|
207
|
-
uint64 public depositTreasuryFeeDivisor;
|
|
153
|
+
BridgeState.Storage internal self;
|
|
208
154
|
|
|
209
155
|
/// TODO: Make it governable.
|
|
210
156
|
/// @notice Maximum amount of BTC transaction fee that can be incurred by
|
|
211
157
|
/// each swept deposit being part of the given sweep
|
|
212
158
|
/// transaction. If the maximum BTC transaction fee is exceeded,
|
|
213
159
|
/// such transaction is considered a fraud.
|
|
160
|
+
/// @dev This is a per-deposit input max fee for the sweep transaction.
|
|
214
161
|
uint64 public depositTxMaxFee;
|
|
215
162
|
|
|
216
163
|
/// TODO: Make it governable.
|
|
@@ -236,6 +183,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
236
183
|
/// each redemption request being part of the given redemption
|
|
237
184
|
/// transaction. If the maximum BTC transaction fee is exceeded, such
|
|
238
185
|
/// transaction is considered a fraud.
|
|
186
|
+
/// @dev This is a per-redemption output max fee for the redemption transaction.
|
|
239
187
|
uint64 public redemptionTxMaxFee;
|
|
240
188
|
|
|
241
189
|
/// TODO: Make it governable.
|
|
@@ -246,25 +194,15 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
246
194
|
/// to the redeemer in full amount.
|
|
247
195
|
uint256 public redemptionTimeout;
|
|
248
196
|
|
|
249
|
-
///
|
|
250
|
-
///
|
|
251
|
-
///
|
|
252
|
-
///
|
|
253
|
-
///
|
|
254
|
-
///
|
|
255
|
-
///
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
/// @notice Collection of all revealed deposits indexed by
|
|
259
|
-
/// keccak256(fundingTxHash | fundingOutputIndex).
|
|
260
|
-
/// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
|
|
261
|
-
/// and fundingOutputIndex an uint32. This mapping may contain valid
|
|
262
|
-
/// and invalid deposits and the wallet is responsible for
|
|
263
|
-
/// validating them before attempting to execute a sweep.
|
|
264
|
-
mapping(uint256 => DepositRequest) public deposits;
|
|
197
|
+
/// TODO: Make it governable.
|
|
198
|
+
/// @notice Maximum amount of the total BTC transaction fee that is
|
|
199
|
+
/// acceptable in a single moving funds transaction.
|
|
200
|
+
/// @dev This is a TOTAL max fee for the moving funds transaction. Note that
|
|
201
|
+
/// `depositTxMaxFee` is per single deposit and `redemptionTxMaxFee`
|
|
202
|
+
/// if per single redemption. `movingFundsTxMaxTotalFee` is a total fee
|
|
203
|
+
/// for the entire transaction.
|
|
204
|
+
uint64 public movingFundsTxMaxTotalFee;
|
|
265
205
|
|
|
266
|
-
//TODO: Remember to update this map when implementing transferring funds
|
|
267
|
-
// between wallets (insert the main UTXO that was used as the input).
|
|
268
206
|
/// @notice Collection of main UTXOs that are honestly spent indexed by
|
|
269
207
|
/// keccak256(fundingTxHash | fundingOutputIndex). The fundingTxHash
|
|
270
208
|
/// is bytes32 (ordered as in Bitcoin internally) and
|
|
@@ -406,6 +344,11 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
406
344
|
bytes32 sighash
|
|
407
345
|
);
|
|
408
346
|
|
|
347
|
+
event MovingFundsCompleted(
|
|
348
|
+
bytes20 walletPubKeyHash,
|
|
349
|
+
bytes32 movingFundsTxHash
|
|
350
|
+
);
|
|
351
|
+
|
|
409
352
|
constructor(
|
|
410
353
|
address _bank,
|
|
411
354
|
address _relay,
|
|
@@ -425,13 +368,16 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
425
368
|
txProofDifficultyFactor = _txProofDifficultyFactor;
|
|
426
369
|
|
|
427
370
|
// TODO: Revisit initial values.
|
|
428
|
-
depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
|
|
429
|
-
depositTxMaxFee =
|
|
430
|
-
depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
|
|
371
|
+
self.depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
|
|
372
|
+
depositTxMaxFee = 10000; // 10000 satoshi
|
|
373
|
+
self.depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
|
|
431
374
|
redemptionDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC
|
|
432
375
|
redemptionTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
|
|
433
|
-
redemptionTxMaxFee =
|
|
376
|
+
redemptionTxMaxFee = 10000; // 10000 satoshi
|
|
434
377
|
redemptionTimeout = 172800; // 48 hours
|
|
378
|
+
movingFundsTxMaxTotalFee = 10000; // 10000 satoshi
|
|
379
|
+
|
|
380
|
+
// TODO: Revisit initial values.
|
|
435
381
|
frauds.setSlashingAmount(10000 * 1e18); // 10000 T
|
|
436
382
|
frauds.setNotifierRewardMultiplier(100); // 100%
|
|
437
383
|
frauds.setChallengeDefeatTimeout(7 days);
|
|
@@ -500,7 +446,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
500
446
|
/// @param isTrusted flag indicating whether the vault is trusted or not
|
|
501
447
|
/// @dev Can only be called by the Governance.
|
|
502
448
|
function setVaultStatus(address vault, bool isTrusted) external onlyOwner {
|
|
503
|
-
isVaultTrusted[vault] = isTrusted;
|
|
449
|
+
self.isVaultTrusted[vault] = isTrusted;
|
|
504
450
|
emit VaultStatusUpdated(vault, isTrusted);
|
|
505
451
|
}
|
|
506
452
|
|
|
@@ -655,124 +601,9 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
655
601
|
/// deposit script unlocks to receive their BTC back.
|
|
656
602
|
function revealDeposit(
|
|
657
603
|
BitcoinTx.Info calldata fundingTx,
|
|
658
|
-
RevealInfo calldata reveal
|
|
604
|
+
Deposit.RevealInfo calldata reveal
|
|
659
605
|
) external {
|
|
660
|
-
|
|
661
|
-
wallets.registeredWallets[reveal.walletPubKeyHash].state ==
|
|
662
|
-
Wallets.WalletState.Live,
|
|
663
|
-
"Wallet is not in Live state"
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
require(
|
|
667
|
-
reveal.vault == address(0) || isVaultTrusted[reveal.vault],
|
|
668
|
-
"Vault is not trusted"
|
|
669
|
-
);
|
|
670
|
-
|
|
671
|
-
// TODO: Should we enforce a specific locktime at contract level?
|
|
672
|
-
|
|
673
|
-
bytes memory expectedScript = abi.encodePacked(
|
|
674
|
-
hex"14", // Byte length of depositor Ethereum address.
|
|
675
|
-
reveal.depositor,
|
|
676
|
-
hex"75", // OP_DROP
|
|
677
|
-
hex"08", // Byte length of blinding factor value.
|
|
678
|
-
reveal.blindingFactor,
|
|
679
|
-
hex"75", // OP_DROP
|
|
680
|
-
hex"76", // OP_DUP
|
|
681
|
-
hex"a9", // OP_HASH160
|
|
682
|
-
hex"14", // Byte length of a compressed Bitcoin public key hash.
|
|
683
|
-
reveal.walletPubKeyHash,
|
|
684
|
-
hex"87", // OP_EQUAL
|
|
685
|
-
hex"63", // OP_IF
|
|
686
|
-
hex"ac", // OP_CHECKSIG
|
|
687
|
-
hex"67", // OP_ELSE
|
|
688
|
-
hex"76", // OP_DUP
|
|
689
|
-
hex"a9", // OP_HASH160
|
|
690
|
-
hex"14", // Byte length of a compressed Bitcoin public key hash.
|
|
691
|
-
reveal.refundPubKeyHash,
|
|
692
|
-
hex"88", // OP_EQUALVERIFY
|
|
693
|
-
hex"04", // Byte length of refund locktime value.
|
|
694
|
-
reveal.refundLocktime,
|
|
695
|
-
hex"b1", // OP_CHECKLOCKTIMEVERIFY
|
|
696
|
-
hex"75", // OP_DROP
|
|
697
|
-
hex"ac", // OP_CHECKSIG
|
|
698
|
-
hex"68" // OP_ENDIF
|
|
699
|
-
);
|
|
700
|
-
|
|
701
|
-
bytes memory fundingOutput = fundingTx
|
|
702
|
-
.outputVector
|
|
703
|
-
.extractOutputAtIndex(reveal.fundingOutputIndex);
|
|
704
|
-
bytes memory fundingOutputHash = fundingOutput.extractHash();
|
|
705
|
-
|
|
706
|
-
if (fundingOutputHash.length == 20) {
|
|
707
|
-
// A 20-byte output hash is used by P2SH. That hash is constructed
|
|
708
|
-
// by applying OP_HASH160 on the locking script. A 20-byte output
|
|
709
|
-
// hash is used as well by P2PKH and P2WPKH (OP_HASH160 on the
|
|
710
|
-
// public key). However, since we compare the actual output hash
|
|
711
|
-
// with an expected locking script hash, this check will succeed only
|
|
712
|
-
// for P2SH transaction type with expected script hash value. For
|
|
713
|
-
// P2PKH and P2WPKH, it will fail on the output hash comparison with
|
|
714
|
-
// the expected locking script hash.
|
|
715
|
-
require(
|
|
716
|
-
fundingOutputHash.slice20(0) == expectedScript.hash160View(),
|
|
717
|
-
"Wrong 20-byte script hash"
|
|
718
|
-
);
|
|
719
|
-
} else if (fundingOutputHash.length == 32) {
|
|
720
|
-
// A 32-byte output hash is used by P2WSH. That hash is constructed
|
|
721
|
-
// by applying OP_SHA256 on the locking script.
|
|
722
|
-
require(
|
|
723
|
-
fundingOutputHash.toBytes32() == sha256(expectedScript),
|
|
724
|
-
"Wrong 32-byte script hash"
|
|
725
|
-
);
|
|
726
|
-
} else {
|
|
727
|
-
revert("Wrong script hash length");
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Resulting TX hash is in native Bitcoin little-endian format.
|
|
731
|
-
bytes32 fundingTxHash = abi
|
|
732
|
-
.encodePacked(
|
|
733
|
-
fundingTx.version,
|
|
734
|
-
fundingTx.inputVector,
|
|
735
|
-
fundingTx.outputVector,
|
|
736
|
-
fundingTx.locktime
|
|
737
|
-
)
|
|
738
|
-
.hash256View();
|
|
739
|
-
|
|
740
|
-
DepositRequest storage deposit = deposits[
|
|
741
|
-
uint256(
|
|
742
|
-
keccak256(
|
|
743
|
-
abi.encodePacked(fundingTxHash, reveal.fundingOutputIndex)
|
|
744
|
-
)
|
|
745
|
-
)
|
|
746
|
-
];
|
|
747
|
-
require(deposit.revealedAt == 0, "Deposit already revealed");
|
|
748
|
-
|
|
749
|
-
uint64 fundingOutputAmount = fundingOutput.extractValue();
|
|
750
|
-
|
|
751
|
-
require(
|
|
752
|
-
fundingOutputAmount >= depositDustThreshold,
|
|
753
|
-
"Deposit amount too small"
|
|
754
|
-
);
|
|
755
|
-
|
|
756
|
-
deposit.amount = fundingOutputAmount;
|
|
757
|
-
deposit.depositor = reveal.depositor;
|
|
758
|
-
/* solhint-disable-next-line not-rely-on-time */
|
|
759
|
-
deposit.revealedAt = uint32(block.timestamp);
|
|
760
|
-
deposit.vault = reveal.vault;
|
|
761
|
-
deposit.treasuryFee = depositTreasuryFeeDivisor > 0
|
|
762
|
-
? fundingOutputAmount / depositTreasuryFeeDivisor
|
|
763
|
-
: 0;
|
|
764
|
-
|
|
765
|
-
emit DepositRevealed(
|
|
766
|
-
fundingTxHash,
|
|
767
|
-
reveal.fundingOutputIndex,
|
|
768
|
-
reveal.depositor,
|
|
769
|
-
fundingOutputAmount,
|
|
770
|
-
reveal.blindingFactor,
|
|
771
|
-
reveal.walletPubKeyHash,
|
|
772
|
-
reveal.refundPubKeyHash,
|
|
773
|
-
reveal.refundLocktime,
|
|
774
|
-
reveal.vault
|
|
775
|
-
);
|
|
606
|
+
self.revealDeposit(wallets, fundingTx, reveal);
|
|
776
607
|
}
|
|
777
608
|
|
|
778
609
|
/// @notice Used by the wallet to prove the BTC deposit sweep transaction
|
|
@@ -1055,7 +886,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1055
886
|
uint256 inputLength
|
|
1056
887
|
) = parseTxInputAt(sweepTxInputVector, inputStartingIndex);
|
|
1057
888
|
|
|
1058
|
-
|
|
889
|
+
Deposit.Request storage deposit = self.deposits[
|
|
1059
890
|
uint256(
|
|
1060
891
|
keccak256(abi.encodePacked(outpointTxHash, outpointIndex))
|
|
1061
892
|
)
|
|
@@ -1200,6 +1031,183 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1200
1031
|
return (depositTxFee, depositTxFeeRemainder);
|
|
1201
1032
|
}
|
|
1202
1033
|
|
|
1034
|
+
/// @notice Submits a fraud challenge indicating that a UTXO being under
|
|
1035
|
+
/// wallet control was unlocked by the wallet but was not used
|
|
1036
|
+
/// according to the protocol rules. That means the wallet signed
|
|
1037
|
+
/// a transaction input pointing to that UTXO and there is a unique
|
|
1038
|
+
/// sighash and signature pair associated with that input. This
|
|
1039
|
+
/// function uses those parameters to create a fraud accusation that
|
|
1040
|
+
/// proves a given transaction input unlocking the given UTXO was
|
|
1041
|
+
/// actually signed by the wallet. This function cannot determine
|
|
1042
|
+
/// whether the transaction was actually broadcast and the input was
|
|
1043
|
+
/// consumed in a fraudulent way so it just opens a challenge period
|
|
1044
|
+
/// during which the wallet can defeat the challenge by submitting
|
|
1045
|
+
/// proof of a transaction that consumes the given input according
|
|
1046
|
+
/// to protocol rules. To prevent spurious allegations, the caller
|
|
1047
|
+
/// must deposit ETH that is returned back upon justified fraud
|
|
1048
|
+
/// challenge or confiscated otherwise.
|
|
1049
|
+
///@param walletPublicKey The public key of the wallet in the uncompressed
|
|
1050
|
+
/// and unprefixed format (64 bytes)
|
|
1051
|
+
/// @param sighash The hash that was used to produce the ECDSA signature
|
|
1052
|
+
/// that is the subject of the fraud claim. This hash is constructed
|
|
1053
|
+
/// by applying double SHA-256 over a serialized subset of the
|
|
1054
|
+
/// transaction. The exact subset used as hash preimage depends on
|
|
1055
|
+
/// the transaction input the signature is produced for. See BIP-143
|
|
1056
|
+
/// for reference
|
|
1057
|
+
/// @param signature Bitcoin signature in the R/S/V format
|
|
1058
|
+
/// @dev Requirements:
|
|
1059
|
+
/// - Wallet behind `walletPubKey` must be in `Live` or `MovingFunds`
|
|
1060
|
+
/// state
|
|
1061
|
+
/// - The challenger must send appropriate amount of ETH used as
|
|
1062
|
+
/// fraud challenge deposit
|
|
1063
|
+
/// - The signature (represented by r, s and v) must be generated by
|
|
1064
|
+
/// the wallet behind `walletPubKey` during signing of `sighash`
|
|
1065
|
+
/// - Wallet can be challenged for the given signature only once
|
|
1066
|
+
function submitFraudChallenge(
|
|
1067
|
+
bytes calldata walletPublicKey,
|
|
1068
|
+
bytes32 sighash,
|
|
1069
|
+
BitcoinTx.RSVSignature calldata signature
|
|
1070
|
+
) external payable {
|
|
1071
|
+
bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
|
|
1072
|
+
walletPublicKey.slice32(0),
|
|
1073
|
+
walletPublicKey.slice32(32)
|
|
1074
|
+
);
|
|
1075
|
+
bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
|
|
1076
|
+
|
|
1077
|
+
Wallets.Wallet storage wallet = wallets.registeredWallets[
|
|
1078
|
+
walletPubKeyHash
|
|
1079
|
+
];
|
|
1080
|
+
|
|
1081
|
+
require(
|
|
1082
|
+
wallet.state == Wallets.WalletState.Live ||
|
|
1083
|
+
wallet.state == Wallets.WalletState.MovingFunds,
|
|
1084
|
+
"Wallet is neither in Live nor MovingFunds state"
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
frauds.submitChallenge(
|
|
1088
|
+
walletPublicKey,
|
|
1089
|
+
walletPubKeyHash,
|
|
1090
|
+
sighash,
|
|
1091
|
+
signature
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/// @notice Allows to defeat a pending fraud challenge against a wallet if
|
|
1096
|
+
/// the transaction that spends the UTXO follows the protocol rules.
|
|
1097
|
+
/// In order to defeat the challenge the same `walletPublicKey` and
|
|
1098
|
+
/// signature (represented by `r`, `s` and `v`) must be provided as
|
|
1099
|
+
/// were used to calculate the sighash during input signing.
|
|
1100
|
+
/// The fraud challenge defeat attempt will only succeed if the
|
|
1101
|
+
/// inputs in the preimage are considered honestly spent by the
|
|
1102
|
+
/// wallet. Therefore the transaction spending the UTXO must be
|
|
1103
|
+
/// proven in the Bridge before a challenge defeat is called.
|
|
1104
|
+
/// If successfully defeated, the fraud challenge is marked as
|
|
1105
|
+
/// resolved and the amount of ether deposited by the challenger is
|
|
1106
|
+
/// sent to the treasury.
|
|
1107
|
+
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
1108
|
+
/// and unprefixed format (64 bytes)
|
|
1109
|
+
/// @param preimage The preimage which produces sighash used to generate the
|
|
1110
|
+
/// ECDSA signature that is the subject of the fraud claim. It is a
|
|
1111
|
+
/// serialized subset of the transaction. The exact subset used as
|
|
1112
|
+
/// the preimage depends on the transaction input the signature is
|
|
1113
|
+
/// produced for. See BIP-143 for reference
|
|
1114
|
+
/// @param witness Flag indicating whether the preimage was produced for a
|
|
1115
|
+
/// witness input. True for witness, false for non-witness input
|
|
1116
|
+
/// @dev Requirements:
|
|
1117
|
+
/// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
|
|
1118
|
+
/// must identify an open fraud challenge
|
|
1119
|
+
/// - the preimage must be a valid preimage of a transaction generated
|
|
1120
|
+
/// according to the protocol rules and already proved in the Bridge
|
|
1121
|
+
/// - before a defeat attempt is made the transaction that spends the
|
|
1122
|
+
/// given UTXO must be proven in the Bridge
|
|
1123
|
+
function defeatFraudChallenge(
|
|
1124
|
+
bytes calldata walletPublicKey,
|
|
1125
|
+
bytes calldata preimage,
|
|
1126
|
+
bool witness
|
|
1127
|
+
) external {
|
|
1128
|
+
uint256 utxoKey = frauds.unwrapChallenge(
|
|
1129
|
+
walletPublicKey,
|
|
1130
|
+
preimage,
|
|
1131
|
+
witness
|
|
1132
|
+
);
|
|
1133
|
+
|
|
1134
|
+
// Check that the UTXO key identifies a correctly spent UTXO.
|
|
1135
|
+
require(
|
|
1136
|
+
self.deposits[utxoKey].sweptAt > 0 || spentMainUTXOs[utxoKey],
|
|
1137
|
+
"Spent UTXO not found among correctly spent UTXOs"
|
|
1138
|
+
);
|
|
1139
|
+
|
|
1140
|
+
frauds.defeatChallenge(walletPublicKey, preimage, treasury);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/// @notice Notifies about defeat timeout for the given fraud challenge.
|
|
1144
|
+
/// Can be called only if there was a fraud challenge identified by
|
|
1145
|
+
/// the provided `walletPublicKey` and `sighash` and it was not
|
|
1146
|
+
/// defeated on time. The amount of time that needs to pass after
|
|
1147
|
+
/// a fraud challenge is reported is indicated by the
|
|
1148
|
+
/// `challengeDefeatTimeout`. After a successful fraud challenge
|
|
1149
|
+
/// defeat timeout notification the fraud challenge is marked as
|
|
1150
|
+
/// resolved, the stake of each operator is slashed, the ether
|
|
1151
|
+
/// deposited is returned to the challenger and the challenger is
|
|
1152
|
+
/// rewarded.
|
|
1153
|
+
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
1154
|
+
/// and unprefixed format (64 bytes)
|
|
1155
|
+
/// @param sighash The hash that was used to produce the ECDSA signature
|
|
1156
|
+
/// that is the subject of the fraud claim. This hash is constructed
|
|
1157
|
+
/// by applying double SHA-256 over a serialized subset of the
|
|
1158
|
+
/// transaction. The exact subset used as hash preimage depends on
|
|
1159
|
+
/// the transaction input the signature is produced for. See BIP-143
|
|
1160
|
+
/// for reference
|
|
1161
|
+
/// @dev Requirements:
|
|
1162
|
+
/// - `walletPublicKey`and `sighash` must identify an open fraud
|
|
1163
|
+
/// challenge
|
|
1164
|
+
/// - the amount of time indicated by `challengeDefeatTimeout` must
|
|
1165
|
+
/// pass after the challenge was reported
|
|
1166
|
+
function notifyFraudChallengeDefeatTimeout(
|
|
1167
|
+
bytes calldata walletPublicKey,
|
|
1168
|
+
bytes32 sighash
|
|
1169
|
+
) external {
|
|
1170
|
+
frauds.notifyChallengeDefeatTimeout(walletPublicKey, sighash);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/// @notice Returns parameters used by the `Frauds` library.
|
|
1174
|
+
/// @return slashingAmount Value of the slashing amount
|
|
1175
|
+
/// @return notifierRewardMultiplier Value of the notifier reward multiplier
|
|
1176
|
+
/// @return challengeDefeatTimeout Value of the challenge defeat timeout
|
|
1177
|
+
/// @return challengeDepositAmount Value of the challenge deposit amount
|
|
1178
|
+
function getFraudParameters()
|
|
1179
|
+
external
|
|
1180
|
+
view
|
|
1181
|
+
returns (
|
|
1182
|
+
uint256 slashingAmount,
|
|
1183
|
+
uint256 notifierRewardMultiplier,
|
|
1184
|
+
uint256 challengeDefeatTimeout,
|
|
1185
|
+
uint256 challengeDepositAmount
|
|
1186
|
+
)
|
|
1187
|
+
{
|
|
1188
|
+
slashingAmount = frauds.slashingAmount;
|
|
1189
|
+
notifierRewardMultiplier = frauds.notifierRewardMultiplier;
|
|
1190
|
+
challengeDefeatTimeout = frauds.challengeDefeatTimeout;
|
|
1191
|
+
challengeDepositAmount = frauds.challengeDepositAmount;
|
|
1192
|
+
|
|
1193
|
+
return (
|
|
1194
|
+
slashingAmount,
|
|
1195
|
+
notifierRewardMultiplier,
|
|
1196
|
+
challengeDefeatTimeout,
|
|
1197
|
+
challengeDepositAmount
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/// @notice Returns the fraud challenge identified by the given key built
|
|
1202
|
+
/// as keccak256(walletPublicKey|sighash).
|
|
1203
|
+
function fraudChallenges(uint256 challengeKey)
|
|
1204
|
+
external
|
|
1205
|
+
view
|
|
1206
|
+
returns (Frauds.FraudChallenge memory)
|
|
1207
|
+
{
|
|
1208
|
+
return frauds.challenges[challengeKey];
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1203
1211
|
/// @notice Requests redemption of the given amount from the specified
|
|
1204
1212
|
/// wallet to the redeemer Bitcoin output script.
|
|
1205
1213
|
/// @param walletPubKeyHash The 20-byte wallet public key hash (computed
|
|
@@ -1418,9 +1426,9 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1418
1426
|
proofDifficultyContext()
|
|
1419
1427
|
);
|
|
1420
1428
|
|
|
1421
|
-
//
|
|
1422
|
-
//
|
|
1423
|
-
|
|
1429
|
+
// Process the redemption transaction input. Specifically, check if it
|
|
1430
|
+
// refers to the expected wallet's main UTXO.
|
|
1431
|
+
processWalletOutboundTxInput(
|
|
1424
1432
|
redemptionTx.inputVector,
|
|
1425
1433
|
mainUtxo,
|
|
1426
1434
|
walletPubKeyHash
|
|
@@ -1469,191 +1477,14 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1469
1477
|
bank.transferBalance(treasury, outputsInfo.totalTreasuryFee);
|
|
1470
1478
|
}
|
|
1471
1479
|
|
|
1472
|
-
/// @notice
|
|
1473
|
-
///
|
|
1474
|
-
///
|
|
1475
|
-
///
|
|
1476
|
-
///
|
|
1477
|
-
///
|
|
1478
|
-
///
|
|
1479
|
-
///
|
|
1480
|
-
/// whether the transaction was actually broadcast and the input was
|
|
1481
|
-
/// consumed in a fraudulent way so it just opens a challenge period
|
|
1482
|
-
/// during which the wallet can defeat the challenge by submitting
|
|
1483
|
-
/// proof of a transaction that consumes the given input according
|
|
1484
|
-
/// to protocol rules. To prevent spurious allegations, the caller
|
|
1485
|
-
/// must deposit ETH that is returned back upon justified fraud
|
|
1486
|
-
/// challenge or confiscated otherwise.
|
|
1487
|
-
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
1488
|
-
/// and unprefixed format (64 bytes)
|
|
1489
|
-
/// @param sighash The hash that was used to produce the ECDSA signature
|
|
1490
|
-
/// that is the subject of the fraud claim. This hash is constructed
|
|
1491
|
-
/// by applying double SHA-256 over a serialized subset of the
|
|
1492
|
-
/// transaction. The exact subset used as hash preimage depends on
|
|
1493
|
-
/// the transaction input the signature is produced for. See BIP-143
|
|
1494
|
-
/// for reference
|
|
1495
|
-
/// @param signature Bitcoin signature in the R/S/V format
|
|
1496
|
-
/// @dev Requirements:
|
|
1497
|
-
/// - Wallet behind `walletPubKey` must be in `Live` or `MovingFunds`
|
|
1498
|
-
/// state
|
|
1499
|
-
/// - The challenger must send appropriate amount of ETH used as
|
|
1500
|
-
/// fraud challenge deposit
|
|
1501
|
-
/// - The signature (represented by r, s and v) must be generated by
|
|
1502
|
-
/// the wallet behind `walletPubKey` during signing of `sighash`
|
|
1503
|
-
/// - Wallet can be challenged for the given signature only once
|
|
1504
|
-
/// TODO: Consider using wallet public key in the X/Y form to avoid slicing.
|
|
1505
|
-
function submitFraudChallenge(
|
|
1506
|
-
bytes calldata walletPublicKey,
|
|
1507
|
-
bytes32 sighash,
|
|
1508
|
-
BitcoinTx.RSVSignature calldata signature
|
|
1509
|
-
) external payable {
|
|
1510
|
-
bytes memory compressedWalletPublicKey = EcdsaLib.compressPublicKey(
|
|
1511
|
-
walletPublicKey.slice32(0),
|
|
1512
|
-
walletPublicKey.slice32(32)
|
|
1513
|
-
);
|
|
1514
|
-
bytes20 walletPubKeyHash = compressedWalletPublicKey.hash160View();
|
|
1515
|
-
|
|
1516
|
-
Wallets.Wallet storage wallet = wallets.registeredWallets[
|
|
1517
|
-
walletPubKeyHash
|
|
1518
|
-
];
|
|
1519
|
-
|
|
1520
|
-
require(
|
|
1521
|
-
wallet.state == Wallets.WalletState.Live ||
|
|
1522
|
-
wallet.state == Wallets.WalletState.MovingFunds,
|
|
1523
|
-
"Wallet is neither in Live nor MovingFunds state"
|
|
1524
|
-
);
|
|
1525
|
-
|
|
1526
|
-
frauds.submitFraudChallenge(
|
|
1527
|
-
walletPublicKey,
|
|
1528
|
-
walletPubKeyHash,
|
|
1529
|
-
sighash,
|
|
1530
|
-
signature
|
|
1531
|
-
);
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
/// @notice Allows to defeat a pending fraud challenge against a wallet if
|
|
1535
|
-
/// the transaction that spends the UTXO follows the protocol rules.
|
|
1536
|
-
/// In order to defeat the challenge the same `walletPublicKey` and
|
|
1537
|
-
/// signature (represented by `r`, `s` and `v`) must be provided as
|
|
1538
|
-
/// were used in the fraud challenge. Additionally a preimage must
|
|
1539
|
-
/// be provided which was used to calculate the sighash during input
|
|
1540
|
-
/// signing. The fraud challenge defeat attempt will only succeed if
|
|
1541
|
-
/// the inputs in the preimage are considered honestly spent by the
|
|
1542
|
-
/// wallet. Therefore the transaction spending the UTXO must be
|
|
1543
|
-
/// proven in the Bridge before a challenge defeat is called.
|
|
1544
|
-
/// If successfully defeated, the fraud challenge is marked as
|
|
1545
|
-
/// resolved and the amount of ether deposited by the challenger is
|
|
1546
|
-
/// sent to the treasury.
|
|
1547
|
-
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
1548
|
-
/// and unprefixed format (64 bytes)
|
|
1549
|
-
/// @param preimage The preimage which produces sighash used to generate the
|
|
1550
|
-
/// ECDSA signature that is the subject of the fraud claim. It is a
|
|
1551
|
-
/// serialized subset of the transaction. The exact subset used as
|
|
1552
|
-
/// the preimage depends on the transaction input the signature is
|
|
1553
|
-
/// produced for. See BIP-143 for reference
|
|
1554
|
-
/// @param witness Flag indicating whether the preimage was produced for a
|
|
1555
|
-
/// witness input. True for witness, false for non-witness input
|
|
1556
|
-
/// @dev Requirements:
|
|
1557
|
-
/// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`
|
|
1558
|
-
/// must identify an open fraud challenge
|
|
1559
|
-
/// - the preimage must be a valid preimage of a transaction generated
|
|
1560
|
-
/// according to the protocol rules and already proved in the Bridge
|
|
1561
|
-
/// - before a defeat attempt is made the transaction that spends the
|
|
1562
|
-
/// given UTXO must be proven in the Bridge
|
|
1563
|
-
/// TODO: Consider using wallet public key in the X/Y form to avoid slicing.
|
|
1564
|
-
function defeatFraudChallenge(
|
|
1565
|
-
bytes calldata walletPublicKey,
|
|
1566
|
-
bytes calldata preimage,
|
|
1567
|
-
bool witness
|
|
1568
|
-
) external {
|
|
1569
|
-
uint256 utxoKey = frauds.unwrapChallenge(
|
|
1570
|
-
walletPublicKey,
|
|
1571
|
-
preimage,
|
|
1572
|
-
witness
|
|
1573
|
-
);
|
|
1574
|
-
|
|
1575
|
-
// Check that the UTXO key identifies a correctly spent UTXO.
|
|
1576
|
-
require(
|
|
1577
|
-
deposits[utxoKey].sweptAt > 0 || spentMainUTXOs[utxoKey],
|
|
1578
|
-
"Spent UTXO not found among correctly spent UTXOs"
|
|
1579
|
-
);
|
|
1580
|
-
|
|
1581
|
-
frauds.defeatChallenge(walletPublicKey, preimage, treasury);
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
/// @notice Notifies about defeat timeout for the given fraud challenge.
|
|
1585
|
-
/// Can be called only if there was a fraud challenge identified by
|
|
1586
|
-
/// the provided `walletPublicKey` and `sighash` and it was not
|
|
1587
|
-
/// defeated on time. The amount of time that needs to pass after
|
|
1588
|
-
/// a fraud challenge is reported is indicated by the
|
|
1589
|
-
/// `challengeDefeatTimeout`. After a successful fraud challenge
|
|
1590
|
-
/// defeat timeout notification the fraud challenge is marked as
|
|
1591
|
-
/// resolved, the stake of each operator is slashed, the ether
|
|
1592
|
-
/// deposited is returned to the challenger and the challenger is
|
|
1593
|
-
/// rewarded.
|
|
1594
|
-
/// @param walletPublicKey The public key of the wallet in the uncompressed
|
|
1595
|
-
/// and unprefixed format (64 bytes)
|
|
1596
|
-
/// @param sighash The hash that was used to produce the ECDSA signature
|
|
1597
|
-
/// that is the subject of the fraud claim. This hash is constructed
|
|
1598
|
-
/// by applying double SHA-256 over a serialized subset of the
|
|
1599
|
-
/// transaction. The exact subset used as hash preimage depends on
|
|
1600
|
-
/// the transaction input the signature is produced for. See BIP-143
|
|
1601
|
-
/// for reference
|
|
1602
|
-
/// @dev Requirements:
|
|
1603
|
-
/// - `walletPublicKey`and `sighash` must identify an open fraud
|
|
1604
|
-
/// challenge
|
|
1605
|
-
/// - the amount of time indicated by `challengeDefeatTimeout` must
|
|
1606
|
-
/// pass after the challenge was reported
|
|
1607
|
-
/// TODO: Consider using wallet public key in the X/Y form to avoid slicing.
|
|
1608
|
-
function notifyFraudChallengeDefeatTimeout(
|
|
1609
|
-
bytes calldata walletPublicKey,
|
|
1610
|
-
bytes32 sighash
|
|
1611
|
-
) external {
|
|
1612
|
-
frauds.notifyFraudChallengeDefeatTimeout(walletPublicKey, sighash);
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
/// @notice Returns parameters used by the `Frauds` library.
|
|
1616
|
-
/// @return slashingAmount Value of the slashing amount
|
|
1617
|
-
/// @return notifierRewardMultiplier Value of the notifier reward multiplier
|
|
1618
|
-
/// @return challengeDefeatTimeout Value of the challenge defeat timeout
|
|
1619
|
-
/// @return challengeDepositAmount Value of the challenge deposit amount
|
|
1620
|
-
function getFraudParameters()
|
|
1621
|
-
external
|
|
1622
|
-
view
|
|
1623
|
-
returns (
|
|
1624
|
-
uint256 slashingAmount,
|
|
1625
|
-
uint256 notifierRewardMultiplier,
|
|
1626
|
-
uint256 challengeDefeatTimeout,
|
|
1627
|
-
uint256 challengeDepositAmount
|
|
1628
|
-
)
|
|
1629
|
-
{
|
|
1630
|
-
slashingAmount = frauds.slashingAmount;
|
|
1631
|
-
notifierRewardMultiplier = frauds.notifierRewardMultiplier;
|
|
1632
|
-
challengeDefeatTimeout = frauds.challengeDefeatTimeout;
|
|
1633
|
-
challengeDepositAmount = frauds.challengeDepositAmount;
|
|
1634
|
-
|
|
1635
|
-
return (
|
|
1636
|
-
slashingAmount,
|
|
1637
|
-
notifierRewardMultiplier,
|
|
1638
|
-
challengeDefeatTimeout,
|
|
1639
|
-
challengeDepositAmount
|
|
1640
|
-
);
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
/// @notice Returns the fraud challenge identified by the given key built
|
|
1644
|
-
/// as keccak256(walletPublicKey|sighash|v|r|s).
|
|
1645
|
-
function fraudChallenges(uint256 challengeKey)
|
|
1646
|
-
external
|
|
1647
|
-
view
|
|
1648
|
-
returns (Frauds.FraudChallenge memory)
|
|
1649
|
-
{
|
|
1650
|
-
return frauds.challenges[challengeKey];
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
/// @notice Validates whether the redemption Bitcoin transaction input
|
|
1654
|
-
/// vector contains a single input referring to the wallet's main
|
|
1655
|
-
/// UTXO. Reverts in case the validation fails.
|
|
1656
|
-
/// @param redemptionTxInputVector Bitcoin redemption transaction input
|
|
1480
|
+
/// @notice Checks whether an outbound Bitcoin transaction performed from
|
|
1481
|
+
/// the given wallet has an input vector that contains a single
|
|
1482
|
+
/// input referring to the wallet's main UTXO. Marks that main UTXO
|
|
1483
|
+
/// as correctly spent if the validation succeeds. Reverts otherwise.
|
|
1484
|
+
/// There are two outbound transactions from a wallet possible: a
|
|
1485
|
+
/// redemption transaction or a moving funds to another wallet
|
|
1486
|
+
/// transaction.
|
|
1487
|
+
/// @param walletOutboundTxInputVector Bitcoin outbound transaction's input
|
|
1657
1488
|
/// vector. This function assumes vector's structure is valid so it
|
|
1658
1489
|
/// must be validated using e.g. `BTCUtils.validateVin` function
|
|
1659
1490
|
/// before it is passed here
|
|
@@ -1661,9 +1492,9 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1661
1492
|
/// the Ethereum chain.
|
|
1662
1493
|
/// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
|
|
1663
1494
|
// HASH160 over the compressed ECDSA public key) of the wallet which
|
|
1664
|
-
/// performed the
|
|
1665
|
-
function
|
|
1666
|
-
bytes memory
|
|
1495
|
+
/// performed the outbound transaction.
|
|
1496
|
+
function processWalletOutboundTxInput(
|
|
1497
|
+
bytes memory walletOutboundTxInputVector,
|
|
1667
1498
|
BitcoinTx.UTXO calldata mainUtxo,
|
|
1668
1499
|
bytes20 walletPubKeyHash
|
|
1669
1500
|
) internal {
|
|
@@ -1686,16 +1517,16 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1686
1517
|
"Invalid main UTXO data"
|
|
1687
1518
|
);
|
|
1688
1519
|
|
|
1689
|
-
// Assert that the single
|
|
1520
|
+
// Assert that the single outbound transaction input actually
|
|
1690
1521
|
// refers to the wallet's main UTXO.
|
|
1691
1522
|
(
|
|
1692
|
-
bytes32
|
|
1693
|
-
uint32
|
|
1694
|
-
) =
|
|
1523
|
+
bytes32 outpointTxHash,
|
|
1524
|
+
uint32 outpointIndex
|
|
1525
|
+
) = parseWalletOutboundTxInput(walletOutboundTxInputVector);
|
|
1695
1526
|
require(
|
|
1696
|
-
mainUtxo.txHash ==
|
|
1697
|
-
mainUtxo.txOutputIndex ==
|
|
1698
|
-
"
|
|
1527
|
+
mainUtxo.txHash == outpointTxHash &&
|
|
1528
|
+
mainUtxo.txOutputIndex == outpointIndex,
|
|
1529
|
+
"Outbound transaction input must point to the wallet's main UTXO"
|
|
1699
1530
|
);
|
|
1700
1531
|
|
|
1701
1532
|
// Main UTXO used as an input, mark it as spent.
|
|
@@ -1708,10 +1539,13 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1708
1539
|
] = true;
|
|
1709
1540
|
}
|
|
1710
1541
|
|
|
1711
|
-
/// @notice
|
|
1712
|
-
///
|
|
1713
|
-
/// index from its outpoint.
|
|
1714
|
-
///
|
|
1542
|
+
/// @notice Parses the input vector of an outbound Bitcoin transaction
|
|
1543
|
+
/// performed from the given wallet. It extracts the single input
|
|
1544
|
+
/// then the transaction hash and output index from its outpoint.
|
|
1545
|
+
/// There are two outbound transactions from a wallet possible: a
|
|
1546
|
+
/// redemption transaction or a moving funds to another wallet
|
|
1547
|
+
/// transaction.
|
|
1548
|
+
/// @param walletOutboundTxInputVector Bitcoin outbound transaction input
|
|
1715
1549
|
/// vector. This function assumes vector's structure is valid so it
|
|
1716
1550
|
/// must be validated using e.g. `BTCUtils.validateVin` function
|
|
1717
1551
|
/// before it is passed here
|
|
@@ -1719,12 +1553,10 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1719
1553
|
/// pointed in the input's outpoint.
|
|
1720
1554
|
/// @return outpointIndex 4-byte index of the Bitcoin transaction output
|
|
1721
1555
|
/// which is pointed in the input's outpoint.
|
|
1722
|
-
function
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
{
|
|
1727
|
-
// To determine the total number of redemption transaction inputs,
|
|
1556
|
+
function parseWalletOutboundTxInput(
|
|
1557
|
+
bytes memory walletOutboundTxInputVector
|
|
1558
|
+
) internal pure returns (bytes32 outpointTxHash, uint32 outpointIndex) {
|
|
1559
|
+
// To determine the total number of Bitcoin transaction inputs,
|
|
1728
1560
|
// we need to parse the compactSize uint (VarInt) the input vector is
|
|
1729
1561
|
// prepended by. That compactSize uint encodes the number of vector
|
|
1730
1562
|
// elements using the format presented in:
|
|
@@ -1732,13 +1564,13 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1732
1564
|
// We don't need asserting the compactSize uint is parseable since it
|
|
1733
1565
|
// was already checked during `validateVin` validation.
|
|
1734
1566
|
// See `BitcoinTx.inputVector` docs for more details.
|
|
1735
|
-
(, uint256 inputsCount) =
|
|
1567
|
+
(, uint256 inputsCount) = walletOutboundTxInputVector.parseVarInt();
|
|
1736
1568
|
require(
|
|
1737
1569
|
inputsCount == 1,
|
|
1738
|
-
"
|
|
1570
|
+
"Outbound transaction must have a single input"
|
|
1739
1571
|
);
|
|
1740
1572
|
|
|
1741
|
-
bytes memory input =
|
|
1573
|
+
bytes memory input = walletOutboundTxInputVector.extractInputAtIndex(0);
|
|
1742
1574
|
|
|
1743
1575
|
outpointTxHash = input.extractInputTxIdLE();
|
|
1744
1576
|
|
|
@@ -1801,11 +1633,26 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1801
1633
|
// scripts that can be used to lock the change. This is done upfront to
|
|
1802
1634
|
// save on gas. Both scripts have a strict format defined by Bitcoin.
|
|
1803
1635
|
//
|
|
1804
|
-
// The P2PKH script has format <0x1976a914> <20-byte PKH> <0x88ac>.
|
|
1636
|
+
// The P2PKH script has the byte format: <0x1976a914> <20-byte PKH> <0x88ac>.
|
|
1637
|
+
// According to https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
|
|
1638
|
+
// - 0x19: Byte length of the entire script
|
|
1639
|
+
// - 0x76: OP_DUP
|
|
1640
|
+
// - 0xa9: OP_HASH160
|
|
1641
|
+
// - 0x14: Byte length of the public key hash
|
|
1642
|
+
// - 0x88: OP_EQUALVERIFY
|
|
1643
|
+
// - 0xac: OP_CHECKSIG
|
|
1644
|
+
// which matches the P2PKH structure as per:
|
|
1645
|
+
// https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash
|
|
1805
1646
|
bytes32 walletP2PKHScriptKeccak = keccak256(
|
|
1806
1647
|
abi.encodePacked(hex"1976a914", walletPubKeyHash, hex"88ac")
|
|
1807
1648
|
);
|
|
1808
|
-
// The P2WPKH script has format <0x160014> <20-byte PKH>.
|
|
1649
|
+
// The P2WPKH script has the byte format: <0x160014> <20-byte PKH>.
|
|
1650
|
+
// According to https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
|
|
1651
|
+
// - 0x16: Byte length of the entire script
|
|
1652
|
+
// - 0x00: OP_0
|
|
1653
|
+
// - 0x14: Byte length of the public key hash
|
|
1654
|
+
// which matches the P2WPKH structure as per:
|
|
1655
|
+
// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH
|
|
1809
1656
|
bytes32 walletP2WPKHScriptKeccak = keccak256(
|
|
1810
1657
|
abi.encodePacked(hex"160014", walletPubKeyHash)
|
|
1811
1658
|
);
|
|
@@ -1831,7 +1678,7 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1831
1678
|
// Extract the value from given output.
|
|
1832
1679
|
uint64 outputValue = output.extractValue();
|
|
1833
1680
|
// The output consists of an 8-byte value and a variable length
|
|
1834
|
-
// script. To extract that script we slice the output
|
|
1681
|
+
// script. To extract that script we slice the output starting from
|
|
1835
1682
|
// 9th byte until the end.
|
|
1836
1683
|
bytes memory outputScript = output.slice(8, output.length - 8);
|
|
1837
1684
|
|
|
@@ -2010,4 +1857,292 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
2010
1857
|
// Return the requested amount of tokens to the redeemer
|
|
2011
1858
|
bank.transferBalance(request.redeemer, request.requestedAmount);
|
|
2012
1859
|
}
|
|
1860
|
+
|
|
1861
|
+
/// @notice Used by the wallet to prove the BTC moving funds transaction
|
|
1862
|
+
/// and to make the necessary state changes. Moving funds is only
|
|
1863
|
+
/// accepted if it satisfies SPV proof.
|
|
1864
|
+
///
|
|
1865
|
+
/// The function validates the moving funds transaction structure
|
|
1866
|
+
/// by checking if it actually spends the main UTXO of the declared
|
|
1867
|
+
/// wallet and locks the value on the pre-committed target wallets
|
|
1868
|
+
/// using a reasonable transaction fee. If all preconditions are
|
|
1869
|
+
/// met, this functions closes the source wallet.
|
|
1870
|
+
///
|
|
1871
|
+
/// It is possible to prove the given moving funds transaction only
|
|
1872
|
+
/// one time.
|
|
1873
|
+
/// @param movingFundsTx Bitcoin moving funds transaction data
|
|
1874
|
+
/// @param movingFundsProof Bitcoin moving funds proof data
|
|
1875
|
+
/// @param mainUtxo Data of the wallet's main UTXO, as currently known on
|
|
1876
|
+
/// the Ethereum chain
|
|
1877
|
+
/// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
|
|
1878
|
+
/// HASH160 over the compressed ECDSA public key) of the wallet
|
|
1879
|
+
/// which performed the moving funds transaction
|
|
1880
|
+
/// @dev Requirements:
|
|
1881
|
+
/// - `movingFundsTx` components must match the expected structure. See
|
|
1882
|
+
/// `BitcoinTx.Info` docs for reference. Their values must exactly
|
|
1883
|
+
/// correspond to appropriate Bitcoin transaction fields to produce
|
|
1884
|
+
/// a provable transaction hash.
|
|
1885
|
+
/// - The `movingFundsTx` should represent a Bitcoin transaction with
|
|
1886
|
+
/// exactly 1 input that refers to the wallet's main UTXO. That
|
|
1887
|
+
/// transaction should have 1..n outputs corresponding to the
|
|
1888
|
+
/// pre-committed target wallets. Outputs must be ordered in the
|
|
1889
|
+
/// same way as their corresponding target wallets are ordered
|
|
1890
|
+
/// within the target wallets commitment.
|
|
1891
|
+
/// - `movingFundsProof` components must match the expected structure.
|
|
1892
|
+
/// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`
|
|
1893
|
+
/// field must contain a valid number of block headers, not less
|
|
1894
|
+
/// than the `txProofDifficultyFactor` contract constant.
|
|
1895
|
+
/// - `mainUtxo` components must point to the recent main UTXO
|
|
1896
|
+
/// of the given wallet, as currently known on the Ethereum chain.
|
|
1897
|
+
/// Additionally, the recent main UTXO on Ethereum must be set.
|
|
1898
|
+
/// - `walletPubKeyHash` must be connected with the main UTXO used
|
|
1899
|
+
/// as transaction single input.
|
|
1900
|
+
/// - The wallet that `walletPubKeyHash` points to must be in the
|
|
1901
|
+
/// MovingFunds state.
|
|
1902
|
+
/// - The target wallets commitment must be submitted by the wallet
|
|
1903
|
+
/// that `walletPubKeyHash` points to.
|
|
1904
|
+
/// - The total Bitcoin transaction fee must be lesser or equal
|
|
1905
|
+
/// to `movingFundsTxMaxTotalFee` governable parameter.
|
|
1906
|
+
function submitMovingFundsProof(
|
|
1907
|
+
BitcoinTx.Info calldata movingFundsTx,
|
|
1908
|
+
BitcoinTx.Proof calldata movingFundsProof,
|
|
1909
|
+
BitcoinTx.UTXO calldata mainUtxo,
|
|
1910
|
+
bytes20 walletPubKeyHash
|
|
1911
|
+
) external {
|
|
1912
|
+
// The actual transaction proof is performed here. After that point, we
|
|
1913
|
+
// can assume the transaction happened on Bitcoin chain and has
|
|
1914
|
+
// a sufficient number of confirmations as determined by
|
|
1915
|
+
// `txProofDifficultyFactor` constant.
|
|
1916
|
+
bytes32 movingFundsTxHash = BitcoinTx.validateProof(
|
|
1917
|
+
movingFundsTx,
|
|
1918
|
+
movingFundsProof,
|
|
1919
|
+
proofDifficultyContext()
|
|
1920
|
+
);
|
|
1921
|
+
|
|
1922
|
+
// Process the moving funds transaction input. Specifically, check if
|
|
1923
|
+
// it refers to the expected wallet's main UTXO.
|
|
1924
|
+
processWalletOutboundTxInput(
|
|
1925
|
+
movingFundsTx.inputVector,
|
|
1926
|
+
mainUtxo,
|
|
1927
|
+
walletPubKeyHash
|
|
1928
|
+
);
|
|
1929
|
+
|
|
1930
|
+
(
|
|
1931
|
+
bytes32 targetWalletsHash,
|
|
1932
|
+
uint256 outputsTotalValue
|
|
1933
|
+
) = processMovingFundsTxOutputs(movingFundsTx.outputVector);
|
|
1934
|
+
|
|
1935
|
+
require(
|
|
1936
|
+
mainUtxo.txOutputValue - outputsTotalValue <=
|
|
1937
|
+
movingFundsTxMaxTotalFee,
|
|
1938
|
+
"Transaction fee is too high"
|
|
1939
|
+
);
|
|
1940
|
+
|
|
1941
|
+
wallets.notifyFundsMoved(walletPubKeyHash, targetWalletsHash);
|
|
1942
|
+
|
|
1943
|
+
emit MovingFundsCompleted(walletPubKeyHash, movingFundsTxHash);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
/// @notice Processes the moving funds Bitcoin transaction output vector
|
|
1947
|
+
/// and extracts information required for further processing.
|
|
1948
|
+
/// @param movingFundsTxOutputVector Bitcoin moving funds transaction output
|
|
1949
|
+
/// vector. This function assumes vector's structure is valid so it
|
|
1950
|
+
/// must be validated using e.g. `BTCUtils.validateVout` function
|
|
1951
|
+
/// before it is passed here
|
|
1952
|
+
/// @return targetWalletsHash keccak256 hash over the list of actual
|
|
1953
|
+
/// target wallets used in the transaction.
|
|
1954
|
+
/// @return outputsTotalValue Sum of all outputs values.
|
|
1955
|
+
/// @dev Requirements:
|
|
1956
|
+
/// - The `movingFundsTxOutputVector` must be parseable, i.e. must
|
|
1957
|
+
/// be validated by the caller as stated in their parameter doc.
|
|
1958
|
+
/// - Each output must refer to a 20-byte public key hash.
|
|
1959
|
+
/// - The total outputs value must be evenly divided over all outputs.
|
|
1960
|
+
function processMovingFundsTxOutputs(bytes memory movingFundsTxOutputVector)
|
|
1961
|
+
internal
|
|
1962
|
+
view
|
|
1963
|
+
returns (bytes32 targetWalletsHash, uint256 outputsTotalValue)
|
|
1964
|
+
{
|
|
1965
|
+
// Determining the total number of Bitcoin transaction outputs in
|
|
1966
|
+
// the same way as for number of inputs. See `BitcoinTx.outputVector`
|
|
1967
|
+
// docs for more details.
|
|
1968
|
+
(
|
|
1969
|
+
uint256 outputsCompactSizeUintLength,
|
|
1970
|
+
uint256 outputsCount
|
|
1971
|
+
) = movingFundsTxOutputVector.parseVarInt();
|
|
1972
|
+
|
|
1973
|
+
// To determine the first output starting index, we must jump over
|
|
1974
|
+
// the compactSize uint which prepends the output vector. One byte
|
|
1975
|
+
// must be added because `BtcUtils.parseVarInt` does not include
|
|
1976
|
+
// compactSize uint tag in the returned length.
|
|
1977
|
+
//
|
|
1978
|
+
// For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`
|
|
1979
|
+
// returns `0`, so we jump over one byte of compactSize uint.
|
|
1980
|
+
//
|
|
1981
|
+
// For >= 253 && <= 0xffff there is `0xfd` tag,
|
|
1982
|
+
// `BTCUtils.determineVarIntDataLengthAt` returns `2` (no
|
|
1983
|
+
// tag byte included) so we need to jump over 1+2 bytes of
|
|
1984
|
+
// compactSize uint.
|
|
1985
|
+
//
|
|
1986
|
+
// Please refer `BTCUtils` library and compactSize uint
|
|
1987
|
+
// docs in `BitcoinTx` library for more details.
|
|
1988
|
+
uint256 outputStartingIndex = 1 + outputsCompactSizeUintLength;
|
|
1989
|
+
|
|
1990
|
+
bytes20[] memory targetWallets = new bytes20[](outputsCount);
|
|
1991
|
+
uint64[] memory outputsValues = new uint64[](outputsCount);
|
|
1992
|
+
|
|
1993
|
+
// Outputs processing loop.
|
|
1994
|
+
for (uint256 i = 0; i < outputsCount; i++) {
|
|
1995
|
+
uint256 outputLength = movingFundsTxOutputVector
|
|
1996
|
+
.determineOutputLengthAt(outputStartingIndex);
|
|
1997
|
+
|
|
1998
|
+
bytes memory output = movingFundsTxOutputVector.slice(
|
|
1999
|
+
outputStartingIndex,
|
|
2000
|
+
outputLength
|
|
2001
|
+
);
|
|
2002
|
+
|
|
2003
|
+
// Extract the output script payload.
|
|
2004
|
+
bytes memory targetWalletPubKeyHashBytes = output.extractHash();
|
|
2005
|
+
// Output script payload must refer to a known wallet public key
|
|
2006
|
+
// hash which is always 20-byte.
|
|
2007
|
+
require(
|
|
2008
|
+
targetWalletPubKeyHashBytes.length == 20,
|
|
2009
|
+
"Target wallet public key hash must have 20 bytes"
|
|
2010
|
+
);
|
|
2011
|
+
|
|
2012
|
+
bytes20 targetWalletPubKeyHash = targetWalletPubKeyHashBytes
|
|
2013
|
+
.slice20(0);
|
|
2014
|
+
|
|
2015
|
+
// The next step is making sure that the 20-byte public key hash
|
|
2016
|
+
// is actually used in the right context of a P2PKH or P2WPKH
|
|
2017
|
+
// output. To do so, we must extract the full script from the output
|
|
2018
|
+
// and compare with the expected P2PKH and P2WPKH scripts
|
|
2019
|
+
// referring to that 20-byte public key hash. The output consists
|
|
2020
|
+
// of an 8-byte value and a variable length script. To extract the
|
|
2021
|
+
// script we slice the output starting from 9th byte until the end.
|
|
2022
|
+
bytes32 outputScriptKeccak = keccak256(
|
|
2023
|
+
output.slice(8, output.length - 8)
|
|
2024
|
+
);
|
|
2025
|
+
// Build the expected P2PKH script which has the following byte
|
|
2026
|
+
// format: <0x1976a914> <20-byte PKH> <0x88ac>. According to
|
|
2027
|
+
// https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
|
|
2028
|
+
// - 0x19: Byte length of the entire script
|
|
2029
|
+
// - 0x76: OP_DUP
|
|
2030
|
+
// - 0xa9: OP_HASH160
|
|
2031
|
+
// - 0x14: Byte length of the public key hash
|
|
2032
|
+
// - 0x88: OP_EQUALVERIFY
|
|
2033
|
+
// - 0xac: OP_CHECKSIG
|
|
2034
|
+
// which matches the P2PKH structure as per:
|
|
2035
|
+
// https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash
|
|
2036
|
+
bytes32 targetWalletP2PKHScriptKeccak = keccak256(
|
|
2037
|
+
abi.encodePacked(
|
|
2038
|
+
hex"1976a914",
|
|
2039
|
+
targetWalletPubKeyHash,
|
|
2040
|
+
hex"88ac"
|
|
2041
|
+
)
|
|
2042
|
+
);
|
|
2043
|
+
// Build the expected P2WPKH script which has the following format:
|
|
2044
|
+
// <0x160014> <20-byte PKH>. According to
|
|
2045
|
+
// https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
|
|
2046
|
+
// - 0x16: Byte length of the entire script
|
|
2047
|
+
// - 0x00: OP_0
|
|
2048
|
+
// - 0x14: Byte length of the public key hash
|
|
2049
|
+
// which matches the P2WPKH structure as per:
|
|
2050
|
+
// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH
|
|
2051
|
+
bytes32 targetWalletP2WPKHScriptKeccak = keccak256(
|
|
2052
|
+
abi.encodePacked(hex"160014", targetWalletPubKeyHash)
|
|
2053
|
+
);
|
|
2054
|
+
// Make sure the actual output script matches either the P2PKH
|
|
2055
|
+
// or P2WPKH format.
|
|
2056
|
+
require(
|
|
2057
|
+
outputScriptKeccak == targetWalletP2PKHScriptKeccak ||
|
|
2058
|
+
outputScriptKeccak == targetWalletP2WPKHScriptKeccak,
|
|
2059
|
+
"Output must be P2PKH or P2WPKH"
|
|
2060
|
+
);
|
|
2061
|
+
|
|
2062
|
+
// Add the wallet public key hash to the list that will be used
|
|
2063
|
+
// to build the result list hash. There is no need to check if
|
|
2064
|
+
// given output is a change here because the actual target wallet
|
|
2065
|
+
// list must be exactly the same as the pre-committed target wallet
|
|
2066
|
+
// list which is guaranteed to be valid.
|
|
2067
|
+
targetWallets[i] = targetWalletPubKeyHash;
|
|
2068
|
+
|
|
2069
|
+
// Extract the value from given output.
|
|
2070
|
+
outputsValues[i] = output.extractValue();
|
|
2071
|
+
outputsTotalValue += outputsValues[i];
|
|
2072
|
+
|
|
2073
|
+
// Make the `outputStartingIndex` pointing to the next output by
|
|
2074
|
+
// increasing it by current output's length.
|
|
2075
|
+
outputStartingIndex += outputLength;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
// Compute the indivisible remainder that remains after dividing the
|
|
2079
|
+
// outputs total value over all outputs evenly.
|
|
2080
|
+
uint256 outputsTotalValueRemainder = outputsTotalValue % outputsCount;
|
|
2081
|
+
// Compute the minimum allowed output value by dividing the outputs
|
|
2082
|
+
// total value (reduced by the remainder) by the number of outputs.
|
|
2083
|
+
uint256 minOutputValue = (outputsTotalValue -
|
|
2084
|
+
outputsTotalValueRemainder) / outputsCount;
|
|
2085
|
+
// Maximum possible value is the minimum value with the remainder included.
|
|
2086
|
+
uint256 maxOutputValue = minOutputValue + outputsTotalValueRemainder;
|
|
2087
|
+
|
|
2088
|
+
for (uint256 i = 0; i < outputsCount; i++) {
|
|
2089
|
+
require(
|
|
2090
|
+
minOutputValue <= outputsValues[i] &&
|
|
2091
|
+
outputsValues[i] <= maxOutputValue,
|
|
2092
|
+
"Transaction amount is not distributed evenly"
|
|
2093
|
+
);
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
targetWalletsHash = keccak256(abi.encodePacked(targetWallets));
|
|
2097
|
+
|
|
2098
|
+
return (targetWalletsHash, outputsTotalValue);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
/// @notice Returns the current values of Bridge deposit parameters.
|
|
2102
|
+
/// @return depositDustThreshold The minimal amount that can be requested
|
|
2103
|
+
/// to deposit. Value of this parameter must take into account the
|
|
2104
|
+
/// value of `depositTreasuryFeeDivisor` and `depositTxMaxFee`
|
|
2105
|
+
/// parameters in order to make requests that can incur the
|
|
2106
|
+
/// treasury and transaction fee and still satisfy the depositor.
|
|
2107
|
+
/// @return depositTreasuryFeeDivisor Divisor used to compute the treasury
|
|
2108
|
+
/// fee taken from each deposit and transferred to the treasury upon
|
|
2109
|
+
/// sweep proof submission. That fee is computed as follows:
|
|
2110
|
+
/// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
|
|
2111
|
+
/// For example, if the treasury fee needs to be 2% of each deposit,
|
|
2112
|
+
/// the `depositTreasuryFeeDivisor` should be set to `50`
|
|
2113
|
+
/// because `1/50 = 0.02 = 2%`.
|
|
2114
|
+
function depositParameters()
|
|
2115
|
+
external
|
|
2116
|
+
view
|
|
2117
|
+
returns (uint64 depositDustThreshold, uint64 depositTreasuryFeeDivisor)
|
|
2118
|
+
{
|
|
2119
|
+
depositDustThreshold = self.depositDustThreshold;
|
|
2120
|
+
depositTreasuryFeeDivisor = self.depositTreasuryFeeDivisor;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
/// @notice Indicates if the vault with the given address is trusted or not.
|
|
2124
|
+
/// Depositors can route their revealed deposits only to trusted
|
|
2125
|
+
/// vaults and have trusted vaults notified about new deposits as
|
|
2126
|
+
/// soon as these deposits get swept. Vaults not trusted by the
|
|
2127
|
+
/// Bridge can still be used by Bank balance owners on their own
|
|
2128
|
+
/// responsibility - anyone can approve their Bank balance to any
|
|
2129
|
+
/// address.
|
|
2130
|
+
function isVaultTrusted(address vault) external view returns (bool) {
|
|
2131
|
+
return self.isVaultTrusted[vault];
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
/// @notice Collection of all revealed deposits indexed by
|
|
2135
|
+
/// keccak256(fundingTxHash | fundingOutputIndex).
|
|
2136
|
+
/// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
|
|
2137
|
+
/// and fundingOutputIndex an uint32. This mapping may contain valid
|
|
2138
|
+
/// and invalid deposits and the wallet is responsible for
|
|
2139
|
+
/// validating them before attempting to execute a sweep.
|
|
2140
|
+
function deposits(uint256 depositKey)
|
|
2141
|
+
external
|
|
2142
|
+
view
|
|
2143
|
+
returns (Deposit.Request memory)
|
|
2144
|
+
{
|
|
2145
|
+
// TODO: rename to getDeposit?
|
|
2146
|
+
return self.deposits[depositKey];
|
|
2147
|
+
}
|
|
2013
2148
|
}
|