github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/p2p/discover/node.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discover 18 19 import ( 20 "crypto/ecdsa" 21 "crypto/elliptic" 22 "encoding/hex" 23 "errors" 24 "fmt" 25 "math/big" 26 "math/rand" 27 "net" 28 "net/url" 29 "strconv" 30 "strings" 31 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/crypto" 34 "github.com/ethereum/go-ethereum/crypto/secp256k1" 35 ) 36 37 const nodeIDBits = 512 38 39 // Node represents a host on the network. 40 type Node struct { 41 IP net.IP // len 4 for IPv4 or 16 for IPv6 42 UDP, TCP uint16 // port numbers 43 ID NodeID // the node's public key 44 45 // This is a cached copy of sha3(ID) which is used for node 46 // distance calculations. This is part of Node in order to make it 47 // possible to write tests that need a node at a certain distance. 48 // In those tests, the content of sha will not actually correspond 49 // with ID. 50 sha common.Hash 51 52 // whether this node is currently being pinged in order to replace 53 // it in a bucket 54 contested bool 55 } 56 57 func newNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { 58 if ipv4 := ip.To4(); ipv4 != nil { 59 ip = ipv4 60 } 61 return &Node{ 62 IP: ip, 63 UDP: udpPort, 64 TCP: tcpPort, 65 ID: id, 66 sha: crypto.Sha3Hash(id[:]), 67 } 68 } 69 70 func (n *Node) addr() *net.UDPAddr { 71 return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} 72 } 73 74 // The string representation of a Node is a URL. 75 // Please see ParseNode for a description of the format. 76 func (n *Node) String() string { 77 addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} 78 u := url.URL{ 79 Scheme: "enode", 80 User: url.User(fmt.Sprintf("%x", n.ID[:])), 81 Host: addr.String(), 82 } 83 if n.UDP != n.TCP { 84 u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) 85 } 86 return u.String() 87 } 88 89 // ParseNode parses a node URL. 90 // 91 // A node URL has scheme "enode". 92 // 93 // The hexadecimal node ID is encoded in the username portion of the 94 // URL, separated from the host by an @ sign. The hostname can only be 95 // given as an IP address, DNS domain names are not allowed. The port 96 // in the host name section is the TCP listening port. If the TCP and 97 // UDP (discovery) ports differ, the UDP port is specified as query 98 // parameter "discport". 99 // 100 // In the following example, the node URL describes 101 // a node with IP address 10.3.58.6, TCP listening port 30303 102 // and UDP discovery port 30301. 103 // 104 // enode://<hex node id>@10.3.58.6:30303?discport=30301 105 func ParseNode(rawurl string) (*Node, error) { 106 var ( 107 id NodeID 108 ip net.IP 109 tcpPort, udpPort uint64 110 ) 111 u, err := url.Parse(rawurl) 112 if u.Scheme != "enode" { 113 return nil, errors.New("invalid URL scheme, want \"enode\"") 114 } 115 // Parse the Node ID from the user portion. 116 if u.User == nil { 117 return nil, errors.New("does not contain node ID") 118 } 119 if id, err = HexID(u.User.String()); err != nil { 120 return nil, fmt.Errorf("invalid node ID (%v)", err) 121 } 122 // Parse the IP address. 123 host, port, err := net.SplitHostPort(u.Host) 124 if err != nil { 125 return nil, fmt.Errorf("invalid host: %v", err) 126 } 127 if ip = net.ParseIP(host); ip == nil { 128 return nil, errors.New("invalid IP address") 129 } 130 // Ensure the IP is 4 bytes long for IPv4 addresses. 131 if ipv4 := ip.To4(); ipv4 != nil { 132 ip = ipv4 133 } 134 // Parse the port numbers. 135 if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { 136 return nil, errors.New("invalid port") 137 } 138 udpPort = tcpPort 139 qv := u.Query() 140 if qv.Get("discport") != "" { 141 udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) 142 if err != nil { 143 return nil, errors.New("invalid discport in query") 144 } 145 } 146 return newNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil 147 } 148 149 // MustParseNode parses a node URL. It panics if the URL is not valid. 150 func MustParseNode(rawurl string) *Node { 151 n, err := ParseNode(rawurl) 152 if err != nil { 153 panic("invalid node URL: " + err.Error()) 154 } 155 return n 156 } 157 158 // NodeID is a unique identifier for each node. 159 // The node identifier is a marshaled elliptic curve public key. 160 type NodeID [nodeIDBits / 8]byte 161 162 // NodeID prints as a long hexadecimal number. 163 func (n NodeID) String() string { 164 return fmt.Sprintf("%x", n[:]) 165 } 166 167 // The Go syntax representation of a NodeID is a call to HexID. 168 func (n NodeID) GoString() string { 169 return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) 170 } 171 172 // HexID converts a hex string to a NodeID. 173 // The string may be prefixed with 0x. 174 func HexID(in string) (NodeID, error) { 175 if strings.HasPrefix(in, "0x") { 176 in = in[2:] 177 } 178 var id NodeID 179 b, err := hex.DecodeString(in) 180 if err != nil { 181 return id, err 182 } else if len(b) != len(id) { 183 return id, fmt.Errorf("wrong length, need %d hex bytes", len(id)) 184 } 185 copy(id[:], b) 186 return id, nil 187 } 188 189 // MustHexID converts a hex string to a NodeID. 190 // It panics if the string is not a valid NodeID. 191 func MustHexID(in string) NodeID { 192 id, err := HexID(in) 193 if err != nil { 194 panic(err) 195 } 196 return id 197 } 198 199 // PubkeyID returns a marshaled representation of the given public key. 200 func PubkeyID(pub *ecdsa.PublicKey) NodeID { 201 var id NodeID 202 pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) 203 if len(pbytes)-1 != len(id) { 204 panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) 205 } 206 copy(id[:], pbytes[1:]) 207 return id 208 } 209 210 // Pubkey returns the public key represented by the node ID. 211 // It returns an error if the ID is not a point on the curve. 212 func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { 213 p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} 214 half := len(id) / 2 215 p.X.SetBytes(id[:half]) 216 p.Y.SetBytes(id[half:]) 217 if !p.Curve.IsOnCurve(p.X, p.Y) { 218 return nil, errors.New("not a point on the S256 curve") 219 } 220 return p, nil 221 } 222 223 // recoverNodeID computes the public key used to sign the 224 // given hash from the signature. 225 func recoverNodeID(hash, sig []byte) (id NodeID, err error) { 226 pubkey, err := secp256k1.RecoverPubkey(hash, sig) 227 if err != nil { 228 return id, err 229 } 230 if len(pubkey)-1 != len(id) { 231 return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) 232 } 233 for i := range id { 234 id[i] = pubkey[i+1] 235 } 236 return id, nil 237 } 238 239 // distcmp compares the distances a->target and b->target. 240 // Returns -1 if a is closer to target, 1 if b is closer to target 241 // and 0 if they are equal. 242 func distcmp(target, a, b common.Hash) int { 243 for i := range target { 244 da := a[i] ^ target[i] 245 db := b[i] ^ target[i] 246 if da > db { 247 return 1 248 } else if da < db { 249 return -1 250 } 251 } 252 return 0 253 } 254 255 // table of leading zero counts for bytes [0..255] 256 var lzcount = [256]int{ 257 8, 7, 6, 6, 5, 5, 5, 5, 258 4, 4, 4, 4, 4, 4, 4, 4, 259 3, 3, 3, 3, 3, 3, 3, 3, 260 3, 3, 3, 3, 3, 3, 3, 3, 261 2, 2, 2, 2, 2, 2, 2, 2, 262 2, 2, 2, 2, 2, 2, 2, 2, 263 2, 2, 2, 2, 2, 2, 2, 2, 264 2, 2, 2, 2, 2, 2, 2, 2, 265 1, 1, 1, 1, 1, 1, 1, 1, 266 1, 1, 1, 1, 1, 1, 1, 1, 267 1, 1, 1, 1, 1, 1, 1, 1, 268 1, 1, 1, 1, 1, 1, 1, 1, 269 1, 1, 1, 1, 1, 1, 1, 1, 270 1, 1, 1, 1, 1, 1, 1, 1, 271 1, 1, 1, 1, 1, 1, 1, 1, 272 1, 1, 1, 1, 1, 1, 1, 1, 273 0, 0, 0, 0, 0, 0, 0, 0, 274 0, 0, 0, 0, 0, 0, 0, 0, 275 0, 0, 0, 0, 0, 0, 0, 0, 276 0, 0, 0, 0, 0, 0, 0, 0, 277 0, 0, 0, 0, 0, 0, 0, 0, 278 0, 0, 0, 0, 0, 0, 0, 0, 279 0, 0, 0, 0, 0, 0, 0, 0, 280 0, 0, 0, 0, 0, 0, 0, 0, 281 0, 0, 0, 0, 0, 0, 0, 0, 282 0, 0, 0, 0, 0, 0, 0, 0, 283 0, 0, 0, 0, 0, 0, 0, 0, 284 0, 0, 0, 0, 0, 0, 0, 0, 285 0, 0, 0, 0, 0, 0, 0, 0, 286 0, 0, 0, 0, 0, 0, 0, 0, 287 0, 0, 0, 0, 0, 0, 0, 0, 288 0, 0, 0, 0, 0, 0, 0, 0, 289 } 290 291 // logdist returns the logarithmic distance between a and b, log2(a ^ b). 292 func logdist(a, b common.Hash) int { 293 lz := 0 294 for i := range a { 295 x := a[i] ^ b[i] 296 if x == 0 { 297 lz += 8 298 } else { 299 lz += lzcount[x] 300 break 301 } 302 } 303 return len(a)*8 - lz 304 } 305 306 // hashAtDistance returns a random hash such that logdist(a, b) == n 307 func hashAtDistance(a common.Hash, n int) (b common.Hash) { 308 if n == 0 { 309 return a 310 } 311 // flip bit at position n, fill the rest with random bits 312 b = a 313 pos := len(a) - n/8 - 1 314 bit := byte(0x01) << (byte(n%8) - 1) 315 if bit == 0 { 316 pos++ 317 bit = 0x80 318 } 319 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 320 for i := pos + 1; i < len(a); i++ { 321 b[i] = byte(rand.Intn(255)) 322 } 323 return b 324 }