From fcf3d00488e471bcaf069862c6d811490d77b53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 3 Jan 2023 13:06:32 +0200 Subject: [PATCH] eth, les: polish forkid a bit, fix races and transition validation --- core/forkid/forkid.go | 128 ++++---- core/forkid/forkid_test.go | 553 ++++++++++++++++----------------- core/headerchain.go | 8 +- eth/handler.go | 2 +- eth/protocols/eth/discovery.go | 3 +- les/server_handler.go | 2 +- 6 files changed, 339 insertions(+), 357 deletions(-) diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index b35fd92b2a..300dca6cfb 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -45,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. @@ -72,8 +78,8 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) I hash := crc32.ChecksumIEEE(genesis[:]) // Calculate the current fork checksum and the next fork block - forks, forksByTime := gatherForks(config) - for _, fork := range forks { + 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) @@ -81,26 +87,26 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) I } return ID{Hash: checksumToBytes(hash), Next: fork} } - var next uint64 for _, fork := range forksByTime { - if time >= fork { - // Fork passed, checksum previous hash and fork time + if fork <= time { + // Fork already passed, checksum the previous hash and fork timestamp hash = checksumUpdate(hash, fork) continue } - next = fork - break + 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(), - chain.CurrentHeader().Time, + head.Number.Uint64(), + head.Time, ) } @@ -111,7 +117,8 @@ func NewFilter(chain Blockchain) Filter { chain.Config(), chain.Genesis().Hash(), func() (uint64, uint64) { - return chain.CurrentHeader().Number.Uint64(), chain.CurrentHeader().Time + head := chain.CurrentHeader() + return head.Number.Uint64(), head.Time }, ) } @@ -128,23 +135,23 @@ func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) 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, forksByTime = gatherForks(config) - sums = make([][4]byte, len(forks)+len(forksByTime)+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 ) - allForks := append(forks, forksByTime...) hash := crc32.ChecksumIEEE(genesis[:]) sums[0] = checksumToBytes(hash) - for i, fork := range allForks { + for i, fork := range forks { hash = checksumUpdate(hash, fork) sums[i+1] = checksumToBytes(hash) } // 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 { - forks = append(forks, math.MaxUint64) + // In purely block based forks, avoid the sentry spilling into timestapt territory + forksByBlock = append(forksByBlock, math.MaxUint64) // Last fork will never be passed } - forksByTime = append(forksByTime, 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: @@ -166,14 +173,24 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u // the remote, but at this current point in time we don't have enough // information. // 4. Reject in all other cases. - - verify := func(index int, headOrTime uint64) error { + 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 { + continue + } // Found the first unpassed fork block, check if our current state matches // the remote checksum (rule #1). - if sums[index] == id.Hash { + 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 && headOrTime >= 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). @@ -181,10 +198,10 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u } // The local and remote nodes are in different forks currently, check if the // remote checksum is a subset of our local forks (rule #2). - for j := 0; j < index; j++ { + for j := 0; j < i; j++ { if sums[j] == id.Hash { // Remote checksum is a subset, validate based on the announced next fork - if allForks[j] != id.Next { + if forks[j] != id.Next { return ErrRemoteStale } return nil @@ -192,7 +209,7 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u } // Remote chain is not a subset of our local one, check if it's a superset by // any chance, signalling that we're simply out of sync (rule #3). - for j := index + 1; j < len(sums); j++ { + for j := i + 1; j < len(sums); j++ { if sums[j] == id.Hash { // Yay, remote checksum is a superset, ignore upcoming forks return nil @@ -201,27 +218,6 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u // No exact, subset or superset match. We are on differing chains, reject. return ErrLocalIncompatibleOrStale } - - head, time := headfn() - // Verify forks by block - for i, fork := range forks { - // 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 { - continue - } - return verify(i, head) - } - // Verify forks by time - for i, fork := range forksByTime { - // 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 time >= fork { - continue - } - return verify(len(forks)+i, time) - } - log.Error("Impossible fork ID validation", "id", id) return nil // Something's very wrong, accept rather than reject } @@ -242,45 +238,45 @@ func checksumToBytes(hash uint32) [4]byte { return blob } -// gatherForks gathers all the known forks and creates a sorted list out of them. +// 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 - var forksByTime []uint64 + 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) - time := false - if !strings.HasSuffix(field.Name, "Block") { - if !strings.HasSuffix(field.Name, "Time") { - continue - } - time = true + + 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 and aggregate it + // Extract the fork rule block number or timestamp and aggregate it rule := conf.Field(i).Interface().(*big.Int) if rule != nil { if time { forksByTime = append(forksByTime, rule.Uint64()) } else { - forks = append(forks, rule.Uint64()) + forksByBlock = append(forksByBlock, rule.Uint64()) } } } - - sort.Slice(forks, func(i, j int) bool { return forks[i] < forks[j] }) + sort.Slice(forksByBlock, func(i, j int) bool { return forksByBlock[i] < forksByBlock[j] }) sort.Slice(forksByTime, func(i, j int) bool { return forksByTime[i] < forksByTime[j] }) - // 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:]...) + // 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-- } } @@ -291,11 +287,11 @@ func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) { } } // 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, forksByTime + return forksByBlock, forksByTime } diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 0158545f34..4e8b4f4111 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -30,177 +30,10 @@ import ( // TestCreation tests that different genesis and fork rule combinations result in // the correct fork ID. func TestCreation(t *testing.T) { - mergeConfig := *params.MainnetChainConfig - mergeConfig.MergeNetsplitBlock = big.NewInt(18000000) - type testcase struct { - head uint64 - want ID - } - tests := []struct { - config *params.ChainConfig - genesis common.Hash - cases []testcase - }{ - // Mainnet test cases - { - params.MainnetChainConfig, - params.MainnetGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced - {1149999, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block - {1150000, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block - {1919999, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block - {1920000, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block - {2462999, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block - {2463000, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block - {2674999, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block - {2675000, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block - {4369999, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block - {4370000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block - {7280000, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block - {9068999, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block - {9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block - {9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block - {9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block - {12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block - {12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block - {12964999, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block - {12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block - {13772999, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block - {13773000, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // First Arrow Glacier block - {15049999, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block - {15050000, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}}, // First Gray Glacier block - {20000000, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}}, // Future Gray Glacier block - }, - }, - // Ropsten test cases - { - params.RopstenChainConfig, - params.RopstenGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Unsynced, last Frontier, Homestead and first Tangerine block - {9, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Last Tangerine block - {10, ID{Hash: checksumToBytes(0x63760190), Next: 1700000}}, // First Spurious block - {1699999, ID{Hash: checksumToBytes(0x63760190), Next: 1700000}}, // Last Spurious block - {1700000, ID{Hash: checksumToBytes(0x3ea159c7), Next: 4230000}}, // First Byzantium block - {4229999, ID{Hash: checksumToBytes(0x3ea159c7), Next: 4230000}}, // Last Byzantium block - {4230000, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // First Constantinople block - {4939393, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // Last Constantinople block - {4939394, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // First Petersburg block - {6485845, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // Last Petersburg block - {6485846, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // First Istanbul block - {7117116, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // Last Istanbul block - {7117117, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // First Muir Glacier block - {9812188, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // Last Muir Glacier block - {9812189, ID{Hash: checksumToBytes(0xa157d377), Next: 10499401}}, // First Berlin block - {10499400, ID{Hash: checksumToBytes(0xa157d377), Next: 10499401}}, // Last Berlin block - {10499401, ID{Hash: checksumToBytes(0x7119b6b3), Next: 0}}, // First London block - {11000000, ID{Hash: checksumToBytes(0x7119b6b3), Next: 0}}, // Future London block - }, - }, - // Rinkeby test cases - { - params.RinkebyChainConfig, - params.RinkebyGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0x3b8e0691), Next: 1}}, // Unsynced, last Frontier block - {1, ID{Hash: checksumToBytes(0x60949295), Next: 2}}, // First and last Homestead block - {2, ID{Hash: checksumToBytes(0x8bde40dd), Next: 3}}, // First and last Tangerine block - {3, ID{Hash: checksumToBytes(0xcb3a64bb), Next: 1035301}}, // First Spurious block - {1035300, ID{Hash: checksumToBytes(0xcb3a64bb), Next: 1035301}}, // Last Spurious block - {1035301, ID{Hash: checksumToBytes(0x8d748b57), Next: 3660663}}, // First Byzantium block - {3660662, ID{Hash: checksumToBytes(0x8d748b57), Next: 3660663}}, // Last Byzantium block - {3660663, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // First Constantinople block - {4321233, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // Last Constantinople block - {4321234, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // First Petersburg block - {5435344, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // Last Petersburg block - {5435345, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // First Istanbul block - {8290927, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // Last Istanbul block - {8290928, ID{Hash: checksumToBytes(0x6910c8bd), Next: 8897988}}, // First Berlin block - {8897987, ID{Hash: checksumToBytes(0x6910c8bd), Next: 8897988}}, // Last Berlin block - {8897988, ID{Hash: checksumToBytes(0x8E29F2F3), Next: 0}}, // First London block - {10000000, ID{Hash: checksumToBytes(0x8E29F2F3), Next: 0}}, // Future London block - }, - }, - // Goerli test cases - { - params.GoerliChainConfig, - params.GoerliGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block - {1561650, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block - {1561651, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block - {4460643, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block - {4460644, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block - {5000000, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block - {5062605, ID{Hash: checksumToBytes(0xB8C6299D), Next: 0}}, // First London block - {6000000, ID{Hash: checksumToBytes(0xB8C6299D), Next: 0}}, // Future London block - }, - }, - // Sepolia test cases - { - params.SepoliaChainConfig, - params.SepoliaGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block - {1735370, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block - {1735371, ID{Hash: checksumToBytes(0xb96cbd13), Next: 0}}, // First MergeNetsplit block - }, - }, - // Merge test cases - { - &mergeConfig, - params.MainnetGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced - {1149999, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block - {1150000, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block - {1919999, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block - {1920000, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block - {2462999, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block - {2463000, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block - {2674999, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block - {2675000, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block - {4369999, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block - {4370000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block - {7280000, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block - {9068999, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block - {9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block - {9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block - {9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block - {12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block - {12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block - {12964999, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block - {12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block - {13772999, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block - {13773000, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // First Arrow Glacier block - {15049999, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block - {15050000, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 18000000}}, // First Gray Glacier block - {18000000, ID{Hash: checksumToBytes(0x4fb8a872), Next: 0}}, // First Merge Start block - {20000000, ID{Hash: checksumToBytes(0x4fb8a872), Next: 0}}, // Future Merge Start block - }, - }, - } - for i, tt := range tests { - for j, ttt := range tt.cases { - 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) - } - } - } -} + // Temporary non-existent scenario TODO(karalabe): delete when Shanghai is enabled + timestampedConfig := *params.MainnetChainConfig + timestampedConfig.ShanghaiTime = big.NewInt(1668000000) -// TestCreationWithTimestamps tests that different genesis and fork rule combinations result in -// the correct fork ID even for time based forks. -func TestCreationWithTimestamps(t *testing.T) { - mergeConfig := *params.MainnetChainConfig - mergeConfig.MergeNetsplitBlock = big.NewInt(18000000) - - withdrawalConfig := *params.MainnetChainConfig - withdrawalConfig.MergeNetsplitBlock = big.NewInt(18000000) - withdrawalConfig.ShanghaiTime = big.NewInt(1668000000) type testcase struct { head uint64 time uint64 @@ -244,40 +77,113 @@ func TestCreationWithTimestamps(t *testing.T) { {20000000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}}, // Future Gray Glacier block }, }, - // Withdrawal test cases + // Ropsten test cases + { + params.RopstenChainConfig, + params.RopstenGenesisHash, + []testcase{ + {0, 0, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Unsynced, last Frontier, Homestead and first Tangerine block + {9, 0, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Last Tangerine block + {10, 0, ID{Hash: checksumToBytes(0x63760190), Next: 1700000}}, // First Spurious block + {1699999, 0, ID{Hash: checksumToBytes(0x63760190), Next: 1700000}}, // Last Spurious block + {1700000, 0, ID{Hash: checksumToBytes(0x3ea159c7), Next: 4230000}}, // First Byzantium block + {4229999, 0, ID{Hash: checksumToBytes(0x3ea159c7), Next: 4230000}}, // Last Byzantium block + {4230000, 0, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // First Constantinople block + {4939393, 0, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // Last Constantinople block + {4939394, 0, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // First Petersburg block + {6485845, 0, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // Last Petersburg block + {6485846, 0, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // First Istanbul block + {7117116, 0, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // Last Istanbul block + {7117117, 0, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // First Muir Glacier block + {9812188, 0, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // Last Muir Glacier block + {9812189, 0, ID{Hash: checksumToBytes(0xa157d377), Next: 10499401}}, // First Berlin block + {10499400, 0, ID{Hash: checksumToBytes(0xa157d377), Next: 10499401}}, // Last Berlin block + {10499401, 0, ID{Hash: checksumToBytes(0x7119b6b3), Next: 0}}, // First London block + {11000000, 0, ID{Hash: checksumToBytes(0x7119b6b3), Next: 0}}, // Future London block + }, + }, + // Rinkeby test cases + { + params.RinkebyChainConfig, + params.RinkebyGenesisHash, + []testcase{ + {0, 0, ID{Hash: checksumToBytes(0x3b8e0691), Next: 1}}, // Unsynced, last Frontier block + {1, 0, ID{Hash: checksumToBytes(0x60949295), Next: 2}}, // First and last Homestead block + {2, 0, ID{Hash: checksumToBytes(0x8bde40dd), Next: 3}}, // First and last Tangerine block + {3, 0, ID{Hash: checksumToBytes(0xcb3a64bb), Next: 1035301}}, // First Spurious block + {1035300, 0, ID{Hash: checksumToBytes(0xcb3a64bb), Next: 1035301}}, // Last Spurious block + {1035301, 0, ID{Hash: checksumToBytes(0x8d748b57), Next: 3660663}}, // First Byzantium block + {3660662, 0, ID{Hash: checksumToBytes(0x8d748b57), Next: 3660663}}, // Last Byzantium block + {3660663, 0, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // First Constantinople block + {4321233, 0, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // Last Constantinople block + {4321234, 0, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // First Petersburg block + {5435344, 0, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // Last Petersburg block + {5435345, 0, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // First Istanbul block + {8290927, 0, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // Last Istanbul block + {8290928, 0, ID{Hash: checksumToBytes(0x6910c8bd), Next: 8897988}}, // First Berlin block + {8897987, 0, ID{Hash: checksumToBytes(0x6910c8bd), Next: 8897988}}, // Last Berlin block + {8897988, 0, ID{Hash: checksumToBytes(0x8E29F2F3), Next: 0}}, // First London block + {10000000, 0, ID{Hash: checksumToBytes(0x8E29F2F3), Next: 0}}, // Future London block + }, + }, + // Goerli test cases { - &withdrawalConfig, + params.GoerliChainConfig, + params.GoerliGenesisHash, + []testcase{ + {0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block + {1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block + {1561651, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block + {4460643, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block + {4460644, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block + {5000000, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block + {5062605, 0, ID{Hash: checksumToBytes(0xB8C6299D), Next: 0}}, // First London block + {6000000, 0, ID{Hash: checksumToBytes(0xB8C6299D), Next: 0}}, // Future London block + }, + }, + // Sepolia test cases + { + params.SepoliaChainConfig, + params.SepoliaGenesisHash, + []testcase{ + {0, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block + {1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block + {1735371, 0, ID{Hash: checksumToBytes(0xb96cbd13), Next: 0}}, // First MergeNetsplit block + }, + }, + // Temporary timestamped test cases + { + ×tampedConfig, params.MainnetGenesisHash, []testcase{ - {0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced - {1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block - {1150000, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block - {1919999, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block - {1920000, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block - {2462999, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block - {2463000, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block - {2674999, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block - {2675000, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block - {4369999, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block - {4370000, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block - {7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block - {7280000, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block - {9068999, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block - {9069000, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block - {9199999, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block - {9200000, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block - {12243999, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block - {12244000, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block - {12964999, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block - {12965000, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block - {13772999, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block - {13773000, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // First Arrow Glacier block - {15049999, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block - {15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 18000000}}, // First Gray Glacier block - {18000000, 0, ID{Hash: checksumToBytes(0x4fb8a872), Next: 1668000000}}, // First Merge Start block - {20000000, 0, ID{Hash: checksumToBytes(0x4fb8a872), Next: 1668000000}}, // Last Merge Start block - {20000000, 1668000000, ID{Hash: checksumToBytes(0xc1fdf181), Next: 0}}, // First Merge Start block - {20000000, 2668000000, ID{Hash: checksumToBytes(0xc1fdf181), Next: 0}}, // Future Merge Start block + {0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced + {1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block + {1150000, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block + {1919999, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block + {1920000, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block + {2462999, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block + {2463000, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block + {2674999, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block + {2675000, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block + {4369999, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block + {4370000, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block + {7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block + {7280000, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block + {9068999, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block + {9069000, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block + {9199999, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block + {9200000, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block + {12243999, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block + {12244000, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block + {12964999, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block + {12965000, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block + {13772999, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block + {13773000, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // First Arrow Glacier block + {15049999, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block + {15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1668000000}}, // First Gray Glacier block + {19999999, 1667999999, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1668000000}}, // Last Gray Glacier block + {20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}}, // First Shanghai block + {20000000, 2668000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}}, // Future Shanghai block }, }, } @@ -293,165 +199,244 @@ func TestCreationWithTimestamps(t *testing.T) { // TestValidation tests that a local peer correctly validates and accepts a remote // fork ID. func TestValidation(t *testing.T) { + // Temporary non-existent scenario TODO(karalabe): delete when Shanghai is enabled + timestampedConfig := *params.MainnetChainConfig + timestampedConfig.ShanghaiTime = big.NewInt(1668000000) + tests := []struct { - head uint64 - id ID - err error + config *params.ChainConfig + head uint64 + time uint64 + 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}, + //------------------ + // Block based tests + //------------------ + + // Local is mainnet Gray Glacier, remote announces the same. No future fork is announced. + {params.MainnetChainConfig, 15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}, nil}, - // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork + // Local is mainnet Gray Glacier, 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}, + {params.MainnetChainConfig, 15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), 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}, + {params.MainnetChainConfig, 7279999, 0, 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}, + {params.MainnetChainConfig, 7279999, 0, 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}, + {params.MainnetChainConfig, 7279999, 0, 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}, + {params.MainnetChainConfig, 7280000, 0, 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}, + {params.MainnetChainConfig, 7987396, 0, 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}, + {params.MainnetChainConfig, 7987396, 0, 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}, + {params.MainnetChainConfig, 7279999, 0, 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}, + {params.MainnetChainConfig, 4369999, 0, 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}, + {params.MainnetChainConfig, 7987396, 0, 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}, + {params.MainnetChainConfig, 7987396, 0, 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}, + {params.MainnetChainConfig, 7279999, 0, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, // Local is mainnet Petersburg, remote is Rinkeby Petersburg. - {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, + {params.MainnetChainConfig, 7987396, 0, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, // Local is mainnet Gray 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(0xf0afd0e3), Next: 88888888}, ErrLocalIncompatibleOrStale}, + // + // TODO(karalabe): This testcase will fail once mainnet gets timestamped forks, make legacy chain config + {params.MainnetChainConfig, 88888888, 0, ID{Hash: checksumToBytes(0xf0afd0e3), 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}, - } - for i, tt := range tests { - 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) - } - } -} + // + // TODO(karalabe): This testcase will fail once mainnet gets timestamped forks, make legacy chain config + {params.MainnetChainConfig, 7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, -// TestValidationByTimestamp tests that a local peer correctly validates and accepts a remote -// fork ID. -func TestValidationByTimestamp(t *testing.T) { - withdrawalConfig := *params.MainnetChainConfig - withdrawalConfig.MergeNetsplitBlock = big.NewInt(18000000) - withdrawalConfig.ShanghaiTime = big.NewInt(1668000000) - tests := []struct { - head uint64 - time uint64 - id ID - err error - }{ - // Local is mainnet Withdrawals, remote announces the same. No future fork is announced. - {20000000, 1668000001, ID{Hash: checksumToBytes(0xc1fdf181), Next: 0}, nil}, + //------------------------------------ + // Block to timestamp transition tests + //------------------------------------ - // Local is mainnet Withdrawals, remote announces the same also announces a next fork - // at block/time 0xffffffff, but that is uncertain. - {20000000, 1668000001, ID{Hash: checksumToBytes(0xc1fdf181), Next: math.MaxUint64}, nil}, + // Local is mainnet currently in Gray Glacier only (so it's aware of Shanghai), remote announces + // also Gray Glacier, but it's not yet aware of Shanghai (e.g. non updated node before the fork). + // In this case we don't know if Shanghai passed yet or not. + {×tampedConfig, 15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}, nil}, - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg & Withdrawals), 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, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, + // Local is mainnet currently in Gray Glacier only (so it's aware of Shanghai), remote announces + // also Gray Glacier, and it's also aware of Shanghai (e.g. updated node before the fork). We + // don't know if Shanghai passed yet (will pass) or not. + {×tampedConfig, 15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1668000000}, nil}, - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg & Withdrawals), 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, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // Local is mainnet currently in Gray Glacier only (so it's aware of Shanghai), remote announces + // also Gray Glacier, and it's also aware of some random fork (e.g. misconfigured Shanghai). As + // neither forks passed at neither nodes, they may mismatch, but we still connect for now. + {×tampedConfig, 15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: math.MaxUint64}, nil}, - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg & Withdrawals), remote announces - // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As + // Local is mainnet exactly on Shanghai, remote announces Gray Glacier + knowledge about Shanghai. Remote + // is simply out of sync, accept. + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1668000000}, nil}, + + // Local is mainnet Shanghai, remote announces Gray Glacier + knowledge about Shanghai. Remote + // is simply out of sync, accept. + {×tampedConfig, 20123456, 1668123456, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1668000000}, nil}, + + // Local is mainnet Shanghai, remote announces Arrow Glacier + knowledge about Gray Glacier. Remote + // is definitely out of sync. It may or may not need the Shanghai update, we don't know yet. + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}, nil}, + + // Local is mainnet Gray Glacier, remote announces Shanghai. Local is out of sync, accept. + {×tampedConfig, 15050000, 0, ID{Hash: checksumToBytes(0x71147644), Next: 0}, nil}, + + // Local is mainnet Arrow Glacier, remote announces Gray Glacier, but is not aware of Shanghai. Local + // out of sync. Local also knows about a future fork, but that is uncertain yet. + {×tampedConfig, 13773000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}, nil}, + + // Local is mainnet Shanghai. remote announces Gray Glacier but is not aware of further forks. + // Remote needs software update. + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 0}, ErrRemoteStale}, + + // Local is mainnet Gray Glacier, and isn't aware of more forks. Remote announces Gray Glacier + + // 0xffffffff. Local needs software update, reject. + {×tampedConfig, 15050000, 0, ID{Hash: checksumToBytes(checksumUpdate(0xf0afd0e3, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Gray Glacier, and is aware of Shanghai. Remote announces Shanghai + + // 0xffffffff. Local needs software update, reject. + {×tampedConfig, 15050000, 0, ID{Hash: checksumToBytes(checksumUpdate(0x71147644, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Gray Glacier, far in the future. Remote announces Gopherium (non existing fork) + // at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible. + // + // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). + {params.MainnetChainConfig, 888888888, 1660000000, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1660000000}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Gray Glacier. Remote is also in Gray Glacier, but announces Gopherium (non existing + // fork) at block 7279999, before Shanghai. Local is incompatible. + {×tampedConfig, 19999999, 1667999999, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1667999999}, ErrLocalIncompatibleOrStale}, + + //---------------------- + // Timestamp based tests + //---------------------- + + // Local is mainnet Shanghai, remote announces the same. No future fork is announced. + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, nil}, + + // Local is mainnet Shanghai, remote announces the same. Remote also announces a next fork + // at time 0xffffffff, but that is uncertain. + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: math.MaxUint64}, nil}, + + // Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces + // also Shanghai, but it's not yet aware of Cancun (e.g. non updated node before the fork). + // In this case we don't know if Cancun passed yet or not. + // + // TODO(karalabe): Enable this when Cancun is specced + //{×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, nil}, + + // Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces + // also Shanghai, and it's also aware of Cancun (e.g. updated node before the fork). We + // don't know if Cancun passed yet (will pass) or not. + // + // TODO(karalabe): Enable this when Cancun is specced and update next timestamp + //{×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil}, + + // Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces + // also Shanghai, and it's also aware of some random fork (e.g. misconfigured Cancun). As // neither forks passed at neither nodes, they may mismatch, but we still connect for now. - {7279999, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil}, + // + // TODO(karalabe): Enable this when Cancun is specced + //{×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: math.MaxUint64}, nil}, - // Local is mainnet exactly on Withdrawals, remote announces Byzantium + knowledge about Petersburg. Remote + // Local is mainnet exactly on Cancun, remote announces Shanghai + knowledge about Cancun. Remote // is simply out of sync, accept. - {20000000, 1668000000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // + // TODO(karalabe): Enable this when Cancun is specced, update local head and time, next timestamp + // {×tampedConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil}, - // Local is mainnet Withdrawals, remote announces Byzantium + knowledge about Petersburg. Remote + // Local is mainnet Cancun, remote announces Shanghai + knowledge about Cancun. Remote // is simply out of sync, accept. - {20000000, 1668000001, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, + // TODO(karalabe): Enable this when Cancun is specced, update local head and time, next timestamp + //{×tampedConfig, 21123456, 1678123456, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil}, - // Local is mainnet Withdrawals, 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. - {20000000, 1668000001, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, + // Local is mainnet Osaka, remote announces Shanghai + knowledge about Cancun. Remote + // is definitely out of sync. It may or may not need the Osaka update, we don't know yet. + // + // TODO(karalabe): Enable this when Cancun **and** Osaka is specced, update all the numbers + //{×tampedConfig, 0, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, - // Local is mainnet Byzantium & pre-withdrawals, remote announces Petersburg. Local is out of sync, accept. - {7279999, 1667999999, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, + // Local is mainnet Shanghai, remote announces Cancun. Local is out of sync, accept. + // + // TODO(karalabe): Enable this when Cancun is specced, update remote checksum + //{×tampedConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x00000000), Next: 0}, nil}, - // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local + // Local is mainnet Shanghai, remote announces Cancun, but is not aware of Osaka. Local // out of sync. Local also knows about a future fork, but that is uncertain yet. - {4369999, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, + // + // TODO(karalabe): Enable this when Cancun **and** Osaka is specced, update remote checksum + //{×tampedConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x00000000), Next: 0}, nil}, - // Local is mainnet Withdrawals. remote announces Byzantium but is not aware of further forks. + // Local is mainnet Cancun. remote announces Shanghai but is not aware of further forks. // Remote needs software update. - {20000000, 1668000001, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale}, + // + // TODO(karalabe): Enable this when Cancun is specced, update local head and time + //{×tampedConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, ErrRemoteStale}, - // Local is mainnet Withdrawals, and isn't aware of more forks. Remote announces Petersburg + + // Local is mainnet Shanghai, and isn't aware of more forks. Remote announces Shanghai + // 0xffffffff. Local needs software update, reject. - {20000000, 1668000001, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(checksumUpdate(0x71147644, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Withdrawals, and is aware of Petersburg. Remote announces Petersburg + + // Local is mainnet Shanghai, and is aware of Cancun. Remote announces Cancun + // 0xffffffff. Local needs software update, reject. - {20000000, 1668000001, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, + // + // TODO(karalabe): Enable this when Cancun is specced, update remote checksum + //{×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(checksumUpdate(0x00000000, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Withdrawals, remote is Rinkeby Petersburg. - {20000000, 1668000001, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, + // Local is mainnet Shanghai, remote is random Shanghai. + {×tampedConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x12345678), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Withdrawals, 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. + // Local is mainnet Shanghai, far in the future. Remote announces Gopherium (non existing fork) + // at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible. // // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - {88888888, 1668000001, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 88888888}, ErrRemoteStale}, + {×tampedConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0x71147644), Next: 8888888888}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Withdrawals. Remote is in Byzantium, but announces Gopherium (non existing - // fork) at block 7279999, before Petersburg. Local is incompatible. - {20000000, 1668000001, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrRemoteStale}, + // Local is mainnet Shanghai. Remote is also in Shanghai, but announces Gopherium (non existing + // fork) at timestamp 1668000000, before Cancun. Local is incompatible. + // + // TODO(karalabe): Enable this when Cancun is specced + //{params.MainnetChainConfig, 20999999, 1677999999, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, ErrLocalIncompatibleOrStale}, } for i, tt := range tests { - filter := newFilter(&withdrawalConfig, params.MainnetGenesisHash, func() (uint64, uint64) { return tt.head, tt.time }) + filter := newFilter(tt.config, params.MainnetGenesisHash, 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/headerchain.go b/core/headerchain.go index ba8f550a8c..b76af1345c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -584,10 +584,10 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat origin = true ) done := func(header *types.Header) bool { - if headBlock != 0 || headTime == 0 { - return header.Number.Uint64() <= headBlock + if headTime > 0 { + return header.Time <= headTime } - return header.Time <= headTime + return header.Number.Uint64() <= headBlock } for hdr := hc.CurrentHeader(); hdr != nil && !done(hdr); hdr = hc.CurrentHeader() { num := hdr.Number.Uint64() @@ -611,7 +611,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat newHead, force := updateFn(markerBatch, parent) if force && ((headTime > 0 && newHead.Time < headTime) || (headTime == 0 && newHead.Number.Uint64() < headBlock)) { log.Warn("Force rewinding till ancient limit", "head", newHead.Number.Uint64()) - headBlock, headTime = newHead.Number.Uint64(), 0 + headBlock, headTime = newHead.Number.Uint64(), 0 // Target timestamp passed, continue rewind in block mode (cleaner) } } // Update head header then. diff --git a/eth/handler.go b/eth/handler.go index c67a62b867..078133f059 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -331,7 +331,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(), h.chain.CurrentHeader().Time) + forkID := forkid.NewID(h.chain.Config(), genesis.Hash(), number, head.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 345c6e5e27..87857244b5 100644 --- a/eth/protocols/eth/discovery.go +++ b/eth/protocols/eth/discovery.go @@ -59,7 +59,8 @@ 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 { + head := chain.CurrentHeader() return &enrEntry{ - ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64(), chain.CurrentHeader().Time), + ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), head.Number.Uint64(), head.Time), } } diff --git a/les/server_handler.go b/les/server_handler.go index b9fb1d19bf..2ea496ac2c 100644 --- a/les/server_handler.go +++ b/les/server_handler.go @@ -117,7 +117,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(), h.blockchain.CurrentBlock().Time()) + forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), number, head.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)