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 }