-
Notifications
You must be signed in to change notification settings - Fork 20.9k
Stateless transition #31532
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: master
Are you sure you want to change the base?
Stateless transition #31532
Conversation
5f4b22f
to
16cb4fa
Compare
@@ -22,7 +22,7 @@ require ( | |||
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 | |||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 | |||
github.com/ethereum/c-kzg-4844 v1.0.0 | |||
github.com/ethereum/go-verkle v0.2.2 | |||
github.com/ethereum/go-verkle v0.2.1 |
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 due to a bug being found in 0.2.2 that breaks the Holesky shadowfork. I will revert this in a further PR, when I have time to perform a deeper investigation of the bug. v0.2.2 is a db-size optimization and has no functional impact.
core/state/database.go
Outdated
// StartVerkleTransition marks the start of the verkle transition | ||
StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, verkleTime *uint64, root common.Hash) | ||
|
||
// EndVerkleTransition marks the end of the verkle transition | ||
EndVerkleTransition() | ||
|
||
// InTransition returns true if the verkle transition is currently ongoing | ||
InTransition() bool | ||
|
||
// Transitioned returns true if the verkle transition has ended | ||
Transitioned() bool | ||
|
||
InitTransitionStatus(bool, bool, common.Hash) | ||
|
||
SetCurrentSlotHash(common.Hash) | ||
|
||
GetCurrentAccountAddress() *common.Address | ||
|
||
SetCurrentAccountAddress(common.Address) | ||
|
||
GetCurrentAccountHash() common.Hash | ||
|
||
GetCurrentSlotHash() common.Hash | ||
|
||
SetStorageProcessed(bool) | ||
|
||
GetStorageProcessed() bool | ||
|
||
GetCurrentPreimageOffset() int64 | ||
|
||
SetCurrentPreimageOffset(int64) | ||
|
||
AddRootTranslation(originalRoot, translatedRoot common.Hash) | ||
|
||
SetLastMerkleRoot(common.Hash) | ||
|
||
SaveTransitionState(common.Hash) | ||
|
||
LoadTransitionState(common.Hash) | ||
|
||
LockCurrentTransitionState() | ||
|
||
UnLockCurrentTransitionState() |
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.
@rjl493456442 these are helper functions to handle the transition. In my own branch, this is the most convenient way to access them. But maybe they could be moved to a verkle-specific TrieDB
if it's cleaner ... what do you think?
if len(val) != 0 { | ||
return val, nil | ||
} | ||
// TODO also insert value into overlay |
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.
That's contrary to the spec
// TODO also insert value into overlay |
} | ||
return data, nil | ||
} | ||
// TODO also insert value into overlay |
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 thing as above
// TODO also insert value into overlay |
return t.base | ||
} | ||
|
||
// TODO(gballet/jsign): consider removing this API. |
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 self: remove that if it's no longer needed
Signed-off-by: Guillaume Ballet <[email protected]>
8e8d10a
to
b9884bf
Compare
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.
I pushed a second commit in order to explore the idea I mentioned earlier, which is to move as much of the transition management code to the TransitionTrie
itself. The code is still incomplete and doesn't compile, but reading my messages will help explain some aspects of it - and hopefully we can converge towards a working and reliable design.
core/state/database.go
Outdated
StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, verkleTime *uint64, root common.Hash) | ||
|
||
// EndVerkleTransition marks the end of the verkle transition | ||
EndVerkleTransition() | ||
|
||
// InTransition returns true if the verkle transition is currently ongoing | ||
InTransition() bool | ||
|
||
// Transitioned returns true if the verkle transition has ended | ||
Transitioned() bool | ||
|
||
InitTransitionStatus(bool, bool, common.Hash) | ||
|
||
// SetCurrentSlotHash provides the next slot to be translated | ||
SetCurrentSlotHash(common.Hash) | ||
|
||
// GetCurrentAccountAddress returns the address of the account that is currently being translated | ||
GetCurrentAccountAddress() *common.Address | ||
|
||
SetCurrentAccountAddress(common.Address) | ||
|
||
GetCurrentAccountHash() common.Hash | ||
|
||
GetCurrentSlotHash() common.Hash | ||
|
||
SetStorageProcessed(bool) | ||
|
||
GetStorageProcessed() bool | ||
|
||
GetCurrentPreimageOffset() int64 | ||
|
||
SetCurrentPreimageOffset(int64) | ||
|
||
AddRootTranslation(originalRoot, translatedRoot common.Hash) | ||
|
||
SetLastMerkleRoot(common.Hash) | ||
|
||
SaveTransitionState(common.Hash) | ||
|
||
LoadTransitionState(common.Hash) | ||
|
||
LockCurrentTransitionState() | ||
|
||
UnLockCurrentTransitionState() |
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.
These are helper functions managing the conversion pointers (current slot being considered, current account, as well as methods to save and restore the state from the db).
In the source branch, these are stored in the unique disk database. Having to partially rewrite the code means that we can consider other methods. For instance, I am exploring the possibility to store the pointers in the MPT or verkle DB (if it's not present there, it means that the transition hasn't started) - and I am thinking it'd be better if the transition tree were the one deciding where the data is - I have a hunch this might simplify the design.
Activation of the forks would still be explicit (no way to work around that anyway, we need a header for chainConfig.IsVerkle()
) but the interface would be that of the tree.
core/state/database.go
Outdated
// Transition-specific fields | ||
CurrentTransitionState *TransitionState | ||
TransitionStatePerRoot lru.BasicLRU[common.Hash, *TransitionState] | ||
transitionStateLock sync.Mutex | ||
addrToPoint *utils.PointCache | ||
baseRoot common.Hash // hash of last read-only MPT base tree |
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.
These could be moved to the TransitionTree
, if it makes the interface/diff simpler (see previous comment)
core/state/database.go
Outdated
} | ||
|
||
// NewDatabase creates a state database with the provided data sources. | ||
func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB { | ||
return &CachingDB{ | ||
disk: triedb.Disk(), | ||
triedb: triedb, | ||
verkledb: nil, // XXX change the interface, but it's a big change so wait for the PR to be reviewed by Gary |
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.
The question of how this gets initialized is still open. I have a hunch we could simply create it no matter what, and keep it empty as long as the transition hasn't happened.
I'm not a 100% clear on how this would work in a fully-verkle tesnet, but I guess that in this case, we could have an empty MPT db with just the info that the transition has completed and therefore, that there is no need to go through it.
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.
Gary: pass verkle db as parameter
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.
and initialize both DBs at the beginning so that we support verkle at genesis networks
core/state/reader.go
Outdated
if !db.IsVerkle() { | ||
tr, err = trie.NewStateTrie(trie.StateTrieID(root), db) | ||
// get the transition status from the MPT triedb | ||
ts, err := db.LoadTransitionState(root) |
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 that this method doesn't currently exist, I'm proposing that we move it from (*state.Database).LoadTransitionState
) and keep it inside the tree somehow.
core/state/reader.go
Outdated
} | ||
tr = trie.NewTransitionTree(mptr, vktr, false) |
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.
so as a follow-up to the message above, this would be NewTransitionTree(mptr, vktr, false, ts)
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.
Not present in this PR yet, but the call to StartVerkleTransition
could be called at the start ofProcess
, since the state should not be needed beforehand.
log.Error("error performing the transition", "err", err) | ||
panic(fmt.Sprintf("error performing the transition: %s", err)) | ||
} | ||
} |
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 self: this is also where the pointers would be saved at the db. And the equivalent "start / save" would have to be added to the miner for block production.
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return tr, nil | ||
} | ||
|
||
// OpenTrie opens the main account trie at a specific root hash. | ||
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) { |
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.
Gary: read the pointers directly from ethdb.KeyValueStore
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.
and store it in the cachingdb, no lock should be needed
core/state/database.go
Outdated
// Transition-specific fields | ||
CurrentTransitionState *TransitionState | ||
TransitionStatePerRoot lru.BasicLRU[common.Hash, *TransitionState] | ||
transitionStateLock sync.Mutex |
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.
Gary: the cache itself has an internal lock, this should not be necessary
500abed
to
7942e52
Compare
This is an implementation of the state conversion process described in EIP-7612.
What this PR does:
TransitionTree
, that is in charge of abstracting which one of the underlying trees (the "base" MPT tree or the "overlay" verkle tree) values should be read from/written to.N
"leaves" (either a single slot or a full account header + code). In order to resume where the previous block stopped, aTransitionState
structure is maintained in the db, keyed by the pre-state root of the block. This helps recovering these pointers in case the process is ran multiple times (e.g. block production, followed by insertion).