github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/enode/localnode.go (about)

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