Han (weihan) - Update 12 & 13

Development Updates

​​​​    __________.__                       .__                .__                   __       .__                       .__                    .___         .___
​​​​    \__    ___|  |__   ____        ____ |  |   ____ ______ |  |__ _____    _____/  |_     |  |__ _____    ______    |  | _____    ____   __| _/____   __| _/
​​​​      |    |  |  |  \_/ __ \     _/ __ \|  | _/ __ \\____ \|  |  \\__  \  /    \   __\    |  |  \\__  \  /  ___/    |  | \__  \  /    \ / __ _/ __ \ / __ |
​​​​      |    |  |   Y  \  ___/     \  ___/|  |_\  ___/|  |_> |   Y  \/ __ \|   |  |  |      |   Y  \/ __ \_\___ \     |  |__/ __ \|   |  / /_/ \  ___// /_/ |
​​​​      |____|  |___|  /\___        \___  |____/\___  |   __/|___|  (____  |___|  |__|      |___|  (____  /_____/     |____(____  |___|  \____ |\___  \____ |
​​​​                                                |__|

All of the components have been built already. Also managed to get a local private Verkle testnet running with the Clique consensus. I'm testing out the state expiry functionality by simulating the hard forks and sending revive transactions. The PoC isn't fully done yet as there are still some bugs here and there, but it's close to being done.

Check out my development branch for Verkle Tree here.

Check out my development branch for Geth here.

Check out my local Verkle testnet setup here.

Code Notes

Integration of all components

On a high level overview, the current block that is being processed has to propagate the state epoch information all the way to the trie node and refresh the access information of the key-value pairs. The following diagram shows the block importing process from the time a block is being received and processed (based on the Geth codebase):

When the Blockchain object receives a block, or it's creating a block, it initializes the underlying state database object (i.e. stateDb) with the epoch based on the predefined configuration logic.

func NewWithStateEpoch(config *params.ChainConfig, TargetBlock *big.Int, root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
	TargetEpoch := types.GetStateEpoch(config, TargetBlock)
	stateDB, err := newStateDB(root, db, snaps, TargetEpoch)
	if err != nil {
		return nil, err
	}

	stateDB.TargetBlock = TargetBlock
	if err != nil {
		return nil, err
	}
	return stateDB, nil
}

There are only 2 opcodes that stores persistent state data: 1) SLOAD and 2) SSTORE. The following diagram shows the flow from how the SSTORE opcode is being executed with a key-value pair (SLOAD is similar):

The crucial part is at the stateObject.GetCommittedState part. It will first attempt to retrieve a value given a key by loading it from local cache. If it's not found, then it will load from snapshot layers. If it's not found again, it will load from the trie persisted in the disk.

During this retrieval process, if the query accesses an expired value (as determined by the state epoch metadata in the leaf node), then an error is returned, all the way back to the EVM layer. Thus, a transaction's execution is reverted.

State Storage Analysis

I've been running a full node to collect KV's data for about a month now. It has synced up to ~14M blocks (about January 2022). It's estimated to sync up until ~16M blocks right before the Devconnect's session. It's unfortunate that I can't generate and present the most updated analysis, but I'll still write a complete article once the data collection process is finished.

Next week's Action Items

  • Complete the PoC
  • Complete the logic to insert MPT to the VKT with KV's block number
Select a repo