github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/discover/node.go (about)

     1  package discover
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"encoding/hex"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"math/rand"
    11  	"net"
    12  	"net/url"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/jonasnick/go-ethereum/crypto/secp256k1"
    18  	"github.com/jonasnick/go-ethereum/rlp"
    19  )
    20  
    21  const nodeIDBits = 512
    22  
    23  // Node represents a host on the network.
    24  type Node struct {
    25  	ID NodeID
    26  	IP net.IP
    27  
    28  	DiscPort int // UDP listening port for discovery protocol
    29  	TCPPort  int // TCP listening port for RLPx
    30  
    31  	active time.Time
    32  }
    33  
    34  func newNode(id NodeID, addr *net.UDPAddr) *Node {
    35  	return &Node{
    36  		ID:       id,
    37  		IP:       addr.IP,
    38  		DiscPort: addr.Port,
    39  		TCPPort:  addr.Port,
    40  		active:   time.Now(),
    41  	}
    42  }
    43  
    44  func (n *Node) isValid() bool {
    45  	// TODO: don't accept localhost, LAN addresses from internet hosts
    46  	return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0
    47  }
    48  
    49  // The string representation of a Node is a URL.
    50  // Please see ParseNode for a description of the format.
    51  func (n *Node) String() string {
    52  	addr := net.TCPAddr{IP: n.IP, Port: n.TCPPort}
    53  	u := url.URL{
    54  		Scheme: "enode",
    55  		User:   url.User(fmt.Sprintf("%x", n.ID[:])),
    56  		Host:   addr.String(),
    57  	}
    58  	if n.DiscPort != n.TCPPort {
    59  		u.RawQuery = "discport=" + strconv.Itoa(n.DiscPort)
    60  	}
    61  	return u.String()
    62  }
    63  
    64  // ParseNode parses a node URL.
    65  //
    66  // A node URL has scheme "enode".
    67  //
    68  // The hexadecimal node ID is encoded in the username portion of the
    69  // URL, separated from the host by an @ sign. The hostname can only be
    70  // given as an IP address, DNS domain names are not allowed. The port
    71  // in the host name section is the TCP listening port. If the TCP and
    72  // UDP (discovery) ports differ, the UDP port is specified as query
    73  // parameter "discport".
    74  //
    75  // In the following example, the node URL describes
    76  // a node with IP address 10.3.58.6, TCP listening port 30303
    77  // and UDP discovery port 30301.
    78  //
    79  //    enode://<hex node id>@10.3.58.6:30303?discport=30301
    80  func ParseNode(rawurl string) (*Node, error) {
    81  	var n Node
    82  	u, err := url.Parse(rawurl)
    83  	if u.Scheme != "enode" {
    84  		return nil, errors.New("invalid URL scheme, want \"enode\"")
    85  	}
    86  	if u.User == nil {
    87  		return nil, errors.New("does not contain node ID")
    88  	}
    89  	if n.ID, err = HexID(u.User.String()); err != nil {
    90  		return nil, fmt.Errorf("invalid node ID (%v)", err)
    91  	}
    92  	ip, port, err := net.SplitHostPort(u.Host)
    93  	if err != nil {
    94  		return nil, fmt.Errorf("invalid host: %v", err)
    95  	}
    96  	if n.IP = net.ParseIP(ip); n.IP == nil {
    97  		return nil, errors.New("invalid IP address")
    98  	}
    99  	if n.TCPPort, err = strconv.Atoi(port); err != nil {
   100  		return nil, errors.New("invalid port")
   101  	}
   102  	qv := u.Query()
   103  	if qv.Get("discport") == "" {
   104  		n.DiscPort = n.TCPPort
   105  	} else {
   106  		if n.DiscPort, err = strconv.Atoi(qv.Get("discport")); err != nil {
   107  			return nil, errors.New("invalid discport in query")
   108  		}
   109  	}
   110  	return &n, nil
   111  }
   112  
   113  // MustParseNode parses a node URL. It panics if the URL is not valid.
   114  func MustParseNode(rawurl string) *Node {
   115  	n, err := ParseNode(rawurl)
   116  	if err != nil {
   117  		panic("invalid node URL: " + err.Error())
   118  	}
   119  	return n
   120  }
   121  
   122  func (n Node) EncodeRLP(w io.Writer) error {
   123  	return rlp.Encode(w, rpcNode{IP: n.IP.String(), Port: uint16(n.TCPPort), ID: n.ID})
   124  }
   125  func (n *Node) DecodeRLP(s *rlp.Stream) (err error) {
   126  	var ext rpcNode
   127  	if err = s.Decode(&ext); err == nil {
   128  		n.TCPPort = int(ext.Port)
   129  		n.DiscPort = int(ext.Port)
   130  		n.ID = ext.ID
   131  		if n.IP = net.ParseIP(ext.IP); n.IP == nil {
   132  			return errors.New("invalid IP string")
   133  		}
   134  	}
   135  	return err
   136  }
   137  
   138  // NodeID is a unique identifier for each node.
   139  // The node identifier is a marshaled elliptic curve public key.
   140  type NodeID [nodeIDBits / 8]byte
   141  
   142  // NodeID prints as a long hexadecimal number.
   143  func (n NodeID) String() string {
   144  	return fmt.Sprintf("%#x", n[:])
   145  }
   146  
   147  // The Go syntax representation of a NodeID is a call to HexID.
   148  func (n NodeID) GoString() string {
   149  	return fmt.Sprintf("discover.HexID(\"%#x\")", n[:])
   150  }
   151  
   152  // HexID converts a hex string to a NodeID.
   153  // The string may be prefixed with 0x.
   154  func HexID(in string) (NodeID, error) {
   155  	if strings.HasPrefix(in, "0x") {
   156  		in = in[2:]
   157  	}
   158  	var id NodeID
   159  	b, err := hex.DecodeString(in)
   160  	if err != nil {
   161  		return id, err
   162  	} else if len(b) != len(id) {
   163  		return id, fmt.Errorf("wrong length, need %d hex bytes", len(id))
   164  	}
   165  	copy(id[:], b)
   166  	return id, nil
   167  }
   168  
   169  // MustHexID converts a hex string to a NodeID.
   170  // It panics if the string is not a valid NodeID.
   171  func MustHexID(in string) NodeID {
   172  	id, err := HexID(in)
   173  	if err != nil {
   174  		panic(err)
   175  	}
   176  	return id
   177  }
   178  
   179  // PubkeyID returns a marshaled representation of the given public key.
   180  func PubkeyID(pub *ecdsa.PublicKey) NodeID {
   181  	var id NodeID
   182  	pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
   183  	if len(pbytes)-1 != len(id) {
   184  		panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes)))
   185  	}
   186  	copy(id[:], pbytes[1:])
   187  	return id
   188  }
   189  
   190  // recoverNodeID computes the public key used to sign the
   191  // given hash from the signature.
   192  func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
   193  	pubkey, err := secp256k1.RecoverPubkey(hash, sig)
   194  	if err != nil {
   195  		return id, err
   196  	}
   197  	if len(pubkey)-1 != len(id) {
   198  		return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8)
   199  	}
   200  	for i := range id {
   201  		id[i] = pubkey[i+1]
   202  	}
   203  	return id, nil
   204  }
   205  
   206  // distcmp compares the distances a->target and b->target.
   207  // Returns -1 if a is closer to target, 1 if b is closer to target
   208  // and 0 if they are equal.
   209  func distcmp(target, a, b NodeID) int {
   210  	for i := range target {
   211  		da := a[i] ^ target[i]
   212  		db := b[i] ^ target[i]
   213  		if da > db {
   214  			return 1
   215  		} else if da < db {
   216  			return -1
   217  		}
   218  	}
   219  	return 0
   220  }
   221  
   222  // table of leading zero counts for bytes [0..255]
   223  var lzcount = [256]int{
   224  	8, 7, 6, 6, 5, 5, 5, 5,
   225  	4, 4, 4, 4, 4, 4, 4, 4,
   226  	3, 3, 3, 3, 3, 3, 3, 3,
   227  	3, 3, 3, 3, 3, 3, 3, 3,
   228  	2, 2, 2, 2, 2, 2, 2, 2,
   229  	2, 2, 2, 2, 2, 2, 2, 2,
   230  	2, 2, 2, 2, 2, 2, 2, 2,
   231  	2, 2, 2, 2, 2, 2, 2, 2,
   232  	1, 1, 1, 1, 1, 1, 1, 1,
   233  	1, 1, 1, 1, 1, 1, 1, 1,
   234  	1, 1, 1, 1, 1, 1, 1, 1,
   235  	1, 1, 1, 1, 1, 1, 1, 1,
   236  	1, 1, 1, 1, 1, 1, 1, 1,
   237  	1, 1, 1, 1, 1, 1, 1, 1,
   238  	1, 1, 1, 1, 1, 1, 1, 1,
   239  	1, 1, 1, 1, 1, 1, 1, 1,
   240  	0, 0, 0, 0, 0, 0, 0, 0,
   241  	0, 0, 0, 0, 0, 0, 0, 0,
   242  	0, 0, 0, 0, 0, 0, 0, 0,
   243  	0, 0, 0, 0, 0, 0, 0, 0,
   244  	0, 0, 0, 0, 0, 0, 0, 0,
   245  	0, 0, 0, 0, 0, 0, 0, 0,
   246  	0, 0, 0, 0, 0, 0, 0, 0,
   247  	0, 0, 0, 0, 0, 0, 0, 0,
   248  	0, 0, 0, 0, 0, 0, 0, 0,
   249  	0, 0, 0, 0, 0, 0, 0, 0,
   250  	0, 0, 0, 0, 0, 0, 0, 0,
   251  	0, 0, 0, 0, 0, 0, 0, 0,
   252  	0, 0, 0, 0, 0, 0, 0, 0,
   253  	0, 0, 0, 0, 0, 0, 0, 0,
   254  	0, 0, 0, 0, 0, 0, 0, 0,
   255  	0, 0, 0, 0, 0, 0, 0, 0,
   256  }
   257  
   258  // logdist returns the logarithmic distance between a and b, log2(a ^ b).
   259  func logdist(a, b NodeID) int {
   260  	lz := 0
   261  	for i := range a {
   262  		x := a[i] ^ b[i]
   263  		if x == 0 {
   264  			lz += 8
   265  		} else {
   266  			lz += lzcount[x]
   267  			break
   268  		}
   269  	}
   270  	return len(a)*8 - lz
   271  }
   272  
   273  // randomID returns a random NodeID such that logdist(a, b) == n
   274  func randomID(a NodeID, n int) (b NodeID) {
   275  	if n == 0 {
   276  		return a
   277  	}
   278  	// flip bit at position n, fill the rest with random bits
   279  	b = a
   280  	pos := len(a) - n/8 - 1
   281  	bit := byte(0x01) << (byte(n%8) - 1)
   282  	if bit == 0 {
   283  		pos++
   284  		bit = 0x80
   285  	}
   286  	b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
   287  	for i := pos + 1; i < len(a); i++ {
   288  		b[i] = byte(rand.Intn(255))
   289  	}
   290  	return b
   291  }