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