github.com/pslzym/go-ethereum@v1.8.17-0.20180926104442-4b6824e07b1b/p2p/enode/node.go (about) 1 // Copyright 2018 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 enode 18 19 import ( 20 "crypto/ecdsa" 21 "encoding/hex" 22 "errors" 23 "fmt" 24 "math/bits" 25 "math/rand" 26 "net" 27 "strings" 28 29 "github.com/ethereum/go-ethereum/p2p/enr" 30 ) 31 32 // Node represents a host on the network. 33 type Node struct { 34 r enr.Record 35 id ID 36 } 37 38 // New wraps a node record. The record must be valid according to the given 39 // identity scheme. 40 func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) { 41 if err := r.VerifySignature(validSchemes); err != nil { 42 return nil, err 43 } 44 node := &Node{r: *r} 45 if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) { 46 return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{})) 47 } 48 return node, nil 49 } 50 51 // ID returns the node identifier. 52 func (n *Node) ID() ID { 53 return n.id 54 } 55 56 // Seq returns the sequence number of the underlying record. 57 func (n *Node) Seq() uint64 { 58 return n.r.Seq() 59 } 60 61 // Incomplete returns true for nodes with no IP address. 62 func (n *Node) Incomplete() bool { 63 return n.IP() == nil 64 } 65 66 // Load retrieves an entry from the underlying record. 67 func (n *Node) Load(k enr.Entry) error { 68 return n.r.Load(k) 69 } 70 71 // IP returns the IP address of the node. 72 func (n *Node) IP() net.IP { 73 var ip net.IP 74 n.Load((*enr.IP)(&ip)) 75 return ip 76 } 77 78 // UDP returns the UDP port of the node. 79 func (n *Node) UDP() int { 80 var port enr.UDP 81 n.Load(&port) 82 return int(port) 83 } 84 85 // UDP returns the TCP port of the node. 86 func (n *Node) TCP() int { 87 var port enr.TCP 88 n.Load(&port) 89 return int(port) 90 } 91 92 // Pubkey returns the secp256k1 public key of the node, if present. 93 func (n *Node) Pubkey() *ecdsa.PublicKey { 94 var key ecdsa.PublicKey 95 if n.Load((*Secp256k1)(&key)) != nil { 96 return nil 97 } 98 return &key 99 } 100 101 // checks whether n is a valid complete node. 102 func (n *Node) ValidateComplete() error { 103 if n.Incomplete() { 104 return errors.New("incomplete node") 105 } 106 if n.UDP() == 0 { 107 return errors.New("missing UDP port") 108 } 109 ip := n.IP() 110 if ip.IsMulticast() || ip.IsUnspecified() { 111 return errors.New("invalid IP (multicast/unspecified)") 112 } 113 // Validate the node key (on curve, etc.). 114 var key Secp256k1 115 return n.Load(&key) 116 } 117 118 // The string representation of a Node is a URL. 119 // Please see ParseNode for a description of the format. 120 func (n *Node) String() string { 121 return n.v4URL() 122 } 123 124 // MarshalText implements encoding.TextMarshaler. 125 func (n *Node) MarshalText() ([]byte, error) { 126 return []byte(n.v4URL()), nil 127 } 128 129 // UnmarshalText implements encoding.TextUnmarshaler. 130 func (n *Node) UnmarshalText(text []byte) error { 131 dec, err := ParseV4(string(text)) 132 if err == nil { 133 *n = *dec 134 } 135 return err 136 } 137 138 // ID is a unique identifier for each node. 139 type ID [32]byte 140 141 // Bytes returns a byte slice representation of the ID 142 func (n ID) Bytes() []byte { 143 return n[:] 144 } 145 146 // ID prints as a long hexadecimal number. 147 func (n ID) String() string { 148 return fmt.Sprintf("%x", n[:]) 149 } 150 151 // The Go syntax representation of a ID is a call to HexID. 152 func (n ID) GoString() string { 153 return fmt.Sprintf("enode.HexID(\"%x\")", n[:]) 154 } 155 156 // TerminalString returns a shortened hex string for terminal logging. 157 func (n ID) TerminalString() string { 158 return hex.EncodeToString(n[:8]) 159 } 160 161 // MarshalText implements the encoding.TextMarshaler interface. 162 func (n ID) MarshalText() ([]byte, error) { 163 return []byte(hex.EncodeToString(n[:])), nil 164 } 165 166 // UnmarshalText implements the encoding.TextUnmarshaler interface. 167 func (n *ID) UnmarshalText(text []byte) error { 168 id, err := parseID(string(text)) 169 if err != nil { 170 return err 171 } 172 *n = id 173 return nil 174 } 175 176 // HexID converts a hex string to an ID. 177 // The string may be prefixed with 0x. 178 // It panics if the string is not a valid ID. 179 func HexID(in string) ID { 180 id, err := parseID(in) 181 if err != nil { 182 panic(err) 183 } 184 return id 185 } 186 187 func parseID(in string) (ID, error) { 188 var id ID 189 b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) 190 if err != nil { 191 return id, err 192 } else if len(b) != len(id) { 193 return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) 194 } 195 copy(id[:], b) 196 return id, nil 197 } 198 199 // DistCmp compares the distances a->target and b->target. 200 // Returns -1 if a is closer to target, 1 if b is closer to target 201 // and 0 if they are equal. 202 func DistCmp(target, a, b ID) int { 203 for i := range target { 204 da := a[i] ^ target[i] 205 db := b[i] ^ target[i] 206 if da > db { 207 return 1 208 } else if da < db { 209 return -1 210 } 211 } 212 return 0 213 } 214 215 // LogDist returns the logarithmic distance between a and b, log2(a ^ b). 216 func LogDist(a, b ID) int { 217 lz := 0 218 for i := range a { 219 x := a[i] ^ b[i] 220 if x == 0 { 221 lz += 8 222 } else { 223 lz += bits.LeadingZeros8(x) 224 break 225 } 226 } 227 return len(a)*8 - lz 228 } 229 230 // RandomID returns a random ID b such that logdist(a, b) == n. 231 func RandomID(a ID, n int) (b ID) { 232 if n == 0 { 233 return a 234 } 235 // flip bit at position n, fill the rest with random bits 236 b = a 237 pos := len(a) - n/8 - 1 238 bit := byte(0x01) << (byte(n%8) - 1) 239 if bit == 0 { 240 pos++ 241 bit = 0x80 242 } 243 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 244 for i := pos + 1; i < len(a); i++ { 245 b[i] = byte(rand.Intn(255)) 246 } 247 return b 248 }