123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- // Copyright 2018 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package enode
- import (
- "crypto/ecdsa"
- "encoding/base64"
- "encoding/hex"
- "errors"
- "fmt"
- "math/bits"
- "net"
- "strings"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/rlp"
- )
- var errMissingPrefix = errors.New("missing 'enr:' prefix for base64-encoded record")
- // Node represents a host on the network.
- type Node struct {
- r enr.Record
- id ID
- }
- // New wraps a node record. The record must be valid according to the given
- // identity scheme.
- func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) {
- if err := r.VerifySignature(validSchemes); err != nil {
- return nil, err
- }
- node := &Node{r: *r}
- if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) {
- return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{}))
- }
- return node, nil
- }
- // MustParse parses a node record or enode:// URL. It panics if the input is invalid.
- func MustParse(rawurl string) *Node {
- n, err := Parse(ValidSchemes, rawurl)
- if err != nil {
- panic("invalid node: " + err.Error())
- }
- return n
- }
- // Parse decodes and verifies a base64-encoded node record.
- func Parse(validSchemes enr.IdentityScheme, input string) (*Node, error) {
- if strings.HasPrefix(input, "enode://") {
- return ParseV4(input)
- }
- if !strings.HasPrefix(input, "enr:") {
- return nil, errMissingPrefix
- }
- bin, err := base64.RawURLEncoding.DecodeString(input[4:])
- if err != nil {
- return nil, err
- }
- var r enr.Record
- if err := rlp.DecodeBytes(bin, &r); err != nil {
- return nil, err
- }
- return New(validSchemes, &r)
- }
- // ID returns the node identifier.
- func (n *Node) ID() ID {
- return n.id
- }
- // Seq returns the sequence number of the underlying record.
- func (n *Node) Seq() uint64 {
- return n.r.Seq()
- }
- // Quorum
- // Incomplete returns true for nodes with no IP address and no hostname if with raftport.
- func (n *Node) Incomplete() bool {
- return n.IP() == nil && (!n.HasRaftPort() || (n.Host() == "" && n.HasRaftPort()))
- }
- // Load retrieves an entry from the underlying record.
- func (n *Node) Load(k enr.Entry) error {
- return n.r.Load(k)
- }
- // IP returns the IP address of the node.
- //
- // Quorum
- // To support DNS lookup in node ip. The function performs hostname lookup if hostname is defined in enr.Hostname
- // and falls back to enr.IP value in case of failure. It also makes sure the resolved IP is in IPv4 or IPv6 format
- func (n *Node) IP() net.IP {
- if n.Host() == "" {
- // no host is set, so use the IP directly
- return n.loadIP()
- }
- // attempt to look up IP addresses if host is a FQDN
- lookupIPs, err := net.LookupIP(n.Host())
- if err != nil {
- return n.loadIP()
- }
- // set to first ip by default & as Ethereum upstream
- ip := lookupIPs[0]
- // Ensure the IP is 4 bytes long for IPv4 addresses.
- if ipv4 := ip.To4(); ipv4 != nil {
- ip = ipv4
- }
- return ip
- }
- func (n *Node) loadIP() net.IP {
- var (
- ip4 enr.IPv4
- ip6 enr.IPv6
- )
- if n.Load(&ip4) == nil {
- return net.IP(ip4)
- }
- if n.Load(&ip6) == nil {
- return net.IP(ip6)
- }
- return nil
- }
- // Quorum
- func (n *Node) Host() string {
- var hostname string
- n.Load((*enr.Hostname)(&hostname))
- return hostname
- }
- // End-Quorum
- // UDP returns the UDP port of the node.
- func (n *Node) UDP() int {
- var port enr.UDP
- n.Load(&port)
- return int(port)
- }
- // used by Quorum RAFT - returns the Raft port of the node
- func (n *Node) RaftPort() int {
- var port enr.RaftPort
- err := n.Load(&port)
- if err != nil {
- return 0
- }
- return int(port)
- }
- func (n *Node) HasRaftPort() bool {
- return n.RaftPort() > 0
- }
- // UDP returns the TCP port of the node.
- func (n *Node) TCP() int {
- var port enr.TCP
- n.Load(&port)
- return int(port)
- }
- // Pubkey returns the secp256k1 public key of the node, if present.
- func (n *Node) Pubkey() *ecdsa.PublicKey {
- var key ecdsa.PublicKey
- if n.Load((*Secp256k1)(&key)) != nil {
- return nil
- }
- return &key
- }
- // Record returns the node's record. The return value is a copy and may
- // be modified by the caller.
- func (n *Node) Record() *enr.Record {
- cpy := n.r
- return &cpy
- }
- // ValidateComplete checks whether n has a valid IP and UDP port.
- // Deprecated: don't use this method.
- func (n *Node) ValidateComplete() error {
- if n.Incomplete() {
- return errors.New("missing IP address")
- }
- if n.UDP() == 0 {
- return errors.New("missing UDP port")
- }
- ip := n.IP()
- if ip.IsMulticast() || ip.IsUnspecified() {
- return errors.New("invalid IP (multicast/unspecified)")
- }
- // Validate the node key (on curve, etc.).
- var key Secp256k1
- return n.Load(&key)
- }
- // String returns the text representation of the record.
- func (n *Node) String() string {
- if isNewV4(n) {
- return n.URLv4() // backwards-compatibility glue for NewV4 nodes
- }
- enc, _ := rlp.EncodeToBytes(&n.r) // always succeeds because record is valid
- b64 := base64.RawURLEncoding.EncodeToString(enc)
- return "enr:" + b64
- }
- // MarshalText implements encoding.TextMarshaler.
- func (n *Node) MarshalText() ([]byte, error) {
- return []byte(n.String()), nil
- }
- // UnmarshalText implements encoding.TextUnmarshaler.
- func (n *Node) UnmarshalText(text []byte) error {
- dec, err := Parse(ValidSchemes, string(text))
- if err == nil {
- *n = *dec
- }
- return err
- }
- // ID is a unique identifier for each node.
- type ID [32]byte
- // Bytes returns a byte slice representation of the ID
- func (n ID) Bytes() []byte {
- return n[:]
- }
- // ID prints as a long hexadecimal number.
- func (n ID) String() string {
- return fmt.Sprintf("%x", n[:])
- }
- // The Go syntax representation of a ID is a call to HexID.
- func (n ID) GoString() string {
- return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
- }
- // TerminalString returns a shortened hex string for terminal logging.
- func (n ID) TerminalString() string {
- return hex.EncodeToString(n[:8])
- }
- // MarshalText implements the encoding.TextMarshaler interface.
- func (n ID) MarshalText() ([]byte, error) {
- return []byte(hex.EncodeToString(n[:])), nil
- }
- // UnmarshalText implements the encoding.TextUnmarshaler interface.
- func (n *ID) UnmarshalText(text []byte) error {
- id, err := ParseID(string(text))
- if err != nil {
- return err
- }
- *n = id
- return nil
- }
- // ID is a unique identifier for each node used by RAFT
- type EnodeID [64]byte
- // ID prints as a long hexadecimal number.
- func (n EnodeID) String() string {
- return fmt.Sprintf("%x", n[:])
- }
- // The Go syntax representation of a ID is a call to HexID.
- func (n EnodeID) GoString() string {
- return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
- }
- // MarshalText implements the encoding.TextMarshaler interface.
- func (n EnodeID) MarshalText() ([]byte, error) {
- return []byte(hex.EncodeToString(n[:])), nil
- }
- // UnmarshalText implements the encoding.TextUnmarshaler interface.
- func (n *EnodeID) UnmarshalText(text []byte) error {
- id, err := RaftHexID(string(text))
- if err != nil {
- return err
- }
- *n = id
- return nil
- }
- // HexID converts a hex string to an ID.
- // The string may be prefixed with 0x.
- // It panics if the string is not a valid ID.
- func HexID(in string) ID {
- id, err := ParseID(in)
- if err != nil {
- panic(err)
- }
- return id
- }
- // used by Quorum RAFT to derive 64 byte nodeId from 128 byte enodeID
- func RaftHexID(in string) (EnodeID, error) {
- var id EnodeID
- b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
- if err != nil {
- return id, err
- } else if len(b) != len(id) {
- return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
- }
- copy(id[:], b)
- return id, nil
- }
- func ParseID(in string) (ID, error) {
- var id ID
- b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
- if err != nil {
- return id, err
- } else if len(b) != len(id) {
- return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
- }
- copy(id[:], b)
- return id, nil
- }
- // DistCmp compares the distances a->target and b->target.
- // Returns -1 if a is closer to target, 1 if b is closer to target
- // and 0 if they are equal.
- func DistCmp(target, a, b ID) int {
- for i := range target {
- da := a[i] ^ target[i]
- db := b[i] ^ target[i]
- if da > db {
- return 1
- } else if da < db {
- return -1
- }
- }
- return 0
- }
- // LogDist returns the logarithmic distance between a and b, log2(a ^ b).
- func LogDist(a, b ID) int {
- lz := 0
- for i := range a {
- x := a[i] ^ b[i]
- if x == 0 {
- lz += 8
- } else {
- lz += bits.LeadingZeros8(x)
- break
- }
- }
- return len(a)*8 - lz
- }
|