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

transaction underpriced #24079

Closed
XWJACK opened this issue Dec 7, 2021 · 10 comments · Fixed by #24080
Closed

transaction underpriced #24079

XWJACK opened this issue Dec 7, 2021 · 10 comments · Fixed by #24080
Assignees
Labels

Comments

@XWJACK
Copy link
Contributor

XWJACK commented Dec 7, 2021

I found that I cannot send tx today.

image

For example:
current tx pool: baseFee == 80
send tx config: maxFeePerGas == 75 maxPriorityFeePerGas == 1

The only way to send this tx is to wait for tx pool baseFeed fall down 74 ?

@fjl
Copy link
Contributor

fjl commented Dec 7, 2021

Thanks for creating a new issue. I think the behavior of geth is kind of correct here. Your tx data with maxFeePerGas specifies that you want to pay a maximum of 75 wei per gas for this transaction. However, when the baseFee is 80, it will cost at least 80 wei/gas, so the transaction is underpriced.

The solution is sending with higher maxFeePerGas.

@XWJACK
Copy link
Contributor Author

XWJACK commented Dec 8, 2021

Thanks for creating a new issue. I think the behavior of geth is kind of correct here. Your tx data with maxFeePerGas specifies that you want to pay a maximum of 75 wei per gas for this transaction. However, when the baseFee is 80, it will cost at least 80 wei/gas, so the transaction is underpriced.

The solution is sending with higher maxFeePerGas.

Perhaps you are right. But i think baseFee is unstable.
image

And pricelimit is a configuration that refuse low gas maxFeePerGas <= pricelimit, not maxFeePerGas <= pool.baseFee

@jasonklein
Copy link

jasonklein commented Dec 8, 2021

I also just got bit by this, I think.

Thanks for creating a new issue. I think the behavior of geth is kind of correct here. Your tx data with maxFeePerGas specifies that you want to pay a maximum of 75 wei per gas for this transaction. However, when the baseFee is 80, it will cost at least 80 wei/gas, so the transaction is underpriced.

The solution is sending with higher maxFeePerGas.

@fjl: to be clear, does your response mean that the threshold for getting into the pool should be the same as the threshold for getting into the next block? Or, said differently, in order to have a transaction admitted into the pool, a user must price the transaction to be mined quickly, regardless of how quickly they need the transaction to be mined?

@XWJACK
Copy link
Contributor Author

XWJACK commented Dec 8, 2021

May be we can add new configuration parameters?

--txpool.basefeelimitpercent value           Minimum base fee percent (percent of real-time base fee) limit to enforce for acceptance into the pool (default: 20)
--txpool.priorityfeelimit value              Minimum priority fee (maxPriorityFeePerGas) limit to enforce for acceptance into the pool (default: 1)
  • the first param basefeelimitpercent represents the percentage of real-time base fee (tx pool).
    For example:
  1. --txpool.basefeelimitpercent 20
  2. current pool.baseFee == 80
    sending tx with baseFeePerGas < 16 (80 * 20%) will be rejected.
  • the second param priorityfeelimit represents the tip for miners.
    For example:
  1. --txpool.priorityfeelimit 1
    sending tx with maxPriorityFeePerGas < 1 will be rejected.

to be legacy tx (gasPrice)
gasPrice < 16 or gasPrice - 16 < 1 will be rejected.

@jhoenicke
Copy link

jhoenicke commented Dec 8, 2021

In my opinion this is a bug in geth 1.10.13. The txpool should not only include transaction that are currently profitable to mine, but also transactions that may be profitable to mine 5 seconds into the future. However the current code will reject transactions that pay 800 Gwei if the base fee is currently 805 Gwei and the txpool only contains transaction below 300 Gwei.

To fix this, revert to the old code and use the maximum priority fee instead of the current priority fee. The code already makes sure that it only includes the most profitable transactions and will evict the transactions that pay the lowest overall fee. The new code will evict any new transactions below the current base fee, but keep transactions in the mempool that pay far less.

To see this in an image, visit https://mempool.jhoenicke.de/#ETH,2w,count,30
The geth-1.10.13 behaviour was active from Nov 27-29. After that I reverted my node back to the previous code. You can see that although the txpool kept transaction below 50 Gwei, it did not include new transactions above 100 Gwei, when the base fee was around 120 Gwei.

