mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-02 04:58:38 +00:00
eth/txtracker: prevent disconnected peers from leaking back into stats
NotifyPeerDrop deleted t.peers[peer] but left t.txs entries pointing to that peer. When those txs later finalized, checkFinalization recreated the peer entry, and the EMA loop decayed it forever. Fix: create peer entries in NotifyAccepted (when txs are first accepted), not in handleChainHead or checkFinalization. Both chain event handlers now skip peers with no entry — disconnected peers whose entries were deleted by NotifyPeerDrop stay deleted.
This commit is contained in:
parent
7f1720b3dc
commit
e2b620ab44
2 changed files with 14 additions and 11 deletions
|
|
@ -131,6 +131,10 @@ func (t *Tracker) NotifyAccepted(peer string, hashes []common.Hash) {
|
||||||
t.txs[hash] = peer
|
t.txs[hash] = peer
|
||||||
t.order = append(t.order, hash)
|
t.order = append(t.order, hash)
|
||||||
}
|
}
|
||||||
|
// Ensure the delivering peer has a stats entry.
|
||||||
|
if len(hashes) > 0 && t.peers[peer] == nil {
|
||||||
|
t.peers[peer] = &peerStats{}
|
||||||
|
}
|
||||||
// Evict oldest entries if over capacity.
|
// Evict oldest entries if over capacity.
|
||||||
for len(t.txs) > maxTracked {
|
for len(t.txs) > maxTracked {
|
||||||
oldest := t.order[0]
|
oldest := t.order[0]
|
||||||
|
|
@ -192,12 +196,9 @@ func (t *Tracker) handleChainHead(ev core.ChainHeadEvent) {
|
||||||
blockIncl[peer]++
|
blockIncl[peer]++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure peers with inclusions in this block have entries.
|
// Only credit peers that are still tracked (not disconnected).
|
||||||
for peer := range blockIncl {
|
// Don't create entries for unknown peers — they may have been
|
||||||
if t.peers[peer] == nil {
|
// removed by NotifyPeerDrop and should not be resurrected.
|
||||||
t.peers[peer] = &peerStats{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Update EMA for all tracked peers (decay inactive ones).
|
// Update EMA for all tracked peers (decay inactive ones).
|
||||||
for peer, ps := range t.peers {
|
for peer, ps := range t.peers {
|
||||||
ps.recentIncluded = (1-emaAlpha)*ps.recentIncluded + emaAlpha*float64(blockIncl[peer])
|
ps.recentIncluded = (1-emaAlpha)*ps.recentIncluded + emaAlpha*float64(blockIncl[peer])
|
||||||
|
|
@ -231,8 +232,7 @@ func (t *Tracker) checkFinalization() {
|
||||||
}
|
}
|
||||||
ps := t.peers[peer]
|
ps := t.peers[peer]
|
||||||
if ps == nil {
|
if ps == nil {
|
||||||
ps = &peerStats{}
|
continue // peer disconnected, skip credit
|
||||||
t.peers[peer] = ps
|
|
||||||
}
|
}
|
||||||
ps.finalized++
|
ps.finalized++
|
||||||
credited++
|
credited++
|
||||||
|
|
|
||||||
|
|
@ -110,10 +110,13 @@ func TestNotifyReceived(t *testing.T) {
|
||||||
txs := []*types.Transaction{makeTx(1), makeTx(2), makeTx(3)}
|
txs := []*types.Transaction{makeTx(1), makeTx(2), makeTx(3)}
|
||||||
tr.NotifyAccepted("peerA", hashTxs(txs))
|
tr.NotifyAccepted("peerA", hashTxs(txs))
|
||||||
|
|
||||||
// No chain events yet — stats should be empty.
|
// No chain events yet — peer entry exists but with zero stats.
|
||||||
stats := tr.GetAllPeerStats()
|
stats := tr.GetAllPeerStats()
|
||||||
if len(stats) != 0 {
|
if stats["peerA"].Finalized != 0 {
|
||||||
t.Fatalf("expected empty stats before any chain events, got %d peers", len(stats))
|
t.Fatalf("expected zero Finalized before chain events, got %d", stats["peerA"].Finalized)
|
||||||
|
}
|
||||||
|
if stats["peerA"].RecentIncluded != 0 {
|
||||||
|
t.Fatalf("expected zero RecentIncluded before chain events, got %f", stats["peerA"].RecentIncluded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue