github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/p2pserver/discover/node.go (about) 1 package discover 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "encoding/hex" 7 "errors" 8 "fmt" 9 "math/big" 10 "math/rand" 11 "net" 12 "net/url" 13 "regexp" 14 "strconv" 15 "strings" 16 17 "github.com/ethereum/go-ethereum/common" 18 "github.com/ethereum/go-ethereum/crypto" 19 "github.com/ethereum/go-ethereum/crypto/secp256k1" 20 ) 21 22 const NodeIDBits = 512 23 24 // Node represents a host on the network. 25 // The fields of Node may not be modified. 26 type Node struct { 27 IP net.IP // len 4 for IPv4 or 16 for IPv6 28 UDP, TCP uint16 // port numbers 29 ID NodeID // the node's public key 30 31 // This is a cached copy of sha3(ID) which is used for node 32 // distance calculations. This is part of Node in order to make it 33 // possible to write tests that need a node at a certain distance. 34 // In those tests, the content of sha will not actually correspond 35 // with ID. 36 sha common.Hash 37 38 // whether this node is currently being pinged in order to replace 39 // it in a bucket 40 contested bool 41 } 42 43 // NewNode creates a new node. It is mostly meant to be used for 44 // testing purposes. 45 func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { 46 if ipv4 := ip.To4(); ipv4 != nil { 47 ip = ipv4 48 } 49 tem := &Node{ 50 IP: ip, 51 UDP: udpPort, 52 TCP: tcpPort, 53 ID: id, 54 sha: crypto.Keccak256Hash(id[:]), 55 } 56 // fmt.Println(" **** NewNode tem:",tem) 57 // fmt.Println(" **** NewNode tem.IP:",tem.IP) 58 // fmt.Println(" **** NewNode tem.UDP:",tem.UDP) 59 // fmt.Println(" **** NewNode tem.TCP:",tem.TCP) 60 // fmt.Println(" **** NewNode tem.ID:",tem.ID) 61 // fmt.Println(" **** NewNode tem.sha:",tem.sha) 62 // fmt.Println(" **** NewNode tem.contested:",tem.contested) 63 64 return tem 65 } 66 67 func (n *Node) PrintNode() { 68 fmt.Println(" **** NewNode n:",n) 69 fmt.Println(" **** NewNode n.IP:",n.IP) 70 fmt.Println(" **** NewNode n.UDP:",n.UDP) 71 fmt.Println(" **** NewNode n.TCP:",n.TCP) 72 fmt.Println(" **** NewNode n.ID:",n.ID) 73 fmt.Println(" **** NewNode n.sha:",n.sha) 74 fmt.Println(" **** NewNode n.contested:",n.contested) 75 } 76 77 func (n *Node) addr() *net.UDPAddr { 78 return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} 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 // NodeID is a unique identifier for each node. 228 // The node identifier is a marshaled elliptic curve public key. 229 type NodeID [NodeIDBits / 8]byte 230 231 // Bytes returns a byte slice representation of the NodeID 232 func (n NodeID) Bytes() []byte { 233 return n[:] 234 } 235 236 // NodeID prints as a long hexadecimal number. 237 func (n NodeID) String() string { 238 return fmt.Sprintf("%x", n[:]) 239 } 240 241 // The Go syntax representation of a NodeID is a call to HexID. 242 func (n NodeID) GoString() string { 243 return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) 244 } 245 246 // TerminalString returns a shortened hex string for terminal logging. 247 func (n NodeID) TerminalString() string { 248 return hex.EncodeToString(n[:8]) 249 } 250 251 // MarshalText implements the encoding.TextMarshaler interface. 252 func (n NodeID) MarshalText() ([]byte, error) { 253 return []byte(hex.EncodeToString(n[:])), nil 254 } 255 256 // UnmarshalText implements the encoding.TextUnmarshaler interface. 257 func (n *NodeID) UnmarshalText(text []byte) error { 258 id, err := HexID(string(text)) 259 if err != nil { 260 return err 261 } 262 *n = id 263 return nil 264 } 265 266 // BytesID converts a byte slice to a NodeID 267 func BytesID(b []byte) (NodeID, error) { 268 var id NodeID 269 if len(b) != len(id) { 270 return id, fmt.Errorf("wrong length, want %d bytes", len(id)) 271 } 272 copy(id[:], b) 273 return id, nil 274 } 275 276 // MustBytesID converts a byte slice to a NodeID. 277 // It panics if the byte slice is not a valid NodeID. 278 func MustBytesID(b []byte) NodeID { 279 id, err := BytesID(b) 280 if err != nil { 281 panic(err) 282 } 283 return id 284 } 285 286 // HexID converts a hex string to a NodeID. 287 // The string may be prefixed with 0x. 288 func HexID(in string) (NodeID, error) { 289 var id NodeID 290 b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) 291 if err != nil { 292 return id, err 293 } else if len(b) != len(id) { 294 return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) 295 } 296 copy(id[:], b) 297 return id, nil 298 } 299 300 // MustHexID converts a hex string to a NodeID. 301 // It panics if the string is not a valid NodeID. 302 func MustHexID(in string) NodeID { 303 id, err := HexID(in) 304 if err != nil { 305 panic(err) 306 } 307 return id 308 } 309 310 // PubkeyID returns a marshaled representation of the given public key. 311 func PubkeyID(pub *ecdsa.PublicKey) NodeID { 312 var id NodeID 313 pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) 314 if len(pbytes)-1 != len(id) { 315 panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) 316 } 317 copy(id[:], pbytes[1:]) 318 return id 319 } 320 321 // Pubkey returns the public key represented by the node ID. 322 // It returns an error if the ID is not a point on the curve. 323 func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { 324 p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} 325 half := len(id) / 2 326 p.X.SetBytes(id[:half]) 327 p.Y.SetBytes(id[half:]) 328 if !p.Curve.IsOnCurve(p.X, p.Y) { 329 return nil, errors.New("id is invalid secp256k1 curve point") 330 } 331 return p, nil 332 } 333 334 // recoverNodeID computes the public key used to sign the 335 // given hash from the signature. 336 func recoverNodeID(hash, sig []byte) (id NodeID, err error) { 337 pubkey, err := secp256k1.RecoverPubkey(hash, sig) 338 if err != nil { 339 return id, err 340 } 341 if len(pubkey)-1 != len(id) { 342 return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) 343 } 344 for i := range id { 345 id[i] = pubkey[i+1] 346 } 347 return id, nil 348 } 349 350 // distcmp compares the distances a->target and b->target. 351 // Returns -1 if a is closer to target, 1 if b is closer to target 352 // and 0 if they are equal. 353 func distcmp(target, a, b common.Hash) int { 354 for i := range target { 355 da := a[i] ^ target[i] 356 db := b[i] ^ target[i] 357 if da > db { 358 return 1 359 } else if da < db { 360 return -1 361 } 362 } 363 return 0 364 } 365 366 // table of leading zero counts for bytes [0..255] 367 var lzcount = [256]int{ 368 8, 7, 6, 6, 5, 5, 5, 5, 369 4, 4, 4, 4, 4, 4, 4, 4, 370 3, 3, 3, 3, 3, 3, 3, 3, 371 3, 3, 3, 3, 3, 3, 3, 3, 372 2, 2, 2, 2, 2, 2, 2, 2, 373 2, 2, 2, 2, 2, 2, 2, 2, 374 2, 2, 2, 2, 2, 2, 2, 2, 375 2, 2, 2, 2, 2, 2, 2, 2, 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 1, 1, 1, 1, 1, 1, 1, 1, 383 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 399 0, 0, 0, 0, 0, 0, 0, 0, 400 } 401 402 // logdist returns the logarithmic distance between a and b, log2(a ^ b). 403 func logdist(a, b common.Hash) int { 404 lz := 0 405 for i := range a { 406 x := a[i] ^ b[i] 407 if x == 0 { 408 lz += 8 409 } else { 410 lz += lzcount[x] 411 break 412 } 413 } 414 return len(a)*8 - lz 415 } 416 417 // hashAtDistance returns a random hash such that logdist(a, b) == n 418 func hashAtDistance(a common.Hash, n int) (b common.Hash) { 419 if n == 0 { 420 return a 421 } 422 // flip bit at position n, fill the rest with random bits 423 b = a 424 pos := len(a) - n/8 - 1 425 bit := byte(0x01) << (byte(n%8) - 1) 426 if bit == 0 { 427 pos++ 428 bit = 0x80 429 } 430 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 431 for i := pos + 1; i < len(a); i++ { 432 b[i] = byte(rand.Intn(255)) 433 } 434 return b 435 }