github.com/core-coin/go-core/v2@v2.1.9/p2p/enode/localnode.go (about)

     1  // Copyright 2018 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package enode
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"reflect"
    23  	"strconv"
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/core-coin/go-core/v2/crypto"
    29  	"github.com/core-coin/go-core/v2/log"
    30  	"github.com/core-coin/go-core/v2/p2p/enr"
    31  	"github.com/core-coin/go-core/v2/p2p/netutil"
    32  )
    33  
    34  const (
    35  	// IP tracker configuration
    36  	iptrackMinStatements = 10
    37  	iptrackWindow        = 5 * time.Minute
    38  	iptrackContactWindow = 10 * time.Minute
    39  )
    40  
    41  // LocalNode produces the signed node record of a local node, i.e. a node run in the
    42  // current process. Setting ENR entries via the Set method updates the record. A new version
    43  // of the record is signed on demand when the Node method is called.
    44  type LocalNode struct {
    45  	cur atomic.Value // holds a non-nil node pointer while the record is up-to-date.
    46  	id  ID
    47  	key *crypto.PrivateKey
    48  	db  *DB
    49  
    50  	// everything below is protected by a lock
    51  	mu        sync.Mutex
    52  	seq       uint64
    53  	entries   map[string]enr.Entry
    54  	endpoint4 lnEndpoint
    55  	endpoint6 lnEndpoint
    56  }
    57  
    58  type lnEndpoint struct {
    59  	track                *netutil.IPTracker
    60  	staticIP, fallbackIP net.IP
    61  	fallbackUDP          int
    62  }
    63  
    64  // NewLocalNode creates a local node.
    65  func NewLocalNode(db *DB, key *crypto.PrivateKey) *LocalNode {
    66  	ln := &LocalNode{
    67  		id:      PubkeyToIDV4(key.PublicKey()),
    68  		db:      db,
    69  		key:     key,
    70  		entries: make(map[string]enr.Entry),
    71  		endpoint4: lnEndpoint{
    72  			track: netutil.NewIPTracker(iptrackWindow, iptrackContactWindow, iptrackMinStatements),
    73  		},
    74  		endpoint6: lnEndpoint{
    75  			track: netutil.NewIPTracker(iptrackWindow, iptrackContactWindow, iptrackMinStatements),
    76  		},
    77  	}
    78  	ln.seq = db.localSeq(ln.id)
    79  	ln.invalidate()
    80  	return ln
    81  }
    82  
    83  // Database returns the node database associated with the local node.
    84  func (ln *LocalNode) Database() *DB {
    85  	return ln.db
    86  }
    87  
    88  // Node returns the current version of the local node record.
    89  func (ln *LocalNode) Node() *Node {
    90  	n := ln.cur.Load().(*Node)
    91  	if n != nil {
    92  		return n
    93  	}
    94  	// Record was invalidated, sign a new copy.
    95  	ln.mu.Lock()
    96  	defer ln.mu.Unlock()
    97  	ln.sign()
    98  	return ln.cur.Load().(*Node)
    99  }
   100  
   101  // Seq returns the current sequence number of the local node record.
   102  func (ln *LocalNode) Seq() uint64 {
   103  	ln.mu.Lock()
   104  	defer ln.mu.Unlock()
   105  
   106  	return ln.seq
   107  }
   108  
   109  // ID returns the local node ID.
   110  func (ln *LocalNode) ID() ID {
   111  	return ln.id
   112  }
   113  
   114  // Set puts the given entry into the local record, overwriting any existing value.
   115  // Use Set*IP and SetFallbackUDP to set IP addresses and UDP port, otherwise they'll
   116  // be overwritten by the endpoint predictor.
   117  func (ln *LocalNode) Set(e enr.Entry) {
   118  	ln.mu.Lock()
   119  	defer ln.mu.Unlock()
   120  
   121  	ln.set(e)
   122  }
   123  
   124  func (ln *LocalNode) set(e enr.Entry) {
   125  	val, exists := ln.entries[e.ENRKey()]
   126  	if !exists || !reflect.DeepEqual(val, e) {
   127  		ln.entries[e.ENRKey()] = e
   128  		ln.invalidate()
   129  	}
   130  }
   131  
   132  // Delete removes the given entry from the local record.
   133  func (ln *LocalNode) Delete(e enr.Entry) {
   134  	ln.mu.Lock()
   135  	defer ln.mu.Unlock()
   136  
   137  	ln.delete(e)
   138  }
   139  
   140  func (ln *LocalNode) delete(e enr.Entry) {
   141  	_, exists := ln.entries[e.ENRKey()]
   142  	if exists {
   143  		delete(ln.entries, e.ENRKey())
   144  		ln.invalidate()
   145  	}
   146  }
   147  
   148  func (ln *LocalNode) endpointForIP(ip net.IP) *lnEndpoint {
   149  	if ip.To4() != nil {
   150  		return &ln.endpoint4
   151  	}
   152  	return &ln.endpoint6
   153  }
   154  
   155  // SetStaticIP sets the local IP to the given one unconditionally.
   156  // This disables endpoint prediction.
   157  func (ln *LocalNode) SetStaticIP(ip net.IP) {
   158  	ln.mu.Lock()
   159  	defer ln.mu.Unlock()
   160  
   161  	ln.endpointForIP(ip).staticIP = ip
   162  	ln.updateEndpoints()
   163  }
   164  
   165  // SetFallbackIP sets the last-resort IP address. This address is used
   166  // if no endpoint prediction can be made and no static IP is set.
   167  func (ln *LocalNode) SetFallbackIP(ip net.IP) {
   168  	ln.mu.Lock()
   169  	defer ln.mu.Unlock()
   170  
   171  	ln.endpointForIP(ip).fallbackIP = ip
   172  	ln.updateEndpoints()
   173  }
   174  
   175  // SetFallbackUDP sets the last-resort UDP-on-IPv4 port. This port is used
   176  // if no endpoint prediction can be made.
   177  func (ln *LocalNode) SetFallbackUDP(port int) {
   178  	ln.mu.Lock()
   179  	defer ln.mu.Unlock()
   180  
   181  	ln.endpoint4.fallbackUDP = port
   182  	ln.endpoint6.fallbackUDP = port
   183  	ln.updateEndpoints()
   184  }
   185  
   186  // UDPEndpointStatement should be called whenever a statement about the local node's
   187  // UDP endpoint is received. It feeds the local endpoint predictor.
   188  func (ln *LocalNode) UDPEndpointStatement(fromaddr, endpoint *net.UDPAddr) {
   189  	ln.mu.Lock()
   190  	defer ln.mu.Unlock()
   191  
   192  	ln.endpointForIP(endpoint.IP).track.AddStatement(fromaddr.String(), endpoint.String())
   193  	ln.updateEndpoints()
   194  }
   195  
   196  // UDPContact should be called whenever the local node has announced itself to another node
   197  // via UDP. It feeds the local endpoint predictor.
   198  func (ln *LocalNode) UDPContact(toaddr *net.UDPAddr) {
   199  	ln.mu.Lock()
   200  	defer ln.mu.Unlock()
   201  
   202  	ln.endpointForIP(toaddr.IP).track.AddContact(toaddr.String())
   203  	ln.updateEndpoints()
   204  }
   205  
   206  // updateEndpoints updates the record with predicted endpoints.
   207  func (ln *LocalNode) updateEndpoints() {
   208  	ip4, udp4 := ln.endpoint4.get()
   209  	ip6, udp6 := ln.endpoint6.get()
   210  
   211  	if ip4 != nil && !ip4.IsUnspecified() {
   212  		ln.set(enr.IPv4(ip4))
   213  	} else {
   214  		ln.delete(enr.IPv4{})
   215  	}
   216  	if ip6 != nil && !ip6.IsUnspecified() {
   217  		ln.set(enr.IPv6(ip6))
   218  	} else {
   219  		ln.delete(enr.IPv6{})
   220  	}
   221  	if udp4 != 0 {
   222  		ln.set(enr.UDP(udp4))
   223  	} else {
   224  		ln.delete(enr.UDP(0))
   225  	}
   226  	if udp6 != 0 && udp6 != udp4 {
   227  		ln.set(enr.UDP6(udp6))
   228  	} else {
   229  		ln.delete(enr.UDP6(0))
   230  	}
   231  }
   232  
   233  // get returns the endpoint with highest precedence.
   234  func (e *lnEndpoint) get() (newIP net.IP, newPort int) {
   235  	newPort = e.fallbackUDP
   236  	if e.fallbackIP != nil {
   237  		newIP = e.fallbackIP
   238  	}
   239  	if e.staticIP != nil {
   240  		newIP = e.staticIP
   241  	} else if ip, port := predictAddr(e.track); ip != nil {
   242  		newIP = ip
   243  		newPort = port
   244  	}
   245  	return newIP, newPort
   246  }
   247  
   248  // predictAddr wraps IPTracker.PredictEndpoint, converting from its string-based
   249  // endpoint representation to IP and port types.
   250  func predictAddr(t *netutil.IPTracker) (net.IP, int) {
   251  	ep := t.PredictEndpoint()
   252  	if ep == "" {
   253  		return nil, 0
   254  	}
   255  	ipString, portString, _ := net.SplitHostPort(ep)
   256  	ip := net.ParseIP(ipString)
   257  	port, _ := strconv.Atoi(portString)
   258  	return ip, port
   259  }
   260  
   261  func (ln *LocalNode) invalidate() {
   262  	ln.cur.Store((*Node)(nil))
   263  }
   264  
   265  func (ln *LocalNode) sign() {
   266  	if n := ln.cur.Load().(*Node); n != nil {
   267  		return // no changes
   268  	}
   269  
   270  	var r enr.Record
   271  	for _, e := range ln.entries {
   272  		r.Set(e)
   273  	}
   274  	ln.bumpSeq()
   275  	r.SetSeq(ln.seq)
   276  	if err := SignV4(&r, ln.key); err != nil {
   277  		panic(fmt.Errorf("enode: can't sign record: %v", err))
   278  	}
   279  	n, err := New(ValidSchemes, &r)
   280  	if err != nil {
   281  		panic(fmt.Errorf("enode: can't verify local record: %v", err))
   282  	}
   283  	ln.cur.Store(n)
   284  	log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IP(), "udp", n.UDP(), "tcp", n.TCP())
   285  }
   286  
   287  func (ln *LocalNode) bumpSeq() {
   288  	ln.seq++
   289  	ln.db.storeLocalSeq(ln.id, ln.seq)
   290  }