test: pin atomicity by redeploying at the address, not by reading code length - #266
Open
thedavidmeister wants to merge 2 commits into
Open
thedavidmeister wants to merge 2 commits into
thedavidmeister wants to merge 2 commits into
Conversation
…e length Three tests read `predicted.code.length == 0` after a `vm.expectRevert`ed call as evidence that "the address is left free rather than occupied by an uninitialized clone". The EVM discards the reverted frame including the `CREATE2` before that line runs, and `predicted` was codeless beforehand, so the assertion holds for every possible library and cannot fail if atomicity breaks. Each now deploys at that address instead. The namespaced failure test retries with data the implementation initializes successfully on, `data` being outside that derivation. The other two swap the implementation's code, since the clone address commits to the implementation's address and not to its code — the open-salt derivation holds `data` fixed, and `TestCloneableRevert` refuses every input. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…atomicity-redeploy
|
Warning Review limit reachedNext included review available in 28 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #166
Three tests ended with
assertEq(predicted.code.length, 0)under NatSpec thatread it as proof of atomicity. It proves nothing. The call above it is wrapped
in
vm.expectRevert, so the EVM has already discarded the reverted frameincluding the
CREATE2, andpredictedheld no code before the call — theassertion is true for every possible library, including one that deployed an
uninitialized clone or deployed at a different salt entirely. A library that
does not revert fails the
expectRevertfirst. The assertion could not changevalue if atomicity broke.
Each site now asserts the claim its NatSpec makes — after the failure the
effective salt is still free and the intended clone still deploys at the address
that was predicted:
testCloneDeterministicInitializeFailureFails—datais outside thenamespaced derivation, so the retry simply passes
abi.encode(ICLONEABLE_V2_SUCCESS).testCloneDeterministicInitializeRevertBubblesandtestCloneDeterministicOpenSaltInitializeFailureFails—datais fixed (orinside the derivation), so instead the implementation's code is swapped with
vm.etch. The clone address commits to the implementation's ADDRESS and notto its code, which is exactly why the prediction survives the swap; the retry
then lands at the same
predictedand the clone carries the rightsData.No test gained or lost: three were rewritten.
QA
(
test/src/lib/LibICloneableFactoryV4.cloneDeterministic.t.sol,test/src/lib/LibICloneableFactoryV4.cloneDeterministicOpenSalt.t.sol).cloneDeterministicdeploys at the open-salt address andcloneDeterministicOpenSaltat the namespaced one. This is the atomicity-ishbreakage the old assertions claimed to catch: the salt the test predicted is
NOT the salt the library consumes. On main all three OLD tests PASS under it
at 2048 runs each — the assertions cannot fail. On this branch all three NEW
ones FAIL. Unmutated, the whole suite is 78 passed / 0 failed.
discarded, so
predicted.code.lengthis 0 after any reverting library and theold assertion carries no information; the redeploy is the only observation
that distinguishes "the salt is still free" from "it is not".
vm.expectRevertthat the revert itself makes true".grep -rn "code.length" test/over the whole tree leaves two other sites, neither in that category:
…OpenSaltDisjointTagsCloseTheSquatassertsopen.code.length == 0after aSUCCESSFUL attacker deploy — it could fail if the two domain tags collided,
and the next lines deploy there for real — and
…CodeGuardRunsBeforeCreate2reads the implementation's own codeafter
vm.etch. The remaining hits arevm.assumeguards andassertTrue(child.code.length > 0)on successful deploys.Touches
test/src/lib/LibICloneableFactoryV4.cloneDeterministic.t.sol(also inthe PR for #122, about 25 lines apart) and
test/src/lib/LibICloneableFactoryV4.cloneDeterministicOpenSalt.t.sol(also inthe PR for #123, which changes the imports and the tail of the file). All branch
off
main.🤖 Generated with Claude Code