From e23e237b3bf6ac88804d0705bd0eab24cfb1b9e5 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 21 Mar 2024 10:57:08 +0100 Subject: [PATCH] rewrite verkle support in evm t8n --- cmd/evm/internal/t8ntool/execution.go | 161 +++++++++++++++++++++++-- cmd/evm/internal/t8ntool/flags.go | 20 +++ cmd/evm/internal/t8ntool/gen_stenv.go | 110 +++++++++++------ cmd/evm/internal/t8ntool/transition.go | 135 +++++++++++++++++++-- cmd/evm/main.go | 19 +++ core/state/database.go | 17 +-- core/state/dump.go | 22 ++++ tests/init.go | 38 ++++++ trie/verkle_iterator.go | 2 +- 9 files changed, 449 insertions(+), 75 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 035b367d34e6..c621a327d1e4 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -21,13 +21,16 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/overlay" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -36,12 +39,14 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-verkle" "golang.org/x/crypto/sha3" ) type Prestate struct { - Env stEnv `json:"env"` - Pre core.GenesisAlloc `json:"pre"` + Env stEnv `json:"env"` + Pre core.GenesisAlloc `json:"pre"` + VKT map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"` } // ExecutionResult contains the execution status after running a state test, any @@ -60,6 +65,17 @@ type ExecutionResult struct { WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` CurrentBlobGasUsed *math.HexOrDecimal64 `json:"currentBlobGasUsed,omitempty"` + + // Verkle witness + VerkleProof *verkle.VerkleProof `json:"verkleProof,omitempty"` + StateDiff verkle.StateDiff `json:"stateDiff,omitempty"` + + // Values to test the verkle conversion + CurrentAccountAddress *common.Address `json:"currentConversionAddress,omitempty" gencodec:"optional"` + CurrentSlotHash *common.Hash `json:"currentConversionSlotHash,omitempty" gencodec:"optional"` + Started *bool `json:"currentConversionStarted,omitempty" gencodec:"optional"` + Ended *bool `json:"currentConversionEnded,omitempty" gencodec:"optional"` + StorageProcessed *bool `json:"currentConversionStorageProcessed,omitempty" gencodec:"optional"` } type ommer struct { @@ -89,6 +105,13 @@ type stEnv struct { ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` ParentHash *common.Hash `json:"parentHash,omitempty"` + + // Values to test the verkle conversion + CurrentAccountAddress *common.Address `json:"currentConversionAddress" gencodec:"optional"` + CurrentSlotHash *common.Hash `json:"currentConversionSlotHash" gencodec:"optional"` + Started *bool `json:"currentConversionStarted" gencodec:"optional"` + Ended *bool `json:"currentConversionEnded" gencodec:"optional"` + StorageProcessed *bool `json:"currentConversionStorageProcessed" gencodec:"optional"` } type stEnvMarshaling struct { @@ -107,6 +130,13 @@ type stEnvMarshaling struct { ExcessBlobGas *math.HexOrDecimal64 ParentExcessBlobGas *math.HexOrDecimal64 ParentBlobGasUsed *math.HexOrDecimal64 + + // Values to test the verkle conversion + CurrentAccountAddress *common.UnprefixedAddress + CurrentSlotHash *common.UnprefixedHash + Started *bool + Ended *bool + StorageProcessed *bool } type rejectedTx struct { @@ -133,7 +163,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return h } var ( - statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre) + statedb = MakePreState(rawdb.NewMemoryDatabase(), chainConfig, pre, chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)) + vtrpre *trie.VerkleTrie signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) gaspool = new(core.GasPool) blockHash = common.Hash{0x13, 0x37} @@ -154,6 +185,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, GasLimit: pre.Env.GasLimit, GetHash: getHash, } + // Save pre verkle tree to build the proof at the end + if len(pre.VKT) > 0 { + vtrpre = statedb.GetTrie().(*trie.VerkleTrie).Copy() + } // If currentBaseFee is defined, add it to the vmContext. if pre.Env.BaseFee != nil { vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee) @@ -303,11 +338,32 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) statedb.AddBalance(w.Address, amount) } + if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + if err := overlay.OverlayVerkleTransition(statedb, common.Hash{}, chainConfig.OverlayStride); err != nil { + log.Error("error performing the transition", "err", err) + } + } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber)) if err != nil { return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err)) } + + // Add the witness to the execution result + var p *verkle.VerkleProof + var k verkle.StateDiff + if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + keys := statedb.Witness().Keys() + + if len(keys) > 0 { + p, k, err = trie.ProveAndSerialize(vtrpre, statedb.GetTrie().(*trie.VerkleTrie), keys, vtrpre.FlatdbNodeResolver) + if err != nil { + return nil, nil, fmt.Errorf("error generating verkle proof for block %d: %w", pre.Env.Number, err) + } + } + } + + sdb := statedb.Database() execRs := &ExecutionResult{ StateRoot: root, TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), @@ -319,6 +375,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), GasUsed: (math.HexOrDecimal64)(gasUsed), BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), + VerkleProof: p, + StateDiff: k, } if pre.Env.Withdrawals != nil { h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) @@ -328,6 +386,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(vmContext.ExcessBlobGas) execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed) } + if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + ended := sdb.Transitioned() + if !ended { + var ( + currentSlotHash = sdb.GetCurrentSlotHash() + started = sdb.InTransition() && sdb.Transitioned() // This can't be true + ) + execRs.CurrentAccountAddress = sdb.GetCurrentAccountAddress() + execRs.CurrentSlotHash = ¤tSlotHash + execRs.Started = &started + } + execRs.Ended = &ended + } // Re-create statedb instance with new root upon the updated database // for accessing latest states. statedb, err = state.New(root, statedb.Database(), nil) @@ -337,10 +408,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return statedb, execRs, nil } -func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB { - sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) - statedb, _ := state.New(types.EmptyRootHash, sdb, nil) - for addr, a := range accounts { +func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prestate, verkle bool) *state.StateDB { + // Start with generating the MPT DB, which should be empty if it's post-verkle transition + mptSdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true, Verkle: false}) + statedb, _ := state.New(types.EmptyRootHash, mptSdb, nil) + + // MPT pre is the same as the pre state for first conversion block + for addr, a := range pre.Pre { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) statedb.SetBalance(addr, a.Balance) @@ -348,9 +422,76 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB statedb.SetState(addr, k, v) } } - // Commit and re-open to start with a clean state. - root, _ := statedb.Commit(0, false) - statedb, _ = state.New(root, sdb, nil) + + // If verkle mode started, establish the conversion + if verkle { + // Commit db an create a snapshot from it. + mptRoot, err := statedb.Commit(0, false) + if err != nil { + panic(err) + } + rawdb.WritePreimages(mptSdb.DiskDB(), statedb.Preimages()) + mptSdb.TrieDB().WritePreimages() + snaps, err := snapshot.New(snapshot.Config{AsyncBuild: false, CacheSize: 10}, mptSdb.DiskDB(), mptSdb.TrieDB(), mptRoot) + if err != nil { + panic(err) + } + if snaps == nil { + panic("snapshot is nil") + } + snaps.Cap(mptRoot, 0) + + // reuse the backend db so that the snapshot can be enumerated + sdb := mptSdb // := state.NewDatabaseWithConfig(db, &trie.Config{Verkle: true}) + + // Load the conversion status + sdb.InitTransitionStatus(pre.Env.Started != nil && *pre.Env.Started, pre.Env.Ended != nil && *pre.Env.Ended) + if pre.Env.CurrentAccountAddress != nil { + sdb.SetCurrentAccountAddress(*pre.Env.CurrentAccountAddress) + } + if pre.Env.CurrentSlotHash != nil { + sdb.SetCurrentSlotHash(*pre.Env.CurrentSlotHash) + } + if pre.Env.StorageProcessed != nil { + sdb.SetStorageProcessed(*pre.Env.StorageProcessed) + } + + // start the conversion on the first block + if !sdb.InTransition() && !sdb.Transitioned() { + sdb.StartVerkleTransition(mptRoot, mptRoot, chainConfig, chainConfig.PragueTime, mptRoot) + } + + statedb, err = state.New(types.EmptyRootHash, sdb, nil) + if err != nil { + panic(err) + } + + // Load verkle tree from prestate + var vtr *trie.VerkleTrie + switch tr := statedb.GetTrie().(type) { + case *trie.VerkleTrie: + vtr = tr + case *trie.TransitionTrie: + vtr = tr.Overlay() + default: + panic("invalid trie type") + } + + // create the vkt, should be empty on first insert + for k, v := range pre.VKT { + values := make([][]byte, 256) + values[k[31]] = make([]byte, 32) + copy(values[k[31]], v) + vtr.UpdateStem(k.Bytes(), values) + } + + root, _ := statedb.Commit(0, false) + statedb, err = state.New(root, sdb, snaps) + if err != nil { + panic(err) + } + } + return statedb } diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index de19dbc851ef..d14d02871956 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -70,6 +70,22 @@ var ( "\t - into the file ", Value: "alloc.json", } + OutputVKTFlag = &cli.StringFlag{ + Name: "output.vkt", + Usage: "Determines where to put the `VKT` of the post-state.\n" + + "\t`stdout` - into the stdout output\n" + + "\t`stderr` - into the stderr output\n" + + "\t - into the file ", + Value: "vkt.json", + } + OutputWitnessFlag = &cli.StringFlag{ + Name: "output.witness", + Usage: "Determines where to put the `witness` of the post-state.\n" + + "\t`stdout` - into the stdout output\n" + + "\t`stderr` - into the stderr output\n" + + "\t - into the file ", + Value: "witness.json", + } OutputResultFlag = &cli.StringFlag{ Name: "output.result", Usage: "Determines where to put the `result` (stateroot, txroot etc) of the post-state.\n" + @@ -91,6 +107,10 @@ var ( Usage: "`stdin` or file name of where to find the prestate alloc to use.", Value: "alloc.json", } + InputVKTFlag = &cli.StringFlag{ + Name: "input.vkt", + Usage: "`stdin` or file name of where to find the prestate VKT.", + } InputEnvFlag = &cli.StringFlag{ Name: "input.env", Usage: "`stdin` or file name of where to find the prestate env to use.", diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index 5436407f07bb..77e2d37b7db9 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -17,26 +17,31 @@ var _ = (*stEnvMarshaling)(nil) // MarshalJSON marshals as JSON. func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { - Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` - Random *math.HexOrDecimal256 `json:"currentRandom"` - ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` - ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` - ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` - ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` - GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - ParentUncleHash common.Hash `json:"parentUncleHash"` - ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` - ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` - ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` - ParentHash *common.Hash `json:"parentHash,omitempty"` + Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` + ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` + ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` + ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` + ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` + GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + ParentUncleHash common.Hash `json:"parentUncleHash"` + ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` + ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` + ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` + CurrentAccountAddress *common.UnprefixedAddress `json:"currentConversionAddress,omitempty" gencodec:"optional"` + CurrentSlotHash *common.UnprefixedHash `json:"currentConversionSlotHash,omitempty" gencodec:"optional"` + Started *bool `json:"currentConversionStarted,omitempty" gencodec:"optional"` + Ended *bool `json:"currentConversionEnded,omitempty" gencodec:"optional"` + StorageProcessed *bool `json:"currentConversionStorageProcessed,omitempty" gencodec:"optional"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) @@ -59,32 +64,42 @@ func (s stEnv) MarshalJSON() ([]byte, error) { enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas) enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) enc.ParentHash = s.ParentHash + enc.CurrentAccountAddress = (*common.UnprefixedAddress)(s.CurrentAccountAddress) + enc.CurrentSlotHash = (*common.UnprefixedHash)(s.CurrentSlotHash) + enc.Started = s.Started + enc.Ended = s.Ended + enc.StorageProcessed = s.StorageProcessed return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { - Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` - Random *math.HexOrDecimal256 `json:"currentRandom"` - ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` - ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` - ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` - ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` - GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - ParentUncleHash *common.Hash `json:"parentUncleHash"` - ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` - ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` - ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` - ParentHash *common.Hash `json:"parentHash,omitempty"` + Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` + ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` + ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` + ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` + ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` + GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + ParentUncleHash *common.Hash `json:"parentUncleHash"` + ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` + ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` + ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` + CurrentAccountAddress *common.UnprefixedAddress `json:"currentConversionAddress,omitempty" gencodec:"optional"` + CurrentSlotHash *common.UnprefixedHash `json:"currentConversionSlotHash,omitempty" gencodec:"optional"` + Started *bool `json:"currentConversionStarted,omitempty" gencodec:"optional"` + Ended *bool `json:"currentConversionEnded,omitempty" gencodec:"optional"` + StorageProcessed *bool `json:"currentConversionStorageProcessed,omitempty" gencodec:"optional"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -154,5 +169,20 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.ParentHash != nil { s.ParentHash = dec.ParentHash } + if dec.CurrentAccountAddress != nil { + s.CurrentAccountAddress = (*common.Address)(dec.CurrentAccountAddress) + } + if dec.CurrentSlotHash != nil { + s.CurrentSlotHash = (*common.Hash)(dec.CurrentSlotHash) + } + if dec.Started != nil { + s.Started = dec.Started + } + if dec.Ended != nil { + s.Ended = dec.Ended + } + if dec.StorageProcessed != nil { + s.StorageProcessed = dec.StorageProcessed + } return nil } diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index cbb39294d04c..6879a8f92927 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -39,6 +40,9 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/ethereum/go-verkle" "github.com/urfave/cli/v2" ) @@ -77,10 +81,11 @@ var ( ) type input struct { - Alloc core.GenesisAlloc `json:"alloc,omitempty"` - Env *stEnv `json:"env,omitempty"` - Txs []*txWithKey `json:"txs,omitempty"` - TxRlp string `json:"txsRlp,omitempty"` + Alloc core.GenesisAlloc `json:"alloc,omitempty"` + Env *stEnv `json:"env,omitempty"` + VKT map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"` + Txs []*txWithKey `json:"txs,omitempty"` + TxRlp string `json:"txsRlp,omitempty"` } func Transition(ctx *cli.Context) error { @@ -149,13 +154,14 @@ func Transition(ctx *cli.Context) error { prestate Prestate txs types.Transactions // txs to apply allocStr = ctx.String(InputAllocFlag.Name) + vktStr = ctx.String(InputVKTFlag.Name) envStr = ctx.String(InputEnvFlag.Name) txStr = ctx.String(InputTxsFlag.Name) inputData = &input{} ) // Figure out the prestate alloc - if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector { + if allocStr == stdinSelector || vktStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector { decoder := json.NewDecoder(os.Stdin) if err := decoder.Decode(inputData); err != nil { return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err)) @@ -167,6 +173,12 @@ func Transition(ctx *cli.Context) error { } } prestate.Pre = inputData.Alloc + if vktStr != stdinSelector && vktStr != "" { + if err := readFile(vktStr, "VKT", &inputData.VKT); err != nil { + return err + } + } + prestate.VKT = inputData.VKT // Set the block environment if envStr != stdinSelector { @@ -300,8 +312,16 @@ func Transition(ctx *cli.Context) error { body, _ := rlp.EncodeToBytes(txs) // Dump the excution result collector := make(Alloc) - s.DumpToCollector(collector, nil) - return dispatchOutput(ctx, baseDir, result, collector, body) + var vktleaves map[common.Hash]hexutil.Bytes + if !chainConfig.IsPrague(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) { + // Only dump accounts in MPT mode, verkle does not have the + // concept of an alloc. + s.DumpToCollector(collector, nil) + } else { + vktleaves = make(map[common.Hash]hexutil.Bytes) + s.DumpVKTLeaves(vktleaves) + } + return dispatchOutput(ctx, baseDir, result, collector, vktleaves, body, result.VerkleProof, result.StateDiff) } // txWithKey is a helper-struct, to allow us to use the types.Transaction along with @@ -426,7 +446,7 @@ func saveFile(baseDir, filename string, data interface{}) error { // dispatchOutput writes the output data to either stderr or stdout, or to the specified // files -func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error { +func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, vkt map[common.Hash]hexutil.Bytes, body hexutil.Bytes, p *verkle.VerkleProof, k verkle.StateDiff) error { stdOutObject := make(map[string]interface{}) stdErrObject := make(map[string]interface{}) dispatch := func(baseDir, fName, name string, obj interface{}) error { @@ -453,6 +473,19 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil { return err } + if vkt != nil { + if err := dispatch(baseDir, ctx.String(OutputVKTFlag.Name), "vkt", vkt); err != nil { + return err + } + } + if p != nil { + if err := dispatch(baseDir, ctx.String(OutputWitnessFlag.Name), "witness", struct { + Proof *verkle.VerkleProof + Diff verkle.StateDiff + }{p, k}); err != nil { + return err + } + } if len(stdOutObject) > 0 { b, err := json.MarshalIndent(stdOutObject, "", " ") if err != nil { @@ -471,3 +504,89 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a } return nil } + +// VerkleKeys computes the tree key given an address and an optional +// slot number. +func VerkleKey(ctx *cli.Context) error { + if ctx.Args().Len() == 0 || ctx.Args().Len() > 2 { + return errors.New("invalid number of arguments: expecting an address and an optional slot number") + } + + addr, err := hexutil.Decode(ctx.Args().Get(0)) + if err != nil { + return fmt.Errorf("error decoding address: %w", err) + } + + ap := utils.EvaluateAddressPoint(addr) + if ctx.Args().Len() == 2 { + slot, err := hexutil.Decode(ctx.Args().Get(1)) + if err != nil { + return fmt.Errorf("error decoding slot: %w", err) + } + fmt.Printf("%#x\n", utils.GetTreeKeyStorageSlotWithEvaluatedAddress(ap, slot)) + } else { + fmt.Printf("%#x\n", utils.GetTreeKeyVersionWithEvaluatedAddress(ap)) + } + return nil +} + +// VerkleKeys computes a set of tree keys given a set of addresses +// and their optional slot numbers. +func VerkleKeys(ctx *cli.Context) error { + var allocStr = ctx.String(InputAllocFlag.Name) + var alloc core.GenesisAlloc + // Figure out the prestate alloc + if allocStr == stdinSelector { + decoder := json.NewDecoder(os.Stdin) + if err := decoder.Decode(&alloc); err != nil { + return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err)) + } + } + if allocStr != stdinSelector { + if err := readFile(allocStr, "alloc", &alloc); err != nil { + return err + } + } + + vkt := trie.NewVerkleTrie(verkle.New(), trie.NewDatabase(rawdb.NewMemoryDatabase()), utils.NewPointCache(), true) + + for addr, acc := range alloc { + for slot, value := range acc.Storage { + err := vkt.UpdateStorage(addr, slot.Bytes(), value.Big().Bytes()) + if err != nil { + return fmt.Errorf("error inserting storage: %w", err) + } + } + + account := &types.StateAccount{ + Balance: acc.Balance, + Nonce: acc.Nonce, + CodeHash: crypto.Keccak256Hash(acc.Code).Bytes(), + Root: common.Hash{}, + } + err := vkt.UpdateAccount(addr, account) + if err != nil { + return fmt.Errorf("error inserting account: %w", err) + } + } + + collector := make(map[common.Hash]hexutil.Bytes) + it, err := vkt.NodeIterator(nil) + if err != nil { + panic(err) + } + for it.Next(true) { + if it.Leaf() { + collector[common.BytesToHash(it.LeafKey())] = it.LeafBlob() + } + } + + output, err := json.MarshalIndent(collector, "", "") + if err != nil { + return fmt.Errorf("error outputting tree: %w", err) + } + + fmt.Println(string(output)) + + return nil +} diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 024be62b9c54..3c68b47513f4 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -141,16 +141,35 @@ var stateTransitionCommand = &cli.Command{ t8ntool.TraceEnableReturnDataFlag, t8ntool.OutputBasedir, t8ntool.OutputAllocFlag, + t8ntool.OutputVKTFlag, t8ntool.OutputResultFlag, t8ntool.OutputBodyFlag, t8ntool.InputAllocFlag, t8ntool.InputEnvFlag, + t8ntool.InputVKTFlag, t8ntool.InputTxsFlag, t8ntool.ForknameFlag, t8ntool.ChainIDFlag, t8ntool.RewardFlag, t8ntool.VerbosityFlag, }, + Subcommands: []*cli.Command{ + &cli.Command{ + Name: "verkle-keys", + Aliases: []string{"v"}, + Usage: "compute a set of verkle tree keys, given their source addresses and optional slot numbers", + Action: t8ntool.VerkleKeys, + Flags: []cli.Flag{ + t8ntool.InputAllocFlag, + }, + }, + &cli.Command{ + Name: "verkle-key", + Aliases: []string{"V"}, + Usage: "compute the verkle tree key given an address and optional slot number", + Action: t8ntool.VerkleKey, + }, + }, } var transactionCommand = &cli.Command{ diff --git a/core/state/database.go b/core/state/database.go index 826c03cd9f05..2184e0580e4a 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -227,13 +227,6 @@ func (db *cachingDB) Transitioned() bool { // Fork implements the fork func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64, root common.Hash) { - fmt.Println(` - __________.__ .__ .__ __ .__ .__ ____ - \__ ___| |__ ____ ____ | | ____ ______ | |__ _____ _____/ |_ | |__ _____ ______ __ _ _|__| ____ / ___\ ______ - | | | | \_/ __ \ _/ __ \| | _/ __ \\____ \| | \\__ \ / \ __\ | | \\__ \ / ___/ \ \/ \/ | |/ \ / /_/ / ___/ - | | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ \ /| | | \\___ /\___ \ - |____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ \/\_/ |__|___| /_____//_____/ - |__|`) db.CurrentTransitionState = &TransitionState{ Started: true, // initialize so that the first storage-less accounts are processed @@ -265,13 +258,6 @@ func (db *cachingDB) EndVerkleTransition() { db.CurrentTransitionState.Started = true } - fmt.Println(` - __________.__ .__ .__ __ .__ .__ .___ .___ - \__ ___| |__ ____ ____ | | ____ ______ | |__ _____ _____/ |_ | |__ _____ ______ | | _____ ____ __| _/____ __| _/ - | | | | \_/ __ \ _/ __ \| | _/ __ \\____ \| | \\__ \ / \ __\ | | \\__ \ / ___/ | | \__ \ / \ / __ _/ __ \ / __ | - | | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ | |__/ __ \| | / /_/ \ ___// /_/ | - |____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ |____(____ |___| \____ |\___ \____ | - |__|`) db.CurrentTransitionState.Ended = true } @@ -349,7 +335,6 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { mpt Trie err error ) - fmt.Printf("opening trie with root %x, %v %v\n", root, db.InTransition(), db.Transitioned()) // TODO separate both cases when I can be certain that it won't // find a Verkle trie where is expects a Transitoion trie. @@ -415,7 +400,7 @@ func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre } } if db.InTransition() { - fmt.Printf("OpenStorageTrie during transition, state root=%x root=%x\n", stateRoot, root) + // fmt.Printf("OpenStorageTrie during transition, state root=%x root=%x\n", stateRoot, root) mpt, err := db.openStorageMPTrie(db.LastMerkleRoot, address, root, nil) if err != nil { return nil, err diff --git a/core/state/dump.go b/core/state/dump.go index 9ce6cd394b88..3e5965d89d44 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -220,6 +220,28 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] return nextKey } +func (s *StateDB) DumpVKTLeaves(collector map[common.Hash]hexutil.Bytes) { + var vtr *trie.VerkleTrie + switch tr := s.trie.(type) { + case *trie.VerkleTrie: + vtr = tr + case *trie.TransitionTrie: + vtr = tr.Overlay() + default: + panic("invalid trie type") + } + + it, err := vtr.NodeIterator(nil) + if err != nil { + panic(err) + } + for it.Next(true) { + if it.Leaf() { + collector[common.BytesToHash(it.LeafKey())] = it.LeafBlob() + } + } +} + // RawDump returns the entire state an a single large object func (s *StateDB) RawDump(opts *DumpConfig) Dump { dump := &Dump{ diff --git a/tests/init.go b/tests/init.go index a04e227dc7af..8fb3c3eea258 100644 --- a/tests/init.go +++ b/tests/init.go @@ -318,6 +318,44 @@ var Forks = map[string]*params.ChainConfig{ ShanghaiTime: u64(0), CancunTime: u64(0), }, + "Prague": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + PragueTime: u64(0), + }, + "ShanghaiToPragueAtTime32": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + PragueTime: u64(32), + }, } // AvailableForks returns the set of defined fork names diff --git a/trie/verkle_iterator.go b/trie/verkle_iterator.go index 16de8746b6d5..cbe73588e5e0 100644 --- a/trie/verkle_iterator.go +++ b/trie/verkle_iterator.go @@ -24,7 +24,7 @@ import ( type verkleNodeIteratorState struct { Node verkle.VerkleNode - Index int // points to _next_ value + Index int // point to the *next* value } type verkleNodeIterator struct {