From 0b43ab3bf403dc4ede303f0d6e2788b3e8156cd6 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Fri, 23 Aug 2024 15:19:07 +0700 Subject: [PATCH] script: update post check --- .../20240411-operators-key-template.s.sol | 6 +- .../20240411-operators-key.s.sol | 12 +- ...0240411-p3-upgrade-bridge-main-chain.s.sol | 14 +- .../20240619-operators-key.s.sol | 12 +- ...0240619-p3-upgrade-bridge-main-chain.s.sol | 78 +++++++++++- .../20240716-operators-key-template.s.sol | 6 +- script/operations/PostCheckCI.s.sol | 1 + .../gateway/PostCheck_Gateway.s.sol | 4 +- ...PostCheck_Gateway_DepositAndWithdraw.s.sol | 120 +++++++++++++++--- .../quorum/PostCheck_Gateway_Quorum.s.sol | 63 +++++++++ .../manager/PostCheck_BridgeManager.s.sol | 2 +- .../PostCheck_BridgeManager_Quorum.s.sol | 78 +++++------- 12 files changed, 306 insertions(+), 90 deletions(-) create mode 100644 script/post-check/gateway/quorum/PostCheck_Gateway_Quorum.s.sol diff --git a/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key-template.s.sol b/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key-template.s.sol index 3381acfb..a69e45a1 100644 --- a/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key-template.s.sol +++ b/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key-template.s.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.19; contract Migration__20240409_GovernorsKey { - function _loadGovernorPKs() internal pure returns (uint256[] memory res) { - res = new uint256[](1); + function _loadGovernors() internal pure returns (address[] memory res) { + res = new address[](1); - res[0] = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef; + res[0] = address(0xdeadbeef); } } diff --git a/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key.s.sol b/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key.s.sol index 78e02f75..f85fde1d 100644 --- a/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key.s.sol +++ b/script/20240411-upgrade-v3.2.0-testnet/20240411-operators-key.s.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.19; contract Migration__20240409_GovernorsKey { - function _loadGovernorPKs() internal pure returns (uint256[] memory res) { - res = new uint256[](4); + function _loadGovernors() internal pure returns (address[] memory res) { + res = new address[](4); - res[3] = 0xe3c1c8220c4ee4a6532d633296c3301db5397cff8a89a920da28f8bec97fcfb6; - res[2] = 0xeb80bc77e3164b6bb3eebf7d5f96f2496eb292fab563377f247d2db5887395e0; - res[0] = 0xed79936f720ac50b7c06138c6fd2d70abc19935de0fb347b0d782bdb6630e5a4; - res[1] = 0x3b3eb1d442ea0d728bc069f9d6d47ba8dcc6c867800cdf42a12117cf231bba59; + res[3] = 0xd24D87DDc1917165435b306aAC68D99e0F49A3Fa; + res[2] = 0xb033ba62EC622dC54D0ABFE0254e79692147CA26; + res[0] = 0x087D08e3ba42e64E3948962dd1371F906D1278b9; + res[1] = 0x52ec2e6BBcE45AfFF8955Da6410bb13812F4289F; } } diff --git a/script/20240411-upgrade-v3.2.0-testnet/20240411-p3-upgrade-bridge-main-chain.s.sol b/script/20240411-upgrade-v3.2.0-testnet/20240411-p3-upgrade-bridge-main-chain.s.sol index 28e42cd2..cbf54bdf 100644 --- a/script/20240411-upgrade-v3.2.0-testnet/20240411-p3-upgrade-bridge-main-chain.s.sol +++ b/script/20240411-upgrade-v3.2.0-testnet/20240411-p3-upgrade-bridge-main-chain.s.sol @@ -188,7 +188,7 @@ contract Migration__20240409_P3_UpgradeBridgeMainchain is Migration, Migration__ supports_[i] = Ballot.VoteType.For; } - Signature[] memory signatures = _generateSignaturesFor(getDomain(), hashLegacyProposal(proposal), _loadGovernorPKs(), Ballot.VoteType.For); + Signature[] memory signatures = _generateSignaturesFor(getDomain(), hashLegacyProposal(proposal), _loadGovernors(), Ballot.VoteType.For); vm.broadcast(_governor); (bool success,) = address(_currMainchainBridgeManager).call{ gas: (proposal.targets.length + 1) * 1_000_000 }( @@ -213,19 +213,19 @@ contract Migration__20240409_P3_UpgradeBridgeMainchain is Migration, Migration__ function _generateSignaturesFor( bytes32 domain, bytes32 proposalHash, - uint256[] memory signerPKs, + address[] memory signers, Ballot.VoteType support ) public pure returns (Signature[] memory sigs) { - sigs = new Signature[](signerPKs.length); + sigs = new Signature[](signers.length); - for (uint256 i; i < signerPKs.length; i++) { + for (uint256 i; i < signers.length; i++) { bytes32 digest = ECDSA.toTypedDataHash(domain, Ballot.hash(proposalHash, support)); - sigs[i] = _sign(signerPKs[i], digest); + sigs[i] = _sign(signers[i], digest); } } - function _sign(uint256 pk, bytes32 digest) internal pure returns (Signature memory sig) { - (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); + function _sign(address signer, bytes32 digest) internal pure returns (Signature memory sig) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signer, digest); sig.v = v; sig.r = r; sig.s = s; diff --git a/script/20240619-upgrade-sepolia/20240619-operators-key.s.sol b/script/20240619-upgrade-sepolia/20240619-operators-key.s.sol index 11c2f3fd..58d745d6 100644 --- a/script/20240619-upgrade-sepolia/20240619-operators-key.s.sol +++ b/script/20240619-upgrade-sepolia/20240619-operators-key.s.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.19; contract Migration__20240619_GovernorsKey { - function _loadGovernorPKs() internal pure returns (uint256[] memory res) { - res = new uint256[](4); + function _loadGovernors() internal pure returns (address[] memory res) { + res = new address[](4); - res[0] = 0x00; - res[1] = 0x00; - res[2] = 0x00; - res[3] = 0x00; + res[0] = address(0x0); + res[1] = address(0x0); + res[2] = address(0x0); + res[3] = address(0x0); } } diff --git a/script/20240619-upgrade-sepolia/20240619-p3-upgrade-bridge-main-chain.s.sol b/script/20240619-upgrade-sepolia/20240619-p3-upgrade-bridge-main-chain.s.sol index eb8f92fd..43238d83 100644 --- a/script/20240619-upgrade-sepolia/20240619-p3-upgrade-bridge-main-chain.s.sol +++ b/script/20240619-upgrade-sepolia/20240619-p3-upgrade-bridge-main-chain.s.sol @@ -80,8 +80,6 @@ contract Migration__20240619_P3_UpgradeBridgeMainchain is Migration, Migration__ governors[0] = 0x087D08e3ba42e64E3948962dd1371F906D1278b9; governors[1] = 0x52ec2e6BBcE45AfFF8955Da6410bb13812F4289F; - _mainchainProposalUtils = new MainchainBridgeAdminUtils(2021, _loadGovernorPKs(), IMainchainBridgeManager(_mainchainBridgeManager), governors[0]); - Proposal.ProposalDetail memory proposal = Proposal.ProposalDetail({ nonce: IMainchainBridgeManager(_mainchainBridgeManager).round(block.chainid) + 1, chainId: block.chainid, @@ -93,16 +91,90 @@ contract Migration__20240619_P3_UpgradeBridgeMainchain is Migration, Migration__ gasAmounts: gasAmounts }); + LegacyProposalDetail memory legacyProposal; + legacyProposal.nonce = proposal.nonce; + legacyProposal.chainId = proposal.chainId; + legacyProposal.expiryTimestamp = proposal.expiryTimestamp; + legacyProposal.targets = proposal.targets; + legacyProposal.values = proposal.values; + legacyProposal.calldatas = proposal.calldatas; + legacyProposal.gasAmounts = proposal.gasAmounts; + Ballot.VoteType[] memory supports_ = new Ballot.VoteType[](4); supports_[0] = Ballot.VoteType.For; supports_[1] = Ballot.VoteType.For; supports_[2] = Ballot.VoteType.For; supports_[3] = Ballot.VoteType.For; - Signature[] memory signatures = _mainchainProposalUtils.generateSignatures(proposal, _loadGovernorPKs()); + bytes32 proposalHash = hashLegacyProposal(legacyProposal); + Signature[] memory signatures = _generateSignaturesFor(getDomain(), proposalHash, _loadGovernors(), supports_[0]); vm.broadcast(governors[0]); // 2_000_000 to assure tx.gasleft is bigger than the gas of the proposal. IMainchainBridgeManager(_mainchainBridgeManager).relayProposal{ gas: 2_000_000 }(proposal, supports_, signatures); } + + function getDomain() public pure returns (bytes32) { + return keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,bytes32 salt)"), + keccak256("BridgeAdmin"), // name hash + keccak256("2"), // version hash + keccak256(abi.encode("BRIDGE_ADMIN", 2021)) // salt + ) + ); + } + + function hashLegacyProposal(LegacyProposalDetail memory proposal) public pure returns (bytes32 digest_) { + bytes32 TYPE_HASH = 0xd051578048e6ff0bbc9fca3b65a42088dbde10f36ca841de566711087ad9b08a; + + uint256[] memory values = proposal.values; + address[] memory targets = proposal.targets; + bytes32[] memory calldataHashList = new bytes32[](proposal.calldatas.length); + uint256[] memory gasAmounts = proposal.gasAmounts; + + for (uint256 i; i < calldataHashList.length; ++i) { + calldataHashList[i] = keccak256(proposal.calldatas[i]); + } + + assembly { + let ptr := mload(0x40) + mstore(ptr, TYPE_HASH) + mstore(add(ptr, 0x20), mload(proposal)) // _proposal.nonce + mstore(add(ptr, 0x40), mload(add(proposal, 0x20))) // _proposal.chainId + mstore(add(ptr, 0x60), mload(add(proposal, 0x40))) // expiry timestamp + + let arrayHashed + arrayHashed := keccak256(add(targets, 32), mul(mload(targets), 32)) // targetsHash + mstore(add(ptr, 0x80), arrayHashed) + arrayHashed := keccak256(add(values, 32), mul(mload(values), 32)) // _valuesHash + mstore(add(ptr, 0xa0), arrayHashed) + arrayHashed := keccak256(add(calldataHashList, 32), mul(mload(calldataHashList), 32)) // _calldatasHash + mstore(add(ptr, 0xc0), arrayHashed) + arrayHashed := keccak256(add(gasAmounts, 32), mul(mload(gasAmounts), 32)) // _gasAmountsHash + mstore(add(ptr, 0xe0), arrayHashed) + digest_ := keccak256(ptr, 0x100) + } + } + + function _generateSignaturesFor( + bytes32 domain, + bytes32 proposalHash, + address[] memory signers, + Ballot.VoteType support + ) public pure returns (Signature[] memory sigs) { + sigs = new Signature[](signers.length); + + for (uint256 i; i < signers.length; i++) { + bytes32 digest = ECDSA.toTypedDataHash(domain, Ballot.hash(proposalHash, support)); + sigs[i] = _sign(signers[i], digest); + } + } + + function _sign(address signer, bytes32 digest) internal pure returns (Signature memory sig) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signer, digest); + sig.v = v; + sig.r = r; + sig.s = s; + } } diff --git a/script/20240716-upgrade-v3.2.0-mainnet/20240716-operators-key-template.s.sol b/script/20240716-upgrade-v3.2.0-mainnet/20240716-operators-key-template.s.sol index fa6c4ab1..0036a60f 100644 --- a/script/20240716-upgrade-v3.2.0-mainnet/20240716-operators-key-template.s.sol +++ b/script/20240716-upgrade-v3.2.0-mainnet/20240716-operators-key-template.s.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.19; contract Migration__20240716_GovernorsKey { - function _loadGovernorPKs() internal pure returns (uint256[] memory res) { - res = new uint256[](1); + function _loadGovernors() internal pure returns (address[] memory res) { + res = new address[](1); - res[0] = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef; + res[0] = address(0xdeadbeef); } } diff --git a/script/operations/PostCheckCI.s.sol b/script/operations/PostCheckCI.s.sol index 24a4da83..78e71cb0 100644 --- a/script/operations/PostCheckCI.s.sol +++ b/script/operations/PostCheckCI.s.sol @@ -28,6 +28,7 @@ contract PostCheckCI is Migration { (, uint256 prevForkId) = switchTo(companionNetwork); address payable ethBM = loadContract(Contract.MainchainBridgeManager.key()); address payable ethGW = loadContract(Contract.MainchainGatewayV3.key()); + _cheatChangePAIfNotSelf(ethBM); _cheatUnpauseIfPaused(ethGW); diff --git a/script/post-check/gateway/PostCheck_Gateway.s.sol b/script/post-check/gateway/PostCheck_Gateway.s.sol index 0abd1f0a..21829192 100644 --- a/script/post-check/gateway/PostCheck_Gateway.s.sol +++ b/script/post-check/gateway/PostCheck_Gateway.s.sol @@ -2,9 +2,11 @@ pragma solidity ^0.8.19; import { PostCheck_Gateway_DepositAndWithdraw } from "./deposit-withdraw/PostCheck_Gateway_DepositAndWithdraw.s.sol"; +import { PostCheck_Gateway_Quorum } from "./quorum/PostCheck_Gateway_Quorum.s.sol"; -abstract contract PostCheck_Gateway is PostCheck_Gateway_DepositAndWithdraw { +abstract contract PostCheck_Gateway is PostCheck_Gateway_DepositAndWithdraw, PostCheck_Gateway_Quorum { function _validate_Gateway() internal onPostCheck("_validate_Gateway") { + _validate_Gateway_Quorum(); _validate_Gateway_DepositAndWithdraw(); } } diff --git a/script/post-check/gateway/deposit-withdraw/PostCheck_Gateway_DepositAndWithdraw.s.sol b/script/post-check/gateway/deposit-withdraw/PostCheck_Gateway_DepositAndWithdraw.s.sol index 0ba9938b..c6920ea8 100644 --- a/script/post-check/gateway/deposit-withdraw/PostCheck_Gateway_DepositAndWithdraw.s.sol +++ b/script/post-check/gateway/deposit-withdraw/PostCheck_Gateway_DepositAndWithdraw.s.sol @@ -135,10 +135,13 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { function _validate_Gateway_DepositAndWithdraw() internal { _setUp(); - validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20(); + validate_Gateway_Deposit_WETH(); + // validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20(); + validate_Gateway_Withdraw_ETH(); validate_Gateway_WETHAddressUnchanged(); validate_Gateway_Deposit_ETH(); validate_Gateway_RevertIf_InsufficientSentValue_Deposit_ETH(); + validate_Gateway_RevertIf_DuplicatedSigs_Withdraw_ERC20(); validate_Gateway_RevertIf_UnsortedSigs_Withdraw_ERC20(); validate_HasBridgeManager(); @@ -149,6 +152,40 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { validate_Gateway_RevertIf_InsufficientThreshold_Withdraw_ERC20(); } + function validate_Gateway_Withdraw_ETH() private onPostCheck("validate_Gateway_Withdraw_ETH") onlyOnRoninNetworkOrLocal { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronWETH); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 100 ether; + + deal(address(ronWETH), user, withdrawReq.info.quantity); + vm.prank(user); + ronWETH.approve(ronGW, withdrawReq.info.quantity); + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + overrideMockBOs(ethBM); + + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM); + assertTrue(minSigRequired > 1, "Invalid test setup"); + + Signature[] memory sigs = _bulkSignReceipt(mockOps.slice(0, minSigRequired), receiptDigest); + + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + assertEq(withdrawReq.recipientAddr.balance, withdrawReq.info.quantity, "Withdraw should be processed"); + + switchBack(prevNetwork, prevForkId); + } + function validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20() private onPostCheck("validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20") @@ -175,20 +212,23 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { overrideMockBOs(ethBM); - uint256 minVW = IQuorum(ethGW).minimumVoteWeight(); - uint256 defaultVW = IBridgeManager(ethBM).getTotalWeight() / IBridgeManager(ethBM).totalBridgeOperator(); - uint256 minSigRequired = minVW / defaultVW; + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM); uint256 unmetSigCount = minSigRequired - 1; assertTrue(unmetSigCount > 1, "Invalid test setup"); // Sign first to get renounced operator signatures Signature[] memory sigs = _bulkSignReceipt(mockOps, receiptDigest); + console.log("Sig count", sigs.length); + // Renounce operators vm.prank(address(ethBM)); ITransparentUpgradeableProxyV2(ethBM).functionDelegateCall( abi.encodeCall(IBridgeManager.removeBridgeOperators, (mockOps.slice(unmetSigCount, mockOps.length))) ); + console.log("Current operator count", IBridgeManager(ethBM).totalBridgeOperator()); + console.log("MinVW After", IQuorum(ethGW).minimumVoteWeight()); + vm.expectRevert(); IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); @@ -212,6 +252,49 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { vm.prank(user); vm.expectRevert(); IMainchainGatewayV3(ethGW).requestDepositFor{ value: depositReq.info.quantity - 1 }(depositReq); + + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_Deposit_WETH() private onPostCheck("validate_Gateway_Deposit_WETH") onlyOnRoninNetworkOrLocal { + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(ethWETH); + depositReq.info.erc = TokenStandard.ERC20; + depositReq.info.id = 0; + depositReq.info.quantity = 100 ether; + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + deal(address(ethWETH), user, depositReq.info.quantity); + + vm.prank(user); + ethWETH.approve(ethGW, depositReq.info.quantity); + + uint256 balBefore = ethGW.balance; + + vm.prank(user); + vm.recordLogs(); + IMainchainGatewayV3(ethGW).requestDepositFor(depositReq); + + uint256 balAfter = ethGW.balance; + + assertEq(balAfter - balBefore, depositReq.info.quantity, "ETH should be deposited"); + + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); + + switchBack(prevNetwork, prevForkId); + + overrideMockBOs(ronBM); + + uint256 minVoteRequired = _calcMinSigOrVoteRequired(ronBM); + assertTrue(minVoteRequired > 1, "Invalid test setup"); + + for (uint256 i; i < minVoteRequired; ++i) { + vm.prank(mockOps[i]); + IRoninGatewayV3(ronGW).depositFor(receipt); + } + + assertEq(ronWETH.balanceOf(depositReq.recipientAddr), depositReq.info.quantity, "Deposit should be processed"); } function validate_Gateway_Deposit_ETH() private onPostCheck("validate_Gateway_Deposit_ETH") onlyOnRoninNetworkOrLocal { @@ -225,18 +308,22 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { vm.deal(user, depositReq.info.quantity); vm.prank(user); + + uint256 balBefore = address(ethGW).balance; + vm.recordLogs(); IMainchainGatewayV3(ethGW).requestDepositFor{ value: depositReq.info.quantity }(depositReq); + uint256 balAfter = address(ethGW).balance; + assertEq(balAfter - balBefore, depositReq.info.quantity, "ETH should be deposited"); + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); switchBack(prevNetwork, prevForkId); overrideMockBOs(ronBM); - uint256 minVW = IQuorum(ronGW).minimumVoteWeight(); - uint256 defaultVW = IBridgeManager(ronBM).getTotalWeight() / IBridgeManager(ronBM).totalBridgeOperator(); - uint256 minVoteRequired = minVW / defaultVW + 1; + uint256 minVoteRequired = _calcMinSigOrVoteRequired(ronBM); assertTrue(minVoteRequired > 1, "Invalid test setup"); for (uint256 i; i < minVoteRequired; ++i) { @@ -370,9 +457,7 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { overrideMockBOs(ronBM); - uint256 minVW = IQuorum(ronGW).minimumVoteWeight(); - uint256 defaultVW = IBridgeManager(ronBM).getTotalWeight() / IBridgeManager(ronBM).totalBridgeOperator(); - uint256 minVoteRequired = minVW / defaultVW + 1; + uint256 minVoteRequired = _calcMinSigOrVoteRequired(ronBM); assertTrue(minVoteRequired > 1, "Invalid test setup"); for (uint256 i; i < minVoteRequired; ++i) { @@ -447,8 +532,9 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); - (, uint256 invalidPK) = makeAddrAndKey(string.concat("invalid-signer-", vm.toString(vm.unixTime()))); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(invalidPK, receiptDigest); + (address invalidSigner, uint256 invalidPK) = makeAddrAndKey(string.concat("invalid-signer-", vm.toString(vm.unixTime()))); + vm.rememberKey(invalidPK); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(invalidSigner, receiptDigest); Signature[] memory sigs = new Signature[](1); sigs[0] = Signature(v, r, s); @@ -484,9 +570,7 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { overrideMockBOs(ethBM); - uint256 minVW = IQuorum(ethGW).minimumVoteWeight(); - uint256 defaultVW = IBridgeManager(ethBM).getTotalWeight() / IBridgeManager(ethBM).totalBridgeOperator(); - uint256 minSigRequired = minVW / defaultVW; + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM); uint256 unmetSigCount = minSigRequired - 1; assertTrue(unmetSigCount > 1, "Invalid test setup"); @@ -532,6 +616,12 @@ abstract contract PostCheck_Gateway_DepositAndWithdraw is BasePostCheck { switchBack(prevNetwork, prevForkId); } + function _calcMinSigOrVoteRequired(address bm) private view returns (uint256 minVoteOrSig) { + uint256 minVW = IQuorum(bm).minimumVoteWeight(); + uint256 defaultVW = IBridgeManager(bm).getTotalWeight() / IBridgeManager(bm).totalBridgeOperator(); + minVoteOrSig = minVW / defaultVW + 1; + } + function _bulkSignReceipt(address[] memory signers, bytes32 receiptDigest) private pure returns (Signature[] memory sigs) { LibArray.inplaceAscSort(signers); diff --git a/script/post-check/gateway/quorum/PostCheck_Gateway_Quorum.s.sol b/script/post-check/gateway/quorum/PostCheck_Gateway_Quorum.s.sol new file mode 100644 index 00000000..add3ddfc --- /dev/null +++ b/script/post-check/gateway/quorum/PostCheck_Gateway_Quorum.s.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IBridgeManager } from "@ronin/contracts/interfaces/bridge/IBridgeManager.sol"; +import { IQuorum } from "@ronin/contracts/interfaces/IQuorum.sol"; +import { BasePostCheck } from "script/post-check/BasePostCheck.s.sol"; +import { LibArray } from "script/shared/libraries/LibArray.sol"; +import { Contract } from "script/utils/Contract.sol"; +import { TNetwork } from "@fdk/types/Types.sol"; +import { Network } from "script/utils/Network.sol"; +import { LibCompanionNetwork } from "script/shared/libraries/LibCompanionNetwork.sol"; + +abstract contract PostCheck_Gateway_Quorum is BasePostCheck { + using LibArray for *; + using LibCompanionNetwork for *; + + function _validate_Gateway_Quorum() internal { + // -------------- Gateway Quorum -------------- + validate_NonZero_MinimumVoteWeight_Gateway(); + validate_NonZero_TotalWeight_Gateway(); + validate_Valid_Threshold_Gateway(); + } + + function validate_Valid_Threshold_Gateway() private onlyOnRoninNetworkOrLocal onPostCheck("validate_Valid_Threshold_Gateway") { + (uint256 num, uint256 denom) = IQuorum(ronGW).getThreshold(); + assertTrue(num > 0 && denom > 0, "Ronin: Gateway's Threshold must be greater than 0"); + assertTrue(num <= denom, "Ronin: Gateway's Threshold numerator must be less than or equal to denominator"); + TNetwork currNetwork = network(); + + (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + (num, denom) = IQuorum(ethGW).getThreshold(); + assertTrue(num > 0 && denom > 0, "Mainchain: Gateway's Threshold must be greater than 0"); + assertTrue(num <= denom, "Mainchain: Gateway's Threshold numerator must be less than or equal to denominator"); + + switchBack(prevNetwork, prevForkId); + } + + function validate_NonZero_TotalWeight_Gateway() private onlyOnRoninNetworkOrLocal onPostCheck("validate_NonZero_TotalWeight_Gateway") { + assertTrue(IBridgeManager(ronGW).getTotalWeight() > 0, "Ronin: Gateway's Total weight must be greater than 0"); + TNetwork currNetwork = network(); + + (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + assertTrue(IBridgeManager(ethGW).getTotalWeight() > 0, "Mainchain: Gateway's Total weight must be greater than 0"); + + switchBack(prevNetwork, prevForkId); + } + + function validate_NonZero_MinimumVoteWeight_Gateway() private onlyOnRoninNetworkOrLocal onPostCheck("validate_NonZero_Threshold_Gateway") { + assertTrue(IQuorum(ronGW).minimumVoteWeight() > 0, "Ronin: Gateway's Minimum vote weight must be greater than 0"); + TNetwork currNetwork = network(); + + (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + assertTrue(IQuorum(ethGW).minimumVoteWeight() > 0, "Mainchain: Gateway's Minimum vote weight must be greater than 0"); + + switchBack(prevNetwork, prevForkId); + } +} diff --git a/script/post-check/manager/PostCheck_BridgeManager.s.sol b/script/post-check/manager/PostCheck_BridgeManager.s.sol index ba953ae9..5face6bf 100644 --- a/script/post-check/manager/PostCheck_BridgeManager.s.sol +++ b/script/post-check/manager/PostCheck_BridgeManager.s.sol @@ -16,6 +16,6 @@ abstract contract PostCheck_BridgeManager is _validate_BridgeManager_CRUD_addBridgeOperators(); _validate_BridgeManager_CRUD_removeBridgeOperators(); _validate_BridgeManager_Proposal(); - // _validate_BridgeManager_Quorum(); + _validate_BridgeManager_Quorum(); } } diff --git a/script/post-check/manager/quorum/PostCheck_BridgeManager_Quorum.s.sol b/script/post-check/manager/quorum/PostCheck_BridgeManager_Quorum.s.sol index f83b2b38..37f6d8b1 100644 --- a/script/post-check/manager/quorum/PostCheck_BridgeManager_Quorum.s.sol +++ b/script/post-check/manager/quorum/PostCheck_BridgeManager_Quorum.s.sol @@ -14,70 +14,52 @@ abstract contract PostCheck_BridgeManager_Quorum is BasePostCheck { using LibArray for *; using LibCompanionNetwork for *; + /// @dev Expected vote weight for BridgeManager's Operator + uint256 private constant expectedVW = 100; + /// @dev Expected minimum vote weight for BridgeManager + uint256 private constant expectedMinTotalWeight = 100 * 3; + function _validate_BridgeManager_Quorum() internal { // -------------- BridgeManager Quorum -------------- + validate_Equal_VoteWeight_Operator_BridgeManager(); validate_NonZero_MinimumVoteWeight_BridgeManager(); - validate_NonZero_TotalWeight_BridgeManager(); + validate_GreaterOrEqualMinExpected_TotalWeight_BridgeManager(); validate_Valid_Threshold_BridgeManager(); - - // -------------- Gateway Quorum -------------- - validate_NonZero_MinimumVoteWeight_Gateway(); - validate_NonZero_TotalWeight_Gateway(); - validate_Valid_Threshold_Gateway(); } - function validate_Valid_Threshold_BridgeManager() internal onlyOnRoninNetworkOrLocal onPostCheck("validate_valid_Threshold_BridgeManager") { - (uint256 num, uint256 denom) = IQuorum(ronBM).getThreshold(); - assertTrue(num > 0 && denom > 0, "Ronin: BridgeManager's Threshold must be greater than 0"); - assertTrue(num <= denom, "Ronin: BridgeManager's Threshold numerator must be less than or equal to denominator"); - TNetwork currNetwork = network(); - - (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); - (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); - - (num, denom) = IQuorum(ethBM).getThreshold(); - assertTrue(num > 0 && denom > 0, "Mainchain: BridgeManager's Threshold must be greater than 0"); - assertTrue(num <= denom, "Mainchain: BridgeManager's Threshold numerator must be less than or equal to denominator"); - - switchBack(prevNetwork, prevForkId); - } + function validate_Equal_VoteWeight_Operator_BridgeManager() private onlyOnRoninNetworkOrLocal onPostCheck("validate_Equal_VoteWeight_Operator_BridgeManager") { + address[] memory operators = IBridgeManager(ronBM).getBridgeOperators(); + for (uint256 i = 0; i < operators.length; i++) { + uint256 voteWeight = IBridgeManager(ronBM).getBridgeOperatorWeight(operators[i]); + assertTrue(voteWeight == expectedVW, "Ronin: BridgeManager's Operator vote weight must be equal to 100"); + } - function validate_Valid_Threshold_Gateway() internal onlyOnRoninNetworkOrLocal onPostCheck("validate_Valid_Threshold_Gateway") { - (uint256 num, uint256 denom) = IQuorum(ronGW).getThreshold(); - assertTrue(num > 0 && denom > 0, "Ronin: Gateway's Threshold must be greater than 0"); - assertTrue(num <= denom, "Ronin: Gateway's Threshold numerator must be less than or equal to denominator"); TNetwork currNetwork = network(); (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); - (num, denom) = IQuorum(ethGW).getThreshold(); - assertTrue(num > 0 && denom > 0, "Mainchain: Gateway's Threshold must be greater than 0"); - assertTrue(num <= denom, "Mainchain: Gateway's Threshold numerator must be less than or equal to denominator"); + operators = IBridgeManager(ethBM).getBridgeOperators(); + for (uint256 i = 0; i < operators.length; i++) { + uint256 voteWeight = IBridgeManager(ethBM).getBridgeOperatorWeight(operators[i]); + assertTrue(voteWeight == expectedVW, "Mainchain: BridgeManager's Operator vote weight must be equal to 100"); + } switchBack(prevNetwork, prevForkId); } - function validate_NonZero_TotalWeight_Gateway() internal onlyOnRoninNetworkOrLocal onPostCheck("validate_NonZero_TotalWeight_Gateway") { - assertTrue(IBridgeManager(ronGW).getTotalWeight() > 0, "Ronin: Gateway's Total weight must be greater than 0"); - TNetwork currNetwork = network(); - - (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); - (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); - - assertTrue(IBridgeManager(ethGW).getTotalWeight() > 0, "Mainchain: Gateway's Total weight must be greater than 0"); - - switchBack(prevNetwork, prevForkId); - } - - function validate_NonZero_MinimumVoteWeight_Gateway() internal onlyOnRoninNetworkOrLocal onPostCheck("validate_NonZero_Threshold_Gateway") { - assertTrue(IQuorum(ronGW).minimumVoteWeight() > 0, "Ronin: Gateway's Minimum vote weight must be greater than 0"); + function validate_Valid_Threshold_BridgeManager() private onlyOnRoninNetworkOrLocal onPostCheck("validate_valid_Threshold_BridgeManager") { + (uint256 num, uint256 denom) = IQuorum(ronBM).getThreshold(); + assertTrue(num > 0 && denom > 0, "Ronin: BridgeManager's Threshold must be greater than 0"); + assertTrue(num <= denom, "Ronin: BridgeManager's Threshold numerator must be less than or equal to denominator"); TNetwork currNetwork = network(); (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); - assertTrue(IQuorum(ethGW).minimumVoteWeight() > 0, "Mainchain: Gateway's Minimum vote weight must be greater than 0"); + (num, denom) = IQuorum(ethBM).getThreshold(); + assertTrue(num > 0 && denom > 0, "Mainchain: BridgeManager's Threshold must be greater than 0"); + assertTrue(num <= denom, "Mainchain: BridgeManager's Threshold numerator must be less than or equal to denominator"); switchBack(prevNetwork, prevForkId); } @@ -94,14 +76,20 @@ abstract contract PostCheck_BridgeManager_Quorum is BasePostCheck { switchBack(prevNetwork, prevForkId); } - function validate_NonZero_TotalWeight_BridgeManager() private onlyOnRoninNetworkOrLocal onPostCheck("validate_NonZero_TotalWeight_BridgeManager") { + function validate_GreaterOrEqualMinExpected_TotalWeight_BridgeManager() + private + onlyOnRoninNetworkOrLocal + onPostCheck("validate_GreaterOrEqualMinExpected_TotalWeight_BridgeManager") + { assertTrue(IBridgeManager(ronBM).getTotalWeight() > 0, "Ronin: BridgeManager's Total weight must be greater than 0"); TNetwork currNetwork = network(); (, TNetwork companionNetwork) = currNetwork.companionNetworkData(); (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); - assertTrue(IBridgeManager(ethBM).getTotalWeight() > 0, "Mainchain: BridgeManager's Total weight must be greater than 0"); + assertTrue( + IBridgeManager(ethBM).getTotalWeight() > expectedMinTotalWeight, "Mainchain: BridgeManager's Total weight must be greater than `expectedMinTotalWeight`" + ); switchBack(prevNetwork, prevForkId); }