mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-02 13:08:41 +00:00
eth: drop PeerInclusionStats wrapper and use txtracker.PeerStats directly
PeerInclusionStats was declared identically to txtracker.PeerStats as a decoupling abstraction: any stats provider could implement the dropper's callback by returning this shape. In practice there's one provider and the two types were kept in sync by a rote copy adapter in backend.go. Delete PeerInclusionStats, have the dropper consume txtracker.PeerStats directly via getPeerStatsFunc. backend.go now passes txTracker.GetAllPeerStats as the callback with no adapter. If a second stats provider ever appears, the abstraction can come back; until then, one fewer type and 8 fewer lines of ceremony.
This commit is contained in:
parent
69a7baefd8
commit
1f2ebc5d59
3 changed files with 33 additions and 46 deletions
|
|
@ -463,14 +463,7 @@ func (s *Ethereum) Start() error {
|
||||||
s.handler.txTracker.Start(s.blockchain)
|
s.handler.txTracker.Start(s.blockchain)
|
||||||
|
|
||||||
// Start the connection manager with inclusion-based peer protection.
|
// Start the connection manager with inclusion-based peer protection.
|
||||||
s.dropper.Start(s.p2pServer, func() bool { return !s.Synced() }, func() map[string]PeerInclusionStats {
|
s.dropper.Start(s.p2pServer, func() bool { return !s.Synced() }, s.handler.txTracker.GetAllPeerStats)
|
||||||
stats := s.handler.txTracker.GetAllPeerStats()
|
|
||||||
result := make(map[string]PeerInclusionStats, len(stats))
|
|
||||||
for id, ps := range stats {
|
|
||||||
result[id] = PeerInclusionStats{Finalized: ps.Finalized, RecentIncluded: ps.RecentIncluded}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
// start log indexer
|
// start log indexer
|
||||||
s.filterMaps.Start()
|
s.filterMaps.Start()
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/txtracker"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
@ -58,30 +59,22 @@ var (
|
||||||
dropSkipped = metrics.NewRegisteredMeter("eth/dropper/skipped", nil)
|
dropSkipped = metrics.NewRegisteredMeter("eth/dropper/skipped", nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// PeerInclusionStats holds the per-peer inclusion data needed by the dropper
|
|
||||||
// to decide which peers to protect. Any stats provider (e.g. txtracker) can
|
|
||||||
// implement getPeerInclusionStatsFunc by returning this struct per peer ID.
|
|
||||||
type PeerInclusionStats struct {
|
|
||||||
Finalized int64 // Cumulative finalized inclusions attributed to this peer
|
|
||||||
RecentIncluded float64 // EMA of per-block inclusions (0 if not tracked)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback type to get per-peer inclusion statistics.
|
// Callback type to get per-peer inclusion statistics.
|
||||||
type getPeerInclusionStatsFunc func() map[string]PeerInclusionStats
|
type getPeerStatsFunc func() map[string]txtracker.PeerStats
|
||||||
|
|
||||||
// protectionCategory defines a peer scoring function and the fraction of peers
|
// protectionCategory defines a peer scoring function and the fraction of peers
|
||||||
// to protect per inbound/dialed category. Multiple categories are unioned.
|
// to protect per inbound/dialed category. Multiple categories are unioned.
|
||||||
type protectionCategory struct {
|
type protectionCategory struct {
|
||||||
name string
|
name string
|
||||||
score func(PeerInclusionStats) float64
|
score func(txtracker.PeerStats) float64
|
||||||
frac float64 // fraction of max peers to protect (0.0–1.0)
|
frac float64 // fraction of max peers to protect (0.0–1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// protectionCategories is the list of protection criteria. Each category
|
// protectionCategories is the list of protection criteria. Each category
|
||||||
// independently selects its top-N peers per pool; the union is protected.
|
// independently selects its top-N peers per pool; the union is protected.
|
||||||
var protectionCategories = []protectionCategory{
|
var protectionCategories = []protectionCategory{
|
||||||
{"total-finalized", func(s PeerInclusionStats) float64 { return float64(s.Finalized) }, inclusionProtectionFrac},
|
{"total-finalized", func(s txtracker.PeerStats) float64 { return float64(s.Finalized) }, inclusionProtectionFrac},
|
||||||
{"recent-included", func(s PeerInclusionStats) float64 { return s.RecentIncluded }, inclusionProtectionFrac},
|
{"recent-included", func(s txtracker.PeerStats) float64 { return s.RecentIncluded }, inclusionProtectionFrac},
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropper monitors the state of the peer pool and introduces churn by
|
// dropper monitors the state of the peer pool and introduces churn by
|
||||||
|
|
@ -107,7 +100,7 @@ type dropper struct {
|
||||||
maxInboundPeers int // maximum number of inbound peers
|
maxInboundPeers int // maximum number of inbound peers
|
||||||
peersFunc getPeersFunc
|
peersFunc getPeersFunc
|
||||||
syncingFunc getSyncingFunc
|
syncingFunc getSyncingFunc
|
||||||
peerStatsFunc getPeerInclusionStatsFunc // optional: inclusion stats for protection
|
peerStatsFunc getPeerStatsFunc // optional: inclusion stats for protection
|
||||||
|
|
||||||
// peerDropTimer introduces churn if we are close to limit capacity.
|
// peerDropTimer introduces churn if we are close to limit capacity.
|
||||||
// We handle Dialed and Inbound connections separately
|
// We handle Dialed and Inbound connections separately
|
||||||
|
|
@ -139,7 +132,7 @@ func newDropper(maxDialPeers, maxInboundPeers int) *dropper {
|
||||||
|
|
||||||
// Start the dropper. peerStatsFunc is optional (nil disables inclusion
|
// Start the dropper. peerStatsFunc is optional (nil disables inclusion
|
||||||
// protection).
|
// protection).
|
||||||
func (cm *dropper) Start(srv *p2p.Server, syncingFunc getSyncingFunc, peerStatsFunc getPeerInclusionStatsFunc) {
|
func (cm *dropper) Start(srv *p2p.Server, syncingFunc getSyncingFunc, peerStatsFunc getPeerStatsFunc) {
|
||||||
cm.peersFunc = srv.Peers
|
cm.peersFunc = srv.Peers
|
||||||
cm.syncingFunc = syncingFunc
|
cm.syncingFunc = syncingFunc
|
||||||
cm.peerStatsFunc = peerStatsFunc
|
cm.peerStatsFunc = peerStatsFunc
|
||||||
|
|
@ -234,7 +227,7 @@ func (cm *dropper) protectedPeers(peers []*p2p.Peer) map[*p2p.Peer]bool {
|
||||||
// Factored from protectedPeers so tests can exercise the per-pool
|
// Factored from protectedPeers so tests can exercise the per-pool
|
||||||
// selection logic without needing to construct direction-flagged
|
// selection logic without needing to construct direction-flagged
|
||||||
// *p2p.Peer instances (which require unexported p2p types).
|
// *p2p.Peer instances (which require unexported p2p types).
|
||||||
func protectedPeersByPool(inbound, dialed []*p2p.Peer, stats map[string]PeerInclusionStats) map[*p2p.Peer]bool {
|
func protectedPeersByPool(inbound, dialed []*p2p.Peer, stats map[string]txtracker.PeerStats) map[*p2p.Peer]bool {
|
||||||
result := make(map[*p2p.Peer]bool)
|
result := make(map[*p2p.Peer]bool)
|
||||||
// protectPool selects the top-frac peers from pool by score and adds them to result.
|
// protectPool selects the top-frac peers from pool by score and adds them to result.
|
||||||
protectPool := func(pool []*p2p.Peer, score func(*p2p.Peer) float64, frac float64) {
|
protectPool := func(pool []*p2p.Peer, score func(*p2p.Peer) float64, frac float64) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/eth/txtracker"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
)
|
)
|
||||||
|
|
@ -35,7 +36,7 @@ func makePeers(n int) []*p2p.Peer {
|
||||||
|
|
||||||
func TestProtectedPeersNoStats(t *testing.T) {
|
func TestProtectedPeersNoStats(t *testing.T) {
|
||||||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||||||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return nil }
|
cm.peerStatsFunc = func() map[string]txtracker.PeerStats { return nil }
|
||||||
|
|
||||||
peers := makePeers(10)
|
peers := makePeers(10)
|
||||||
protected := cm.protectedPeers(peers)
|
protected := cm.protectedPeers(peers)
|
||||||
|
|
@ -46,8 +47,8 @@ func TestProtectedPeersNoStats(t *testing.T) {
|
||||||
|
|
||||||
func TestProtectedPeersEmptyStats(t *testing.T) {
|
func TestProtectedPeersEmptyStats(t *testing.T) {
|
||||||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||||||
cm.peerStatsFunc = func() map[string]PeerInclusionStats {
|
cm.peerStatsFunc = func() map[string]txtracker.PeerStats {
|
||||||
return map[string]PeerInclusionStats{}
|
return map[string]txtracker.PeerStats{}
|
||||||
}
|
}
|
||||||
|
|
||||||
peers := makePeers(10)
|
peers := makePeers(10)
|
||||||
|
|
@ -62,11 +63,11 @@ func TestProtectedPeersTopPeer(t *testing.T) {
|
||||||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||||||
|
|
||||||
peers := makePeers(20)
|
peers := makePeers(20)
|
||||||
stats := make(map[string]PeerInclusionStats)
|
stats := make(map[string]txtracker.PeerStats)
|
||||||
stats[peers[0].ID().String()] = PeerInclusionStats{Finalized: 100}
|
stats[peers[0].ID().String()] = txtracker.PeerStats{Finalized: 100}
|
||||||
stats[peers[1].ID().String()] = PeerInclusionStats{RecentIncluded: 5.0}
|
stats[peers[1].ID().String()] = txtracker.PeerStats{RecentIncluded: 5.0}
|
||||||
|
|
||||||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
cm.peerStatsFunc = func() map[string]txtracker.PeerStats { return stats }
|
||||||
|
|
||||||
protected := cm.protectedPeers(peers)
|
protected := cm.protectedPeers(peers)
|
||||||
if len(protected) != 2 {
|
if len(protected) != 2 {
|
||||||
|
|
@ -84,11 +85,11 @@ func TestProtectedPeersZeroScore(t *testing.T) {
|
||||||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||||||
|
|
||||||
peers := makePeers(10)
|
peers := makePeers(10)
|
||||||
stats := make(map[string]PeerInclusionStats)
|
stats := make(map[string]txtracker.PeerStats)
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
stats[p.ID().String()] = PeerInclusionStats{}
|
stats[p.ID().String()] = txtracker.PeerStats{}
|
||||||
}
|
}
|
||||||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
cm.peerStatsFunc = func() map[string]txtracker.PeerStats { return stats }
|
||||||
|
|
||||||
protected := cm.protectedPeers(peers)
|
protected := cm.protectedPeers(peers)
|
||||||
if len(protected) != 0 {
|
if len(protected) != 0 {
|
||||||
|
|
@ -101,10 +102,10 @@ func TestProtectedPeersOverlap(t *testing.T) {
|
||||||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||||||
|
|
||||||
peers := makePeers(20)
|
peers := makePeers(20)
|
||||||
stats := make(map[string]PeerInclusionStats)
|
stats := make(map[string]txtracker.PeerStats)
|
||||||
stats[peers[0].ID().String()] = PeerInclusionStats{Finalized: 100, RecentIncluded: 5.0}
|
stats[peers[0].ID().String()] = txtracker.PeerStats{Finalized: 100, RecentIncluded: 5.0}
|
||||||
|
|
||||||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
cm.peerStatsFunc = func() map[string]txtracker.PeerStats { return stats }
|
||||||
|
|
||||||
protected := cm.protectedPeers(peers)
|
protected := cm.protectedPeers(peers)
|
||||||
if len(protected) != 1 {
|
if len(protected) != 1 {
|
||||||
|
|
@ -137,12 +138,12 @@ func TestProtectedByPoolPerPoolTopN(t *testing.T) {
|
||||||
dialed[i] = p2p.NewPeer(id, fmt.Sprintf("dialed%d", i), nil)
|
dialed[i] = p2p.NewPeer(id, fmt.Sprintf("dialed%d", i), nil)
|
||||||
}
|
}
|
||||||
// Strictly increasing scores: highest wins in each pool.
|
// Strictly increasing scores: highest wins in each pool.
|
||||||
stats := make(map[string]PeerInclusionStats)
|
stats := make(map[string]txtracker.PeerStats)
|
||||||
for i, p := range inbound {
|
for i, p := range inbound {
|
||||||
stats[p.ID().String()] = PeerInclusionStats{Finalized: int64(1 + i)}
|
stats[p.ID().String()] = txtracker.PeerStats{Finalized: int64(1 + i)}
|
||||||
}
|
}
|
||||||
for i, p := range dialed {
|
for i, p := range dialed {
|
||||||
stats[p.ID().String()] = PeerInclusionStats{Finalized: int64(1 + i)}
|
stats[p.ID().String()] = txtracker.PeerStats{Finalized: int64(1 + i)}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected := protectedPeersByPool(inbound, dialed, stats)
|
protected := protectedPeersByPool(inbound, dialed, stats)
|
||||||
|
|
@ -172,10 +173,10 @@ func TestProtectedByPoolCrossCategoryOverlap(t *testing.T) {
|
||||||
// Finalized winners: P2 (tie-broken-ok), P0
|
// Finalized winners: P2 (tie-broken-ok), P0
|
||||||
// RecentIncluded winners: P2, P1
|
// RecentIncluded winners: P2, P1
|
||||||
// Union: {P0, P1, P2}.
|
// Union: {P0, P1, P2}.
|
||||||
stats := make(map[string]PeerInclusionStats)
|
stats := make(map[string]txtracker.PeerStats)
|
||||||
stats[dialed[0].ID().String()] = PeerInclusionStats{Finalized: 100, RecentIncluded: 0}
|
stats[dialed[0].ID().String()] = txtracker.PeerStats{Finalized: 100, RecentIncluded: 0}
|
||||||
stats[dialed[1].ID().String()] = PeerInclusionStats{Finalized: 0, RecentIncluded: 5.0}
|
stats[dialed[1].ID().String()] = txtracker.PeerStats{Finalized: 0, RecentIncluded: 5.0}
|
||||||
stats[dialed[2].ID().String()] = PeerInclusionStats{Finalized: 200, RecentIncluded: 10.0}
|
stats[dialed[2].ID().String()] = txtracker.PeerStats{Finalized: 200, RecentIncluded: 10.0}
|
||||||
|
|
||||||
protected := protectedPeersByPool(nil, dialed, stats)
|
protected := protectedPeersByPool(nil, dialed, stats)
|
||||||
|
|
||||||
|
|
@ -202,13 +203,13 @@ func TestProtectedByPoolPerPoolIndependence(t *testing.T) {
|
||||||
id := enode.ID{byte(100 + i)}
|
id := enode.ID{byte(100 + i)}
|
||||||
dialed[i] = p2p.NewPeer(id, fmt.Sprintf("dialed%d", i), nil)
|
dialed[i] = p2p.NewPeer(id, fmt.Sprintf("dialed%d", i), nil)
|
||||||
}
|
}
|
||||||
stats := make(map[string]PeerInclusionStats)
|
stats := make(map[string]txtracker.PeerStats)
|
||||||
// Every inbound peer outscores every dialed peer.
|
// Every inbound peer outscores every dialed peer.
|
||||||
for i, p := range inbound {
|
for i, p := range inbound {
|
||||||
stats[p.ID().String()] = PeerInclusionStats{Finalized: int64(1000 + i)}
|
stats[p.ID().String()] = txtracker.PeerStats{Finalized: int64(1000 + i)}
|
||||||
}
|
}
|
||||||
for i, p := range dialed {
|
for i, p := range dialed {
|
||||||
stats[p.ID().String()] = PeerInclusionStats{Finalized: int64(1 + i)}
|
stats[p.ID().String()] = txtracker.PeerStats{Finalized: int64(1 + i)}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected := protectedPeersByPool(inbound, dialed, stats)
|
protected := protectedPeersByPool(inbound, dialed, stats)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue