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