github.com/phillinzzz/newBsc@v1.1.6/les/odr.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 18 19 import ( 20 "context" 21 "sort" 22 "time" 23 24 "github.com/phillinzzz/newBsc/common/mclock" 25 "github.com/phillinzzz/newBsc/core" 26 "github.com/phillinzzz/newBsc/ethdb" 27 "github.com/phillinzzz/newBsc/light" 28 ) 29 30 // LesOdr implements light.OdrBackend 31 type LesOdr struct { 32 db ethdb.Database 33 indexerConfig *light.IndexerConfig 34 chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer 35 peers *serverPeerSet 36 retriever *retrieveManager 37 stop chan struct{} 38 } 39 40 func NewLesOdr(db ethdb.Database, config *light.IndexerConfig, peers *serverPeerSet, retriever *retrieveManager) *LesOdr { 41 return &LesOdr{ 42 db: db, 43 indexerConfig: config, 44 peers: peers, 45 retriever: retriever, 46 stop: make(chan struct{}), 47 } 48 } 49 50 // Stop cancels all pending retrievals 51 func (odr *LesOdr) Stop() { 52 close(odr.stop) 53 } 54 55 // Database returns the backing database 56 func (odr *LesOdr) Database() ethdb.Database { 57 return odr.db 58 } 59 60 // SetIndexers adds the necessary chain indexers to the ODR backend 61 func (odr *LesOdr) SetIndexers(chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer) { 62 odr.chtIndexer = chtIndexer 63 odr.bloomTrieIndexer = bloomTrieIndexer 64 odr.bloomIndexer = bloomIndexer 65 } 66 67 // ChtIndexer returns the CHT chain indexer 68 func (odr *LesOdr) ChtIndexer() *core.ChainIndexer { 69 return odr.chtIndexer 70 } 71 72 // BloomTrieIndexer returns the bloom trie chain indexer 73 func (odr *LesOdr) BloomTrieIndexer() *core.ChainIndexer { 74 return odr.bloomTrieIndexer 75 } 76 77 // BloomIndexer returns the bloombits chain indexer 78 func (odr *LesOdr) BloomIndexer() *core.ChainIndexer { 79 return odr.bloomIndexer 80 } 81 82 // IndexerConfig returns the indexer config. 83 func (odr *LesOdr) IndexerConfig() *light.IndexerConfig { 84 return odr.indexerConfig 85 } 86 87 const ( 88 MsgBlockHeaders = iota 89 MsgBlockBodies 90 MsgCode 91 MsgReceipts 92 MsgProofsV2 93 MsgHelperTrieProofs 94 MsgTxStatus 95 ) 96 97 // Msg encodes a LES message that delivers reply data for a request 98 type Msg struct { 99 MsgType int 100 ReqID uint64 101 Obj interface{} 102 } 103 104 // peerByTxHistory is a heap.Interface implementation which can sort 105 // the peerset by transaction history. 106 type peerByTxHistory []*serverPeer 107 108 func (h peerByTxHistory) Len() int { return len(h) } 109 func (h peerByTxHistory) Less(i, j int) bool { 110 if h[i].txHistory == txIndexUnlimited { 111 return false 112 } 113 if h[j].txHistory == txIndexUnlimited { 114 return true 115 } 116 return h[i].txHistory < h[j].txHistory 117 } 118 func (h peerByTxHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 119 120 const ( 121 maxTxStatusRetry = 3 // The maximum retrys will be made for tx status request. 122 maxTxStatusCandidates = 5 // The maximum les servers the tx status requests will be sent to. 123 ) 124 125 // RetrieveTxStatus retrieves the transaction status from the LES network. 126 // There is no guarantee in the LES protocol that the mined transaction will 127 // be retrieved back for sure because of different reasons(the transaction 128 // is unindexed, the malicous server doesn't reply it deliberately, etc). 129 // Therefore, unretrieved transactions(UNKNOWN) will receive a certain number 130 // of retries, thus giving a weak guarantee. 131 func (odr *LesOdr) RetrieveTxStatus(ctx context.Context, req *light.TxStatusRequest) error { 132 // Sort according to the transaction history supported by the peer and 133 // select the peers with longest history. 134 var ( 135 retries int 136 peers []*serverPeer 137 missing = len(req.Hashes) 138 result = make([]light.TxStatus, len(req.Hashes)) 139 canSend = make(map[string]bool) 140 ) 141 for _, peer := range odr.peers.allPeers() { 142 if peer.txHistory == txIndexDisabled { 143 continue 144 } 145 peers = append(peers, peer) 146 } 147 sort.Sort(sort.Reverse(peerByTxHistory(peers))) 148 for i := 0; i < maxTxStatusCandidates && i < len(peers); i++ { 149 canSend[peers[i].id] = true 150 } 151 // Send out the request and assemble the result. 152 for { 153 if retries >= maxTxStatusRetry || len(canSend) == 0 { 154 break 155 } 156 var ( 157 // Deep copy the request, so that the partial result won't be mixed. 158 req = &TxStatusRequest{Hashes: req.Hashes} 159 id = genReqID() 160 distreq = &distReq{ 161 getCost: func(dp distPeer) uint64 { return req.GetCost(dp.(*serverPeer)) }, 162 canSend: func(dp distPeer) bool { return canSend[dp.(*serverPeer).id] }, 163 request: func(dp distPeer) func() { 164 p := dp.(*serverPeer) 165 p.fcServer.QueuedRequest(id, req.GetCost(p)) 166 delete(canSend, p.id) 167 return func() { req.Request(id, p) } 168 }, 169 } 170 ) 171 if err := odr.retriever.retrieve(ctx, id, distreq, func(p distPeer, msg *Msg) error { return req.Validate(odr.db, msg) }, odr.stop); err != nil { 172 return err 173 } 174 // Collect the response and assemble them to the final result. 175 // All the response is not verifiable, so always pick the first 176 // one we get. 177 for index, status := range req.Status { 178 if result[index].Status != core.TxStatusUnknown { 179 continue 180 } 181 if status.Status == core.TxStatusUnknown { 182 continue 183 } 184 result[index], missing = status, missing-1 185 } 186 // Abort the procedure if all the status are retrieved 187 if missing == 0 { 188 break 189 } 190 retries += 1 191 } 192 req.Status = result 193 return nil 194 } 195 196 // Retrieve tries to fetch an object from the LES network. It's a common API 197 // for most of the LES requests except for the TxStatusRequest which needs 198 // the additional retry mechanism. 199 // If the network retrieval was successful, it stores the object in local db. 200 func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) { 201 lreq := LesRequest(req) 202 203 reqID := genReqID() 204 rq := &distReq{ 205 getCost: func(dp distPeer) uint64 { 206 return lreq.GetCost(dp.(*serverPeer)) 207 }, 208 canSend: func(dp distPeer) bool { 209 p := dp.(*serverPeer) 210 if !p.onlyAnnounce { 211 return lreq.CanSend(p) 212 } 213 return false 214 }, 215 request: func(dp distPeer) func() { 216 p := dp.(*serverPeer) 217 cost := lreq.GetCost(p) 218 p.fcServer.QueuedRequest(reqID, cost) 219 return func() { lreq.Request(reqID, p) } 220 }, 221 } 222 223 defer func(sent mclock.AbsTime) { 224 if err != nil { 225 return 226 } 227 requestRTT.Update(time.Duration(mclock.Now() - sent)) 228 }(mclock.Now()) 229 230 if err := odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err != nil { 231 return err 232 } 233 req.StoreResult(odr.db) 234 return nil 235 }