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