github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/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  	"math/rand"
    27  	"net"
    28  	"net/url"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/crypto"
    34  	"github.com/ethereum/go-ethereum/crypto/secp256k1"
    35  )
    36  
    37  const nodeIDBits = 512
    38  
    39  // Node represents a host on the network.
    40  type Node struct {
    41  	IP       net.IP // len 4 for IPv4 or 16 for IPv6
    42  	UDP, TCP uint16 // port numbers
    43  	ID       NodeID // the node's public key
    44  
    45  	// This is a cached copy of sha3(ID) which is used for node
    46  	// distance calculations. This is part of Node in order to make it
    47  	// possible to write tests that need a node at a certain distance.
    48  	// In those tests, the content of sha will not actually correspond
    49  	// with ID.
    50  	sha common.Hash
    51  
    52  	// whether this node is currently being pinged in order to replace
    53  	// it in a bucket
    54  	contested bool
    55  }
    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.Sha3Hash(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  // The string representation of a Node is a URL.
    75  // Please see ParseNode for a description of the format.
    76  func (n *Node) String() string {
    77  	addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)}
    78  	u := url.URL{
    79  		Scheme: "enode",
    80  		User:   url.User(fmt.Sprintf("%x", n.ID[:])),
    81  		Host:   addr.String(),
    82  	}
    83  	if n.UDP != n.TCP {
    84  		u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP))
    85  	}
    86  	return u.String()
    87  }
    88  
    89  // ParseNode parses a node URL.
    90  //
    91  // A node URL has scheme "enode".
    92  //
    93  // The hexadecimal node ID is encoded in the username portion of the
    94  // URL, separated from the host by an @ sign. The hostname can only be
    95  // given as an IP address, DNS domain names are not allowed. The port
    96  // in the host name section is the TCP listening port. If the TCP and
    97  // UDP (discovery) ports differ, the UDP port is specified as query
    98  // parameter "discport".
    99  //
   100  // In the following example, the node URL describes
   101  // a node with IP address 10.3.58.6, TCP listening port 30303
   102  // and UDP discovery port 30301.
   103  //
   104  //    enode://<hex node id>@10.3.58.6:30303?discport=30301
   105  func ParseNode(rawurl string) (*Node, error) {
   106  	var (
   107  		id               NodeID
   108  		ip               net.IP
   109  		tcpPort, udpPort uint64
   110  	)
   111  	u, err := url.Parse(rawurl)
   112  	if u.Scheme != "enode" {
   113  		return nil, errors.New("invalid URL scheme, want \"enode\"")
   114  	}
   115  	// Parse the Node ID from the user portion.
   116  	if u.User == nil {
   117  		return nil, errors.New("does not contain node ID")
   118  	}
   119  	if id, err = HexID(u.User.String()); err != nil {
   120  		return nil, fmt.Errorf("invalid node ID (%v)", err)
   121  	}
   122  	// Parse the IP address.
   123  	host, port, err := net.SplitHostPort(u.Host)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("invalid host: %v", err)
   126  	}
   127  	if ip = net.ParseIP(host); ip == nil {
   128  		return nil, errors.New("invalid IP address")
   129  	}
   130  	// Ensure the IP is 4 bytes long for IPv4 addresses.
   131  	if ipv4 := ip.To4(); ipv4 != nil {
   132  		ip = ipv4
   133  	}
   134  	// Parse the port numbers.
   135  	if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
   136  		return nil, errors.New("invalid port")
   137  	}
   138  	udpPort = tcpPort
   139  	qv := u.Query()
   140  	if qv.Get("discport") != "" {
   141  		udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
   142  		if err != nil {
   143  			return nil, errors.New("invalid discport in query")
   144  		}
   145  	}
   146  	return newNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil
   147  }
   148  
   149  // MustParseNode parses a node URL. It panics if the URL is not valid.
   150  func MustParseNode(rawurl string) *Node {
   151  	n, err := ParseNode(rawurl)
   152  	if err != nil {
   153  		panic("invalid node URL: " + err.Error())
   154  	}
   155  	return n
   156  }
   157  
   158  // NodeID is a unique identifier for each node.
   159  // The node identifier is a marshaled elliptic curve public key.
   160  type NodeID [nodeIDBits / 8]byte
   161  
   162  // NodeID prints as a long hexadecimal number.
   163  func (n NodeID) String() string {
   164  	return fmt.Sprintf("%x", n[:])
   165  }
   166  
   167  // The Go syntax representation of a NodeID is a call to HexID.
   168  func (n NodeID) GoString() string {
   169  	return fmt.Sprintf("discover.HexID(\"%x\")", n[:])
   170  }
   171  
   172  // HexID converts a hex string to a NodeID.
   173  // The string may be prefixed with 0x.
   174  func HexID(in string) (NodeID, error) {
   175  	if strings.HasPrefix(in, "0x") {
   176  		in = in[2:]
   177  	}
   178  	var id NodeID
   179  	b, err := hex.DecodeString(in)
   180  	if err != nil {
   181  		return id, err
   182  	} else if len(b) != len(id) {
   183  		return id, fmt.Errorf("wrong length, need %d hex bytes", len(id))
   184  	}
   185  	copy(id[:], b)
   186  	return id, nil
   187  }
   188  
   189  // MustHexID converts a hex string to a NodeID.
   190  // It panics if the string is not a valid NodeID.
   191  func MustHexID(in string) NodeID {
   192  	id, err := HexID(in)
   193  	if err != nil {
   194  		panic(err)
   195  	}
   196  	return id
   197  }
   198  
   199  // PubkeyID returns a marshaled representation of the given public key.
   200  func PubkeyID(pub *ecdsa.PublicKey) NodeID {
   201  	var id NodeID
   202  	pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
   203  	if len(pbytes)-1 != len(id) {
   204  		panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes)))
   205  	}
   206  	copy(id[:], pbytes[1:])
   207  	return id
   208  }
   209  
   210  // Pubkey returns the public key represented by the node ID.
   211  // It returns an error if the ID is not a point on the curve.
   212  func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) {
   213  	p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)}
   214  	half := len(id) / 2
   215  	p.X.SetBytes(id[:half])
   216  	p.Y.SetBytes(id[half:])
   217  	if !p.Curve.IsOnCurve(p.X, p.Y) {
   218  		return nil, errors.New("not a point on the S256 curve")
   219  	}
   220  	return p, nil
   221  }
   222  
   223  // recoverNodeID computes the public key used to sign the
   224  // given hash from the signature.
   225  func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
   226  	pubkey, err := secp256k1.RecoverPubkey(hash, sig)
   227  	if err != nil {
   228  		return id, err
   229  	}
   230  	if len(pubkey)-1 != len(id) {
   231  		return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8)
   232  	}
   233  	for i := range id {
   234  		id[i] = pubkey[i+1]
   235  	}
   236  	return id, nil
   237  }
   238  
   239  // distcmp compares the distances a->target and b->target.
   240  // Returns -1 if a is closer to target, 1 if b is closer to target
   241  // and 0 if they are equal.
   242  func distcmp(target, a, b common.Hash) int {
   243  	for i := range target {
   244  		da := a[i] ^ target[i]
   245  		db := b[i] ^ target[i]
   246  		if da > db {
   247  			return 1
   248  		} else if da < db {
   249  			return -1
   250  		}
   251  	}
   252  	return 0
   253  }
   254  
   255  // table of leading zero counts for bytes [0..255]
   256  var lzcount = [256]int{
   257  	8, 7, 6, 6, 5, 5, 5, 5,
   258  	4, 4, 4, 4, 4, 4, 4, 4,
   259  	3, 3, 3, 3, 3, 3, 3, 3,
   260  	3, 3, 3, 3, 3, 3, 3, 3,
   261  	2, 2, 2, 2, 2, 2, 2, 2,
   262  	2, 2, 2, 2, 2, 2, 2, 2,
   263  	2, 2, 2, 2, 2, 2, 2, 2,
   264  	2, 2, 2, 2, 2, 2, 2, 2,
   265  	1, 1, 1, 1, 1, 1, 1, 1,
   266  	1, 1, 1, 1, 1, 1, 1, 1,
   267  	1, 1, 1, 1, 1, 1, 1, 1,
   268  	1, 1, 1, 1, 1, 1, 1, 1,
   269  	1, 1, 1, 1, 1, 1, 1, 1,
   270  	1, 1, 1, 1, 1, 1, 1, 1,
   271  	1, 1, 1, 1, 1, 1, 1, 1,
   272  	1, 1, 1, 1, 1, 1, 1, 1,
   273  	0, 0, 0, 0, 0, 0, 0, 0,
   274  	0, 0, 0, 0, 0, 0, 0, 0,
   275  	0, 0, 0, 0, 0, 0, 0, 0,
   276  	0, 0, 0, 0, 0, 0, 0, 0,
   277  	0, 0, 0, 0, 0, 0, 0, 0,
   278  	0, 0, 0, 0, 0, 0, 0, 0,
   279  	0, 0, 0, 0, 0, 0, 0, 0,
   280  	0, 0, 0, 0, 0, 0, 0, 0,
   281  	0, 0, 0, 0, 0, 0, 0, 0,
   282  	0, 0, 0, 0, 0, 0, 0, 0,
   283  	0, 0, 0, 0, 0, 0, 0, 0,
   284  	0, 0, 0, 0, 0, 0, 0, 0,
   285  	0, 0, 0, 0, 0, 0, 0, 0,
   286  	0, 0, 0, 0, 0, 0, 0, 0,
   287  	0, 0, 0, 0, 0, 0, 0, 0,
   288  	0, 0, 0, 0, 0, 0, 0, 0,
   289  }
   290  
   291  // logdist returns the logarithmic distance between a and b, log2(a ^ b).
   292  func logdist(a, b common.Hash) int {
   293  	lz := 0
   294  	for i := range a {
   295  		x := a[i] ^ b[i]
   296  		if x == 0 {
   297  			lz += 8
   298  		} else {
   299  			lz += lzcount[x]
   300  			break
   301  		}
   302  	}
   303  	return len(a)*8 - lz
   304  }
   305  
   306  // hashAtDistance returns a random hash such that logdist(a, b) == n
   307  func hashAtDistance(a common.Hash, n int) (b common.Hash) {
   308  	if n == 0 {
   309  		return a
   310  	}
   311  	// flip bit at position n, fill the rest with random bits
   312  	b = a
   313  	pos := len(a) - n/8 - 1
   314  	bit := byte(0x01) << (byte(n%8) - 1)
   315  	if bit == 0 {
   316  		pos++
   317  		bit = 0x80
   318  	}
   319  	b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
   320  	for i := pos + 1; i < len(a); i++ {
   321  		b[i] = byte(rand.Intn(255))
   322  	}
   323  	return b
   324  }