github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/enode/urlv4.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:41</date> 10 //</624450104589291520> 11 12 13 package enode 14 15 import ( 16 "crypto/ecdsa" 17 "encoding/hex" 18 "errors" 19 "fmt" 20 "net" 21 "net/url" 22 "regexp" 23 "strconv" 24 25 "github.com/ethereum/go-ethereum/common/math" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/p2p/enr" 28 ) 29 30 var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([09a- f] +)$ 31 32 //mustParsev4解析节点URL。如果URL无效,它会恐慌。 33 func MustParseV4(rawurl string) *Node { 34 n, err := ParseV4(rawurl) 35 if err != nil { 36 panic("invalid node URL: " + err.Error()) 37 } 38 return n 39 } 40 41 //Parsev4解析节点URL。 42 // 43 //节点URL有两种基本形式: 44 // 45 //-节点不完整,只有公钥(节点ID) 46 //-包含公钥和IP/端口信息的完整节点 47 // 48 //对于不完整的节点,指示器必须类似于 49 // 50 //enode://<hex node id> 51 //<十六进制节点ID > 52 // 53 //对于完整的节点,节点ID编码在用户名部分 54 //以@符号与主机分隔的URL。主机名可以 55 //仅作为IP地址提供,不允许使用DNS域名。 56 //主机名部分中的端口是TCP侦听端口。如果 57 //TCP和UDP(发现)端口不同,UDP端口指定为 58 //查询参数“discport”。 59 // 60 //在下面的示例中,节点URL描述了 61 //IP地址为10.3.58.6、TCP侦听端口为30303的节点。 62 //和UDP发现端口30301。 63 // 64 //enode://<hex node id>@10.3.58.6:30303?磁盘端口=30301 65 func ParseV4(rawurl string) (*Node, error) { 66 if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { 67 id, err := parsePubkey(m[1]) 68 if err != nil { 69 return nil, fmt.Errorf("invalid node ID (%v)", err) 70 } 71 return NewV4(id, nil, 0, 0), nil 72 } 73 return parseComplete(rawurl) 74 } 75 76 //newv4根据发现v4节点信息创建节点。记录 77 //包含在节点中的签名长度为零。 78 func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node { 79 var r enr.Record 80 if ip != nil { 81 r.Set(enr.IP(ip)) 82 } 83 if udp != 0 { 84 r.Set(enr.UDP(udp)) 85 } 86 if tcp != 0 { 87 r.Set(enr.TCP(tcp)) 88 } 89 signV4Compat(&r, pubkey) 90 n, err := New(v4CompatID{}, &r) 91 if err != nil { 92 panic(err) 93 } 94 return n 95 } 96 97 func parseComplete(rawurl string) (*Node, error) { 98 var ( 99 id *ecdsa.PublicKey 100 ip net.IP 101 tcpPort, udpPort uint64 102 ) 103 u, err := url.Parse(rawurl) 104 if err != nil { 105 return nil, err 106 } 107 if u.Scheme != "enode" { 108 return nil, errors.New("invalid URL scheme, want \"enode\"") 109 } 110 //从用户部分分析节点ID。 111 if u.User == nil { 112 return nil, errors.New("does not contain node ID") 113 } 114 if id, err = parsePubkey(u.User.String()); err != nil { 115 return nil, fmt.Errorf("invalid node ID (%v)", err) 116 } 117 //分析IP地址。 118 host, port, err := net.SplitHostPort(u.Host) 119 if err != nil { 120 return nil, fmt.Errorf("invalid host: %v", err) 121 } 122 if ip = net.ParseIP(host); ip == nil { 123 return nil, errors.New("invalid IP address") 124 } 125 //确保IPv4地址的IP长度为4字节。 126 if ipv4 := ip.To4(); ipv4 != nil { 127 ip = ipv4 128 } 129 //分析端口号。 130 if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { 131 return nil, errors.New("invalid port") 132 } 133 udpPort = tcpPort 134 qv := u.Query() 135 if qv.Get("discport") != "" { 136 udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) 137 if err != nil { 138 return nil, errors.New("invalid discport in query") 139 } 140 } 141 return NewV4(id, ip, int(tcpPort), int(udpPort)), nil 142 } 143 144 //parsepubkey解析十六进制编码的secp256k1公钥。 145 func parsePubkey(in string) (*ecdsa.PublicKey, error) { 146 b, err := hex.DecodeString(in) 147 if err != nil { 148 return nil, err 149 } else if len(b) != 64 { 150 return nil, fmt.Errorf("wrong length, want %d hex chars", 128) 151 } 152 b = append([]byte{0x4}, b...) 153 return crypto.UnmarshalPubkey(b) 154 } 155 156 func (n *Node) v4URL() string { 157 var ( 158 scheme enr.ID 159 nodeid string 160 key ecdsa.PublicKey 161 ) 162 n.Load(&scheme) 163 n.Load((*Secp256k1)(&key)) 164 switch { 165 case scheme == "v4" || key != ecdsa.PublicKey{}: 166 nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:]) 167 default: 168 nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:]) 169 } 170 u := url.URL{Scheme: "enode"} 171 if n.Incomplete() { 172 u.Host = nodeid 173 } else { 174 addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()} 175 u.User = url.User(nodeid) 176 u.Host = addr.String() 177 if n.UDP() != n.TCP() { 178 u.RawQuery = "discport=" + strconv.Itoa(n.UDP()) 179 } 180 } 181 return u.String() 182 } 183 184 //pubKeyToIDv4从给定的公钥派生v4节点地址。 185 func PubkeyToIDV4(key *ecdsa.PublicKey) ID { 186 e := make([]byte, 64) 187 math.ReadBits(key.X, e[:len(e)/2]) 188 math.ReadBits(key.Y, e[len(e)/2:]) 189 return ID(crypto.Keccak256Hash(e)) 190 } 191