github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/p2p/enode/localnode.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  	"fmt"
    22  	"net"
    23  	"reflect"
    24  	"strconv"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/ethereum/go-ethereum/log"
    30  	"github.com/ethereum/go-ethereum/p2p/enr"
    31  	"github.com/ethereum/go-ethereum/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 *ecdsa.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  	udpTrack    *netutil.IPTracker // predicts external UDP endpoint
    55  	staticIP    net.IP
    56  	fallbackIP  net.IP
    57  	fallbackUDP int
    58  
    59  	networkId uint64
    60  }
    61  
    62  // NewLocalNode creates a local node.
    63  func NewLocalNode(db *DB, key *ecdsa.PrivateKey, networkId uint64) *LocalNode {
    64  	ln := &LocalNode{
    65  		id:        PubkeyToIDV4(&key.PublicKey),
    66  		db:        db,
    67  		key:       key,
    68  		udpTrack:  netutil.NewIPTracker(iptrackWindow, iptrackContactWindow, iptrackMinStatements),
    69  		entries:   make(map[string]enr.Entry),
    70  		networkId: networkId,
    71  	}
    72  	ln.seq = db.localSeq(ln.id)
    73  	ln.invalidate()
    74  	return ln
    75  }
    76  
    77  // Database returns the node database associated with the local node.
    78  func (ln *LocalNode) Database() *DB {
    79  	return ln.db
    80  }
    81  
    82  // Node returns the current version of the local node record.
    83  func (ln *LocalNode) Node() *Node {
    84  	n := ln.cur.Load().(*Node)
    85  	if n != nil {
    86  		return n
    87  	}
    88  	// Record was invalidated, sign a new copy.
    89  	ln.mu.Lock()
    90  	defer ln.mu.Unlock()
    91  	ln.sign()
    92  	return ln.cur.Load().(*Node)
    93  }
    94  
    95  // ID returns the local node ID.
    96  func (ln *LocalNode) ID() ID {
    97  	return ln.id
    98  }
    99  
   100  // Set puts the given entry into the local record, overwriting
   101  // any existing value.
   102  func (ln *LocalNode) Set(e enr.Entry) {
   103  	ln.mu.Lock()
   104  	defer ln.mu.Unlock()
   105  
   106  	ln.set(e)
   107  }
   108  
   109  func (ln *LocalNode) set(e enr.Entry) {
   110  	val, exists := ln.entries[e.ENRKey()]
   111  	if !exists || !reflect.DeepEqual(val, e) {
   112  		ln.entries[e.ENRKey()] = e
   113  		ln.invalidate()
   114  	}
   115  }
   116  
   117  // Delete removes the given entry from the local record.
   118  func (ln *LocalNode) Delete(e enr.Entry) {
   119  	ln.mu.Lock()
   120  	defer ln.mu.Unlock()
   121  
   122  	ln.delete(e)
   123  }
   124  
   125  func (ln *LocalNode) delete(e enr.Entry) {
   126  	_, exists := ln.entries[e.ENRKey()]
   127  	if exists {
   128  		delete(ln.entries, e.ENRKey())
   129  		ln.invalidate()
   130  	}
   131  }
   132  
   133  // SetStaticIP sets the local IP to the given one unconditionally.
   134  // This disables endpoint prediction.
   135  func (ln *LocalNode) SetStaticIP(ip net.IP) {
   136  	ln.mu.Lock()
   137  	defer ln.mu.Unlock()
   138  
   139  	ln.staticIP = ip
   140  	ln.updateEndpoints()
   141  }
   142  
   143  // SetFallbackIP sets the last-resort IP address. This address is used
   144  // if no endpoint prediction can be made and no static IP is set.
   145  func (ln *LocalNode) SetFallbackIP(ip net.IP) {
   146  	ln.mu.Lock()
   147  	defer ln.mu.Unlock()
   148  
   149  	ln.fallbackIP = ip
   150  	ln.updateEndpoints()
   151  }
   152  
   153  // SetFallbackUDP sets the last-resort UDP port. This port is used
   154  // if no endpoint prediction can be made.
   155  func (ln *LocalNode) SetFallbackUDP(port int) {
   156  	ln.mu.Lock()
   157  	defer ln.mu.Unlock()
   158  
   159  	ln.fallbackUDP = port
   160  	ln.updateEndpoints()
   161  }
   162  
   163  // UDPEndpointStatement should be called whenever a statement about the local node's
   164  // UDP endpoint is received. It feeds the local endpoint predictor.
   165  func (ln *LocalNode) UDPEndpointStatement(fromaddr, endpoint *net.UDPAddr) {
   166  	ln.mu.Lock()
   167  	defer ln.mu.Unlock()
   168  
   169  	ln.udpTrack.AddStatement(fromaddr.String(), endpoint.String())
   170  	ln.updateEndpoints()
   171  }
   172  
   173  // UDPContact should be called whenever the local node has announced itself to another node
   174  // via UDP. It feeds the local endpoint predictor.
   175  func (ln *LocalNode) UDPContact(toaddr *net.UDPAddr) {
   176  	ln.mu.Lock()
   177  	defer ln.mu.Unlock()
   178  
   179  	ln.udpTrack.AddContact(toaddr.String())
   180  	ln.updateEndpoints()
   181  }
   182  
   183  func (ln *LocalNode) updateEndpoints() {
   184  	// Determine the endpoints.
   185  	newIP := ln.fallbackIP
   186  	newUDP := ln.fallbackUDP
   187  	if ln.staticIP != nil {
   188  		newIP = ln.staticIP
   189  	} else if ip, port := predictAddr(ln.udpTrack); ip != nil {
   190  		newIP = ip
   191  		newUDP = port
   192  	}
   193  
   194  	// Update the record.
   195  	if newIP != nil && !newIP.IsUnspecified() {
   196  		ln.set(enr.IP(newIP))
   197  		if newUDP != 0 {
   198  			ln.set(enr.UDP(newUDP))
   199  		} else {
   200  			ln.delete(enr.UDP(0))
   201  		}
   202  	} else {
   203  		ln.delete(enr.IP{})
   204  	}
   205  }
   206  
   207  // predictAddr wraps IPTracker.PredictEndpoint, converting from its string-based
   208  // endpoint representation to IP and port types.
   209  func predictAddr(t *netutil.IPTracker) (net.IP, int) {
   210  	ep := t.PredictEndpoint()
   211  	if ep == "" {
   212  		return nil, 0
   213  	}
   214  	ipString, portString, _ := net.SplitHostPort(ep)
   215  	ip := net.ParseIP(ipString)
   216  	port, _ := strconv.Atoi(portString)
   217  	return ip, port
   218  }
   219  
   220  func (ln *LocalNode) invalidate() {
   221  	ln.cur.Store((*Node)(nil))
   222  }
   223  
   224  func (ln *LocalNode) sign() {
   225  	if n := ln.cur.Load().(*Node); n != nil {
   226  		return // no changes
   227  	}
   228  
   229  	var r enr.Record
   230  	for _, e := range ln.entries {
   231  		r.Set(e)
   232  	}
   233  	ln.bumpSeq()
   234  	r.SetSeq(ln.seq)
   235  	if err := SignV4(&r, ln.key); err != nil {
   236  		panic(fmt.Errorf("enode: can't sign record: %v", err))
   237  	}
   238  	n, err := New(ValidSchemes, &r)
   239  	if err != nil {
   240  		panic(fmt.Errorf("enode: can't verify local record: %v", err))
   241  	}
   242  	ln.cur.Store(n)
   243  	log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IP(), "udp", n.UDP(), "tcp", n.TCP())
   244  }
   245  
   246  func (ln *LocalNode) bumpSeq() {
   247  	ln.seq++
   248  	ln.db.storeLocalSeq(ln.id, ln.seq)
   249  }
   250  
   251  func (ln *LocalNode) NetworkId() uint64 {
   252  	ln.mu.Lock()
   253  	defer ln.mu.Unlock()
   254  
   255  	return ln.networkId
   256  }