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 }