mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 16:59:26 +00:00
txtracker tests (7 tests): - NotifyReceived: stats empty before chain events - InclusionEMA: EMA increases on inclusion, decays on empty blocks - Finalization: Finalized counter credited after finalization - MultiplePeers: each peer credited for own txs only - FirstDelivererWins: duplicate delivery ignored - NoFinalizationCredit: no credit without finalization - EMADecay: EMA approaches zero after 30 empty blocks dropper tests (6 tests): - FilterProtectedNoStats: nil stats → all droppable - FilterProtectedEmptyStats: empty map → all droppable - FilterProtectedTopPeer: top-scored peers removed from droppable - FilterProtectedZeroScore: zero scores → no protection - FilterProtectedOverlap: peer top in both categories → counted once - FilterProtectedAllProtected: all droppable protected → empty list Also fix: create peer entries during EMA update for peers with inclusions in the current block (previously only created during finalization, so EMA was not tracked before first finalization).
125 lines
4 KiB
Go
125 lines
4 KiB
Go
package eth
|
||
|
||
import (
|
||
"fmt"
|
||
"testing"
|
||
|
||
"github.com/ethereum/go-ethereum/p2p"
|
||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||
)
|
||
|
||
func makePeers(n int) []*p2p.Peer {
|
||
peers := make([]*p2p.Peer, n)
|
||
for i := range peers {
|
||
id := enode.ID{byte(i)}
|
||
peers[i] = p2p.NewPeer(id, fmt.Sprintf("peer%d", i), nil)
|
||
}
|
||
return peers
|
||
}
|
||
|
||
func TestFilterProtectedNoStats(t *testing.T) {
|
||
// When the stats func returns nil/empty, all peers remain droppable.
|
||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return nil }
|
||
|
||
peers := makePeers(10)
|
||
result := cm.filterProtectedPeers(peers)
|
||
if len(result) != len(peers) {
|
||
t.Fatalf("expected all peers droppable with nil stats, got %d/%d", len(result), len(peers))
|
||
}
|
||
}
|
||
|
||
func TestFilterProtectedEmptyStats(t *testing.T) {
|
||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||
cm.peerStatsFunc = func() map[string]PeerInclusionStats {
|
||
return map[string]PeerInclusionStats{}
|
||
}
|
||
|
||
peers := makePeers(10)
|
||
result := cm.filterProtectedPeers(peers)
|
||
if len(result) != len(peers) {
|
||
t.Fatalf("expected all peers droppable with empty stats, got %d/%d", len(result), len(peers))
|
||
}
|
||
}
|
||
|
||
func TestFilterProtectedTopPeer(t *testing.T) {
|
||
// 20 peers, maxDialPeers=20, 10% = 2 protected per category.
|
||
// NewPeer creates non-inbound peers, so all go to dialed bucket.
|
||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||
|
||
peers := makePeers(20)
|
||
stats := make(map[string]PeerInclusionStats)
|
||
// Peer 0: top by Finalized
|
||
stats[peers[0].ID().String()] = PeerInclusionStats{Finalized: 100}
|
||
// Peer 1: top by RecentIncluded
|
||
stats[peers[1].ID().String()] = PeerInclusionStats{RecentIncluded: 5.0}
|
||
|
||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
||
|
||
result := cm.filterProtectedPeers(peers)
|
||
// 2 categories × 2 protected each = up to 4, but peers 0 and 1 are
|
||
// different so both should be removed. Other peers have zero scores.
|
||
protected := len(peers) - len(result)
|
||
if protected != 2 {
|
||
t.Fatalf("expected 2 protected peers, got %d", protected)
|
||
}
|
||
// Verify peers 0 and 1 are not in result.
|
||
for _, p := range result {
|
||
id := p.ID().String()
|
||
if id == peers[0].ID().String() || id == peers[1].ID().String() {
|
||
t.Fatalf("peer %s should be protected", id)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestFilterProtectedZeroScore(t *testing.T) {
|
||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||
|
||
peers := makePeers(10)
|
||
stats := make(map[string]PeerInclusionStats)
|
||
// All peers have zero stats.
|
||
for _, p := range peers {
|
||
stats[p.ID().String()] = PeerInclusionStats{}
|
||
}
|
||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
||
|
||
result := cm.filterProtectedPeers(peers)
|
||
if len(result) != len(peers) {
|
||
t.Fatalf("expected no protection with zero scores, got %d protected", len(peers)-len(result))
|
||
}
|
||
}
|
||
|
||
func TestFilterProtectedOverlap(t *testing.T) {
|
||
// One peer is top in both categories — should only be removed once.
|
||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||
|
||
peers := makePeers(20)
|
||
stats := make(map[string]PeerInclusionStats)
|
||
// Peer 0 is top in both.
|
||
stats[peers[0].ID().String()] = PeerInclusionStats{Finalized: 100, RecentIncluded: 5.0}
|
||
|
||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
||
|
||
result := cm.filterProtectedPeers(peers)
|
||
protected := len(peers) - len(result)
|
||
if protected != 1 {
|
||
t.Fatalf("expected 1 protected peer (overlap), got %d", protected)
|
||
}
|
||
}
|
||
|
||
func TestFilterProtectedAllProtected(t *testing.T) {
|
||
// Only 2 droppable peers, both are top by different categories.
|
||
cm := &dropper{maxDialPeers: 20, maxInboundPeers: 30}
|
||
|
||
peers := makePeers(2)
|
||
stats := make(map[string]PeerInclusionStats)
|
||
stats[peers[0].ID().String()] = PeerInclusionStats{Finalized: 100}
|
||
stats[peers[1].ID().String()] = PeerInclusionStats{RecentIncluded: 5.0}
|
||
|
||
cm.peerStatsFunc = func() map[string]PeerInclusionStats { return stats }
|
||
|
||
result := cm.filterProtectedPeers(peers)
|
||
if len(result) != 0 {
|
||
t.Fatalf("expected all peers protected, got %d droppable", len(result))
|
||
}
|
||
}
|