github.com/core-coin/go-core/v2@v2.1.9/p2p/discv5/node.go (about) 1 // Copyright 2015 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discv5 18 19 import ( 20 "bytes" 21 "encoding/hex" 22 "errors" 23 "fmt" 24 "math/rand" 25 "net" 26 "net/url" 27 "regexp" 28 "strconv" 29 "strings" 30 31 "github.com/core-coin/go-core/v2/common" 32 "github.com/core-coin/go-core/v2/crypto" 33 "github.com/core-coin/go-core/v2/log" 34 ) 35 36 // Node represents a host on the network. 37 // The public fields of Node may not be modified. 38 type Node struct { 39 IP net.IP // len 4 for IPv4 or 16 for IPv6 40 UDP, TCP uint16 // port numbers 41 ID NodeID // the node's public key 42 43 // Network-related fields are contained in nodeNetGuts. 44 // These fields are not supposed to be used off the 45 // Network.loop goroutine. 46 nodeNetGuts 47 } 48 49 // NewNode creates a new node. It is mostly meant to be used for 50 // testing purposes. 51 func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { 52 if ipv4 := ip.To4(); ipv4 != nil { 53 ip = ipv4 54 } 55 return &Node{ 56 IP: ip, 57 UDP: udpPort, 58 TCP: tcpPort, 59 ID: id, 60 nodeNetGuts: nodeNetGuts{sha: crypto.SHA3Hash(id[:])}, 61 } 62 } 63 64 func (n *Node) addr() *net.UDPAddr { 65 return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} 66 } 67 68 // Incomplete returns true for nodes with no IP address. 69 func (n *Node) Incomplete() bool { 70 return n.IP == nil 71 } 72 73 // checks whether n is a valid complete node. 74 func (n *Node) validateComplete() error { 75 if n.Incomplete() { 76 return errors.New("incomplete node") 77 } 78 if n.UDP == 0 { 79 return errors.New("missing UDP port") 80 } 81 if n.TCP == 0 { 82 return errors.New("missing TCP port") 83 } 84 if n.IP.IsMulticast() || n.IP.IsUnspecified() { 85 return errors.New("invalid IP (multicast/unspecified)") 86 } 87 _, err := n.ID.Pubkey() // validate the key (on curve, etc.) 88 return err 89 } 90 91 // The string representation of a Node is a URL. 92 // Please see ParseNode for a description of the format. 93 func (n *Node) String() string { 94 u := url.URL{Scheme: "enode"} 95 if n.Incomplete() { 96 u.Host = fmt.Sprintf("%x", n.ID[:]) 97 } else { 98 addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} 99 u.User = url.User(fmt.Sprintf("%x", n.ID[:])) 100 u.Host = addr.String() 101 if n.UDP != n.TCP { 102 u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) 103 } 104 } 105 return u.String() 106 } 107 108 var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") 109 110 // ParseNode parses a node designator. 111 // 112 // There are two basic forms of node designators 113 // - incomplete nodes, which only have the public key (node ID) 114 // - complete nodes, which contain the public key and IP/Port information 115 // 116 // For incomplete nodes, the designator must look like one of these 117 // 118 // enode://<hex node id> 119 // <hex node id> 120 // 121 // For complete nodes, the node ID is encoded in the username portion 122 // of the URL, separated from the host by an @ sign. The hostname can 123 // only be given as an IP address, DNS domain names are not allowed. 124 // The port in the host name section is the TCP listening port. If the 125 // TCP and UDP (discovery) ports differ, the UDP port is specified as 126 // query parameter "discport". 127 // 128 // In the following example, the node URL describes 129 // a node with IP address 10.3.58.6, TCP listening port 30300 130 // and UDP discovery port 30330. 131 // 132 // enode://<hex node id>@10.3.58.6:30300?discport=30330 133 func ParseNode(rawurl string) (*Node, error) { 134 if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { 135 id, err := HexID(m[1]) 136 if err != nil { 137 return nil, fmt.Errorf("invalid node ID (%v)", err) 138 } 139 return NewNode(id, nil, 0, 0), nil 140 } 141 return parseComplete(rawurl) 142 } 143 144 func parseComplete(rawurl string) (*Node, error) { 145 var ( 146 id NodeID 147 ip net.IP 148 tcpPort, udpPort uint64 149 ) 150 u, err := url.Parse(rawurl) 151 if err != nil { 152 return nil, err 153 } 154 if u.Scheme != "enode" { 155 return nil, errors.New("invalid URL scheme, want \"enode\"") 156 } 157 // Parse the Node ID from the user portion. 158 if u.User == nil { 159 return nil, errors.New("does not contain node ID") 160 } 161 if id, err = HexID(u.User.String()); err != nil { 162 return nil, fmt.Errorf("invalid node ID (%v)", err) 163 } 164 // Parse the IP address. 165 host, port, err := net.SplitHostPort(u.Host) 166 if err != nil { 167 return nil, fmt.Errorf("invalid host: %v", err) 168 } 169 if ip = net.ParseIP(host); ip == nil { 170 return nil, errors.New("invalid IP address") 171 } 172 // Ensure the IP is 4 bytes long for IPv4 addresses. 173 if ipv4 := ip.To4(); ipv4 != nil { 174 ip = ipv4 175 } 176 // Parse the port numbers. 177 if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { 178 return nil, errors.New("invalid port") 179 } 180 udpPort = tcpPort 181 qv := u.Query() 182 if qv.Get("discport") != "" { 183 udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) 184 if err != nil { 185 return nil, errors.New("invalid discport in query") 186 } 187 } 188 return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil 189 } 190 191 // MustParseNode parses a node URL. It panics if the URL is not valid. 192 func MustParseNode(rawurl string) *Node { 193 n, err := ParseNode(rawurl) 194 if err != nil { 195 log.Error("invalid node URL: " + err.Error()) 196 } 197 return n 198 } 199 200 // MarshalText implements encoding.TextMarshaler. 201 func (n *Node) MarshalText() ([]byte, error) { 202 return []byte(n.String()), nil 203 } 204 205 // UnmarshalText implements encoding.TextUnmarshaler. 206 func (n *Node) UnmarshalText(text []byte) error { 207 dec, err := ParseNode(string(text)) 208 if err == nil { 209 *n = *dec 210 } 211 return err 212 } 213 214 // type nodeQueue []*Node 215 // 216 // // pushNew adds n to the end if it is not present. 217 // func (nl *nodeList) appendNew(n *Node) { 218 // for _, entry := range n { 219 // if entry == n { 220 // return 221 // } 222 // } 223 // *nq = append(*nq, n) 224 // } 225 // 226 // // popRandom removes a random node. Nodes closer to 227 // // to the head of the beginning of the have a slightly higher probability. 228 // func (nl *nodeList) popRandom() *Node { 229 // ix := rand.Intn(len(*nq)) 230 // //TODO: probability as mentioned above. 231 // nl.removeIndex(ix) 232 // } 233 // 234 // func (nl *nodeList) removeIndex(i int) *Node { 235 // slice = *nl 236 // if len(*slice) <= i { 237 // return nil 238 // } 239 // *nl = append(slice[:i], slice[i+1:]...) 240 // } 241 242 const nodeIDBits = 456 243 244 // NodeID is a unique identifier for each node. 245 // The node identifier is a marshaled elliptic curve public key. 246 type NodeID [nodeIDBits / 8]byte 247 248 // NodeID prints as a long hexadecimal number. 249 func (n NodeID) String() string { 250 return fmt.Sprintf("%x", n[:]) 251 } 252 253 // The Go syntax representation of a NodeID is a call to HexID. 254 func (n NodeID) GoString() string { 255 return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) 256 } 257 258 // TerminalString returns a shortened hex string for terminal logging. 259 func (n NodeID) TerminalString() string { 260 return hex.EncodeToString(n[:8]) 261 } 262 263 // HexID converts a hex string to a NodeID. 264 // The string may be prefixed with 0x. 265 func HexID(in string) (NodeID, error) { 266 var id NodeID 267 b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) 268 if err != nil { 269 return id, err 270 } else if len(b) != len(id) { 271 return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) 272 } 273 copy(id[:], b) 274 return id, nil 275 } 276 277 // MustHexID converts a hex string to a NodeID. 278 // It panics if the string is not a valid NodeID. 279 func MustHexID(in string) NodeID { 280 id, err := HexID(in) 281 if err != nil { 282 panic(err) 283 } 284 return id 285 } 286 287 // PubkeyID returns a marshaled representation of the given public key. 288 func PubkeyID(pub *crypto.PublicKey) NodeID { 289 var id NodeID 290 if len(pub) != len(id) { 291 panic("id len != pub len") 292 } 293 copy(id[:], pub[:]) 294 return id 295 } 296 297 // Pubkey returns the public key represented by the node ID. 298 // It returns an error if the ID is not a point on the curve. 299 func (n NodeID) Pubkey() (*crypto.PublicKey, error) { 300 if bytes.Equal(n[:], (&crypto.PublicKey{})[:]) { 301 return nil, errors.New("invalid node id") 302 } 303 return crypto.UnmarshalPubKey(n[:]) 304 } 305 306 // recoverNodeID computes the public key used to sign the 307 // given hash from the signature. 308 func recoverNodeID(hash, sig []byte) (id NodeID, err error) { 309 pubkey, err := crypto.Ecrecover(hash, sig) 310 if err != nil { 311 return id, err 312 } 313 if len(pubkey) != len(id) { 314 return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id))*8) 315 } 316 for i := range id { 317 id[i] = pubkey[i] 318 } 319 return id, nil 320 } 321 322 // distcmp compares the distances a->target and b->target. 323 // Returns -1 if a is closer to target, 1 if b is closer to target 324 // and 0 if they are equal. 325 func distcmp(target, a, b common.Hash) int { 326 for i := range target { 327 da := a[i] ^ target[i] 328 db := b[i] ^ target[i] 329 if da > db { 330 return 1 331 } else if da < db { 332 return -1 333 } 334 } 335 return 0 336 } 337 338 // table of leading zero counts for bytes [0..255] 339 var lzcount = [256]int{ 340 8, 7, 6, 6, 5, 5, 5, 5, 341 4, 4, 4, 4, 4, 4, 4, 4, 342 3, 3, 3, 3, 3, 3, 3, 3, 343 3, 3, 3, 3, 3, 3, 3, 3, 344 2, 2, 2, 2, 2, 2, 2, 2, 345 2, 2, 2, 2, 2, 2, 2, 2, 346 2, 2, 2, 2, 2, 2, 2, 2, 347 2, 2, 2, 2, 2, 2, 2, 2, 348 1, 1, 1, 1, 1, 1, 1, 1, 349 1, 1, 1, 1, 1, 1, 1, 1, 350 1, 1, 1, 1, 1, 1, 1, 1, 351 1, 1, 1, 1, 1, 1, 1, 1, 352 1, 1, 1, 1, 1, 1, 1, 1, 353 1, 1, 1, 1, 1, 1, 1, 1, 354 1, 1, 1, 1, 1, 1, 1, 1, 355 1, 1, 1, 1, 1, 1, 1, 1, 356 0, 0, 0, 0, 0, 0, 0, 0, 357 0, 0, 0, 0, 0, 0, 0, 0, 358 0, 0, 0, 0, 0, 0, 0, 0, 359 0, 0, 0, 0, 0, 0, 0, 0, 360 0, 0, 0, 0, 0, 0, 0, 0, 361 0, 0, 0, 0, 0, 0, 0, 0, 362 0, 0, 0, 0, 0, 0, 0, 0, 363 0, 0, 0, 0, 0, 0, 0, 0, 364 0, 0, 0, 0, 0, 0, 0, 0, 365 0, 0, 0, 0, 0, 0, 0, 0, 366 0, 0, 0, 0, 0, 0, 0, 0, 367 0, 0, 0, 0, 0, 0, 0, 0, 368 0, 0, 0, 0, 0, 0, 0, 0, 369 0, 0, 0, 0, 0, 0, 0, 0, 370 0, 0, 0, 0, 0, 0, 0, 0, 371 0, 0, 0, 0, 0, 0, 0, 0, 372 } 373 374 // logdist returns the logarithmic distance between a and b, log2(a ^ b). 375 func logdist(a, b common.Hash) int { 376 lz := 0 377 for i := range a { 378 x := a[i] ^ b[i] 379 if x == 0 { 380 lz += 8 381 } else { 382 lz += lzcount[x] 383 break 384 } 385 } 386 return len(a)*8 - lz 387 } 388 389 // hashAtDistance returns a random hash such that logdist(a, b) == n 390 func hashAtDistance(a common.Hash, n int) (b common.Hash) { 391 if n == 0 { 392 return a 393 } 394 // flip bit at position n, fill the rest with random bits 395 b = a 396 pos := len(a) - n/8 - 1 397 bit := byte(0x01) << (byte(n%8) - 1) 398 if bit == 0 { 399 pos++ 400 bit = 0x80 401 } 402 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 403 for i := pos + 1; i < len(a); i++ { 404 b[i] = byte(rand.Intn(255)) 405 } 406 return b 407 }