forked from cerc-io/plugeth
cmd/utils: use eth DNS tree for snap discovery (#22808)
This removes auto-configuration of the snap.*.ethdisco.net DNS discovery tree. Since measurements have shown that > 75% of nodes in all.*.ethdisco.net support snap, we have decided to retire the dedicated index for snap and just use the eth tree instead. The dial iterators of eth and snap now use the same DNS tree in the default configuration, so both iterators should use the same DNS discovery client instance. This ensures that the record cache and rate limit are shared. Records will not be requested multiple times. While testing the change, I noticed that duplicate DNS requests do happen even when the client instance is shared. This is because the two iterators request the tree root, link tree root, and first levels of the tree in lockstep. To avoid this problem, the change also adds a singleflight.Group instance in the client. When one iterator attempts to resolve an entry which is already being resolved, the singleflight object waits for the existing resolve call to finish and returns the entry to both places.
This commit is contained in:
parent
640d2c5e30
commit
b8040a430e
@ -1690,11 +1690,7 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {
|
|||||||
}
|
}
|
||||||
if url := params.KnownDNSNetwork(genesis, protocol); url != "" {
|
if url := params.KnownDNSNetwork(genesis, protocol); url != "" {
|
||||||
cfg.EthDiscoveryURLs = []string{url}
|
cfg.EthDiscoveryURLs = []string{url}
|
||||||
}
|
cfg.SnapDiscoveryURLs = cfg.EthDiscoveryURLs
|
||||||
if cfg.SyncMode == downloader.SnapSync {
|
|
||||||
if url := params.KnownDNSNetwork(genesis, "snap"); url != "" {
|
|
||||||
cfg.SnapDiscoveryURLs = []string{url}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
@ -239,14 +240,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||||||
}
|
}
|
||||||
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
|
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
|
||||||
|
|
||||||
eth.ethDialCandidates, err = setupDiscovery(eth.config.EthDiscoveryURLs)
|
// Setup DNS discovery iterators.
|
||||||
|
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
|
||||||
|
eth.ethDialCandidates, err = dnsclient.NewIterator(eth.config.EthDiscoveryURLs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
eth.snapDialCandidates, err = setupDiscovery(eth.config.SnapDiscoveryURLs)
|
eth.snapDialCandidates, err = dnsclient.NewIterator(eth.config.SnapDiscoveryURLs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the RPC service
|
// Start the RPC service
|
||||||
eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, config.NetworkId)
|
eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, config.NetworkId)
|
||||||
|
|
||||||
@ -544,6 +548,8 @@ func (s *Ethereum) Start() error {
|
|||||||
// 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.snapDialCandidates.Close()
|
||||||
s.handler.Stop()
|
s.handler.Stop()
|
||||||
|
|
||||||
// Then stop everything else.
|
// Then stop everything else.
|
||||||
|
@ -19,7 +19,6 @@ package eth
|
|||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/forkid"
|
"github.com/ethereum/go-ethereum/core/forkid"
|
||||||
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
@ -62,13 +61,3 @@ func (eth *Ethereum) currentEthEntry() *ethEntry {
|
|||||||
return ðEntry{ForkID: forkid.NewID(eth.blockchain.Config(), eth.blockchain.Genesis().Hash(),
|
return ðEntry{ForkID: forkid.NewID(eth.blockchain.Config(), eth.blockchain.Genesis().Hash(),
|
||||||
eth.blockchain.CurrentHeader().Number.Uint64())}
|
eth.blockchain.CurrentHeader().Number.Uint64())}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupDiscovery creates the node discovery source for the `eth` and `snap`
|
|
||||||
// protocols.
|
|
||||||
func setupDiscovery(urls []string) (enode.Iterator, error) {
|
|
||||||
if len(urls) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
client := dnsdisc.NewClient(dnsdisc.Config{})
|
|
||||||
return client.NewIterator(urls...)
|
|
||||||
}
|
|
||||||
|
@ -84,6 +84,12 @@ 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, dnsdisc enode.Iterator) []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
|
||||||
|
1
go.mod
1
go.mod
@ -60,6 +60,7 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
||||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
|
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988
|
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988
|
||||||
golang.org/x/text v0.3.4
|
golang.org/x/text v0.3.4
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
|
2
go.sum
2
go.sum
@ -455,6 +455,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -32,15 +32,17 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
"golang.org/x/sync/singleflight"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client discovers nodes by querying DNS servers.
|
// Client discovers nodes by querying DNS servers.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
clock mclock.Clock
|
clock mclock.Clock
|
||||||
entries *lru.Cache
|
entries *lru.Cache
|
||||||
ratelimit *rate.Limiter
|
ratelimit *rate.Limiter
|
||||||
|
singleflight singleflight.Group
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config holds configuration options for the client.
|
// Config holds configuration options for the client.
|
||||||
@ -135,17 +137,20 @@ func (c *Client) NewIterator(urls ...string) (enode.Iterator, error) {
|
|||||||
|
|
||||||
// resolveRoot retrieves a root entry via DNS.
|
// resolveRoot retrieves a root entry via DNS.
|
||||||
func (c *Client) resolveRoot(ctx context.Context, loc *linkEntry) (rootEntry, error) {
|
func (c *Client) resolveRoot(ctx context.Context, loc *linkEntry) (rootEntry, error) {
|
||||||
txts, err := c.cfg.Resolver.LookupTXT(ctx, loc.domain)
|
e, err, _ := c.singleflight.Do(loc.str, func() (interface{}, error) {
|
||||||
c.cfg.Logger.Trace("Updating DNS discovery root", "tree", loc.domain, "err", err)
|
txts, err := c.cfg.Resolver.LookupTXT(ctx, loc.domain)
|
||||||
if err != nil {
|
c.cfg.Logger.Trace("Updating DNS discovery root", "tree", loc.domain, "err", err)
|
||||||
return rootEntry{}, err
|
if err != nil {
|
||||||
}
|
return rootEntry{}, err
|
||||||
for _, txt := range txts {
|
|
||||||
if strings.HasPrefix(txt, rootPrefix) {
|
|
||||||
return parseAndVerifyRoot(txt, loc)
|
|
||||||
}
|
}
|
||||||
}
|
for _, txt := range txts {
|
||||||
return rootEntry{}, nameError{loc.domain, errNoRoot}
|
if strings.HasPrefix(txt, rootPrefix) {
|
||||||
|
return parseAndVerifyRoot(txt, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rootEntry{}, nameError{loc.domain, errNoRoot}
|
||||||
|
})
|
||||||
|
return e.(rootEntry), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAndVerifyRoot(txt string, loc *linkEntry) (rootEntry, error) {
|
func parseAndVerifyRoot(txt string, loc *linkEntry) (rootEntry, error) {
|
||||||
@ -168,17 +173,21 @@ func (c *Client) resolveEntry(ctx context.Context, domain, hash string) (entry,
|
|||||||
if err := c.ratelimit.Wait(ctx); err != nil {
|
if err := c.ratelimit.Wait(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := truncateHash(hash)
|
cacheKey := truncateHash(hash)
|
||||||
if e, ok := c.entries.Get(cacheKey); ok {
|
if e, ok := c.entries.Get(cacheKey); ok {
|
||||||
return e.(entry), nil
|
return e.(entry), nil
|
||||||
}
|
}
|
||||||
e, err := c.doResolveEntry(ctx, domain, hash)
|
|
||||||
if err != nil {
|
ei, err, _ := c.singleflight.Do(cacheKey, func() (interface{}, error) {
|
||||||
return nil, err
|
e, err := c.doResolveEntry(ctx, domain, hash)
|
||||||
}
|
if err != nil {
|
||||||
c.entries.Add(cacheKey, e)
|
return nil, err
|
||||||
return e, nil
|
}
|
||||||
|
c.entries.Add(cacheKey, e)
|
||||||
|
return e, nil
|
||||||
|
})
|
||||||
|
e, _ := ei.(entry)
|
||||||
|
return e, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// doResolveEntry fetches an entry via DNS.
|
// doResolveEntry fetches an entry via DNS.
|
||||||
|
Loading…
Reference in New Issue
Block a user