Skip to content
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

EVM/Common: SimpleStateManager #3482

Merged
merged 24 commits into from
Jul 12, 2024
Merged

Conversation

holgerd77
Copy link
Member

@holgerd77 holgerd77 commented Jul 4, 2024

This is a proof of concept if it is possible This PR implements a SimpleStateManager with the goal to replace the default state manager implementation and depdendency from the EVM with a very simple < 5KB state manager located in Common without any further depdencies.

This would allow to ship with a significantly trimmed down base EVM, both with a substantially reduced dependency set and bundle size, going from 1.23 MB to 850 KB.

Before:

evm-bundle_before_SM_removal

After (also note the "number of boxes": statemanager out, trie out, js-sdsl out, readable-stream out, Buffer out, lru-cache out). So if you click to enlarge, this is so much more a representation of what the EVM is about than before:

evm-bundle_after_StateManager_removal

This would ease browser usage for simple EVM use cases and integrations.

Some more comments to make here, short on time right now.

I have integrated this into the general SM test suite, all checkpoint tests are passing.

For the EVM this also already gives pretty solid results, with adhoc (without any modification) only 4 / 1107 tests failing.

grafik

@holgerd77
Copy link
Member Author

holgerd77 commented Jul 5, 2024

Ok, ready! 🎉

All EVM tests passing, the one thing I needed to do for this is to also move the OriginalStorageCache short implementation to Common and integrate, since otherwise the pre-state setting use/test cases were not working.

So: I think for the EVM this is a big deal! So this lifts a weight of close to 400KB to the EVM in addition to the other reductions, and so brings this package in the realm of becoming a "normal" browser library which people just side-integrate among other dependencies. The overall code adition "on the other side" is still < 8 KB. Beyond the code reduction this frees EVM users from all browser-related hazzles coming from the statemanager side of the dependency tree (if they can live with the limitations of this state manager).

Want to nevertheless make some more cases here respectively give some additional context/explanation about design choices, so to make things more transparent and "followable".

Why Common ?

So, this is admittedly relatively low in the stack. This was mainly guided by the following design "needs":

  1. The implementation should implement the EVMStateManager interface being located in Common (also for the same "independent from the statemanager package reason)
  2. Being "lower" than statemanager made it possible to relatively easily and without broader moving things around re-use the tests from statemanager and run them directly

We could also think about moving this to the EVM package directly instead. This would make 2. more complicated. I also find the idea relatively charming to just have a simple state manager available "everywhere" which can be used to manage Ethereum state on a basic level for whatever use cases, so for me it feels a bit as a general Utility which fits well into the chosen setting. Comparing the total Common bundle size of 178 KB this feels worth and in-relation to me to add. I would therefore pledge for the solution.

I think this new state manager will be generally useful, also for our own use cases, mainly e.g. regarding testing, stumbled upon this thought yesterday when seeing you guys battling with the DefaultStateManager bug. So with this simpler state manager makes it really handy to dig into state changes during EVM execution, since one can plainly see the state evolving in the simple map structures. Used this yesterday during a debugging session within VSCode, and this feels really handy to have cache/trie and so on out of the mix.

Anyhow: ready for review! 🙂

Note that this PR builds upon the BLS PR (for bundle comparison reasons). I think it's better to merge BLS first since these are conceptually unrelated.

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, LGTM in general (and indeed, also super simple yet elegant! 😄 )

For the state manager tests, some now run on the DefaultStateManager and the SimpleStateManager - but only the checkpoint tests. Are there other tests which we want to incorporate here?

Also, small side note, for code coverage the tests from StateManager -> Common will not get tracked, so our common coverage will likely go down, since likely (?) SimpleStateManager is not covered by the tests.

packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/common/src/state/simple.ts Outdated Show resolved Hide resolved
packages/statemanager/README.md Outdated Show resolved Hide resolved
packages/statemanager/src/cache/originalStorageCache.ts Outdated Show resolved Hide resolved
if ((await this.getAccount(address)) === undefined) {
await this.putAccount(address, new Account())
}
await this.modifyAccountFields(address, { codeHash: keccak256(value) })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not have a super strong opinion on this but it seems like we should follow the same pattern we follow through the rest of the libraries where we allow users to pass in their own crypto and not require use of this keccak256 function (like we do here) in the DefaultStateManager.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do/integrate. First thought in terms of "simplification", but this might not be one actually. 😆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest the same and then saw Andrew's comment. I agree that it would be preferable not to "hardcode" this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have added

Copy link
Contributor

@acolytec3 acolytec3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome implementation, love the simpleness of this :-). That said, I've read through the explanation of why this should be in Common and I'm not tracking. This is only used in evm and nowhere else so doesn't make sense to me include this in common. What is the driving goal for not putting this in statemanager? If it implements the EVMstateManagerInterface, I don't see any reason that tree shaking wouldn't take care of any excess bundle size concerns if a user imports it from @ethereumjs/statemanager.

Base automatically changed from evm-js-default-for-bls-precompiles to master July 5, 2024 13:46
for (const [address, account] of newTopA) {
const accountCopy =
account !== undefined
? Object.assign(Object.create(Object.getPrototypeOf(account)), account)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that this would be more efficient:

Object.create(Object.getPrototypeOf(account), Object.getOwnPropertyDescriptors(account));

Since it avoids the intermediate step of creating an empty object and then copying properties over to it, and it ensures all properties, including non-enumerable ones, are copied.

}

async getContractStorage(address: Address, key: Uint8Array): Promise<Uint8Array> {
return this.topS().get(`${address.toString()}_${bytesToHex(key)}`) ?? new Uint8Array(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could create a helper for storageKeyFormatting that parsed an address + key into the expected string? This could be helpful notably if we need to switch around the format (pre and post verkle comes to mind), then we don't have to modify it in multiple places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will keep for now, we can still easily do later on

Copy link
Contributor

@gabrocheleau gabrocheleau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that's some great work! Really like how this removes the statemanager package dependency from the evm. Left some minor comments.

@jochem-brouwer
Copy link
Member

The git diff is looking messed up 🤔 It tries to re-import the changes to BLS?

@holgerd77
Copy link
Member Author

The git diff is looking messed up 🤔 It tries to re-import the changes to BLS?

Yeah, I've cherry picked the commits out locally, will force-push

@holgerd77 holgerd77 force-pushed the add-simple-state-manager branch from d701dfa to 2252759 Compare July 8, 2024 11:46
*
* The `@ethereumjs/statemanager` package provides a variety of state manager
* implementations for different needs (tree backed, RPC, experimental verkle)
* which can be used by this option as a replacement.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please update the docs to now reflect the new SSM location?

Copy link
Member Author

@holgerd77 holgerd77 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks otherwise good, feel free to merge (also ok to spare out this one doc change from my side, just saw it's really this one very mention, can also update later, not sure if worth to trigger whole CI)

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 small-ish comments

packages/statemanager/README.md Outdated Show resolved Hide resolved
account.nonce = accountFields.nonce ?? account.nonce
account.balance = accountFields.balance ?? account.balance
account.storageRoot = accountFields.storageRoot ?? account.storageRoot
account.codeHash = accountFields.codeHash ?? account.codeHash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked, even if account is returned undefined, and we provide the accountFields which is Partial<Account> this still works fine (we default the account to default values)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a question here? I'm not sure what you're getting at?

packages/statemanager/src/stateManager.ts Outdated Show resolved Hide resolved
Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jochem-brouwer jochem-brouwer merged commit ef20930 into master Jul 12, 2024
34 checks passed
@jochem-brouwer jochem-brouwer deleted the add-simple-state-manager branch July 12, 2024 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants