diff --git a/les/fetcher.go b/les/fetcher.go index cc539c42b..f0d3b188d 100644 --- a/les/fetcher.go +++ b/les/fetcher.go @@ -32,8 +32,9 @@ import ( ) const ( - blockDelayTimeout = time.Second * 10 // timeout for a peer to announce a head that has already been confirmed by others - maxNodeCount = 20 // maximum number of fetcherTreeNode entries remembered for each peer + blockDelayTimeout = time.Second * 10 // timeout for a peer to announce a head that has already been confirmed by others + maxNodeCount = 20 // maximum number of fetcherTreeNode entries remembered for each peer + serverStateAvailable = 100 // number of recent blocks where state availability is assumed ) // lightFetcher implements retrieval of newly announced headers. It also provides a peerHasBlock function for the @@ -215,8 +216,8 @@ func (f *lightFetcher) syncLoop() { // registerPeer adds a new peer to the fetcher's peer set func (f *lightFetcher) registerPeer(p *peer) { p.lock.Lock() - p.hasBlock = func(hash common.Hash, number uint64) bool { - return f.peerHasBlock(p, hash, number) + p.hasBlock = func(hash common.Hash, number uint64, hasState bool) bool { + return f.peerHasBlock(p, hash, number, hasState) } p.lock.Unlock() @@ -344,21 +345,27 @@ func (f *lightFetcher) announce(p *peer, head *announceData) { // peerHasBlock returns true if we can assume the peer knows the given block // based on its announcements -func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64) bool { +func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64, hasState bool) bool { f.lock.Lock() defer f.lock.Unlock() + fp := f.peers[p] + if fp == nil || fp.root == nil { + return false + } + + if hasState { + if fp.lastAnnounced == nil || fp.lastAnnounced.number > number+serverStateAvailable { + return false + } + } + if f.syncing { // always return true when syncing // false positives are acceptable, a more sophisticated condition can be implemented later return true } - fp := f.peers[p] - if fp == nil || fp.root == nil { - return false - } - if number >= fp.root.number { // it is recent enough that if it is known, is should be in the peer's block tree return fp.nodeByHash[hash] != nil diff --git a/les/odr_requests.go b/les/odr_requests.go index 77b1b6d0c..0f2e5dd9e 100644 --- a/les/odr_requests.go +++ b/les/odr_requests.go @@ -84,7 +84,7 @@ func (r *BlockRequest) GetCost(peer *peer) uint64 { // CanSend tells if a certain peer is suitable for serving the given request func (r *BlockRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Hash, r.Number) + return peer.HasBlock(r.Hash, r.Number, false) } // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -140,7 +140,7 @@ func (r *ReceiptsRequest) GetCost(peer *peer) uint64 { // CanSend tells if a certain peer is suitable for serving the given request func (r *ReceiptsRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Hash, r.Number) + return peer.HasBlock(r.Hash, r.Number, false) } // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -202,7 +202,7 @@ func (r *TrieRequest) GetCost(peer *peer) uint64 { // CanSend tells if a certain peer is suitable for serving the given request func (r *TrieRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber) + return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true) } // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -272,7 +272,7 @@ func (r *CodeRequest) GetCost(peer *peer) uint64 { // CanSend tells if a certain peer is suitable for serving the given request func (r *CodeRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber) + return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true) } // Request sends an ODR request to the LES network (implementation of LesOdrRequest) diff --git a/les/odr_test.go b/les/odr_test.go index e6458adf5..ac81fbcf0 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -194,7 +194,7 @@ func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { client.peers.Register(client.rPeer) time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed client.peers.lock.Lock() - client.rPeer.hasBlock = func(common.Hash, uint64) bool { return true } + client.rPeer.hasBlock = func(common.Hash, uint64, bool) bool { return true } client.peers.lock.Unlock() test(5) // still expect all retrievals to pass, now data should be cached locally diff --git a/les/peer.go b/les/peer.go index 1f343847e..678384f0e 100644 --- a/les/peer.go +++ b/les/peer.go @@ -67,7 +67,7 @@ type peer struct { sendQueue *execQueue poolEntry *poolEntry - hasBlock func(common.Hash, uint64) bool + hasBlock func(common.Hash, uint64, bool) bool responseErrors int fcClient *flowcontrol.ClientNode // nil if the peer is server only @@ -171,11 +171,11 @@ func (p *peer) GetRequestCost(msgcode uint64, amount int) uint64 { } // HasBlock checks if the peer has a given block -func (p *peer) HasBlock(hash common.Hash, number uint64) bool { +func (p *peer) HasBlock(hash common.Hash, number uint64, hasState bool) bool { p.lock.RLock() hasBlock := p.hasBlock p.lock.RUnlock() - return hasBlock != nil && hasBlock(hash, number) + return hasBlock != nil && hasBlock(hash, number, hasState) } // SendAnnounce announces the availability of a number of blocks through diff --git a/les/request_test.go b/les/request_test.go index f02c2a3d7..c9c185198 100644 --- a/les/request_test.go +++ b/les/request_test.go @@ -115,7 +115,7 @@ func testAccess(t *testing.T, protocol int, fn accessTestFn) { client.peers.Register(client.rPeer) time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed client.rPeer.lock.Lock() - client.rPeer.hasBlock = func(common.Hash, uint64) bool { return true } + client.rPeer.hasBlock = func(common.Hash, uint64, bool) bool { return true } client.rPeer.lock.Unlock() // expect all retrievals to pass test(5)