-
Notifications
You must be signed in to change notification settings - Fork 71
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
add support for prank(sender, origin) and startPrank(sender, origin) cheatcodes #336
Conversation
tests/regression/test/Prank.t.sol
Outdated
function check_prank_ConstructorCreate2(address user, bytes32 salt) public { | ||
vm.prank(user); | ||
ConstructorRecorder recorder = new ConstructorRecorder{salt:salt}(); | ||
assert(recorder.caller() == user); | ||
} |
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.
look at us, we do support CREATE2 now 🙌
not sure what's going on with CI, some jobs flagging with:
|
fixed itself with enough re-running 🤷♂️ |
(it removes the extra dependency on forge-std)
The foundry step is failing with weird errors: Submodule 'tests/lib/forge-std' (https://github.com/foundry-rs/forge-std) registered for path '../lib/forge-std' Submodule 'tests/lib/halmos-cheatcodes' (https://github.com/a16z/halmos-cheatcodes) registered for path '../lib/halmos-cheatcodes' Submodule 'tests/lib/openzeppelin-contracts' (https://github.com/OpenZeppelin/openzeppelin-contracts) registered for path '../lib/openzeppelin-contracts' Submodule 'tests/lib/solady' (https://github.com/Vectorized/solady) registered for path '../lib/solady' Submodule 'tests/lib/solmate' (https://github.com/transmissions11/solmate) registered for path '../lib/solmate' fatal: not a git repository: /home/runner/work/halmos/halmos/tests/lib/forge-std/../../../.git/modules/tests/lib/forge-std Failed to clone 'tests/lib/forge-std'. Retry scheduled fatal: destination path '/home/runner/work/halmos/halmos/tests/lib/halmos-cheatcodes' already exists and is not an empty directory. fatal: clone of 'https://github.com/a16z/halmos-cheatcodes' into submodule path '/home/runner/work/halmos/halmos/tests/lib/halmos-cheatcodes' failed Failed to clone 'tests/lib/halmos-cheatcodes'. Retry scheduled
Suspecting race conditions here: ``` Build failed: ['forge', 'build', '--ast', '--root', 'tests/regression', '--extra-output', 'storageLayout', 'metadata'] ----------------------------- Captured stderr call ----------------------------- Failed to install solc 0.8.26: Text file busy (os error 26) Error: Text file busy (os error 26) ```
This reverts commit 8bb0d6f.
@@ -53,4 +53,4 @@ jobs: | |||
run: python -m pip install -e . | |||
|
|||
- name: Run pytest | |||
run: pytest -n 4 -v -k "not long and not ffi" --ignore=tests/lib --halmos-options="-st ${{ matrix.parallel }} --storage-layout ${{ matrix.storage-layout }} --solver-timeout-assertion 0 ${{ inputs.halmos-options }}" ${{ inputs.pytest-options }} | |||
run: pytest -n 1 -v -k "not long and not ffi" --ignore=tests/lib --halmos-options="--debug -st ${{ matrix.parallel }} --storage-layout ${{ matrix.storage-layout }} --solver-timeout-assertion 0 ${{ inputs.halmos-options }}" ${{ inputs.pytest-options }} |
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.
pytest -n 1
is what fixed CI, it seems that the issue was running multiple forge commands in parallel would result in race conditions trying to clone submodules or install solc
src/halmos/sevm.py
Outdated
@@ -1610,6 +1619,7 @@ def callback(new_ex: Exec, stack, step_id): | |||
new_ex.jumpis = deepcopy(ex.jumpis) | |||
new_ex.symbolic = ex.symbolic | |||
new_ex.prank = deepcopy(ex.prank) | |||
new_ex.origin = ex.origin |
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.
nice to restore it here, but don't forget it in other places that restore vm states:
- in the callback for create():
Lines 1938 to 1944 in 09b6438
# restore vm state new_ex.pgm = ex.pgm new_ex.pc = ex.pc new_ex.st = deepcopy(ex.st) new_ex.jumpis = deepcopy(ex.jumpis) new_ex.symbolic = ex.symbolic new_ex.prank = deepcopy(ex.prank) - at the end of deploy_test():
Lines 461 to 466 in 09b6438
# reset vm state ex.pc = 0 ex.st = State() ex.context.output = CallOutput() ex.jumpis = {} ex.prank = Prank()
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.
probably we need more tests to cover these cases:
- checking tx.origin restored after contract creation
- checking tx.origin restored after test contract constructor when there is startPrank() but no closing stopPrank() in the constructor
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.
tests added in 1ea690c
tests/regression/test/Prank.t.sol
Outdated
assert(target.origin() == origin); | ||
|
||
// but the inner call is not pranked | ||
checkNotPranked(inner, address(target)); |
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 also fails in foundry 🤨 is there an issue or is my mental model just wrong?
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.
update: my mental model was wrong, and so the implementation was wrong too. I checked that all the tests in PrankTest now behave the same in both foundry and halmos (df2c7a3)
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 love the new method of maintaining the caller and origin using the call context and having them seamlessly interact with the prank context. Also appreciate the comprehensive tests! Thanks for the effort!
src/halmos/cheatcodes.py
Outdated
return True | ||
def startPrank(self, sender: Address, origin: Address | None = None) -> bool: | ||
result = self.prank(sender, origin) | ||
self.keep = result if result else self.keep |
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 accurate but a bit convoluted. For better code readability, I'd suggest having prank()
take an additional argument (defaulting to False
if not provided) that is assigned to self.keep
. Then, have startPrank()
call prank(..., keep=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.
yeah you're right, in 17eba86
No description provided.