github.com/theQRL/go-zond@v0.1.1/les/server_handler.go (about) 1 // Copyright 2019 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 18 19 import ( 20 "errors" 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/theQRL/go-zond/common" 26 "github.com/theQRL/go-zond/common/mclock" 27 "github.com/theQRL/go-zond/core" 28 "github.com/theQRL/go-zond/core/forkid" 29 "github.com/theQRL/go-zond/core/rawdb" 30 "github.com/theQRL/go-zond/core/txpool" 31 "github.com/theQRL/go-zond/core/types" 32 "github.com/theQRL/go-zond/les/flowcontrol" 33 "github.com/theQRL/go-zond/light" 34 "github.com/theQRL/go-zond/log" 35 "github.com/theQRL/go-zond/metrics" 36 "github.com/theQRL/go-zond/p2p" 37 "github.com/theQRL/go-zond/trie" 38 "github.com/theQRL/go-zond/zonddb" 39 ) 40 41 const ( 42 softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. 43 estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header 44 45 MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request 46 MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request 47 MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request 48 MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request 49 MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request 50 MaxHelperTrieProofsFetch = 64 // Amount of helper tries to be fetched per retrieval request 51 MaxTxSend = 64 // Amount of transactions to be send per request 52 MaxTxStatus = 256 // Amount of transactions to queried per request 53 ) 54 55 var ( 56 errTooManyInvalidRequest = errors.New("too many invalid requests made") 57 ) 58 59 // serverHandler is responsible for serving light client and process 60 // all incoming light requests. 61 type serverHandler struct { 62 forkFilter forkid.Filter 63 blockchain *core.BlockChain 64 chainDb zonddb.Database 65 txpool *txpool.TxPool 66 server *LesServer 67 68 closeCh chan struct{} // Channel used to exit all background routines of handler. 69 wg sync.WaitGroup // WaitGroup used to track all background routines of handler. 70 synced func() bool // Callback function used to determine whether local node is synced. 71 72 // Testing fields 73 addTxsSync bool 74 } 75 76 func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb zonddb.Database, txpool *txpool.TxPool, synced func() bool) *serverHandler { 77 handler := &serverHandler{ 78 forkFilter: forkid.NewFilter(blockchain), 79 server: server, 80 blockchain: blockchain, 81 chainDb: chainDb, 82 txpool: txpool, 83 closeCh: make(chan struct{}), 84 synced: synced, 85 } 86 return handler 87 } 88 89 // start starts the server handler. 90 func (h *serverHandler) start() { 91 h.wg.Add(1) 92 go h.broadcastLoop() 93 } 94 95 // stop stops the server handler. 96 func (h *serverHandler) stop() { 97 close(h.closeCh) 98 h.wg.Wait() 99 } 100 101 // runPeer is the p2p protocol run function for the given version. 102 func (h *serverHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error { 103 peer := newClientPeer(int(version), h.server.config.NetworkId, p, newMeteredMsgWriter(rw, int(version))) 104 defer peer.close() 105 h.wg.Add(1) 106 defer h.wg.Done() 107 return h.handle(peer) 108 } 109 110 func (h *serverHandler) handle(p *clientPeer) error { 111 p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) 112 113 // Execute the LES handshake 114 var ( 115 head = h.blockchain.CurrentHeader() 116 hash = head.Hash() 117 number = head.Number.Uint64() 118 td = h.blockchain.GetTd(hash, number) 119 forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis(), number, head.Time) 120 ) 121 if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil { 122 p.Log().Debug("Light Ethereum handshake failed", "err", err) 123 return err 124 } 125 // Connected to another server, no messages expected, just wait for disconnection 126 if p.server { 127 if err := h.server.serverset.register(p); err != nil { 128 return err 129 } 130 _, err := p.rw.ReadMsg() 131 h.server.serverset.unregister(p) 132 return err 133 } 134 // Setup flow control mechanism for the peer 135 p.fcClient = flowcontrol.NewClientNode(h.server.fcManager, p.fcParams) 136 defer p.fcClient.Disconnect() 137 138 // Reject light clients if server is not synced. Put this checking here, so 139 // that "non-synced" les-server peers are still allowed to keep the connection. 140 if !h.synced() { 141 p.Log().Debug("Light server not synced, rejecting peer") 142 return p2p.DiscRequested 143 } 144 145 // Register the peer into the peerset and clientpool 146 if err := h.server.peers.register(p); err != nil { 147 return err 148 } 149 if p.balance = h.server.clientPool.Register(p); p.balance == nil { 150 h.server.peers.unregister(p.ID()) 151 p.Log().Debug("Client pool already closed") 152 return p2p.DiscRequested 153 } 154 p.connectedAt = mclock.Now() 155 156 var wg sync.WaitGroup // Wait group used to track all in-flight task routines. 157 defer func() { 158 wg.Wait() // Ensure all background task routines have exited. 159 h.server.clientPool.Unregister(p) 160 h.server.peers.unregister(p.ID()) 161 p.balance = nil 162 connectionTimer.Update(time.Duration(mclock.Now() - p.connectedAt)) 163 }() 164 165 // Mark the peer as being served. 166 p.serving.Store(true) 167 defer p.serving.Store(false) 168 169 // Spawn a main loop to handle all incoming messages. 170 for { 171 select { 172 case err := <-p.errCh: 173 p.Log().Debug("Failed to send light ethereum response", "err", err) 174 return err 175 default: 176 } 177 if err := h.handleMsg(p, &wg); err != nil { 178 p.Log().Debug("Light Ethereum message handling failed", "err", err) 179 return err 180 } 181 } 182 } 183 184 // beforeHandle will do a series of prechecks before handling message. 185 func (h *serverHandler) beforeHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, reqCnt uint64, maxCount uint64) (*servingTask, uint64) { 186 // Ensure that the request sent by client peer is valid 187 inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0) 188 if reqCnt == 0 || reqCnt > maxCount { 189 p.fcClient.OneTimeCost(inSizeCost) 190 return nil, 0 191 } 192 // Ensure that the client peer complies with the flow control 193 // rules agreed by both sides. 194 if p.isFrozen() { 195 p.fcClient.OneTimeCost(inSizeCost) 196 return nil, 0 197 } 198 maxCost := p.fcCosts.getMaxCost(msg.Code, reqCnt) 199 accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost) 200 if !accepted { 201 p.freeze() 202 p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge))) 203 p.fcClient.OneTimeCost(inSizeCost) 204 return nil, 0 205 } 206 // Create a multi-stage task, estimate the time it takes for the task to 207 // execute, and cache it in the request service queue. 208 factor := h.server.costTracker.globalFactor() 209 if factor < 0.001 { 210 factor = 1 211 p.Log().Error("Invalid global cost factor", "factor", factor) 212 } 213 maxTime := uint64(float64(maxCost) / factor) 214 task := h.server.servingQueue.newTask(p, maxTime, priority) 215 if !task.start() { 216 p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost) 217 return nil, 0 218 } 219 return task, maxCost 220 } 221 222 // Afterhandle will perform a series of operations after message handling, 223 // such as updating flow control data, sending reply, etc. 224 func (h *serverHandler) afterHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, maxCost uint64, reqCnt uint64, task *servingTask, reply *reply) { 225 if reply != nil { 226 task.done() 227 } 228 p.responseLock.Lock() 229 defer p.responseLock.Unlock() 230 231 // Short circuit if the client is already frozen. 232 if p.isFrozen() { 233 realCost := h.server.costTracker.realCost(task.servingTime, msg.Size, 0) 234 p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) 235 return 236 } 237 // Positive correction buffer value with real cost. 238 var replySize uint32 239 if reply != nil { 240 replySize = reply.size() 241 } 242 var realCost uint64 243 if h.server.costTracker.testing { 244 realCost = maxCost // Assign a fake cost for testing purpose 245 } else { 246 realCost = h.server.costTracker.realCost(task.servingTime, msg.Size, replySize) 247 if realCost > maxCost { 248 realCost = maxCost 249 } 250 } 251 bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) 252 if reply != nil { 253 // Feed cost tracker request serving statistic. 254 h.server.costTracker.updateStats(msg.Code, reqCnt, task.servingTime, realCost) 255 // Reduce priority "balance" for the specific peer. 256 p.balance.RequestServed(realCost) 257 p.queueSend(func() { 258 if err := reply.send(bv); err != nil { 259 select { 260 case p.errCh <- err: 261 default: 262 } 263 } 264 }) 265 } 266 } 267 268 // handleMsg is invoked whenever an inbound message is received from a remote 269 // peer. The remote connection is torn down upon returning any error. 270 func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { 271 // Read the next message from the remote peer, and ensure it's fully consumed 272 msg, err := p.rw.ReadMsg() 273 if err != nil { 274 return err 275 } 276 p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size) 277 278 // Discard large message which exceeds the limitation. 279 if msg.Size > ProtocolMaxMsgSize { 280 clientErrorMeter.Mark(1) 281 return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) 282 } 283 defer msg.Discard() 284 285 // Lookup the request handler table, ensure it's supported 286 // message type by the protocol. 287 req, ok := Les3[msg.Code] 288 if !ok { 289 p.Log().Trace("Received invalid message", "code", msg.Code) 290 clientErrorMeter.Mark(1) 291 return errResp(ErrInvalidMsgCode, "%v", msg.Code) 292 } 293 p.Log().Trace("Received " + req.Name) 294 295 // Decode the p2p message, resolve the concrete handler for it. 296 serve, reqID, reqCnt, err := req.Handle(msg) 297 if err != nil { 298 clientErrorMeter.Mark(1) 299 return errResp(ErrDecode, "%v: %v", msg, err) 300 } 301 if metrics.EnabledExpensive { 302 req.InPacketsMeter.Mark(1) 303 req.InTrafficMeter.Mark(int64(msg.Size)) 304 } 305 p.responseCount++ 306 responseCount := p.responseCount 307 308 // First check this client message complies all rules before 309 // handling it and return a processor if all checks are passed. 310 task, maxCost := h.beforeHandle(p, reqID, responseCount, msg, reqCnt, req.MaxCount) 311 if task == nil { 312 return nil 313 } 314 wg.Add(1) 315 go func() { 316 defer wg.Done() 317 318 reply := serve(h, p, task.waitOrStop) 319 h.afterHandle(p, reqID, responseCount, msg, maxCost, reqCnt, task, reply) 320 321 if metrics.EnabledExpensive { 322 size := uint32(0) 323 if reply != nil { 324 size = reply.size() 325 } 326 req.OutPacketsMeter.Mark(1) 327 req.OutTrafficMeter.Mark(int64(size)) 328 req.ServingTimeMeter.Update(time.Duration(task.servingTime)) 329 } 330 }() 331 // If the client has made too much invalid request(e.g. request a non-existent data), 332 // reject them to prevent SPAM attack. 333 if p.getInvalid() > maxRequestErrors { 334 clientErrorMeter.Mark(1) 335 return errTooManyInvalidRequest 336 } 337 return nil 338 } 339 340 // BlockChain implements serverBackend 341 func (h *serverHandler) BlockChain() *core.BlockChain { 342 return h.blockchain 343 } 344 345 // TxPool implements serverBackend 346 func (h *serverHandler) TxPool() *txpool.TxPool { 347 return h.txpool 348 } 349 350 // ArchiveMode implements serverBackend 351 func (h *serverHandler) ArchiveMode() bool { 352 return h.server.archiveMode 353 } 354 355 // AddTxsSync implements serverBackend 356 func (h *serverHandler) AddTxsSync() bool { 357 return h.addTxsSync 358 } 359 360 // getAccount retrieves an account from the state based on root. 361 func getAccount(triedb *trie.Database, root common.Hash, addr common.Address) (types.StateAccount, error) { 362 trie, err := trie.NewStateTrie(trie.StateTrieID(root), triedb) 363 if err != nil { 364 return types.StateAccount{}, err 365 } 366 acc, err := trie.GetAccount(addr) 367 if err != nil { 368 return types.StateAccount{}, err 369 } 370 if acc == nil { 371 return types.StateAccount{}, fmt.Errorf("account %#x is not present", addr) 372 } 373 return *acc, nil 374 } 375 376 // GetHelperTrie returns the post-processed trie root for the given trie ID and section index 377 func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie { 378 var ( 379 root common.Hash 380 prefix string 381 ) 382 switch typ { 383 case htCanonical: 384 sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1) 385 root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), string(rawdb.ChtTablePrefix) 386 case htBloomBits: 387 sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1) 388 root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), string(rawdb.BloomTrieTablePrefix) 389 } 390 if root == (common.Hash{}) { 391 return nil 392 } 393 triedb := trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix), trie.HashDefaults) 394 trie, _ := trie.New(trie.TrieID(root), triedb) 395 return trie 396 } 397 398 // broadcastLoop broadcasts new block information to all connected light 399 // clients. According to the agreement between client and server, server should 400 // only broadcast new announcement if the total difficulty is higher than the 401 // last one. Besides server will add the signature if client requires. 402 func (h *serverHandler) broadcastLoop() { 403 defer h.wg.Done() 404 405 headCh := make(chan core.ChainHeadEvent, 10) 406 headSub := h.blockchain.SubscribeChainHeadEvent(headCh) 407 defer headSub.Unsubscribe() 408 409 var ( 410 lastHead = h.blockchain.CurrentHeader() 411 lastTd = common.Big0 412 ) 413 for { 414 select { 415 case ev := <-headCh: 416 header := ev.Block.Header() 417 hash, number := header.Hash(), header.Number.Uint64() 418 td := h.blockchain.GetTd(hash, number) 419 if td == nil || td.Cmp(lastTd) <= 0 { 420 continue 421 } 422 var reorg uint64 423 if lastHead != nil { 424 // If a setHead has been performed, the common ancestor can be nil. 425 if ancestor := rawdb.FindCommonAncestor(h.chainDb, header, lastHead); ancestor != nil { 426 reorg = lastHead.Number.Uint64() - ancestor.Number.Uint64() 427 } 428 } 429 lastHead, lastTd = header, td 430 log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) 431 h.server.peers.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}) 432 case <-h.closeCh: 433 return 434 } 435 } 436 }