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