github.com/myafeier/go-ethereum@v1.6.8-0.20170719123245-3e0dbe0eaa72/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 "encoding/binary" 22 "math" 23 "sync" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/eth" 30 "github.com/ethereum/go-ethereum/ethdb" 31 "github.com/ethereum/go-ethereum/les/flowcontrol" 32 "github.com/ethereum/go-ethereum/light" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/p2p" 35 "github.com/ethereum/go-ethereum/p2p/discv5" 36 "github.com/ethereum/go-ethereum/rlp" 37 "github.com/ethereum/go-ethereum/trie" 38 ) 39 40 type LesServer struct { 41 protocolManager *ProtocolManager 42 fcManager *flowcontrol.ClientManager // nil if our node is client only 43 fcCostStats *requestCostStats 44 defParams *flowcontrol.ServerParams 45 lesTopic discv5.Topic 46 quitSync chan struct{} 47 stopped bool 48 } 49 50 func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { 51 quitSync := make(chan struct{}) 52 pm, err := NewProtocolManager(eth.BlockChain().Config(), false, config.NetworkId, eth.EventMux(), eth.Engine(), newPeerSet(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil, quitSync, new(sync.WaitGroup)) 53 if err != nil { 54 return nil, err 55 } 56 pm.blockLoop() 57 58 srv := &LesServer{ 59 protocolManager: pm, 60 quitSync: quitSync, 61 lesTopic: lesTopic(eth.BlockChain().Genesis().Hash()), 62 } 63 pm.server = srv 64 65 srv.defParams = &flowcontrol.ServerParams{ 66 BufLimit: 300000000, 67 MinRecharge: 50000, 68 } 69 srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000) 70 srv.fcCostStats = newCostStats(eth.ChainDb()) 71 return srv, nil 72 } 73 74 func (s *LesServer) Protocols() []p2p.Protocol { 75 return s.protocolManager.SubProtocols 76 } 77 78 // Start starts the LES server 79 func (s *LesServer) Start(srvr *p2p.Server) { 80 s.protocolManager.Start() 81 go func() { 82 logger := log.New("topic", s.lesTopic) 83 logger.Info("Starting topic registration") 84 defer logger.Info("Terminated topic registration") 85 86 srvr.DiscV5.RegisterTopic(s.lesTopic, s.quitSync) 87 }() 88 } 89 90 // Stop stops the LES service 91 func (s *LesServer) Stop() { 92 s.fcCostStats.store() 93 s.fcManager.Stop() 94 go func() { 95 <-s.protocolManager.noMorePeers 96 }() 97 s.protocolManager.Stop() 98 } 99 100 type requestCosts struct { 101 baseCost, reqCost uint64 102 } 103 104 type requestCostTable map[uint64]*requestCosts 105 106 type RequestCostList []struct { 107 MsgCode, BaseCost, ReqCost uint64 108 } 109 110 func (list RequestCostList) decode() requestCostTable { 111 table := make(requestCostTable) 112 for _, e := range list { 113 table[e.MsgCode] = &requestCosts{ 114 baseCost: e.BaseCost, 115 reqCost: e.ReqCost, 116 } 117 } 118 return table 119 } 120 121 func (table requestCostTable) encode() RequestCostList { 122 list := make(RequestCostList, len(table)) 123 for idx, code := range reqList { 124 list[idx].MsgCode = code 125 list[idx].BaseCost = table[code].baseCost 126 list[idx].ReqCost = table[code].reqCost 127 } 128 return list 129 } 130 131 type linReg struct { 132 sumX, sumY, sumXX, sumXY float64 133 cnt uint64 134 } 135 136 const linRegMaxCnt = 100000 137 138 func (l *linReg) add(x, y float64) { 139 if l.cnt >= linRegMaxCnt { 140 sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt 141 l.sumX -= l.sumX * sub 142 l.sumY -= l.sumY * sub 143 l.sumXX -= l.sumXX * sub 144 l.sumXY -= l.sumXY * sub 145 l.cnt = linRegMaxCnt - 1 146 } 147 l.cnt++ 148 l.sumX += x 149 l.sumY += y 150 l.sumXX += x * x 151 l.sumXY += x * y 152 } 153 154 func (l *linReg) calc() (b, m float64) { 155 if l.cnt == 0 { 156 return 0, 0 157 } 158 cnt := float64(l.cnt) 159 d := cnt*l.sumXX - l.sumX*l.sumX 160 if d < 0.001 { 161 return l.sumY / cnt, 0 162 } 163 m = (cnt*l.sumXY - l.sumX*l.sumY) / d 164 b = (l.sumY / cnt) - (m * l.sumX / cnt) 165 return b, m 166 } 167 168 func (l *linReg) toBytes() []byte { 169 var arr [40]byte 170 binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX)) 171 binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY)) 172 binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX)) 173 binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY)) 174 binary.BigEndian.PutUint64(arr[32:40], l.cnt) 175 return arr[:] 176 } 177 178 func linRegFromBytes(data []byte) *linReg { 179 if len(data) != 40 { 180 return nil 181 } 182 l := &linReg{} 183 l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8])) 184 l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16])) 185 l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24])) 186 l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32])) 187 l.cnt = binary.BigEndian.Uint64(data[32:40]) 188 return l 189 } 190 191 type requestCostStats struct { 192 lock sync.RWMutex 193 db ethdb.Database 194 stats map[uint64]*linReg 195 } 196 197 type requestCostStatsRlp []struct { 198 MsgCode uint64 199 Data []byte 200 } 201 202 var rcStatsKey = []byte("_requestCostStats") 203 204 func newCostStats(db ethdb.Database) *requestCostStats { 205 stats := make(map[uint64]*linReg) 206 for _, code := range reqList { 207 stats[code] = &linReg{cnt: 100} 208 } 209 210 if db != nil { 211 data, err := db.Get(rcStatsKey) 212 var statsRlp requestCostStatsRlp 213 if err == nil { 214 err = rlp.DecodeBytes(data, &statsRlp) 215 } 216 if err == nil { 217 for _, r := range statsRlp { 218 if stats[r.MsgCode] != nil { 219 if l := linRegFromBytes(r.Data); l != nil { 220 stats[r.MsgCode] = l 221 } 222 } 223 } 224 } 225 } 226 227 return &requestCostStats{ 228 db: db, 229 stats: stats, 230 } 231 } 232 233 func (s *requestCostStats) store() { 234 s.lock.Lock() 235 defer s.lock.Unlock() 236 237 statsRlp := make(requestCostStatsRlp, len(reqList)) 238 for i, code := range reqList { 239 statsRlp[i].MsgCode = code 240 statsRlp[i].Data = s.stats[code].toBytes() 241 } 242 243 if data, err := rlp.EncodeToBytes(statsRlp); err == nil { 244 s.db.Put(rcStatsKey, data) 245 } 246 } 247 248 func (s *requestCostStats) getCurrentList() RequestCostList { 249 s.lock.Lock() 250 defer s.lock.Unlock() 251 252 list := make(RequestCostList, len(reqList)) 253 //fmt.Println("RequestCostList") 254 for idx, code := range reqList { 255 b, m := s.stats[code].calc() 256 //fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000) 257 if m < 0 { 258 b += m 259 m = 0 260 } 261 if b < 0 { 262 b = 0 263 } 264 265 list[idx].MsgCode = code 266 list[idx].BaseCost = uint64(b * 2) 267 list[idx].ReqCost = uint64(m * 2) 268 } 269 return list 270 } 271 272 func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) { 273 s.lock.Lock() 274 defer s.lock.Unlock() 275 276 c, ok := s.stats[msgCode] 277 if !ok || reqCnt == 0 { 278 return 279 } 280 c.add(float64(reqCnt), float64(cost)) 281 } 282 283 func (pm *ProtocolManager) blockLoop() { 284 pm.wg.Add(1) 285 sub := pm.eventMux.Subscribe(core.ChainHeadEvent{}) 286 newCht := make(chan struct{}, 10) 287 newCht <- struct{}{} 288 go func() { 289 var mu sync.Mutex 290 var lastHead *types.Header 291 lastBroadcastTd := common.Big0 292 for { 293 select { 294 case ev := <-sub.Chan(): 295 peers := pm.peers.AllPeers() 296 if len(peers) > 0 { 297 header := ev.Data.(core.ChainHeadEvent).Block.Header() 298 hash := header.Hash() 299 number := header.Number.Uint64() 300 td := core.GetTd(pm.chainDb, hash, number) 301 if td != nil && td.Cmp(lastBroadcastTd) > 0 { 302 var reorg uint64 303 if lastHead != nil { 304 reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() 305 } 306 lastHead = header 307 lastBroadcastTd = td 308 309 log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) 310 311 announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} 312 for _, p := range peers { 313 select { 314 case p.announceChn <- announce: 315 default: 316 pm.removePeer(p.id) 317 } 318 } 319 } 320 } 321 newCht <- struct{}{} 322 case <-newCht: 323 go func() { 324 mu.Lock() 325 more := makeCht(pm.chainDb) 326 mu.Unlock() 327 if more { 328 time.Sleep(time.Millisecond * 10) 329 newCht <- struct{}{} 330 } 331 }() 332 case <-pm.quitSync: 333 sub.Unsubscribe() 334 pm.wg.Done() 335 return 336 } 337 } 338 }() 339 } 340 341 var ( 342 lastChtKey = []byte("LastChtNumber") // chtNum (uint64 big endian) 343 chtPrefix = []byte("cht") // chtPrefix + chtNum (uint64 big endian) -> trie root hash 344 ) 345 346 func getChtRoot(db ethdb.Database, num uint64) common.Hash { 347 var encNumber [8]byte 348 binary.BigEndian.PutUint64(encNumber[:], num) 349 data, _ := db.Get(append(chtPrefix, encNumber[:]...)) 350 return common.BytesToHash(data) 351 } 352 353 func storeChtRoot(db ethdb.Database, num uint64, root common.Hash) { 354 var encNumber [8]byte 355 binary.BigEndian.PutUint64(encNumber[:], num) 356 db.Put(append(chtPrefix, encNumber[:]...), root[:]) 357 } 358 359 func makeCht(db ethdb.Database) bool { 360 headHash := core.GetHeadBlockHash(db) 361 headNum := core.GetBlockNumber(db, headHash) 362 363 var newChtNum uint64 364 if headNum > light.ChtConfirmations { 365 newChtNum = (headNum - light.ChtConfirmations) / light.ChtFrequency 366 } 367 368 var lastChtNum uint64 369 data, _ := db.Get(lastChtKey) 370 if len(data) == 8 { 371 lastChtNum = binary.BigEndian.Uint64(data[:]) 372 } 373 if newChtNum <= lastChtNum { 374 return false 375 } 376 377 var t *trie.Trie 378 if lastChtNum > 0 { 379 var err error 380 t, err = trie.New(getChtRoot(db, lastChtNum), db) 381 if err != nil { 382 lastChtNum = 0 383 } 384 } 385 if lastChtNum == 0 { 386 t, _ = trie.New(common.Hash{}, db) 387 } 388 389 for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ { 390 hash := core.GetCanonicalHash(db, num) 391 if hash == (common.Hash{}) { 392 panic("Canonical hash not found") 393 } 394 td := core.GetTd(db, hash, num) 395 if td == nil { 396 panic("TD not found") 397 } 398 var encNumber [8]byte 399 binary.BigEndian.PutUint64(encNumber[:], num) 400 var node light.ChtNode 401 node.Hash = hash 402 node.Td = td 403 data, _ := rlp.EncodeToBytes(node) 404 t.Update(encNumber[:], data) 405 } 406 407 root, err := t.Commit() 408 if err != nil { 409 lastChtNum = 0 410 } else { 411 lastChtNum++ 412 413 log.Trace("Generated CHT", "number", lastChtNum, "root", root.Hex()) 414 415 storeChtRoot(db, lastChtNum, root) 416 var data [8]byte 417 binary.BigEndian.PutUint64(data[:], lastChtNum) 418 db.Put(lastChtKey, data[:]) 419 } 420 421 return newChtNum > lastChtNum 422 }