diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 196cbc857c..e06da9157b 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -396,7 +396,7 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea return nil, errors.New("post-state tree is not available") } vktPreTrie, okpre := preTrie.(*trie.VerkleTrie) - vktPostTrie, okpost := postTrie.(*trie.VerkleTrie) + vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie) // The witness is only attached iff both parent and current block are // using verkle tree. diff --git a/core/blockchain.go b/core/blockchain.go index d52990ec5a..371163569f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -341,13 +341,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, if cfg == nil { cfg = DefaultConfig() } - - // Open trie database with provided config - enableVerkle, err := EnableVerkleAtGenesis(db, genesis) - if err != nil { - return nil, err - } - triedb := triedb.NewDatabase(db, cfg.triedbConfig(enableVerkle)) + triedb := triedb.NewDatabase(db, cfg.triedbConfig(genesis.IsVerkle())) // Write the supplied genesis to the database if it has not been initialized // yet. The corresponding chain config will be returned, either from the diff --git a/core/chain_makers.go b/core/chain_makers.go index b2559495a1..674a9d8196 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -540,8 +540,10 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine return block, b.receipts } + sdb := state.NewDatabase(trdb, nil) + for i := 0; i < n; i++ { - statedb, err := state.New(parent.Root(), state.NewDatabase(trdb, nil)) + statedb, err := state.New(parent.Root(), sdb) if err != nil { panic(err) } @@ -579,6 +581,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (common.Hash, ethdb.Database, []*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) { db := rawdb.NewMemoryDatabase() + saveVerkleTransitionStatusAtVerlkeGenesis(db) cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme) cacheConfig.SnapshotLimit = 0 triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) diff --git a/core/genesis.go b/core/genesis.go index f1a620da57..58deafae08 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -18,6 +18,7 @@ package core import ( "bytes" + "encoding/gob" "encoding/json" "errors" "fmt" @@ -145,6 +146,9 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) { emptyRoot = types.EmptyVerkleHash } db := rawdb.NewMemoryDatabase() + if isVerkle { + saveVerkleTransitionStatusAtVerlkeGenesis(db) + } statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), nil)) if err != nil { return common.Hash{}, err @@ -276,6 +280,24 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error { return cfg.CheckConfigForkOrder() } +// saveVerkleTransitionStatusAtVerlkeGenesis saves a conversion marker +// representing a converted state, which is used in devnets that activate +// verkle at genesis. +func saveVerkleTransitionStatusAtVerlkeGenesis(db ethdb.Database) { + saveVerkleTransitionStatus(db, common.Hash{}, &state.TransitionState{Ended: true}) +} + +func saveVerkleTransitionStatus(db ethdb.Database, root common.Hash, ts *state.TransitionState) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(ts) + if err != nil { + log.Error("failed to encode transition state", "err", err) + return + } + rawdb.WriteVerkleTransitionState(db, root, buf.Bytes()) +} + // SetupGenesisBlock writes or updates the genesis block in db. // The block that will be used is: // @@ -299,6 +321,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g if genesis != nil && genesis.Config == nil { return nil, common.Hash{}, nil, errGenesisNoConfig } + // In case of verkle-at-genesis, we need to ensure that the conversion + // markers are indicating that the conversion has completed. + if genesis != nil && genesis.Config.VerkleTime != nil && *genesis.Config.VerkleTime == genesis.Timestamp { + saveVerkleTransitionStatusAtVerlkeGenesis(db) + } // Commit the genesis if the database is empty ghash := rawdb.ReadCanonicalHash(db, 0) if (ghash == common.Hash{}) { @@ -446,7 +473,7 @@ func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainCo // IsVerkle indicates whether the state is already stored in a verkle // tree at genesis time. func (g *Genesis) IsVerkle() bool { - return g.Config.IsVerkleGenesis() + return g.Config.VerkleTime != nil && *g.Config.VerkleTime == g.Timestamp } // ToBlock returns the genesis block according to genesis specification. @@ -550,6 +577,9 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo if err != nil { return nil, err } + if g.IsVerkle() { + saveVerkleTransitionStatus(db, block.Root(), &state.TransitionState{Ended: true}) + } batch := db.NewBatch() rawdb.WriteGenesisStateSpec(batch, block.Hash(), blob) rawdb.WriteBlock(batch, block) @@ -572,29 +602,6 @@ func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types. return block } -// EnableVerkleAtGenesis indicates whether the verkle fork should be activated -// at genesis. This is a temporary solution only for verkle devnet testing, where -// verkle fork is activated at genesis, and the configured activation date has -// already passed. -// -// In production networks (mainnet and public testnets), verkle activation always -// occurs after the genesis block, making this function irrelevant in those cases. -func EnableVerkleAtGenesis(db ethdb.Database, genesis *Genesis) (bool, error) { - if genesis != nil { - if genesis.Config == nil { - return false, errGenesisNoConfig - } - return genesis.Config.EnableVerkleAtGenesis, nil - } - if ghash := rawdb.ReadCanonicalHash(db, 0); ghash != (common.Hash{}) { - chainCfg := rawdb.ReadChainConfig(db, ghash) - if chainCfg != nil { - return chainCfg.EnableVerkleAtGenesis, nil - } - } - return false, nil -} - // DefaultGenesisBlock returns the Ethereum main net genesis block. func DefaultGenesisBlock() *Genesis { return &Genesis{ diff --git a/core/genesis_test.go b/core/genesis_test.go index a41dfce578..0077863193 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -287,7 +287,6 @@ func TestVerkleGenesisCommit(t *testing.T) { OsakaTime: &verkleTime, VerkleTime: &verkleTime, TerminalTotalDifficulty: big.NewInt(0), - EnableVerkleAtGenesis: true, Ethash: nil, Clique: nil, BlobScheduleConfig: ¶ms.BlobScheduleConfig{ @@ -315,7 +314,7 @@ func TestVerkleGenesisCommit(t *testing.T) { } db := rawdb.NewMemoryDatabase() - + saveVerkleTransitionStatusAtVerlkeGenesis(db) config := *pathdb.Defaults config.NoAsyncFlush = true diff --git a/core/rawdb/accessors_overlay.go b/core/rawdb/accessors_overlay.go new file mode 100644 index 0000000000..364cc889d1 --- /dev/null +++ b/core/rawdb/accessors_overlay.go @@ -0,0 +1,30 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +func ReadVerkleTransitionState(db ethdb.KeyValueReader, hash common.Hash) ([]byte, error) { + return db.Get(transitionStateKey(hash)) +} + +func WriteVerkleTransitionState(db ethdb.KeyValueWriter, hash common.Hash, state []byte) error { + return db.Put(transitionStateKey(hash), state) +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 388a08f243..72f9bd34ec 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -158,6 +158,9 @@ var ( preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) preimageHitsCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) preimageMissCounter = metrics.NewRegisteredCounter("db/preimage/miss", nil) + + // Verkle transition information + VerkleTransitionStatePrefix = []byte("verkle-transition-state-") ) // LegacyTxLookupEntry is the legacy TxLookupEntry definition with some unnecessary @@ -397,3 +400,8 @@ func storageHistoryIndexBlockKey(addressHash common.Hash, storageHash common.Has binary.BigEndian.PutUint32(buf[:], blockID) return append(append(append(StateHistoryStorageBlockPrefix, addressHash.Bytes()...), storageHash.Bytes()...), buf[:]...) } + +// transitionStateKey = transitionStatusKey + hash +func transitionStateKey(hash common.Hash) []byte { + return append(VerkleTransitionStatePrefix, hash.Bytes()...) +} diff --git a/core/state/database.go b/core/state/database.go index 5fb198a629..d65489f4aa 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -17,7 +17,10 @@ package state import ( + "bytes" + "encoding/gob" "fmt" + "reflect" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" @@ -26,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/utils" @@ -62,6 +66,12 @@ type Database interface { // Snapshot returns the underlying state snapshot. Snapshot() *snapshot.Tree + + // SaveTransitionState saves the tree transition progress markers to the database. + SaveTransitionState(common.Hash, *TransitionState) + + // LoadTransitionState loads the tree transition progress markers from the database. + LoadTransitionState(common.Hash) *TransitionState } // Trie is a Ethereum Merkle Patricia trie. @@ -141,6 +151,50 @@ type Trie interface { IsVerkle() bool } +// TransitionState is a structure that holds the progress markers of the +// translation process. +type TransitionState struct { + CurrentAccountAddress *common.Address // addresss of the last translated account + CurrentSlotHash common.Hash // hash of the last translated storage slot + CurrentPreimageOffset int64 // next byte to read from the preimage file + Started, Ended bool + + // Mark whether the storage for an account has been processed. This is useful if the + // maximum number of leaves of the conversion is reached before the whole storage is + // processed. + StorageProcessed bool + + BaseRoot common.Hash // hash of the last read-only MPT base tree +} + +// InTransition returns true if the translation process is in progress. +func (ts *TransitionState) InTransition() bool { + return ts != nil && ts.Started && !ts.Ended +} + +// Transitioned returns true if the translation process has been completed. +func (ts *TransitionState) Transitioned() bool { + return ts != nil && ts.Ended +} + +// Copy returns a deep copy of the TransitionState object. +func (ts *TransitionState) Copy() *TransitionState { + ret := &TransitionState{ + Started: ts.Started, + Ended: ts.Ended, + CurrentSlotHash: ts.CurrentSlotHash, + CurrentPreimageOffset: ts.CurrentPreimageOffset, + StorageProcessed: ts.StorageProcessed, + } + + if ts.CurrentAccountAddress != nil { + ret.CurrentAccountAddress = &common.Address{} + copy(ret.CurrentAccountAddress[:], ts.CurrentAccountAddress[:]) + } + + return ret +} + // CachingDB is an implementation of Database interface. It leverages both trie and // state snapshot to provide functionalities for state access. It's meant to be a // long-live object and has a few caches inside for sharing between blocks. @@ -151,17 +205,21 @@ type CachingDB struct { codeCache *lru.SizeConstrainedCache[common.Hash, []byte] codeSizeCache *lru.Cache[common.Hash, int] pointCache *utils.PointCache + + // Transition-specific fields + TransitionStatePerRoot lru.BasicLRU[common.Hash, *TransitionState] } // 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, - snap: snap, - codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), - codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), - pointCache: utils.NewPointCache(pointCacheSize), + disk: triedb.Disk(), + triedb: triedb, + snap: snap, + codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), + codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), + pointCache: utils.NewPointCache(pointCacheSize), + TransitionStatePerRoot: lru.NewBasicLRU[common.Hash, *TransitionState](1000), } } @@ -184,6 +242,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { readers = append(readers, newFlatReader(snap)) } } + ts := db.LoadTransitionState(stateRoot) // Configure the state reader using the path database in path mode. // This reader offers improved performance but is optional and only // partially useful if the snapshot data in path database is not @@ -196,7 +255,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { } // Configure the trie reader, which is expected to be available as the // gatekeeper unless the state is corrupted. - tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache) + tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache, ts) if err != nil { return nil, err } @@ -223,7 +282,11 @@ func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (ReaderWithSta // OpenTrie opens the main account trie at a specific root hash. func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) { - if db.triedb.IsVerkle() { + ts := db.LoadTransitionState(root) + if ts.InTransition() { + panic("transition isn't supported yet") + } + if ts.Transitioned() { return trie.NewVerkleTrie(root, db.triedb, db.pointCache) } tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb) @@ -235,10 +298,11 @@ func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) { // OpenStorageTrie opens the storage trie of an account. func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) { + ts := db.LoadTransitionState(stateRoot) // In the verkle case, there is only one tree. But the two-tree structure // is hardcoded in the codebase. So we need to return the same trie in this // case. - if db.triedb.IsVerkle() { + if ts.InTransition() || ts.Transitioned() { return self, nil } tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb) @@ -290,3 +354,86 @@ func mustCopyTrie(t Trie) Trie { panic(fmt.Errorf("unknown trie type %T", t)) } } + +// SaveTransitionState saves the transition state to the cache and commits +// it to the database if it's not already in the cache. +func (db *CachingDB) SaveTransitionState(root common.Hash, ts *TransitionState) { + if ts == nil { + panic("nil transition state") + } + + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(ts) + if err != nil { + log.Error("failed to encode transition state", "err", err) + return + } + + if !db.TransitionStatePerRoot.Contains(root) { + // Copy so that the address pointer isn't updated after + // it has been saved. + db.TransitionStatePerRoot.Add(root, ts.Copy()) + rawdb.WriteVerkleTransitionState(db.TrieDB().Disk(), root, buf.Bytes()) + } else { + // Check that the state is consistent with what is in the cache, + // which is not strictly necessary but a good sanity check. Can + // be removed when the transition is stable. + cachedState, _ := db.TransitionStatePerRoot.Get(root) + if !reflect.DeepEqual(cachedState, ts) { + fmt.Println("transition state mismatch", "cached state", cachedState, "new state", ts) + panic("transition state mismatch") + } + } + + log.Debug("saving transition state", "storage processed", ts.StorageProcessed, "addr", ts.CurrentAccountAddress, "slot hash", ts.CurrentSlotHash, "root", root, "ended", ts.Ended, "started", ts.Started) +} + +func (db *CachingDB) LoadTransitionState(root common.Hash) *TransitionState { + // Try to get the transition state from the cache and + // the DB if it's not there. + ts, ok := db.TransitionStatePerRoot.Get(root) + if !ok { + // Not in the cache, try getting it from the DB + data, _ := rawdb.ReadVerkleTransitionState(db.TrieDB().Disk(), root) + // if err != nil && errors.Is(err, triedb.ErrNotFound) { + // log.Error("failed to read transition state", "err", err) + // return nil + // } + + // if a state could be read from the db, attempt to decode it + if len(data) > 0 { + var ( + newts TransitionState + buf = bytes.NewBuffer(data[:]) + dec = gob.NewDecoder(buf) + ) + // Decode transition state + err := dec.Decode(&newts) + if err != nil { + log.Error("failed to decode transition state", "err", err) + return nil + } + ts = &newts + } + + // Fallback that should only happen before the transition + if ts == nil { + // Initialize the first transition state, with the "ended" + // field set to true if the database was created + // as a verkle database. + log.Debug("no transition state found, starting fresh", "is verkle", db.triedb.IsVerkle()) + // Start with a fresh state + ts = &TransitionState{Ended: db.triedb.IsVerkle()} + } + + db.TransitionStatePerRoot.Add(root, ts) + } + + // Copy so that the CurrentAddress pointer in the map + // doesn't get overwritten. + // db.CurrentTransitionState = ts.Copy() + + log.Debug("loaded transition state", "storage processed", ts.StorageProcessed, "addr", ts.CurrentAccountAddress, "slot hash", ts.CurrentSlotHash, "root", root, "ended", ts.Ended, "started", ts.Started) + return ts +} diff --git a/core/state/database_history.go b/core/state/database_history.go index 314c56c470..705c1dd0ac 100644 --- a/core/state/database_history.go +++ b/core/state/database_history.go @@ -153,3 +153,11 @@ func (db *HistoricDB) TrieDB() *triedb.Database { func (db *HistoricDB) Snapshot() *snapshot.Tree { return nil } + +func (db *HistoricDB) LoadTransitionState(common.Hash) *TransitionState { + panic("should not be called") +} + +func (db *HistoricDB) SaveTransitionState(common.Hash, *TransitionState) { + panic("should not be called") +} diff --git a/core/state/reader.go b/core/state/reader.go index 4628f4d5db..7f08ef1a56 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -233,12 +233,12 @@ type trieReader struct { // trieReader constructs a trie reader of the specific state. An error will be // returned if the associated trie specified by root is not existent. -func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) { +func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache, ts *TransitionState) (*trieReader, error) { var ( tr Trie err error ) - if !db.IsVerkle() { + if !ts.Transitioned() && !ts.InTransition() { tr, err = trie.NewStateTrie(trie.StateTrieID(root), db) } else { tr, err = trie.NewVerkleTrie(root, db, cache) diff --git a/core/state/statedb.go b/core/state/statedb.go index e805885079..f431016413 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1305,6 +1305,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorag return nil, err } } + s.db.SaveTransitionState(ret.root, &TransitionState{Ended: true}) if !ret.empty() { // If snapshotting is enabled, update the snapshot tree with this new version if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil { diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go index a89672e6e5..47e3c885d9 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "encoding/hex" + "fmt" "math/big" "slices" "testing" @@ -58,7 +59,6 @@ var ( ShanghaiTime: u64(0), VerkleTime: u64(0), TerminalTotalDifficulty: common.Big0, - EnableVerkleAtGenesis: true, BlobScheduleConfig: ¶ms.BlobScheduleConfig{ Verkle: params.DefaultPragueBlobConfig, }, @@ -82,7 +82,6 @@ var ( ShanghaiTime: u64(0), VerkleTime: u64(0), TerminalTotalDifficulty: common.Big0, - EnableVerkleAtGenesis: true, BlobScheduleConfig: ¶ms.BlobScheduleConfig{ Verkle: params.DefaultPragueBlobConfig, }, @@ -202,6 +201,9 @@ func TestProcessVerkle(t *testing.T) { t.Log("verified verkle proof, inserting blocks into the chain") + for i, b := range chain { + fmt.Printf("%d %x\n", i, b.Root()) + } endnum, err := blockchain.InsertChain(chain) if err != nil { t.Fatalf("block %d imported with error: %v", endnum, err) diff --git a/params/config.go b/params/config.go index 85619bbe22..4e74bf2082 100644 --- a/params/config.go +++ b/params/config.go @@ -423,19 +423,6 @@ type ChainConfig struct { DepositContractAddress common.Address `json:"depositContractAddress,omitempty"` - // EnableVerkleAtGenesis is a flag that specifies whether the network uses - // the Verkle tree starting from the genesis block. If set to true, the - // genesis state will be committed using the Verkle tree, eliminating the - // need for any Verkle transition later. - // - // This is a temporary flag only for verkle devnet testing, where verkle is - // activated at genesis, and the configured activation date has already passed. - // - // In production networks (mainnet and public testnets), verkle activation - // always occurs after the genesis block, making this flag irrelevant in - // those cases. - EnableVerkleAtGenesis bool `json:"enableVerkleAtGenesis,omitempty"` - // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` @@ -704,20 +691,6 @@ func (c *ChainConfig) IsBPO5(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.BPO5Time, time) } -// IsVerkleGenesis checks whether the verkle fork is activated at the genesis block. -// -// Verkle mode is considered enabled if the verkle fork time is configured, -// regardless of whether the local time has surpassed the fork activation time. -// This is a temporary workaround for verkle devnet testing, where verkle is -// activated at genesis, and the configured activation date has already passed. -// -// In production networks (mainnet and public testnets), verkle activation -// always occurs after the genesis block, making this function irrelevant in -// those cases. -func (c *ChainConfig) IsVerkleGenesis() bool { - return c.EnableVerkleAtGenesis -} - // IsEIP4762 returns whether eip 4762 has been activated at given block. func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool { return c.IsVerkle(num, time)