-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Smr 2459 invariant testing #89
Conversation
📃CI ReportCompiling 156 files with 0.8.19 �[1;33mWarning (9302)�[0m�[1;37m: Return value of low-level calls not used.�[0m Analysing contracts...
For a full HTML report run: |
test/invariant/InvariantBridge.t.sol
Outdated
|
||
vm.selectFork(rootId); | ||
uint256 balanceL1 = ChildERC20(rootToken).balanceOf(user); | ||
address childToken = rootBridge.rootTokenToChildToken(rootToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can move outside of internal for-loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
test/invariant/InvariantBridge.t.sol
Outdated
vm.prank(address(0x234)); | ||
ChildERC20 rootToken = new ChildERC20(); | ||
vm.prank(address(0x234)); | ||
rootToken.initialize(address(123), "Test", "TST", 18); | ||
// Mint token to user | ||
for (uint256 j = 0; j < NO_OF_USERS; j++) { | ||
vm.prank(address(0x234)); | ||
rootToken.mint(users[j], MAX_AMOUNT); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: suggest startPrank(...)
and stopPrank()
to avoid doing a separate prank for each call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, updated
test/invariant/InvariantBridge.t.sol
Outdated
assertEq(bridgeBalance, totalSupply); | ||
assertEq(bridgeBalance, userBalanceSum); | ||
} | ||
vm.selectFork(resetId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these changes to the resetId
fork necessary at the end of each invariant test? I removed all of them and run the tests a few times, and they pass.
|
||
vm.selectFork(rootId); | ||
rootHelper.finaliseWithdrawal(user, previousLen); | ||
vm.selectFork(childId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this required?
vm.prank(recipient); | ||
ChildERC20(rootToken).transfer(user, amount); | ||
} | ||
vm.selectFork(childId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given the fork switch right below, is this fork change required?
// Fund difference | ||
vm.selectFork(rootId); | ||
rootHelper.deposit(user, rootToken, amount - currentBalance, gasAmt); | ||
vm.selectFork(childId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't look like this is required?
Smr 2459 invariant testing
🚨 Report Summary
For more details view the full report in OpenZeppelin Code Inspector |
test/invariant/InvariantBridge.t.sol
Outdated
rootBridge = new RootERC20BridgeFlowRate(address(this)); | ||
ChildERC20 rootIMXToken = new ChildERC20(); | ||
rootIMXToken.initialize(address(123), "Immutable X", "IMX", 18); | ||
WIMX wETH = new WIMX(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is slightly confusing. Should this be an instance of WETH contract instead of WIMX, even though they are functionally similar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, yes, it is supposed to be WETH - though WIMX is adapted from WETH contract with very minimum change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
} | ||
// Create tokens. | ||
for (uint256 i = 0; i < NO_OF_TOKENS; i++) { | ||
vm.startPrank(address(0x234)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is pranking this address required here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, my thought is to deploy and mint tokens from a different address other than the testing contract itself so that it is not possible to further mint the token after the setup process in case we add some more logic in the future that may break the setup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add a one line comment on this pls? 🙏🏿
test/invariant/InvariantBridge.t.sol
Outdated
// Deploy contracts on reset chain. | ||
vm.selectFork(resetId); | ||
vm.startPrank(ADMIN); | ||
ChildERC20 resetTokenTemplate = new ChildERC20(); | ||
resetTokenTemplate.initialize(address(123), "Test", "TST", 18); | ||
new MockAdaptor(); | ||
vm.stopPrank(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove the deployment of contracts on the reset chain. Tests should still pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, just have this removed and tests passed. I think this was to prevent an issue previously but maybe no longer required.
test/invariant/InvariantBridge.t.sol
Outdated
vm.selectFork(resetId); | ||
vm.startPrank(ADMIN); | ||
new ChildHelper(payable(childBridge)); | ||
new RootHelper(ADMIN, payable(rootBridge)); | ||
new ChildERC20BridgeHandler(childId, rootId, users, rootTokens, address(childHelper), address(rootHelper)); | ||
new RootERC20BridgeFlowRateHandler( | ||
childId, rootId, users, rootTokens, address(childHelper), address(rootHelper) | ||
); | ||
vm.stopPrank(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove the deployment and config of contracts on the reset chain. Tests should still pass.
test/invariant/InvariantBridge.t.sol
Outdated
|
||
assertEq(balanceL1 + balanceL2, MAX_AMOUNT); | ||
} | ||
vm.selectFork(resetId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we can remove this
/// forge-config: default.invariant.runs = 256 | ||
/// forge-config: default.invariant.depth = 15 | ||
/// forge-config: default.invariant.fail-on-revert = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these local configuration values for the invariant run actually used or is there a global config that overrides them? No matter what value I set, the tests alway run 256 times, with a depth of 15
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, they are used, a different value actually gives a different run on my machine.
scripts/localdev/chains.sh
Outdated
npx hardhat node --config ./rootchain.config.ts --port 8500 > /dev/null 2>&1 & | ||
npx hardhat node --config ./childchain.config.ts --port 8501 > /dev/null 2>&1 & | ||
npx hardhat node --config ./resetchain.config.ts --port 8502 > /dev/null 2>&1 & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can avoid having three chains running, and just use a single chain with different forks from the the same chain?
test/invariant/InvariantBridge.t.sol
Outdated
string public constant CHILD_CHAIN_URL = "http://127.0.0.1:8500"; | ||
string public constant ROOT_CHAIN_URL = "http://127.0.0.1:8501"; | ||
// Forge has an issue that fails to reset state at the end of each run. | ||
// For example, we found out that if the context stays at child chain at the end of setUp(), | ||
// the state on child chain will not be reset or if the context stays at root chain, the state | ||
// on the root chain will not be reset, which causes subsequent runs to fail. | ||
// We introduced a third chain called reset chain and we make the context to stay on the reset chain | ||
// in order to reset state on both child chain and root chain. | ||
string public constant RESET_CHAIN_URL = "http://127.0.0.1:8502"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can avoid having three chains running, and just use a single chain with different forks from the the same chain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good idea
accounts: [], | ||
}, | ||
localhost: { | ||
url: "http://127.0.0.1:8502/", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this required?
This PR adds invariant tests to the bridge and also enables invariant testing in CI.
Tests are created under test/invariant.
There are 9 invariant tests:
For each invariant test, forge is configured to generate 15 random calls (in 1 run) distributed uniformly across the following 16 functions and attest invariance after every call:
Forge is configured to run 256 runs for each test.