Skip to content

Commit 72ab6d3

Browse files
committed
p2p/discover: track sha3(ID) in Node
1 parent b34a8ef commit 72ab6d3

File tree

8 files changed

+166
-99
lines changed

8 files changed

+166
-99
lines changed

p2p/discover/database.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"sync"
1111
"time"
1212

13+
"github.com/ethereum/go-ethereum/crypto"
1314
"github.com/ethereum/go-ethereum/logger"
1415
"github.com/ethereum/go-ethereum/logger/glog"
1516
"github.com/ethereum/go-ethereum/rlp"
@@ -167,6 +168,7 @@ func (db *nodeDB) node(id NodeID) *Node {
167168
glog.V(logger.Warn).Infof("failed to decode node RLP: %v", err)
168169
return nil
169170
}
171+
node.sha = crypto.Sha3Hash(node.ID[:])
170172
return node
171173
}
172174

p2p/discover/database_test.go

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ func TestNodeDBInt64(t *testing.T) {
8686
}
8787

8888
func TestNodeDBFetchStore(t *testing.T) {
89-
node := &Node{
90-
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
91-
IP: net.IP([]byte{192, 168, 0, 1}),
92-
UDP: 30303,
93-
TCP: 30303,
94-
}
89+
node := newNode(
90+
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
91+
net.IP{192, 168, 0, 1},
92+
30303,
93+
30303,
94+
)
9595
inst := time.Now()
9696

9797
db, _ := newNodeDB("", Version)
@@ -132,28 +132,34 @@ func TestNodeDBFetchStore(t *testing.T) {
132132
}
133133

134134
var nodeDBSeedQueryNodes = []struct {
135-
node Node
135+
node *Node
136136
pong time.Time
137137
}{
138138
{
139-
node: Node{
140-
ID: MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
141-
IP: []byte{127, 0, 0, 1},
142-
},
139+
node: newNode(
140+
MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
141+
net.IP{127, 0, 0, 1},
142+
30303,
143+
30303,
144+
),
143145
pong: time.Now().Add(-2 * time.Second),
144146
},
145147
{
146-
node: Node{
147-
ID: MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
148-
IP: []byte{127, 0, 0, 2},
149-
},
148+
node: newNode(
149+
MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
150+
net.IP{127, 0, 0, 2},
151+
30303,
152+
30303,
153+
),
150154
pong: time.Now().Add(-3 * time.Second),
151155
},
152156
{
153-
node: Node{
154-
ID: MustHexID("0x03d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
155-
IP: []byte{127, 0, 0, 3},
156-
},
157+
node: newNode(
158+
MustHexID("0x03d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
159+
net.IP{127, 0, 0, 3},
160+
30303,
161+
30303,
162+
),
157163
pong: time.Now().Add(-1 * time.Second),
158164
},
159165
}
@@ -164,7 +170,7 @@ func TestNodeDBSeedQuery(t *testing.T) {
164170

165171
// Insert a batch of nodes for querying
166172
for i, seed := range nodeDBSeedQueryNodes {
167-
if err := db.updateNode(&seed.node); err != nil {
173+
if err := db.updateNode(seed.node); err != nil {
168174
t.Fatalf("node %d: failed to insert: %v", i, err)
169175
}
170176
}
@@ -204,7 +210,7 @@ func TestNodeDBSeedQueryContinuation(t *testing.T) {
204210

205211
// Insert a batch of nodes for querying
206212
for i, seed := range nodeDBSeedQueryNodes {
207-
if err := db.updateNode(&seed.node); err != nil {
213+
if err := db.updateNode(seed.node); err != nil {
208214
t.Fatalf("node %d: failed to insert: %v", i, err)
209215
}
210216
}
@@ -268,22 +274,26 @@ func TestNodeDBPersistency(t *testing.T) {
268274
}
269275

270276
var nodeDBExpirationNodes = []struct {
271-
node Node
277+
node *Node
272278
pong time.Time
273279
exp bool
274280
}{
275281
{
276-
node: Node{
277-
ID: MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
278-
IP: []byte{127, 0, 0, 1},
279-
},
282+
node: newNode(
283+
MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
284+
net.IP{127, 0, 0, 1},
285+
30303,
286+
30303,
287+
),
280288
pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute),
281289
exp: false,
282290
}, {
283-
node: Node{
284-
ID: MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
285-
IP: []byte{127, 0, 0, 2},
286-
},
291+
node: newNode(
292+
MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
293+
net.IP{127, 0, 0, 2},
294+
30303,
295+
30303,
296+
),
287297
pong: time.Now().Add(-nodeDBNodeExpiration - time.Minute),
288298
exp: true,
289299
},
@@ -295,7 +305,7 @@ func TestNodeDBExpiration(t *testing.T) {
295305

296306
// Add all the test nodes and set their last pong time
297307
for i, seed := range nodeDBExpirationNodes {
298-
if err := db.updateNode(&seed.node); err != nil {
308+
if err := db.updateNode(seed.node); err != nil {
299309
t.Fatalf("node %d: failed to insert: %v", i, err)
300310
}
301311
if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil {

p2p/discover/node.go

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"strconv"
1414
"strings"
1515

16+
"github.com/ethereum/go-ethereum/common"
1617
"github.com/ethereum/go-ethereum/crypto"
1718
"github.com/ethereum/go-ethereum/crypto/secp256k1"
1819
)
@@ -23,19 +24,26 @@ const nodeIDBits = 512
2324
type Node struct {
2425
IP net.IP // len 4 for IPv4 or 16 for IPv6
2526
UDP, TCP uint16 // port numbers
26-
ID NodeID
27+
ID NodeID // the node's public key
28+
29+
// This is a cached copy of sha3(ID) which is used for node
30+
// distance calculations. This is part of Node in order to make it
31+
// possible to write tests that need a node at a certain distance.
32+
// In those tests, the content of sha will not actually correspond
33+
// with ID.
34+
sha common.Hash
2735
}
2836

29-
func newNode(id NodeID, addr *net.UDPAddr) *Node {
30-
ip := addr.IP.To4()
31-
if ip == nil {
32-
ip = addr.IP.To16()
37+
func newNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node {
38+
if ipv4 := ip.To4(); ipv4 != nil {
39+
ip = ipv4
3340
}
3441
return &Node{
3542
IP: ip,
36-
UDP: uint16(addr.Port),
37-
TCP: uint16(addr.Port),
43+
UDP: udpPort,
44+
TCP: tcpPort,
3845
ID: id,
46+
sha: crypto.Sha3Hash(id[:]),
3947
}
4048
}
4149

@@ -75,40 +83,47 @@ func (n *Node) String() string {
7583
//
7684
// enode://<hex node id>@10.3.58.6:30303?discport=30301
7785
func ParseNode(rawurl string) (*Node, error) {
78-
var n Node
86+
var (
87+
id NodeID
88+
ip net.IP
89+
tcpPort, udpPort uint64
90+
)
7991
u, err := url.Parse(rawurl)
8092
if u.Scheme != "enode" {
8193
return nil, errors.New("invalid URL scheme, want \"enode\"")
8294
}
95+
// Parse the Node ID from the user portion.
8396
if u.User == nil {
8497
return nil, errors.New("does not contain node ID")
8598
}
86-
if n.ID, err = HexID(u.User.String()); err != nil {
99+
if id, err = HexID(u.User.String()); err != nil {
87100
return nil, fmt.Errorf("invalid node ID (%v)", err)
88101
}
89-
ip, port, err := net.SplitHostPort(u.Host)
102+
// Parse the IP address.
103+
host, port, err := net.SplitHostPort(u.Host)
90104
if err != nil {
91105
return nil, fmt.Errorf("invalid host: %v", err)
92106
}
93-
if n.IP = net.ParseIP(ip); n.IP == nil {
107+
if ip = net.ParseIP(host); ip == nil {
94108
return nil, errors.New("invalid IP address")
95109
}
96-
tcp, err := strconv.ParseUint(port, 10, 16)
97-
if err != nil {
110+
// Ensure the IP is 4 bytes long for IPv4 addresses.
111+
if ipv4 := ip.To4(); ipv4 != nil {
112+
ip = ipv4
113+
}
114+
// Parse the port numbers.
115+
if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
98116
return nil, errors.New("invalid port")
99117
}
100-
n.TCP = uint16(tcp)
118+
udpPort = tcpPort
101119
qv := u.Query()
102-
if qv.Get("discport") == "" {
103-
n.UDP = n.TCP
104-
} else {
105-
udp, err := strconv.ParseUint(qv.Get("discport"), 10, 16)
120+
if qv.Get("discport") != "" {
121+
udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
106122
if err != nil {
107123
return nil, errors.New("invalid discport in query")
108124
}
109-
n.UDP = uint16(udp)
110125
}
111-
return &n, nil
126+
return newNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil
112127
}
113128

114129
// MustParseNode parses a node URL. It panics if the URL is not valid.

p2p/discover/node_test.go

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,46 +48,61 @@ var parseNodeTests = []struct {
4848
},
4949
{
5050
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
51-
wantResult: &Node{
52-
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
53-
IP: net.ParseIP("127.0.0.1"),
54-
UDP: 52150,
55-
TCP: 52150,
56-
},
51+
wantResult: newNode(
52+
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
53+
net.IP{0x7f, 0x0, 0x0, 0x1},
54+
52150,
55+
52150,
56+
),
5757
},
5858
{
5959
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
60-
wantResult: &Node{
61-
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
62-
IP: net.ParseIP("::"),
63-
UDP: 52150,
64-
TCP: 52150,
65-
},
60+
wantResult: newNode(
61+
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
62+
net.ParseIP("::"),
63+
52150,
64+
52150,
65+
),
66+
},
67+
{
68+
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
69+
wantResult: newNode(
70+
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
71+
net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
72+
52150,
73+
52150,
74+
),
6675
},
6776
{
6877
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
69-
wantResult: &Node{
70-
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
71-
IP: net.ParseIP("127.0.0.1"),
72-
UDP: 22334,
73-
TCP: 52150,
74-
},
78+
wantResult: newNode(
79+
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
80+
net.IP{0x7f, 0x0, 0x0, 0x1},
81+
22334,
82+
52150,
83+
),
7584
},
7685
}
7786

7887
func TestParseNode(t *testing.T) {
7988
for i, test := range parseNodeTests {
8089
n, err := ParseNode(test.rawurl)
81-
if err == nil && test.wantError != "" {
82-
t.Errorf("test %d: got nil error, expected %#q", i, test.wantError)
83-
continue
84-
}
85-
if err != nil && err.Error() != test.wantError {
86-
t.Errorf("test %d: got error %#q, expected %#q", i, err.Error(), test.wantError)
87-
continue
88-
}
89-
if !reflect.DeepEqual(n, test.wantResult) {
90-
t.Errorf("test %d: result mismatch:\ngot: %#v, want: %#v", i, n, test.wantResult)
90+
if test.wantError != "" {
91+
if err == nil {
92+
t.Errorf("test %d: got nil error, expected %#q", i, test.wantError)
93+
continue
94+
} else if err.Error() != test.wantError {
95+
t.Errorf("test %d: got error %#q, expected %#q", i, err.Error(), test.wantError)
96+
continue
97+
}
98+
} else {
99+
if err != nil {
100+
t.Errorf("test %d: unexpected error: %v", i, err)
101+
continue
102+
}
103+
if !reflect.DeepEqual(n, test.wantResult) {
104+
t.Errorf("test %d: result mismatch:\ngot: %#v, want: %#v", i, n, test.wantResult)
105+
}
91106
}
92107
}
93108
}

p2p/discover/table.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"sync"
1313
"time"
1414

15+
"github.com/ethereum/go-ethereum/crypto"
1516
"github.com/ethereum/go-ethereum/logger"
1617
"github.com/ethereum/go-ethereum/logger/glog"
1718
)
@@ -71,7 +72,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
7172
tab := &Table{
7273
net: t,
7374
db: db,
74-
self: newNode(ourID, ourAddr),
75+
self: newNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)),
7576
bonding: make(map[NodeID]*bondproc),
7677
bondslots: make(chan struct{}, maxBondingPingPongs),
7778
}
@@ -105,6 +106,7 @@ func (tab *Table) Bootstrap(nodes []*Node) {
105106
tab.nursery = make([]*Node, 0, len(nodes))
106107
for _, n := range nodes {
107108
cpy := *n
109+
cpy.sha = crypto.Sha3Hash(n.ID[:])
108110
tab.nursery = append(tab.nursery, &cpy)
109111
}
110112
tab.mutex.Unlock()
@@ -299,7 +301,7 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd
299301
tab.net.waitping(id)
300302
}
301303
// Bonding succeeded, update the node database
302-
w.n = &Node{ID: id, IP: addr.IP, UDP: uint16(addr.Port), TCP: tcpPort}
304+
w.n = newNode(id, addr.IP, uint16(addr.Port), tcpPort)
303305
tab.db.updateNode(w.n)
304306
close(w.done)
305307
}
@@ -340,9 +342,8 @@ func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error {
340342
func (tab *Table) add(entries []*Node) {
341343
outer:
342344
for _, n := range entries {
343-
if n == nil || n.ID == tab.self.ID {
344-
// skip bad entries. The RLP decoder returns nil for empty
345-
// input lists.
345+
if n.ID == tab.self.ID {
346+
// don't add self.
346347
continue
347348
}
348349
bucket := tab.buckets[logdist(tab.self.ID, n.ID)]

p2p/discover/table_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ func TestTable_Lookup(t *testing.T) {
224224
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
225225
}
226226
// seed table with initial node (otherwise lookup will terminate immediately)
227-
tab.add([]*Node{newNode(randomID(target, 200), &net.UDPAddr{Port: 200})})
227+
tab.add([]*Node{newNode(randomID(target, 200), net.ParseIP("127.0.0.1"), 200, 200)})
228228

229229
results := tab.Lookup(target)
230230
t.Logf("results:")

0 commit comments

Comments
 (0)