github.com/ethereum/go-ethereum@v1.16.1/p2p/enode/node.go (about)

     1  // Copyright 2018 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 enode
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/base64"
    22  	"encoding/hex"
    23  	"errors"
    24  	"fmt"
    25  	"math/bits"
    26  	"net"
    27  	"net/netip"
    28  	"strings"
    29  
    30  	"github.com/ethereum/go-ethereum/p2p/enr"
    31  	"github.com/ethereum/go-ethereum/rlp"
    32  )
    33  
    34  var errMissingPrefix = errors.New("missing 'enr:' prefix for base64-encoded record")
    35  
    36  // Node represents a host on the network.
    37  type Node struct {
    38  	r  enr.Record
    39  	id ID
    40  
    41  	// hostname tracks the DNS name of the node.
    42  	hostname string
    43  
    44  	// endpoint information
    45  	ip  netip.Addr
    46  	udp uint16
    47  	tcp uint16
    48  }
    49  
    50  // New wraps a node record. The record must be valid according to the given
    51  // identity scheme.
    52  func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) {
    53  	if err := r.VerifySignature(validSchemes); err != nil {
    54  		return nil, err
    55  	}
    56  	var id ID
    57  	if n := copy(id[:], validSchemes.NodeAddr(r)); n != len(id) {
    58  		return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(id))
    59  	}
    60  	return newNodeWithID(r, id), nil
    61  }
    62  
    63  func newNodeWithID(r *enr.Record, id ID) *Node {
    64  	n := &Node{r: *r, id: id}
    65  	// Set the preferred endpoint.
    66  	// Here we decide between IPv4 and IPv6, choosing the 'most global' address.
    67  	var ip4 netip.Addr
    68  	var ip6 netip.Addr
    69  	n.Load((*enr.IPv4Addr)(&ip4))
    70  	n.Load((*enr.IPv6Addr)(&ip6))
    71  	valid4 := validIP(ip4)
    72  	valid6 := validIP(ip6)
    73  	switch {
    74  	case valid4 && valid6:
    75  		if localityScore(ip4) >= localityScore(ip6) {
    76  			n.setIP4(ip4)
    77  		} else {
    78  			n.setIP6(ip6)
    79  		}
    80  	case valid4:
    81  		n.setIP4(ip4)
    82  	case valid6:
    83  		n.setIP6(ip6)
    84  	default:
    85  		n.setIPv4Ports()
    86  	}
    87  	return n
    88  }
    89  
    90  // validIP reports whether 'ip' is a valid node endpoint IP address.
    91  func validIP(ip netip.Addr) bool {
    92  	return ip.IsValid() && !ip.IsMulticast()
    93  }
    94  
    95  func localityScore(ip netip.Addr) int {
    96  	switch {
    97  	case ip.IsUnspecified():
    98  		return 0
    99  	case ip.IsLoopback():
   100  		return 1
   101  	case ip.IsLinkLocalUnicast():
   102  		return 2
   103  	case ip.IsPrivate():
   104  		return 3
   105  	default:
   106  		return 4
   107  	}
   108  }
   109  
   110  func (n *Node) setIP4(ip netip.Addr) {
   111  	n.ip = ip
   112  	n.setIPv4Ports()
   113  }
   114  
   115  func (n *Node) setIPv4Ports() {
   116  	n.Load((*enr.UDP)(&n.udp))
   117  	n.Load((*enr.TCP)(&n.tcp))
   118  }
   119  
   120  func (n *Node) setIP6(ip netip.Addr) {
   121  	if ip.Is4In6() {
   122  		n.setIP4(ip)
   123  		return
   124  	}
   125  	n.ip = ip
   126  	if err := n.Load((*enr.UDP6)(&n.udp)); err != nil {
   127  		n.Load((*enr.UDP)(&n.udp))
   128  	}
   129  	if err := n.Load((*enr.TCP6)(&n.tcp)); err != nil {
   130  		n.Load((*enr.TCP)(&n.tcp))
   131  	}
   132  }
   133  
   134  // MustParse parses a node record or enode:// URL. It panics if the input is invalid.
   135  func MustParse(rawurl string) *Node {
   136  	n, err := Parse(ValidSchemes, rawurl)
   137  	if err != nil {
   138  		panic("invalid node: " + err.Error())
   139  	}
   140  	return n
   141  }
   142  
   143  // Parse decodes and verifies a base64-encoded node record.
   144  func Parse(validSchemes enr.IdentityScheme, input string) (*Node, error) {
   145  	if strings.HasPrefix(input, "enode://") {
   146  		return ParseV4(input)
   147  	}
   148  	if !strings.HasPrefix(input, "enr:") {
   149  		return nil, errMissingPrefix
   150  	}
   151  	bin, err := base64.RawURLEncoding.DecodeString(input[4:])
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	var r enr.Record
   156  	if err := rlp.DecodeBytes(bin, &r); err != nil {
   157  		return nil, err
   158  	}
   159  	return New(validSchemes, &r)
   160  }
   161  
   162  // ID returns the node identifier.
   163  func (n *Node) ID() ID {
   164  	return n.id
   165  }
   166  
   167  // Seq returns the sequence number of the underlying record.
   168  func (n *Node) Seq() uint64 {
   169  	return n.r.Seq()
   170  }
   171  
   172  // Load retrieves an entry from the underlying record.
   173  func (n *Node) Load(k enr.Entry) error {
   174  	return n.r.Load(k)
   175  }
   176  
   177  // IP returns the IP address of the node.
   178  func (n *Node) IP() net.IP {
   179  	return net.IP(n.ip.AsSlice())
   180  }
   181  
   182  // IPAddr returns the IP address of the node.
   183  func (n *Node) IPAddr() netip.Addr {
   184  	return n.ip
   185  }
   186  
   187  // UDP returns the UDP port of the node.
   188  func (n *Node) UDP() int {
   189  	return int(n.udp)
   190  }
   191  
   192  // TCP returns the TCP port of the node.
   193  func (n *Node) TCP() int {
   194  	return int(n.tcp)
   195  }
   196  
   197  // WithHostname adds a DNS hostname to the node.
   198  func (n *Node) WithHostname(hostname string) *Node {
   199  	cpy := *n
   200  	cpy.hostname = hostname
   201  	return &cpy
   202  }
   203  
   204  // Hostname returns the DNS name assigned by WithHostname.
   205  func (n *Node) Hostname() string {
   206  	return n.hostname
   207  }
   208  
   209  // UDPEndpoint returns the announced UDP endpoint.
   210  func (n *Node) UDPEndpoint() (netip.AddrPort, bool) {
   211  	if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 {
   212  		return netip.AddrPort{}, false
   213  	}
   214  	return netip.AddrPortFrom(n.ip, n.udp), true
   215  }
   216  
   217  // TCPEndpoint returns the announced TCP endpoint.
   218  func (n *Node) TCPEndpoint() (netip.AddrPort, bool) {
   219  	if !n.ip.IsValid() || n.ip.IsUnspecified() || n.tcp == 0 {
   220  		return netip.AddrPort{}, false
   221  	}
   222  	return netip.AddrPortFrom(n.ip, n.tcp), true
   223  }
   224  
   225  // QUICEndpoint returns the announced QUIC endpoint.
   226  func (n *Node) QUICEndpoint() (netip.AddrPort, bool) {
   227  	var quic uint16
   228  	if n.ip.Is4() || n.ip.Is4In6() {
   229  		n.Load((*enr.QUIC)(&quic))
   230  	} else if n.ip.Is6() {
   231  		n.Load((*enr.QUIC6)(&quic))
   232  	}
   233  	if !n.ip.IsValid() || n.ip.IsUnspecified() || quic == 0 {
   234  		return netip.AddrPort{}, false
   235  	}
   236  	return netip.AddrPortFrom(n.ip, quic), true
   237  }
   238  
   239  // Pubkey returns the secp256k1 public key of the node, if present.
   240  func (n *Node) Pubkey() *ecdsa.PublicKey {
   241  	var key ecdsa.PublicKey
   242  	if n.Load((*Secp256k1)(&key)) != nil {
   243  		return nil
   244  	}
   245  	return &key
   246  }
   247  
   248  // Record returns the node's record. The return value is a copy and may
   249  // be modified by the caller.
   250  func (n *Node) Record() *enr.Record {
   251  	cpy := n.r
   252  	return &cpy
   253  }
   254  
   255  // ValidateComplete checks whether n has a valid IP and UDP port.
   256  // Deprecated: don't use this method.
   257  func (n *Node) ValidateComplete() error {
   258  	if !n.ip.IsValid() {
   259  		return errors.New("missing IP address")
   260  	}
   261  	if n.ip.IsMulticast() || n.ip.IsUnspecified() {
   262  		return errors.New("invalid IP (multicast/unspecified)")
   263  	}
   264  	if n.udp == 0 {
   265  		return errors.New("missing UDP port")
   266  	}
   267  	// Validate the node key (on curve, etc.).
   268  	var key Secp256k1
   269  	return n.Load(&key)
   270  }
   271  
   272  // String returns the text representation of the record.
   273  func (n *Node) String() string {
   274  	if isNewV4(n) {
   275  		return n.URLv4() // backwards-compatibility glue for NewV4 nodes
   276  	}
   277  	enc, _ := rlp.EncodeToBytes(&n.r) // always succeeds because record is valid
   278  	b64 := base64.RawURLEncoding.EncodeToString(enc)
   279  	return "enr:" + b64
   280  }
   281  
   282  // MarshalText implements encoding.TextMarshaler.
   283  func (n *Node) MarshalText() ([]byte, error) {
   284  	return []byte(n.String()), nil
   285  }
   286  
   287  // UnmarshalText implements encoding.TextUnmarshaler.
   288  func (n *Node) UnmarshalText(text []byte) error {
   289  	dec, err := Parse(ValidSchemes, string(text))
   290  	if err == nil {
   291  		*n = *dec
   292  	}
   293  	return err
   294  }
   295  
   296  // ID is a unique identifier for each node.
   297  type ID [32]byte
   298  
   299  // Bytes returns a byte slice representation of the ID
   300  func (n ID) Bytes() []byte {
   301  	return n[:]
   302  }
   303  
   304  // ID prints as a long hexadecimal number.
   305  func (n ID) String() string {
   306  	return fmt.Sprintf("%x", n[:])
   307  }
   308  
   309  // GoString returns the Go syntax representation of a ID is a call to HexID.
   310  func (n ID) GoString() string {
   311  	return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
   312  }
   313  
   314  // TerminalString returns a shortened hex string for terminal logging.
   315  func (n ID) TerminalString() string {
   316  	return hex.EncodeToString(n[:8])
   317  }
   318  
   319  // MarshalText implements the encoding.TextMarshaler interface.
   320  func (n ID) MarshalText() ([]byte, error) {
   321  	return []byte(hex.EncodeToString(n[:])), nil
   322  }
   323  
   324  // UnmarshalText implements the encoding.TextUnmarshaler interface.
   325  func (n *ID) UnmarshalText(text []byte) error {
   326  	id, err := ParseID(string(text))
   327  	if err != nil {
   328  		return err
   329  	}
   330  	*n = id
   331  	return nil
   332  }
   333  
   334  // HexID converts a hex string to an ID.
   335  // The string may be prefixed with 0x.
   336  // It panics if the string is not a valid ID.
   337  func HexID(in string) ID {
   338  	id, err := ParseID(in)
   339  	if err != nil {
   340  		panic(err)
   341  	}
   342  	return id
   343  }
   344  
   345  func ParseID(in string) (ID, error) {
   346  	var id ID
   347  	b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
   348  	if err != nil {
   349  		return id, err
   350  	} else if len(b) != len(id) {
   351  		return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
   352  	}
   353  	copy(id[:], b)
   354  	return id, nil
   355  }
   356  
   357  // DistCmp compares the distances a->target and b->target.
   358  // Returns -1 if a is closer to target, 1 if b is closer to target
   359  // and 0 if they are equal.
   360  func DistCmp(target, a, b ID) int {
   361  	for i := range target {
   362  		da := a[i] ^ target[i]
   363  		db := b[i] ^ target[i]
   364  		if da > db {
   365  			return 1
   366  		} else if da < db {
   367  			return -1
   368  		}
   369  	}
   370  	return 0
   371  }
   372  
   373  // LogDist returns the logarithmic distance between a and b, log2(a ^ b).
   374  func LogDist(a, b ID) int {
   375  	lz := 0
   376  	for i := range a {
   377  		x := a[i] ^ b[i]
   378  		if x == 0 {
   379  			lz += 8
   380  		} else {
   381  			lz += bits.LeadingZeros8(x)
   382  			break
   383  		}
   384  	}
   385  	return len(a)*8 - lz
   386  }