-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
compiling tests via-ir
causes issues with warp & block.timestamp
stored in memory
#1373
Comments
I'm not surethis is a Foundry issue. When you call It might be that Solidity instead optimizes by putting For example this is what I imagine is happening (pseudo-code): // test code (without IR this passes)
vm.warp(100);
assertEq(block.timestamp, 100);
vm.warp(200);
assertEq(block.timestamp, 200);
// solidity IR optimization (crudely)
uint256 timestamp = block.timestamp; // for us, this is 0 here
vm.warp(100); // sets block.timestamp, so accessing it directly would result in 100
assertEq(timestamp, 100); // but its already on the stack, and solc reuses that, so this assertion fails
vm.warp(200);
assertEq(timestamp, 200); The only real fix we could do is track the location of stack items that have been produced by uint256 timestampBefore = block.timestamp;
vm.warp(block.timestamp + x);
assertEq(timestamp - x, timestampBefore); I'm not really sure what we should do here. Pinging @hrkrshnn and @gakonst |
ye so likely we will need to provide a helper function here that blackboxes it from the compiler. @hrkrshnn what do you think about going more general and native with a blackbox inside solidity? |
Hmm, looking at this more: the Edit: Also, another fix would be to bump priority on per-contract optimizations, e.g. disabling the optimizer for test contracts always but enabling it for the user contracts, if possible. I don't think there's a specific reason you'd want to run tests with the optimizer on for test contracts other than measuring gas of your actual contracts Edit: Ok last edit I swear, I just remembered that the optimizer might be relevant for test contracts if you hit the "stack too deep" or w/e error 😞 |
@onbjerg Might be possible to config the compiler so that optimization is mostly off but "stack too deep" doesn't happen. |
Interesting as a workaround, but I'm not sure we can do that in Forge since it would require some knowledge of the expected test outcome, and for us to bruteforce the optimizer settings until it might work? 🤔 |
Good point. I think it's easiest to fix this: made ethereum/solidity#12963 to track. @onbjerg |
Thanks for flagging @wminshew, closing given:
|
@onbjerg Just checked this. This doesn't seem to be true: interface VM {
function warp(uint) external;
}
contract C {
VM vm = VM(address(0));
function f() external {
// test code (without IR this passes)
vm.warp(100);
assert(block.timestamp == 100);
vm.warp(200);
assert(block.timestamp == 200);
}
} Looked at both the |
Right, I think I had it confused; what is actually happening is that you replace all occurrences of a variable that just contains interface VM {
function warp(uint) external;
}
contract C {
VM vm = VM(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D));
function testF() external {
// test code (without IR this passes)
uint256 start = block.timestamp;
vm.warp(100);
assert(start == 0);
}
} Not sure if we should re-open @gakonst, I don't know if there is anything we can do on our side. Any way to get around this @hrkrshnn? |
added a minimal repro in ethereum/solidity#12963 (comment) |
* fix URLs * Add PaymentManager * format * Added deployment scripts for first 3 contracts * Having fun with the OneScriptToRuleThemAll, lol * Exact replica of setup of E2eTest.sol * updated dev.env * Changed the mock authorizer to list authorizer * still debugging * tried a few more things with the script * Fix * Cleanup * enable via-ir * Created the required deployment script for the inverter project * better naming convention * Added a short doc on how to run the script for the FE team * setting via-ir to true and compiler version as latest * Initial commit for setup script * A new test proposal is created * introduced more scoping into the script * Proposal created, milestone manager initiated, problem with adding milestone * reading receipts throws an upper bound memory error. Gonna try something different * I FUCKING DID ITgit add . Although I think Mr Escobar will prolly kill me now, anyway i would have died for a good cause * FUCK YES. It's done. But a lot of decisions need to made tbh. Btw this setup script is a mirror of the e2e test:MilestoneLifecycle.t.sol * restoring the original number of fuzz_runs. had reduced it back when i had not separated the logic for milestone manager in 2 separate contract * ran forge fmt * Deployment Scripts work now * Setup scripts works now with the latest changes from main * reverted any changes to module.sol * ran forge fmt on the repo * fix URLs * Add PaymentManager * format rebased with merge * Added deployment scripts for first 3 contracts * Having fun with the OneScriptToRuleThemAll, lol * Exact replica of setup of E2eTest.sol * updated dev.env * Changed the mock authorizer to list authorizer * still debugging * tried a few more things with the script * Fix * Cleanup Resolved lcov.info conflicts * enable via-ir * Created the required deployment script for the inverter project * better naming convention * Added a short doc on how to run the script for the FE team * setting via-ir to true and compiler version as latest * Initial commit for setup script * A new test proposal is created * introduced more scoping into the script * Proposal created, milestone manager initiated, problem with adding milestone * reading receipts throws an upper bound memory error. Gonna try something different removed random debugger statements (events) * Resolved conflicts in MilestoneManager and Module.sol * FUCK YES. It's done. But a lot of decisions need to made tbh. Btw this setup script is a mirror of the e2e test:MilestoneLifecycle.t.sol * restoring the original number of fuzz_runs. had reduced it back when i had not separated the logic for milestone manager in 2 separate contract * resolved formatting conflict in Module.sol * Deployment Scripts work now * Setup scripts works now with the latest changes from main * reverted any changes to module.sol * ran forge fmt on the repo * Updating the Module onlyAuthorizedOrManger modifier * updated naming convention in MetadataManager from owner to manager * reverted formatting changes in milestonemanager.sol * reverting changes in lcov.info * removed relative paths from scripts * Set via_ir to false foundry-rs/foundry#1373 * pre-commit * fix URLs * Add PaymentManager * Added deployment scripts for first 3 contracts * Having fun with the OneScriptToRuleThemAll, lol * Exact replica of setup of E2eTest.sol * updated dev.env * Changed the mock authorizer to list authorizer * still debugging * tried a few more things with the script * Fix * Cleanup * enable via-ir * Created the required deployment script for the inverter project * better naming convention * Added a short doc on how to run the script for the FE team * setting via-ir to true and compiler version as latest * Initial commit for setup script * A new test proposal is created * introduced more scoping into the script * Proposal created, milestone manager initiated, problem with adding milestone * reading receipts throws an upper bound memory error. Gonna try something different * I FUCKING DID ITgit add . Although I think Mr Escobar will prolly kill me now, anyway i would have died for a good cause * FUCK YES. It's done. But a lot of decisions need to made tbh. Btw this setup script is a mirror of the e2e test:MilestoneLifecycle.t.sol * restoring the original number of fuzz_runs. had reduced it back when i had not separated the logic for milestone manager in 2 separate contract * ran forge fmt * Deployment Scripts work now * Setup scripts works now with the latest changes from main * reverted any changes to module.sol * ran forge fmt on the repo * format rebased with merge * Added deployment scripts for first 3 contracts * Having fun with the OneScriptToRuleThemAll, lol * Exact replica of setup of E2eTest.sol * Changed the mock authorizer to list authorizer * still debugging * tried a few more things with the script * Fix * Cleanup Resolved lcov.info conflicts * enable via-ir * Created the required deployment script for the inverter project * better naming convention * introduced more scoping into the script * Proposal created, milestone manager initiated, problem with adding milestone * reading receipts throws an upper bound memory error. Gonna try something different removed random debugger statements (events) * Resolved conflicts in MilestoneManager and Module.sol * FUCK YES. It's done. But a lot of decisions need to made tbh. Btw this setup script is a mirror of the e2e test:MilestoneLifecycle.t.sol * restoring the original number of fuzz_runs. had reduced it back when i had not separated the logic for milestone manager in 2 separate contract * resolved formatting conflict in Module.sol * Deployment Scripts work now * Setup scripts works now with the latest changes from main * reverted any changes to module.sol * ran forge fmt on the repo * Updating the Module onlyAuthorizedOrManger modifier * updated naming convention in MetadataManager from owner to manager * reverted formatting changes in milestonemanager.sol * reverting changes in lcov.info * Set via_ir to false foundry-rs/foundry#1373 * removed relative paths from scripts * pre-commit * Fix rebase * Fix Rebase II * Remove Split Too bad Rahul * Go down to 5000 optimizer runs * Update Make pre-commit * added files to gitignore * removed unnecessary comments from the setup scripts * Revert "pulled latest changes" This reverts commit 1ba9ab0, reversing changes made to a565cd5. * Delete MilestoneManagerViewContract.sol * Revert "Revert "pulled latest changes"" This reverts commit 013269e. * Hopefully final rebase fix * Update Makefile * Fix DeployScripts --------- Co-authored-by: 0xNuggan <[email protected]> Co-authored-by: FHieser <[email protected]>
Hi guys, I'm facing the same on 0.8.27 |
I'm facing this in 0.8.28. function testGetPastAccumulator_ExactMatch_OldestAccumulator(
uint32 aStartTime,
uint256 aBlockTime,
uint256 aObservationsToWrite
) external randomizeStartTime(aStartTime) {
// assume
uint256 lBlockTime = bound(aBlockTime, 1, 30);
uint16 lObservationsToWrite = uint16(bound(aObservationsToWrite, 3, Buffer.SIZE * 3)); // go around it 3 times maximum
// arrange
uint256 lStartTime = block.timestamp;
console2.log("after assignment", lStartTime);
_fillBuffer(lBlockTime, lObservationsToWrite);
(,,, uint16 lIndex) = _pair.getReserves();
// act
vm.startPrank(address(_queryProcessor));
console2.log("start", lStartTime);
console2.log(_pair.observation(lIndex.next()).timestamp);
uint256 lAgo = lObservationsToWrite > Buffer.SIZE
? block.timestamp - _pair.observation(lIndex.next()).timestamp
: block.timestamp - (lStartTime + lBlockTime);
int256 lAcc = _queryProcessor.getPastAccumulator(_pair, PriceType.RAW_PRICE, lIndex, lAgo);
// assert
Observation memory lObs = _pair.observation(lObservationsToWrite > Buffer.SIZE ? lIndex.next() : 0);
assertEq(lAcc, lObs.logAccRawPrice);
vm.stopPrank();
} In this case The workaround I managed to find was to do: uint256 lStartTime = (block.timestamp << 1) >> 1;
console2.log(lStartTime);
// use lStartTime later
... = lStartTime + ... Since timestamp is way smaller than 256 bits i guess this is safe enough in my case. |
I am also getting the similar issue in 0.8.28 where I store block.timestamp as uint256 in memory in test, and then it gets arbitrarily modified without modification. The workaround suggested by @xenide worked for me uint256 startTimestamp = (block.timestamp << 1) >> 1; |
These cheatcodes could potentially be useful: /// Gets the current `block.number`.
/// You should use this instead of `block.number` if you use `vm.roll`, as `block.number` is assumed to be constant across a transaction,
/// and as a result will get optimized out by the compiler.
/// See https://github.com/foundry-rs/foundry/issues/6180
function getBlockNumber() external view returns (uint256 height);
/// Gets the current `block.timestamp`.
/// You should use this instead of `block.timestamp` if you use `vm.warp`, as `block.timestamp` is assumed to be constant across a transaction,
/// and as a result will get optimized out by the compiler.
/// See https://github.com/foundry-rs/foundry/issues/6180
function getBlockTimestamp() external view returns (uint256 timestamp); https://github.com/foundry-rs/forge-std/blob/master/src/Vm.sol#L577-L587 |
Component
Forge
Have you ensured that all of these are up to date?
What version of Foundry are you on?
forge 0.2.0 (276a122 2022-04-20T18:04:32.540251+00:00)
What command(s) is the bug in?
forge test
Operating System
macOS (amd)
Describe the bug
Can explore with this repo on commit
b2dd8cfafa79d02394826e8f01138e849d78f784
forge install
make test
foundry.toml
to buildvia-ir = true
forge test -vvv --match-test createERC20
the error appears to be with
vestingStart
, which isblock.timestamp
saved to memory at the beginning & later tested against. It appears to jump forward with the warp. My guess (not confirmed browsing opcode output) is thatvia-ir
is trying to save me gas by skipping the m{store,load} for the timestamp (bc in regular contracts block timestamps don't change w/i execution of a fn) & just re-loadingblock.timestamp
freshly on later reference(to debug this I had to use
emit log_uint
-- couldn't seem to getconsole2
orconsole
to output to trace.. but that's a separate topic)fortunately this is easy enough to fix in my own case since
vestingStart
is only ever 0 how/where I was currently using it (andvia-ir
doesn't appear to save any gas for these contracts)wanted to flag for others in the meantime
The text was updated successfully, but these errors were encountered: