github.com/alanchchen/go-ethereum@v1.6.6-0.20170601190819-6171d01b1195/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 "crypto/rand" 22 "encoding/binary" 23 "sync" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common/mclock" 27 "github.com/ethereum/go-ethereum/ethdb" 28 "github.com/ethereum/go-ethereum/light" 29 "github.com/ethereum/go-ethereum/log" 30 ) 31 32 var ( 33 softRequestTimeout = time.Millisecond * 500 34 hardRequestTimeout = time.Second * 10 35 ) 36 37 // peerDropFn is a callback type for dropping a peer detected as malicious. 38 type peerDropFn func(id string) 39 40 type odrPeerSelector interface { 41 adjustResponseTime(*poolEntry, time.Duration, bool) 42 } 43 44 type LesOdr struct { 45 light.OdrBackend 46 db ethdb.Database 47 stop chan struct{} 48 removePeer peerDropFn 49 mlock, clock sync.Mutex 50 sentReqs map[uint64]*sentReq 51 serverPool odrPeerSelector 52 reqDist *requestDistributor 53 } 54 55 func NewLesOdr(db ethdb.Database) *LesOdr { 56 return &LesOdr{ 57 db: db, 58 stop: make(chan struct{}), 59 sentReqs: make(map[uint64]*sentReq), 60 } 61 } 62 63 func (odr *LesOdr) Stop() { 64 close(odr.stop) 65 } 66 67 func (odr *LesOdr) Database() ethdb.Database { 68 return odr.db 69 } 70 71 // validatorFunc is a function that processes a message. 72 type validatorFunc func(ethdb.Database, *Msg) error 73 74 // sentReq is a request waiting for an answer that satisfies its valFunc 75 type sentReq struct { 76 valFunc validatorFunc 77 sentTo map[*peer]chan struct{} 78 lock sync.RWMutex // protects acces to sentTo 79 answered chan struct{} // closed and set to nil when any peer answers it 80 } 81 82 const ( 83 MsgBlockBodies = iota 84 MsgCode 85 MsgReceipts 86 MsgProofs 87 MsgHeaderProofs 88 ) 89 90 // Msg encodes a LES message that delivers reply data for a request 91 type Msg struct { 92 MsgType int 93 ReqID uint64 94 Obj interface{} 95 } 96 97 // Deliver is called by the LES protocol manager to deliver ODR reply messages to waiting requests 98 func (self *LesOdr) Deliver(peer *peer, msg *Msg) error { 99 var delivered chan struct{} 100 self.mlock.Lock() 101 req, ok := self.sentReqs[msg.ReqID] 102 self.mlock.Unlock() 103 if ok { 104 req.lock.Lock() 105 delivered, ok = req.sentTo[peer] 106 req.lock.Unlock() 107 } 108 109 if !ok { 110 return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) 111 } 112 113 if err := req.valFunc(self.db, msg); err != nil { 114 peer.Log().Warn("Invalid odr response", "err", err) 115 return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID) 116 } 117 close(delivered) 118 req.lock.Lock() 119 delete(req.sentTo, peer) 120 if req.answered != nil { 121 close(req.answered) 122 req.answered = nil 123 } 124 req.lock.Unlock() 125 return nil 126 } 127 128 func (self *LesOdr) requestPeer(req *sentReq, peer *peer, delivered, timeout chan struct{}, reqWg *sync.WaitGroup) { 129 stime := mclock.Now() 130 defer func() { 131 req.lock.Lock() 132 delete(req.sentTo, peer) 133 req.lock.Unlock() 134 reqWg.Done() 135 }() 136 137 select { 138 case <-delivered: 139 if self.serverPool != nil { 140 self.serverPool.adjustResponseTime(peer.poolEntry, time.Duration(mclock.Now()-stime), false) 141 } 142 return 143 case <-time.After(softRequestTimeout): 144 close(timeout) 145 case <-self.stop: 146 return 147 } 148 149 select { 150 case <-delivered: 151 case <-time.After(hardRequestTimeout): 152 peer.Log().Debug("Request timed out hard") 153 go self.removePeer(peer.id) 154 case <-self.stop: 155 return 156 } 157 if self.serverPool != nil { 158 self.serverPool.adjustResponseTime(peer.poolEntry, time.Duration(mclock.Now()-stime), true) 159 } 160 } 161 162 // networkRequest sends a request to known peers until an answer is received 163 // or the context is cancelled 164 func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) error { 165 answered := make(chan struct{}) 166 req := &sentReq{ 167 valFunc: lreq.Validate, 168 sentTo: make(map[*peer]chan struct{}), 169 answered: answered, // reply delivered by any peer 170 } 171 172 exclude := make(map[*peer]struct{}) 173 174 reqWg := new(sync.WaitGroup) 175 reqWg.Add(1) 176 defer reqWg.Done() 177 178 var timeout chan struct{} 179 reqID := getNextReqID() 180 rq := &distReq{ 181 getCost: func(dp distPeer) uint64 { 182 return lreq.GetCost(dp.(*peer)) 183 }, 184 canSend: func(dp distPeer) bool { 185 p := dp.(*peer) 186 _, ok := exclude[p] 187 return !ok && lreq.CanSend(p) 188 }, 189 request: func(dp distPeer) func() { 190 p := dp.(*peer) 191 exclude[p] = struct{}{} 192 delivered := make(chan struct{}) 193 timeout = make(chan struct{}) 194 req.lock.Lock() 195 req.sentTo[p] = delivered 196 req.lock.Unlock() 197 reqWg.Add(1) 198 cost := lreq.GetCost(p) 199 p.fcServer.QueueRequest(reqID, cost) 200 go self.requestPeer(req, p, delivered, timeout, reqWg) 201 return func() { lreq.Request(reqID, p) } 202 }, 203 } 204 205 self.mlock.Lock() 206 self.sentReqs[reqID] = req 207 self.mlock.Unlock() 208 209 go func() { 210 reqWg.Wait() 211 self.mlock.Lock() 212 delete(self.sentReqs, reqID) 213 self.mlock.Unlock() 214 }() 215 216 for { 217 peerChn := self.reqDist.queue(rq) 218 select { 219 case <-ctx.Done(): 220 self.reqDist.cancel(rq) 221 return ctx.Err() 222 case <-answered: 223 self.reqDist.cancel(rq) 224 return nil 225 case _, ok := <-peerChn: 226 if !ok { 227 return ErrNoPeers 228 } 229 } 230 231 select { 232 case <-ctx.Done(): 233 return ctx.Err() 234 case <-answered: 235 return nil 236 case <-timeout: 237 } 238 } 239 } 240 241 // Retrieve tries to fetch an object from the LES network. 242 // If the network retrieval was successful, it stores the object in local db. 243 func (self *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) { 244 lreq := LesRequest(req) 245 err = self.networkRequest(ctx, lreq) 246 if err == nil { 247 // retrieved from network, store in db 248 req.StoreResult(self.db) 249 } else { 250 log.Debug("Failed to retrieve data from network", "err", err) 251 } 252 return 253 } 254 255 func getNextReqID() uint64 { 256 var rnd [8]byte 257 rand.Read(rnd[:]) 258 return binary.BigEndian.Uint64(rnd[:]) 259 }