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