github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discv5/node.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 12:09:43</date> 10 //</624342656998182912> 11 12 13 package discv5 14 15 import ( 16 "crypto/ecdsa" 17 "crypto/elliptic" 18 "encoding/hex" 19 "errors" 20 "fmt" 21 "math/big" 22 "math/rand" 23 "net" 24 "net/url" 25 "regexp" 26 "strconv" 27 "strings" 28 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/crypto" 31 ) 32 33 //节点表示网络上的主机。 34 //不能修改节点的公共字段。 35 type Node struct { 36 IP net.IP //IPv4的len 4或IPv6的len 16 37 UDP, TCP uint16 //端口号 38 ID NodeID //节点的公钥 39 40 //网络相关字段包含在节点中。 41 //这些字段不应该在 42 //network.loop goroutine。 43 nodeNetGuts 44 } 45 46 //new node创建新节点。它主要用于 47 //测试目的。 48 func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { 49 if ipv4 := ip.To4(); ipv4 != nil { 50 ip = ipv4 51 } 52 return &Node{ 53 IP: ip, 54 UDP: udpPort, 55 TCP: tcpPort, 56 ID: id, 57 nodeNetGuts: nodeNetGuts{sha: crypto.Keccak256Hash(id[:])}, 58 } 59 } 60 61 func (n *Node) addr() *net.UDPAddr { 62 return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} 63 } 64 65 func (n *Node) setAddr(a *net.UDPAddr) { 66 n.IP = a.IP 67 if ipv4 := a.IP.To4(); ipv4 != nil { 68 n.IP = ipv4 69 } 70 n.UDP = uint16(a.Port) 71 } 72 73 //将给定地址与存储值进行比较。 74 func (n *Node) addrEqual(a *net.UDPAddr) bool { 75 ip := a.IP 76 if ipv4 := a.IP.To4(); ipv4 != nil { 77 ip = ipv4 78 } 79 return n.UDP == uint16(a.Port) && n.IP.Equal(ip) 80 } 81 82 //对于没有IP地址的节点,不完整返回true。 83 func (n *Node) Incomplete() bool { 84 return n.IP == nil 85 } 86 87 //检查n是否为有效的完整节点。 88 func (n *Node) validateComplete() error { 89 if n.Incomplete() { 90 return errors.New("incomplete node") 91 } 92 if n.UDP == 0 { 93 return errors.New("missing UDP port") 94 } 95 if n.TCP == 0 { 96 return errors.New("missing TCP port") 97 } 98 if n.IP.IsMulticast() || n.IP.IsUnspecified() { 99 return errors.New("invalid IP (multicast/unspecified)") 100 } 101 _, err := n.ID.Pubkey() //验证密钥(在曲线上等) 102 return err 103 } 104 105 //节点的字符串表示形式是一个URL。 106 //有关格式的描述,请参阅ParseNode。 107 func (n *Node) String() string { 108 u := url.URL{Scheme: "enode"} 109 if n.Incomplete() { 110 u.Host = fmt.Sprintf("%x", n.ID[:]) 111 } else { 112 addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} 113 u.User = url.User(fmt.Sprintf("%x", n.ID[:])) 114 u.Host = addr.String() 115 if n.UDP != n.TCP { 116 u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) 117 } 118 } 119 return u.String() 120 } 121 122 var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([09a- f] +)$ 123 124 //ParseNode解析节点指示符。 125 // 126 //节点指示符有两种基本形式 127 //-节点不完整,只有公钥(节点ID) 128 //-包含公钥和IP/端口信息的完整节点 129 // 130 //对于不完整的节点,指示器必须类似于 131 // 132 //enode://<hex node id> 133 //<十六进制节点ID > 134 // 135 //对于完整的节点,节点ID编码在用户名部分 136 //以@符号与主机分隔的URL。主机名可以 137 //仅作为IP地址提供,不允许使用DNS域名。 138 //主机名部分中的端口是TCP侦听端口。如果 139 //TCP和UDP(发现)端口不同,UDP端口指定为 140 //查询参数“discport”。 141 // 142 //在下面的示例中,节点URL描述了 143 //IP地址为10.3.58.6、TCP侦听端口为30303的节点。 144 //和UDP发现端口30301。 145 // 146 //enode://<hex node id>@10.3.58.6:30303?磁盘端口=30301 147 func ParseNode(rawurl string) (*Node, error) { 148 if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { 149 id, err := HexID(m[1]) 150 if err != nil { 151 return nil, fmt.Errorf("invalid node ID (%v)", err) 152 } 153 return NewNode(id, nil, 0, 0), nil 154 } 155 return parseComplete(rawurl) 156 } 157 158 func parseComplete(rawurl string) (*Node, error) { 159 var ( 160 id NodeID 161 ip net.IP 162 tcpPort, udpPort uint64 163 ) 164 u, err := url.Parse(rawurl) 165 if err != nil { 166 return nil, err 167 } 168 if u.Scheme != "enode" { 169 return nil, errors.New("invalid URL scheme, want \"enode\"") 170 } 171 //从用户部分分析节点ID。 172 if u.User == nil { 173 return nil, errors.New("does not contain node ID") 174 } 175 if id, err = HexID(u.User.String()); err != nil { 176 return nil, fmt.Errorf("invalid node ID (%v)", err) 177 } 178 //分析IP地址。 179 host, port, err := net.SplitHostPort(u.Host) 180 if err != nil { 181 return nil, fmt.Errorf("invalid host: %v", err) 182 } 183 if ip = net.ParseIP(host); ip == nil { 184 return nil, errors.New("invalid IP address") 185 } 186 //确保IPv4地址的IP长度为4字节。 187 if ipv4 := ip.To4(); ipv4 != nil { 188 ip = ipv4 189 } 190 //分析端口号。 191 if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { 192 return nil, errors.New("invalid port") 193 } 194 udpPort = tcpPort 195 qv := u.Query() 196 if qv.Get("discport") != "" { 197 udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) 198 if err != nil { 199 return nil, errors.New("invalid discport in query") 200 } 201 } 202 return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil 203 } 204 205 //mustParseNode解析节点URL。如果URL无效,它会恐慌。 206 func MustParseNode(rawurl string) *Node { 207 n, err := ParseNode(rawurl) 208 if err != nil { 209 panic("invalid node URL: " + err.Error()) 210 } 211 return n 212 } 213 214 //MarshalText实现Encoding.TextMarshaler。 215 func (n *Node) MarshalText() ([]byte, error) { 216 return []byte(n.String()), nil 217 } 218 219 //UnmarshalText实现encoding.textUnmarshaller。 220 func (n *Node) UnmarshalText(text []byte) error { 221 dec, err := ParseNode(string(text)) 222 if err == nil { 223 *n = *dec 224 } 225 return err 226 } 227 228 //键入nodequeue[]*node 229 // 230 ////如果pushnew不存在,则在末尾添加n。 231 //func(nl*nodelist)appendnew(n*node) 232 //对于u,输入:=范围n 233 //IF项=n{ 234 //返回 235 //} 236 //} 237 //*nq=附加(*nq,n) 238 //} 239 // 240 ////PopRandom删除一个随机节点。节点接近 241 ////到开头的概率稍高。 242 //func(nl*nodelist)popRandom()*节点 243 //ix:=rand.intn(长度(*nq)) 244 ////TODO:如上所述的概率。 245 //nl.删除索引(ix) 246 //} 247 // 248 //func(nl*nodelist)removeindex(i int)*节点 249 //切片=*nl 250 //如果len(*slice)<=i 251 //返回零 252 //} 253 //*nl=append(slice[:i],slice[i+1:……) 254 //} 255 256 const nodeIDBits = 512 257 258 //nodeid是每个节点的唯一标识符。 259 //节点标识符是一个封送椭圆曲线公钥。 260 type NodeID [nodeIDBits / 8]byte 261 262 //nodeid以十六进制长数字打印。 263 func (n NodeID) String() string { 264 return fmt.Sprintf("%x", n[:]) 265 } 266 267 //nodeid的go语法表示是对hexid的调用。 268 func (n NodeID) GoString() string { 269 return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) 270 } 271 272 //TerminalString返回用于终端日志记录的缩短的十六进制字符串。 273 func (n NodeID) TerminalString() string { 274 return hex.EncodeToString(n[:8]) 275 } 276 277 //hexid将十六进制字符串转换为nodeid。 278 //字符串的前缀可以是0x。 279 func HexID(in string) (NodeID, error) { 280 var id NodeID 281 b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) 282 if err != nil { 283 return id, err 284 } else if len(b) != len(id) { 285 return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) 286 } 287 copy(id[:], b) 288 return id, nil 289 } 290 291 //musthexid将十六进制字符串转换为nodeid。 292 //如果字符串不是有效的nodeid,它会恐慌。 293 func MustHexID(in string) NodeID { 294 id, err := HexID(in) 295 if err != nil { 296 panic(err) 297 } 298 return id 299 } 300 301 //pubkeyid返回给定公钥的封送表示形式。 302 func PubkeyID(pub *ecdsa.PublicKey) NodeID { 303 var id NodeID 304 pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) 305 if len(pbytes)-1 != len(id) { 306 panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) 307 } 308 copy(id[:], pbytes[1:]) 309 return id 310 } 311 312 //pubkey返回由节点ID表示的公钥。 313 //如果ID不是曲线上的点,则返回错误。 314 func (n NodeID) Pubkey() (*ecdsa.PublicKey, error) { 315 p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} 316 half := len(n) / 2 317 p.X.SetBytes(n[:half]) 318 p.Y.SetBytes(n[half:]) 319 if !p.Curve.IsOnCurve(p.X, p.Y) { 320 return nil, errors.New("id is invalid secp256k1 curve point") 321 } 322 return p, nil 323 } 324 325 func (id NodeID) mustPubkey() ecdsa.PublicKey { 326 pk, err := id.Pubkey() 327 if err != nil { 328 panic(err) 329 } 330 return *pk 331 } 332 333 //recovernodeid计算用于签名的公钥 334 //从签名中给定哈希。 335 func recoverNodeID(hash, sig []byte) (id NodeID, err error) { 336 pubkey, err := crypto.Ecrecover(hash, sig) 337 if err != nil { 338 return id, err 339 } 340 if len(pubkey)-1 != len(id) { 341 return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) 342 } 343 for i := range id { 344 id[i] = pubkey[i+1] 345 } 346 return id, nil 347 } 348 349 //distcmp比较距离a->target和b->target。 350 //如果a接近目标返回-1,如果b接近目标返回1 351 //如果相等,则为0。 352 func distcmp(target, a, b common.Hash) int { 353 for i := range target { 354 da := a[i] ^ target[i] 355 db := b[i] ^ target[i] 356 if da > db { 357 return 1 358 } else if da < db { 359 return -1 360 } 361 } 362 return 0 363 } 364 365 //字节[0..255]的前导零计数表 366 var lzcount = [256]int{ 367 8, 7, 6, 6, 5, 5, 5, 5, 368 4, 4, 4, 4, 4, 4, 4, 4, 369 3, 3, 3, 3, 3, 3, 3, 3, 370 3, 3, 3, 3, 3, 3, 3, 3, 371 2, 2, 2, 2, 2, 2, 2, 2, 372 2, 2, 2, 2, 2, 2, 2, 2, 373 2, 2, 2, 2, 2, 2, 2, 2, 374 2, 2, 2, 2, 2, 2, 2, 2, 375 1, 1, 1, 1, 1, 1, 1, 1, 376 1, 1, 1, 1, 1, 1, 1, 1, 377 1, 1, 1, 1, 1, 1, 1, 1, 378 1, 1, 1, 1, 1, 1, 1, 1, 379 1, 1, 1, 1, 1, 1, 1, 1, 380 1, 1, 1, 1, 1, 1, 1, 1, 381 1, 1, 1, 1, 1, 1, 1, 1, 382 1, 1, 1, 1, 1, 1, 1, 1, 383 0, 0, 0, 0, 0, 0, 0, 0, 384 0, 0, 0, 0, 0, 0, 0, 0, 385 0, 0, 0, 0, 0, 0, 0, 0, 386 0, 0, 0, 0, 0, 0, 0, 0, 387 0, 0, 0, 0, 0, 0, 0, 0, 388 0, 0, 0, 0, 0, 0, 0, 0, 389 0, 0, 0, 0, 0, 0, 0, 0, 390 0, 0, 0, 0, 0, 0, 0, 0, 391 0, 0, 0, 0, 0, 0, 0, 0, 392 0, 0, 0, 0, 0, 0, 0, 0, 393 0, 0, 0, 0, 0, 0, 0, 0, 394 0, 0, 0, 0, 0, 0, 0, 0, 395 0, 0, 0, 0, 0, 0, 0, 0, 396 0, 0, 0, 0, 0, 0, 0, 0, 397 0, 0, 0, 0, 0, 0, 0, 0, 398 0, 0, 0, 0, 0, 0, 0, 0, 399 } 400 401 //logdist返回a和b之间的对数距离,log2(a^b)。 402 func logdist(a, b common.Hash) int { 403 lz := 0 404 for i := range a { 405 x := a[i] ^ b[i] 406 if x == 0 { 407 lz += 8 408 } else { 409 lz += lzcount[x] 410 break 411 } 412 } 413 return len(a)*8 - lz 414 } 415 416 //hashatDistance返回一个随机哈希,使logDist(a,b)=n 417 func hashAtDistance(a common.Hash, n int) (b common.Hash) { 418 if n == 0 { 419 return a 420 } 421 //在n位置翻转钻头,其余部分用随机钻头填满。 422 b = a 423 pos := len(a) - n/8 - 1 424 bit := byte(0x01) << (byte(n%8) - 1) 425 if bit == 0 { 426 pos++ 427 bit = 0x80 428 } 429 b[pos] = a[pos]&^bit | ^a[pos]&bit //TODO:随机结束位 430 for i := pos + 1; i < len(a); i++ { 431 b[i] = byte(rand.Intn(255)) 432 } 433 return b 434 } 435