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 }