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