github.com/annchain/OG@v0.0.9/p2p/onode/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 onode
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"github.com/annchain/OG/p2p/enr"
    23  	"github.com/annchain/OG/p2p/netutil"
    24  	"github.com/sirupsen/logrus"
    25  	"net"
    26  	"reflect"
    27  	"strconv"
    28  	"sync"
    29  	"sync/atomic"
    30  	"time"
    31  )
    32  
    33  const (
    34  	// IP tracker configuration
    35  	iptrackMinStatements = 10
    36  	iptrackWindow        = 5 * time.Minute
    37  	iptrackContactWindow = 10 * time.Minute
    38  )
    39  
    40  // LocalNode produces the signed node record of a local node, i.e. a node run in the
    41  // current process. Setting ENR entries via the Set method updates the record. A new version
    42  // of the record is signed on demand when the Node method is called.
    43  type LocalNode struct {
    44  	cur atomic.Value // holds a non-nil node pointer while the record is up-to-date.
    45  	id  ID
    46  	key *ecdsa.PrivateKey
    47  	db  *DB
    48  
    49  	// everything below is protected by a lock
    50  	mu          sync.Mutex
    51  	seq         uint64
    52  	entries     map[string]enr.Entry
    53  	udpTrack    *netutil.IPTracker // predicts external UDP endpoint
    54  	staticIP    net.IP
    55  	fallbackIP  net.IP
    56  	fallbackUDP int
    57  }
    58  
    59  // NewLocalNode creates a local node.
    60  func NewLocalNode(db *DB, key *ecdsa.PrivateKey) *LocalNode {
    61  	ln := &LocalNode{
    62  		id:       PubkeyToIDV4(&key.PublicKey),
    63  		db:       db,
    64  		key:      key,
    65  		udpTrack: netutil.NewIPTracker(iptrackWindow, iptrackContactWindow, iptrackMinStatements),
    66  		entries:  make(map[string]enr.Entry),
    67  	}
    68  	ln.seq = db.localSeq(ln.id)
    69  	ln.invalidate()
    70  	return ln
    71  }
    72  
    73  // Database returns the node database associated with the local node.
    74  func (ln *LocalNode) Database() *DB {
    75  	return ln.db
    76  }
    77  
    78  // Node returns the current version of the local node record.
    79  func (ln *LocalNode) Node() *Node {
    80  	n := ln.cur.Load().(*Node)
    81  	if n != nil {
    82  		return n
    83  	}
    84  	// Record was invalidated, sign a new copy.
    85  	ln.mu.Lock()
    86  	defer ln.mu.Unlock()
    87  	ln.sign()
    88  	return ln.cur.Load().(*Node)
    89  }
    90  
    91  // ID returns the local node ID.
    92  func (ln *LocalNode) ID() ID {
    93  	return ln.id
    94  }
    95  
    96  // Set puts the given entry into the local record, overwriting
    97  // any existing value.
    98  func (ln *LocalNode) Set(e enr.Entry) {
    99  	ln.mu.Lock()
   100  	defer ln.mu.Unlock()
   101  
   102  	ln.set(e)
   103  }
   104  
   105  func (ln *LocalNode) set(e enr.Entry) {
   106  	val, exists := ln.entries[e.ENRKey()]
   107  	if !exists || !reflect.DeepEqual(val, e) {
   108  		ln.entries[e.ENRKey()] = e
   109  		ln.invalidate()
   110  	}
   111  }
   112  
   113  // Delete removes the given entry from the local record.
   114  func (ln *LocalNode) Delete(e enr.Entry) {
   115  	ln.mu.Lock()
   116  	defer ln.mu.Unlock()
   117  
   118  	ln.delete(e)
   119  }
   120  
   121  func (ln *LocalNode) delete(e enr.Entry) {
   122  	_, exists := ln.entries[e.ENRKey()]
   123  	if exists {
   124  		delete(ln.entries, e.ENRKey())
   125  		ln.invalidate()
   126  	}
   127  }
   128  
   129  // SetStaticIP sets the local IP to the given one unconditionally.
   130  // This disables endpoint prediction.
   131  func (ln *LocalNode) SetStaticIP(ip net.IP) {
   132  	ln.mu.Lock()
   133  	defer ln.mu.Unlock()
   134  
   135  	ln.staticIP = ip
   136  	ln.updateEndpoints()
   137  }
   138  
   139  // SetFallbackIP sets the last-resort IP address. This address is used
   140  // if no endpoint prediction can be made and no static IP is set.
   141  func (ln *LocalNode) SetFallbackIP(ip net.IP) {
   142  	ln.mu.Lock()
   143  	defer ln.mu.Unlock()
   144  
   145  	ln.fallbackIP = ip
   146  	ln.updateEndpoints()
   147  }
   148  
   149  // SetFallbackUDP sets the last-resort UDP port. This port is used
   150  // if no endpoint prediction can be made.
   151  func (ln *LocalNode) SetFallbackUDP(port int) {
   152  	ln.mu.Lock()
   153  	defer ln.mu.Unlock()
   154  
   155  	ln.fallbackUDP = port
   156  	ln.updateEndpoints()
   157  }
   158  
   159  // UDPEndpointStatement should be called whenever a statement about the local node's
   160  // UDP endpoint is received. It feeds the local endpoint predictor.
   161  func (ln *LocalNode) UDPEndpointStatement(fromaddr, endpoint *net.UDPAddr) {
   162  	ln.mu.Lock()
   163  	defer ln.mu.Unlock()
   164  
   165  	ln.udpTrack.AddStatement(fromaddr.String(), endpoint.String())
   166  	ln.updateEndpoints()
   167  }
   168  
   169  // UDPContact should be called whenever the local node has announced itself to another node
   170  // via UDP. It feeds the local endpoint predictor.
   171  func (ln *LocalNode) UDPContact(toaddr *net.UDPAddr) {
   172  	ln.mu.Lock()
   173  	defer ln.mu.Unlock()
   174  
   175  	ln.udpTrack.AddContact(toaddr.String())
   176  	ln.updateEndpoints()
   177  }
   178  
   179  func (ln *LocalNode) updateEndpoints() {
   180  	// Determine the endpoints.
   181  	newIP := ln.fallbackIP
   182  	newUDP := ln.fallbackUDP
   183  	if ln.staticIP != nil {
   184  		newIP = ln.staticIP
   185  	} else if ip, port := predictAddr(ln.udpTrack); ip != nil {
   186  		newIP = ip
   187  		newUDP = port
   188  	}
   189  
   190  	// Update the record.
   191  	if newIP != nil && !newIP.IsUnspecified() {
   192  		nIp := enr.IP(newIP)
   193  		ln.set(&nIp)
   194  		if newUDP != 0 {
   195  			nU := enr.UDP(newUDP)
   196  			ln.set(&nU)
   197  		} else {
   198  			u := enr.UDP(0)
   199  			ln.delete(&u)
   200  		}
   201  	} else {
   202  		i := enr.IP{}
   203  		ln.delete(&i)
   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("onode: can't sign record: %v", err))
   237  	}
   238  	n, err := New(ValidSchemes, &r)
   239  	if err != nil {
   240  		panic(fmt.Errorf("onode: can't verify local record: %v", err))
   241  	}
   242  	ln.cur.Store(n)
   243  	log.WithFields(logrus.Fields{
   244  		"seq": ln.seq, "id": n.ID(),
   245  		"ip":  n.IP(),
   246  		"udp": n.UDP(),
   247  		"tcp": n.TCP(),
   248  	}).Info("New local node record")
   249  }
   250  
   251  func (ln *LocalNode) bumpSeq() {
   252  	ln.seq++
   253  	ln.db.storeLocalSeq(ln.id, ln.seq)
   254  }