@@ -6,59 +6,62 @@ import (
6
6
"encoding/hex"
7
7
"errors"
8
8
"fmt"
9
- "io"
10
9
"math/big"
11
10
"math/rand"
12
11
"net"
13
12
"net/url"
14
13
"strconv"
15
14
"strings"
16
15
16
+ "github.com/ethereum/go-ethereum/common"
17
17
"github.com/ethereum/go-ethereum/crypto"
18
18
"github.com/ethereum/go-ethereum/crypto/secp256k1"
19
- "github.com/ethereum/go-ethereum/rlp"
20
19
)
21
20
22
21
const nodeIDBits = 512
23
22
24
23
// Node represents a host on the network.
25
24
type Node struct {
26
- ID NodeID
27
- IP net.IP
25
+ IP net.IP // len 4 for IPv4 or 16 for IPv6
26
+ UDP , TCP uint16 // port numbers
27
+ ID NodeID // the node's public key
28
28
29
- DiscPort int // UDP listening port for discovery protocol
30
- TCPPort int // TCP listening port for RLPx
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
31
35
}
32
36
33
- func newNode (id NodeID , addr * net.UDPAddr ) * Node {
37
+ func newNode (id NodeID , ip net.IP , udpPort , tcpPort uint16 ) * Node {
38
+ if ipv4 := ip .To4 (); ipv4 != nil {
39
+ ip = ipv4
40
+ }
34
41
return & Node {
35
- ID : id ,
36
- IP : addr .IP ,
37
- DiscPort : addr .Port ,
38
- TCPPort : addr .Port ,
42
+ IP : ip ,
43
+ UDP : udpPort ,
44
+ TCP : tcpPort ,
45
+ ID : id ,
46
+ sha : crypto .Sha3Hash (id [:]),
39
47
}
40
48
}
41
49
42
- func (n * Node ) isValid () bool {
43
- // TODO: don't accept localhost, LAN addresses from internet hosts
44
- return ! n .IP .IsMulticast () && ! n .IP .IsUnspecified () && n .TCPPort != 0 && n .DiscPort != 0
45
- }
46
-
47
50
func (n * Node ) addr () * net.UDPAddr {
48
- return & net.UDPAddr {IP : n .IP , Port : n . DiscPort }
51
+ return & net.UDPAddr {IP : n .IP , Port : int ( n . UDP ) }
49
52
}
50
53
51
54
// The string representation of a Node is a URL.
52
55
// Please see ParseNode for a description of the format.
53
56
func (n * Node ) String () string {
54
- addr := net.TCPAddr {IP : n .IP , Port : n . TCPPort }
57
+ addr := net.TCPAddr {IP : n .IP , Port : int ( n . TCP ) }
55
58
u := url.URL {
56
59
Scheme : "enode" ,
57
60
User : url .User (fmt .Sprintf ("%x" , n .ID [:])),
58
61
Host : addr .String (),
59
62
}
60
- if n .DiscPort != n .TCPPort {
61
- u .RawQuery = "discport=" + strconv .Itoa (n . DiscPort )
63
+ if n .UDP != n .TCP {
64
+ u .RawQuery = "discport=" + strconv .Itoa (int ( n . UDP ) )
62
65
}
63
66
return u .String ()
64
67
}
@@ -80,36 +83,47 @@ func (n *Node) String() string {
80
83
//
81
84
// enode://<hex node id>@10.3.58.6:30303?discport=30301
82
85
func ParseNode (rawurl string ) (* Node , error ) {
83
- var n Node
86
+ var (
87
+ id NodeID
88
+ ip net.IP
89
+ tcpPort , udpPort uint64
90
+ )
84
91
u , err := url .Parse (rawurl )
85
92
if u .Scheme != "enode" {
86
93
return nil , errors .New ("invalid URL scheme, want \" enode\" " )
87
94
}
95
+ // Parse the Node ID from the user portion.
88
96
if u .User == nil {
89
97
return nil , errors .New ("does not contain node ID" )
90
98
}
91
- if n . ID , err = HexID (u .User .String ()); err != nil {
99
+ if id , err = HexID (u .User .String ()); err != nil {
92
100
return nil , fmt .Errorf ("invalid node ID (%v)" , err )
93
101
}
94
- ip , port , err := net .SplitHostPort (u .Host )
102
+ // Parse the IP address.
103
+ host , port , err := net .SplitHostPort (u .Host )
95
104
if err != nil {
96
105
return nil , fmt .Errorf ("invalid host: %v" , err )
97
106
}
98
- if n . IP = net .ParseIP (ip ); n . IP == nil {
107
+ if ip = net .ParseIP (host ); ip == nil {
99
108
return nil , errors .New ("invalid IP address" )
100
109
}
101
- if n .TCPPort , err = strconv .Atoi (port ); 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 {
102
116
return nil , errors .New ("invalid port" )
103
117
}
118
+ udpPort = tcpPort
104
119
qv := u .Query ()
105
- if qv .Get ("discport" ) == "" {
106
- n .DiscPort = n .TCPPort
107
- } else {
108
- if n .DiscPort , err = strconv .Atoi (qv .Get ("discport" )); err != nil {
120
+ if qv .Get ("discport" ) != "" {
121
+ udpPort , err = strconv .ParseUint (qv .Get ("discport" ), 10 , 16 )
122
+ if err != nil {
109
123
return nil , errors .New ("invalid discport in query" )
110
124
}
111
125
}
112
- return & n , nil
126
+ return newNode ( id , ip , uint16 ( udpPort ), uint16 ( tcpPort )) , nil
113
127
}
114
128
115
129
// MustParseNode parses a node URL. It panics if the URL is not valid.
@@ -121,22 +135,6 @@ func MustParseNode(rawurl string) *Node {
121
135
return n
122
136
}
123
137
124
- func (n Node ) EncodeRLP (w io.Writer ) error {
125
- return rlp .Encode (w , rpcNode {IP : n .IP .String (), Port : uint16 (n .TCPPort ), ID : n .ID })
126
- }
127
- func (n * Node ) DecodeRLP (s * rlp.Stream ) (err error ) {
128
- var ext rpcNode
129
- if err = s .Decode (& ext ); err == nil {
130
- n .TCPPort = int (ext .Port )
131
- n .DiscPort = int (ext .Port )
132
- n .ID = ext .ID
133
- if n .IP = net .ParseIP (ext .IP ); n .IP == nil {
134
- return errors .New ("invalid IP string" )
135
- }
136
- }
137
- return err
138
- }
139
-
140
138
// NodeID is a unique identifier for each node.
141
139
// The node identifier is a marshaled elliptic curve public key.
142
140
type NodeID [nodeIDBits / 8 ]byte
@@ -221,7 +219,7 @@ func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
221
219
// distcmp compares the distances a->target and b->target.
222
220
// Returns -1 if a is closer to target, 1 if b is closer to target
223
221
// and 0 if they are equal.
224
- func distcmp (target , a , b NodeID ) int {
222
+ func distcmp (target , a , b common. Hash ) int {
225
223
for i := range target {
226
224
da := a [i ] ^ target [i ]
227
225
db := b [i ] ^ target [i ]
@@ -271,7 +269,7 @@ var lzcount = [256]int{
271
269
}
272
270
273
271
// logdist returns the logarithmic distance between a and b, log2(a ^ b).
274
- func logdist (a , b NodeID ) int {
272
+ func logdist (a , b common. Hash ) int {
275
273
lz := 0
276
274
for i := range a {
277
275
x := a [i ] ^ b [i ]
@@ -285,8 +283,8 @@ func logdist(a, b NodeID) int {
285
283
return len (a )* 8 - lz
286
284
}
287
285
288
- // randomID returns a random NodeID such that logdist(a, b) == n
289
- func randomID (a NodeID , n int ) (b NodeID ) {
286
+ // hashAtDistance returns a random hash such that logdist(a, b) == n
287
+ func hashAtDistance (a common. Hash , n int ) (b common. Hash ) {
290
288
if n == 0 {
291
289
return a
292
290
}
0 commit comments