Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

p2p, p2p/discover: add signed ENR generation #17753

Merged
merged 15 commits into from
Oct 12, 2018
Prev Previous commit
Next Next commit
p2p, p2p/discover: integrate enode.LocalNode
Startup is much faster because it doesn't need to wait for the external
IP query anymore.
  • Loading branch information
fjl committed Oct 11, 2018
commit 16b135060ba8c17ddba8cca35992a9f470a7e6ae
7 changes: 4 additions & 3 deletions p2p/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type dialstate struct {
maxDynDials int
ntab discoverTable
netrestrict *netutil.Netlist
self enode.ID

lookupRunning bool
dialing map[enode.ID]connFlag
Expand All @@ -84,7 +85,6 @@ type dialstate struct {
}

type discoverTable interface {
Self() *enode.Node
Close()
Resolve(*enode.Node) *enode.Node
LookupRandom() []*enode.Node
Expand Down Expand Up @@ -126,10 +126,11 @@ type waitExpireTask struct {
time.Duration
}

func newDialState(static []*enode.Node, bootnodes []*enode.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
func newDialState(self enode.ID, static []*enode.Node, bootnodes []*enode.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
s := &dialstate{
maxDynDials: maxdyn,
ntab: ntab,
self: self,
netrestrict: netrestrict,
static: make(map[enode.ID]*dialTask),
dialing: make(map[enode.ID]connFlag),
Expand Down Expand Up @@ -266,7 +267,7 @@ func (s *dialstate) checkDial(n *enode.Node, peers map[enode.ID]*Peer) error {
return errAlreadyDialing
case peers[n.ID()] != nil:
return errAlreadyConnected
case s.ntab != nil && n.ID() == s.ntab.Self().ID():
case n.ID() == s.self:
return errSelf
case s.netrestrict != nil && !s.netrestrict.Contains(n.IP()):
return errNotWhitelisted
Expand Down
16 changes: 8 additions & 8 deletions p2p/dial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (t fakeTable) ReadRandomNodes(buf []*enode.Node) int { return copy(buf, t)
// This test checks that dynamic dials are launched from discovery results.
func TestDialStateDynDial(t *testing.T) {
runDialTest(t, dialtest{
init: newDialState(nil, nil, fakeTable{}, 5, nil),
init: newDialState(enode.ID{}, nil, nil, fakeTable{}, 5, nil),
rounds: []round{
// A discovery query is launched.
{
Expand Down Expand Up @@ -236,7 +236,7 @@ func TestDialStateDynDialBootnode(t *testing.T) {
newNode(uintID(8), nil),
}
runDialTest(t, dialtest{
init: newDialState(nil, bootnodes, table, 5, nil),
init: newDialState(enode.ID{}, nil, bootnodes, table, 5, nil),
rounds: []round{
// 2 dynamic dials attempted, bootnodes pending fallback interval
{
Expand Down Expand Up @@ -324,7 +324,7 @@ func TestDialStateDynDialFromTable(t *testing.T) {
}

runDialTest(t, dialtest{
init: newDialState(nil, nil, table, 10, nil),
init: newDialState(enode.ID{}, nil, nil, table, 10, nil),
rounds: []round{
// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
{
Expand Down Expand Up @@ -430,7 +430,7 @@ func TestDialStateNetRestrict(t *testing.T) {
restrict.Add("127.0.2.0/24")

runDialTest(t, dialtest{
init: newDialState(nil, nil, table, 10, restrict),
init: newDialState(enode.ID{}, nil, nil, table, 10, restrict),
rounds: []round{
{
new: []task{
Expand All @@ -453,7 +453,7 @@ func TestDialStateStaticDial(t *testing.T) {
}

runDialTest(t, dialtest{
init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
init: newDialState(enode.ID{}, wantStatic, nil, fakeTable{}, 0, nil),
rounds: []round{
// Static dials are launched for the nodes that
// aren't yet connected.
Expand Down Expand Up @@ -557,7 +557,7 @@ func TestDialStaticAfterReset(t *testing.T) {
},
}
dTest := dialtest{
init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
init: newDialState(enode.ID{}, wantStatic, nil, fakeTable{}, 0, nil),
rounds: rounds,
}
runDialTest(t, dTest)
Expand All @@ -578,7 +578,7 @@ func TestDialStateCache(t *testing.T) {
}

runDialTest(t, dialtest{
init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
init: newDialState(enode.ID{}, wantStatic, nil, fakeTable{}, 0, nil),
rounds: []round{
// Static dials are launched for the nodes that
// aren't yet connected.
Expand Down Expand Up @@ -640,7 +640,7 @@ func TestDialStateCache(t *testing.T) {
func TestDialResolve(t *testing.T) {
resolved := newNode(uintID(1), net.IP{127, 0, 55, 234})
table := &resolveMock{answer: resolved}
state := newDialState(nil, nil, table, 0, nil)
state := newDialState(enode.ID{}, nil, nil, table, 0, nil)

// Check that the task is generated with an incomplete ID.
dest := newNode(uintID(1), nil)
Expand Down
27 changes: 12 additions & 15 deletions p2p/discover/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,20 @@ type Table struct {
ips netutil.DistinctNetSet

db *enode.DB // database of known nodes
net transport
refreshReq chan chan struct{}
initDone chan struct{}
closeReq chan struct{}
closed chan struct{}

nodeAddedHook func(*node) // for testing

net transport
self *node // metadata of the local node
}

// transport is implemented by the UDP transport.
// it is an interface so we can test without opening lots of UDP
// sockets and without generating a private key.
type transport interface {
self() *enode.Node
ping(enode.ID, *net.UDPAddr) error
findnode(toid enode.ID, addr *net.UDPAddr, target encPubkey) ([]*node, error)
close()
Expand All @@ -100,11 +99,10 @@ type bucket struct {
ips netutil.DistinctNetSet
}

func newTable(t transport, self *enode.Node, db *enode.DB, bootnodes []*enode.Node) (*Table, error) {
func newTable(t transport, db *enode.DB, bootnodes []*enode.Node) (*Table, error) {
tab := &Table{
net: t,
db: db,
self: wrapNode(self),
refreshReq: make(chan chan struct{}),
initDone: make(chan struct{}),
closeReq: make(chan struct{}),
Expand All @@ -127,6 +125,10 @@ func newTable(t transport, self *enode.Node, db *enode.DB, bootnodes []*enode.No
return tab, nil
}

func (tab *Table) self() *enode.Node {
return tab.net.self()
}

func (tab *Table) seedRand() {
var b [8]byte
crand.Read(b[:])
Expand All @@ -136,11 +138,6 @@ func (tab *Table) seedRand() {
tab.mutex.Unlock()
}

// Self returns the local node.
func (tab *Table) Self() *enode.Node {
return unwrapNode(tab.self)
}

// 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) {
Expand Down Expand Up @@ -257,7 +254,7 @@ func (tab *Table) lookup(targetKey encPubkey, refreshIfEmpty bool) []*node {
)
// don't query further if we hit ourself.
// unlikely to happen often in practice.
asked[tab.self.ID()] = true
asked[tab.self().ID()] = true

for {
tab.mutex.Lock()
Expand Down Expand Up @@ -408,7 +405,7 @@ func (tab *Table) doRefresh(done chan struct{}) {
// Run self lookup to discover new neighbor nodes.
// We can only do this if we have a secp256k1 identity.
var key ecdsa.PublicKey
if err := tab.self.Load((*enode.Secp256k1)(&key)); err == nil {
if err := tab.self().Load((*enode.Secp256k1)(&key)); err == nil {
tab.lookup(encodePubkey(&key), false)
}

Expand Down Expand Up @@ -530,7 +527,7 @@ func (tab *Table) len() (n int) {

// bucket returns the bucket for the given node ID hash.
func (tab *Table) bucket(id enode.ID) *bucket {
d := enode.LogDist(tab.self.ID(), id)
d := enode.LogDist(tab.self().ID(), id)
if d <= bucketMinDistance {
return tab.buckets[0]
}
Expand All @@ -543,7 +540,7 @@ func (tab *Table) bucket(id enode.ID) *bucket {
//
// The caller must not hold tab.mutex.
func (tab *Table) add(n *node) {
if n.ID() == tab.self.ID() {
if n.ID() == tab.self().ID() {
return
}

Expand Down Expand Up @@ -576,7 +573,7 @@ func (tab *Table) stuff(nodes []*node) {
defer tab.mutex.Unlock()

for _, n := range nodes {
if n.ID() == tab.self.ID() {
if n.ID() == tab.self().ID() {
continue // don't add self
}
b := tab.bucket(n.ID())
Expand Down
10 changes: 7 additions & 3 deletions p2p/discover/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestTable_IPLimit(t *testing.T) {
defer db.Close()

for i := 0; i < tableIPLimit+1; i++ {
n := nodeAtDistance(tab.self.ID(), i, net.IP{172, 0, 1, byte(i)})
n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)})
tab.add(n)
}
if tab.len() > tableIPLimit {
Expand All @@ -158,7 +158,7 @@ func TestTable_BucketIPLimit(t *testing.T) {

d := 3
for i := 0; i < bucketIPLimit+1; i++ {
n := nodeAtDistance(tab.self.ID(), d, net.IP{172, 0, 1, byte(i)})
n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)})
tab.add(n)
}
if tab.len() > bucketIPLimit {
Expand Down Expand Up @@ -240,7 +240,7 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {

for i := 0; i < len(buf); i++ {
ld := cfg.Rand.Intn(len(tab.buckets))
tab.stuff([]*node{nodeAtDistance(tab.self.ID(), ld, intIP(ld))})
tab.stuff([]*node{nodeAtDistance(tab.self().ID(), ld, intIP(ld))})
}
gotN := tab.ReadRandomNodes(buf)
if gotN != tab.len() {
Expand Down Expand Up @@ -510,6 +510,10 @@ type preminedTestnet struct {
dists [hashBits + 1][]encPubkey
}

func (tn *preminedTestnet) self() *enode.Node {
return nullNode
}

func (tn *preminedTestnet) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
// current log distance is encoded in port number
// fmt.Println("findnode query at dist", toaddr.Port)
Expand Down
25 changes: 20 additions & 5 deletions p2p/discover/table_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@ import (
"github.com/ethereum/go-ethereum/p2p/enr"
)

func newTestTable(t transport) (*Table, *enode.DB) {
var nullNode *enode.Node

func init() {
var r enr.Record
r.Set(enr.IP{0, 0, 0, 0})
n := enode.SignNull(&r, enode.ID{})
nullNode = enode.SignNull(&r, enode.ID{})
}

func newTestTable(t transport) (*Table, *enode.DB) {
db, _ := enode.OpenDB("")
tab, _ := newTable(t, n, db, nil)
tab, _ := newTable(t, db, nil)
return tab, db
}

Expand Down Expand Up @@ -70,26 +75,36 @@ func intIP(i int) net.IP {

// fillBucket inserts nodes into the given bucket until it is full.
func fillBucket(tab *Table, n *node) (last *node) {
ld := enode.LogDist(tab.self.ID(), n.ID())
ld := enode.LogDist(tab.self().ID(), n.ID())
b := tab.bucket(n.ID())
for len(b.entries) < bucketSize {
b.entries = append(b.entries, nodeAtDistance(tab.self.ID(), ld, intIP(ld)))
b.entries = append(b.entries, nodeAtDistance(tab.self().ID(), ld, intIP(ld)))
}
return b.entries[bucketSize-1]
}

type pingRecorder struct {
mu sync.Mutex
dead, pinged map[enode.ID]bool
n *enode.Node
}

func newPingRecorder() *pingRecorder {
var r enr.Record
r.Set(enr.IP{0, 0, 0, 0})
n := enode.SignNull(&r, enode.ID{})

return &pingRecorder{
dead: make(map[enode.ID]bool),
pinged: make(map[enode.ID]bool),
n: n,
}
}

func (t *pingRecorder) self() *enode.Node {
return nullNode
}

func (t *pingRecorder) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
return nil, nil
}
Expand Down
Loading