Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/txpool: remove locals-tracking from pools (part 2) #30559

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

holiman
Copy link
Contributor

@holiman holiman commented Oct 8, 2024

Replaces #29297, descendant from #27535


This PR removes locals as a concept from transaction pools. Therefore, the pool now acts as very a good simulation/approximation of how our peers' pools behave. What this PR does instead, is implement a locals-tracker, which basically is a little thing which, from time to time, asks the pool "did you forget this transaction?". If it did, the tracker resubmits it.

If the txpool had forgotten it, chances are that the peers had also forgotten it. It will be propagated again.

Doing this change means that we can simplify the pool internals, quite a lot.

The semantics of local

Historically, there has been two features, or usecases, that has been combined into the concept of locals.

  1. "I want my local node to remember this transaction indefinitely, and resubmit to the network occasionally"
  2. "I want this (valid) transaction included to be top-prio for my miner"

This PR splits these features up, let's call it 1: local and 2: prio. The prio is not actually individual transaction, but rather a set of addresses to prioritize.
The attribute local means it will be tracked, and prio means it will be prioritized by miner.

For local: anything transaction received via the RPC is marked as local, and tracked by the tracker.
For prio: any transactions from this sender is included first, when building a block. The existing commandline-flag --txpool.locals sets the set of prio addresses.

@holiman holiman force-pushed the local_pool_pt2 branch 2 times, most recently from cffa982 to 165e892 Compare October 11, 2024 07:19
@holiman holiman marked this pull request as ready for review October 11, 2024 07:58
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// Package legacypool implements the normal EVM execution transaction pool.
package tracking
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rename it to tracker or txtracker?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

