diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 035b367d34e6..ce59d378ad34 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,17 @@ 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 pre.VKT != nil && len(pre.VKT) > 0 { + switch tr := statedb.GetTrie().(type) { + case *trie.VerkleTrie: + vtrpre = tr.Copy() + case *trie.TransitionTrie: + vtrpre = tr.Overlay().Copy() + default: + panic("invalid trie type") + } + } // If currentBaseFee is defined, add it to the vmContext. if pre.Env.BaseFee != nil { vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee) @@ -184,10 +226,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, misc.ApplyDAOHardFork(statedb) } if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { - // insert all parent hashes in the contract - for i := pre.Env.Number - 1; i > 0 && i >= pre.Env.Number-257; i-- { - core.ProcessParentBlockHash(statedb, i, pre.Env.BlockHashes[math.HexOrDecimal64(i)]) - } + // insert the parent hash in the contract + parentNum := pre.Env.Number - 1 + core.ProcessParentBlockHash(statedb, parentNum, pre.Env.BlockHashes[math.HexOrDecimal64(parentNum)]) } var blobGasUsed uint64 for i, tx := range txs { @@ -215,6 +256,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, snapshot = statedb.Snapshot() prevGas = gaspool.Gas() ) + txContext.Accesses = statedb.NewAccessWitness() evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) // (ret []byte, usedGas uint64, failed bool, err error) @@ -269,7 +311,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) } - + statedb.Witness().Merge(txContext.Accesses) txIndex++ } statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) @@ -303,11 +345,39 @@ 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 { + return nil, nil, fmt.Errorf("error performing the transition, err=%w", 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 vktProof *verkle.VerkleProof + var vktStateDiff verkle.StateDiff + if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + keys := statedb.Witness().Keys() + if len(keys) > 0 && vtrpre != nil { + var proofTrie *trie.VerkleTrie + switch tr := statedb.GetTrie().(type) { + case *trie.VerkleTrie: + proofTrie = tr + case *trie.TransitionTrie: + proofTrie = tr.Overlay() + default: + return nil, nil, fmt.Errorf("invalid tree type in proof generation: %v", tr) + } + vktProof, vktStateDiff, err = trie.ProveAndSerialize(vtrpre, proofTrie, keys, vtrpre.FlatdbNodeResolver) + if err != nil { + return nil, nil, fmt.Errorf("error generating verkle proof for block %d: %w", pre.Env.Number, err) + } + } + } + execRs := &ExecutionResult{ StateRoot: root, TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), @@ -319,6 +389,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: vktProof, + StateDiff: vktStateDiff, } if pre.Env.Withdrawals != nil { h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) @@ -328,6 +400,22 @@ 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) { + sdb := statedb.Database() + ended := sdb.Transitioned() + if !ended { + var ( + currentSlotHash = sdb.GetCurrentSlotHash() + started = sdb.InTransition() + storageProcessed = sdb.GetStorageProcessed() + ) + execRs.CurrentAccountAddress = sdb.GetCurrentAccountAddress() + execRs.CurrentSlotHash = ¤tSlotHash + execRs.Started = &started + execRs.StorageProcessed = &storageProcessed + } + 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 +425,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}) +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 + sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true, Verkle: false}) statedb, _ := state.New(types.EmptyRootHash, sdb, nil) - for addr, a := range accounts { + + // 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 +439,94 @@ 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 { + state.NoBanner() + // Commit db an create a snapshot from it. + mptRoot, err := statedb.Commit(0, false) + if err != nil { + panic(err) + } + rawdb.WritePreimages(sdb.DiskDB(), statedb.Preimages()) + sdb.TrieDB().WritePreimages() + snaps, err := snapshot.New(snapshot.Config{AsyncBuild: false, CacheSize: 10}, sdb.DiskDB(), sdb.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 + + // Load the conversion status + log.Info("loading conversion status", "started", pre.Env.Started) + if pre.Env.Started != nil { + log.Info("non-nil started", "started", *pre.Env.Started) + } + sdb.InitTransitionStatus(pre.Env.Started != nil && *pre.Env.Started, pre.Env.Ended != nil && *pre.Env.Ended, mptRoot) + log.Info("loading current account address, if available", "available", pre.Env.CurrentAccountAddress != nil) + if pre.Env.CurrentAccountAddress != nil { + log.Info("loading current account address", "address", *pre.Env.CurrentAccountAddress) + 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 + log.Info("starting verkle transition?", "in transition", sdb.InTransition(), "transitioned", sdb.Transitioned(), "mpt root", mptRoot) + if !sdb.InTransition() && !sdb.Transitioned() { + sdb.StartVerkleTransition(mptRoot, mptRoot, chainConfig, chainConfig.PragueTime, mptRoot) + } + + // create the state database without the snapshot, so that it's not overwritten + // when the vkt values are inserted. This state db is used temporarily in order + // to dump the tree that was passed as parameters, but is not suited for block + // execution since it's missing the snapshot and therefore it can't go through + // the conversion. + 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) + + // recreate the verkle db with the tree root, but this time with the mpt snapshot, + // so that the conversion can proceed. + statedb, err = state.New(root, sdb, snaps) + if err != nil { + panic(err) + } + } + + if statedb.Database().InTransition() || statedb.Database().Transitioned() { + log.Info("at end of makestate", "in transition", statedb.Database().InTransition(), "during", statedb.Database().Transitioned(), "account hash", statedb.Database().GetCurrentAccountHash()) + } 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..e066463116e6 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,10 @@ 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/holiman/uint256" "github.com/urfave/cli/v2" ) @@ -77,10 +82,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 +155,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 +174,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 +313,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 +447,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 +474,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 +505,132 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a } return nil } + +// VerkleKey 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 genesis alloc. +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) + } + + err = vkt.UpdateContractCode(addr, common.BytesToHash(account.CodeHash), acc.Code) + if err != nil { + return fmt.Errorf("error inserting code: %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 +} + +// VerkleCodeChunkKey computes the tree key of a code-chunk for a given address. +func VerkleCodeChunkKey(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 code-chunk number") + } + + addr, err := hexutil.Decode(ctx.Args().Get(0)) + if err != nil { + return fmt.Errorf("error decoding address: %w", err) + } + chunkNumberBytes, err := hexutil.Decode(ctx.Args().Get(1)) + if err != nil { + return fmt.Errorf("error decoding chunk number: %w", err) + } + var chunkNumber uint256.Int + chunkNumber.SetBytes(chunkNumberBytes) + + fmt.Printf("%#x\n", utils.GetTreeKeyCodeChunk(addr, &chunkNumber)) + + return nil +} + +// VerkleChunkifyCode returns the code chunkification for a given code. +func VerkleChunkifyCode(ctx *cli.Context) error { + if ctx.Args().Len() == 0 || ctx.Args().Len() > 1 { + return errors.New("invalid number of arguments: expecting a bytecode") + } + + bytecode, err := hexutil.Decode(ctx.Args().Get(0)) + if err != nil { + return fmt.Errorf("error decoding address: %w", err) + } + + chunkedCode := trie.ChunkifyCode(bytecode) + fmt.Printf("%#x\n", chunkedCode) + + return nil +} diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 024be62b9c54..b2f70f0930f2 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -141,10 +141,13 @@ var stateTransitionCommand = &cli.Command{ t8ntool.TraceEnableReturnDataFlag, t8ntool.OutputBasedir, t8ntool.OutputAllocFlag, + t8ntool.OutputVKTFlag, + t8ntool.OutputWitnessFlag, t8ntool.OutputResultFlag, t8ntool.OutputBodyFlag, t8ntool.InputAllocFlag, t8ntool.InputEnvFlag, + t8ntool.InputVKTFlag, t8ntool.InputTxsFlag, t8ntool.ForknameFlag, t8ntool.ChainIDFlag, @@ -153,6 +156,41 @@ var stateTransitionCommand = &cli.Command{ }, } +var verkleCommand = &cli.Command{ + Name: "verkle", + Aliases: []string{"vkt"}, + Usage: "Verkle helpers", + Subcommands: []*cli.Command{ + { + Name: "tree-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, + }, + }, + { + Name: "single-key", + Aliases: []string{"V"}, + Usage: "compute the verkle tree key given an address and optional slot number", + Action: t8ntool.VerkleKey, + }, + { + Name: "code-chunk-key", + Aliases: []string{"VCK"}, + Usage: "compute the verkle tree key given an address and chunk number", + Action: t8ntool.VerkleCodeChunkKey, + }, + { + Name: "chunkify-code", + Aliases: []string{"VCC"}, + Usage: "chunkify a given bytecode", + Action: t8ntool.VerkleChunkifyCode, + }, + }, +} + var transactionCommand = &cli.Command{ Name: "transaction", Aliases: []string{"t9n"}, @@ -220,6 +258,7 @@ func init() { stateTransitionCommand, transactionCommand, blockBuilderCommand, + verkleCommand, } } diff --git a/core/blockchain.go b/core/blockchain.go index 1ffb36ffb523..d3ad0dc6d338 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -326,7 +326,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis // for it to be able to recover if interrupted during the transition // but that's left out to a later PR since there's not really a need // right now. - bc.stateCache.InitTransitionStatus(true, true) + bc.stateCache.InitTransitionStatus(true, true, common.Hash{}) bc.stateCache.EndVerkleTransition() } @@ -1772,7 +1772,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) // If the verkle activation time hasn't started, declare it as "not started". // This is so that if the miner activates the conversion, the insertion happens // in the correct mode. - bc.stateCache.InitTransitionStatus(false, false) + bc.stateCache.InitTransitionStatus(false, false, common.Hash{}) } if parent.Number.Uint64() == conversionBlock { bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time, parent.Root) diff --git a/core/state/database.go b/core/state/database.go index f2592f7de03a..d8208e49d264 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -45,6 +45,12 @@ const ( codeCacheSize = 64 * 1024 * 1024 ) +var useBanner bool = true + +func NoBanner() { + useBanner = false +} + // Database wraps access to tries and contract code. type Database interface { // OpenTrie opens the main account trie. @@ -68,7 +74,7 @@ type Database interface { // TrieDB retrieves the low level trie database used for data storage. TrieDB() *trie.Database - StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunTime *uint64, root common.Hash) + StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64, root common.Hash) ReorgThroughVerkleTransition() @@ -78,7 +84,7 @@ type Database interface { Transitioned() bool - InitTransitionStatus(bool, bool) + InitTransitionStatus(bool, bool, common.Hash) SetCurrentSlotHash(common.Hash) @@ -227,13 +233,15 @@ 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(` + if useBanner { + fmt.Println(` __________.__ .__ .__ __ .__ .__ ____ \__ ___| |__ ____ ____ | | ____ ______ | |__ _____ _____/ |_ | |__ _____ ______ __ _ _|__| ____ / ___\ ______ | | | | \_/ __ \ _/ __ \| | _/ __ \\____ \| | \\__ \ / \ __\ | | \\__ \ / ___/ \ \/ \/ | |/ \ / /_/ / ___/ | | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ \ /| | | \\___ /\___ \ |____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ \/\_/ |__|___| /_____//_____/ |__|`) + } db.CurrentTransitionState = &TransitionState{ Started: true, // initialize so that the first storage-less accounts are processed @@ -252,12 +260,13 @@ func (db *cachingDB) ReorgThroughVerkleTransition() { log.Warn("trying to reorg through the transition, which makes no sense at this point") } -func (db *cachingDB) InitTransitionStatus(started, ended bool) { +func (db *cachingDB) InitTransitionStatus(started, ended bool, baseRoot common.Hash) { db.CurrentTransitionState = &TransitionState{ Ended: ended, Started: started, // TODO add other fields when we handle mid-transition interrupts } + db.baseRoot = baseRoot } func (db *cachingDB) EndVerkleTransition() { @@ -265,13 +274,15 @@ func (db *cachingDB) EndVerkleTransition() { db.CurrentTransitionState.Started = true } - fmt.Println(` + if useBanner { + fmt.Println(` __________.__ .__ .__ __ .__ .__ .___ .___ \__ ___| |__ ____ ____ | | ____ ______ | |__ _____ _____/ |_ | |__ _____ ______ | | _____ ____ __| _/____ __| _/ | | | | \_/ __ \ _/ __ \| | _/ __ \\____ \| | \\__ \ / \ __\ | | \\__ \ / ___/ | | \__ \ / \ / __ _/ __ \ / __ | | | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ | |__/ __ \| | / /_/ \ ___// /_/ | |____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ |____(____ |___| \____ |\___ \____ | |__|`) + } db.CurrentTransitionState.Ended = true } @@ -344,12 +355,6 @@ func (db *cachingDB) openVKTrie(root common.Hash) (Trie, error) { // OpenTrie opens the main account trie at a specific root hash. func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { - var ( - 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. if db.InTransition() || db.Transitioned() { @@ -369,21 +374,17 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { // Otherwise, return a transition trie, with a base MPT // trie and an overlay, verkle trie. - mpt, err = db.openMPTTrie(db.baseRoot) + mpt, err := db.openMPTTrie(db.baseRoot) if err != nil { log.Error("failed to open the mpt", "err", err, "root", db.baseRoot) return nil, err } return trie.NewTransitionTree(mpt.(*trie.SecureTrie), vkt.(*trie.VerkleTrie), false), nil - } else { - mpt, err = db.openMPTTrie(root) - if err != nil { - return nil, err - } } - return mpt, nil + log.Info("not in transition, opening mpt alone", "root", root) + return db.openMPTTrie(root) } func (db *cachingDB) openStorageMPTrie(stateRoot common.Hash, address common.Address, root common.Hash, _ Trie) (Trie, error) { 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/core/state_processor.go b/core/state_processor.go index d6a01673c6ab..68193b3d0982 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -79,12 +79,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg signer = types.MakeSigner(p.config, header.Number, header.Time) ) if p.config.IsPrague(block.Number(), block.Time()) { - parent := p.bc.GetBlockByHash(block.ParentHash()) - if !p.config.IsPrague(parent.Number(), parent.Time()) { - InsertBlockHashHistoryAtEip2935Fork(statedb, block.NumberU64()-1, block.ParentHash(), p.bc) - } else { - ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash()) - } + ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash()) } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { diff --git a/light/trie.go b/light/trie.go index 7e7c03bc16c1..2f5999230896 100644 --- a/light/trie.go +++ b/light/trie.go @@ -121,7 +121,7 @@ func (db *odrDatabase) Transitioned() bool { panic("not implemented") // TODO: Implement } -func (db *odrDatabase) InitTransitionStatus(bool, bool) { +func (db *odrDatabase) InitTransitionStatus(bool, bool, common.Hash) { panic("not implemented") // TODO: Implement } diff --git a/miner/worker.go b/miner/worker.go index 3fb4a3fa43e5..651424b06364 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -918,12 +918,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { return nil, err } if w.chainConfig.IsPrague(header.Number, header.Time) { - parent := w.chain.GetHeaderByNumber(header.Number.Uint64() - 1) - if !w.chain.Config().IsPrague(parent.Number, parent.Time) { - core.InsertBlockHashHistoryAtEip2935Fork(env.state, header.Number.Uint64()-1, header.ParentHash, w.chain) - } else { - core.ProcessParentBlockHash(env.state, header.Number.Uint64()-1, header.ParentHash) - } + core.ProcessParentBlockHash(env.state, header.Number.Uint64()-1, header.ParentHash) } return env, nil } 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