Edit: I should add that my node has a huge number of slot settings. With the default 4k the node would evict low fee transactions as soon as the pool reaches 4k transactions. The difference between old and new code is then that the old code would keep the 4k most paying transactions, while the new code would stop accepting new transactions long before it reaches the limit and keep most of the old low fee transactions.

@fjl
Copy link
Contributor

fjl commented Dec 8, 2021

Thanks a lot for your input. @jhoenicke your graph is very helpful.

@mrballsauce
Copy link

This is a bug. It should be possible for a user to choose a gas fee as low as they want. User should be able to wait patiently for network to be less congested and accept a low "bid." Auto rejecting low fee transactions is harmful to the network in long term.

@zsfelfoldi
Copy link
Contributor

zsfelfoldi commented Dec 9, 2021

Rejecting transactions with an error because maxFeePerGas < baseFee is indeed wrong. We should allow transactions in the pool that have a chance of being included later with lower base fee. Checking maxPriorityFeePerGas aagainst a sanity check threshold (like we did earlier) makes sense (even though it's usually the smaller component of the final price). For also sanity checking the maxFeePerGas we can go with the suggestion from @XWJACK. I should note though that the suggested check condition is not correct, transactions don't offer a baseFeePerGas. The correct check condition should look like

tx.maxPriorityFeePerGas >= txpool.priorityfeelimit
tx.maxFeePerGas >= pendingBlock.baseFee * txpool.basefeelimitpercent / 100 + txpool.priorityfeelimit

@ligi ligi removed the status:triage label Dec 9, 2021
@ryanschneider
Copy link
Contributor

ryanschneider commented Dec 9, 2021

Also, I think there's an off-by-one error?

focusing on the change here: #23855

if !local && tx.EffectiveGasTipIntCmp(pool.gasPrice, pendingBaseFee) < 0 {
  return ErrUnderpriced
}

I'm not really familar with this code, so it's entirely possible I missed something (I know I got it wrong the first time, this was from my second attempt at unpacking these lines). This simple-looking check is actually packing a lot of logic into it that takes a bit to unwind, so I do so below, focusing on the tx.EffectiveGasTipIntCmp portion.

  • For reference, pool.gasPrice for a non-miner is taken from the txpool.pricelimit arg:
  --txpool.pricelimit value           Minimum gas price limit to enforce for acceptance into the pool (default: 1)

Thus for most geth installs we have:

pool.gasPrice := 1 
pendingBaseFee := pool.priced.urgent.baseFee

And now if we focus on this part:

tx.EffectiveGasTipIntCmp(1, pendingBaseFee) < 0

We can "simplify" it to the code below, based on the logic inside EffectiveGasTipIntCmp:

tx.EffectiveGasTipValue(pendingBaseFee).Cmp(1)

EffectiveGasTipValue is basically min(tx.GasTipCap(), gasFeeCap.Sub(tx.GasFeeCap(), baseFee))

tx.EffectiveGasTipValue = min(tx.GasTipCap(), tx.GasFeeCap() - pendingBaseFee)

For legacy TXs, GasTipCap and GasFeeCap are both tx.GasPrice.

tx.EffectiveGasTipValue = min(tx.GasPrice, tx.GasPrice - pendingBaseFee)

Now we can plug EffectiveGasTipValue back into the original equation:

min(tx.GasPrice, tx.GasPrice - pendingBaseFee).Cmp(1) < 0

x.Cmp(y) is basically return -1 if x < y, return 0 if equal else return 1. So x.Cmp(y) < 0 is basically x < y.

min(tx.GasPrice, tx.GasPrice - pendingBaseFee) < 1

min(x, x - N) should always be x - N for non-negative N so:

(tx.GasPrice - pendingBaseFee) < 1

Won't this lead to txs where tx.GasPrice == pendingBaseFee also getting rejected?

@jhoenicke
Copy link

jhoenicke commented Dec 9, 2021

Won't this lead to txs where tx.GasPrice == pendingBaseFee also getting rejected?

Yes, but that is intentional. The comparison is (tx.GasPrice - pendingBaseFee) < pricelimit. The pricelimit is the minimum tip a miner wants for himself for including the tx. The default pricelimit is actually 1 Gweiwei. The only problem here is that the actual miner tip changes too fast to use it for deciding txpool inclusion. So it's better to use the max tip instead.

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

Successfully merging a pull request may close this issue.

8 participants