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