diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go index 6201f5baf..c75f9588d 100644 --- a/cmd/devp2p/internal/ethtest/chain.go +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -77,7 +77,7 @@ func (c *Chain) RootAt(height int) common.Hash { // ForkID gets the fork id of the chain. func (c *Chain) ForkID() forkid.ID { - return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len())) + return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len()), c.blocks[0].Time()) } // Shorten returns a copy chain of a desired height from the imported diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index 51073b20c..e4036a740 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -24,6 +24,7 @@ import ( "math" "math/big" "reflect" + "slices" "strings" "github.com/morph-l2/go-ethereum/common" @@ -44,6 +45,12 @@ var ( ErrLocalIncompatibleOrStale = errors.New("local incompatible or needs update") ) +// timestampThreshold is the Morph mainnet genesis timestamp. It is used to +// differentiate if a forkid.next field is a block number or a timestamp. Whilst +// very hacky, something's needed to split the validation during the transition +// period (block forks -> time forks). +const timestampThreshold = 1729490400 + // Blockchain defines all necessary method to build a forkID. type Blockchain interface { // Config retrieves the chain's fork configuration. @@ -65,31 +72,41 @@ type ID struct { // Filter is a fork id filter to validate a remotely advertised ID. type Filter func(id ID) error -// NewID calculates the Ethereum fork ID from the chain config, genesis hash, and head. -func NewID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { +// NewID calculates the Ethereum fork ID from the chain config, genesis hash, head and time. +func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) ID { // Calculate the starting checksum from the genesis hash hash := crc32.ChecksumIEEE(genesis[:]) // Calculate the current fork checksum and the next fork block - var next uint64 - for _, fork := range gatherForks(config) { + forksByBlock, forksByTime := gatherForks(config) + for _, fork := range forksByBlock { if fork <= head { // Fork already passed, checksum the previous hash and the fork number hash = checksumUpdate(hash, fork) continue } - next = fork - break + return ID{Hash: checksumToBytes(hash), Next: fork} + } + for _, fork := range forksByTime { + if fork <= time { + // Fork already passed, checksum the previous hash and fork timestamp + hash = checksumUpdate(hash, fork) + continue + } + return ID{Hash: checksumToBytes(hash), Next: fork} } - return ID{Hash: checksumToBytes(hash), Next: next} + return ID{Hash: checksumToBytes(hash), Next: 0} } // NewIDWithChain calculates the Ethereum fork ID from an existing chain instance. func NewIDWithChain(chain Blockchain) ID { + head := chain.CurrentHeader() + return NewID( chain.Config(), chain.Genesis().Hash(), - chain.CurrentHeader().Number.Uint64(), + head.Number.Uint64(), + head.Time, ) } @@ -99,26 +116,28 @@ func NewFilter(chain Blockchain) Filter { return newFilter( chain.Config(), chain.Genesis().Hash(), - func() uint64 { - return chain.CurrentHeader().Number.Uint64() + func() (uint64, uint64) { + head := chain.CurrentHeader() + return head.Number.Uint64(), head.Time }, ) } // NewStaticFilter creates a filter at block zero. func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) Filter { - head := func() uint64 { return 0 } + head := func() (uint64, uint64) { return 0, 0 } return newFilter(config, genesis, head) } // newFilter is the internal version of NewFilter, taking closures as its arguments // instead of a chain. The reason is to allow testing it without having to simulate // an entire blockchain. -func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() uint64) Filter { +func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (uint64, uint64)) Filter { // Calculate the all the valid fork hash and fork next combos var ( - forks = gatherForks(config) - sums = make([][4]byte, len(forks)+1) // 0th is the genesis + forksByBlock, forksByTime = gatherForks(config) + forks = append(append([]uint64{}, forksByBlock...), forksByTime...) + sums = make([][4]byte, len(forks)+1) // 0th is the genesis ) hash := crc32.ChecksumIEEE(genesis[:]) sums[0] = checksumToBytes(hash) @@ -129,7 +148,10 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui // Add two sentries to simplify the fork checks and don't require special // casing the last one. forks = append(forks, math.MaxUint64) // Last fork will never be passed - + if len(forksByTime) == 0 { + // In purely block based forks, avoid the sentry spilling into timestapt territory + forksByBlock = append(forksByBlock, math.MaxUint64) // Last fork will never be passed + } // Create a validator that will filter out incompatible chains return func(id ID) error { // Run the fork checksum validation ruleset: @@ -151,8 +173,13 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui // the remote, but at this current point in time we don't have enough // information. // 4. Reject in all other cases. - head := headfn() + block, time := headfn() for i, fork := range forks { + // Pick the head comparison based on fork progression + head := block + if i >= len(forksByBlock) { + head = time + } // If our head is beyond this fork, continue to the next (we have a dummy // fork of maxuint64 as the last item to always fail this check eventually). if head >= fork { @@ -163,7 +190,7 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui if sums[i] == id.Hash { // Fork checksum matched, check if a remote future fork block already passed // locally without the local node being aware of it (rule #1a). - if id.Next > 0 && head >= id.Next { + if id.Next > 0 && (head >= id.Next || (id.Next > timestampThreshold && time >= id.Next)) { return ErrLocalIncompatibleOrStale } // Haven't passed locally a remote-only fork, accept the connection (rule #1b). @@ -211,46 +238,60 @@ func checksumToBytes(hash uint32) [4]byte { return blob } -// gatherForks gathers all the known forks and creates a sorted list out of them. -func gatherForks(config *params.ChainConfig) []uint64 { +// gatherForks gathers all the known forks and creates two sorted lists out of +// them, one for the block number based forks and the second for the timestamps. +func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) { // Gather all the fork block numbers via reflection kind := reflect.TypeOf(params.ChainConfig{}) conf := reflect.ValueOf(config).Elem() - - var forks []uint64 + x := uint64(0) + var ( + forksByBlock []uint64 + forksByTime []uint64 + ) for i := 0; i < kind.NumField(); i++ { // Fetch the next field and skip non-fork rules field := kind.Field(i) - if !strings.HasSuffix(field.Name, "Block") { + + time := strings.HasSuffix(field.Name, "Time") + if !time && !strings.HasSuffix(field.Name, "Block") { continue } - if field.Type != reflect.TypeOf(new(big.Int)) { - continue + + // Extract the fork rule block number or timestamp and aggregate it + if field.Type == reflect.TypeOf(&x) { + if rule := conf.Field(i).Interface().(*uint64); rule != nil { + forksByTime = append(forksByTime, *rule) + } } - // Extract the fork rule block number and aggregate it - rule := conf.Field(i).Interface().(*big.Int) - if rule != nil { - forks = append(forks, rule.Uint64()) + if field.Type == reflect.TypeOf(new(big.Int)) { + if rule := conf.Field(i).Interface().(*big.Int); rule != nil { + forksByBlock = append(forksByBlock, rule.Uint64()) + } } } - // Sort the fork block numbers to permit chronological XOR - for i := 0; i < len(forks); i++ { - for j := i + 1; j < len(forks); j++ { - if forks[i] > forks[j] { - forks[i], forks[j] = forks[j], forks[i] - } + slices.Sort(forksByBlock) + slices.Sort(forksByTime) + + // Deduplicate fork identifiers applying multiple forks + for i := 1; i < len(forksByBlock); i++ { + if forksByBlock[i] == forksByBlock[i-1] { + forksByBlock = append(forksByBlock[:i], forksByBlock[i+1:]...) + i-- } } - // Deduplicate block numbers applying multiple forks - for i := 1; i < len(forks); i++ { - if forks[i] == forks[i-1] { - forks = append(forks[:i], forks[i+1:]...) + for i := 1; i < len(forksByTime); i++ { + if forksByTime[i] == forksByTime[i-1] { + forksByTime = append(forksByTime[:i], forksByTime[i+1:]...) i-- } } // Skip any forks in block 0, that's the genesis ruleset - if len(forks) > 0 && forks[0] == 0 { - forks = forks[1:] + if len(forksByBlock) > 0 && forksByBlock[0] == 0 { + forksByBlock = forksByBlock[1:] + } + if len(forksByTime) > 0 && forksByTime[0] == 0 { + forksByTime = forksByTime[1:] } - return forks + return forksByBlock, forksByTime } diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 37dad9f0a..7696fd11f 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -136,7 +136,7 @@ func TestCreation(t *testing.T) { } for i, tt := range tests { for j, ttt := range tt.cases { - if have := NewID(tt.config, tt.genesis, ttt.head); have != ttt.want { + if have := NewID(tt.config, tt.genesis, ttt.head, 0); have != ttt.want { t.Errorf("test %d, case %d: fork ID mismatch: have %x, want %x", i, j, have, ttt.want) } } @@ -218,7 +218,7 @@ func TestValidation(t *testing.T) { {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, } for i, tt := range tests { - filter := newFilter(params.MainnetChainConfig, params.MainnetGenesisHash, func() uint64 { return tt.head }) + filter := newFilter(params.MainnetChainConfig, params.MainnetGenesisHash, func() (uint64, uint64) { return tt.head, 0 }) if err := filter(tt.id); err != tt.err { t.Errorf("test %d: validation error mismatch: have %v, want %v", i, err, tt.err) } @@ -247,3 +247,59 @@ func TestEncoding(t *testing.T) { } } } + +func TestTimeBasedForkInGenesis(t *testing.T) { + // Config that has not timestamp enabled + legacyConfig := *params.MorphMainnetChainConfig + legacyConfig.Morph203Time = nil + + morphMainnetConfig := *params.MorphMainnetChainConfig + morph203Time := uint64(1741579200) + morphMainnetConfig.Morph203Time = params.NewUint64(morph203Time) // 2025-03-10 04:00:00 UTC + + tests := []struct { + config *params.ChainConfig + head uint64 + time uint64 + id ID + err error + }{ + //------------------ + // Block based tests + //------------------ + + // Local is mainnet, remote announces the same. No future fork is announced. + {&legacyConfig, 0, 0, ID{Hash: checksumToBytes(0xb0709522), Next: 0}, nil}, + + {&legacyConfig, 0, morph203Time + 1, ID{Hash: checksumToBytes(0xb0709522), Next: morph203Time}, ErrLocalIncompatibleOrStale}, + + //------------------ + // Timestamp based tests + //------------------ + + // unpassed fork + {&morphMainnetConfig, 6656942, morph203Time - 1, ID{Hash: checksumToBytes(0xb0709522), Next: 0}, nil}, + + // passed fork + {&morphMainnetConfig, 6656942, morph203Time + 1, ID{Hash: checksumToBytes(0xb0709522), Next: 0}, ErrRemoteStale}, + + // unpassed fork + {&morphMainnetConfig, 6656942, morph203Time - 1, ID{Hash: checksumToBytes(0xb0709522), Next: morph203Time}, nil}, + + // passed fork + {&morphMainnetConfig, 6656942, morph203Time + 1, ID{Hash: checksumToBytes(0xb0709522), Next: morph203Time}, nil}, + + // subset fork + {&morphMainnetConfig, 6656942, morph203Time + 1, ID{Hash: checksumToBytes(0xb0709522), Next: morph203Time - 1}, ErrRemoteStale}, + + // superset fork, Local is mainnet before morph203, remote announces morph203. Local is out of sync, accept. + {&morphMainnetConfig, 6656942, morph203Time - 1, ID{Hash: checksumToBytes(0x90106828), Next: 0}, nil}, + } + + for i, tt := range tests { + filter := newFilter(tt.config, params.MorphMainnetGenesisHash, func() (uint64, uint64) { return tt.head, tt.time }) + if err := filter(tt.id); err != tt.err { + t.Errorf("test %d: validation error mismatch: have %v, want %v", i, err, tt.err) + } + } +} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index d90b8b3a3..0ae1901bb 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -35,7 +35,8 @@ import ( ) var ( - errPrecompileDisabled = errors.New("sha256, ripemd160, blake2f precompiles temporarily disabled") + errModexpUnsupportedInput = errors.New("modexp temporarily only accepts inputs of 32 bytes (256 bits) or less") + errPrecompileDisabled = errors.New("sha256, ripemd160, blake2f precompiles temporarily disabled") ) // PrecompiledContract is the basic interface for native Go contracts. The implementation @@ -131,10 +132,10 @@ var PrecompiledContractsMorph203 = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true, morph203: true}, common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{morph203: true}, common.BytesToAddress([]byte{9}): &blake2F{}, } @@ -324,7 +325,8 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { // bigModExp implements a native big integer exponential modular operation. type bigModExp struct { - eip2565 bool + eip2565 bool + morph203 bool } var ( @@ -456,6 +458,14 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { expLen = expLenBigInt.Uint64() modLen = modLenBigInt.Uint64() ) + // Check that all inputs are `u256` (32 - bytes) or less, revert otherwise + if !c.morph203 { + var lenLimit = new(big.Int).SetInt64(32) + if baseLenBigInt.Cmp(lenLimit) > 0 || expLenBigInt.Cmp(lenLimit) > 0 || modLenBigInt.Cmp(lenLimit) > 0 { + return nil, errModexpUnsupportedInput + } + } + if len(input) > 96 { input = input[96:] } else { @@ -591,7 +601,12 @@ var ( // runBn256Pairing implements the Bn256Pairing precompile, referenced by both // Byzantium and Istanbul operations. -func runBn256Pairing(input []byte) ([]byte, error) { +func runBn256Pairing(input []byte, morph203 bool) ([]byte, error) { + // Allow at most 4 inputs + if !morph203 && len(input) > 4*192 { + return nil, errBadPairingInput + } + // Handle some corner cases cheaply if len(input)%192 > 0 { return nil, errBadPairingInput @@ -622,7 +637,7 @@ func runBn256Pairing(input []byte) ([]byte, error) { // bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. -type bn256PairingIstanbul struct{} +type bn256PairingIstanbul struct{ morph203 bool } // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { @@ -630,7 +645,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { } func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { - return runBn256Pairing(input) + return runBn256Pairing(input, c.morph203) } // bn256PairingByzantium implements a pairing pre-compile for the bn256 curve @@ -643,7 +658,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { } func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { - return runBn256Pairing(input) + return runBn256Pairing(input, false) } type blake2F struct{} diff --git a/eth/discovery.go b/eth/discovery.go index b44cd681a..ba7c9af38 100644 --- a/eth/discovery.go +++ b/eth/discovery.go @@ -59,5 +59,5 @@ func (eth *Ethereum) startEthEntryUpdate(ln *enode.LocalNode) { func (eth *Ethereum) currentEthEntry() *ethEntry { return ðEntry{ForkID: forkid.NewID(eth.blockchain.Config(), eth.blockchain.Genesis().Hash(), - eth.blockchain.CurrentHeader().Number.Uint64())} + eth.blockchain.CurrentHeader().Number.Uint64(), eth.blockchain.CurrentHeader().Time)} } diff --git a/eth/handler.go b/eth/handler.go index 46c586736..b03a4bb09 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -274,7 +274,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { number = head.Number.Uint64() td = h.chain.GetTd(hash, number) ) - forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64()) + forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64(), h.chain.CurrentHeader().Time) if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { peer.Log().Debug("Ethereum handshake failed", "err", err) return err diff --git a/eth/protocols/eth/discovery.go b/eth/protocols/eth/discovery.go index 9c80a741e..abe843612 100644 --- a/eth/protocols/eth/discovery.go +++ b/eth/protocols/eth/discovery.go @@ -60,6 +60,6 @@ func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) { // currentENREntry constructs an `eth` ENR entry based on the current state of the chain. func currentENREntry(chain *core.BlockChain) *enrEntry { return &enrEntry{ - ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64()), + ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64(), chain.CurrentHeader().Time), } } diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go index b752ec1ab..db7347313 100644 --- a/eth/protocols/eth/handshake_test.go +++ b/eth/protocols/eth/handshake_test.go @@ -40,7 +40,7 @@ func testHandshake(t *testing.T, protocol uint) { genesis = backend.chain.Genesis() head = backend.chain.CurrentBlock() td = backend.chain.GetTd(head.Hash(), head.NumberU64()) - forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64()) + forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time) ) tests := []struct { code uint64 diff --git a/les/client_handler.go b/les/client_handler.go index 4e1b92827..1f6fcaf3f 100644 --- a/les/client_handler.go +++ b/les/client_handler.go @@ -111,7 +111,7 @@ func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error { p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) // Execute the LES handshake - forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64()) + forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64(), h.backend.blockchain.CurrentHeader().Time) if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil { p.Log().Debug("Light Ethereum handshake failed", "err", err) return err diff --git a/les/peer_test.go b/les/peer_test.go index c0a4d1108..7f487c048 100644 --- a/les/peer_test.go +++ b/les/peer_test.go @@ -125,8 +125,8 @@ func TestHandshake(t *testing.T) { genesis = common.HexToHash("cafebabe") chain1, chain2 = &fakeChain{}, &fakeChain{} - forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64()) - forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64()) + forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64(), chain1.CurrentHeader().Time) + forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64(), chain2.CurrentHeader().Time) filter1, filter2 = forkid.NewFilter(chain1), forkid.NewFilter(chain2) ) diff --git a/les/server_handler.go b/les/server_handler.go index 419be95f1..4afbe1a84 100644 --- a/les/server_handler.go +++ b/les/server_handler.go @@ -116,7 +116,7 @@ func (h *serverHandler) handle(p *clientPeer) error { hash = head.Hash() number = head.Number.Uint64() td = h.blockchain.GetTd(hash, number) - forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), h.blockchain.CurrentBlock().NumberU64()) + forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), h.blockchain.CurrentBlock().NumberU64(), h.blockchain.CurrentBlock().Time()) ) if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil { p.Log().Debug("Light Ethereum handshake failed", "err", err) diff --git a/les/test_helper.go b/les/test_helper.go index 116c30381..c169d519a 100644 --- a/les/test_helper.go +++ b/les/test_helper.go @@ -486,7 +486,7 @@ func (client *testClient) newRawPeer(t *testing.T, name string, version int, rec head = client.handler.backend.blockchain.CurrentHeader() td = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64()) ) - forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64()) + forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time) tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default // Ensure the connection is established or exits when any error occurs @@ -550,7 +550,7 @@ func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*t head = server.handler.blockchain.CurrentHeader() td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) ) - forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64()) + forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time) tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID) // Ensure the connection is established or exits when any error occurs diff --git a/params/config.go b/params/config.go index 93a939f58..4a52df94a 100644 --- a/params/config.go +++ b/params/config.go @@ -27,6 +27,8 @@ import ( "github.com/morph-l2/go-ethereum/rollup/rcfg" ) +func NewUint64(val uint64) *uint64 { return &val } + // Genesis hashes to enforce below configs on. var ( MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")