-
Notifications
You must be signed in to change notification settings - Fork 417
feat(testing/fill): Account-hash-based deploy addresses #2139
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
base: forks/amsterdam
Are you sure you want to change the base?
feat(testing/fill): Account-hash-based deploy addresses #2139
Conversation
This is not strictly necessary and is currently skipped in #1964 see https://github.com/danceratopz/execution-specs/blob/6fe382c0370ce94a935f65c89eac1073bc6324b6/packages/testing/src/execution_testing/cli/pytest_commands/plugins/consume/simulators/simulator_logic/test_via_engine.py#L43-L76. We can certainly benchmark to see how much time is saved by avoiding the initial FCU! But the initial plan was to skip it unless it was considered necessary for intermittent garbage collection (e.g. benchmarking efforts hinted that this would be necessary for Besu). A follow-up PR was to add Edit: We don't FCU post-genesis, but the effect is the same (as we don't FCU after the test either) - we're always building on top of genesis in engine x :) |
spencer-tb
left a comment
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.
Smart optimization. Assuming this will save memory during phase 1 and intial stage of phase 2 filling. The salt mechanism is cool.
| If the address is already present in the pre-alloc the amount will be | ||
| added to its existing balance. |
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.
Docstring update suggestion
| If the address is already present in the pre-alloc the amount will be | |
| added to its existing balance. | |
| Add a funded account to the pre-allocation. | |
| The address must not already exist in the pre-allocation. To set the | |
| balance of an account, use the `amount` parameter in `fund_eoa()` or | |
| the `balance` parameter in `deploy_contract()` at creation time. |
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.
Added, thanks!
| class FrozenStorage(Storage): | ||
| """Frozen storage.""" |
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.
Assuming will be used later.
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.
Removed as it ended up not being necessary 👍
b9697da to
2c7576c
Compare
|
@danceratopz @spencer-tb I added another very nice enhancement that virtually makes the |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## forks/amsterdam #2139 +/- ##
================================================
Coverage 86.07% 86.07%
================================================
Files 599 599
Lines 39472 39472
Branches 3780 3780
================================================
Hits 33977 33977
Misses 4862 4862
Partials 633 633
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| ) -> None: | ||
| """Set account associated with an address.""" | ||
| raise ValueError( | ||
| "Tests are not allowed to set pre-alloc items in execute mode" |
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.
To reviewers: This check has been moved to SharedAlloc in the form of flags (see alloc_flags fixture for the skipping logic).
| ) | ||
| sender = pre.fund_eoa() | ||
| tx_value = 1 | ||
| pre.fund_address(sender, tx_value) |
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.
Note to reviewers: execute command handles this automatically since #1822 and for fill I think we should simply increment the default value if for some reason we come up with a test that requires more balance.
| if self._deleted_addresses: | ||
| buffer += b"\1" | ||
| for deleted_address in sorted(self._deleted_addresses): | ||
| buffer += deleted_address |
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.
Note to reviewers: We could even completely ban this behavior and make this change a bit cleaner. In cases where we "remove" system contracts from the state we don't really remove them, we instead do pre[SYSTEM_CONTRACT_ADDRESS] = Account(<empty account>) to overwrite it.
fselmo
left a comment
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 a lot to review but I think it looks good. Really clever change that makes a lot of sense 🔥.
It's really difficult to know though, imo, if this will create any friction with execute remote. I think this is "less crucial" of a way to test (and more rnd) and we can certainly fix any quirks that bubble up after this PR is in... but I do wonder if there aren't some basic sanity check unit tests we can write for some of these methods that guarantee we are not introducing bugs to this functionality. I think this would be pretty difficult though... curious if others have any ideas here.
I asked Claude to look at this and I think there's a potential bug with setting self[address] instead of using __internal_set_item__(address, ...). I couldn't reproduce it in kurtosis bc I think it'd only be present in some very unique scenario... but asking here to make sure.
Everything else looks pretty good to me. This will be very nice to get in 👍🏼
| account = self[address] | ||
| if account is not None: | ||
| account.balance = ZeroPaddedHexNumber(new_balance) | ||
| self[address] = account.copy(balance=new_balance) |
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.
Clarifying q... doesn't this also need to use __internal_set_item__(address, ...) in order to actually run through the RPC funding logic?
It doesn't seem easy, but do you think it's possible to write basic, sanity checking unit tests to make sure these execute mode expectations don't break? It would make reviews easier to have confidence in imo as it's not so straightforward / quick to test execute remote things as a reviewer.
| account = self[address] | ||
| if account is not None: | ||
| account.balance = ZeroPaddedHexNumber(current_balance) | ||
| self[address] = account.copy(balance=current_balance) |
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.
Same thought here about using __internal_setitem__()
| """ | ||
| Deploy a contract to the allocation. | ||
|
|
||
| Warning: `address` parameter is a temporary solution to allow tests to |
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.
Stray thought... I actually only now realized this is supposed to be temporary. It looks like only one test uses this and it makes sense. Would this be a good time to change this to something like _address (private attr... do not use unless you know what you're doing kind of thing?). I feel like it may be easy for a test that uses this to sneak by if it's a public property on pre.deploy_contract().
|
|
||
| model_config = { | ||
| **CamelModel.model_config, | ||
| "frozen": 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.
👌🏼
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.
Nothing to add here. Just a thought that since this is frozen I think we could probably cache the hash in case we ever end up calling it more than once we don't end up hashing it multiple times. I'm pretty sure the happy path in this PR only calls it once though so consider this just a nit.
Co-authored-by: felipe <fselmo2@gmail.com>
🗒️ Description
Enhancement 1: Use account hashes to determine contract addresses
TL;DR
Reduce account duplication during pre-alloc grouping by making contract addresses a function of the account’s contents.
Details
Normally, account collisions are undesirable because once a test touches an account, its nonce increases, its storage may be modified, and its balance can change.
This is particularly relevant during test execution, where we deploy fresh contracts on a live network to start each test from the most deterministic state possible.
However, this constraint does not apply to tests that are filled and then consumed, since test execution always starts from a clean state.
In the case of Engine X tests, we always perform a forkchoice-update back to genesis. As a result, it does not matter if the same contract is used by multiple tests, because reverting to genesis rolls back all state changes. We can therefore safely reuse the same account across tests, provided it has the same initial state.
This PR modifies the behavior of
Preto calculate a contract’s address as a hash of the account’s contents, including its initial balance, nonce, storage, and code.We also add a salt to the calculated hash to support cases where a single test requires multiple identical copies of the same account in the state.
Examples
Same contract, two different tests:
Outcomes
contract_test_1 == contract_test_2Same contract, two different tests, multiple times:
Outcomes
contract_test_1_a == contract_test_2_acontract_test_1_b == contract_test_2_bcontract_test_1_a != contract_test_1_bcontract_test_2_a != contract_test_2_bSame contract, two different tests, different properties:
Outcomes
contract_test_1 != contract_test_2EOA, no arguments, two different tests:
Outcomes
sender_1 == sender_2EOA, same fund amount, two different tests:
Outcomes
sender_1 == sender_2EOA, different fund amount, two different tests:
Outcomes
sender_1 != sender_2Enhancement 2: Detect Grouping Separation Automatically
TL;DR
Automatically detect changes in the
prethat would result in conflicts with other tests and put the test into a different group without having to rely onpytest.mark.pre_alloc_group.Details
Keeps track of the addresses that were modified in the following ways:
pre[address] = Account(...)pre.fund_address(...)pre.deploy_contract(..., address=0x1234..., ...)del pre[address]All of these actions allow the test to directly modify an account at an specific address' contents, which would affect other tests.
We keep track of the addresses that were modified this way and hash the contents of each account to add it to the pre-alloc group hashing.
Reasoning behind
pre.fund_address(...)in particular is that most tests expect other addresses (such as system addresses or precompiles, or even created contracts) to start with zero balance. This is different from specifying a non-zero balance in thepre.deploy_contractbecause this modifies the resulting address due to the hashing, which makes the collision impossible.All usages of
pytest.mark.pre_alloc_grouphave been removed from tests and pre-alloc grouping test execution of all Amsterdam test did not result in any issues. The marker itself was not removed just to retain the ability to group tests manually.🔗 Related Issues or PRs
N/A.
✅ Checklist
toxchecks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:uvx tox -e statictype(scope):.mkdocs servelocally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.@ported_frommarker.Cute Animal Picture