github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/p2p/discover/node.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package discover
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"crypto/elliptic"
    22  	"encoding/hex"
    23  	"errors"
    24  	"fmt"
    25  	"math/big"
    26  	"net"
    27  	"net/url"
    28  	"regexp"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"time"
    33  
    34  	"github.com/ethereumproject/go-ethereum/common"
    35  	"github.com/ethereumproject/go-ethereum/crypto"
    36  	"github.com/ethereumproject/go-ethereum/crypto/secp256k1"
    37  )
    38  
    39  const nodeIDBits = 512
    40  
    41  // Node represents a host on the network.
    42  // The fields of Node may not be modified.
    43  type Node struct {
    44  	IP net.IP `json:"ip"` // len 4 for IPv4 or 16 for IPv6
    45  	// port numbers
    46  	UDP uint16 `json:"udp"`
    47  	TCP uint16 `json:"tcp"`
    48  	ID  NodeID `json:"node_id"` // the node's public key
    49  
    50  	// This is a cached copy of sha3(ID) which is used for node
    51  	// distance calculations. This is part of Node in order to make it
    52  	// possible to write tests that need a node at a certain distance.
    53  	// In those tests, the content of sha will not actually correspond
    54  	// with ID.
    55  	sha common.Hash `json:"sha"`
    56  
    57  	// whether this node is currently being pinged in order to replace
    58  	// it in a bucket
    59  	contested bool `json:"contested"`
    60  
    61  	// Time when the node was added to the table.
    62  	addedAt time.Time
    63  }
    64  
    65  // NewNode creates a new node. It is mostly meant to be used for
    66  // testing purposes.
    67  func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node {
    68  	if ipv4 := ip.To4(); ipv4 != nil {
    69  		ip = ipv4
    70  	}
    71  	return &Node{
    72  		IP:  ip,
    73  		UDP: udpPort,
    74  		TCP: tcpPort,
    75  		ID:  id,
    76  		sha: crypto.Keccak256Hash(id[:]),
    77  	}
    78  }
    79  
    80  func (n *Node) addr() *net.UDPAddr {
    81  	return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
    82  }
    83  
    84  // Incomplete returns true for nodes with no IP address.
    85  func (n *Node) Incomplete() bool {
    86  	return n.IP == nil
    87  }
    88  
    89  // checks whether n is a valid complete node.
    90  func (n *Node) validateComplete() error {
    91  	if n.Incomplete() {
    92  		return errors.New("incomplete node")
    93  	}
    94  	if n.UDP == 0 {
    95  		return errors.New("missing UDP port")
    96  	}
    97  	if n.TCP == 0 {
    98  		return errors.New("missing TCP port")
    99  	}
   100  	if n.IP.IsMulticast() || n.IP.IsUnspecified() {
   101  		return errors.New("invalid IP (multicast/unspecified)")
   102  	}
   103  	_, err := n.ID.Pubkey() // validate the key (on curve, etc.)
   104  	return err
   105  }
   106  
   107  // The string representation of a Node is a URL.
   108  // Please see ParseNode for a description of the format.
   109  func (n *Node) String() string {
   110  	u := url.URL{Scheme: "enode"}
   111  	if n.Incomplete() {
   112  		u.Host = fmt.Sprintf("%x", n.ID[:])
   113  	} else {
   114  		addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)}
   115  		u.User = url.User(fmt.Sprintf("%x", n.ID[:]))
   116  		u.Host = addr.String()
   117  		if n.UDP != n.TCP {
   118  			u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP))
   119  		}
   120  	}
   121  	return u.String()
   122  }
   123  
   124  var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
   125  
   126  // ParseNode parses a node designator.
   127  //
   128  // There are two basic forms of node designators
   129  //   - incomplete nodes, which only have the public key (node ID)
   130  //   - complete nodes, which contain the public key and IP/Port information
   131  //
   132  // For incomplete nodes, the designator must look like one of these
   133  //
   134  //    enode://<hex node id>
   135  //    <hex node id>
   136  //
   137  // For complete nodes, the node ID is encoded in the username portion
   138  // of the URL, separated from the host by an @ sign. The hostname can
   139  // only be given as an IP address, DNS domain names are not allowed.
   140  // The port in the host name section is the TCP listening port. If the
   141  // TCP and UDP (discovery) ports differ, the UDP port is specified as
   142  // query parameter "discport".
   143  //
   144  // In the following example, the node URL describes
   145  // a node with IP address 10.3.58.6, TCP listening port 30303
   146  // and UDP discovery port 30301.
   147  //
   148  //    enode://<hex node id>@10.3.58.6:30303?discport=30301
   149  func ParseNode(rawurl string) (*Node, error) {
   150  	if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
   151  		id, err := HexID(m[1])
   152  		if err != nil {
   153  			return nil, fmt.Errorf("invalid node ID (%v)", err)
   154  		}
   155  		return NewNode(id, nil, 0, 0), nil
   156  	}
   157  	return parseComplete(rawurl)
   158  }
   159  
   160  func parseComplete(rawurl string) (*Node, error) {
   161  	var (
   162  		id               NodeID
   163  		ip               net.IP
   164  		tcpPort, udpPort uint64
   165  	)
   166  	u, err := url.Parse(rawurl)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	if u.Scheme != "enode" {
   171  		return nil, errors.New("invalid URL scheme, want \"enode\"")
   172  	}
   173  	// Parse the Node ID from the user portion.
   174  	if u.User == nil {
   175  		return nil, errors.New("does not contain node ID")
   176  	}
   177  	if id, err = HexID(u.User.String()); err != nil {
   178  		return nil, fmt.Errorf("invalid node ID (%v)", err)
   179  	}
   180  	// Parse the IP address.
   181  	host, port, err := net.SplitHostPort(u.Host)
   182  	if err != nil {
   183  		return nil, fmt.Errorf("invalid host: %v", err)
   184  	}
   185  	if ip = net.ParseIP(host); ip == nil {
   186  		return nil, errors.New("invalid IP address")
   187  	}
   188  	// Ensure the IP is 4 bytes long for IPv4 addresses.
   189  	if ipv4 := ip.To4(); ipv4 != nil {
   190  		ip = ipv4
   191  	}
   192  	// Parse the port numbers.
   193  	if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
   194  		return nil, errors.New("invalid port")
   195  	}
   196  	udpPort = tcpPort
   197  	qv := u.Query()
   198  	if qv.Get("discport") != "" {
   199  		udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
   200  		if err != nil {
   201  			return nil, errors.New("invalid discport in query")
   202  		}
   203  	}
   204  	return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil
   205  }
   206  
   207  // MustParseNode parses a node URL. It panics if the URL is not valid.
   208  func MustParseNode(rawurl string) *Node {
   209  	n, err := ParseNode(rawurl)
   210  	if err != nil {
   211  		panic("invalid node URL: " + err.Error())
   212  	}
   213  	return n
   214  }
   215  
   216  // NodeID is a unique identifier for each node.
   217  // The node identifier is a marshaled elliptic curve public key.
   218  type NodeID [nodeIDBits / 8]byte
   219  
   220  // NodeID prints as a long hexadecimal number.
   221  func (n NodeID) String() string {
   222  	return fmt.Sprintf("%x", n[:])
   223  }
   224  
   225  // The Go syntax representation of a NodeID is a call to HexID.
   226  func (n NodeID) GoString() string {
   227  	return fmt.Sprintf("discover.HexID(\"%x\")", n[:])
   228  }
   229  
   230  // HexID converts a hex string to a NodeID.
   231  // The string may be prefixed with 0x.
   232  func HexID(in string) (NodeID, error) {
   233  	if strings.HasPrefix(in, "0x") {
   234  		in = in[2:]
   235  	}
   236  	var id NodeID
   237  	b, err := hex.DecodeString(in)
   238  	if err != nil {
   239  		return id, err
   240  	} else if len(b) != len(id) {
   241  		return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
   242  	}
   243  	copy(id[:], b)
   244  	return id, nil
   245  }
   246  
   247  // MustHexID converts a hex string to a NodeID.
   248  // It panics if the string is not a valid NodeID.
   249  func MustHexID(in string) NodeID {
   250  	id, err := HexID(in)
   251  	if err != nil {
   252  		panic(err)
   253  	}
   254  	return id
   255  }
   256  
   257  // PubkeyID returns a marshaled representation of the given public key.
   258  func PubkeyID(pub *ecdsa.PublicKey) NodeID {
   259  	var id NodeID
   260  	pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
   261  	if len(pbytes)-1 != len(id) {
   262  		panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes)))
   263  	}
   264  	copy(id[:], pbytes[1:])
   265  	return id
   266  }
   267  
   268  // Pubkey returns the public key represented by the node ID.
   269  // It returns an error if the ID is not a point on the curve.
   270  func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) {
   271  	p := &ecdsa.PublicKey{Curve: secp256k1.S256(), X: new(big.Int), Y: new(big.Int)}
   272  	half := len(id) / 2
   273  	p.X.SetBytes(id[:half])
   274  	p.Y.SetBytes(id[half:])
   275  	if !p.Curve.IsOnCurve(p.X, p.Y) {
   276  		return nil, errors.New("id is invalid secp256k1 curve point")
   277  	}
   278  	return p, nil
   279  }
   280  
   281  // recoverNodeID computes the public key used to sign the
   282  // given hash from the signature.
   283  func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
   284  	pubkey, err := secp256k1.RecoverPubkey(hash, sig)
   285  	if err != nil {
   286  		return id, err
   287  	}
   288  	if len(pubkey)-1 != len(id) {
   289  		return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8)
   290  	}
   291  	for i := range id {
   292  		id[i] = pubkey[i+1]
   293  	}
   294  	return id, nil
   295  }