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