@keep-network/tbtc-v2 0.1.0 → 0.1.1-dev
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/.chainId +1 -1
- package/artifacts/Bank.json +807 -0
- package/artifacts/Bridge.json +2300 -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 +36 -35
- package/artifacts/TBTCToken.json +738 -0
- 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 +34 -33
- 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/5e62cff1ead0900b07facca4b559e818.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 +4 -0
- package/build/contracts/bank/Bank.sol/Bank.json +542 -0
- 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 +4 -0
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +4 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.json +2686 -0
- 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 +4 -4
- 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 +4 -0
- package/build/contracts/vault/IVault.sol/IVault.json +52 -0
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +4 -0
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +449 -0
- package/contracts/GovernanceUtils.sol +4 -4
- package/contracts/bank/Bank.sol +436 -0
- package/contracts/bank/IReceiveBalanceApproval.sol +45 -0
- package/contracts/bridge/BitcoinTx.sol +326 -0
- package/contracts/bridge/Bridge.sol +1793 -0
- package/contracts/bridge/BridgeState.sol +739 -0
- package/contracts/bridge/Deposit.sol +269 -0
- package/contracts/bridge/DepositSweep.sol +574 -0
- package/contracts/bridge/EcdsaLib.sol +45 -0
- package/contracts/bridge/Fraud.sol +579 -0
- package/contracts/bridge/Heartbeat.sol +112 -0
- package/contracts/bridge/IRelay.sol +28 -0
- package/contracts/bridge/MovingFunds.sol +1077 -0
- package/contracts/bridge/Redemption.sol +1020 -0
- package/contracts/bridge/VendingMachine.sol +2 -2
- package/contracts/bridge/Wallets.sol +719 -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 +44 -0
- package/contracts/vault/TBTCVault.sol +305 -0
- package/deploy/00_resolve_relay.ts +28 -0
- package/deploy/00_resolve_tbtc_v1_token.ts +1 -1
- package/deploy/01_deploy_tbtc_v2_token.ts +8 -1
- package/deploy/02_deploy_vending_machine.ts +7 -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/deploy/00_resolve_relay.js +24 -0
- package/export/deploy/00_resolve_tbtc_v1_token.js +24 -0
- package/export/deploy/01_deploy_tbtc_v2_token.js +19 -0
- package/export/deploy/02_deploy_vending_machine.js +25 -0
- package/export/deploy/03_transfer_vending_machine_roles.js +19 -0
- package/export/deploy/04_deploy_bank.js +21 -0
- package/export/deploy/05_deploy_bridge.js +69 -0
- package/export/deploy/06_deploy_tbtc_vault.js +24 -0
- package/export/deploy/07_bank_update_bridge.js +13 -0
- package/export/deploy/08_transfer_bank_ownership.js +11 -0
- package/export/deploy/09_transfer_tbtc_vault_ownership.js +11 -0
- package/export/deploy/10_transfer_bridge_governance.js +11 -0
- package/export/deploy/11_initialize_wallet_owner.js +14 -0
- package/export/deploy/11_transfer_proxy_admin_ownership.js +23 -0
- package/export/deploy/12_deploy_proxy_admin_with_deputy.js +22 -0
- package/export/hardhat.config.js +169 -0
- package/export/test/bank/Bank.test.js +1012 -0
- package/export/test/bridge/Bridge.Deployment.test.js +76 -0
- package/export/test/bridge/Bridge.Deposit.test.js +1834 -0
- package/export/test/bridge/Bridge.Frauds.test.js +1349 -0
- package/export/test/bridge/Bridge.MovingFunds.test.js +2437 -0
- package/export/test/bridge/Bridge.Parameters.test.js +400 -0
- package/export/test/bridge/Bridge.Redemption.test.js +2523 -0
- package/export/test/bridge/Bridge.Vaults.test.js +74 -0
- package/export/test/bridge/Bridge.Wallets.test.js +1017 -0
- package/export/test/bridge/EcdsaLib.test.js +46 -0
- package/export/test/bridge/Heartbeat.test.js +77 -0
- package/export/test/bridge/VendingMachine.Upgrade.test.js +160 -0
- package/export/test/bridge/VendingMachine.test.js +762 -0
- package/export/test/data/deposit-sweep.js +655 -0
- package/export/test/data/ecdsa.js +18 -0
- package/export/test/data/fraud.js +158 -0
- package/export/test/data/moving-funds.js +815 -0
- package/export/test/data/redemption.js +1011 -0
- package/export/test/fixtures/bridge.js +54 -0
- package/export/test/fixtures/index.js +57 -0
- package/export/test/helpers/contract-test-helpers.js +18 -0
- package/export/test/integration/Slashing.test.js +279 -0
- package/export/test/integration/WalleCreation.test.js +66 -0
- package/export/test/integration/utils/ecdsa-wallet-registry.js +137 -0
- package/export/test/integration/utils/fixture.js +77 -0
- package/export/test/integration/utils/gas.js +36 -0
- package/export/test/integration/utils/random-beacon.js +26 -0
- package/export/test/integration/utils/staking.js +19 -0
- package/export/test/vault/DonationVault.test.js +202 -0
- package/export/test/vault/TBTCVault.Redemption.test.js +357 -0
- package/export/test/vault/TBTCVault.test.js +768 -0
- package/export/typechain/BTCUtils.js +2 -0
- package/export/typechain/Bank.js +2 -0
- package/export/typechain/BankStub.js +2 -0
- package/export/typechain/Bridge.js +2 -0
- package/export/typechain/BridgeState.js +2 -0
- package/export/typechain/BridgeStub.js +2 -0
- package/export/typechain/Deposit.js +2 -0
- package/export/typechain/DepositSweep.js +2 -0
- package/export/typechain/DonationVault.js +2 -0
- package/export/typechain/ERC165.js +2 -0
- package/export/typechain/ERC1967Proxy.js +2 -0
- package/export/typechain/ERC1967Upgrade.js +2 -0
- package/export/typechain/ERC20WithPermit.js +2 -0
- package/export/typechain/ERC721.js +2 -0
- package/export/typechain/EcdsaAuthorization.js +2 -0
- package/export/typechain/EcdsaDkg.js +2 -0
- package/export/typechain/EcdsaDkgValidator.js +2 -0
- package/export/typechain/EcdsaInactivity.js +2 -0
- package/export/typechain/Fraud.js +2 -0
- package/export/typechain/Governable.js +2 -0
- package/export/typechain/HeartbeatStub.js +2 -0
- package/export/typechain/IApplication.js +2 -0
- package/export/typechain/IApproveAndCall.js +2 -0
- package/export/typechain/IBeacon.js +2 -0
- package/export/typechain/IERC165.js +2 -0
- package/export/typechain/IERC1822Proxiable.js +2 -0
- package/export/typechain/IERC20.js +2 -0
- package/export/typechain/IERC20Metadata.js +2 -0
- package/export/typechain/IERC20WithPermit.js +2 -0
- package/export/typechain/IERC721.js +2 -0
- package/export/typechain/IERC721Metadata.js +2 -0
- package/export/typechain/IERC721Receiver.js +2 -0
- package/export/typechain/IRandomBeacon.js +2 -0
- package/export/typechain/IRandomBeaconConsumer.js +2 -0
- package/export/typechain/IReceiveApproval.js +2 -0
- package/export/typechain/IReceiveBalanceApproval.js +2 -0
- package/export/typechain/IRelay.js +2 -0
- package/export/typechain/IStaking.js +2 -0
- package/export/typechain/IVault.js +2 -0
- package/export/typechain/IWalletOwner.js +2 -0
- package/export/typechain/IWalletRegistry.js +2 -0
- package/export/typechain/Initializable.js +2 -0
- package/export/typechain/MisfundRecovery.js +2 -0
- package/export/typechain/MovingFunds.js +2 -0
- package/export/typechain/Ownable.js +2 -0
- package/export/typechain/Proxy.js +2 -0
- package/export/typechain/ProxyAdmin.js +2 -0
- package/export/typechain/ReceiveApprovalStub.js +2 -0
- package/export/typechain/Redemption.js +2 -0
- package/export/typechain/Reimbursable.js +2 -0
- package/export/typechain/ReimbursementPool.js +2 -0
- package/export/typechain/Rewards.js +2 -0
- package/export/typechain/SortitionPool.js +2 -0
- package/export/typechain/SortitionTree.js +2 -0
- package/export/typechain/TBTC.js +2 -0
- package/export/typechain/TBTCVault.js +2 -0
- package/export/typechain/TestERC20.js +2 -0
- package/export/typechain/TestERC721.js +2 -0
- package/export/typechain/TestEcdsaLib.js +2 -0
- package/export/typechain/TestRelay.js +2 -0
- package/export/typechain/TransparentUpgradeableProxy.js +2 -0
- package/export/typechain/VendingMachine.js +2 -0
- package/export/typechain/WalletRegistry.js +2 -0
- package/export/typechain/Wallets.js +2 -0
- package/export/typechain/common.js +2 -0
- package/export/typechain/factories/BTCUtils__factory.js +94 -0
- package/export/typechain/factories/BankStub__factory.js +586 -0
- package/export/typechain/factories/Bank__factory.js +573 -0
- package/export/typechain/factories/BridgeState__factory.js +257 -0
- package/export/typechain/factories/BridgeStub__factory.js +2912 -0
- package/export/typechain/factories/Bridge__factory.js +2526 -0
- package/export/typechain/factories/DepositSweep__factory.js +61 -0
- package/export/typechain/factories/Deposit__factory.js +103 -0
- package/export/typechain/factories/DonationVault__factory.js +139 -0
- package/export/typechain/factories/ERC165__factory.js +38 -0
- package/export/typechain/factories/ERC1967Proxy__factory.js +111 -0
- package/export/typechain/factories/ERC1967Upgrade__factory.js +64 -0
- package/export/typechain/factories/ERC20WithPermit__factory.js +524 -0
- package/export/typechain/factories/ERC721__factory.js +388 -0
- package/export/typechain/factories/EcdsaAuthorization__factory.js +211 -0
- package/export/typechain/factories/EcdsaDkgValidator__factory.js +441 -0
- package/export/typechain/factories/EcdsaDkg__factory.js +192 -0
- package/export/typechain/factories/EcdsaInactivity__factory.js +134 -0
- package/export/typechain/factories/Fraud__factory.js +117 -0
- package/export/typechain/factories/Governable__factory.js +64 -0
- package/export/typechain/factories/HeartbeatStub__factory.js +61 -0
- package/export/typechain/factories/IApplication__factory.js +152 -0
- package/export/typechain/factories/IApproveAndCall__factory.js +48 -0
- package/export/typechain/factories/IBeacon__factory.js +32 -0
- package/export/typechain/factories/IERC165__factory.js +38 -0
- package/export/typechain/factories/IERC1822Proxiable__factory.js +32 -0
- package/export/typechain/factories/IERC20Metadata__factory.js +241 -0
- package/export/typechain/factories/IERC20WithPermit__factory.js +389 -0
- package/export/typechain/factories/IERC20__factory.js +202 -0
- package/export/typechain/factories/IERC721Metadata__factory.js +349 -0
- package/export/typechain/factories/IERC721Receiver__factory.js +53 -0
- package/export/typechain/factories/IERC721__factory.js +304 -0
- package/export/typechain/factories/IRandomBeaconConsumer__factory.js +37 -0
- package/export/typechain/factories/IRandomBeacon__factory.js +32 -0
- package/export/typechain/factories/IReceiveApproval__factory.js +47 -0
- package/export/typechain/factories/IReceiveBalanceApproval__factory.js +42 -0
- package/export/typechain/factories/IRelay__factory.js +45 -0
- package/export/typechain/factories/IStaking__factory.js +722 -0
- package/export/typechain/factories/IVault__factory.js +60 -0
- package/export/typechain/factories/IWalletOwner__factory.js +65 -0
- package/export/typechain/factories/IWalletRegistry__factory.js +138 -0
- package/export/typechain/factories/Initializable__factory.js +32 -0
- package/export/typechain/factories/MisfundRecovery__factory.js +145 -0
- package/export/typechain/factories/MovingFunds__factory.js +169 -0
- package/export/typechain/factories/Ownable__factory.js +71 -0
- package/export/typechain/factories/ProxyAdmin__factory.js +191 -0
- package/export/typechain/factories/Proxy__factory.js +27 -0
- package/export/typechain/factories/ReceiveApprovalStub__factory.js +127 -0
- package/export/typechain/factories/Redemption__factory.js +123 -0
- package/export/typechain/factories/Reimbursable__factory.js +58 -0
- package/export/typechain/factories/ReimbursementPool__factory.js +350 -0
- package/export/typechain/factories/Rewards__factory.js +117 -0
- package/export/typechain/factories/SortitionPool__factory.js +610 -0
- package/export/typechain/factories/SortitionTree__factory.js +149 -0
- package/export/typechain/factories/TBTCVault__factory.js +480 -0
- package/export/typechain/factories/TBTC__factory.js +564 -0
- package/export/typechain/factories/TestERC20__factory.js +539 -0
- package/export/typechain/factories/TestERC721__factory.js +421 -0
- package/export/typechain/factories/TestEcdsaLib__factory.js +66 -0
- package/export/typechain/factories/TestRelay__factory.js +94 -0
- package/export/typechain/factories/TransparentUpgradeableProxy__factory.js +186 -0
- package/export/typechain/factories/VendingMachine__factory.js +549 -0
- package/export/typechain/factories/WalletRegistry__factory.js +1919 -0
- package/export/typechain/factories/Wallets__factory.js +143 -0
- package/export/typechain/index.js +132 -0
- package/export.json +15932 -503
- package/package.json +47 -26
- package/artifacts/solcInputs/7cc3eda3cb3ff2522d18b5e7b31ea228.json +0 -104
|
@@ -0,0 +1,1349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable no-underscore-dangle */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
21
|
+
if (mod && mod.__esModule) return mod;
|
|
22
|
+
var result = {};
|
|
23
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
24
|
+
__setModuleDefault(result, mod);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
28
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
29
|
+
};
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
const hardhat_1 = require("hardhat");
|
|
32
|
+
const chai_1 = __importStar(require("chai"));
|
|
33
|
+
const smock_1 = require("@defi-wonderland/smock");
|
|
34
|
+
const fraud_1 = require("../data/fraud");
|
|
35
|
+
const fixtures_1 = require("../fixtures");
|
|
36
|
+
const bridge_1 = __importDefault(require("../fixtures/bridge"));
|
|
37
|
+
const ecdsa_1 = require("../data/ecdsa");
|
|
38
|
+
chai_1.default.use(smock_1.smock.matchers);
|
|
39
|
+
const { createSnapshot, restoreSnapshot } = hardhat_1.helpers.snapshot;
|
|
40
|
+
const { lastBlockTime, increaseTime } = hardhat_1.helpers.time;
|
|
41
|
+
const { keccak256, sha256 } = hardhat_1.ethers.utils;
|
|
42
|
+
const { publicKey: walletPublicKey, pubKeyHash160: walletPublicKeyHash } = fraud_1.wallet;
|
|
43
|
+
describe("Bridge - Fraud", () => {
|
|
44
|
+
let thirdParty;
|
|
45
|
+
let treasury;
|
|
46
|
+
let walletRegistry;
|
|
47
|
+
let bridge;
|
|
48
|
+
let fraudChallengeDepositAmount;
|
|
49
|
+
let fraudChallengeDefeatTimeout;
|
|
50
|
+
let fraudSlashingAmount;
|
|
51
|
+
let fraudNotifierRewardMultiplier;
|
|
52
|
+
before(async () => {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-extra-semi
|
|
54
|
+
;
|
|
55
|
+
({ thirdParty, treasury, walletRegistry, bridge } =
|
|
56
|
+
await hardhat_1.waffle.loadFixture(bridge_1.default));
|
|
57
|
+
({
|
|
58
|
+
fraudChallengeDepositAmount,
|
|
59
|
+
fraudChallengeDefeatTimeout,
|
|
60
|
+
fraudSlashingAmount,
|
|
61
|
+
fraudNotifierRewardMultiplier,
|
|
62
|
+
} = await bridge.fraudParameters());
|
|
63
|
+
});
|
|
64
|
+
describe("submitFraudChallenge", () => {
|
|
65
|
+
const data = fraud_1.witnessSignSingleInputTx;
|
|
66
|
+
context("when the wallet is in Live state", () => {
|
|
67
|
+
context("when the amount of ETH deposited is enough", () => {
|
|
68
|
+
context("when the data needed for signature verification is correct", () => {
|
|
69
|
+
context("when the fraud challenge does not exist yet", () => {
|
|
70
|
+
let tx;
|
|
71
|
+
before(async () => {
|
|
72
|
+
await createSnapshot();
|
|
73
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
74
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
75
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
76
|
+
pendingRedemptionsValue: 0,
|
|
77
|
+
createdAt: await lastBlockTime(),
|
|
78
|
+
movingFundsRequestedAt: 0,
|
|
79
|
+
closingStartedAt: 0,
|
|
80
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
81
|
+
state: fixtures_1.walletState.Live,
|
|
82
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
83
|
+
});
|
|
84
|
+
tx = await bridge
|
|
85
|
+
.connect(thirdParty)
|
|
86
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
87
|
+
value: fraudChallengeDepositAmount,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
after(async () => {
|
|
91
|
+
await restoreSnapshot();
|
|
92
|
+
});
|
|
93
|
+
it("should transfer ether from the caller to the bridge", async () => {
|
|
94
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(thirdParty, fraudChallengeDepositAmount.mul(-1));
|
|
95
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount);
|
|
96
|
+
});
|
|
97
|
+
it("should store the fraud challenge data", async () => {
|
|
98
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
99
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
100
|
+
(0, chai_1.expect)(fraudChallenge.challenger).to.equal(await thirdParty.getAddress());
|
|
101
|
+
(0, chai_1.expect)(fraudChallenge.depositAmount).to.equal(fraudChallengeDepositAmount);
|
|
102
|
+
(0, chai_1.expect)(fraudChallenge.reportedAt).to.equal(await lastBlockTime());
|
|
103
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.equal(false);
|
|
104
|
+
});
|
|
105
|
+
it("should emit FraudChallengeSubmitted event", async () => {
|
|
106
|
+
await (0, chai_1.expect)(tx)
|
|
107
|
+
.to.emit(bridge, "FraudChallengeSubmitted")
|
|
108
|
+
.withArgs(walletPublicKeyHash, data.sighash, data.signature.v, data.signature.r, data.signature.s);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
context("when the fraud challenge already exists", () => {
|
|
112
|
+
before(async () => {
|
|
113
|
+
await createSnapshot();
|
|
114
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
115
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
116
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
117
|
+
pendingRedemptionsValue: 0,
|
|
118
|
+
createdAt: await lastBlockTime(),
|
|
119
|
+
movingFundsRequestedAt: 0,
|
|
120
|
+
closingStartedAt: 0,
|
|
121
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
122
|
+
state: fixtures_1.walletState.Live,
|
|
123
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
124
|
+
});
|
|
125
|
+
await bridge
|
|
126
|
+
.connect(thirdParty)
|
|
127
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
128
|
+
value: fraudChallengeDepositAmount,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
after(async () => {
|
|
132
|
+
await restoreSnapshot();
|
|
133
|
+
});
|
|
134
|
+
it("should revert", async () => {
|
|
135
|
+
await (0, chai_1.expect)(bridge
|
|
136
|
+
.connect(thirdParty)
|
|
137
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
138
|
+
value: fraudChallengeDepositAmount,
|
|
139
|
+
})).to.be.revertedWith("Fraud challenge already exists");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
context("when incorrect wallet public key is used", () => {
|
|
144
|
+
// Unrelated Bitcoin public key
|
|
145
|
+
const incorrectWalletPublicKey = "0xffc045ade19f8a5d464299146ce069049cdcc2390a9b44d9abcd83f11d8cce4" +
|
|
146
|
+
"01ea6800e307b87aadebdcd2f7293cc60f0526afaff1a7b1abddfd787e6c5871e";
|
|
147
|
+
const incorrectWalletPublicKeyHash = "0xb5222794425b9b8cd8c3358e73a50dea73480927";
|
|
148
|
+
before(async () => {
|
|
149
|
+
await createSnapshot();
|
|
150
|
+
await bridge.setWallet(incorrectWalletPublicKeyHash, {
|
|
151
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
152
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
153
|
+
pendingRedemptionsValue: 0,
|
|
154
|
+
createdAt: await lastBlockTime(),
|
|
155
|
+
movingFundsRequestedAt: 0,
|
|
156
|
+
closingStartedAt: 0,
|
|
157
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
158
|
+
state: fixtures_1.walletState.Live,
|
|
159
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
after(async () => {
|
|
163
|
+
await restoreSnapshot();
|
|
164
|
+
});
|
|
165
|
+
it("should revert", async () => {
|
|
166
|
+
await (0, chai_1.expect)(bridge
|
|
167
|
+
.connect(thirdParty)
|
|
168
|
+
.submitFraudChallenge(incorrectWalletPublicKey, data.preimageSha256, data.signature, {
|
|
169
|
+
value: fraudChallengeDepositAmount,
|
|
170
|
+
})).to.be.revertedWith("Signature verification failure");
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
context("when incorrect sighash is used", () => {
|
|
174
|
+
// Random hex-string
|
|
175
|
+
const incorrectSighash = "0x9e8e249791a5636e5e007fc15487b5a5bd6e60f73f7e236a7025cd63b904650b";
|
|
176
|
+
before(async () => {
|
|
177
|
+
await createSnapshot();
|
|
178
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
179
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
180
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
181
|
+
pendingRedemptionsValue: 0,
|
|
182
|
+
createdAt: await lastBlockTime(),
|
|
183
|
+
movingFundsRequestedAt: 0,
|
|
184
|
+
closingStartedAt: 0,
|
|
185
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
186
|
+
state: fixtures_1.walletState.Live,
|
|
187
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
after(async () => {
|
|
191
|
+
await restoreSnapshot();
|
|
192
|
+
});
|
|
193
|
+
it("should revert", async () => {
|
|
194
|
+
await (0, chai_1.expect)(bridge
|
|
195
|
+
.connect(thirdParty)
|
|
196
|
+
.submitFraudChallenge(walletPublicKey, incorrectSighash, data.signature, {
|
|
197
|
+
value: fraudChallengeDepositAmount,
|
|
198
|
+
})).to.be.revertedWith("Signature verification failure");
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
context("when incorrect recovery ID is used", () => {
|
|
202
|
+
// Increase the value of v by 1
|
|
203
|
+
const incorrectV = data.signature.v + 1;
|
|
204
|
+
before(async () => {
|
|
205
|
+
await createSnapshot();
|
|
206
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
207
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
208
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
209
|
+
pendingRedemptionsValue: 0,
|
|
210
|
+
createdAt: await lastBlockTime(),
|
|
211
|
+
movingFundsRequestedAt: 0,
|
|
212
|
+
closingStartedAt: 0,
|
|
213
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
214
|
+
state: fixtures_1.walletState.Live,
|
|
215
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
after(async () => {
|
|
219
|
+
await restoreSnapshot();
|
|
220
|
+
});
|
|
221
|
+
it("should revert", async () => {
|
|
222
|
+
await (0, chai_1.expect)(bridge.connect(thirdParty).submitFraudChallenge(walletPublicKey, data.preimageSha256, {
|
|
223
|
+
r: data.signature.r,
|
|
224
|
+
s: data.signature.s,
|
|
225
|
+
v: incorrectV,
|
|
226
|
+
}, {
|
|
227
|
+
value: fraudChallengeDepositAmount,
|
|
228
|
+
})).to.be.revertedWith("Signature verification failure");
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
context("when incorrect signature data is used", () => {
|
|
232
|
+
// Swap r and s
|
|
233
|
+
const incorrectS = data.signature.r;
|
|
234
|
+
const incorrectR = data.signature.s;
|
|
235
|
+
before(async () => {
|
|
236
|
+
await createSnapshot();
|
|
237
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
238
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
239
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
240
|
+
pendingRedemptionsValue: 0,
|
|
241
|
+
createdAt: await lastBlockTime(),
|
|
242
|
+
movingFundsRequestedAt: 0,
|
|
243
|
+
closingStartedAt: 0,
|
|
244
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
245
|
+
state: fixtures_1.walletState.Live,
|
|
246
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
after(async () => {
|
|
250
|
+
await restoreSnapshot();
|
|
251
|
+
});
|
|
252
|
+
it("should revert", async () => {
|
|
253
|
+
await (0, chai_1.expect)(bridge.connect(thirdParty).submitFraudChallenge(walletPublicKey, data.preimageSha256, {
|
|
254
|
+
r: incorrectR,
|
|
255
|
+
s: incorrectS,
|
|
256
|
+
v: data.signature.v,
|
|
257
|
+
}, {
|
|
258
|
+
value: fraudChallengeDepositAmount,
|
|
259
|
+
})).to.be.revertedWith("Signature verification failure");
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
context("when the amount of ETH deposited is too low", () => {
|
|
264
|
+
before(async () => {
|
|
265
|
+
await createSnapshot();
|
|
266
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
267
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
268
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
269
|
+
pendingRedemptionsValue: 0,
|
|
270
|
+
createdAt: await lastBlockTime(),
|
|
271
|
+
movingFundsRequestedAt: 0,
|
|
272
|
+
closingStartedAt: 0,
|
|
273
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
274
|
+
state: fixtures_1.walletState.Live,
|
|
275
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
after(async () => {
|
|
279
|
+
await restoreSnapshot();
|
|
280
|
+
});
|
|
281
|
+
it("should revert", async () => {
|
|
282
|
+
await (0, chai_1.expect)(bridge
|
|
283
|
+
.connect(thirdParty)
|
|
284
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
285
|
+
value: fraudChallengeDepositAmount.sub(1),
|
|
286
|
+
})).to.be.revertedWith("The amount of ETH deposited is too low");
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
context("when the wallet is in MovingFunds state", () => {
|
|
291
|
+
before(async () => {
|
|
292
|
+
await createSnapshot();
|
|
293
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
294
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
295
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
296
|
+
pendingRedemptionsValue: 0,
|
|
297
|
+
createdAt: await lastBlockTime(),
|
|
298
|
+
movingFundsRequestedAt: 0,
|
|
299
|
+
closingStartedAt: 0,
|
|
300
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
301
|
+
state: fixtures_1.walletState.MovingFunds,
|
|
302
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
after(async () => {
|
|
306
|
+
await restoreSnapshot();
|
|
307
|
+
});
|
|
308
|
+
it("should succeed", async () => {
|
|
309
|
+
await (0, chai_1.expect)(bridge
|
|
310
|
+
.connect(thirdParty)
|
|
311
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
312
|
+
value: fraudChallengeDepositAmount,
|
|
313
|
+
})).to.not.be.reverted;
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
context("when the wallet is in Closing state", () => {
|
|
317
|
+
before(async () => {
|
|
318
|
+
await createSnapshot();
|
|
319
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
320
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
321
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
322
|
+
pendingRedemptionsValue: 0,
|
|
323
|
+
createdAt: await lastBlockTime(),
|
|
324
|
+
movingFundsRequestedAt: 0,
|
|
325
|
+
closingStartedAt: 0,
|
|
326
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
327
|
+
state: fixtures_1.walletState.Closing,
|
|
328
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
after(async () => {
|
|
332
|
+
await restoreSnapshot();
|
|
333
|
+
});
|
|
334
|
+
it("should succeed", async () => {
|
|
335
|
+
await (0, chai_1.expect)(bridge
|
|
336
|
+
.connect(thirdParty)
|
|
337
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
338
|
+
value: fraudChallengeDepositAmount,
|
|
339
|
+
})).to.not.be.reverted;
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
context("when the wallet is in neither Live nor MovingFunds nor Closing state", () => {
|
|
343
|
+
const testData = [
|
|
344
|
+
{
|
|
345
|
+
testName: "when wallet state is Unknown",
|
|
346
|
+
walletState: fixtures_1.walletState.Unknown,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
testName: "when wallet state is Closed",
|
|
350
|
+
walletState: fixtures_1.walletState.Closed,
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
testName: "when wallet state is Terminated",
|
|
354
|
+
walletState: fixtures_1.walletState.Terminated,
|
|
355
|
+
},
|
|
356
|
+
];
|
|
357
|
+
testData.forEach((test) => {
|
|
358
|
+
context(test.testName, () => {
|
|
359
|
+
before(async () => {
|
|
360
|
+
await createSnapshot();
|
|
361
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
362
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
363
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
364
|
+
pendingRedemptionsValue: 0,
|
|
365
|
+
createdAt: await lastBlockTime(),
|
|
366
|
+
movingFundsRequestedAt: 0,
|
|
367
|
+
closingStartedAt: 0,
|
|
368
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
369
|
+
state: test.walletState,
|
|
370
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
after(async () => {
|
|
374
|
+
await restoreSnapshot();
|
|
375
|
+
});
|
|
376
|
+
it("should revert", async () => {
|
|
377
|
+
await (0, chai_1.expect)(bridge
|
|
378
|
+
.connect(thirdParty)
|
|
379
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
380
|
+
value: fraudChallengeDepositAmount,
|
|
381
|
+
})).to.be.revertedWith("Wallet must be in Live or MovingFunds or Closing state");
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
describe("defeatFraudChallengeWithHeartbeat", () => {
|
|
388
|
+
let heartbeatWalletPublicKey;
|
|
389
|
+
let heartbeatWalletPublicKeyHash;
|
|
390
|
+
let heartbeatWalletSigningKey;
|
|
391
|
+
before(async () => {
|
|
392
|
+
await createSnapshot();
|
|
393
|
+
// For `defeatFraudChallengeWithHeartbeat` unit tests we do not use test
|
|
394
|
+
// data from `fraud.ts`. Instead, we create random wallet and use its
|
|
395
|
+
// SigningKey.
|
|
396
|
+
//
|
|
397
|
+
// This approach is better long-term. In case the format of the heartbeat
|
|
398
|
+
// message changes or in case we want to add more unit tests, we can simply
|
|
399
|
+
// call appropriate function to compute another signature. Also, we do not
|
|
400
|
+
// use any BTC-specific data for this set of unit tests.
|
|
401
|
+
const wallet = hardhat_1.ethers.Wallet.createRandom();
|
|
402
|
+
// We use `ethers.utils.SigningKey` for a `Wallet` instead of
|
|
403
|
+
// `Signer.signMessage` to do not add '\x19Ethereum Signed Message:\n'
|
|
404
|
+
// prefix to the signed message. The format of the heartbeat message is
|
|
405
|
+
// the same no matter on which host chain TBTC is deployed.
|
|
406
|
+
heartbeatWalletSigningKey = new hardhat_1.ethers.utils.SigningKey(wallet.privateKey);
|
|
407
|
+
// Public key obtained as `wallet.publicKey` is an uncompressed key,
|
|
408
|
+
// prefixed with `0x04`. To compute raw ECDSA key, we need to drop `0x04`.
|
|
409
|
+
heartbeatWalletPublicKey = `0x${wallet.publicKey.substring(4)}`;
|
|
410
|
+
const walletID = keccak256(heartbeatWalletPublicKey);
|
|
411
|
+
const walletPublicKeyX = `0x${heartbeatWalletPublicKey.substring(2, 66)}`;
|
|
412
|
+
const walletPublicKeyY = `0x${heartbeatWalletPublicKey.substring(66)}`;
|
|
413
|
+
await bridge
|
|
414
|
+
.connect(walletRegistry.wallet)
|
|
415
|
+
.__ecdsaWalletCreatedCallback(walletID, walletPublicKeyX, walletPublicKeyY);
|
|
416
|
+
heartbeatWalletPublicKeyHash = await bridge.activeWalletPubKeyHash();
|
|
417
|
+
});
|
|
418
|
+
after(async () => {
|
|
419
|
+
await restoreSnapshot();
|
|
420
|
+
});
|
|
421
|
+
context("when the challenge exists", () => {
|
|
422
|
+
context("when the challenge is open", () => {
|
|
423
|
+
context("when the heartbeat message has correct format", () => {
|
|
424
|
+
const heartbeatMessage = "0xFFFFFFFFFFFFFFFF0000000000E0EED7";
|
|
425
|
+
const heartbeatMessageSha256 = sha256(heartbeatMessage);
|
|
426
|
+
const sighash = sha256(heartbeatMessageSha256);
|
|
427
|
+
let tx;
|
|
428
|
+
before(async () => {
|
|
429
|
+
await createSnapshot();
|
|
430
|
+
const signature = hardhat_1.ethers.utils.splitSignature(heartbeatWalletSigningKey.signDigest(sighash));
|
|
431
|
+
await bridge
|
|
432
|
+
.connect(thirdParty)
|
|
433
|
+
.submitFraudChallenge(heartbeatWalletPublicKey, heartbeatMessageSha256, signature, {
|
|
434
|
+
value: fraudChallengeDepositAmount,
|
|
435
|
+
});
|
|
436
|
+
tx = await bridge
|
|
437
|
+
.connect(thirdParty)
|
|
438
|
+
.defeatFraudChallengeWithHeartbeat(heartbeatWalletPublicKey, heartbeatMessage);
|
|
439
|
+
});
|
|
440
|
+
after(async () => {
|
|
441
|
+
await restoreSnapshot();
|
|
442
|
+
});
|
|
443
|
+
it("should mark the challenge as resolved", async () => {
|
|
444
|
+
const challengeKey = buildChallengeKey(heartbeatWalletPublicKey, sighash);
|
|
445
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
446
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.equal(true);
|
|
447
|
+
});
|
|
448
|
+
it("should send the ether deposited by the challenger to the treasury", async () => {
|
|
449
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
450
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(treasury, fraudChallengeDepositAmount);
|
|
451
|
+
});
|
|
452
|
+
it("should emit FraudChallengeDefeated event", async () => {
|
|
453
|
+
await (0, chai_1.expect)(tx)
|
|
454
|
+
.to.emit(bridge, "FraudChallengeDefeated")
|
|
455
|
+
.withArgs(heartbeatWalletPublicKeyHash, sighash);
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
context("when the heartbeat message has no correct format", () => {
|
|
459
|
+
const notHeartbeatMessage = "0xAAFFFFFFFFFFFFFF0000000000E0EED7";
|
|
460
|
+
const heartbeatMessageSha256 = sha256(notHeartbeatMessage);
|
|
461
|
+
const sighash = sha256(heartbeatMessageSha256);
|
|
462
|
+
before(async () => {
|
|
463
|
+
await createSnapshot();
|
|
464
|
+
const signature = hardhat_1.ethers.utils.splitSignature(heartbeatWalletSigningKey.signDigest(sighash));
|
|
465
|
+
await bridge
|
|
466
|
+
.connect(thirdParty)
|
|
467
|
+
.submitFraudChallenge(heartbeatWalletPublicKey, heartbeatMessageSha256, signature, {
|
|
468
|
+
value: fraudChallengeDepositAmount,
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
after(async () => {
|
|
472
|
+
await restoreSnapshot();
|
|
473
|
+
});
|
|
474
|
+
it("should revert", async () => {
|
|
475
|
+
await (0, chai_1.expect)(bridge
|
|
476
|
+
.connect(thirdParty)
|
|
477
|
+
.defeatFraudChallengeWithHeartbeat(heartbeatWalletPublicKey, notHeartbeatMessage)).to.be.revertedWith("Not a valid heartbeat message");
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
context("when the challenge is resolved by defeat", () => {
|
|
482
|
+
const heartbeatMessage = "0xFFFFFFFFFFFFFFFF0000000000E0EED7";
|
|
483
|
+
const heartbeatMessageSha256 = sha256(heartbeatMessage);
|
|
484
|
+
const sighash = sha256(heartbeatMessageSha256);
|
|
485
|
+
before(async () => {
|
|
486
|
+
await createSnapshot();
|
|
487
|
+
const signature = hardhat_1.ethers.utils.splitSignature(heartbeatWalletSigningKey.signDigest(sighash));
|
|
488
|
+
await bridge
|
|
489
|
+
.connect(thirdParty)
|
|
490
|
+
.submitFraudChallenge(heartbeatWalletPublicKey, heartbeatMessageSha256, signature, {
|
|
491
|
+
value: fraudChallengeDepositAmount,
|
|
492
|
+
});
|
|
493
|
+
await bridge
|
|
494
|
+
.connect(thirdParty)
|
|
495
|
+
.defeatFraudChallengeWithHeartbeat(heartbeatWalletPublicKey, heartbeatMessage);
|
|
496
|
+
});
|
|
497
|
+
after(async () => {
|
|
498
|
+
await restoreSnapshot();
|
|
499
|
+
});
|
|
500
|
+
it("should revert", async () => {
|
|
501
|
+
await (0, chai_1.expect)(bridge
|
|
502
|
+
.connect(thirdParty)
|
|
503
|
+
.defeatFraudChallengeWithHeartbeat(heartbeatWalletPublicKey, heartbeatMessage)).to.be.revertedWith("Fraud challenge has already been resolved");
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
context("when the challenge is resolved by timeout", () => {
|
|
507
|
+
const heartbeatMessage = "0xFFFFFFFFFFFFFFFF0000000000E0EED7";
|
|
508
|
+
const heartbeatMessageSha256 = sha256(heartbeatMessage);
|
|
509
|
+
const sighash = sha256(heartbeatMessageSha256);
|
|
510
|
+
before(async () => {
|
|
511
|
+
await createSnapshot();
|
|
512
|
+
const signature = hardhat_1.ethers.utils.splitSignature(heartbeatWalletSigningKey.signDigest(sighash));
|
|
513
|
+
await bridge
|
|
514
|
+
.connect(thirdParty)
|
|
515
|
+
.submitFraudChallenge(heartbeatWalletPublicKey, heartbeatMessageSha256, signature, {
|
|
516
|
+
value: fraudChallengeDepositAmount,
|
|
517
|
+
});
|
|
518
|
+
await increaseTime(fraudChallengeDefeatTimeout);
|
|
519
|
+
await bridge
|
|
520
|
+
.connect(thirdParty)
|
|
521
|
+
.notifyFraudChallengeDefeatTimeout(heartbeatWalletPublicKey, [], heartbeatMessageSha256);
|
|
522
|
+
});
|
|
523
|
+
after(async () => {
|
|
524
|
+
await restoreSnapshot();
|
|
525
|
+
});
|
|
526
|
+
it("should revert", async () => {
|
|
527
|
+
await (0, chai_1.expect)(bridge
|
|
528
|
+
.connect(thirdParty)
|
|
529
|
+
.defeatFraudChallengeWithHeartbeat(heartbeatWalletPublicKey, heartbeatMessage)).to.be.revertedWith("Fraud challenge has already been resolved");
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
context("when the challenge does not exist", () => {
|
|
534
|
+
const heartbeatMessage = "0xFFFFFFFFFFFFFFFF0000000000E0EED7";
|
|
535
|
+
const heartbeatMessageSha256 = sha256(heartbeatMessage);
|
|
536
|
+
const sighash = sha256(heartbeatMessageSha256);
|
|
537
|
+
before(async () => {
|
|
538
|
+
await createSnapshot();
|
|
539
|
+
const signature = hardhat_1.ethers.utils.splitSignature(heartbeatWalletSigningKey.signDigest(sighash));
|
|
540
|
+
await bridge
|
|
541
|
+
.connect(thirdParty)
|
|
542
|
+
.submitFraudChallenge(heartbeatWalletPublicKey, heartbeatMessageSha256, signature, {
|
|
543
|
+
value: fraudChallengeDepositAmount,
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
after(async () => {
|
|
547
|
+
await restoreSnapshot();
|
|
548
|
+
});
|
|
549
|
+
it("should revert", async () => {
|
|
550
|
+
await (0, chai_1.expect)(bridge.connect(thirdParty).defeatFraudChallengeWithHeartbeat(heartbeatWalletPublicKey, "0xFFFFFFFFFFFFFFFF0000000000E0EED8" // ...D7 -> ...D8
|
|
551
|
+
)).to.be.revertedWith("Fraud challenge does not exist");
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
describe("defeatFraudChallenge", () => {
|
|
556
|
+
context("when the challenge exists", () => {
|
|
557
|
+
context("when the challenge is open", () => {
|
|
558
|
+
context("when the sighash type is correct", () => {
|
|
559
|
+
context("when the input is non-witness", () => {
|
|
560
|
+
context("when the transaction has single input", () => {
|
|
561
|
+
context("when the input is marked as correctly spent in the Bridge", () => {
|
|
562
|
+
const data = fraud_1.nonWitnessSignSingleInputTx;
|
|
563
|
+
let tx;
|
|
564
|
+
before(async () => {
|
|
565
|
+
await createSnapshot();
|
|
566
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
567
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
568
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
569
|
+
pendingRedemptionsValue: 0,
|
|
570
|
+
createdAt: await lastBlockTime(),
|
|
571
|
+
movingFundsRequestedAt: 0,
|
|
572
|
+
closingStartedAt: 0,
|
|
573
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
574
|
+
state: fixtures_1.walletState.Live,
|
|
575
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
576
|
+
});
|
|
577
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
578
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
579
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
580
|
+
await bridge
|
|
581
|
+
.connect(thirdParty)
|
|
582
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
583
|
+
value: fraudChallengeDepositAmount,
|
|
584
|
+
});
|
|
585
|
+
tx = await bridge
|
|
586
|
+
.connect(thirdParty)
|
|
587
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness);
|
|
588
|
+
});
|
|
589
|
+
after(async () => {
|
|
590
|
+
await restoreSnapshot();
|
|
591
|
+
});
|
|
592
|
+
it("should mark the challenge as resolved", async () => {
|
|
593
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
594
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
595
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.equal(true);
|
|
596
|
+
});
|
|
597
|
+
it("should send the ether deposited by the challenger to the treasury", async () => {
|
|
598
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
599
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(treasury, fraudChallengeDepositAmount);
|
|
600
|
+
});
|
|
601
|
+
it("should emit FraudChallengeDefeated event", async () => {
|
|
602
|
+
await (0, chai_1.expect)(tx)
|
|
603
|
+
.to.emit(bridge, "FraudChallengeDefeated")
|
|
604
|
+
.withArgs(walletPublicKeyHash, data.sighash);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
context("when the input is not marked as correctly spent in the Bridge", () => {
|
|
608
|
+
const data = fraud_1.nonWitnessSignSingleInputTx;
|
|
609
|
+
before(async () => {
|
|
610
|
+
await createSnapshot();
|
|
611
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
612
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
613
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
614
|
+
pendingRedemptionsValue: 0,
|
|
615
|
+
createdAt: await lastBlockTime(),
|
|
616
|
+
movingFundsRequestedAt: 0,
|
|
617
|
+
closingStartedAt: 0,
|
|
618
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
619
|
+
state: fixtures_1.walletState.Live,
|
|
620
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
621
|
+
});
|
|
622
|
+
await bridge
|
|
623
|
+
.connect(thirdParty)
|
|
624
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
625
|
+
value: fraudChallengeDepositAmount,
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
after(async () => {
|
|
629
|
+
await restoreSnapshot();
|
|
630
|
+
});
|
|
631
|
+
it("should revert", async () => {
|
|
632
|
+
await (0, chai_1.expect)(bridge
|
|
633
|
+
.connect(thirdParty)
|
|
634
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness)).to.be.revertedWith("Spent UTXO not found among correctly spent UTXOs");
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
context("when the transaction has multiple inputs", () => {
|
|
639
|
+
context("when the input is marked as correctly spent in the Bridge", () => {
|
|
640
|
+
const data = fraud_1.nonWitnessSignMultipleInputsTx;
|
|
641
|
+
let tx;
|
|
642
|
+
before(async () => {
|
|
643
|
+
await createSnapshot();
|
|
644
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
645
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
646
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
647
|
+
pendingRedemptionsValue: 0,
|
|
648
|
+
createdAt: await lastBlockTime(),
|
|
649
|
+
movingFundsRequestedAt: 0,
|
|
650
|
+
closingStartedAt: 0,
|
|
651
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
652
|
+
state: fixtures_1.walletState.Live,
|
|
653
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
654
|
+
});
|
|
655
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
656
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
657
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
658
|
+
await bridge
|
|
659
|
+
.connect(thirdParty)
|
|
660
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
661
|
+
value: fraudChallengeDepositAmount,
|
|
662
|
+
});
|
|
663
|
+
tx = await bridge
|
|
664
|
+
.connect(thirdParty)
|
|
665
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness);
|
|
666
|
+
});
|
|
667
|
+
after(async () => {
|
|
668
|
+
await restoreSnapshot();
|
|
669
|
+
});
|
|
670
|
+
it("should mark the challenge as resolved", async () => {
|
|
671
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
672
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
673
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.equal(true);
|
|
674
|
+
});
|
|
675
|
+
it("should send the ether deposited by the challenger to the treasury", async () => {
|
|
676
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
677
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(treasury, fraudChallengeDepositAmount);
|
|
678
|
+
});
|
|
679
|
+
it("should emit FraudChallengeDefeated event", async () => {
|
|
680
|
+
await (0, chai_1.expect)(tx)
|
|
681
|
+
.to.emit(bridge, "FraudChallengeDefeated")
|
|
682
|
+
.withArgs(walletPublicKeyHash, data.sighash);
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
context("when the input is not marked as correctly spent in the Bridge", () => {
|
|
686
|
+
const data = fraud_1.nonWitnessSignMultipleInputsTx;
|
|
687
|
+
before(async () => {
|
|
688
|
+
await createSnapshot();
|
|
689
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
690
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
691
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
692
|
+
pendingRedemptionsValue: 0,
|
|
693
|
+
createdAt: await lastBlockTime(),
|
|
694
|
+
movingFundsRequestedAt: 0,
|
|
695
|
+
closingStartedAt: 0,
|
|
696
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
697
|
+
state: fixtures_1.walletState.Live,
|
|
698
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
699
|
+
});
|
|
700
|
+
await bridge
|
|
701
|
+
.connect(thirdParty)
|
|
702
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
703
|
+
value: fraudChallengeDepositAmount,
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
after(async () => {
|
|
707
|
+
await restoreSnapshot();
|
|
708
|
+
});
|
|
709
|
+
it("should revert", async () => {
|
|
710
|
+
await (0, chai_1.expect)(bridge
|
|
711
|
+
.connect(thirdParty)
|
|
712
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness)).to.be.revertedWith("Spent UTXO not found among correctly spent UTXOs");
|
|
713
|
+
});
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
context("when the input is witness", () => {
|
|
718
|
+
context("when the transaction has single input", () => {
|
|
719
|
+
context("when the input is marked as correctly spent in the Bridge", () => {
|
|
720
|
+
const data = fraud_1.witnessSignSingleInputTx;
|
|
721
|
+
let tx;
|
|
722
|
+
before(async () => {
|
|
723
|
+
await createSnapshot();
|
|
724
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
725
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
726
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
727
|
+
pendingRedemptionsValue: 0,
|
|
728
|
+
createdAt: await lastBlockTime(),
|
|
729
|
+
movingFundsRequestedAt: 0,
|
|
730
|
+
closingStartedAt: 0,
|
|
731
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
732
|
+
state: fixtures_1.walletState.Live,
|
|
733
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
734
|
+
});
|
|
735
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
736
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
737
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
738
|
+
await bridge
|
|
739
|
+
.connect(thirdParty)
|
|
740
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
741
|
+
value: fraudChallengeDepositAmount,
|
|
742
|
+
});
|
|
743
|
+
tx = await bridge
|
|
744
|
+
.connect(thirdParty)
|
|
745
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness);
|
|
746
|
+
});
|
|
747
|
+
after(async () => {
|
|
748
|
+
await restoreSnapshot();
|
|
749
|
+
});
|
|
750
|
+
it("should mark the challenge as resolved", async () => {
|
|
751
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
752
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
753
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.equal(true);
|
|
754
|
+
});
|
|
755
|
+
it("should send the ether deposited by the challenger to the treasury", async () => {
|
|
756
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
757
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(treasury, fraudChallengeDepositAmount);
|
|
758
|
+
});
|
|
759
|
+
it("should emit FraudChallengeDefeated event", async () => {
|
|
760
|
+
await (0, chai_1.expect)(tx)
|
|
761
|
+
.to.emit(bridge, "FraudChallengeDefeated")
|
|
762
|
+
.withArgs(walletPublicKeyHash, data.sighash);
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
context("when the input is not marked as correctly spent in the Bridge", () => {
|
|
766
|
+
const data = fraud_1.witnessSignSingleInputTx;
|
|
767
|
+
before(async () => {
|
|
768
|
+
await createSnapshot();
|
|
769
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
770
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
771
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
772
|
+
pendingRedemptionsValue: 0,
|
|
773
|
+
createdAt: await lastBlockTime(),
|
|
774
|
+
movingFundsRequestedAt: 0,
|
|
775
|
+
closingStartedAt: 0,
|
|
776
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
777
|
+
state: fixtures_1.walletState.Live,
|
|
778
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
779
|
+
});
|
|
780
|
+
await bridge
|
|
781
|
+
.connect(thirdParty)
|
|
782
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
783
|
+
value: fraudChallengeDepositAmount,
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
after(async () => {
|
|
787
|
+
await restoreSnapshot();
|
|
788
|
+
});
|
|
789
|
+
it("should revert", async () => {
|
|
790
|
+
await (0, chai_1.expect)(bridge
|
|
791
|
+
.connect(thirdParty)
|
|
792
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness)).to.be.revertedWith("Spent UTXO not found among correctly spent UTXOs");
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
context("when the transaction has multiple inputs", () => {
|
|
797
|
+
context("when the input is marked as correctly spent in the Bridge", () => {
|
|
798
|
+
const data = fraud_1.witnessSignMultipleInputTx;
|
|
799
|
+
let tx;
|
|
800
|
+
before(async () => {
|
|
801
|
+
await createSnapshot();
|
|
802
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
803
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
804
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
805
|
+
pendingRedemptionsValue: 0,
|
|
806
|
+
createdAt: await lastBlockTime(),
|
|
807
|
+
movingFundsRequestedAt: 0,
|
|
808
|
+
closingStartedAt: 0,
|
|
809
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
810
|
+
state: fixtures_1.walletState.Live,
|
|
811
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
812
|
+
});
|
|
813
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
814
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
815
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
816
|
+
await bridge
|
|
817
|
+
.connect(thirdParty)
|
|
818
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
819
|
+
value: fraudChallengeDepositAmount,
|
|
820
|
+
});
|
|
821
|
+
tx = await bridge
|
|
822
|
+
.connect(thirdParty)
|
|
823
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness);
|
|
824
|
+
});
|
|
825
|
+
after(async () => {
|
|
826
|
+
await restoreSnapshot();
|
|
827
|
+
});
|
|
828
|
+
it("should mark the challenge as resolved", async () => {
|
|
829
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
830
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
831
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.equal(true);
|
|
832
|
+
});
|
|
833
|
+
it("should send the ether deposited by the challenger to the treasury", async () => {
|
|
834
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
835
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(treasury, fraudChallengeDepositAmount);
|
|
836
|
+
});
|
|
837
|
+
it("should emit FraudChallengeDefeated event", async () => {
|
|
838
|
+
await (0, chai_1.expect)(tx)
|
|
839
|
+
.to.emit(bridge, "FraudChallengeDefeated")
|
|
840
|
+
.withArgs(walletPublicKeyHash, data.sighash);
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
context("when the input is not marked as correctly spent in the Bridge", () => {
|
|
844
|
+
const data = fraud_1.witnessSignMultipleInputTx;
|
|
845
|
+
before(async () => {
|
|
846
|
+
await createSnapshot();
|
|
847
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
848
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
849
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
850
|
+
pendingRedemptionsValue: 0,
|
|
851
|
+
createdAt: await lastBlockTime(),
|
|
852
|
+
movingFundsRequestedAt: 0,
|
|
853
|
+
closingStartedAt: 0,
|
|
854
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
855
|
+
state: fixtures_1.walletState.Live,
|
|
856
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
857
|
+
});
|
|
858
|
+
await bridge
|
|
859
|
+
.connect(thirdParty)
|
|
860
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
861
|
+
value: fraudChallengeDepositAmount,
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
after(async () => {
|
|
865
|
+
await restoreSnapshot();
|
|
866
|
+
});
|
|
867
|
+
it("should revert", async () => {
|
|
868
|
+
await (0, chai_1.expect)(bridge
|
|
869
|
+
.connect(thirdParty)
|
|
870
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness)).to.be.revertedWith("Spent UTXO not found among correctly spent UTXOs");
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
context("when the sighash type is incorrect", () => {
|
|
877
|
+
// Wrong sighash was used (SIGHASH_NONE | SIGHASH_ANYONECANPAY) during
|
|
878
|
+
// input signing
|
|
879
|
+
const data = fraud_1.wrongSighashType;
|
|
880
|
+
before(async () => {
|
|
881
|
+
await createSnapshot();
|
|
882
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
883
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
884
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
885
|
+
pendingRedemptionsValue: 0,
|
|
886
|
+
createdAt: await lastBlockTime(),
|
|
887
|
+
movingFundsRequestedAt: 0,
|
|
888
|
+
closingStartedAt: 0,
|
|
889
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
890
|
+
state: fixtures_1.walletState.Live,
|
|
891
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
892
|
+
});
|
|
893
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
894
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
895
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
896
|
+
await bridge
|
|
897
|
+
.connect(thirdParty)
|
|
898
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
899
|
+
value: fraudChallengeDepositAmount,
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
after(async () => {
|
|
903
|
+
await restoreSnapshot();
|
|
904
|
+
});
|
|
905
|
+
it("should revert", async () => {
|
|
906
|
+
await (0, chai_1.expect)(bridge
|
|
907
|
+
.connect(thirdParty)
|
|
908
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, data.witness)).to.be.revertedWith("Wrong sighash type");
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
});
|
|
912
|
+
context("when the challenge is resolved by defeat", () => {
|
|
913
|
+
const data = fraud_1.nonWitnessSignSingleInputTx;
|
|
914
|
+
before(async () => {
|
|
915
|
+
await createSnapshot();
|
|
916
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
917
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
918
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
919
|
+
pendingRedemptionsValue: 0,
|
|
920
|
+
createdAt: await lastBlockTime(),
|
|
921
|
+
movingFundsRequestedAt: 0,
|
|
922
|
+
closingStartedAt: 0,
|
|
923
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
924
|
+
state: fixtures_1.walletState.Live,
|
|
925
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
926
|
+
});
|
|
927
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
928
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
929
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
930
|
+
await bridge
|
|
931
|
+
.connect(thirdParty)
|
|
932
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
933
|
+
value: fraudChallengeDepositAmount,
|
|
934
|
+
});
|
|
935
|
+
await bridge
|
|
936
|
+
.connect(thirdParty)
|
|
937
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, false);
|
|
938
|
+
});
|
|
939
|
+
after(async () => {
|
|
940
|
+
await restoreSnapshot();
|
|
941
|
+
});
|
|
942
|
+
it("should revert", async () => {
|
|
943
|
+
await (0, chai_1.expect)(bridge
|
|
944
|
+
.connect(thirdParty)
|
|
945
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, false)).to.be.revertedWith("Fraud challenge has already been resolved");
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
context("when the challenge is resolved by timeout", () => {
|
|
949
|
+
const data = fraud_1.nonWitnessSignSingleInputTx;
|
|
950
|
+
before(async () => {
|
|
951
|
+
await createSnapshot();
|
|
952
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
953
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
954
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
955
|
+
pendingRedemptionsValue: 0,
|
|
956
|
+
createdAt: await lastBlockTime(),
|
|
957
|
+
movingFundsRequestedAt: 0,
|
|
958
|
+
closingStartedAt: 0,
|
|
959
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
960
|
+
state: fixtures_1.walletState.Live,
|
|
961
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
962
|
+
});
|
|
963
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
964
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
965
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
966
|
+
await bridge
|
|
967
|
+
.connect(thirdParty)
|
|
968
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
969
|
+
value: fraudChallengeDepositAmount,
|
|
970
|
+
});
|
|
971
|
+
await increaseTime(fraudChallengeDefeatTimeout);
|
|
972
|
+
await bridge
|
|
973
|
+
.connect(thirdParty)
|
|
974
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256);
|
|
975
|
+
});
|
|
976
|
+
after(async () => {
|
|
977
|
+
walletRegistry.closeWallet.reset();
|
|
978
|
+
walletRegistry.seize.reset();
|
|
979
|
+
await restoreSnapshot();
|
|
980
|
+
});
|
|
981
|
+
it("should revert", async () => {
|
|
982
|
+
await (0, chai_1.expect)(bridge
|
|
983
|
+
.connect(thirdParty)
|
|
984
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, false)).to.be.revertedWith("Fraud challenge has already been resolved");
|
|
985
|
+
});
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
context("when the challenge does not exist", () => {
|
|
989
|
+
const data = fraud_1.nonWitnessSignMultipleInputsTx;
|
|
990
|
+
before(async () => {
|
|
991
|
+
await createSnapshot();
|
|
992
|
+
});
|
|
993
|
+
after(async () => {
|
|
994
|
+
await restoreSnapshot();
|
|
995
|
+
});
|
|
996
|
+
it("should revert", async () => {
|
|
997
|
+
await (0, chai_1.expect)(bridge
|
|
998
|
+
.connect(thirdParty)
|
|
999
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, false)).to.be.revertedWith("Fraud challenge does not exist");
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
describe("notifyFraudChallengeDefeatTimeout", () => {
|
|
1004
|
+
const data = fraud_1.nonWitnessSignSingleInputTx;
|
|
1005
|
+
context("when the fraud challenge exists", () => {
|
|
1006
|
+
context("when the fraud challenge is open", () => {
|
|
1007
|
+
context("when the fraud challenge has timed out", () => {
|
|
1008
|
+
const walletDraft = {
|
|
1009
|
+
ecdsaWalletID: ecdsa_1.ecdsaWalletTestData.walletID,
|
|
1010
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
1011
|
+
pendingRedemptionsValue: 0,
|
|
1012
|
+
createdAt: 0,
|
|
1013
|
+
movingFundsRequestedAt: 0,
|
|
1014
|
+
closingStartedAt: 0,
|
|
1015
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
1016
|
+
state: fixtures_1.walletState.Unknown,
|
|
1017
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
1018
|
+
};
|
|
1019
|
+
const walletMembersIDs = [1, 2, 3, 4, 5];
|
|
1020
|
+
context("when the wallet is in the Live or MovingFunds or Closing state", () => {
|
|
1021
|
+
const testData = [
|
|
1022
|
+
{
|
|
1023
|
+
testName: "when wallet state is Live but the wallet is not the active one",
|
|
1024
|
+
walletState: fixtures_1.walletState.Live,
|
|
1025
|
+
additionalSetup: async () => {
|
|
1026
|
+
// The active wallet is a different wallet than the active one
|
|
1027
|
+
await bridge.setActiveWallet("0x0b9f85c224b0e018a5865392927b3f9e16cf5e79");
|
|
1028
|
+
},
|
|
1029
|
+
additionalAssertions: async () => {
|
|
1030
|
+
it("should decrease the live wallets count", async () => {
|
|
1031
|
+
(0, chai_1.expect)(await bridge.liveWalletsCount()).to.be.equal(0);
|
|
1032
|
+
});
|
|
1033
|
+
it("should not unset the active wallet", async () => {
|
|
1034
|
+
(0, chai_1.expect)(await bridge.activeWalletPubKeyHash()).to.be.not.equal("0x0000000000000000000000000000000000000000");
|
|
1035
|
+
});
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
testName: "when wallet state is Live and the wallet is the active one",
|
|
1040
|
+
walletState: fixtures_1.walletState.Live,
|
|
1041
|
+
additionalSetup: async () => {
|
|
1042
|
+
await bridge.setActiveWallet(walletPublicKeyHash);
|
|
1043
|
+
},
|
|
1044
|
+
additionalAssertions: async () => {
|
|
1045
|
+
it("should decrease the live wallets count", async () => {
|
|
1046
|
+
(0, chai_1.expect)(await bridge.liveWalletsCount()).to.be.equal(0);
|
|
1047
|
+
});
|
|
1048
|
+
it("should unset the active wallet", async () => {
|
|
1049
|
+
(0, chai_1.expect)(await bridge.activeWalletPubKeyHash()).to.be.equal("0x0000000000000000000000000000000000000000");
|
|
1050
|
+
});
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
testName: "when wallet state is MovingFunds",
|
|
1055
|
+
walletState: fixtures_1.walletState.MovingFunds,
|
|
1056
|
+
additionalSetup: async () => { },
|
|
1057
|
+
additionalAssertions: async () => { },
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
testName: "when wallet state is Closing",
|
|
1061
|
+
walletState: fixtures_1.walletState.Closing,
|
|
1062
|
+
additionalSetup: async () => { },
|
|
1063
|
+
additionalAssertions: async () => { },
|
|
1064
|
+
},
|
|
1065
|
+
];
|
|
1066
|
+
testData.forEach((test) => {
|
|
1067
|
+
context(test.testName, async () => {
|
|
1068
|
+
let tx;
|
|
1069
|
+
before(async () => {
|
|
1070
|
+
await createSnapshot();
|
|
1071
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1072
|
+
...walletDraft,
|
|
1073
|
+
state: test.walletState,
|
|
1074
|
+
});
|
|
1075
|
+
await bridge
|
|
1076
|
+
.connect(thirdParty)
|
|
1077
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
1078
|
+
value: fraudChallengeDepositAmount,
|
|
1079
|
+
});
|
|
1080
|
+
await increaseTime(fraudChallengeDefeatTimeout);
|
|
1081
|
+
await test.additionalSetup();
|
|
1082
|
+
tx = await bridge
|
|
1083
|
+
.connect(thirdParty)
|
|
1084
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, walletMembersIDs, data.preimageSha256);
|
|
1085
|
+
});
|
|
1086
|
+
after(async () => {
|
|
1087
|
+
walletRegistry.closeWallet.reset();
|
|
1088
|
+
walletRegistry.seize.reset();
|
|
1089
|
+
await restoreSnapshot();
|
|
1090
|
+
});
|
|
1091
|
+
it("should mark the fraud challenge as resolved", async () => {
|
|
1092
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
1093
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
1094
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.be.true;
|
|
1095
|
+
});
|
|
1096
|
+
it("should return the deposited ether to the challenger", async () => {
|
|
1097
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
1098
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(thirdParty, fraudChallengeDepositAmount);
|
|
1099
|
+
});
|
|
1100
|
+
it("should emit FraudChallengeDefeatTimedOut event", async () => {
|
|
1101
|
+
await (0, chai_1.expect)(tx)
|
|
1102
|
+
.to.emit(bridge, "FraudChallengeDefeatTimedOut")
|
|
1103
|
+
.withArgs(walletPublicKeyHash, data.sighash);
|
|
1104
|
+
});
|
|
1105
|
+
it("should change the wallet state to Terminated", async () => {
|
|
1106
|
+
(0, chai_1.expect)((await bridge.wallets(walletPublicKeyHash)).state).to.be.equal(fixtures_1.walletState.Terminated);
|
|
1107
|
+
});
|
|
1108
|
+
it("should emit WalletTerminated event", async () => {
|
|
1109
|
+
await (0, chai_1.expect)(tx)
|
|
1110
|
+
.to.emit(bridge, "WalletTerminated")
|
|
1111
|
+
.withArgs(walletDraft.ecdsaWalletID, walletPublicKeyHash);
|
|
1112
|
+
});
|
|
1113
|
+
it("should call the ECDSA wallet registry's closeWallet function", async () => {
|
|
1114
|
+
(0, chai_1.expect)(walletRegistry.closeWallet).to.have.been.calledOnceWith(walletDraft.ecdsaWalletID);
|
|
1115
|
+
});
|
|
1116
|
+
it("should call the ECDSA wallet registry's seize function", async () => {
|
|
1117
|
+
(0, chai_1.expect)(walletRegistry.seize).to.have.been.calledOnceWith(fraudSlashingAmount, fraudNotifierRewardMultiplier, await thirdParty.getAddress(), ecdsa_1.ecdsaWalletTestData.walletID, walletMembersIDs);
|
|
1118
|
+
});
|
|
1119
|
+
// TODO: Check if the gas consumption of functions calling `seize`
|
|
1120
|
+
// is not too high (use a real `staking` and `walletRegistry`).
|
|
1121
|
+
// Perhaps add a separate deployment with the non-mocked contracts
|
|
1122
|
+
// or test it in a system test?
|
|
1123
|
+
await test.additionalAssertions();
|
|
1124
|
+
});
|
|
1125
|
+
});
|
|
1126
|
+
});
|
|
1127
|
+
context("when the wallet is in the Terminated state", () => {
|
|
1128
|
+
let tx;
|
|
1129
|
+
before(async () => {
|
|
1130
|
+
await createSnapshot();
|
|
1131
|
+
// First, the wallet must be Live to make fraud challenge
|
|
1132
|
+
// submission possible.
|
|
1133
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1134
|
+
...walletDraft,
|
|
1135
|
+
state: fixtures_1.walletState.Live,
|
|
1136
|
+
});
|
|
1137
|
+
await bridge
|
|
1138
|
+
.connect(thirdParty)
|
|
1139
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
1140
|
+
value: fraudChallengeDepositAmount,
|
|
1141
|
+
});
|
|
1142
|
+
await increaseTime(fraudChallengeDefeatTimeout);
|
|
1143
|
+
// Then, the state of the wallet changes to the Terminated
|
|
1144
|
+
// state.
|
|
1145
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1146
|
+
...walletDraft,
|
|
1147
|
+
state: fixtures_1.walletState.Terminated,
|
|
1148
|
+
});
|
|
1149
|
+
tx = await bridge
|
|
1150
|
+
.connect(thirdParty)
|
|
1151
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, walletMembersIDs, data.preimageSha256);
|
|
1152
|
+
});
|
|
1153
|
+
after(async () => {
|
|
1154
|
+
await restoreSnapshot();
|
|
1155
|
+
});
|
|
1156
|
+
it("should mark the fraud challenge as resolved", async () => {
|
|
1157
|
+
const challengeKey = buildChallengeKey(walletPublicKey, data.sighash);
|
|
1158
|
+
const fraudChallenge = await bridge.fraudChallenges(challengeKey);
|
|
1159
|
+
(0, chai_1.expect)(fraudChallenge.resolved).to.be.true;
|
|
1160
|
+
});
|
|
1161
|
+
it("should return the deposited ether to the challenger", async () => {
|
|
1162
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(bridge, fraudChallengeDepositAmount.mul(-1));
|
|
1163
|
+
await (0, chai_1.expect)(tx).to.changeEtherBalance(thirdParty, fraudChallengeDepositAmount);
|
|
1164
|
+
});
|
|
1165
|
+
it("should emit FraudChallengeDefeatTimedOut event", async () => {
|
|
1166
|
+
await (0, chai_1.expect)(tx)
|
|
1167
|
+
.to.emit(bridge, "FraudChallengeDefeatTimedOut")
|
|
1168
|
+
.withArgs(walletPublicKeyHash, data.sighash);
|
|
1169
|
+
});
|
|
1170
|
+
it("should not change the wallet state", async () => {
|
|
1171
|
+
(0, chai_1.expect)((await bridge.wallets(walletPublicKeyHash)).state).to.be.equal(fixtures_1.walletState.Terminated);
|
|
1172
|
+
});
|
|
1173
|
+
it("should not call the ECDSA wallet registry's seize function", async () => {
|
|
1174
|
+
(0, chai_1.expect)(walletRegistry.seize).not.to.have.been.called;
|
|
1175
|
+
});
|
|
1176
|
+
});
|
|
1177
|
+
context("when the wallet is neither in the Live nor MovingFunds nor Closing nor Terminated state", () => {
|
|
1178
|
+
const testData = [
|
|
1179
|
+
{
|
|
1180
|
+
testName: "when the wallet is in the Unknown state",
|
|
1181
|
+
walletState: fixtures_1.walletState.Unknown,
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
testName: "when the wallet is in the Closed state",
|
|
1185
|
+
walletState: fixtures_1.walletState.Closed,
|
|
1186
|
+
},
|
|
1187
|
+
];
|
|
1188
|
+
testData.forEach((test) => {
|
|
1189
|
+
context(test.testName, () => {
|
|
1190
|
+
before(async () => {
|
|
1191
|
+
await createSnapshot();
|
|
1192
|
+
// First, the wallet must be Live to make fraud challenge
|
|
1193
|
+
// submission possible.
|
|
1194
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1195
|
+
...walletDraft,
|
|
1196
|
+
state: fixtures_1.walletState.Live,
|
|
1197
|
+
});
|
|
1198
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
1199
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
1200
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
1201
|
+
await bridge
|
|
1202
|
+
.connect(thirdParty)
|
|
1203
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
1204
|
+
value: fraudChallengeDepositAmount,
|
|
1205
|
+
});
|
|
1206
|
+
await increaseTime(fraudChallengeDefeatTimeout);
|
|
1207
|
+
// Then, the state of the wallet changes to the tested
|
|
1208
|
+
// state.
|
|
1209
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1210
|
+
...walletDraft,
|
|
1211
|
+
state: test.walletState,
|
|
1212
|
+
});
|
|
1213
|
+
});
|
|
1214
|
+
after(async () => {
|
|
1215
|
+
await restoreSnapshot();
|
|
1216
|
+
});
|
|
1217
|
+
it("should revert", async () => {
|
|
1218
|
+
await (0, chai_1.expect)(bridge
|
|
1219
|
+
.connect(thirdParty)
|
|
1220
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256)).to.be.revertedWith("Wallet must be in Live or MovingFunds or Closing or Terminated state");
|
|
1221
|
+
});
|
|
1222
|
+
});
|
|
1223
|
+
});
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
1226
|
+
context("when the fraud challenge has not timed out yet", () => {
|
|
1227
|
+
before(async () => {
|
|
1228
|
+
await createSnapshot();
|
|
1229
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1230
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
1231
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
1232
|
+
pendingRedemptionsValue: 0,
|
|
1233
|
+
createdAt: await lastBlockTime(),
|
|
1234
|
+
movingFundsRequestedAt: 0,
|
|
1235
|
+
closingStartedAt: 0,
|
|
1236
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
1237
|
+
state: fixtures_1.walletState.Live,
|
|
1238
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
1239
|
+
});
|
|
1240
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
1241
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
1242
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
1243
|
+
await bridge
|
|
1244
|
+
.connect(thirdParty)
|
|
1245
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
1246
|
+
value: fraudChallengeDepositAmount,
|
|
1247
|
+
});
|
|
1248
|
+
await increaseTime(fraudChallengeDefeatTimeout.sub(2));
|
|
1249
|
+
});
|
|
1250
|
+
after(async () => {
|
|
1251
|
+
await restoreSnapshot();
|
|
1252
|
+
});
|
|
1253
|
+
it("should revert", async () => {
|
|
1254
|
+
await (0, chai_1.expect)(bridge
|
|
1255
|
+
.connect(thirdParty)
|
|
1256
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256)).to.be.revertedWith("Fraud challenge defeat period did not time out yet");
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
});
|
|
1260
|
+
context("when the fraud challenge is resolved by challenge defeat", () => {
|
|
1261
|
+
before(async () => {
|
|
1262
|
+
await createSnapshot();
|
|
1263
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1264
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
1265
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
1266
|
+
pendingRedemptionsValue: 0,
|
|
1267
|
+
createdAt: await lastBlockTime(),
|
|
1268
|
+
movingFundsRequestedAt: 0,
|
|
1269
|
+
closingStartedAt: 0,
|
|
1270
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
1271
|
+
state: fixtures_1.walletState.Live,
|
|
1272
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
1273
|
+
});
|
|
1274
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
1275
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
1276
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
1277
|
+
await bridge
|
|
1278
|
+
.connect(thirdParty)
|
|
1279
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
1280
|
+
value: fraudChallengeDepositAmount,
|
|
1281
|
+
});
|
|
1282
|
+
await bridge
|
|
1283
|
+
.connect(thirdParty)
|
|
1284
|
+
.defeatFraudChallenge(walletPublicKey, data.preimage, false);
|
|
1285
|
+
});
|
|
1286
|
+
after(async () => {
|
|
1287
|
+
await restoreSnapshot();
|
|
1288
|
+
});
|
|
1289
|
+
it("should revert", async () => {
|
|
1290
|
+
await (0, chai_1.expect)(bridge
|
|
1291
|
+
.connect(thirdParty)
|
|
1292
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256)).to.be.revertedWith("Fraud challenge has already been resolved");
|
|
1293
|
+
});
|
|
1294
|
+
});
|
|
1295
|
+
context("when the fraud challenge is resolved by previous timeout notification", () => {
|
|
1296
|
+
before(async () => {
|
|
1297
|
+
await createSnapshot();
|
|
1298
|
+
await bridge.setWallet(walletPublicKeyHash, {
|
|
1299
|
+
ecdsaWalletID: hardhat_1.ethers.constants.HashZero,
|
|
1300
|
+
mainUtxoHash: hardhat_1.ethers.constants.HashZero,
|
|
1301
|
+
pendingRedemptionsValue: 0,
|
|
1302
|
+
createdAt: await lastBlockTime(),
|
|
1303
|
+
movingFundsRequestedAt: 0,
|
|
1304
|
+
closingStartedAt: 0,
|
|
1305
|
+
pendingMovedFundsSweepRequestsCount: 0,
|
|
1306
|
+
state: fixtures_1.walletState.Live,
|
|
1307
|
+
movingFundsTargetWalletsCommitmentHash: hardhat_1.ethers.constants.HashZero,
|
|
1308
|
+
});
|
|
1309
|
+
await bridge.setSweptDeposits(data.deposits);
|
|
1310
|
+
await bridge.setSpentMainUtxos(data.spentMainUtxos);
|
|
1311
|
+
await bridge.setProcessedMovedFundsSweepRequests(data.movedFundsSweepRequests);
|
|
1312
|
+
await bridge
|
|
1313
|
+
.connect(thirdParty)
|
|
1314
|
+
.submitFraudChallenge(walletPublicKey, data.preimageSha256, data.signature, {
|
|
1315
|
+
value: fraudChallengeDepositAmount,
|
|
1316
|
+
});
|
|
1317
|
+
await increaseTime(fraudChallengeDefeatTimeout);
|
|
1318
|
+
await bridge
|
|
1319
|
+
.connect(thirdParty)
|
|
1320
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256);
|
|
1321
|
+
});
|
|
1322
|
+
after(async () => {
|
|
1323
|
+
await restoreSnapshot();
|
|
1324
|
+
});
|
|
1325
|
+
it("should revert", async () => {
|
|
1326
|
+
await (0, chai_1.expect)(bridge
|
|
1327
|
+
.connect(thirdParty)
|
|
1328
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256)).to.be.revertedWith("Fraud challenge has already been resolved");
|
|
1329
|
+
});
|
|
1330
|
+
});
|
|
1331
|
+
});
|
|
1332
|
+
context("when the fraud challenge does not exist", () => {
|
|
1333
|
+
before(async () => {
|
|
1334
|
+
await createSnapshot();
|
|
1335
|
+
});
|
|
1336
|
+
after(async () => {
|
|
1337
|
+
await restoreSnapshot();
|
|
1338
|
+
});
|
|
1339
|
+
it("should revert", async () => {
|
|
1340
|
+
await (0, chai_1.expect)(bridge
|
|
1341
|
+
.connect(thirdParty)
|
|
1342
|
+
.notifyFraudChallengeDefeatTimeout(walletPublicKey, [], data.preimageSha256)).to.be.revertedWith("Fraud challenge does not exist");
|
|
1343
|
+
});
|
|
1344
|
+
});
|
|
1345
|
+
});
|
|
1346
|
+
function buildChallengeKey(publicKey, sighash) {
|
|
1347
|
+
return hardhat_1.ethers.utils.solidityKeccak256(["bytes", "bytes32"], [publicKey, sighash]);
|
|
1348
|
+
}
|
|
1349
|
+
});
|