github.com/letterj/go-ethereum@v1.8.22-0.20190204142846-520024dfd689/les/server.go (about) 1 // Copyright 2016 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 les implements the Light Ethereum Subprotocol. 18 package les 19 20 import ( 21 "crypto/ecdsa" 22 "encoding/binary" 23 "math" 24 "sync" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/eth" 31 "github.com/ethereum/go-ethereum/ethdb" 32 "github.com/ethereum/go-ethereum/les/flowcontrol" 33 "github.com/ethereum/go-ethereum/light" 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/ethereum/go-ethereum/p2p" 36 "github.com/ethereum/go-ethereum/p2p/discv5" 37 "github.com/ethereum/go-ethereum/params" 38 "github.com/ethereum/go-ethereum/rlp" 39 ) 40 41 type LesServer struct { 42 lesCommons 43 44 fcManager *flowcontrol.ClientManager // nil if our node is client only 45 fcCostStats *requestCostStats 46 defParams *flowcontrol.ServerParams 47 lesTopics []discv5.Topic 48 privateKey *ecdsa.PrivateKey 49 quitSync chan struct{} 50 onlyAnnounce bool 51 } 52 53 func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { 54 quitSync := make(chan struct{}) 55 pm, err := NewProtocolManager( 56 eth.BlockChain().Config(), 57 light.DefaultServerIndexerConfig, 58 false, 59 config.NetworkId, 60 eth.EventMux(), 61 eth.Engine(), 62 newPeerSet(), 63 eth.BlockChain(), 64 eth.TxPool(), 65 eth.ChainDb(), 66 nil, 67 nil, 68 nil, 69 quitSync, 70 new(sync.WaitGroup), 71 config.ULC) 72 if err != nil { 73 return nil, err 74 } 75 76 lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) 77 for i, pv := range AdvertiseProtocolVersions { 78 lesTopics[i] = lesTopic(eth.BlockChain().Genesis().Hash(), pv) 79 } 80 81 srv := &LesServer{ 82 lesCommons: lesCommons{ 83 config: config, 84 chainDb: eth.ChainDb(), 85 iConfig: light.DefaultServerIndexerConfig, 86 chtIndexer: light.NewChtIndexer(eth.ChainDb(), nil, params.CHTFrequencyServer, params.HelperTrieProcessConfirmations), 87 bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency), 88 protocolManager: pm, 89 }, 90 quitSync: quitSync, 91 lesTopics: lesTopics, 92 onlyAnnounce: config.OnlyAnnounce, 93 } 94 95 logger := log.New() 96 97 chtV1SectionCount, _, _ := srv.chtIndexer.Sections() // indexer still uses LES/1 4k section size for backwards server compatibility 98 chtV2SectionCount := chtV1SectionCount / (params.CHTFrequencyClient / params.CHTFrequencyServer) 99 if chtV2SectionCount != 0 { 100 // convert to LES/2 section 101 chtLastSection := chtV2SectionCount - 1 102 // convert last LES/2 section index back to LES/1 index for chtIndexer.SectionHead 103 chtLastSectionV1 := (chtLastSection+1)*(params.CHTFrequencyClient/params.CHTFrequencyServer) - 1 104 chtSectionHead := srv.chtIndexer.SectionHead(chtLastSectionV1) 105 chtRoot := light.GetChtRoot(pm.chainDb, chtLastSectionV1, chtSectionHead) 106 logger.Info("Loaded CHT", "section", chtLastSection, "head", chtSectionHead, "root", chtRoot) 107 } 108 bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections() 109 if bloomTrieSectionCount != 0 { 110 bloomTrieLastSection := bloomTrieSectionCount - 1 111 bloomTrieSectionHead := srv.bloomTrieIndexer.SectionHead(bloomTrieLastSection) 112 bloomTrieRoot := light.GetBloomTrieRoot(pm.chainDb, bloomTrieLastSection, bloomTrieSectionHead) 113 logger.Info("Loaded bloom trie", "section", bloomTrieLastSection, "head", bloomTrieSectionHead, "root", bloomTrieRoot) 114 } 115 116 srv.chtIndexer.Start(eth.BlockChain()) 117 pm.server = srv 118 119 srv.defParams = &flowcontrol.ServerParams{ 120 BufLimit: 300000000, 121 MinRecharge: 50000, 122 } 123 srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000) 124 srv.fcCostStats = newCostStats(eth.ChainDb()) 125 return srv, nil 126 } 127 128 func (s *LesServer) Protocols() []p2p.Protocol { 129 return s.makeProtocols(ServerProtocolVersions) 130 } 131 132 // Start starts the LES server 133 func (s *LesServer) Start(srvr *p2p.Server) { 134 s.protocolManager.Start(s.config.LightPeers) 135 if srvr.DiscV5 != nil { 136 for _, topic := range s.lesTopics { 137 topic := topic 138 go func() { 139 logger := log.New("topic", topic) 140 logger.Info("Starting topic registration") 141 defer logger.Info("Terminated topic registration") 142 143 srvr.DiscV5.RegisterTopic(topic, s.quitSync) 144 }() 145 } 146 } 147 s.privateKey = srvr.PrivateKey 148 s.protocolManager.blockLoop() 149 } 150 151 func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) { 152 bloomIndexer.AddChildIndexer(s.bloomTrieIndexer) 153 } 154 155 // Stop stops the LES service 156 func (s *LesServer) Stop() { 157 s.chtIndexer.Close() 158 // bloom trie indexer is closed by parent bloombits indexer 159 s.fcCostStats.store() 160 s.fcManager.Stop() 161 go func() { 162 <-s.protocolManager.noMorePeers 163 }() 164 s.protocolManager.Stop() 165 } 166 167 type requestCosts struct { 168 baseCost, reqCost uint64 169 } 170 171 type requestCostTable map[uint64]*requestCosts 172 173 type RequestCostList []struct { 174 MsgCode, BaseCost, ReqCost uint64 175 } 176 177 func (list RequestCostList) decode() requestCostTable { 178 table := make(requestCostTable) 179 for _, e := range list { 180 table[e.MsgCode] = &requestCosts{ 181 baseCost: e.BaseCost, 182 reqCost: e.ReqCost, 183 } 184 } 185 return table 186 } 187 188 type linReg struct { 189 sumX, sumY, sumXX, sumXY float64 190 cnt uint64 191 } 192 193 const linRegMaxCnt = 100000 194 195 func (l *linReg) add(x, y float64) { 196 if l.cnt >= linRegMaxCnt { 197 sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt 198 l.sumX -= l.sumX * sub 199 l.sumY -= l.sumY * sub 200 l.sumXX -= l.sumXX * sub 201 l.sumXY -= l.sumXY * sub 202 l.cnt = linRegMaxCnt - 1 203 } 204 l.cnt++ 205 l.sumX += x 206 l.sumY += y 207 l.sumXX += x * x 208 l.sumXY += x * y 209 } 210 211 func (l *linReg) calc() (b, m float64) { 212 if l.cnt == 0 { 213 return 0, 0 214 } 215 cnt := float64(l.cnt) 216 d := cnt*l.sumXX - l.sumX*l.sumX 217 if d < 0.001 { 218 return l.sumY / cnt, 0 219 } 220 m = (cnt*l.sumXY - l.sumX*l.sumY) / d 221 b = (l.sumY / cnt) - (m * l.sumX / cnt) 222 return b, m 223 } 224 225 func (l *linReg) toBytes() []byte { 226 var arr [40]byte 227 binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX)) 228 binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY)) 229 binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX)) 230 binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY)) 231 binary.BigEndian.PutUint64(arr[32:40], l.cnt) 232 return arr[:] 233 } 234 235 func linRegFromBytes(data []byte) *linReg { 236 if len(data) != 40 { 237 return nil 238 } 239 l := &linReg{} 240 l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8])) 241 l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16])) 242 l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24])) 243 l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32])) 244 l.cnt = binary.BigEndian.Uint64(data[32:40]) 245 return l 246 } 247 248 type requestCostStats struct { 249 lock sync.RWMutex 250 db ethdb.Database 251 stats map[uint64]*linReg 252 } 253 254 type requestCostStatsRlp []struct { 255 MsgCode uint64 256 Data []byte 257 } 258 259 var rcStatsKey = []byte("_requestCostStats") 260 261 func newCostStats(db ethdb.Database) *requestCostStats { 262 stats := make(map[uint64]*linReg) 263 for _, code := range reqList { 264 stats[code] = &linReg{cnt: 100} 265 } 266 267 if db != nil { 268 data, err := db.Get(rcStatsKey) 269 var statsRlp requestCostStatsRlp 270 if err == nil { 271 err = rlp.DecodeBytes(data, &statsRlp) 272 } 273 if err == nil { 274 for _, r := range statsRlp { 275 if stats[r.MsgCode] != nil { 276 if l := linRegFromBytes(r.Data); l != nil { 277 stats[r.MsgCode] = l 278 } 279 } 280 } 281 } 282 } 283 284 return &requestCostStats{ 285 db: db, 286 stats: stats, 287 } 288 } 289 290 func (s *requestCostStats) store() { 291 s.lock.Lock() 292 defer s.lock.Unlock() 293 294 statsRlp := make(requestCostStatsRlp, len(reqList)) 295 for i, code := range reqList { 296 statsRlp[i].MsgCode = code 297 statsRlp[i].Data = s.stats[code].toBytes() 298 } 299 300 if data, err := rlp.EncodeToBytes(statsRlp); err == nil { 301 s.db.Put(rcStatsKey, data) 302 } 303 } 304 305 func (s *requestCostStats) getCurrentList() RequestCostList { 306 s.lock.Lock() 307 defer s.lock.Unlock() 308 309 list := make(RequestCostList, len(reqList)) 310 for idx, code := range reqList { 311 b, m := s.stats[code].calc() 312 if m < 0 { 313 b += m 314 m = 0 315 } 316 if b < 0 { 317 b = 0 318 } 319 320 list[idx].MsgCode = code 321 list[idx].BaseCost = uint64(b * 2) 322 list[idx].ReqCost = uint64(m * 2) 323 } 324 return list 325 } 326 327 func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) { 328 s.lock.Lock() 329 defer s.lock.Unlock() 330 331 c, ok := s.stats[msgCode] 332 if !ok || reqCnt == 0 { 333 return 334 } 335 c.add(float64(reqCnt), float64(cost)) 336 } 337 338 func (pm *ProtocolManager) blockLoop() { 339 pm.wg.Add(1) 340 headCh := make(chan core.ChainHeadEvent, 10) 341 headSub := pm.blockchain.SubscribeChainHeadEvent(headCh) 342 go func() { 343 var lastHead *types.Header 344 lastBroadcastTd := common.Big0 345 for { 346 select { 347 case ev := <-headCh: 348 peers := pm.peers.AllPeers() 349 if len(peers) > 0 { 350 header := ev.Block.Header() 351 hash := header.Hash() 352 number := header.Number.Uint64() 353 td := rawdb.ReadTd(pm.chainDb, hash, number) 354 if td != nil && td.Cmp(lastBroadcastTd) > 0 { 355 var reorg uint64 356 if lastHead != nil { 357 reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() 358 } 359 lastHead = header 360 lastBroadcastTd = td 361 362 log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) 363 364 announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} 365 var ( 366 signed bool 367 signedAnnounce announceData 368 ) 369 370 for _, p := range peers { 371 switch p.announceType { 372 373 case announceTypeSimple: 374 select { 375 case p.announceChn <- announce: 376 default: 377 pm.removePeer(p.id) 378 } 379 380 case announceTypeSigned: 381 if !signed { 382 signedAnnounce = announce 383 signedAnnounce.sign(pm.server.privateKey) 384 signed = true 385 } 386 387 select { 388 case p.announceChn <- signedAnnounce: 389 default: 390 pm.removePeer(p.id) 391 } 392 } 393 } 394 } 395 } 396 case <-pm.quitSync: 397 headSub.Unsubscribe() 398 pm.wg.Done() 399 return 400 } 401 } 402 }() 403 }