eth: dial nodes from discv5

Unlike discv4, the discv5 random nodes iterator will always provide full ENRs. This means
we can apply filtering to the results and will only try dialing nodes which explictly opt
into the eth protocol with a matching chain.

I have also removed the dial iterator from snap. We don't have an official DNS list for
snap anymore, and I doubt anyone else is running one. While we could potentially filter
for snap on discv5, there will be very few nodes announcing it, and the extra iterator
would just stall the dialer.
This commit is contained in:
Felix Lange 2024-08-14 19:25:38 +02:00
parent c35684709c
commit b402bf66ee
4 changed files with 54 additions and 33 deletions

View file

@ -23,6 +23,7 @@ import (
"math/big" "math/big"
"runtime" "runtime"
"sync" "sync"
"time"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -64,15 +65,13 @@ type Config = ethconfig.Config
// Ethereum implements the Ethereum full node service. // Ethereum implements the Ethereum full node service.
type Ethereum struct { type Ethereum struct {
// core protocol objects
config *ethconfig.Config config *ethconfig.Config
// Handlers
txPool *txpool.TxPool txPool *txpool.TxPool
blockchain *core.BlockChain
blockchain *core.BlockChain handler *handler
handler *handler discmix *enode.FairMix
ethDialCandidates enode.Iterator
snapDialCandidates enode.Iterator
// DB interfaces // DB interfaces
chainDb ethdb.Database // Block chain database chainDb ethdb.Database // Block chain database
@ -162,6 +161,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
bloomRequests: make(chan chan *bloombits.Retrieval), bloomRequests: make(chan chan *bloombits.Retrieval),
bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
p2pServer: stack.Server(), p2pServer: stack.Server(),
discmix: enode.NewFairMix(2 * time.Second),
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb), shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
} }
bcVersion := rawdb.ReadDatabaseVersion(chainDb) bcVersion := rawdb.ReadDatabaseVersion(chainDb)
@ -266,17 +266,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
} }
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice) eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)
// Setup DNS discovery iterators.
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
eth.ethDialCandidates, err = dnsclient.NewIterator(eth.config.EthDiscoveryURLs...)
if err != nil {
return nil, err
}
eth.snapDialCandidates, err = dnsclient.NewIterator(eth.config.SnapDiscoveryURLs...)
if err != nil {
return nil, err
}
// Start the RPC service // Start the RPC service
eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID) eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID)
@ -359,9 +348,9 @@ func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
// Protocols returns all the currently configured // Protocols returns all the currently configured
// network protocols to start. // network protocols to start.
func (s *Ethereum) Protocols() []p2p.Protocol { func (s *Ethereum) Protocols() []p2p.Protocol {
protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates) protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.discmix)
if s.config.SnapshotCache > 0 { if s.config.SnapshotCache > 0 {
protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler))...)
} }
return protos return protos
} }
@ -369,7 +358,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
// Start implements node.Lifecycle, starting all internal goroutines needed by the // Start implements node.Lifecycle, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *Ethereum) Start() error { func (s *Ethereum) Start() error {
eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) s.setupDiscovery()
// Start the bloom bits servicing goroutines // Start the bloom bits servicing goroutines
s.startBloomHandlers(params.BloomBitsBlocks) s.startBloomHandlers(params.BloomBitsBlocks)
@ -382,12 +371,40 @@ func (s *Ethereum) Start() error {
return nil return nil
} }
func (s *Ethereum) setupDiscovery() error {
eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode())
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
if len(s.config.EthDiscoveryURLs) > 0 {
iter, err := dnsclient.NewIterator(s.config.EthDiscoveryURLs...)
if err != nil {
return err
}
s.discmix.AddSource(iter)
}
if len(s.config.SnapDiscoveryURLs) > 0 {
iter, err := dnsclient.NewIterator(s.config.SnapDiscoveryURLs...)
if err != nil {
return err
}
s.discmix.AddSource(iter)
}
if s.p2pServer.DiscoveryV5() != nil {
filter := eth.NewNodeFilter(s.blockchain)
iter := enode.Filter(s.p2pServer.DiscoveryV5().RandomNodes(), filter)
s.discmix.AddSource(iter)
}
return nil
}
// Stop implements node.Lifecycle, terminating all internal goroutines used by the // Stop implements node.Lifecycle, terminating all internal goroutines used by the
// Ethereum protocol. // Ethereum protocol.
func (s *Ethereum) Stop() error { func (s *Ethereum) Stop() error {
// Stop all the peer-related stuff first. // Stop all the peer-related stuff first.
s.ethDialCandidates.Close() s.discmix.Close()
s.snapDialCandidates.Close()
s.handler.Stop() s.handler.Stop()
// Then stop everything else. // Then stop everything else.

View file

@ -64,3 +64,15 @@ func currentENREntry(chain *core.BlockChain) *enrEntry {
ForkID: forkid.NewID(chain.Config(), chain.Genesis(), head.Number.Uint64(), head.Time), ForkID: forkid.NewID(chain.Config(), chain.Genesis(), head.Number.Uint64(), head.Time),
} }
} }
func NewNodeFilter(chain *core.BlockChain) func(*enode.Node) bool {
filter := forkid.NewFilter(chain)
return func(n *enode.Node) bool {
var entry enrEntry
if err := n.Load(entry); err != nil {
return false
}
err := filter(entry.ForkID)
return err == nil
}
}

View file

@ -113,8 +113,7 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
PeerInfo: func(id enode.ID) interface{} { PeerInfo: func(id enode.ID) interface{} {
return backend.PeerInfo(id) return backend.PeerInfo(id)
}, },
Attributes: []enr.Entry{currentENREntry(backend.Chain())}, Attributes: []enr.Entry{currentENREntry(backend.Chain())},
DialCandidates: dnsdisc,
}) })
} }
return protocols return protocols

View file

@ -82,13 +82,7 @@ type Backend interface {
} }
// MakeProtocols constructs the P2P protocol definitions for `snap`. // MakeProtocols constructs the P2P protocol definitions for `snap`.
func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol { func MakeProtocols(backend Backend) []p2p.Protocol {
// Filter the discovery iterator for nodes advertising snap support.
dnsdisc = enode.Filter(dnsdisc, func(n *enode.Node) bool {
var snap enrEntry
return n.Load(&snap) == nil
})
protocols := make([]p2p.Protocol, len(ProtocolVersions)) protocols := make([]p2p.Protocol, len(ProtocolVersions))
for i, version := range ProtocolVersions { for i, version := range ProtocolVersions {
version := version // Closure version := version // Closure
@ -108,8 +102,7 @@ func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol {
PeerInfo: func(id enode.ID) interface{} { PeerInfo: func(id enode.ID) interface{} {
return backend.PeerInfo(id) return backend.PeerInfo(id)
}, },
Attributes: []enr.Entry{&enrEntry{}}, Attributes: []enr.Entry{&enrEntry{}},
DialCandidates: dnsdisc,
} }
} }
return protocols return protocols