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
cmd, core, eth, miner: remove txpool gas price limits #14442
Conversation
core/tx_list.go
Outdated
key := newPriceKey(price) | ||
if m.items[key] == nil { | ||
m.items[key] = make(map[common.Hash]*types.Transaction) | ||
heap.Push(m.index, price) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see m.index
being incremented anywhere.... under the hood?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m.index
is a heap that contains the current price points (O(log n) complexity sorted data struct).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core/tx_list.go
Outdated
if len(m.items[key]) == 0 { | ||
m.stales++ | ||
} | ||
// If the number of stale entries reached a critical threshold (10%), re-heap and clean up |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this thrash a lot when there aren't many transaction levels? If there are 1000 transactions spread over <10 price levels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bumped the percentage to 25%, but no. Stale means that the entire price point got empty (i.e all th transactions from that point were removed). If I have 1K txs spread evenly over 10 levels, it means I need to remove at least 100tx for the first stale point to appear.
b8564a7
to
d67c6fa
Compare
if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 { | ||
return false, nil | ||
if old != nil { | ||
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+minPriceBumpPercent)), big.NewInt(100)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
old gasPrice==0
=> threshold
= 0.
old gasPrice==3
=> threshold
= 3
(with minbumppercent at 25).
Should add some handling of the lower extremes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, if the price == threshold, the new transaction is discarded.
if threshold.Cmp(tx.GasPrice()) >= 0 {
return false, nil
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, there are explicit tests testing the lower extremes :) https://github.com/ethereum/go-ethereum/pull/14442/files/d67c6fa44d9001e3f484732e44c4a6ec8a7faa3b#diff-59daeee0a5084a262da2e62280567af0R976
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, well done!
return false, nil | ||
if old != nil { | ||
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+minPriceBumpPercent)), big.NewInt(100)) | ||
if threshold.Cmp(tx.GasPrice()) >= 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this return if the value is greater than the threshold?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comparison sign is always in the same "direction" that the operations are being compared.
threshold.Cmp(tx.GasPrice()) >= 0
=== threshold >= tx.GasPrice()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's only useful if you know the operator in question! I'm used to cmp(x,y) being equivalent to (x-y). :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And that's exactly the same thing
core/tx_list.go
Outdated
// If the number of stale entries reached a critical threshold (25%), re-heap and clean up | ||
if m.stales > len(*m.index)/4 { | ||
m.stales, m.index = 0, new(priceHeap) | ||
for key, txs := range m.items { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should definitely be a separate function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
d67c6fa
to
778ee5c
Compare
@Arachnid PTAL |
@karalabe What changed? I don't see any new commits. |
Separated out that reheap function for you.
…On May 9, 2017 7:33 PM, "Nick Johnson" ***@***.***> wrote:
@karalabe <https://github.com/karalabe> What changed? I don't see any new
commits.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#14442 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAH6GevLJK5gT4GbNXNRcEdqiUjPkFEyks5r4JV0gaJpZM4NUOxc>
.
|
Historically our transaction pool enforced all transactions to meet a certain pricing threshold to accept and forward. This however is limiting the network from propagating low price transactions, even if there is some miner out there willing to process them. This PR attempts to fix this.
The motto of the PR (i.e. base design rationale): We don't care if the pool contains a ton of non-executable-because-insanely-cheap transactions, since we'll have a hard upper cap and the cheap transactions get kicked out the moment there's something more expensive arriving.
Side note: the PR reduces the default gas price from 20 Shannon to 18 Shannon. This is actually a noop since our miner code had a weird snippet that set its accepted gas limit to 90% of what thee user specified. :| I removed that.
cmd, core, eth, miner: remove txpool gas price limits
First up, our previous code was messy when it came to gas price handling and tracking local transactions as both were done both in the miner as well as the tx pool. However it makes absolutely no sense to track these in two places. If the pool already enforces the correct prices, the miner shouldn't need to care at all, just mint whatever it's given. As such, this commit drops all notions of gas price and local transactions from the miner, and whenever
miner.setGasPrice
is called, it actually sets it on the tx pool directly. This has the nice side effect that it also gets rid of acore.GasPriceChanged
event which was propagated from the miner to the pool via the event bus.The crux of the commit is the implementation of
txPricedMap
. It is a data structure that maintains the set of all transactions, grouped into price-point buckets (hash map), also featuring a heap based index to quickly find the cheapest price points. This data structure supports a few handy operations such as adding a new transaction, removing an existing one, discarding all transaction below a price threshold, discarding a number of cheapest transactions and checking if a tx is under priced or not. All these operations have special handling for local transactions, which are not subject to the pricing rules.With this data structure given, we can add it as a "virtual" layer on top of the existing transaction pool. Every time a transaction is added or removed from the pool via any existing method, it's added to this price pool too without caring about its price at all. However if the transaction pool saturates (count >= maxPending + maxQueued), any new transaction being added will first ensure it's not under priced (more expensive than the cheapest). If so, the price pool can discard the cheapest N transactions to make room for the new one.
If the user changes the minimum gas price via
miner.setGasPrice
(implicitly done when runningminer.Start
), the pool gains a new limit and enforces it immediately via capping the price pool to the new threshold, discarding any transactions priced below it.core: enforce min 10% price bump for replacement txs
Since the first commit enables users to potentially add transactions that are way below any price point accepted by miners, those transactions will get stuck in the network until they are flushed out by more expensive ones.
The catch here is that a malicious user might add a transaction at
1 Wei
price, replace it with2 Wei
, then3 Wei
and so on. Each subsequent transaction is more expensive, so is accepted, but would wreck havoc in the networking layer with transactions rotating like crazy. This commit prevents that by enforcing a minimum price bump of 10% for any transaction that wishes to replace an already existing one in the pool.It also features an extra optimization compared to the old code, whereby replacement transactions are injected directly into the executable list (if it has a good nonce), instead of adding it into the non-executable queue and then promoting it later. This should help prevent replacement transactions from wasting computing resources by placing them into the queue and afterwards moving the to pending.
core: replace txpool priced map with a simple heap
The worst case scenario for the priced map data structure is if all transactions in the dataset have different price points. However with an active fee market, this is somewhat to be expected. As such, per the suggestion of @Arachnid, this commit dumps the map out of the data structure, and instead of a price-point heap, it uses a full transaction heap sorted by gas price. This should make the code a tad simpler to reason about while maintaining the same (expected case) runtime/memory complexity.