mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Revert "p2p/discover: add Table configuration and Nodes method (#27387)"
This reverts commit c51cd2d8f8.
This commit is contained in:
parent
74d25c4c98
commit
c6bbfd6685
6 changed files with 85 additions and 72 deletions
|
|
@ -19,7 +19,6 @@ package discover
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -36,39 +35,29 @@ type UDPConn interface {
|
||||||
LocalAddr() net.Addr
|
LocalAddr() net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type V5Config struct {
|
||||||
|
ProtocolID *[6]byte
|
||||||
|
}
|
||||||
|
|
||||||
// Config holds settings for the discovery listener.
|
// Config holds settings for the discovery listener.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// These settings are required and configure the UDP listener:
|
// These settings are required and configure the UDP listener:
|
||||||
PrivateKey *ecdsa.PrivateKey
|
PrivateKey *ecdsa.PrivateKey
|
||||||
|
|
||||||
// All remaining settings are optional.
|
// These settings are optional:
|
||||||
|
|
||||||
// Packet handling configuration:
|
|
||||||
NetRestrict *netutil.Netlist // list of allowed IP networks
|
NetRestrict *netutil.Netlist // list of allowed IP networks
|
||||||
Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
|
|
||||||
|
|
||||||
// Node table configuration:
|
|
||||||
Bootnodes []*enode.Node // list of bootstrap nodes
|
Bootnodes []*enode.Node // list of bootstrap nodes
|
||||||
PingInterval time.Duration // speed of node liveness check
|
Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
|
||||||
RefreshInterval time.Duration // used in bucket refresh
|
|
||||||
|
|
||||||
// The options below are useful in very specific cases, like in unit tests.
|
|
||||||
V5ProtocolID *[6]byte
|
|
||||||
Log log.Logger // if set, log messages go here
|
Log log.Logger // if set, log messages go here
|
||||||
|
|
||||||
|
// V5ProtocolID configures the discv5 protocol identifier.
|
||||||
|
V5ProtocolID *[6]byte
|
||||||
|
|
||||||
ValidSchemes enr.IdentityScheme // allowed identity schemes
|
ValidSchemes enr.IdentityScheme // allowed identity schemes
|
||||||
Clock mclock.Clock
|
Clock mclock.Clock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg Config) withDefaults() Config {
|
func (cfg Config) withDefaults() Config {
|
||||||
// Node table configuration:
|
|
||||||
if cfg.PingInterval == 0 {
|
|
||||||
cfg.PingInterval = 10 * time.Second
|
|
||||||
}
|
|
||||||
if cfg.RefreshInterval == 0 {
|
|
||||||
cfg.RefreshInterval = 30 * time.Minute
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug/test settings:
|
|
||||||
if cfg.Log == nil {
|
if cfg.Log == nil {
|
||||||
cfg.Log = log.Root()
|
cfg.Log = log.Root()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ const (
|
||||||
bucketIPLimit, bucketSubnet = 2, 24 // at most 2 addresses from the same /24
|
bucketIPLimit, bucketSubnet = 2, 24 // at most 2 addresses from the same /24
|
||||||
tableIPLimit, tableSubnet = 10, 24
|
tableIPLimit, tableSubnet = 10, 24
|
||||||
|
|
||||||
|
refreshInterval = 30 * time.Minute
|
||||||
|
revalidateInterval = 10 * time.Second
|
||||||
copyNodesInterval = 30 * time.Second
|
copyNodesInterval = 30 * time.Second
|
||||||
seedMinTableTime = 5 * time.Minute
|
seedMinTableTime = 5 * time.Minute
|
||||||
seedCount = 30
|
seedCount = 30
|
||||||
|
|
@ -69,12 +71,9 @@ type Table struct {
|
||||||
rand *mrand.Rand // source of randomness, periodically reseeded
|
rand *mrand.Rand // source of randomness, periodically reseeded
|
||||||
ips netutil.DistinctNetSet
|
ips netutil.DistinctNetSet
|
||||||
|
|
||||||
|
log log.Logger
|
||||||
db *enode.DB // database of known nodes
|
db *enode.DB // database of known nodes
|
||||||
net transport
|
net transport
|
||||||
cfg Config
|
|
||||||
log log.Logger
|
|
||||||
|
|
||||||
// loop channels
|
|
||||||
refreshReq chan chan struct{}
|
refreshReq chan chan struct{}
|
||||||
initDone chan struct{}
|
initDone chan struct{}
|
||||||
closeReq chan struct{}
|
closeReq chan struct{}
|
||||||
|
|
@ -100,21 +99,19 @@ type bucket struct {
|
||||||
ips netutil.DistinctNetSet
|
ips netutil.DistinctNetSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
|
func newTable(t transport, db *enode.DB, bootnodes []*enode.Node, log log.Logger) (*Table, error) {
|
||||||
cfg = cfg.withDefaults()
|
|
||||||
tab := &Table{
|
tab := &Table{
|
||||||
net: t,
|
net: t,
|
||||||
db: db,
|
db: db,
|
||||||
cfg: cfg,
|
|
||||||
log: cfg.Log,
|
|
||||||
refreshReq: make(chan chan struct{}),
|
refreshReq: make(chan chan struct{}),
|
||||||
initDone: make(chan struct{}),
|
initDone: make(chan struct{}),
|
||||||
closeReq: make(chan struct{}),
|
closeReq: make(chan struct{}),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
rand: mrand.New(mrand.NewSource(0)),
|
rand: mrand.New(mrand.NewSource(0)),
|
||||||
ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},
|
ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil {
|
if err := tab.setFallbackNodes(bootnodes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for i := range tab.buckets {
|
for i := range tab.buckets {
|
||||||
|
|
@ -128,24 +125,6 @@ func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
|
||||||
return tab, nil
|
return tab, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all nodes contained in the table.
|
|
||||||
func (tab *Table) Nodes() []*enode.Node {
|
|
||||||
if !tab.isInitDone() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tab.mutex.Lock()
|
|
||||||
defer tab.mutex.Unlock()
|
|
||||||
|
|
||||||
var nodes []*enode.Node
|
|
||||||
for _, b := range &tab.buckets {
|
|
||||||
for _, n := range b.entries {
|
|
||||||
nodes = append(nodes, unwrapNode(n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tab *Table) self() *enode.Node {
|
func (tab *Table) self() *enode.Node {
|
||||||
return tab.net.Self()
|
return tab.net.Self()
|
||||||
}
|
}
|
||||||
|
|
@ -159,6 +138,29 @@ func (tab *Table) seedRand() {
|
||||||
tab.mutex.Unlock()
|
tab.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadRandomNodes fills the given slice with random nodes from the table. The results
|
||||||
|
// are guaranteed to be unique for a single invocation, no node will appear twice.
|
||||||
|
func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) {
|
||||||
|
if !tab.isInitDone() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
tab.mutex.Lock()
|
||||||
|
defer tab.mutex.Unlock()
|
||||||
|
|
||||||
|
var nodes []*enode.Node
|
||||||
|
for _, b := range &tab.buckets {
|
||||||
|
for _, n := range b.entries {
|
||||||
|
nodes = append(nodes, unwrapNode(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Shuffle.
|
||||||
|
for i := 0; i < len(nodes); i++ {
|
||||||
|
j := tab.rand.Intn(len(nodes))
|
||||||
|
nodes[i], nodes[j] = nodes[j], nodes[i]
|
||||||
|
}
|
||||||
|
return copy(buf, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
// getNode returns the node with the given ID or nil if it isn't in the table.
|
// getNode returns the node with the given ID or nil if it isn't in the table.
|
||||||
func (tab *Table) getNode(id enode.ID) *enode.Node {
|
func (tab *Table) getNode(id enode.ID) *enode.Node {
|
||||||
tab.mutex.Lock()
|
tab.mutex.Lock()
|
||||||
|
|
@ -216,7 +218,7 @@ func (tab *Table) refresh() <-chan struct{} {
|
||||||
func (tab *Table) loop() {
|
func (tab *Table) loop() {
|
||||||
var (
|
var (
|
||||||
revalidate = time.NewTimer(tab.nextRevalidateTime())
|
revalidate = time.NewTimer(tab.nextRevalidateTime())
|
||||||
refresh = time.NewTimer(tab.nextRefreshTime())
|
refresh = time.NewTicker(refreshInterval)
|
||||||
copyNodes = time.NewTicker(copyNodesInterval)
|
copyNodes = time.NewTicker(copyNodesInterval)
|
||||||
refreshDone = make(chan struct{}) // where doRefresh reports completion
|
refreshDone = make(chan struct{}) // where doRefresh reports completion
|
||||||
revalidateDone chan struct{} // where doRevalidate reports completion
|
revalidateDone chan struct{} // where doRevalidate reports completion
|
||||||
|
|
@ -249,7 +251,6 @@ loop:
|
||||||
close(ch)
|
close(ch)
|
||||||
}
|
}
|
||||||
waiting, refreshDone = nil, nil
|
waiting, refreshDone = nil, nil
|
||||||
refresh.Reset(tab.nextRefreshTime())
|
|
||||||
case <-revalidate.C:
|
case <-revalidate.C:
|
||||||
revalidateDone = make(chan struct{})
|
revalidateDone = make(chan struct{})
|
||||||
go tab.doRevalidate(revalidateDone)
|
go tab.doRevalidate(revalidateDone)
|
||||||
|
|
@ -372,15 +373,7 @@ func (tab *Table) nextRevalidateTime() time.Duration {
|
||||||
tab.mutex.Lock()
|
tab.mutex.Lock()
|
||||||
defer tab.mutex.Unlock()
|
defer tab.mutex.Unlock()
|
||||||
|
|
||||||
return time.Duration(tab.rand.Int63n(int64(tab.cfg.PingInterval)))
|
return time.Duration(tab.rand.Int63n(int64(revalidateInterval)))
|
||||||
}
|
|
||||||
|
|
||||||
func (tab *Table) nextRefreshTime() time.Duration {
|
|
||||||
tab.mutex.Lock()
|
|
||||||
defer tab.mutex.Unlock()
|
|
||||||
|
|
||||||
half := tab.cfg.RefreshInterval / 2
|
|
||||||
return half + time.Duration(tab.rand.Int63n(int64(half)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyLiveNodes adds nodes from the table to the database if they have been in the table
|
// copyLiveNodes adds nodes from the table to the database if they have been in the table
|
||||||
|
|
@ -488,12 +481,10 @@ func (tab *Table) addSeenNode(n *node) {
|
||||||
// Can't add: IP limit reached.
|
// Can't add: IP limit reached.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to end of bucket:
|
// Add to end of bucket:
|
||||||
b.entries = append(b.entries, n)
|
b.entries = append(b.entries, n)
|
||||||
b.replacements = deleteNode(b.replacements, n)
|
b.replacements = deleteNode(b.replacements, n)
|
||||||
n.addedAt = time.Now()
|
n.addedAt = time.Now()
|
||||||
|
|
||||||
if tab.nodeAddedHook != nil {
|
if tab.nodeAddedHook != nil {
|
||||||
tab.nodeAddedHook(n)
|
tab.nodeAddedHook(n)
|
||||||
}
|
}
|
||||||
|
|
@ -532,12 +523,10 @@ func (tab *Table) addVerifiedNode(n *node) {
|
||||||
// Can't add: IP limit reached.
|
// Can't add: IP limit reached.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to front of bucket.
|
// Add to front of bucket.
|
||||||
b.entries, _ = pushNode(b.entries, n, bucketSize)
|
b.entries, _ = pushNode(b.entries, n, bucketSize)
|
||||||
b.replacements = deleteNode(b.replacements, n)
|
b.replacements = deleteNode(b.replacements, n)
|
||||||
n.addedAt = time.Now()
|
n.addedAt = time.Now()
|
||||||
|
|
||||||
if tab.nodeAddedHook != nil {
|
if tab.nodeAddedHook != nil {
|
||||||
tab.nodeAddedHook(n)
|
tab.nodeAddedHook(n)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,41 @@ func TestTable_findnodeByID(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTable_ReadRandomNodesGetAll(t *testing.T) {
|
||||||
|
cfg := &quick.Config{
|
||||||
|
MaxCount: 200,
|
||||||
|
Rand: rand.New(rand.NewSource(time.Now().Unix())),
|
||||||
|
Values: func(args []reflect.Value, rand *rand.Rand) {
|
||||||
|
args[0] = reflect.ValueOf(make([]*enode.Node, rand.Intn(1000)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test := func(buf []*enode.Node) bool {
|
||||||
|
transport := newPingRecorder()
|
||||||
|
tab, db := newTestTable(transport)
|
||||||
|
defer db.Close()
|
||||||
|
defer tab.close()
|
||||||
|
<-tab.initDone
|
||||||
|
|
||||||
|
for i := 0; i < len(buf); i++ {
|
||||||
|
ld := cfg.Rand.Intn(len(tab.buckets))
|
||||||
|
fillTable(tab, []*node{nodeAtDistance(tab.self().ID(), ld, intIP(ld))})
|
||||||
|
}
|
||||||
|
gotN := tab.ReadRandomNodes(buf)
|
||||||
|
if gotN != tab.len() {
|
||||||
|
t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.len())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if hasDuplicates(wrapNodes(buf[:gotN])) {
|
||||||
|
t.Errorf("result contains duplicates")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err := quick.Check(test, cfg); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type closeTest struct {
|
type closeTest struct {
|
||||||
Self enode.ID
|
Self enode.ID
|
||||||
Target enode.ID
|
Target enode.ID
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
@ -41,9 +42,8 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestTable(t transport) (*Table, *enode.DB) {
|
func newTestTable(t transport) (*Table, *enode.DB) {
|
||||||
cfg := Config{}
|
|
||||||
db, _ := enode.OpenDB("")
|
db, _ := enode.OpenDB("")
|
||||||
tab, _ := newTable(t, db, cfg)
|
tab, _ := newTable(t, db, nil, log.Root())
|
||||||
go tab.loop()
|
go tab.loop()
|
||||||
return tab, db
|
return tab, db
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ func ListenV4(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) {
|
||||||
log: cfg.Log,
|
log: cfg.Log,
|
||||||
}
|
}
|
||||||
|
|
||||||
tab, err := newTable(t, ln.Database(), cfg)
|
tab, err := newTable(t, ln.Database(), cfg.Bootnodes, t.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) {
|
||||||
cancelCloseCtx: cancelCloseCtx,
|
cancelCloseCtx: cancelCloseCtx,
|
||||||
}
|
}
|
||||||
t.talk = newTalkSystem(t)
|
t.talk = newTalkSystem(t)
|
||||||
tab, err := newTable(t, t.db, cfg)
|
tab, err := newTable(t, t.db, cfg.Bootnodes, cfg.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue