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