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