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