From 775658ee87cecc39cb680a7f23aa98b29a11ce88 Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Fri, 7 Mar 2025 22:31:52 +0800 Subject: [PATCH 1/8] implement forkid changes for time base forks --- cmd/devp2p/internal/ethtest/chain.go | 2 +- core/forkid/forkid.go | 123 ++++++++++++++++++--------- core/forkid/forkid_test.go | 48 ++++++++++- eth/discovery.go | 2 +- eth/handler.go | 2 +- eth/protocols/eth/discovery.go | 2 +- eth/protocols/eth/handshake_test.go | 2 +- les/client_handler.go | 2 +- les/peer_test.go | 4 +- les/server_handler.go | 2 +- les/test_helper.go | 4 +- params/config.go | 2 + 12 files changed, 141 insertions(+), 54 deletions(-) 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..c9dd2036e 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 Ethereum 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 = 1438269973 + // 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..11b0170a8 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,47 @@ 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-09-09 00:00:00 UTC + + tests := []struct { + config *params.ChainConfig + head uint64 + time uint64 + id ID + err error + }{ + //------------------ + // Block based tests + //------------------ + + // Local is mainnet Gray Glacier, remote announces the same. No future fork is announced. + {&legacyConfig, 0, 0, ID{Hash: checksumToBytes(0xb0709522), Next: 0}, nil}, + + //------------------ + // Timestamp based tests + //------------------ + + // 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}, + + {&morphMainnetConfig, 6656942, morph203Time + 1, ID{Hash: checksumToBytes(0xb0709522), Next: morph203Time - 1}, ErrRemoteStale}, + } + + 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/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") From 69309a599b0ec066daf1f30ffbabb59703cdb239 Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Fri, 7 Mar 2025 22:45:07 +0800 Subject: [PATCH 2/8] pre-compile contracts add fork check --- core/vm/contracts.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) 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{} From ee55b8cbefc5549d30083c2680dd59a2aa97ce62 Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Sat, 8 Mar 2025 08:16:41 +0800 Subject: [PATCH 3/8] more forkid test cases --- core/forkid/forkid_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 11b0170a8..b2d9fb961 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -271,16 +271,25 @@ func TestTimeBasedForkInGenesis(t *testing.T) { // Local is mainnet Gray Glacier, 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}, } From 20cd52d0dffcd1a1487a43f280e8215343e2e30a Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Sat, 8 Mar 2025 08:18:23 +0800 Subject: [PATCH 4/8] fix typo --- core/forkid/forkid_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index b2d9fb961..68abdd1f5 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -268,7 +268,7 @@ func TestTimeBasedForkInGenesis(t *testing.T) { // Block based tests //------------------ - // Local is mainnet Gray Glacier, remote announces the same. No future fork is announced. + // 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}, From bf37518f984b4244e5b66d6709db4622104ea0fd Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:15:54 +0800 Subject: [PATCH 5/8] use morph genesis timestamp --- core/forkid/forkid.go | 2 +- core/forkid/forkid_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index c9dd2036e..d8f70cb87 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -49,7 +49,7 @@ var ( // 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 = 1438269973 +const timestampThreshold = 1729490400 // Blockchain defines all necessary method to build a forkID. type Blockchain interface { diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 68abdd1f5..f6583f116 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -255,7 +255,7 @@ func TestTimeBasedForkInGenesis(t *testing.T) { morphMainnetConfig := *params.MorphMainnetChainConfig morph203Time := uint64(1741579200) - morphMainnetConfig.Morph203Time = params.NewUint64(morph203Time) // 2025-09-09 00:00:00 UTC + morphMainnetConfig.Morph203Time = params.NewUint64(morph203Time) // 2025-03-10 04:00:00 UTC tests := []struct { config *params.ChainConfig From 7a3068a0dc315f2b036abafe614a104c9c4b9d0b Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:20:43 +0800 Subject: [PATCH 6/8] fix comment typo --- core/forkid/forkid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index d8f70cb87..e4036a740 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -45,7 +45,7 @@ var ( ErrLocalIncompatibleOrStale = errors.New("local incompatible or needs update") ) -// timestampThreshold is the Ethereum mainnet genesis timestamp. It is used to +// 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). From 153b32e1803b446dc6427118eb17e45a71debd5f Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Sat, 8 Mar 2025 21:20:56 +0800 Subject: [PATCH 7/8] more test cases --- core/forkid/forkid_test.go | 99 ++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index f6583f116..9e45d74fd 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -151,71 +151,71 @@ func TestValidation(t *testing.T) { id ID err error }{ - // Local is mainnet Petersburg, remote announces the same. No future fork is announced. - {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, + // // Local is mainnet Petersburg, remote announces the same. No future fork is announced. + // {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, - // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork - // at block 0xffffffff, but that is uncertain. - {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: math.MaxUint64}, nil}, + // // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork + // // at block 0xffffffff, but that is uncertain. + // {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: math.MaxUint64}, nil}, - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). - // In this case we don't know if Petersburg passed yet or not. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, + // // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). + // // In this case we don't know if Petersburg passed yet or not. + // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We - // don't know if Petersburg passed yet (will pass) or not. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We + // // don't know if Petersburg passed yet (will pass) or not. + // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As - // neither forks passed at neither nodes, they may mismatch, but we still connect for now. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil}, + // // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As + // // neither forks passed at neither nodes, they may mismatch, but we still connect for now. + // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil}, - // Local is mainnet exactly on Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote - // is simply out of sync, accept. - {7280000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // // Local is mainnet exactly on Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote + // // is simply out of sync, accept. + // {7280000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote - // is simply out of sync, accept. - {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote + // // is simply out of sync, accept. + // {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote - // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. - {7987396, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, + // // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote + // // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. + // {7987396, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, // Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. {7279999, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, - // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local - // out of sync. Local also knows about a future fork, but that is uncertain yet. - {4369999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, + // // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local + // // out of sync. Local also knows about a future fork, but that is uncertain yet. + // {4369999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, - // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. - // Remote needs software update. - {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale}, + // // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. + // // Remote needs software update. + // {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale}, - // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - {7987396, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, + // // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + + // // 0xffffffff. Local needs software update, reject. + // {7987396, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - {7279999, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, + // // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + + // // 0xffffffff. Local needs software update, reject. + // {7279999, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Petersburg, remote is Rinkeby Petersburg. - {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, + // // Local is mainnet Petersburg, remote is Rinkeby Petersburg. + // {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Arrow Glacier, far in the future. Remote announces Gopherium (non existing fork) - // at some future block 88888888, for itself, but past block for local. Local is incompatible. - // - // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - {88888888, ID{Hash: checksumToBytes(0x20c327fc), Next: 88888888}, ErrLocalIncompatibleOrStale}, + // // Local is mainnet Arrow Glacier, far in the future. Remote announces Gopherium (non existing fork) + // // at some future block 88888888, for itself, but past block for local. Local is incompatible. + // // + // // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). + // {88888888, ID{Hash: checksumToBytes(0x20c327fc), Next: 88888888}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing - // fork) at block 7279999, before Petersburg. Local is incompatible. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, + // // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing + // // fork) at block 7279999, before Petersburg. Local is incompatible. + // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, } for i, tt := range tests { filter := newFilter(params.MainnetChainConfig, params.MainnetGenesisHash, func() (uint64, uint64) { return tt.head, 0 }) @@ -291,6 +291,9 @@ func TestTimeBasedForkInGenesis(t *testing.T) { // 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 { From c4f3794813a9e3f05ab117be734da4f9b94a05b0 Mon Sep 17 00:00:00 2001 From: ryanmorphl2 <163962984+ryanmorphl2@users.noreply.github.com> Date: Sat, 8 Mar 2025 21:22:06 +0800 Subject: [PATCH 8/8] restore test cases --- core/forkid/forkid_test.go | 96 +++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 9e45d74fd..7696fd11f 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -151,71 +151,71 @@ func TestValidation(t *testing.T) { id ID err error }{ - // // Local is mainnet Petersburg, remote announces the same. No future fork is announced. - // {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, + // Local is mainnet Petersburg, remote announces the same. No future fork is announced. + {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, - // // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork - // // at block 0xffffffff, but that is uncertain. - // {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: math.MaxUint64}, nil}, + // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork + // at block 0xffffffff, but that is uncertain. + {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: math.MaxUint64}, nil}, - // // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). - // // In this case we don't know if Petersburg passed yet or not. - // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, + // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). + // In this case we don't know if Petersburg passed yet or not. + {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, - // // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We - // // don't know if Petersburg passed yet (will pass) or not. - // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We + // don't know if Petersburg passed yet (will pass) or not. + {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - // // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As - // // neither forks passed at neither nodes, they may mismatch, but we still connect for now. - // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil}, + // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As + // neither forks passed at neither nodes, they may mismatch, but we still connect for now. + {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil}, - // // Local is mainnet exactly on Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote - // // is simply out of sync, accept. - // {7280000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // Local is mainnet exactly on Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote + // is simply out of sync, accept. + {7280000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - // // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote - // // is simply out of sync, accept. - // {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote + // is simply out of sync, accept. + {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - // // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote - // // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. - // {7987396, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, + // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote + // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. + {7987396, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, // Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. {7279999, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, - // // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local - // // out of sync. Local also knows about a future fork, but that is uncertain yet. - // {4369999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, + // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local + // out of sync. Local also knows about a future fork, but that is uncertain yet. + {4369999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, - // // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. - // // Remote needs software update. - // {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale}, + // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. + // Remote needs software update. + {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale}, - // // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + - // // 0xffffffff. Local needs software update, reject. - // {7987396, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, + // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + + // 0xffffffff. Local needs software update, reject. + {7987396, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, - // // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + - // // 0xffffffff. Local needs software update, reject. - // {7279999, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, + // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + + // 0xffffffff. Local needs software update, reject. + {7279999, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, - // // Local is mainnet Petersburg, remote is Rinkeby Petersburg. - // {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, + // Local is mainnet Petersburg, remote is Rinkeby Petersburg. + {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, - // // Local is mainnet Arrow Glacier, far in the future. Remote announces Gopherium (non existing fork) - // // at some future block 88888888, for itself, but past block for local. Local is incompatible. - // // - // // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - // {88888888, ID{Hash: checksumToBytes(0x20c327fc), Next: 88888888}, ErrLocalIncompatibleOrStale}, + // Local is mainnet Arrow Glacier, far in the future. Remote announces Gopherium (non existing fork) + // at some future block 88888888, for itself, but past block for local. Local is incompatible. + // + // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). + {88888888, ID{Hash: checksumToBytes(0x20c327fc), Next: 88888888}, ErrLocalIncompatibleOrStale}, - // // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing - // // fork) at block 7279999, before Petersburg. Local is incompatible. - // {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, + // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing + // fork) at block 7279999, before Petersburg. Local is incompatible. + {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, } for i, tt := range tests { filter := newFilter(params.MainnetChainConfig, params.MainnetGenesisHash, func() (uint64, uint64) { return tt.head, 0 })