github.com/core-coin/go-core/v2@v2.1.9/p2p/enode/node.go (about) 1 // Copyright 2018 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package enode 18 19 import ( 20 "encoding/base64" 21 "encoding/hex" 22 "errors" 23 "fmt" 24 "math/bits" 25 "net" 26 "strings" 27 28 "github.com/core-coin/go-core/v2/crypto" 29 "github.com/core-coin/go-core/v2/p2p/enr" 30 "github.com/core-coin/go-core/v2/rlp" 31 ) 32 33 var errMissingPrefix = errors.New("missing 'enr:' prefix for base64-encoded record") 34 35 // Node represents a host on the network. 36 type Node struct { 37 r enr.Record 38 id ID 39 } 40 41 // New wraps a node record. The record must be valid according to the given 42 // identity scheme. 43 func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) { 44 if err := r.VerifySignature(validSchemes); err != nil { 45 return nil, err 46 } 47 node := &Node{r: *r} 48 if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) { 49 return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{})) 50 } 51 return node, nil 52 } 53 54 // MustParse parses a node record or enode:// URL. It panics if the input is invalid. 55 func MustParse(rawurl string) *Node { 56 n, err := Parse(ValidSchemes, rawurl) 57 if err != nil { 58 panic("invalid node: " + err.Error()) 59 } 60 return n 61 } 62 63 // Parse decodes and verifies a base64-encoded node record. 64 func Parse(validSchemes enr.IdentityScheme, input string) (*Node, error) { 65 if strings.HasPrefix(input, "enode://") { 66 return ParseV4(input) 67 } 68 if !strings.HasPrefix(input, "enr:") { 69 return nil, errMissingPrefix 70 } 71 bin, err := base64.RawURLEncoding.DecodeString(input[4:]) 72 if err != nil { 73 return nil, err 74 } 75 var r enr.Record 76 if err := rlp.DecodeBytes(bin, &r); err != nil { 77 return nil, err 78 } 79 return New(validSchemes, &r) 80 } 81 82 // ID returns the node identifier. 83 func (n *Node) ID() ID { 84 return n.id 85 } 86 87 // Seq returns the sequence number of the underlying record. 88 func (n *Node) Seq() uint64 { 89 return n.r.Seq() 90 } 91 92 // Incomplete returns true for nodes with no IP address. 93 func (n *Node) Incomplete() bool { 94 return n.IP() == nil 95 } 96 97 // Load retrieves an entry from the underlying record. 98 func (n *Node) Load(k enr.Entry) error { 99 return n.r.Load(k) 100 } 101 102 // IP returns the IP address of the node. This prefers IPv4 addresses. 103 func (n *Node) IP() net.IP { 104 var ( 105 ip4 enr.IPv4 106 ip6 enr.IPv6 107 ) 108 if n.Load(&ip4) == nil { 109 return net.IP(ip4) 110 } 111 if n.Load(&ip6) == nil { 112 return net.IP(ip6) 113 } 114 return nil 115 } 116 117 // UDP returns the UDP port of the node. 118 func (n *Node) UDP() int { 119 var port enr.UDP 120 n.Load(&port) 121 return int(port) 122 } 123 124 // UDP returns the TCP port of the node. 125 func (n *Node) TCP() int { 126 var port enr.TCP 127 n.Load(&port) 128 return int(port) 129 } 130 131 // Pubkey returns the ed448 public key of the node, if present. 132 func (n *Node) Pubkey() *crypto.PublicKey { 133 var key crypto.PublicKey 134 if n.Load((*Ed448)(&key)) != nil { 135 return nil 136 } 137 return &key 138 } 139 140 // Record returns the node's record. The return value is a copy and may 141 // be modified by the caller. 142 func (n *Node) Record() *enr.Record { 143 cpy := n.r 144 return &cpy 145 } 146 147 // ValidateComplete checks whether n has a valid IP and UDP port. 148 // Deprecated: don't use this method. 149 func (n *Node) ValidateComplete() error { 150 if n.Incomplete() { 151 return errors.New("missing IP address") 152 } 153 if n.UDP() == 0 { 154 return errors.New("missing UDP port") 155 } 156 ip := n.IP() 157 if ip.IsMulticast() || ip.IsUnspecified() { 158 return errors.New("invalid IP (multicast/unspecified)") 159 } 160 // Validate the node key (on curve, etc.). 161 var key Ed448 162 return n.Load(&key) 163 } 164 165 // String returns the text representation of the record. 166 func (n *Node) String() string { 167 if isNewV4(n) { 168 return n.URLv4() // backwards-compatibility glue for NewV4 nodes 169 } 170 enc, _ := rlp.EncodeToBytes(&n.r) // always succeeds because record is valid 171 b64 := base64.RawURLEncoding.EncodeToString(enc) 172 return "enr:" + b64 173 } 174 175 // MarshalText implements encoding.TextMarshaler. 176 func (n *Node) MarshalText() ([]byte, error) { 177 return []byte(n.String()), nil 178 } 179 180 // UnmarshalText implements encoding.TextUnmarshaler. 181 func (n *Node) UnmarshalText(text []byte) error { 182 dec, err := Parse(ValidSchemes, string(text)) 183 if err == nil { 184 *n = *dec 185 } 186 return err 187 } 188 189 // ID is a unique identifier for each node. 190 type ID [32]byte 191 192 // Bytes returns a byte slice representation of the ID 193 func (n ID) Bytes() []byte { 194 return n[:] 195 } 196 197 // ID prints as a long hexadecimal number. 198 func (n ID) String() string { 199 return fmt.Sprintf("%x", n[:]) 200 } 201 202 // The Go syntax representation of a ID is a call to HexID. 203 func (n ID) GoString() string { 204 return fmt.Sprintf("enode.HexID(\"%x\")", n[:]) 205 } 206 207 // TerminalString returns a shortened hex string for terminal logging. 208 func (n ID) TerminalString() string { 209 return hex.EncodeToString(n[:8]) 210 } 211 212 // MarshalText implements the encoding.TextMarshaler interface. 213 func (n ID) MarshalText() ([]byte, error) { 214 return []byte(hex.EncodeToString(n[:])), nil 215 } 216 217 // UnmarshalText implements the encoding.TextUnmarshaler interface. 218 func (n *ID) UnmarshalText(text []byte) error { 219 id, err := ParseID(string(text)) 220 if err != nil { 221 return err 222 } 223 *n = id 224 return nil 225 } 226 227 // HexID converts a hex string to an ID. 228 // The string may be prefixed with 0x. 229 // It panics if the string is not a valid ID. 230 func HexID(in string) ID { 231 id, err := ParseID(in) 232 if err != nil { 233 panic(err) 234 } 235 return id 236 } 237 238 func ParseID(in string) (ID, error) { 239 var id ID 240 b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) 241 if err != nil { 242 return id, err 243 } else if len(b) != len(id) { 244 return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) 245 } 246 copy(id[:], b) 247 return id, nil 248 } 249 250 // DistCmp compares the distances a->target and b->target. 251 // Returns -1 if a is closer to target, 1 if b is closer to target 252 // and 0 if they are equal. 253 func DistCmp(target, a, b ID) int { 254 for i := range target { 255 da := a[i] ^ target[i] 256 db := b[i] ^ target[i] 257 if da > db { 258 return 1 259 } else if da < db { 260 return -1 261 } 262 } 263 return 0 264 } 265 266 // LogDist returns the logarithmic distance between a and b, log2(a ^ b). 267 func LogDist(a, b ID) int { 268 lz := 0 269 for i := range a { 270 x := a[i] ^ b[i] 271 if x == 0 { 272 lz += 8 273 } else { 274 lz += bits.LeadingZeros8(x) 275 break 276 } 277 } 278 return len(a)*8 - lz 279 }