erigontech / erigontech/erigon

txpool: deduplicate addTxns and addTxnsOnNewBlock

Open
#20,998 9 comments 0 reactions 1 assignee Claimed by @yperbasis View on GitHub
tech debt reduction
Dominant language
Go
Stars
3.6k
Forks
1.5k
Avg merge
1d 16h
Merged PRs (30d)
455

Description

The `// TODO: Looks like a copy of the above` comment at [`txnprovider/txpool/pool.go:1520`](https://github.com/erigontech/erigon/blob/main/txnprovider/txpool/pool.go#L1520) has been outstanding since 2023 (added in adef9719957). The two functions — `addTxns` (used by `AddLocalTxns` and `processRemoteTxns`) and `addTxnsOnNewBlock` (used by `OnNewBlock` for unwound reorg txs) — share the bulk of their structure but diverge in several specifics, which makes the duplication non-trivial to collapse.

## Material differences today

| | `addTxns` | `addTxnsOnNewBlock` |
|---|---|---|
| Return value | `(Announcements, []DiscardReason, error)` | `(Announcements, error)` |
| Duplicate-hash handling | Records `DuplicateHash`; pokes rebroadcast for stuck local txs | Silently skips |
| `addLocked` failure | Records reason; does **not** call `discardLocked` | Calls `discardLocked` |
| Sender source | New-tx senders only | New-tx senders **plus** `stateChanges.ChangeBatch` UPSERTs **plus** queued-tx senders (added in #20996) |
| `senderLastActivity` | Initialized to `blockNum` only if absent (preserve existing) | Reset to `blockNum` for state-change senders; **not touched** for queued-only senders (#20996) |
| `promote()` | Called inline | Called by `OnNewBlock` after this function returns |
| `collect` parameter | Yes | No |

The shared scaffolding is the new-tx loop (`byHash` check → `newMetaTxn` → `addLocked` → collect senderID), the per-sender `senders.info` + `onSenderStateChange` loop, and the announcements bookkeeping.

## Why this is worth doing now

#20996 added the queued-sender re-evaluation pass to `addTxnsOnNewBlock` only. Strictly speaking the same race that pass exists to recover from can also be hit by `processRemoteTxns` (which goes through `addTxns`) — though in practice the next `OnNewBlock` covers it. A shared helper would make it natural to have one canonical re-evaluation site rather than two slightly-different ones, and would make future invariants easier to keep consistent across both paths.

It would also clear the TODO that's been sitting in the file for two years.

## Suggested approach

Extract a `addTxnsCore` (or similar) helper that takes the new txs and a set of additional senders to re-evaluate, plus a small struct of behavior knobs:

- whether duplicate hashes should produce `DiscardReasons` or be silent;
- whether `addLocked` failure should call `discardLocked`;
- the `senderLastActivity` policy (initialize-if-absent vs reset);
- whether to call `promote()` inline.

`addTxns` and `addTxnsOnNewBlock` then become thin wrappers that compose the additional senders (stateChanges UPSERTs + queued-tx senders for the OnNewBlock path) and pass the appropriate behavior flags. The existing test suite (especially `TestStalePendingEvictionViaMineNonce`, `TestQueuedTxnPromotedAfterStaleAddLocal`, the unwind-related tests) should catch regressions in the consolidation.

Not urgent — pure refactor with no behavior change intended. Picking up "good first issue" only if someone is already comfortable with this corner of the txpool, since the divergences are subtle.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.