@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.
Files changed (296) hide show
  1. package/README.adoc +12 -0
  2. package/artifacts/.chainId +1 -1
  3. package/artifacts/Bank.json +807 -0
  4. package/artifacts/Bridge.json +2300 -0
  5. package/artifacts/Deposit.json +117 -0
  6. package/artifacts/DepositSweep.json +77 -0
  7. package/artifacts/EcdsaDkgValidator.json +532 -0
  8. package/artifacts/EcdsaInactivity.json +156 -0
  9. package/artifacts/EcdsaSortitionPool.json +1004 -0
  10. package/artifacts/Fraud.json +164 -0
  11. package/artifacts/KeepRegistry.json +99 -0
  12. package/artifacts/KeepStake.json +286 -0
  13. package/artifacts/KeepToken.json +711 -0
  14. package/artifacts/KeepTokenStaking.json +483 -0
  15. package/artifacts/MovingFunds.json +249 -0
  16. package/artifacts/NuCypherStakingEscrow.json +256 -0
  17. package/artifacts/NuCypherToken.json +711 -0
  18. package/artifacts/RandomBeaconStub.json +141 -0
  19. package/artifacts/Redemption.json +174 -0
  20. package/artifacts/ReimbursementPool.json +509 -0
  21. package/artifacts/Relay.json +123 -0
  22. package/artifacts/T.json +1148 -0
  23. package/artifacts/TBTC.json +36 -35
  24. package/artifacts/TBTCToken.json +738 -0
  25. package/artifacts/TBTCVault.json +691 -0
  26. package/artifacts/TokenStaking.json +2288 -0
  27. package/artifacts/TokenholderGovernor.json +1795 -0
  28. package/artifacts/TokenholderTimelock.json +1058 -0
  29. package/artifacts/VendingMachine.json +34 -33
  30. package/artifacts/VendingMachineKeep.json +400 -0
  31. package/artifacts/VendingMachineNuCypher.json +400 -0
  32. package/artifacts/WalletRegistry.json +1843 -0
  33. package/artifacts/WalletRegistryGovernance.json +2754 -0
  34. package/artifacts/Wallets.json +186 -0
  35. package/artifacts/solcInputs/5e62cff1ead0900b07facca4b559e818.json +314 -0
  36. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  37. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.json +2 -2
  38. package/build/contracts/bank/Bank.sol/Bank.dbg.json +4 -0
  39. package/build/contracts/bank/Bank.sol/Bank.json +542 -0
  40. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +4 -0
  41. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.json +34 -0
  42. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
  43. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
  44. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +4 -0
  45. package/build/contracts/bridge/Bridge.sol/Bridge.json +2686 -0
  46. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  47. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +226 -0
  48. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  49. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  50. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +4 -0
  51. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.json +30 -0
  52. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
  53. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
  54. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
  55. package/build/contracts/bridge/Fraud.sol/Fraud.json +86 -0
  56. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +4 -0
  57. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.json +10 -0
  58. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
  59. package/build/contracts/bridge/IRelay.sol/IRelay.json +37 -0
  60. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +4 -0
  61. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +138 -0
  62. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
  63. package/build/contracts/bridge/Redemption.sol/OutboundTx.json +10 -0
  64. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
  65. package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
  66. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  67. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
  68. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
  69. package/build/contracts/bridge/Wallets.sol/Wallets.json +112 -0
  70. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  71. package/build/contracts/token/TBTC.sol/TBTC.json +4 -4
  72. package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +4 -0
  73. package/build/contracts/vault/DonationVault.sol/DonationVault.json +108 -0
  74. package/build/contracts/vault/IVault.sol/IVault.dbg.json +4 -0
  75. package/build/contracts/vault/IVault.sol/IVault.json +52 -0
  76. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +4 -0
  77. package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +449 -0
  78. package/contracts/GovernanceUtils.sol +4 -4
  79. package/contracts/bank/Bank.sol +436 -0
  80. package/contracts/bank/IReceiveBalanceApproval.sol +45 -0
  81. package/contracts/bridge/BitcoinTx.sol +326 -0
  82. package/contracts/bridge/Bridge.sol +1793 -0
  83. package/contracts/bridge/BridgeState.sol +739 -0
  84. package/contracts/bridge/Deposit.sol +269 -0
  85. package/contracts/bridge/DepositSweep.sol +574 -0
  86. package/contracts/bridge/EcdsaLib.sol +45 -0
  87. package/contracts/bridge/Fraud.sol +579 -0
  88. package/contracts/bridge/Heartbeat.sol +112 -0
  89. package/contracts/bridge/IRelay.sol +28 -0
  90. package/contracts/bridge/MovingFunds.sol +1077 -0
  91. package/contracts/bridge/Redemption.sol +1020 -0
  92. package/contracts/bridge/VendingMachine.sol +2 -2
  93. package/contracts/bridge/Wallets.sol +719 -0
  94. package/contracts/hardhat-dependency-compiler/.hardhat-dependency-compiler +1 -0
  95. package/contracts/hardhat-dependency-compiler/@keep-network/ecdsa/contracts/WalletRegistry.sol +3 -0
  96. package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol +3 -0
  97. package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol +3 -0
  98. package/contracts/token/TBTC.sol +1 -1
  99. package/contracts/vault/DonationVault.sol +125 -0
  100. package/contracts/vault/IVault.sol +44 -0
  101. package/contracts/vault/TBTCVault.sol +305 -0
  102. package/deploy/00_resolve_relay.ts +28 -0
  103. package/deploy/00_resolve_tbtc_v1_token.ts +1 -1
  104. package/deploy/01_deploy_tbtc_v2_token.ts +8 -1
  105. package/deploy/02_deploy_vending_machine.ts +7 -0
  106. package/deploy/{03_transfer_roles.ts → 03_transfer_vending_machine_roles.ts} +1 -1
  107. package/deploy/04_deploy_bank.ts +27 -0
  108. package/deploy/05_deploy_bridge.ts +80 -0
  109. package/deploy/06_deploy_tbtc_vault.ts +30 -0
  110. package/deploy/07_bank_update_bridge.ts +19 -0
  111. package/deploy/08_transfer_bank_ownership.ts +15 -0
  112. package/deploy/09_transfer_tbtc_vault_ownership.ts +15 -0
  113. package/deploy/10_transfer_bridge_governance.ts +20 -0
  114. package/deploy/11_initialize_wallet_owner.ts +18 -0
  115. package/deploy/11_transfer_proxy_admin_ownership.ts +30 -0
  116. package/deploy/12_deploy_proxy_admin_with_deputy.ts +33 -0
  117. package/export/deploy/00_resolve_relay.js +24 -0
  118. package/export/deploy/00_resolve_tbtc_v1_token.js +24 -0
  119. package/export/deploy/01_deploy_tbtc_v2_token.js +19 -0
  120. package/export/deploy/02_deploy_vending_machine.js +25 -0
  121. package/export/deploy/03_transfer_vending_machine_roles.js +19 -0
  122. package/export/deploy/04_deploy_bank.js +21 -0
  123. package/export/deploy/05_deploy_bridge.js +69 -0
  124. package/export/deploy/06_deploy_tbtc_vault.js +24 -0
  125. package/export/deploy/07_bank_update_bridge.js +13 -0
  126. package/export/deploy/08_transfer_bank_ownership.js +11 -0
  127. package/export/deploy/09_transfer_tbtc_vault_ownership.js +11 -0
  128. package/export/deploy/10_transfer_bridge_governance.js +11 -0
  129. package/export/deploy/11_initialize_wallet_owner.js +14 -0
  130. package/export/deploy/11_transfer_proxy_admin_ownership.js +23 -0
  131. package/export/deploy/12_deploy_proxy_admin_with_deputy.js +22 -0
  132. package/export/hardhat.config.js +169 -0
  133. package/export/test/bank/Bank.test.js +1012 -0
  134. package/export/test/bridge/Bridge.Deployment.test.js +76 -0
  135. package/export/test/bridge/Bridge.Deposit.test.js +1834 -0
  136. package/export/test/bridge/Bridge.Frauds.test.js +1349 -0
  137. package/export/test/bridge/Bridge.MovingFunds.test.js +2437 -0
  138. package/export/test/bridge/Bridge.Parameters.test.js +400 -0
  139. package/export/test/bridge/Bridge.Redemption.test.js +2523 -0
  140. package/export/test/bridge/Bridge.Vaults.test.js +74 -0
  141. package/export/test/bridge/Bridge.Wallets.test.js +1017 -0
  142. package/export/test/bridge/EcdsaLib.test.js +46 -0
  143. package/export/test/bridge/Heartbeat.test.js +77 -0
  144. package/export/test/bridge/VendingMachine.Upgrade.test.js +160 -0
  145. package/export/test/bridge/VendingMachine.test.js +762 -0
  146. package/export/test/data/deposit-sweep.js +655 -0
  147. package/export/test/data/ecdsa.js +18 -0
  148. package/export/test/data/fraud.js +158 -0
  149. package/export/test/data/moving-funds.js +815 -0
  150. package/export/test/data/redemption.js +1011 -0
  151. package/export/test/fixtures/bridge.js +54 -0
  152. package/export/test/fixtures/index.js +57 -0
  153. package/export/test/helpers/contract-test-helpers.js +18 -0
  154. package/export/test/integration/Slashing.test.js +279 -0
  155. package/export/test/integration/WalleCreation.test.js +66 -0
  156. package/export/test/integration/utils/ecdsa-wallet-registry.js +137 -0
  157. package/export/test/integration/utils/fixture.js +77 -0
  158. package/export/test/integration/utils/gas.js +36 -0
  159. package/export/test/integration/utils/random-beacon.js +26 -0
  160. package/export/test/integration/utils/staking.js +19 -0
  161. package/export/test/vault/DonationVault.test.js +202 -0
  162. package/export/test/vault/TBTCVault.Redemption.test.js +357 -0
  163. package/export/test/vault/TBTCVault.test.js +768 -0
  164. package/export/typechain/BTCUtils.js +2 -0
  165. package/export/typechain/Bank.js +2 -0
  166. package/export/typechain/BankStub.js +2 -0
  167. package/export/typechain/Bridge.js +2 -0
  168. package/export/typechain/BridgeState.js +2 -0
  169. package/export/typechain/BridgeStub.js +2 -0
  170. package/export/typechain/Deposit.js +2 -0
  171. package/export/typechain/DepositSweep.js +2 -0
  172. package/export/typechain/DonationVault.js +2 -0
  173. package/export/typechain/ERC165.js +2 -0
  174. package/export/typechain/ERC1967Proxy.js +2 -0
  175. package/export/typechain/ERC1967Upgrade.js +2 -0
  176. package/export/typechain/ERC20WithPermit.js +2 -0
  177. package/export/typechain/ERC721.js +2 -0
  178. package/export/typechain/EcdsaAuthorization.js +2 -0
  179. package/export/typechain/EcdsaDkg.js +2 -0
  180. package/export/typechain/EcdsaDkgValidator.js +2 -0
  181. package/export/typechain/EcdsaInactivity.js +2 -0
  182. package/export/typechain/Fraud.js +2 -0
  183. package/export/typechain/Governable.js +2 -0
  184. package/export/typechain/HeartbeatStub.js +2 -0
  185. package/export/typechain/IApplication.js +2 -0
  186. package/export/typechain/IApproveAndCall.js +2 -0
  187. package/export/typechain/IBeacon.js +2 -0
  188. package/export/typechain/IERC165.js +2 -0
  189. package/export/typechain/IERC1822Proxiable.js +2 -0
  190. package/export/typechain/IERC20.js +2 -0
  191. package/export/typechain/IERC20Metadata.js +2 -0
  192. package/export/typechain/IERC20WithPermit.js +2 -0
  193. package/export/typechain/IERC721.js +2 -0
  194. package/export/typechain/IERC721Metadata.js +2 -0
  195. package/export/typechain/IERC721Receiver.js +2 -0
  196. package/export/typechain/IRandomBeacon.js +2 -0
  197. package/export/typechain/IRandomBeaconConsumer.js +2 -0
  198. package/export/typechain/IReceiveApproval.js +2 -0
  199. package/export/typechain/IReceiveBalanceApproval.js +2 -0
  200. package/export/typechain/IRelay.js +2 -0
  201. package/export/typechain/IStaking.js +2 -0
  202. package/export/typechain/IVault.js +2 -0
  203. package/export/typechain/IWalletOwner.js +2 -0
  204. package/export/typechain/IWalletRegistry.js +2 -0
  205. package/export/typechain/Initializable.js +2 -0
  206. package/export/typechain/MisfundRecovery.js +2 -0
  207. package/export/typechain/MovingFunds.js +2 -0
  208. package/export/typechain/Ownable.js +2 -0
  209. package/export/typechain/Proxy.js +2 -0
  210. package/export/typechain/ProxyAdmin.js +2 -0
  211. package/export/typechain/ReceiveApprovalStub.js +2 -0
  212. package/export/typechain/Redemption.js +2 -0
  213. package/export/typechain/Reimbursable.js +2 -0
  214. package/export/typechain/ReimbursementPool.js +2 -0
  215. package/export/typechain/Rewards.js +2 -0
  216. package/export/typechain/SortitionPool.js +2 -0
  217. package/export/typechain/SortitionTree.js +2 -0
  218. package/export/typechain/TBTC.js +2 -0
  219. package/export/typechain/TBTCVault.js +2 -0
  220. package/export/typechain/TestERC20.js +2 -0
  221. package/export/typechain/TestERC721.js +2 -0
  222. package/export/typechain/TestEcdsaLib.js +2 -0
  223. package/export/typechain/TestRelay.js +2 -0
  224. package/export/typechain/TransparentUpgradeableProxy.js +2 -0
  225. package/export/typechain/VendingMachine.js +2 -0
  226. package/export/typechain/WalletRegistry.js +2 -0
  227. package/export/typechain/Wallets.js +2 -0
  228. package/export/typechain/common.js +2 -0
  229. package/export/typechain/factories/BTCUtils__factory.js +94 -0
  230. package/export/typechain/factories/BankStub__factory.js +586 -0
  231. package/export/typechain/factories/Bank__factory.js +573 -0
  232. package/export/typechain/factories/BridgeState__factory.js +257 -0
  233. package/export/typechain/factories/BridgeStub__factory.js +2912 -0
  234. package/export/typechain/factories/Bridge__factory.js +2526 -0
  235. package/export/typechain/factories/DepositSweep__factory.js +61 -0
  236. package/export/typechain/factories/Deposit__factory.js +103 -0
  237. package/export/typechain/factories/DonationVault__factory.js +139 -0
  238. package/export/typechain/factories/ERC165__factory.js +38 -0
  239. package/export/typechain/factories/ERC1967Proxy__factory.js +111 -0
  240. package/export/typechain/factories/ERC1967Upgrade__factory.js +64 -0
  241. package/export/typechain/factories/ERC20WithPermit__factory.js +524 -0
  242. package/export/typechain/factories/ERC721__factory.js +388 -0
  243. package/export/typechain/factories/EcdsaAuthorization__factory.js +211 -0
  244. package/export/typechain/factories/EcdsaDkgValidator__factory.js +441 -0
  245. package/export/typechain/factories/EcdsaDkg__factory.js +192 -0
  246. package/export/typechain/factories/EcdsaInactivity__factory.js +134 -0
  247. package/export/typechain/factories/Fraud__factory.js +117 -0
  248. package/export/typechain/factories/Governable__factory.js +64 -0
  249. package/export/typechain/factories/HeartbeatStub__factory.js +61 -0
  250. package/export/typechain/factories/IApplication__factory.js +152 -0
  251. package/export/typechain/factories/IApproveAndCall__factory.js +48 -0
  252. package/export/typechain/factories/IBeacon__factory.js +32 -0
  253. package/export/typechain/factories/IERC165__factory.js +38 -0
  254. package/export/typechain/factories/IERC1822Proxiable__factory.js +32 -0
  255. package/export/typechain/factories/IERC20Metadata__factory.js +241 -0
  256. package/export/typechain/factories/IERC20WithPermit__factory.js +389 -0
  257. package/export/typechain/factories/IERC20__factory.js +202 -0
  258. package/export/typechain/factories/IERC721Metadata__factory.js +349 -0
  259. package/export/typechain/factories/IERC721Receiver__factory.js +53 -0
  260. package/export/typechain/factories/IERC721__factory.js +304 -0
  261. package/export/typechain/factories/IRandomBeaconConsumer__factory.js +37 -0
  262. package/export/typechain/factories/IRandomBeacon__factory.js +32 -0
  263. package/export/typechain/factories/IReceiveApproval__factory.js +47 -0
  264. package/export/typechain/factories/IReceiveBalanceApproval__factory.js +42 -0
  265. package/export/typechain/factories/IRelay__factory.js +45 -0
  266. package/export/typechain/factories/IStaking__factory.js +722 -0
  267. package/export/typechain/factories/IVault__factory.js +60 -0
  268. package/export/typechain/factories/IWalletOwner__factory.js +65 -0
  269. package/export/typechain/factories/IWalletRegistry__factory.js +138 -0
  270. package/export/typechain/factories/Initializable__factory.js +32 -0
  271. package/export/typechain/factories/MisfundRecovery__factory.js +145 -0
  272. package/export/typechain/factories/MovingFunds__factory.js +169 -0
  273. package/export/typechain/factories/Ownable__factory.js +71 -0
  274. package/export/typechain/factories/ProxyAdmin__factory.js +191 -0
  275. package/export/typechain/factories/Proxy__factory.js +27 -0
  276. package/export/typechain/factories/ReceiveApprovalStub__factory.js +127 -0
  277. package/export/typechain/factories/Redemption__factory.js +123 -0
  278. package/export/typechain/factories/Reimbursable__factory.js +58 -0
  279. package/export/typechain/factories/ReimbursementPool__factory.js +350 -0
  280. package/export/typechain/factories/Rewards__factory.js +117 -0
  281. package/export/typechain/factories/SortitionPool__factory.js +610 -0
  282. package/export/typechain/factories/SortitionTree__factory.js +149 -0
  283. package/export/typechain/factories/TBTCVault__factory.js +480 -0
  284. package/export/typechain/factories/TBTC__factory.js +564 -0
  285. package/export/typechain/factories/TestERC20__factory.js +539 -0
  286. package/export/typechain/factories/TestERC721__factory.js +421 -0
  287. package/export/typechain/factories/TestEcdsaLib__factory.js +66 -0
  288. package/export/typechain/factories/TestRelay__factory.js +94 -0
  289. package/export/typechain/factories/TransparentUpgradeableProxy__factory.js +186 -0
  290. package/export/typechain/factories/VendingMachine__factory.js +549 -0
  291. package/export/typechain/factories/WalletRegistry__factory.js +1919 -0
  292. package/export/typechain/factories/Wallets__factory.js +143 -0
  293. package/export/typechain/index.js +132 -0
  294. package/export.json +15932 -503
  295. package/package.json +47 -26
  296. 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
+ });