@keep-network/tbtc-v2 0.1.1-dev.9 → 0.1.1-dev.90
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 +817 -0
- package/artifacts/Bridge.json +2666 -0
- package/artifacts/Deposit.json +117 -0
- package/artifacts/DepositSweep.json +77 -0
- package/artifacts/EcdsaDkgValidator.json +532 -0
- package/artifacts/EcdsaInactivity.json +156 -0
- package/artifacts/EcdsaSortitionPool.json +1004 -0
- package/artifacts/Fraud.json +164 -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 +249 -0
- package/artifacts/NuCypherStakingEscrow.json +256 -0
- package/artifacts/NuCypherToken.json +711 -0
- package/artifacts/RandomBeaconStub.json +141 -0
- package/artifacts/Redemption.json +174 -0
- package/artifacts/ReimbursementPool.json +509 -0
- package/artifacts/Relay.json +123 -0
- package/artifacts/T.json +1148 -0
- package/artifacts/TBTC.json +27 -26
- package/artifacts/TBTCToken.json +27 -26
- package/artifacts/TBTCVault.json +691 -0
- 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 +1843 -0
- package/artifacts/WalletRegistryGovernance.json +2754 -0
- package/artifacts/Wallets.json +186 -0
- package/artifacts/solcInputs/6e28b66ed60b6dd7b2b25b3a1c4fe89b.json +314 -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 +27 -4
- package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +4 -0
- package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.json +34 -0
- 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 +2547 -196
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
- package/build/contracts/bridge/BridgeState.sol/BridgeState.json +226 -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/DepositSweep.sol/DepositSweep.dbg.json +4 -0
- package/build/contracts/bridge/DepositSweep.sol/DepositSweep.json +30 -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/Heartbeat.sol/Heartbeat.dbg.json +4 -0
- package/build/contracts/bridge/Heartbeat.sol/Heartbeat.json +10 -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 +138 -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/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/DonationVault.sol/DonationVault.dbg.json +4 -0
- package/build/contracts/vault/DonationVault.sol/DonationVault.json +108 -0
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.json +24 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +295 -9
- package/contracts/GovernanceUtils.sol +4 -4
- package/contracts/bank/Bank.sol +119 -57
- package/contracts/bank/IReceiveBalanceApproval.sol +45 -0
- package/contracts/bridge/BitcoinTx.sol +232 -10
- package/contracts/bridge/Bridge.sol +1692 -244
- package/contracts/bridge/BridgeState.sol +739 -0
- package/contracts/bridge/Deposit.sol +269 -0
- package/contracts/bridge/DepositSweep.sol +571 -0
- package/contracts/bridge/EcdsaLib.sol +45 -0
- package/contracts/bridge/Fraud.sol +604 -0
- package/contracts/bridge/Heartbeat.sol +112 -0
- package/contracts/bridge/IRelay.sol +28 -0
- package/contracts/bridge/MovingFunds.sol +1089 -0
- package/contracts/bridge/Redemption.sol +1021 -0
- package/contracts/bridge/VendingMachine.sol +1 -1
- package/contracts/bridge/Wallets.sol +553 -0
- package/contracts/hardhat-dependency-compiler/.hardhat-dependency-compiler +1 -0
- package/contracts/hardhat-dependency-compiler/@keep-network/ecdsa/contracts/WalletRegistry.sol +3 -0
- package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol +3 -0
- package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol +3 -0
- package/contracts/token/TBTC.sol +1 -1
- package/contracts/vault/DonationVault.sol +125 -0
- package/contracts/vault/IVault.sol +19 -13
- package/contracts/vault/TBTCVault.sol +200 -23
- package/deploy/00_resolve_relay.ts +28 -0
- package/deploy/{03_transfer_roles.ts → 03_transfer_vending_machine_roles.ts} +1 -1
- package/deploy/04_deploy_bank.ts +27 -0
- package/deploy/05_deploy_bridge.ts +80 -0
- package/deploy/06_deploy_tbtc_vault.ts +30 -0
- package/deploy/07_bank_update_bridge.ts +19 -0
- package/deploy/08_transfer_bank_ownership.ts +15 -0
- package/deploy/09_transfer_tbtc_vault_ownership.ts +15 -0
- package/deploy/10_transfer_bridge_governance.ts +20 -0
- package/deploy/11_initialize_wallet_owner.ts +18 -0
- package/deploy/11_transfer_proxy_admin_ownership.ts +30 -0
- package/deploy/12_deploy_proxy_admin_with_deputy.ts +33 -0
- package/export.json +16150 -443
- package/package.json +32 -25
- package/artifacts/solcInputs/58d5b3ee7688835879381470de985d6b.json +0 -128
package/contracts/bank/Bank.sol
CHANGED
|
@@ -13,24 +13,25 @@
|
|
|
13
13
|
// ▐████▌ ▐████▌
|
|
14
14
|
// ▐████▌ ▐████▌
|
|
15
15
|
|
|
16
|
-
pragma solidity 0.8.
|
|
16
|
+
pragma solidity ^0.8.9;
|
|
17
17
|
|
|
18
18
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
19
19
|
|
|
20
|
+
import "./IReceiveBalanceApproval.sol";
|
|
20
21
|
import "../vault/IVault.sol";
|
|
21
22
|
|
|
22
23
|
/// @title Bitcoin Bank
|
|
23
24
|
/// @notice Bank is a central component tracking Bitcoin balances. Balances can
|
|
24
|
-
/// be transferred between
|
|
25
|
-
/// balances to be spent by others. Balances in the Bank
|
|
26
|
-
/// depositors who
|
|
27
|
-
/// Bridge can increase balances.
|
|
25
|
+
/// be transferred between balance owners, and balance owners can
|
|
26
|
+
/// approve their balances to be spent by others. Balances in the Bank
|
|
27
|
+
/// are updated for depositors who deposited their Bitcoin into the
|
|
28
|
+
/// Bridge and only the Bridge can increase balances.
|
|
28
29
|
/// @dev Bank is a governable contract and the Governance can upgrade the Bridge
|
|
29
30
|
/// address.
|
|
30
31
|
contract Bank is Ownable {
|
|
31
32
|
address public bridge;
|
|
32
33
|
|
|
33
|
-
/// @notice The balance of
|
|
34
|
+
/// @notice The balance of the given account in the Bank. Zero by default.
|
|
34
35
|
mapping(address => uint256) public balanceOf;
|
|
35
36
|
|
|
36
37
|
/// @notice The remaining amount of balance a spender will be
|
|
@@ -38,16 +39,17 @@ contract Bank is Ownable {
|
|
|
38
39
|
/// `transferBalanceFrom`. Zero by default.
|
|
39
40
|
mapping(address => mapping(address => uint256)) public allowance;
|
|
40
41
|
|
|
41
|
-
/// @notice Returns the current nonce for EIP2612 permission for the
|
|
42
|
-
/// provided balance owner
|
|
43
|
-
/// construct EIP2612 signature provided to `permit`
|
|
42
|
+
/// @notice Returns the current nonce for an EIP2612 permission for the
|
|
43
|
+
/// provided balance owner to protect against replay attacks. Used
|
|
44
|
+
/// to construct an EIP2612 signature provided to the `permit`
|
|
45
|
+
/// function.
|
|
44
46
|
mapping(address => uint256) public nonce;
|
|
45
47
|
|
|
46
48
|
uint256 public immutable cachedChainId;
|
|
47
49
|
bytes32 public immutable cachedDomainSeparator;
|
|
48
50
|
|
|
49
|
-
/// @notice Returns EIP2612 Permit message hash. Used to construct
|
|
50
|
-
/// signature provided to `permit` function.
|
|
51
|
+
/// @notice Returns an EIP2612 Permit message hash. Used to construct
|
|
52
|
+
/// an EIP2612 signature provided to the `permit` function.
|
|
51
53
|
bytes32 public constant PERMIT_TYPEHASH =
|
|
52
54
|
keccak256(
|
|
53
55
|
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
|
@@ -86,6 +88,9 @@ contract Bank is Ownable {
|
|
|
86
88
|
/// check the status of the Bridge. The Governance implementation needs
|
|
87
89
|
/// to ensure all requirements for the upgrade are satisfied before
|
|
88
90
|
/// executing this function.
|
|
91
|
+
/// Requirements:
|
|
92
|
+
/// - The new Bridge address must not be zero.
|
|
93
|
+
/// @param _bridge The new Bridge address.
|
|
89
94
|
function updateBridge(address _bridge) external onlyOwner {
|
|
90
95
|
require(_bridge != address(0), "Bridge address must not be 0x0");
|
|
91
96
|
bridge = _bridge;
|
|
@@ -97,25 +102,59 @@ contract Bank is Ownable {
|
|
|
97
102
|
/// @dev Requirements:
|
|
98
103
|
/// - `recipient` cannot be the zero address,
|
|
99
104
|
/// - the caller must have a balance of at least `amount`.
|
|
105
|
+
/// @param recipient The recipient of the balance.
|
|
106
|
+
/// @param amount The amount of the balance transferred.
|
|
100
107
|
function transferBalance(address recipient, uint256 amount) external {
|
|
101
108
|
_transferBalance(msg.sender, recipient, amount);
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
/// @notice Sets `amount` as the allowance of `spender` over the caller's
|
|
105
112
|
/// balance.
|
|
106
|
-
/// @dev If the `amount` is set to `type(uint256).max
|
|
113
|
+
/// @dev If the `amount` is set to `type(uint256).max`,
|
|
107
114
|
/// `transferBalanceFrom` will not reduce an allowance.
|
|
108
115
|
/// Beware that changing an allowance with this function brings the
|
|
109
116
|
/// risk that someone may use both the old and the new allowance by
|
|
110
117
|
/// unfortunate transaction ordering. Please use
|
|
111
118
|
/// `increaseBalanceAllowance` and `decreaseBalanceAllowance` to
|
|
112
119
|
/// eliminate the risk.
|
|
120
|
+
/// @param spender The address that will be allowed to spend the balance.
|
|
121
|
+
/// @param amount The amount the spender is allowed to spend.
|
|
113
122
|
function approveBalance(address spender, uint256 amount) external {
|
|
114
123
|
_approveBalance(msg.sender, spender, amount);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
/// @notice
|
|
118
|
-
///
|
|
126
|
+
/// @notice Sets the `amount` as an allowance of a smart contract `spender`
|
|
127
|
+
/// over the caller's balance and calls the `spender` via
|
|
128
|
+
/// `receiveBalanceApproval`.
|
|
129
|
+
/// @dev If the `amount` is set to `type(uint256).max`, the potential
|
|
130
|
+
/// `transferBalanceFrom` executed in `receiveBalanceApproval` of
|
|
131
|
+
/// `spender` will not reduce an allowance. Beware that changing an
|
|
132
|
+
/// allowance with this function brings the risk that `spender` may use
|
|
133
|
+
/// both the old and the new allowance by unfortunate transaction
|
|
134
|
+
/// ordering. Please use `increaseBalanceAllowance` and
|
|
135
|
+
/// `decreaseBalanceAllowance` to eliminate the risk.
|
|
136
|
+
/// @param spender The smart contract that will be allowed to spend the
|
|
137
|
+
/// balance.
|
|
138
|
+
/// @param amount The amount the spender contract is allowed to spend.
|
|
139
|
+
/// @param extraData Extra data passed to the `spender` contract via
|
|
140
|
+
/// `receiveBalanceApproval` call.
|
|
141
|
+
function approveBalanceAndCall(
|
|
142
|
+
address spender,
|
|
143
|
+
uint256 amount,
|
|
144
|
+
bytes calldata extraData
|
|
145
|
+
) external {
|
|
146
|
+
_approveBalance(msg.sender, spender, amount);
|
|
147
|
+
IReceiveBalanceApproval(spender).receiveBalanceApproval(
|
|
148
|
+
msg.sender,
|
|
149
|
+
amount,
|
|
150
|
+
extraData
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// @notice Atomically increases the caller's balance allowance granted to
|
|
155
|
+
/// `spender` by the given `addedValue`.
|
|
156
|
+
/// @param spender The spender address for which the allowance is increased.
|
|
157
|
+
/// @param addedValue The amount by which the allowance is increased.
|
|
119
158
|
function increaseBalanceAllowance(address spender, uint256 addedValue)
|
|
120
159
|
external
|
|
121
160
|
{
|
|
@@ -126,8 +165,14 @@ contract Bank is Ownable {
|
|
|
126
165
|
);
|
|
127
166
|
}
|
|
128
167
|
|
|
129
|
-
/// @notice Atomically decreases the balance allowance granted to
|
|
130
|
-
///
|
|
168
|
+
/// @notice Atomically decreases the caller's balance allowance granted to
|
|
169
|
+
/// `spender` by the given `subtractedValue`.
|
|
170
|
+
/// @dev Requirements:
|
|
171
|
+
/// - `spender` must not be the zero address,
|
|
172
|
+
/// - the current allowance for `spender` must not be lower than
|
|
173
|
+
/// the `subtractedValue`.
|
|
174
|
+
/// @param spender The spender address for which the allowance is decreased.
|
|
175
|
+
/// @param subtractedValue The amount by which the allowance is decreased.
|
|
131
176
|
function decreaseBalanceAllowance(address spender, uint256 subtractedValue)
|
|
132
177
|
external
|
|
133
178
|
{
|
|
@@ -151,8 +196,11 @@ contract Bank is Ownable {
|
|
|
151
196
|
/// @dev Requirements:
|
|
152
197
|
/// - `recipient` cannot be the zero address,
|
|
153
198
|
/// - `spender` must have a balance of at least `amount`,
|
|
154
|
-
/// - the caller must have allowance for `spender`'s balance of at
|
|
199
|
+
/// - the caller must have an allowance for `spender`'s balance of at
|
|
155
200
|
/// least `amount`.
|
|
201
|
+
/// @param spender The address from which the balance is transferred.
|
|
202
|
+
/// @param recipient The address to which the balance is transferred.
|
|
203
|
+
/// @param amount The amount of balance that is transferred.
|
|
156
204
|
function transferBalanceFrom(
|
|
157
205
|
address spender,
|
|
158
206
|
address recipient,
|
|
@@ -171,12 +219,13 @@ contract Bank is Ownable {
|
|
|
171
219
|
_transferBalance(spender, recipient, amount);
|
|
172
220
|
}
|
|
173
221
|
|
|
174
|
-
/// @notice EIP2612 approval made with secp256k1 signature.
|
|
175
|
-
///
|
|
176
|
-
/// conforming EIP712 standard, rather than an on-chain
|
|
177
|
-
/// from their address. Anyone can submit this signature
|
|
178
|
-
/// user's behalf by calling the permit function, paying
|
|
179
|
-
/// and possibly performing other actions in the same
|
|
222
|
+
/// @notice An EIP2612 approval made with secp256k1 signature. Users can
|
|
223
|
+
/// authorize a transfer of their balance with a signature
|
|
224
|
+
/// conforming to the EIP712 standard, rather than an on-chain
|
|
225
|
+
/// transaction from their address. Anyone can submit this signature
|
|
226
|
+
/// on the user's behalf by calling the `permit` function, paying
|
|
227
|
+
/// gas fees, and possibly performing other actions in the same
|
|
228
|
+
/// transaction.
|
|
180
229
|
/// @dev The deadline argument can be set to `type(uint256).max to create
|
|
181
230
|
/// permits that effectively never expire. If the `amount` is set
|
|
182
231
|
/// to `type(uint256).max` then `transferBalanceFrom` will not
|
|
@@ -185,6 +234,13 @@ contract Bank is Ownable {
|
|
|
185
234
|
/// new allowance by unfortunate transaction ordering. Please use
|
|
186
235
|
/// `increaseBalanceAllowance` and `decreaseBalanceAllowance` to
|
|
187
236
|
/// eliminate the risk.
|
|
237
|
+
/// @param owner The balance owner who signed the permission.
|
|
238
|
+
/// @param spender The address that will be allowed to spend the balance.
|
|
239
|
+
/// @param amount The amount the spender is allowed to spend.
|
|
240
|
+
/// @param deadline The UNIX time until which the permit is valid.
|
|
241
|
+
/// @param v V part of the permit signature.
|
|
242
|
+
/// @param r R part of the permit signature.
|
|
243
|
+
/// @param s S part of the permit signature.
|
|
188
244
|
function permit(
|
|
189
245
|
address owner,
|
|
190
246
|
address spender,
|
|
@@ -207,23 +263,22 @@ contract Bank is Ownable {
|
|
|
207
263
|
);
|
|
208
264
|
require(v == 27 || v == 28, "Invalid signature 'v' value");
|
|
209
265
|
|
|
210
|
-
bytes32 digest =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
deadline
|
|
223
|
-
)
|
|
266
|
+
bytes32 digest = keccak256(
|
|
267
|
+
abi.encodePacked(
|
|
268
|
+
"\x19\x01",
|
|
269
|
+
DOMAIN_SEPARATOR(),
|
|
270
|
+
keccak256(
|
|
271
|
+
abi.encode(
|
|
272
|
+
PERMIT_TYPEHASH,
|
|
273
|
+
owner,
|
|
274
|
+
spender,
|
|
275
|
+
amount,
|
|
276
|
+
nonce[owner]++,
|
|
277
|
+
deadline
|
|
224
278
|
)
|
|
225
279
|
)
|
|
226
|
-
)
|
|
280
|
+
)
|
|
281
|
+
);
|
|
227
282
|
address recoveredAddress = ecrecover(digest, v, r, s);
|
|
228
283
|
require(
|
|
229
284
|
recoveredAddress != address(0) && recoveredAddress == owner,
|
|
@@ -235,7 +290,10 @@ contract Bank is Ownable {
|
|
|
235
290
|
/// @notice Increases balances of the provided `recipients` by the provided
|
|
236
291
|
/// `amounts`. Can only be called by the Bridge.
|
|
237
292
|
/// @dev Requirements:
|
|
238
|
-
/// - length of `recipients` and `amounts` must be the same
|
|
293
|
+
/// - length of `recipients` and `amounts` must be the same,
|
|
294
|
+
/// - none of `recipients` addresses must point to the Bank.
|
|
295
|
+
/// @param recipients Balance increase recipients.
|
|
296
|
+
/// @param amounts Amounts by which balances are increased.
|
|
239
297
|
function increaseBalances(
|
|
240
298
|
address[] calldata recipients,
|
|
241
299
|
uint256[] calldata amounts
|
|
@@ -251,6 +309,10 @@ contract Bank is Ownable {
|
|
|
251
309
|
|
|
252
310
|
/// @notice Increases balance of the provided `recipient` by the provided
|
|
253
311
|
/// `amount`. Can only be called by the Bridge.
|
|
312
|
+
/// @dev Requirements:
|
|
313
|
+
/// - `recipient` address must not point to the Bank.
|
|
314
|
+
/// @param recipient Balance increase recipient.
|
|
315
|
+
/// @param amount Amount by which the balance is increased.
|
|
254
316
|
function increaseBalance(address recipient, uint256 amount)
|
|
255
317
|
external
|
|
256
318
|
onlyBridge
|
|
@@ -259,39 +321,37 @@ contract Bank is Ownable {
|
|
|
259
321
|
}
|
|
260
322
|
|
|
261
323
|
/// @notice Increases the given smart contract `vault`'s balance and
|
|
262
|
-
/// notifies the `vault` contract
|
|
263
|
-
/// the deposits routed by depositors to that `vault` have been
|
|
264
|
-
/// swept by the Bridge. This way, the depositor does not have to
|
|
265
|
-
/// issue a separate transaction to the `vault` contract.
|
|
324
|
+
/// notifies the `vault` contract about it.
|
|
266
325
|
/// Can be called only by the Bridge.
|
|
267
326
|
/// @dev Requirements:
|
|
268
327
|
/// - `vault` must implement `IVault` interface,
|
|
269
|
-
/// - length of `
|
|
270
|
-
/// @param vault Address of `IVault` recipient contract
|
|
271
|
-
/// @param
|
|
272
|
-
/// @param
|
|
273
|
-
/// swept. The `vault`'s balance in the Bank will be increased by the
|
|
274
|
-
/// sum of all elements in this array.
|
|
328
|
+
/// - length of `recipients` and `amounts` must be the same.
|
|
329
|
+
/// @param vault Address of `IVault` recipient contract.
|
|
330
|
+
/// @param recipients Balance increase recipients.
|
|
331
|
+
/// @param amounts Amounts by which balances are increased.
|
|
275
332
|
function increaseBalanceAndCall(
|
|
276
333
|
address vault,
|
|
277
|
-
address[] calldata
|
|
278
|
-
uint256[] calldata
|
|
334
|
+
address[] calldata recipients,
|
|
335
|
+
uint256[] calldata amounts
|
|
279
336
|
) external onlyBridge {
|
|
280
337
|
require(
|
|
281
|
-
|
|
338
|
+
recipients.length == amounts.length,
|
|
282
339
|
"Arrays must have the same length"
|
|
283
340
|
);
|
|
284
341
|
uint256 totalAmount = 0;
|
|
285
|
-
for (uint256 i = 0; i <
|
|
286
|
-
totalAmount +=
|
|
342
|
+
for (uint256 i = 0; i < amounts.length; i++) {
|
|
343
|
+
totalAmount += amounts[i];
|
|
287
344
|
}
|
|
288
345
|
_increaseBalance(vault, totalAmount);
|
|
289
|
-
IVault(vault).
|
|
346
|
+
IVault(vault).receiveBalanceIncrease(recipients, amounts);
|
|
290
347
|
}
|
|
291
348
|
|
|
292
349
|
/// @notice Decreases caller's balance by the provided `amount`. There is no
|
|
293
350
|
/// way to restore the balance so do not call this function unless
|
|
294
351
|
/// you really know what you are doing!
|
|
352
|
+
/// @dev Requirements:
|
|
353
|
+
/// - The caller must have a balance of at least `amount`.
|
|
354
|
+
/// @param amount The amount by which the balance is decreased.
|
|
295
355
|
function decreaseBalance(uint256 amount) external {
|
|
296
356
|
balanceOf[msg.sender] -= amount;
|
|
297
357
|
emit BalanceDecreased(msg.sender, amount);
|
|
@@ -299,7 +359,7 @@ contract Bank is Ownable {
|
|
|
299
359
|
|
|
300
360
|
/// @notice Returns hash of EIP712 Domain struct with `TBTC Bank` as
|
|
301
361
|
/// a signing domain and Bank contract as a verifying contract.
|
|
302
|
-
/// Used to construct EIP2612 signature provided to `permit`
|
|
362
|
+
/// Used to construct an EIP2612 signature provided to the `permit`
|
|
303
363
|
/// function.
|
|
304
364
|
/* solhint-disable-next-line func-name-mixedcase */
|
|
305
365
|
function DOMAIN_SEPARATOR() public view returns (bytes32) {
|
|
@@ -342,7 +402,9 @@ contract Bank is Ownable {
|
|
|
342
402
|
|
|
343
403
|
uint256 spenderBalance = balanceOf[spender];
|
|
344
404
|
require(spenderBalance >= amount, "Transfer amount exceeds balance");
|
|
345
|
-
unchecked {
|
|
405
|
+
unchecked {
|
|
406
|
+
balanceOf[spender] = spenderBalance - amount;
|
|
407
|
+
}
|
|
346
408
|
balanceOf[recipient] += amount;
|
|
347
409
|
emit BalanceTransferred(spender, recipient, amount);
|
|
348
410
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 IReceiveBalanceApproval
|
|
19
|
+
/// @notice `IReceiveBalanceApproval` is an interface for a smart contract
|
|
20
|
+
/// consuming Bank balances approved to them in the same transaction by
|
|
21
|
+
/// other contracts or externally owned accounts (EOA).
|
|
22
|
+
interface IReceiveBalanceApproval {
|
|
23
|
+
/// @notice Called by the Bank in `approveBalanceAndCall` function after
|
|
24
|
+
/// the balance `owner` approved `amount` of their balance in the
|
|
25
|
+
/// Bank for the contract. This way, the depositor can approve
|
|
26
|
+
/// balance and call the contract to use the approved balance in
|
|
27
|
+
/// a single transaction.
|
|
28
|
+
/// @param owner Address of the Bank balance owner who approved their
|
|
29
|
+
/// balance to be used by the contract.
|
|
30
|
+
/// @param amount The amount of the Bank balance approved by the owner
|
|
31
|
+
/// to be used by the contract.
|
|
32
|
+
/// @param extraData The `extraData` passed to `Bank.approveBalanceAndCall`.
|
|
33
|
+
/// @dev The implementation must ensure this function can only be called
|
|
34
|
+
/// by the Bank. The Bank does _not_ guarantee that the `amount`
|
|
35
|
+
/// approved by the `owner` currently exists on their balance. That is,
|
|
36
|
+
/// the `owner` could approve more balance than they currently have.
|
|
37
|
+
/// This works the same as `Bank.approve` function. The contract must
|
|
38
|
+
/// ensure the actual balance is checked before performing any action
|
|
39
|
+
/// based on it.
|
|
40
|
+
function receiveBalanceApproval(
|
|
41
|
+
address owner,
|
|
42
|
+
uint256 amount,
|
|
43
|
+
bytes calldata extraData
|
|
44
|
+
) external;
|
|
45
|
+
}
|
|
@@ -13,20 +13,26 @@
|
|
|
13
13
|
// ▐████▌ ▐████▌
|
|
14
14
|
// ▐████▌ ▐████▌
|
|
15
15
|
|
|
16
|
-
pragma solidity 0.8.
|
|
16
|
+
pragma solidity ^0.8.9;
|
|
17
|
+
|
|
18
|
+
import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
|
|
19
|
+
import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
|
|
20
|
+
import {ValidateSPV} from "@keep-network/bitcoin-spv-sol/contracts/ValidateSPV.sol";
|
|
21
|
+
|
|
22
|
+
import "./BridgeState.sol";
|
|
17
23
|
|
|
18
24
|
/// @title Bitcoin transaction
|
|
19
25
|
/// @notice Allows to reference Bitcoin raw transaction in Solidity.
|
|
20
26
|
/// @dev See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format
|
|
21
27
|
///
|
|
22
|
-
/// Raw
|
|
28
|
+
/// Raw Bitcoin transaction data:
|
|
23
29
|
///
|
|
24
30
|
/// | Bytes | Name | BTC type | Description |
|
|
25
31
|
/// |--------|--------------|------------------------|---------------------------|
|
|
26
32
|
/// | 4 | version | int32_t (LE) | TX version number |
|
|
27
33
|
/// | varies | tx_in_count | compactSize uint (LE) | Number of TX inputs |
|
|
28
34
|
/// | varies | tx_in | txIn[] | TX inputs |
|
|
29
|
-
/// | varies |
|
|
35
|
+
/// | varies | tx_out_count | compactSize uint (LE) | Number of TX outputs |
|
|
30
36
|
/// | varies | tx_out | txOut[] | TX outputs |
|
|
31
37
|
/// | 4 | lock_time | uint32_t (LE) | Unix time or block number |
|
|
32
38
|
///
|
|
@@ -36,8 +42,8 @@ pragma solidity 0.8.4;
|
|
|
36
42
|
/// | Bytes | Name | BTC type | Description |
|
|
37
43
|
/// |--------|------------------|------------------------|---------------------------------------------|
|
|
38
44
|
/// | 36 | previous_output | outpoint | The previous outpoint being spent |
|
|
39
|
-
/// | varies |
|
|
40
|
-
/// | varies |
|
|
45
|
+
/// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the signature script |
|
|
46
|
+
/// | varies | signature_script | char[] | The signature script, empty for P2WSH |
|
|
41
47
|
/// | 4 | sequence | uint32_t (LE) | Sequence number |
|
|
42
48
|
///
|
|
43
49
|
///
|
|
@@ -68,17 +74,37 @@ pragma solidity 0.8.4;
|
|
|
68
74
|
///
|
|
69
75
|
/// (*) compactSize uint is often references as VarInt)
|
|
70
76
|
///
|
|
77
|
+
/// Coinbase transaction input (txIn):
|
|
78
|
+
///
|
|
79
|
+
/// | Bytes | Name | BTC type | Description |
|
|
80
|
+
/// |--------|------------------|------------------------|---------------------------------------------|
|
|
81
|
+
/// | 32 | hash | char[32] | A 32-byte 0x0 null (no previous_outpoint) |
|
|
82
|
+
/// | 4 | index | uint32_t (LE) | 0xffffffff (no previous_outpoint) |
|
|
83
|
+
/// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the coinbase script |
|
|
84
|
+
/// | varies | height | char[] | The block height of this block (BIP34) (*) |
|
|
85
|
+
/// | varies | coinbase_script | none | Arbitrary data, max 100 bytes |
|
|
86
|
+
/// | 4 | sequence | uint32_t (LE) | Sequence number
|
|
87
|
+
///
|
|
88
|
+
/// (*) Uses script language: starts with a data-pushing opcode that indicates how many bytes to push to
|
|
89
|
+
/// the stack followed by the block height as a little-endian unsigned integer. This script must be as
|
|
90
|
+
/// short as possible, otherwise it may be rejected. The data-pushing opcode will be 0x03 and the total
|
|
91
|
+
/// size four bytes until block 16,777,216 about 300 years from now.
|
|
71
92
|
library BitcoinTx {
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
using BTCUtils for bytes;
|
|
94
|
+
using BTCUtils for uint256;
|
|
95
|
+
using BytesLib for bytes;
|
|
96
|
+
using ValidateSPV for bytes;
|
|
97
|
+
using ValidateSPV for bytes32;
|
|
98
|
+
|
|
99
|
+
/// @notice Represents Bitcoin transaction data.
|
|
74
100
|
struct Info {
|
|
75
|
-
/// @notice Bitcoin transaction version
|
|
76
|
-
/// @dev `version` from raw
|
|
101
|
+
/// @notice Bitcoin transaction version.
|
|
102
|
+
/// @dev `version` from raw Bitcoin transaction data.
|
|
77
103
|
/// Encoded as 4-bytes signed integer, little endian.
|
|
78
104
|
bytes4 version;
|
|
79
105
|
/// @notice All Bitcoin transaction inputs, prepended by the number of
|
|
80
106
|
/// transaction inputs.
|
|
81
|
-
/// @dev `tx_in_count | tx_in` from raw
|
|
107
|
+
/// @dev `tx_in_count | tx_in` from raw Bitcoin transaction data.
|
|
82
108
|
///
|
|
83
109
|
/// The number of transaction inputs encoded as compactSize
|
|
84
110
|
/// unsigned integer, little-endian.
|
|
@@ -100,5 +126,201 @@ library BitcoinTx {
|
|
|
100
126
|
/// @dev `lock_time` from raw Bitcoin transaction data.
|
|
101
127
|
/// Encoded as 4-bytes unsigned integer, little endian.
|
|
102
128
|
bytes4 locktime;
|
|
129
|
+
// This struct doesn't contain `__gap` property as the structure is not
|
|
130
|
+
// stored, it is used as a function's calldata argument.
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// @notice Represents data needed to perform a Bitcoin SPV proof.
|
|
134
|
+
struct Proof {
|
|
135
|
+
/// @notice The merkle proof of transaction inclusion in a block.
|
|
136
|
+
bytes merkleProof;
|
|
137
|
+
/// @notice Transaction index in the block (0-indexed).
|
|
138
|
+
uint256 txIndexInBlock;
|
|
139
|
+
/// @notice Single byte-string of 80-byte bitcoin headers,
|
|
140
|
+
/// lowest height first.
|
|
141
|
+
bytes bitcoinHeaders;
|
|
142
|
+
// This struct doesn't contain `__gap` property as the structure is not
|
|
143
|
+
// stored, it is used as a function's calldata argument.
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// @notice Represents info about an unspent transaction output.
|
|
147
|
+
struct UTXO {
|
|
148
|
+
/// @notice Hash of the transaction the output belongs to.
|
|
149
|
+
/// @dev Byte order corresponds to the Bitcoin internal byte order.
|
|
150
|
+
bytes32 txHash;
|
|
151
|
+
/// @notice Index of the transaction output (0-indexed).
|
|
152
|
+
uint32 txOutputIndex;
|
|
153
|
+
/// @notice Value of the transaction output.
|
|
154
|
+
uint64 txOutputValue;
|
|
155
|
+
// This struct doesn't contain `__gap` property as the structure is not
|
|
156
|
+
// stored, it is used as a function's calldata argument.
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// @notice Represents Bitcoin signature in the R/S/V format.
|
|
160
|
+
struct RSVSignature {
|
|
161
|
+
/// @notice Signature r value.
|
|
162
|
+
bytes32 r;
|
|
163
|
+
/// @notice Signature s value.
|
|
164
|
+
bytes32 s;
|
|
165
|
+
/// @notice Signature recovery value.
|
|
166
|
+
uint8 v;
|
|
167
|
+
// This struct doesn't contain `__gap` property as the structure is not
|
|
168
|
+
// stored, it is used as a function's calldata argument.
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// @notice Validates the SPV proof of the Bitcoin transaction.
|
|
172
|
+
/// Reverts in case the validation or proof verification fail.
|
|
173
|
+
/// @param txInfo Bitcoin transaction data.
|
|
174
|
+
/// @param proof Bitcoin proof data.
|
|
175
|
+
/// @return txHash Proven 32-byte transaction hash.
|
|
176
|
+
function validateProof(
|
|
177
|
+
BridgeState.Storage storage self,
|
|
178
|
+
Info calldata txInfo,
|
|
179
|
+
Proof calldata proof
|
|
180
|
+
) internal view returns (bytes32 txHash) {
|
|
181
|
+
require(
|
|
182
|
+
txInfo.inputVector.validateVin(),
|
|
183
|
+
"Invalid input vector provided"
|
|
184
|
+
);
|
|
185
|
+
require(
|
|
186
|
+
txInfo.outputVector.validateVout(),
|
|
187
|
+
"Invalid output vector provided"
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
txHash = abi
|
|
191
|
+
.encodePacked(
|
|
192
|
+
txInfo.version,
|
|
193
|
+
txInfo.inputVector,
|
|
194
|
+
txInfo.outputVector,
|
|
195
|
+
txInfo.locktime
|
|
196
|
+
)
|
|
197
|
+
.hash256View();
|
|
198
|
+
|
|
199
|
+
require(
|
|
200
|
+
txHash.prove(
|
|
201
|
+
proof.bitcoinHeaders.extractMerkleRootLE(),
|
|
202
|
+
proof.merkleProof,
|
|
203
|
+
proof.txIndexInBlock
|
|
204
|
+
),
|
|
205
|
+
"Tx merkle proof is not valid for provided header and tx hash"
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
evaluateProofDifficulty(self, proof.bitcoinHeaders);
|
|
209
|
+
|
|
210
|
+
return txHash;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// @notice Evaluates the given Bitcoin proof difficulty against the actual
|
|
214
|
+
/// Bitcoin chain difficulty provided by the relay oracle.
|
|
215
|
+
/// Reverts in case the evaluation fails.
|
|
216
|
+
/// @param bitcoinHeaders Bitcoin headers chain being part of the SPV
|
|
217
|
+
/// proof. Used to extract the observed proof difficulty.
|
|
218
|
+
function evaluateProofDifficulty(
|
|
219
|
+
BridgeState.Storage storage self,
|
|
220
|
+
bytes memory bitcoinHeaders
|
|
221
|
+
) internal view {
|
|
222
|
+
IRelay relay = self.relay;
|
|
223
|
+
uint256 currentEpochDifficulty = relay.getCurrentEpochDifficulty();
|
|
224
|
+
uint256 previousEpochDifficulty = relay.getPrevEpochDifficulty();
|
|
225
|
+
|
|
226
|
+
uint256 requestedDiff = 0;
|
|
227
|
+
uint256 firstHeaderDiff = bitcoinHeaders
|
|
228
|
+
.extractTarget()
|
|
229
|
+
.calculateDifficulty();
|
|
230
|
+
|
|
231
|
+
if (firstHeaderDiff == currentEpochDifficulty) {
|
|
232
|
+
requestedDiff = currentEpochDifficulty;
|
|
233
|
+
} else if (firstHeaderDiff == previousEpochDifficulty) {
|
|
234
|
+
requestedDiff = previousEpochDifficulty;
|
|
235
|
+
} else {
|
|
236
|
+
revert("Not at current or previous difficulty");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
uint256 observedDiff = bitcoinHeaders.validateHeaderChain();
|
|
240
|
+
|
|
241
|
+
require(
|
|
242
|
+
observedDiff != ValidateSPV.getErrBadLength(),
|
|
243
|
+
"Invalid length of the headers chain"
|
|
244
|
+
);
|
|
245
|
+
require(
|
|
246
|
+
observedDiff != ValidateSPV.getErrInvalidChain(),
|
|
247
|
+
"Invalid headers chain"
|
|
248
|
+
);
|
|
249
|
+
require(
|
|
250
|
+
observedDiff != ValidateSPV.getErrLowWork(),
|
|
251
|
+
"Insufficient work in a header"
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
require(
|
|
255
|
+
observedDiff >= requestedDiff * self.txProofDifficultyFactor,
|
|
256
|
+
"Insufficient accumulated difficulty in header chain"
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/// @notice Extracts public key hash from the provided P2PKH or P2WPKH output.
|
|
261
|
+
/// Reverts if the validation fails.
|
|
262
|
+
/// @param output The transaction output.
|
|
263
|
+
/// @return pubKeyHash 20-byte public key hash the output locks funds on.
|
|
264
|
+
/// @dev Requirements:
|
|
265
|
+
/// - The output must be of P2PKH or P2WPKH type and lock the funds
|
|
266
|
+
/// on a 20-byte public key hash.
|
|
267
|
+
function extractPubKeyHash(BridgeState.Storage storage, bytes memory output)
|
|
268
|
+
internal
|
|
269
|
+
pure
|
|
270
|
+
returns (bytes20 pubKeyHash)
|
|
271
|
+
{
|
|
272
|
+
bytes memory pubKeyHashBytes = output.extractHash();
|
|
273
|
+
|
|
274
|
+
require(
|
|
275
|
+
pubKeyHashBytes.length == 20,
|
|
276
|
+
"Output's public key hash must have 20 bytes"
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
pubKeyHash = pubKeyHashBytes.slice20(0);
|
|
280
|
+
|
|
281
|
+
// We need to make sure that the 20-byte public key hash
|
|
282
|
+
// is actually used in the right context of a P2PKH or P2WPKH
|
|
283
|
+
// output. To do so, we must extract the full script from the output
|
|
284
|
+
// and compare with the expected P2PKH and P2WPKH scripts
|
|
285
|
+
// referring to that 20-byte public key hash. The output consists
|
|
286
|
+
// of an 8-byte value and a variable length script. To extract the
|
|
287
|
+
// script we slice the output starting from 9th byte until the end.
|
|
288
|
+
bytes32 outputScriptKeccak = keccak256(
|
|
289
|
+
output.slice(8, output.length - 8)
|
|
290
|
+
);
|
|
291
|
+
// Build the expected P2PKH script which has the following byte
|
|
292
|
+
// format: <0x1976a914> <20-byte PKH> <0x88ac>. According to
|
|
293
|
+
// https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
|
|
294
|
+
// - 0x19: Byte length of the entire script
|
|
295
|
+
// - 0x76: OP_DUP
|
|
296
|
+
// - 0xa9: OP_HASH160
|
|
297
|
+
// - 0x14: Byte length of the public key hash
|
|
298
|
+
// - 0x88: OP_EQUALVERIFY
|
|
299
|
+
// - 0xac: OP_CHECKSIG
|
|
300
|
+
// which matches the P2PKH structure as per:
|
|
301
|
+
// https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash
|
|
302
|
+
bytes32 P2PKHScriptKeccak = keccak256(
|
|
303
|
+
abi.encodePacked(hex"1976a914", pubKeyHash, hex"88ac")
|
|
304
|
+
);
|
|
305
|
+
// Build the expected P2WPKH script which has the following format:
|
|
306
|
+
// <0x160014> <20-byte PKH>. According to
|
|
307
|
+
// https://en.bitcoin.it/wiki/Script#Opcodes this translates to:
|
|
308
|
+
// - 0x16: Byte length of the entire script
|
|
309
|
+
// - 0x00: OP_0
|
|
310
|
+
// - 0x14: Byte length of the public key hash
|
|
311
|
+
// which matches the P2WPKH structure as per:
|
|
312
|
+
// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH
|
|
313
|
+
bytes32 P2WPKHScriptKeccak = keccak256(
|
|
314
|
+
abi.encodePacked(hex"160014", pubKeyHash)
|
|
315
|
+
);
|
|
316
|
+
// Make sure the actual output script matches either the P2PKH
|
|
317
|
+
// or P2WPKH format.
|
|
318
|
+
require(
|
|
319
|
+
outputScriptKeccak == P2PKHScriptKeccak ||
|
|
320
|
+
outputScriptKeccak == P2WPKHScriptKeccak,
|
|
321
|
+
"Output must be P2PKH or P2WPKH"
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
return pubKeyHash;
|
|
103
325
|
}
|
|
104
326
|
}
|