Commit graph

10 commits

Author SHA1 Message Date
Csaba Kiraly
7f1720b3dc eth/txtracker: compact FIFO order slice to prevent memory leak
order = order[1:] reslices without releasing the backing array.
After N total insertions the array retains N hashes (32 bytes each)
but only the last maxTracked are live. On a long-running node
processing ~100 txs/s this leaks ~275 MB/day.

Compact by copying to a fresh array when capacity exceeds 2×maxTracked.
2026-04-10 16:40:08 +02:00
Csaba Kiraly
f66323d768 eth: add LGPL copyright headers to new files
Add the standard go-ethereum LGPL header to tracker.go,
tracker_test.go, and dropper_test.go.
2026-04-10 12:18:56 +02:00
Csaba Kiraly
6d53acfa22 eth/txtracker: prune peer stats on disconnect
Peer stats were never pruned, so the peers map grew with every peer
ever seen. The EMA decay loop and stats copy iterated all historical
peers on every block/query.

Add NotifyPeerDrop(peer) that deletes the peer's stats entry. Called
from handler.unregisterPeer alongside txFetcher.Drop.
2026-04-10 10:35:28 +02:00
Csaba Kiraly
3785d43db4 eth/txtracker: fetch head block by hash to avoid reorg-stale EMA
handleChainHead fetched the block by number only. If the tracker
goroutine lagged and that height was reorged before processing,
the EMA was computed from the wrong canonical block.

Use GetBlock(hash, number) with the header hash from the event to
fetch the exact block the event refers to, not whatever is currently
canonical at that height.
2026-04-10 10:34:33 +02:00
Csaba Kiraly
a1a5d73324 eth: only record deliverers for pool-accepted transactions
NotifyReceived was called before pool validation, allowing a peer
to claim deliverer credit by replaying already-included txs or
sending invalid packets.

Rename to NotifyAccepted (takes hashes, not full txs). Call it from
a new enqueueAndTrack helper in handler_eth.go that runs after
Enqueue and checks pool.Has to identify accepted txs. Only accepted
txs are credited to the delivering peer.
2026-04-10 10:33:36 +02:00
Csaba Kiraly
e99330b2bc eth/txtracker: seed lastFinalNum at startup to prevent genesis backfill
lastFinalNum started at 0, so the first checkFinalization after
startup iterated from block 1 to the current finalized head (~20M
blocks on mainnet) under the mutex, stalling the tracker and
potentially awarding bogus credit for ancient txs whose hashes
happened to match recently-received ones.

Seed lastFinalNum from chain.CurrentFinalBlock() in Start() so only
blocks finalized after startup are processed.
2026-04-10 10:26:33 +02:00
Csaba Kiraly
8bfddee2ea eth: add tests for txtracker and dropper peer protection
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).
2026-04-10 09:07:38 +02:00
Csaba Kiraly
58556173f6 eth: improve package and type documentation for txtracker and dropper
Expand the txtracker package doc to describe the tracking flow
(NotifyReceived → chain head → finalization → peer credit) and its
role as stats provider for the dropper.

Rewrite the dropper struct comment to document the full behavior
including the inclusion-based peer protection: two scoring categories
(total finalized + recent EMA), top 10% per pool, union of protected
sets.
2026-04-10 08:59:09 +02:00
Csaba Kiraly
98ffc7bd37 eth: use finalized count for total protection, keep EMA on inclusions
Change the long-term protection category from total inclusions to
total finalized inclusions. Finalized txs are harder to game (require
actual block finality, not just inclusion) and represent confirmed
on-chain value.

The recent-inclusion EMA stays on chain head inclusions for
responsiveness — a peer delivering txs that appear in the latest
blocks gets quick protection without waiting for finalization.

The tracker now checks CurrentFinalBlock() on each chain head event
and credits delivering peers for all newly finalized blocks since
the last check.
2026-04-10 08:56:32 +02:00
Csaba Kiraly
9f2575efeb eth/txtracker: add minimal tracker as inclusion stats provider
Minimal txtracker that records which peer delivered each transaction
and credits peers when their transactions appear on chain. Provides
the PeerInclusionStats needed by the dropper's protection logic.

Design:
- NotifyReceived(peer, txs): records deliverer per tx hash (called
  from handler_eth.go when tx bodies arrive via P2P)
- Subscribes to ChainHeadEvent, fetches block txs, credits the
  delivering peer for each included tx
- Per-peer EMA of recent inclusions (alpha=0.05), updated every block
- LRU eviction at 262K entries to bound memory
- Mutex-based (not channel-based) for simplicity — the hot path
  (NotifyReceived) is a fast map insert

Wired into the dropper via an adapter callback in backend.go that
converts txtracker.PeerStats to the dropper's PeerInclusionStats.
2026-04-10 08:30:21 +02:00