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