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