Skip to content

Commit

Permalink
les: limit state ODR retrievals to the last 100 blocks (ethereum#17744)
Browse files Browse the repository at this point in the history
  • Loading branch information
zsfelfoldi authored and holiman committed Oct 1, 2018
1 parent 96fd50b commit b7bbe66
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 19 deletions.
27 changes: 17 additions & 10 deletions les/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions les/odr_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion les/odr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions les/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion les/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit b7bbe66

Please sign in to comment.