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 }