Skip to content

Commit a20bd12

Browse files
fjljakub-freebit
authored andcommitted
p2p/discover: refactor node and endpoint representation (ethereum#29844)
Here we clean up internal uses of type discover.node, converting most code to use enode.Node instead. The discover.node type used to be the canonical representation of network hosts before ENR was introduced. Most code worked with *node to avoid conversions when interacting with Table methods. Since *node also contains internal state of Table and is a mutable type, using *node outside of Table code is prone to data races. It's also cleaner not having to wrap/unwrap *enode.Node all the time. discover.node has been renamed to tableNode to clarify its purpose. While here, we also change most uses of net.UDPAddr into netip.AddrPort. While this is technically a separate refactoring from the *node -> *enode.Node change, it is more convenient because *enode.Node handles IP addresses as netip.Addr. The switch to package netip in discovery would've happened very soon anyway. The change to netip.AddrPort stops at certain interface points. For example, since package p2p/netutil has not been converted to use netip.Addr yet, we still have to convert to net.IP/net.UDPAddr in a few places.
1 parent b79a5fe commit a20bd12

File tree

18 files changed

+426
-414
lines changed

18 files changed

+426
-414
lines changed

cmd/devp2p/internal/v4test/framework.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint {
110110
}
111111

112112
func (te *testenv) remoteEndpoint() v4wire.Endpoint {
113-
return v4wire.NewEndpoint(te.remoteAddr, 0)
113+
return v4wire.NewEndpoint(te.remoteAddr.AddrPort(), 0)
114114
}
115115

116116
func contains(ns []v4wire.Node, key v4wire.Pubkey) bool {

p2p/discover/common.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"encoding/binary"
2323
"math/rand"
2424
"net"
25+
"net/netip"
2526
"sync"
2627
"time"
2728

@@ -34,8 +35,8 @@ import (
3435

3536
// UDPConn is a network connection on which discovery can operate.
3637
type UDPConn interface {
37-
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
38-
WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error)
38+
ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error)
39+
WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (n int, err error)
3940
Close() error
4041
LocalAddr() net.Addr
4142
}
@@ -96,7 +97,7 @@ func ListenUDP(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) {
9697
// channel if configured.
9798
type ReadPacket struct {
9899
Data []byte
99-
Addr *net.UDPAddr
100+
Addr netip.AddrPort
100101
}
101102

102103
type randomSource interface {

p2p/discover/lookup.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@ import (
2929
// not need to be an actual node identifier.
3030
type lookup struct {
3131
tab *Table
32-
queryfunc func(*node) ([]*node, error)
33-
replyCh chan []*node
32+
queryfunc queryFunc
33+
replyCh chan []*enode.Node
3434
cancelCh <-chan struct{}
3535
asked, seen map[enode.ID]bool
3636
result nodesByDistance
37-
replyBuffer []*node
37+
replyBuffer []*enode.Node
3838
queries int
3939
}
4040

41-
type queryFunc func(*node) ([]*node, error)
41+
type queryFunc func(*enode.Node) ([]*enode.Node, error)
4242

4343
func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup {
4444
it := &lookup{
@@ -47,7 +47,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
4747
asked: make(map[enode.ID]bool),
4848
seen: make(map[enode.ID]bool),
4949
result: nodesByDistance{target: target},
50-
replyCh: make(chan []*node, alpha),
50+
replyCh: make(chan []*enode.Node, alpha),
5151
cancelCh: ctx.Done(),
5252
queries: -1,
5353
}
@@ -61,7 +61,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
6161
func (it *lookup) run() []*enode.Node {
6262
for it.advance() {
6363
}
64-
return unwrapNodes(it.result.entries)
64+
return it.result.entries
6565
}
6666

6767
// advance advances the lookup until any new nodes have been found.
@@ -145,7 +145,7 @@ func (it *lookup) slowdown(d time.Duration) {
145145
}
146146
}
147147

148-
func (it *lookup) query(n *node, reply chan<- []*node) {
148+
func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) {
149149
// ADDED by Jakub Pajek (reduce disc traffic)
150150
it.slowdown(1 * time.Second)
151151
r, err := it.queryfunc(n)
@@ -162,7 +162,7 @@ func (it *lookup) query(n *node, reply chan<- []*node) {
162162
// lookupIterator performs lookup operations and iterates over all seen nodes.
163163
// When a lookup finishes, a new one is created through nextLookup.
164164
type lookupIterator struct {
165-
buffer []*node
165+
buffer []*enode.Node
166166
nextLookup lookupFunc
167167
ctx context.Context
168168
cancel func()
@@ -181,7 +181,7 @@ func (it *lookupIterator) Node() *enode.Node {
181181
if len(it.buffer) == 0 {
182182
return nil
183183
}
184-
return unwrapNode(it.buffer[0])
184+
return it.buffer[0]
185185
}
186186

187187
// Next moves to the next node.

p2p/discover/metrics.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package discover
1818

1919
import (
2020
"fmt"
21-
"net"
21+
"net/netip"
2222

2323
"github.com/ethereum/go-ethereum/metrics"
2424
)
@@ -58,16 +58,16 @@ func newMeteredConn(conn UDPConn) UDPConn {
5858
return &meteredUdpConn{UDPConn: conn}
5959
}
6060

61-
// ReadFromUDP delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way.
62-
func (c *meteredUdpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
63-
n, addr, err = c.UDPConn.ReadFromUDP(b)
61+
// ReadFromUDPAddrPort delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way.
62+
func (c *meteredUdpConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
63+
n, addr, err = c.UDPConn.ReadFromUDPAddrPort(b)
6464
ingressTrafficMeter.Mark(int64(n))
6565
return n, addr, err
6666
}
6767

68-
// Write delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way.
69-
func (c *meteredUdpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) {
70-
n, err = c.UDPConn.WriteToUDP(b, addr)
68+
// WriteToUDP delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way.
69+
func (c *meteredUdpConn) WriteToUDP(b []byte, addr netip.AddrPort) (n int, err error) {
70+
n, err = c.UDPConn.WriteToUDPAddrPort(b, addr)
7171
egressTrafficMeter.Mark(int64(n))
7272
return n, err
7373
}

p2p/discover/node.go

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import (
2121
"crypto/elliptic"
2222
"errors"
2323
"math/big"
24-
"net"
24+
"slices"
25+
"sort"
2526
"time"
2627

2728
"github.com/ethereum/go-ethereum/common/math"
@@ -37,9 +38,8 @@ type BucketNode struct {
3738
Live bool `json:"live"`
3839
}
3940

40-
// node represents a host on the network.
41-
// The fields of Node may not be modified.
42-
type node struct {
41+
// tableNode is an entry in Table.
42+
type tableNode struct {
4343
*enode.Node
4444
revalList *revalidationList
4545
addedToTable time.Time // first time node was added to bucket or replacement list
@@ -75,34 +75,59 @@ func (e encPubkey) id() enode.ID {
7575
return enode.ID(crypto.Keccak256Hash(e[:]))
7676
}
7777

78-
func wrapNode(n *enode.Node) *node {
79-
return &node{Node: n}
80-
}
81-
82-
func wrapNodes(ns []*enode.Node) []*node {
83-
result := make([]*node, len(ns))
78+
func unwrapNodes(ns []*tableNode) []*enode.Node {
79+
result := make([]*enode.Node, len(ns))
8480
for i, n := range ns {
85-
result[i] = wrapNode(n)
81+
result[i] = n.Node
8682
}
8783
return result
8884
}
8985

90-
func unwrapNode(n *node) *enode.Node {
91-
return n.Node
86+
func (n *tableNode) String() string {
87+
return n.Node.String()
88+
}
89+
90+
// nodesByDistance is a list of nodes, ordered by distance to target.
91+
type nodesByDistance struct {
92+
entries []*enode.Node
93+
target enode.ID
9294
}
9395

94-
func unwrapNodes(ns []*node) []*enode.Node {
95-
result := make([]*enode.Node, len(ns))
96-
for i, n := range ns {
97-
result[i] = unwrapNode(n)
96+
// push adds the given node to the list, keeping the total size below maxElems.
97+
func (h *nodesByDistance) push(n *enode.Node, maxElems int) {
98+
ix := sort.Search(len(h.entries), func(i int) bool {
99+
return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
100+
})
101+
102+
end := len(h.entries)
103+
if len(h.entries) < maxElems {
104+
h.entries = append(h.entries, n)
105+
}
106+
if ix < end {
107+
// Slide existing entries down to make room.
108+
// This will overwrite the entry we just appended.
109+
copy(h.entries[ix+1:], h.entries[ix:])
110+
h.entries[ix] = n
98111
}
99-
return result
100112
}
101113

102-
func (n *node) addr() *net.UDPAddr {
103-
return &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
114+
type nodeType interface {
115+
ID() enode.ID
104116
}
105117

106-
func (n *node) String() string {
107-
return n.Node.String()
118+
// containsID reports whether ns contains a node with the given ID.
119+
func containsID[N nodeType](ns []N, id enode.ID) bool {
120+
for _, n := range ns {
121+
if n.ID() == id {
122+
return true
123+
}
124+
}
125+
return false
126+
}
127+
128+
// deleteNode removes a node from the list.
129+
func deleteNode[N nodeType](list []N, id enode.ID) []N {
130+
return slices.DeleteFunc(list, func(n N) bool {
131+
return n.ID() == id
132+
})
108133
}

0 commit comments

Comments
 (0)