github.com/bhs-gq/quorum-hotstuff@v21.1.0+incompatible/p2p/enode/urlv4.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 "net" 25 "net/url" 26 "regexp" 27 "strconv" 28 29 "github.com/ethereum/go-ethereum/common/math" 30 "github.com/ethereum/go-ethereum/crypto" 31 "github.com/ethereum/go-ethereum/p2p/enr" 32 ) 33 34 var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") 35 36 // MustParseV4 parses a node URL. It panics if the URL is not valid. 37 func MustParseV4(rawurl string) *Node { 38 n, err := ParseV4(rawurl) 39 if err != nil { 40 panic("invalid node URL: " + err.Error()) 41 } 42 return n 43 } 44 45 // ParseV4 parses a node URL. 46 // 47 // There are two basic forms of node URLs: 48 // 49 // - incomplete nodes, which only have the public key (node ID) 50 // - complete nodes, which contain the public key and IP/Port information 51 // 52 // For incomplete nodes, the designator must look like one of these 53 // 54 // enode://<hex node id> 55 // <hex node id> 56 // 57 // For complete nodes, the node ID is encoded in the username portion 58 // of the URL, separated from the host by an @ sign. The hostname can 59 // be given as an IP address or a DNS domain name. 60 // The port in the host name section is the TCP listening port. If the 61 // TCP and UDP (discovery) ports differ, the UDP port is specified as 62 // query parameter "discport". 63 // 64 // In the following example, the node URL describes 65 // a node with IP address 10.3.58.6, TCP listening port 30303 66 // and UDP discovery port 30301. 67 // 68 // enode://<hex node id>@10.3.58.6:30303?discport=30301 69 func ParseV4(rawurl string) (*Node, error) { 70 if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { 71 id, err := parsePubkey(m[1]) 72 if err != nil { 73 return nil, fmt.Errorf("invalid public key (%v)", err) 74 } 75 return NewV4(id, nil, 0, 0), nil 76 } 77 return parseComplete(rawurl) 78 } 79 80 // NewV4 creates a node from discovery v4 node information. The record 81 // contained in the node has a zero-length signature. 82 func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node { 83 var r enr.Record 84 if len(ip) > 0 { 85 r.Set(enr.IP(ip)) 86 } 87 return newV4(pubkey, r, tcp, udp) 88 } 89 90 // broken out from `func NewV4` (above) same in upstream go-ethereum, but taken out 91 // to avoid code duplication b/t NewV4 and NewV4Hostname 92 func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp int) *Node { 93 if udp != 0 { 94 r.Set(enr.UDP(udp)) 95 } 96 if tcp != 0 { 97 r.Set(enr.TCP(tcp)) 98 } 99 signV4Compat(&r, pubkey) 100 n, err := New(v4CompatID{}, &r) 101 if err != nil { 102 panic(err) 103 } 104 return n 105 } 106 107 // isNewV4 returns true for nodes created by NewV4. 108 func isNewV4(n *Node) bool { 109 var k s256raw 110 return n.r.IdentityScheme() == "" && n.r.Load(&k) == nil && len(n.r.Signature()) == 0 111 } 112 113 // Quorum 114 115 // NewV4Hostname creates a node from discovery v4 node information. The record 116 // contained in the node has a zero-length signature. It sets the hostname or ip 117 // of the node depends on hostname context 118 func NewV4Hostname(pubkey *ecdsa.PublicKey, hostname string, tcp, udp, raftPort int) *Node { 119 var r enr.Record 120 121 if ip := net.ParseIP(hostname); ip == nil { 122 r.Set(enr.Hostname(hostname)) 123 } else { 124 r.Set(enr.IP(ip)) 125 } 126 127 if raftPort != 0 { 128 r.Set(enr.RaftPort(raftPort)) 129 } 130 131 return newV4(pubkey, r, tcp, udp) 132 } 133 134 // End-Quorum 135 136 func parseComplete(rawurl string) (*Node, error) { 137 var ( 138 id *ecdsa.PublicKey 139 ip net.IP 140 tcpPort, udpPort uint64 141 ) 142 u, err := url.Parse(rawurl) 143 if err != nil { 144 return nil, err 145 } 146 if u.Scheme != "enode" { 147 return nil, errors.New("invalid URL scheme, want \"enode\"") 148 } 149 // Parse the Node ID from the user portion. 150 if u.User == nil { 151 return nil, errors.New("does not contain node ID") 152 } 153 if id, err = parsePubkey(u.User.String()); err != nil { 154 return nil, fmt.Errorf("invalid public key (%v)", err) 155 } 156 // move qv up to here 157 qv := u.Query() 158 // Parse the IP address. 159 ips, err := net.LookupIP(u.Hostname()) 160 if err != nil { 161 // Quorum: if IP look up fail don't return error for raft url 162 if qv.Get("raftport") == "" { 163 return nil, err 164 } 165 } else { 166 ip = ips[0] 167 // Ensure the IP is 4 bytes long for IPv4 addresses. 168 if ipv4 := ip.To4(); ipv4 != nil { 169 ip = ipv4 170 } 171 } 172 // Parse the port numbers. 173 if tcpPort, err = strconv.ParseUint(u.Port(), 10, 16); err != nil { 174 return nil, errors.New("invalid port") 175 } 176 udpPort = tcpPort 177 if qv.Get("discport") != "" { 178 udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) 179 if err != nil { 180 return nil, errors.New("invalid discport in query") 181 } 182 } 183 184 // Quorum 185 if qv.Get("raftport") != "" { 186 raftPort, err := strconv.ParseUint(qv.Get("raftport"), 10, 16) 187 if err != nil { 188 return nil, errors.New("invalid raftport in query") 189 } 190 if u.Hostname() == "" { 191 return nil, errors.New("empty hostname in raft url") 192 } 193 return NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), int(raftPort)), nil 194 } 195 // End-Quorum 196 197 return NewV4(id, ip, int(tcpPort), int(udpPort)), nil 198 } 199 200 func HexPubkey(h string) (*ecdsa.PublicKey, error) { 201 k, err := parsePubkey(h) 202 if err != nil { 203 return nil, err 204 } 205 return k, err 206 } 207 208 // parsePubkey parses a hex-encoded secp256k1 public key. 209 func parsePubkey(in string) (*ecdsa.PublicKey, error) { 210 b, err := hex.DecodeString(in) 211 if err != nil { 212 return nil, err 213 } else if len(b) != 64 { 214 return nil, fmt.Errorf("wrong length, want %d hex chars", 128) 215 } 216 b = append([]byte{0x4}, b...) 217 return crypto.UnmarshalPubkey(b) 218 } 219 220 // used by Quorum RAFT - to derive enodeID 221 func (n *Node) EnodeID() string { 222 var ( 223 scheme enr.ID 224 nodeid string 225 key ecdsa.PublicKey 226 ) 227 n.Load(&scheme) 228 n.Load((*Secp256k1)(&key)) 229 switch { 230 case scheme == "v4" || key != ecdsa.PublicKey{}: 231 nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:]) 232 default: 233 nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:]) 234 } 235 return nodeid 236 } 237 238 func (n *Node) URLv4() string { 239 var ( 240 scheme enr.ID 241 nodeid string 242 key ecdsa.PublicKey 243 ) 244 n.Load(&scheme) 245 n.Load((*Secp256k1)(&key)) 246 switch { 247 case scheme == "v4" || key != ecdsa.PublicKey{}: 248 nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:]) 249 default: 250 nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:]) 251 } 252 u := url.URL{Scheme: "enode"} 253 if n.Incomplete() { 254 u.Host = nodeid 255 } else { 256 u.User = url.User(nodeid) 257 if n.Host() != "" && net.ParseIP(n.Host()) == nil { 258 // Quorum 259 u.Host = net.JoinHostPort(n.Host(), strconv.Itoa(n.TCP())) 260 } else { 261 addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()} 262 u.Host = addr.String() 263 } 264 if n.UDP() != n.TCP() { 265 u.RawQuery = "discport=" + strconv.Itoa(n.UDP()) 266 } 267 // Quorum 268 if n.HasRaftPort() { 269 raftQuery := "raftport=" + strconv.Itoa(n.RaftPort()) 270 if len(u.RawQuery) > 0 { 271 u.RawQuery = u.RawQuery + "&" + raftQuery 272 } else { 273 u.RawQuery = raftQuery 274 } 275 } 276 } 277 return u.String() 278 } 279 280 // PubkeyToIDV4 derives the v4 node address from the given public key. 281 func PubkeyToIDV4(key *ecdsa.PublicKey) ID { 282 e := make([]byte, 64) 283 math.ReadBits(key.X, e[:len(e)/2]) 284 math.ReadBits(key.Y, e[len(e)/2:]) 285 return ID(crypto.Keccak256Hash(e)) 286 }