core/txpool/tracking/tx_tracker.go Outdated Show resolved Hide resolved
)
for sender, txs := range tracker.byAddr {
stales := txs.Forward(tracker.pool.Nonce(sender))
// Wipe the stales
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stales refers to the transactions with nonce lower than tracker.pool.Nonce(sender).
However, tracker.pool.Nonce(sender) is the current pending nonce of the pool, including the executable txs applied.

Shouldn't we use statedb(latest_chain_head).Nonce(sender) instead?

Furthermore, do we care about the reorg here? should we care about the reorg here?

  • If we don't care, some local transactions might be dropped first and reorg occurs later, they won't be able to be resubmitted;
  • If we care, the "stale" local transactions should somehow be kept for a few (maybe a few blocks) and try to resubmit them if reorg occurs;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use statedb(latest_chain_head).Nonce(sender) instead?

Yes, good observation!

Furthermore, do we care about the reorg here? should we care about the reorg here?

* If we don't care, some local transactions might be dropped first and reorg occurs later, they won't be able to be resubmitted;

* If we care, the "stale" local transactions should somehow be kept for a few (maybe a few blocks) and try to resubmit them if reorg occurs;

Once it's been included in a block, I think it can be untracked. It will most likely be picked up again if it's reorged. 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use statedb(latest_chain_head).Nonce(sender) instead?

Yes, good observation!

No wait. Our job here is to make the pool accept the tx. If the pool already has nonce=5 from this sender, then we can consider nonce=5 as stale, and not worry about it anymore. I guess the caveat here is that it might be a replacement... :/

core/txpool/tracking/tx_tracker.go Outdated Show resolved Hide resolved
core/txpool/tracking/tx_tracker.go Outdated Show resolved Hide resolved
eth/backend.go Outdated Show resolved Hide resolved
core/txpool/blobpool/blobpool.go Show resolved Hide resolved
core/txpool/tracking/tx_tracker.go Outdated Show resolved Hide resolved
core/txpool/legacypool/legacypool.go Show resolved Hide resolved
type lookup struct {
slots int
lock sync.RWMutex
locals map[common.Hash]*types.Transaction
remotes map[common.Hash]*types.Transaction
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can do rename in a following pr, just want to mention here

@rjl493456442
Copy link
Member

I think the general idea is good and it's a good direction to "distribute the complexity" of txpool graduately.

@holiman holiman force-pushed the local_pool_pt2 branch 2 times, most recently from e61a7cf to 82c9789 Compare November 4, 2024 08:59
@holiman
Copy link
Contributor Author

holiman commented Nov 4, 2024

TestUnderpricing is flaky in this PR. Not sure why

@holiman
Copy link
Contributor Author

holiman commented Dec 12, 2024

TestUnderpricing is flaky in this PR. Not sure why

Fixed (crosses fingers)

@MariusVanDerWijden
Copy link
Member

I'm wondering whether we should save users from shooting themselves in the foot by only allowing non-blob transactions in the tracker

@MariusVanDerWijden
Copy link
Member

Overall this PR looks very good to me. I love the concept and implementing it as an additional service is genius. I added some minor comments where this PR changes the behavior of the transaction pool, they boil down to the following:

  • previously local blob transactions were not tracked, now they are tracked and journalled
  • Invalid transactions might make it into the tracker, previously only txs that passed the basic validation would be tracked
  • Only addresses that are configured on startup will get priority, previously all txs from accounts that were sent via RPC got prio
  • Remote txs from a sender that once sent via the RPC were still considered local and tracked, now they are not anymore

I'm not saying that all of those are issues that need to be fixed necessarily, I think we should discuss them though before merging this PR to make sure we don't rug users

@holiman
Copy link
Contributor Author

holiman commented Jan 3, 2025 via email

@holiman
Copy link
Contributor Author

holiman commented Jan 7, 2025

Thanks @MariusVanDerWijden, I have addressed the points you raised. Also rebased on master

core/txpool/legacypool/legacypool.go Show resolved Hide resolved
core/txpool/tracker/tx_tracker.go Outdated Show resolved Hide resolved
@fjl fjl removed the status:triage label Jan 7, 2025
Copy link
Member

@MariusVanDerWijden MariusVanDerWijden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zsfelfoldi zsfelfoldi self-assigned this Jan 22, 2025
Copy link
Contributor

@zsfelfoldi zsfelfoldi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the MinTip question, otherwise looks good.

@@ -615,9 +544,6 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
MaxSize: txMaxSize,
MinTip: pool.gasTip.Load().ToBig(),
}
if local {
opts.MinTip = new(big.Int)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly that txs from prioritized addresses are now also subject to the minimum tip threshold and there is no way to force accept certain txs with low tip? If so then are you sure this is the right behavior? I would think that we should still set MinTip to zero if it's signed by one of the prio address set since the user's intention there was to include those txs whenever possible regardless of low price.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The transaction pool does not have a concept of local transactions (or prioritized addresses).
The mintip enforcement or exception is instead a concern for miner, during block building

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But to answer the question. See worker.go

	miner.confMu.RLock()
	tip := miner.config.GasPrice
	prio := miner.prio
	miner.confMu.RUnlock()

	// Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees
	filter := txpool.PendingFilter{
		MinTip: uint256.MustFromBig(tip),
	}

So yes, the filter which we use to pick them from the pool does use the mintip indiscriminately, i.e the miner.GasPrice. After the mintip-filtering, the prio set is included first.

Copy link
Member

@rjl493456442 rjl493456442 Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand the concern from Zsolt.

Originally, the local transactions can bypass the minTip restrictions. Now, they will always be rejected by txpool, regardless of it's local or not.

With the local tracker, the local transactions will be persisted and be resubmitted all the time, but none of the attempt will succeed.

While It's a breaking change, I think it's acceptable. The consequence is local transactions will be rejected and won't be propagated into the networks if minTip is not reached.

The semantic of "local" in txpool has been changed, it will try to best effort to resubmit the tx into pool, but with no guarantee, e.g. if tip is too low, txpool is full, etc, it will fail

@@ -279,7 +279,10 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
}

func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
return b.eth.txPool.Add([]*types.Transaction{signedTx}, true, false)[0]
if locals := b.eth.localTxTracker; locals != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why copy it to locals?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean -- it's the api backend, it's where we tag them as locals? Did I do something wrong?

}
tracker.byAddr[addr].Put(tx)
if tracker.journal != nil {
_ = tracker.journal.insert(tx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, why the _ =? Does it do anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah no, it silences some linters which complain about ignored returnvalues. We don't use those linters though, so it's not how we usually write it. Don't know why it crept in

@@ -234,6 +237,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
legacyPool := legacypool.New(config.TxPool, eth.blockchain)

eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, blobPool})

if !config.TxPool.NoLocals {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to move the local tracker into the txpool.Pool?

I think this is an internal mechanism of the transaction pool and would be better “hidden” under the hood.

Copy link
Member

@rjl493456442 rjl493456442 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM!

I think it's a good change and simplify the internal logic a lot!
I would prefer to deploy it on one of our bootnoode, ensuring nothing is broken.

Also, we must state the breaking change clearly, that for the transactions with low gas price, they won't be accepted/propagated regardless of whether they are local or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants