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

core/state: remove notion of fake storage #24916

Merged
merged 6 commits into from
Jan 10, 2023

Conversation

holiman
Copy link
Contributor

@holiman holiman commented May 20, 2022

This PR fixes #24456 .

This PR removes the notion of fakeStorage from the state objects, and instead, for any state modifications that are needed, it simply makes the changes and commits it.

I'm not fully certain if there are any tangible negative side-effects of doing a Commit (such as, does it have potential to flush stuff to disk? I think not).

One other change is that this change makes it not possible to replace the entire storage in one go, rather we can only update slots explicitly. I don't know how much of a biggie that is.

@holiman holiman changed the title Fix fakestorage core/state: remove notion of fake storage May 20, 2022
@@ -885,6 +885,8 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
}
}
}
// Now commit the changes
state.Commit(false)
Copy link
Member

Choose a reason for hiding this comment

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

That will actually write into the trie database, which is a big nono. It might still be "just in ram", but it will compete with the memory cache of real trie nodes. I'd wager it also creates a diff layer on top of the snapshots if one exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha! Using finalize seems to work too

@holiman holiman marked this pull request as ready for review May 23, 2022 16:48
s.fakeStorage[key] = value
//
//@deprecated use SetState per item instead. This method no longer fully replaces
// all slots.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not following this deprecation warning? It didn't fully replace all the slots before either.

Copy link
Member

Choose a reason for hiding this comment

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

Can't we just delete this altogether if there's a better alternative?

@karalabe
Copy link
Member

The change now seems ok, apart from the notes

@holiman
Copy link
Contributor Author

holiman commented May 24, 2022

Triage discussion: we need to somethow fix the SetStorage too

// Now finalize the changes. Finalize is normally performed between transactions.
// By using finalize, the overrides are semantically behaving as
// if they were created in a transaction just before the tracing occur.
state.Finalise(false)
Copy link

Choose a reason for hiding this comment

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

Could you please explain what that parameter affects and why it should be false?

@holiman
Copy link
Contributor Author

holiman commented Dec 19, 2022

I've made a stab at fixing this PR now, so that it should be possible to override the whole state. I haven't added any test for it yet.

@rjl493456442
Copy link
Member

I will take a look tomorrow.

@holiman
Copy link
Contributor Author

holiman commented Dec 19, 2022

Test added!

// We set the root to empty root, to make prevent GetCommittedState from
// reading the original data, it should thus only have access to what is
// set in the loop below.
s.data.Root = emptyRoot
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, it's not enough. State can still be hit in snapshot. e.g. https://github.com/ethereum/go-ethereum/blob/master/core/state/state_object.go#L216

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I think we're executing snapshot-free when we're tracing

@rjl493456442
Copy link
Member

My feeling is: this approach is OK, but in the future we should introduce some extra notions:
(1) StateReader
(2) StateReadWriter

The former mostly be used for serving requests and read-only actions(of course, state can be mutated but change should never be committed), and latter is used for state mutation.

@holiman
Copy link
Contributor Author

holiman commented Dec 20, 2022

Lifting this out

@rjl493456442 said:

Hmm, it's not enough. State can still be hit in snapshot. e.g. https://github.com/ethereum/go-ethereum/blob/master/core/state/state_object.go#L216

@holiman said:

No, I think we're executing snapshot-free when we're tracing


The state overrides come from two places, either ethapi/api.go DoCall:

	state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
	if state == nil || err != nil {
		return nil, err
	}
	if err := overrides.Apply(state); err != nil {

This one uses

		stateDb, err := b.eth.BlockChain().StateAt(header.Root)

->

// StateAt returns a new mutable state based on a particular point in time.
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
	return state.New(root, bc.stateCache, bc.snaps)
}

So, if the b.eth.Blockchain() has snapshots, then so does the state we're tracing on.


Or eth/tracers/api.go, API.TraceCall

	statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
	if err != nil {
		return nil, err
	}
	defer release()

	vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
	// Apply the customization rules if required.
	if config != nil {
		if err := config.StateOverrides.Apply(statedb); err != nil {

This one is a bit different, though, because StateAtBlock explicitly creates a new state without snapshots, for tracing.

TL;DR

For tracing, we are (probably) trie-only, and don't have to worry about snapshot. For eth_Call variants, however, we do, and this PR will not work properly.

@rjl493456442
Copy link
Member

@holiman I think we shouldn't implicitly hold the assumption that snapshot is not available. Theoretically, if a state is for reading, both trie and snapshot might be available.

@holiman
Copy link
Contributor Author

holiman commented Dec 21, 2022

@holiman I think we shouldn't implicitly hold the assumption that snapshot is not available. Theoretically, if a state is for reading, both trie and snapshot might be available.

Yeah, that makes a lot of sense

@holiman
Copy link
Contributor Author

holiman commented Dec 28, 2022

I think we shouldn't implicitly hold the assumption that snapshot is not available.

I think this PR now respects this idea. One might consider the way it's done to be slightly hacky, but I really do not see a better way to achieve the same effect.

Copy link
Member

@MariusVanDerWijden MariusVanDerWijden left a comment

Choose a reason for hiding this comment

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

LGTM

@holiman holiman added this to the 1.11.0 milestone Jan 10, 2023
@holiman holiman merged commit 7a48962 into ethereum:master Jan 10, 2023
shekhirin pushed a commit to shekhirin/go-ethereum that referenced this pull request Jun 6, 2023
This PR removes the notion of fakeStorage from the state objects, and instead, for any state modifications that are needed, it simply makes the changes.
MoonShiesty pushed a commit to MoonShiesty/go-ethereum that referenced this pull request Aug 30, 2023
This PR removes the notion of fakeStorage from the state objects, and instead, for any state modifications that are needed, it simply makes the changes.
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.

EVM revert does not revert state_override storage
5 participants