github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/discover/node.go (about) 1 package discover 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "encoding/hex" 7 "errors" 8 "fmt" 9 "io" 10 "math/rand" 11 "net" 12 "net/url" 13 "strconv" 14 "strings" 15 "time" 16 17 "github.com/jonasnick/go-ethereum/crypto/secp256k1" 18 "github.com/jonasnick/go-ethereum/rlp" 19 ) 20 21 const nodeIDBits = 512 22 23 // Node represents a host on the network. 24 type Node struct { 25 ID NodeID 26 IP net.IP 27 28 DiscPort int // UDP listening port for discovery protocol 29 TCPPort int // TCP listening port for RLPx 30 31 active time.Time 32 } 33 34 func newNode(id NodeID, addr *net.UDPAddr) *Node { 35 return &Node{ 36 ID: id, 37 IP: addr.IP, 38 DiscPort: addr.Port, 39 TCPPort: addr.Port, 40 active: time.Now(), 41 } 42 } 43 44 func (n *Node) isValid() bool { 45 // TODO: don't accept localhost, LAN addresses from internet hosts 46 return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0 47 } 48 49 // The string representation of a Node is a URL. 50 // Please see ParseNode for a description of the format. 51 func (n *Node) String() string { 52 addr := net.TCPAddr{IP: n.IP, Port: n.TCPPort} 53 u := url.URL{ 54 Scheme: "enode", 55 User: url.User(fmt.Sprintf("%x", n.ID[:])), 56 Host: addr.String(), 57 } 58 if n.DiscPort != n.TCPPort { 59 u.RawQuery = "discport=" + strconv.Itoa(n.DiscPort) 60 } 61 return u.String() 62 } 63 64 // ParseNode parses a node URL. 65 // 66 // A node URL has scheme "enode". 67 // 68 // The hexadecimal node ID is encoded in the username portion of the 69 // URL, separated from the host by an @ sign. The hostname can only be 70 // given as an IP address, DNS domain names are not allowed. The port 71 // in the host name section is the TCP listening port. If the TCP and 72 // UDP (discovery) ports differ, the UDP port is specified as query 73 // parameter "discport". 74 // 75 // In the following example, the node URL describes 76 // a node with IP address 10.3.58.6, TCP listening port 30303 77 // and UDP discovery port 30301. 78 // 79 // enode://<hex node id>@10.3.58.6:30303?discport=30301 80 func ParseNode(rawurl string) (*Node, error) { 81 var n Node 82 u, err := url.Parse(rawurl) 83 if u.Scheme != "enode" { 84 return nil, errors.New("invalid URL scheme, want \"enode\"") 85 } 86 if u.User == nil { 87 return nil, errors.New("does not contain node ID") 88 } 89 if n.ID, err = HexID(u.User.String()); err != nil { 90 return nil, fmt.Errorf("invalid node ID (%v)", err) 91 } 92 ip, port, err := net.SplitHostPort(u.Host) 93 if err != nil { 94 return nil, fmt.Errorf("invalid host: %v", err) 95 } 96 if n.IP = net.ParseIP(ip); n.IP == nil { 97 return nil, errors.New("invalid IP address") 98 } 99 if n.TCPPort, err = strconv.Atoi(port); err != nil { 100 return nil, errors.New("invalid port") 101 } 102 qv := u.Query() 103 if qv.Get("discport") == "" { 104 n.DiscPort = n.TCPPort 105 } else { 106 if n.DiscPort, err = strconv.Atoi(qv.Get("discport")); err != nil { 107 return nil, errors.New("invalid discport in query") 108 } 109 } 110 return &n, nil 111 } 112 113 // MustParseNode parses a node URL. It panics if the URL is not valid. 114 func MustParseNode(rawurl string) *Node { 115 n, err := ParseNode(rawurl) 116 if err != nil { 117 panic("invalid node URL: " + err.Error()) 118 } 119 return n 120 } 121 122 func (n Node) EncodeRLP(w io.Writer) error { 123 return rlp.Encode(w, rpcNode{IP: n.IP.String(), Port: uint16(n.TCPPort), ID: n.ID}) 124 } 125 func (n *Node) DecodeRLP(s *rlp.Stream) (err error) { 126 var ext rpcNode 127 if err = s.Decode(&ext); err == nil { 128 n.TCPPort = int(ext.Port) 129 n.DiscPort = int(ext.Port) 130 n.ID = ext.ID 131 if n.IP = net.ParseIP(ext.IP); n.IP == nil { 132 return errors.New("invalid IP string") 133 } 134 } 135 return err 136 } 137 138 // NodeID is a unique identifier for each node. 139 // The node identifier is a marshaled elliptic curve public key. 140 type NodeID [nodeIDBits / 8]byte 141 142 // NodeID prints as a long hexadecimal number. 143 func (n NodeID) String() string { 144 return fmt.Sprintf("%#x", n[:]) 145 } 146 147 // The Go syntax representation of a NodeID is a call to HexID. 148 func (n NodeID) GoString() string { 149 return fmt.Sprintf("discover.HexID(\"%#x\")", n[:]) 150 } 151 152 // HexID converts a hex string to a NodeID. 153 // The string may be prefixed with 0x. 154 func HexID(in string) (NodeID, error) { 155 if strings.HasPrefix(in, "0x") { 156 in = in[2:] 157 } 158 var id NodeID 159 b, err := hex.DecodeString(in) 160 if err != nil { 161 return id, err 162 } else if len(b) != len(id) { 163 return id, fmt.Errorf("wrong length, need %d hex bytes", len(id)) 164 } 165 copy(id[:], b) 166 return id, nil 167 } 168 169 // MustHexID converts a hex string to a NodeID. 170 // It panics if the string is not a valid NodeID. 171 func MustHexID(in string) NodeID { 172 id, err := HexID(in) 173 if err != nil { 174 panic(err) 175 } 176 return id 177 } 178 179 // PubkeyID returns a marshaled representation of the given public key. 180 func PubkeyID(pub *ecdsa.PublicKey) NodeID { 181 var id NodeID 182 pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) 183 if len(pbytes)-1 != len(id) { 184 panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) 185 } 186 copy(id[:], pbytes[1:]) 187 return id 188 } 189 190 // recoverNodeID computes the public key used to sign the 191 // given hash from the signature. 192 func recoverNodeID(hash, sig []byte) (id NodeID, err error) { 193 pubkey, err := secp256k1.RecoverPubkey(hash, sig) 194 if err != nil { 195 return id, err 196 } 197 if len(pubkey)-1 != len(id) { 198 return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) 199 } 200 for i := range id { 201 id[i] = pubkey[i+1] 202 } 203 return id, nil 204 } 205 206 // distcmp compares the distances a->target and b->target. 207 // Returns -1 if a is closer to target, 1 if b is closer to target 208 // and 0 if they are equal. 209 func distcmp(target, a, b NodeID) int { 210 for i := range target { 211 da := a[i] ^ target[i] 212 db := b[i] ^ target[i] 213 if da > db { 214 return 1 215 } else if da < db { 216 return -1 217 } 218 } 219 return 0 220 } 221 222 // table of leading zero counts for bytes [0..255] 223 var lzcount = [256]int{ 224 8, 7, 6, 6, 5, 5, 5, 5, 225 4, 4, 4, 4, 4, 4, 4, 4, 226 3, 3, 3, 3, 3, 3, 3, 3, 227 3, 3, 3, 3, 3, 3, 3, 3, 228 2, 2, 2, 2, 2, 2, 2, 2, 229 2, 2, 2, 2, 2, 2, 2, 2, 230 2, 2, 2, 2, 2, 2, 2, 2, 231 2, 2, 2, 2, 2, 2, 2, 2, 232 1, 1, 1, 1, 1, 1, 1, 1, 233 1, 1, 1, 1, 1, 1, 1, 1, 234 1, 1, 1, 1, 1, 1, 1, 1, 235 1, 1, 1, 1, 1, 1, 1, 1, 236 1, 1, 1, 1, 1, 1, 1, 1, 237 1, 1, 1, 1, 1, 1, 1, 1, 238 1, 1, 1, 1, 1, 1, 1, 1, 239 1, 1, 1, 1, 1, 1, 1, 1, 240 0, 0, 0, 0, 0, 0, 0, 0, 241 0, 0, 0, 0, 0, 0, 0, 0, 242 0, 0, 0, 0, 0, 0, 0, 0, 243 0, 0, 0, 0, 0, 0, 0, 0, 244 0, 0, 0, 0, 0, 0, 0, 0, 245 0, 0, 0, 0, 0, 0, 0, 0, 246 0, 0, 0, 0, 0, 0, 0, 0, 247 0, 0, 0, 0, 0, 0, 0, 0, 248 0, 0, 0, 0, 0, 0, 0, 0, 249 0, 0, 0, 0, 0, 0, 0, 0, 250 0, 0, 0, 0, 0, 0, 0, 0, 251 0, 0, 0, 0, 0, 0, 0, 0, 252 0, 0, 0, 0, 0, 0, 0, 0, 253 0, 0, 0, 0, 0, 0, 0, 0, 254 0, 0, 0, 0, 0, 0, 0, 0, 255 0, 0, 0, 0, 0, 0, 0, 0, 256 } 257 258 // logdist returns the logarithmic distance between a and b, log2(a ^ b). 259 func logdist(a, b NodeID) int { 260 lz := 0 261 for i := range a { 262 x := a[i] ^ b[i] 263 if x == 0 { 264 lz += 8 265 } else { 266 lz += lzcount[x] 267 break 268 } 269 } 270 return len(a)*8 - lz 271 } 272 273 // randomID returns a random NodeID such that logdist(a, b) == n 274 func randomID(a NodeID, n int) (b NodeID) { 275 if n == 0 { 276 return a 277 } 278 // flip bit at position n, fill the rest with random bits 279 b = a 280 pos := len(a) - n/8 - 1 281 bit := byte(0x01) << (byte(n%8) - 1) 282 if bit == 0 { 283 pos++ 284 bit = 0x80 285 } 286 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 287 for i := pos + 1; i < len(a); i++ { 288 b[i] = byte(rand.Intn(255)) 289 } 290 return b 291 }