Commit d37d91cc authored by Andreas Bigger's avatar Andreas Bigger

set up a blocked map sidecar for better performance

parent 81a51448
...@@ -142,6 +142,20 @@ func (_m *ConnectionGater) InterceptUpgraded(_a0 network.Conn) (bool, control.Di ...@@ -142,6 +142,20 @@ func (_m *ConnectionGater) InterceptUpgraded(_a0 network.Conn) (bool, control.Di
return r0, r1 return r0, r1
} }
// IsBlocked provides a mock function with given fields: p
func (_m *ConnectionGater) IsBlocked(p peer.ID) bool {
ret := _m.Called(p)
var r0 bool
if rf, ok := ret.Get(0).(func(peer.ID) bool); ok {
r0 = rf(p)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// ListBlockedAddrs provides a mock function with given fields: // ListBlockedAddrs provides a mock function with given fields:
func (_m *ConnectionGater) ListBlockedAddrs() []net.IP { func (_m *ConnectionGater) ListBlockedAddrs() []net.IP {
ret := _m.Called() ret := _m.Called()
......
...@@ -13,6 +13,20 @@ type PeerGater struct { ...@@ -13,6 +13,20 @@ type PeerGater struct {
mock.Mock mock.Mock
} }
// IsBlocked provides a mock function with given fields: _a0
func (_m *PeerGater) IsBlocked(_a0 peer.ID) bool {
ret := _m.Called(_a0)
var r0 bool
if rf, ok := ret.Get(0).(func(peer.ID) bool); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// Update provides a mock function with given fields: _a0, _a1 // Update provides a mock function with given fields: _a0, _a1
func (_m *PeerGater) Update(_a0 peer.ID, _a1 float64) { func (_m *PeerGater) Update(_a0 peer.ID, _a1 float64) {
_m.Called(_a0, _a1) _m.Called(_a0, _a1)
......
...@@ -3,7 +3,6 @@ package p2p ...@@ -3,7 +3,6 @@ package p2p
import ( import (
log "github.com/ethereum/go-ethereum/log" log "github.com/ethereum/go-ethereum/log"
peer "github.com/libp2p/go-libp2p/core/peer" peer "github.com/libp2p/go-libp2p/core/peer"
slices "golang.org/x/exp/slices"
) )
// ConnectionFactor is the factor by which we multiply the connection score. // ConnectionFactor is the factor by which we multiply the connection score.
...@@ -15,6 +14,7 @@ const PeerScoreThreshold = -100 ...@@ -15,6 +14,7 @@ const PeerScoreThreshold = -100
// gater is an internal implementation of the [PeerGater] interface. // gater is an internal implementation of the [PeerGater] interface.
type gater struct { type gater struct {
connGater ConnectionGater connGater ConnectionGater
blockedMap map[peer.ID]bool
log log.Logger log log.Logger
banEnabled bool banEnabled bool
} }
...@@ -25,30 +25,43 @@ type gater struct { ...@@ -25,30 +25,43 @@ type gater struct {
type PeerGater interface { type PeerGater interface {
// Update handles a peer score update and blocks/unblocks the peer if necessary. // Update handles a peer score update and blocks/unblocks the peer if necessary.
Update(peer.ID, float64) Update(peer.ID, float64)
// IsBlocked returns true if the given [peer.ID] is blocked.
IsBlocked(peer.ID) bool
} }
// NewPeerGater returns a new peer gater. // NewPeerGater returns a new peer gater.
func NewPeerGater(connGater ConnectionGater, log log.Logger, banEnabled bool) PeerGater { func NewPeerGater(connGater ConnectionGater, log log.Logger, banEnabled bool) PeerGater {
return &gater{ return &gater{
connGater: connGater, connGater: connGater,
blockedMap: make(map[peer.ID]bool),
log: log, log: log,
banEnabled: banEnabled, banEnabled: banEnabled,
} }
} }
// IsBlocked returns true if the given [peer.ID] is blocked.
func (s *gater) IsBlocked(peerID peer.ID) bool {
return s.blockedMap[peerID]
}
// setBlocked sets the blocked status of the given [peer.ID].
func (s *gater) setBlocked(peerID peer.ID, blocked bool) {
s.blockedMap[peerID] = blocked
}
// Update handles a peer score update and blocks/unblocks the peer if necessary. // Update handles a peer score update and blocks/unblocks the peer if necessary.
func (s *gater) Update(id peer.ID, score float64) { func (s *gater) Update(id peer.ID, score float64) {
// Check if the peer score is below the threshold // Check if the peer score is below the threshold
// If so, we need to block the peer // If so, we need to block the peer
isAlreadyBlocked := slices.Contains(s.connGater.ListBlockedPeers(), id) isAlreadyBlocked := s.IsBlocked(id)
if score < PeerScoreThreshold && s.banEnabled { if score < PeerScoreThreshold && s.banEnabled && !isAlreadyBlocked {
if !isAlreadyBlocked { s.log.Warn("peer blocking enabled, blocking peer", "id", id.String(), "score", score)
s.log.Warn("peer blocking enabled, blocking peer", "id", id.String(), "score", score)
}
err := s.connGater.BlockPeer(id) err := s.connGater.BlockPeer(id)
if err != nil { if err != nil {
s.log.Warn("connection gater failed to block peer", "id", id.String(), "err", err) s.log.Warn("connection gater failed to block peer", "id", id.String(), "err", err)
} }
// Set the peer as blocked in the blocked map
s.setBlocked(id, true)
} }
// Unblock peers whose score has recovered to an acceptable level // Unblock peers whose score has recovered to an acceptable level
if (score > PeerScoreThreshold) && isAlreadyBlocked { if (score > PeerScoreThreshold) && isAlreadyBlocked {
...@@ -56,5 +69,7 @@ func (s *gater) Update(id peer.ID, score float64) { ...@@ -56,5 +69,7 @@ func (s *gater) Update(id peer.ID, score float64) {
if err != nil { if err != nil {
s.log.Warn("connection gater failed to unblock peer", "id", id.String(), "err", err) s.log.Warn("connection gater failed to unblock peer", "id", id.String(), "err", err)
} }
// Set the peer as unblocked in the blocked map
s.setBlocked(id, false)
} }
} }
...@@ -37,7 +37,7 @@ func (testSuite *PeerGaterTestSuite) TestPeerScoreConstants() { ...@@ -37,7 +37,7 @@ func (testSuite *PeerGaterTestSuite) TestPeerScoreConstants() {
} }
// TestPeerGaterUpdate tests the peer gater update hook. // TestPeerGaterUpdate tests the peer gater update hook.
func (testSuite *PeerGaterTestSuite) TestPeerGaterUpdate() { func (testSuite *PeerGaterTestSuite) TestPeerGater_UpdateBansPeers() {
gater := p2p.NewPeerGater( gater := p2p.NewPeerGater(
testSuite.mockGater, testSuite.mockGater,
testSuite.logger, testSuite.logger,
...@@ -45,19 +45,32 @@ func (testSuite *PeerGaterTestSuite) TestPeerGaterUpdate() { ...@@ -45,19 +45,32 @@ func (testSuite *PeerGaterTestSuite) TestPeerGaterUpdate() {
) )
// Return an empty list of already blocked peers // Return an empty list of already blocked peers
testSuite.mockGater.On("ListBlockedPeers").Return([]peer.ID{}) testSuite.mockGater.On("ListBlockedPeers").Return([]peer.ID{}).Once()
// Mock a connection gater peer block call // Mock a connection gater peer block call
// Since the peer score is below the [PeerScoreThreshold] of -100, // Since the peer score is below the [PeerScoreThreshold] of -100,
// the [BlockPeer] method should be called // the [BlockPeer] method should be called
testSuite.mockGater.On("BlockPeer", peer.ID("peer1")).Return(nil) testSuite.mockGater.On("BlockPeer", peer.ID("peer1")).Return(nil).Once()
// The peer should initially be unblocked
testSuite.False(gater.IsBlocked(peer.ID("peer1")))
// Apply the peer gater update // Apply the peer gater update
gater.Update(peer.ID("peer1"), float64(-100)) gater.Update(peer.ID("peer1"), float64(-101))
// The peer should be considered blocked
testSuite.True(gater.IsBlocked(peer.ID("peer1")))
// Now let's unblock the peer
testSuite.mockGater.On("UnblockPeer", peer.ID("peer1")).Return(nil).Once()
gater.Update(peer.ID("peer1"), float64(0))
// The peer should be considered unblocked
testSuite.False(gater.IsBlocked(peer.ID("peer1")))
} }
// TestPeerGaterUpdateNoBanning tests the peer gater update hook without banning set // TestPeerGaterUpdateNoBanning tests the peer gater update hook without banning set
func (testSuite *PeerGaterTestSuite) TestPeerGaterUpdateNoBanning() { func (testSuite *PeerGaterTestSuite) TestPeerGater_UpdateNoBanning() {
gater := p2p.NewPeerGater( gater := p2p.NewPeerGater(
testSuite.mockGater, testSuite.mockGater,
testSuite.logger, testSuite.logger,
...@@ -68,5 +81,15 @@ func (testSuite *PeerGaterTestSuite) TestPeerGaterUpdateNoBanning() { ...@@ -68,5 +81,15 @@ func (testSuite *PeerGaterTestSuite) TestPeerGaterUpdateNoBanning() {
testSuite.mockGater.On("ListBlockedPeers").Return([]peer.ID{}) testSuite.mockGater.On("ListBlockedPeers").Return([]peer.ID{})
// Notice: [BlockPeer] should not be called since banning is not enabled // Notice: [BlockPeer] should not be called since banning is not enabled
// even though the peer score is way below the [PeerScoreThreshold] of -100
gater.Update(peer.ID("peer1"), float64(-100000)) gater.Update(peer.ID("peer1"), float64(-100000))
// The peer should be unblocked
testSuite.False(gater.IsBlocked(peer.ID("peer1")))
// Make sure that if we then "unblock" the peer, nothing happens
gater.Update(peer.ID("peer1"), float64(0))
// The peer should still be unblocked
testSuite.False(gater.IsBlocked(peer.ID("peer1")))
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment