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

all: re-implement state overriding #29950

Closed
wants to merge 1 commit into from

Conversation

rjl493456442
Copy link
Member

No description provided.

@rjl493456442 rjl493456442 force-pushed the override-reader branch 3 times, most recently from 379059d to ed88ef0 Compare June 12, 2024 03:08
@rjl493456442 rjl493456442 changed the title WIP Override reader all: re-implement state overriding Jun 12, 2024
}

// OverrideState applies the state overrides into the provided live state database.
func OverrideState(state *StateDB, overrides map[common.Address]OverrideAccount) (*StateDB, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we keep the StateOverride type alias? more cumbersome to type map[common.Address]OverrideAccount 😄

@holiman
Copy link
Contributor

holiman commented Aug 26, 2024

Ah, so this one is really a follow-up on #29761 ?

@s1na
Copy link
Contributor

s1na commented Aug 26, 2024

We had a triage discussion about this. As it stands the PR doesn't let us compute the state root off of the overridden state. This feature is desired for #27720.

@rjl493456442
Copy link
Member Author

But I think this branch is still better than the existing solution for state override. e.g. we can use this feature to override the states for "read-only" purposes.

For multicall, it's a bit different. It feels like a call simulation which may have the state mutations (but they are never persisted). In this case, we can use the existing solution to apply the overrides as mutation.

@holiman
Copy link
Contributor

holiman commented Sep 13, 2024

Needs a rebase

@holiman
Copy link
Contributor

holiman commented Oct 11, 2024

I think the approach here is good.

the PR doesn't let us compute the state root off of the overridden state.

I think that's a pretty hard criteria to judge by. That sounds like a pretty difficult thing to achieve, IMO.

@rjl493456442
Copy link
Member Author

I can rebase it

@@ -356,6 +356,14 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, bc.statedb)
}

// StateWithOverrides returns a new mutable state with provided state overrides.
func (bc *BlockChain) StateWithOverrides(root common.Hash, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why pass *map instead of just a map?

@holiman
Copy link
Contributor

holiman commented Nov 9, 2024

Getting back to this. So, in my branch layerd_override, I started banging out how a layered-statedb implementation of this would look. I mean, if, instead of adding the overrides in the Reader-layer below the StateDB, we instead layer it like the hookedStateDB, on top of the StateDB.

First of all, that approach requires the defnition of a statedb interface, basically a copy of vm.StateDB inside the state package, so that the layers can be OverrideStateDB <> HookedState <> StateDB OR OverrideStateDB <> StateDB both. Unfortunately, that makes it so none of the hooks for modifications in the overridden layers are invoked. In order to make it possible to do HookedState <> OverrideStateDB <>StateDB, we need to add some extra paths to work around the spot where hooked reaches into statedb s.inner.journal.dirties to handle the selfdestruct burns. It can be worked out, somehow.

A larger problem is that all journalling is implemented in side *StateDB struct. And the journal operates on a *StateDB. Separating the journal from the StateDB, again, operating on some form of interface, but without involving recursion...
Because, consider balance changes - a revert happens directly on the state object:

func (ch nonceChange) revert(s *StateDB) {
	s.getStateObject(ch.account).setNonce(ch.prev)
}

Because the public SetNonce method journal the reversion, if it was used:

func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
	stateObject := s.getOrNewStateObject(addr)
	if stateObject != nil {
		stateObject.SetNonce(nonce)
	}
}
// --> invokes
func (s *stateObject) SetNonce(nonce uint64) {
	s.db.journal.nonceChange(s.address, s.data.Nonce)
	s.setNonce(nonce)
}

All in all, my idea about putting overrides as a layer-on-top doesn't seem to be very good. Please rebase and I'm all for getting this PR merged.

@holiman
Copy link
Contributor

holiman commented Nov 9, 2024

It was a bit tricky to rebase this. I made an attempt, here: https://github.com/ethereum/go-ethereum/compare/master...holiman:go-ethereum:override-reader-3?expand=1 , but I didn't want to force-push on your branch, because it's pretty raw. It compiles, but I haven't verified any functionality. YMMV, up to you if you want to use it or not :)

@rjl493456442
Copy link
Member Author

rjl493456442 commented Nov 10, 2024

@holiman

I am reconsidering our direction here.

A while ago, we implemented eth_simulateV1 with support for state overrides. However, this implementation differs slightly from the one in eth_call or eth_estimateGas. In eth_simulateV1, it’s possible to override states and generate a block hash that includes all overridden states as if they were standard state mutations. This means the overridden states are hashed as well.

During initial discussions around eth_simulateV1, we agreed that the block hash should indeed be an output of this API, as it would be useful for result comparison across different clients. In this context, a read-only state override is insufficient, as producing a hash requires the ability to include state mutations.

The limitation of the new implementation is that it only supports read-only state overrides. Given this, I would lean toward using our existing approach: applying mutations on top of overrides, as it is compatible with both use cases.

@holiman
Copy link
Contributor

holiman commented Nov 11, 2024

The limitation of the new implementation is that it only supports read-only state overrides. Given this, I would lean toward using our existing approach: applying mutations on top of overrides, as it is compatible with both use cases.

Yep, makes sense to me. Let's close this then

@holiman holiman closed this Nov 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants