Skip to content

Commit c879bcc

Browse files
authored
Merge branch 'ethereum:master' into gethintegration
2 parents d207036 + 51b34ef commit c879bcc

35 files changed

+758
-663
lines changed

cmd/devp2p/internal/ethtest/suite.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func (s *Suite) EthTests() []utesting.Test {
7070
{Name: "Status", Fn: s.TestStatus},
7171
// get block headers
7272
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
73+
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
7374
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
7475
{Name: "SameRequestID", Fn: s.TestSameRequestID},
7576
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
@@ -158,6 +159,48 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
158159
}
159160
}
160161

162+
func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
163+
t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
164+
to check if the node disconnects after receiving multiple invalid requests.`)
165+
166+
conn, err := s.dial()
167+
if err != nil {
168+
t.Fatalf("dial failed: %v", err)
169+
}
170+
defer conn.Close()
171+
172+
if err := conn.peer(s.chain, nil); err != nil {
173+
t.Fatalf("peering failed: %v", err)
174+
}
175+
176+
// Create request with max uint64 value for a nonexistent block
177+
badReq := &eth.GetBlockHeadersPacket{
178+
GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
179+
Origin: eth.HashOrNumber{Number: ^uint64(0)},
180+
Amount: 1,
181+
Skip: 0,
182+
Reverse: false,
183+
},
184+
}
185+
186+
// Send request 10 times. Some clients are lient on the first few invalids.
187+
for i := 0; i < 10; i++ {
188+
badReq.RequestId = uint64(i)
189+
if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil {
190+
if err == errDisc {
191+
t.Fatalf("peer disconnected after %d requests", i+1)
192+
}
193+
t.Fatalf("write failed: %v", err)
194+
}
195+
}
196+
197+
// Check if peer disconnects at the end.
198+
code, _, err := conn.Read()
199+
if err == errDisc || code == discMsg {
200+
t.Fatal("peer improperly disconnected")
201+
}
202+
}
203+
161204
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
162205
t.Log(`This test requests blocks headers from the node, performing two requests
163206
concurrently, with different request IDs.`)

cmd/geth/consolecmd_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ const (
3939
// child g gets a temporary data directory.
4040
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
4141
// --holesky to make the 'writing genesis to disk' faster (no accounts)
42-
// --networkid=1337 to avoid cache bump
4342
// --syncmode=full to avoid allocating fast sync bloom
44-
allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
43+
allArgs := []string{"--holesky", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
4544
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
4645
"--datadir.minfreedisk", "0"}
4746
return runGeth(t, append(allArgs, args...)...)

cmd/utils/flags.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,8 +1571,8 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
15711571

15721572
// SetEthConfig applies eth-related command line flags to the config.
15731573
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
1574-
// Avoid conflicting network flags
1575-
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag)
1574+
// Avoid conflicting network flags, don't allow network id override on preset networks
1575+
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag, NetworkIdFlag)
15761576
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
15771577

15781578
// Set configurations from CLI flags
@@ -1743,33 +1743,23 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
17431743
// Override any default configs for hard coded networks.
17441744
switch {
17451745
case ctx.Bool(MainnetFlag.Name):
1746-
if !ctx.IsSet(NetworkIdFlag.Name) {
1747-
cfg.NetworkId = 1
1748-
}
1746+
cfg.NetworkId = 1
17491747
cfg.Genesis = core.DefaultGenesisBlock()
17501748
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
17511749
case ctx.Bool(HoleskyFlag.Name):
1752-
if !ctx.IsSet(NetworkIdFlag.Name) {
1753-
cfg.NetworkId = 17000
1754-
}
1750+
cfg.NetworkId = 17000
17551751
cfg.Genesis = core.DefaultHoleskyGenesisBlock()
17561752
SetDNSDiscoveryDefaults(cfg, params.HoleskyGenesisHash)
17571753
case ctx.Bool(SepoliaFlag.Name):
1758-
if !ctx.IsSet(NetworkIdFlag.Name) {
1759-
cfg.NetworkId = 11155111
1760-
}
1754+
cfg.NetworkId = 11155111
17611755
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
17621756
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
17631757
case ctx.Bool(HoodiFlag.Name):
1764-
if !ctx.IsSet(NetworkIdFlag.Name) {
1765-
cfg.NetworkId = 560048
1766-
}
1758+
cfg.NetworkId = 560048
17671759
cfg.Genesis = core.DefaultHoodiGenesisBlock()
17681760
SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash)
17691761
case ctx.Bool(DeveloperFlag.Name):
1770-
if !ctx.IsSet(NetworkIdFlag.Name) {
1771-
cfg.NetworkId = 1337
1772-
}
1762+
cfg.NetworkId = 1337
17731763
cfg.SyncMode = ethconfig.FullSync
17741764
// Create new developer account or reuse existing one
17751765
var (

core/blockchain_reader.go

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -270,42 +270,20 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
270270
// GetTransactionLookup retrieves the lookup along with the transaction
271271
// itself associate with the given transaction hash.
272272
//
273-
// An error will be returned if the transaction is not found, and background
274-
// indexing for transactions is still in progress. The transaction might be
275-
// reachable shortly once it's indexed.
276-
//
277-
// A null will be returned in the transaction is not found and background
278-
// transaction indexing is already finished. The transaction is not existent
279-
// from the node's perspective.
280-
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
273+
// A null will be returned if the transaction is not found. This can be due to
274+
// the transaction indexer not being finished. The caller must explicitly check
275+
// the indexer progress.
276+
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
281277
bc.txLookupLock.RLock()
282278
defer bc.txLookupLock.RUnlock()
283279

284280
// Short circuit if the txlookup already in the cache, retrieve otherwise
285281
if item, exist := bc.txLookupCache.Get(hash); exist {
286-
return item.lookup, item.transaction, nil
282+
return item.lookup, item.transaction
287283
}
288284
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
289285
if tx == nil {
290-
progress, err := bc.TxIndexProgress()
291-
if err != nil {
292-
// No error is returned if the transaction indexing progress is unreachable
293-
// due to unexpected internal errors. In such cases, it is impossible to
294-
// determine whether the transaction does not exist or has simply not been
295-
// indexed yet without a progress marker.
296-
//
297-
// In such scenarios, the transaction is treated as unreachable, though
298-
// this is clearly an unintended and unexpected situation.
299-
return nil, nil, nil
300-
}
301-
// The transaction indexing is not finished yet, returning an
302-
// error to explicitly indicate it.
303-
if !progress.Done() {
304-
return nil, nil, errors.New("transaction indexing still in progress")
305-
}
306-
// The transaction is already indexed, the transaction is either
307-
// not existent or not in the range of index, returning null.
308-
return nil, nil, nil
286+
return nil, nil
309287
}
310288
lookup := &rawdb.LegacyTxLookupEntry{
311289
BlockHash: blockHash,
@@ -316,7 +294,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
316294
lookup: lookup,
317295
transaction: tx,
318296
})
319-
return lookup, tx, nil
297+
return lookup, tx
298+
}
299+
300+
// TxIndexDone returns true if the transaction indexer has finished indexing.
301+
func (bc *BlockChain) TxIndexDone() bool {
302+
progress, err := bc.TxIndexProgress()
303+
if err != nil {
304+
// No error is returned if the transaction indexing progress is unreachable
305+
// due to unexpected internal errors. In such cases, it is impossible to
306+
// determine whether the transaction does not exist or has simply not been
307+
// indexed yet without a progress marker.
308+
//
309+
// In such scenarios, the transaction is treated as unreachable, though
310+
// this is clearly an unintended and unexpected situation.
311+
return true
312+
}
313+
return progress.Done()
320314
}
321315

322316
// HasState checks if state trie is fully present in the database or not.
@@ -412,7 +406,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
412406
if bc.txIndexer == nil {
413407
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
414408
}
415-
return bc.txIndexer.txIndexProgress()
409+
return bc.txIndexer.txIndexProgress(), nil
416410
}
417411

418412
// HistoryPruningCutoff returns the configured history pruning point.

core/filtermaps/chain_view.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,6 @@ func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
135135
return common.NewRange(0, sharedLen)
136136
}
137137

138-
// limitedView returns a new chain view that is a truncated version of the parent view.
139-
func (cv *ChainView) limitedView(newHead uint64) *ChainView {
140-
if newHead >= cv.headNumber {
141-
return cv
142-
}
143-
return NewChainView(cv.chain, newHead, cv.BlockHash(newHead))
144-
}
145-
146138
// equalViews returns true if the two chain views are equivalent.
147139
func equalViews(cv1, cv2 *ChainView) bool {
148140
if cv1 == nil || cv2 == nil {

core/filtermaps/filtermaps.go

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ var (
5050
)
5151

5252
const (
53-
databaseVersion = 1 // reindexed if database version does not match
53+
databaseVersion = 2 // reindexed if database version does not match
5454
cachedLastBlocks = 1000 // last block of map pointers
5555
cachedLvPointers = 1000 // first log value pointer of block pointers
5656
cachedBaseRows = 100 // groups of base layer filter row data
@@ -244,6 +244,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
244244
disabledCh: make(chan struct{}),
245245
exportFileName: config.ExportFileName,
246246
Params: params,
247+
targetView: initView,
248+
indexedView: initView,
247249
indexedRange: filterMapsRange{
248250
initialized: initialized,
249251
headIndexed: rs.HeadIndexed,
@@ -265,16 +267,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
265267
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
266268
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
267269
}
270+
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
268271

269-
// Set initial indexer target.
270-
f.targetView = initView
271-
if f.indexedRange.initialized {
272-
f.indexedView = f.initChainView(f.targetView)
273-
f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.HeadNumber()+1
274-
if !f.indexedRange.headIndexed {
275-
f.indexedRange.headDelimiter = 0
276-
}
277-
}
278272
if f.indexedRange.hasIndexedBlocks() {
279273
log.Info("Initialized log indexer",
280274
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
@@ -303,29 +297,40 @@ func (f *FilterMaps) Stop() {
303297
f.closeWg.Wait()
304298
}
305299

306-
// initChainView returns a chain view consistent with both the current target
307-
// view and the current state of the log index as found in the database, based
308-
// on the last block of stored maps.
309-
// Note that the returned view might be shorter than the existing index if
310-
// the latest maps are not consistent with targetView.
311-
func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
312-
mapIndex := f.indexedRange.maps.AfterLast()
313-
for {
314-
var ok bool
315-
mapIndex, ok = f.lastMapBoundaryBefore(mapIndex)
316-
if !ok {
317-
break
300+
// checkRevertRange checks whether the existing index is consistent with the
301+
// current indexed view and reverts inconsistent maps if necessary.
302+
func (f *FilterMaps) checkRevertRange() {
303+
if f.indexedRange.maps.Count() == 0 {
304+
return
305+
}
306+
lastMap := f.indexedRange.maps.Last()
307+
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap)
308+
if err != nil {
309+
log.Error("Error initializing log index database; resetting log index", "error", err)
310+
f.reset()
311+
return
312+
}
313+
for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId {
314+
// revert last map
315+
if f.indexedRange.maps.Count() == 1 {
316+
f.reset() // reset database if no rendered maps remained
317+
return
318318
}
319-
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex)
319+
lastMap--
320+
newRange := f.indexedRange
321+
newRange.maps.SetLast(lastMap)
322+
lastBlockNumber, lastBlockId, err = f.getLastBlockOfMap(lastMap)
320323
if err != nil {
321-
log.Error("Could not initialize indexed chain view", "error", err)
322-
break
323-
}
324-
if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId {
325-
return chainView.limitedView(lastBlockNumber)
324+
log.Error("Error initializing log index database; resetting log index", "error", err)
325+
f.reset()
326+
return
326327
}
328+
newRange.blocks.SetAfterLast(lastBlockNumber) // lastBlockNumber is probably partially indexed
329+
newRange.headIndexed = false
330+
newRange.headDelimiter = 0
331+
// only shorten range and leave map data; next head render will overwrite it
332+
f.setRange(f.db, f.indexedView, newRange, false)
327333
}
328-
return chainView.limitedView(0)
329334
}
330335

331336
// reset un-initializes the FilterMaps structure and removes all related data from
@@ -662,15 +667,11 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 {
662667
}
663668

664669
// getBlockLvPointer returns the starting log value index where the log values
665-
// generated by the given block are located. If blockNumber is beyond the current
666-
// head then the first unoccupied log value index is returned.
670+
// generated by the given block are located.
667671
//
668672
// Note that this function assumes that the indexer read lock is being held when
669673
// called from outside the indexerLoop goroutine.
670674
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
671-
if blockNumber >= f.indexedRange.blocks.AfterLast() && f.indexedRange.headIndexed {
672-
return f.indexedRange.headDelimiter + 1, nil
673-
}
674675
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
675676
return lvPointer, nil
676677
}

core/filtermaps/indexer.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func (f *FilterMaps) tryIndexHead() error {
254254
((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) ||
255255
time.Since(f.lastLogHeadIndex) > logFrequency) {
256256
log.Info("Log index head rendering in progress",
257-
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
257+
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
258258
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
259259
"remaining", f.indexedView.HeadNumber()-f.indexedRange.blocks.Last(),
260260
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
@@ -266,7 +266,7 @@ func (f *FilterMaps) tryIndexHead() error {
266266
}
267267
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
268268
log.Info("Log index head rendering finished",
269-
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
269+
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
270270
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
271271
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
272272
}
@@ -323,7 +323,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
323323
if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.blocks.First() &&
324324
(!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) {
325325
log.Info("Log index tail rendering in progress",
326-
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
326+
"firstblock", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
327327
"processed", f.ptrTailIndex-f.indexedRange.blocks.First()+tpb,
328328
"remaining", remaining,
329329
"next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch,
@@ -346,7 +346,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
346346
}
347347
if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() {
348348
log.Info("Log index tail rendering finished",
349-
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
349+
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
350350
"processed", f.ptrTailIndex-f.indexedRange.blocks.First(),
351351
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
352352
f.loggedTailIndex = false

core/filtermaps/map_renderer.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,15 +468,25 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error {
468468
r.f.filterMapCache.Remove(mapIndex)
469469
}
470470
}
471+
var blockNumber uint64
472+
if r.finished.First() > 0 {
473+
// in order to always ensure continuous block pointers, initialize
474+
// blockNumber based on the last block of the previous map, then verify
475+
// against the first block associated with each rendered map
476+
lastBlock, _, err := r.f.getLastBlockOfMap(r.finished.First() - 1)
477+
if err != nil {
478+
return fmt.Errorf("failed to get last block of previous map %d: %v", r.finished.First()-1, err)
479+
}
480+
blockNumber = lastBlock + 1
481+
}
471482
// add or update block pointers
472-
blockNumber := r.finishedMaps[r.finished.First()].firstBlock()
473483
for mapIndex := range r.finished.Iter() {
474484
renderedMap := r.finishedMaps[mapIndex]
475-
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
476-
checkWriteCnt()
477485
if blockNumber != renderedMap.firstBlock() {
478-
panic("non-continuous block numbers")
486+
return fmt.Errorf("non-continuous block numbers in rendered map %d (next expected: %d first rendered: %d)", mapIndex, blockNumber, renderedMap.firstBlock())
479487
}
488+
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
489+
checkWriteCnt()
480490
for _, lvPtr := range renderedMap.blockLvPtrs {
481491
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
482492
checkWriteCnt()

0 commit comments

Comments
 (0)