Commit 551aaee4 authored by Joshua Gutow's avatar Joshua Gutow

txmgr: Don't enforce price bump if new price is not larger

This handles the case that a transaction is being resubmitted while
the L1 Basefee & Tip have not increased. In this case we reuse the
previous values. If the new values are between the old values & the
threshold, we then increase that value to the threshold. If it is
larger than the threshold, we then use the suggested value.

We need to not always bump the transaction price to ensure that we
don't go exponential when resubmitting transactions.
parent 33da2e1f
...@@ -102,8 +102,10 @@ type SimpleTxManager struct { ...@@ -102,8 +102,10 @@ type SimpleTxManager struct {
} }
// IncreaseGasPrice takes the previous transaction & potentially clones then signs it with a higher tip. // IncreaseGasPrice takes the previous transaction & potentially clones then signs it with a higher tip.
// If the basefee + priority fee did not increase by a minimum percent (geth's replacement percent) an // If the tip + basefee suggested by the network are not greater than the previous values, the same transaction
// error will be returned. // will be returned. If they are greater, this function will ensure that they are at least greater by 15% than
// the previous transaction's value to ensure that the price bump is large enough.
//
// We do not re-estimate the amount of gas used because for some stateful transactions (like output proposals) the // We do not re-estimate the amount of gas used because for some stateful transactions (like output proposals) the
// act of including the transaction renders the repeat of the transaction invalid. // act of including the transaction renders the repeat of the transaction invalid.
func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) { func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) {
...@@ -124,7 +126,10 @@ func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transa ...@@ -124,7 +126,10 @@ func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transa
// Enforce a min priceBump on the tip. Do this before the feeCap is calculated // Enforce a min priceBump on the tip. Do this before the feeCap is calculated
thresholdTip := new(big.Int).Mul(priceBumpPercent, tx.GasTipCap()) thresholdTip := new(big.Int).Mul(priceBumpPercent, tx.GasTipCap())
thresholdTip = thresholdTip.Div(thresholdTip, oneHundred) thresholdTip = thresholdTip.Div(thresholdTip, oneHundred)
if thresholdTip.Cmp(gasTipCap) > 0 { if tx.GasTipCapIntCmp(gasTipCap) >= 0 {
m.l.Debug("Reusing the previous tip", "previous", tx.GasTipCap(), "suggested", gasTipCap)
gasTipCap = tx.GasTipCap()
} else if thresholdTip.Cmp(gasTipCap) > 0 {
m.l.Debug("Overriding the tip to enforce a price bump", "previous", tx.GasTipCap(), "suggested", gasTipCap, "new", thresholdTip) m.l.Debug("Overriding the tip to enforce a price bump", "previous", tx.GasTipCap(), "suggested", gasTipCap, "new", thresholdTip)
gasTipCap = thresholdTip gasTipCap = thresholdTip
} }
...@@ -142,7 +147,10 @@ func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transa ...@@ -142,7 +147,10 @@ func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transa
// Enforce a min priceBump on the feeCap // Enforce a min priceBump on the feeCap
thresholdFeeCap := new(big.Int).Mul(priceBumpPercent, tx.GasFeeCap()) thresholdFeeCap := new(big.Int).Mul(priceBumpPercent, tx.GasFeeCap())
thresholdFeeCap = thresholdFeeCap.Div(thresholdFeeCap, oneHundred) thresholdFeeCap = thresholdFeeCap.Div(thresholdFeeCap, oneHundred)
if thresholdFeeCap.Cmp(gasFeeCap) > 0 { if tx.GasFeeCapIntCmp(gasFeeCap) >= 0 {
m.l.Debug("Reusing the previous fee cap", "previous", tx.GasFeeCap(), "suggested", gasFeeCap)
gasFeeCap = tx.GasFeeCap()
} else if thresholdFeeCap.Cmp(gasFeeCap) > 0 {
m.l.Debug("Overriding the fee cap to enforce a price bump", "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap) m.l.Debug("Overriding the fee cap to enforce a price bump", "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap)
gasFeeCap = thresholdFeeCap gasFeeCap = thresholdFeeCap
} }
......
...@@ -613,8 +613,8 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) { ...@@ -613,8 +613,8 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) {
t.Parallel() t.Parallel()
borkedBackend := failingBackend{ borkedBackend := failingBackend{
gasTip: common.Big0, gasTip: big.NewInt(101),
baseFee: common.Big0, baseFee: big.NewInt(460),
} }
mgr := &SimpleTxManager{ mgr := &SimpleTxManager{
...@@ -634,8 +634,8 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) { ...@@ -634,8 +634,8 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) {
} }
tx := types.NewTx(&types.DynamicFeeTx{ tx := types.NewTx(&types.DynamicFeeTx{
GasTipCap: big.NewInt(10), GasTipCap: big.NewInt(100),
GasFeeCap: big.NewInt(100), GasFeeCap: big.NewInt(1000),
}) })
ctx := context.Background() ctx := context.Background()
...@@ -645,6 +645,49 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) { ...@@ -645,6 +645,49 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) {
require.True(t, newTx.GasTipCap().Cmp(tx.GasTipCap()) > 0, "new tx tip must be larger") require.True(t, newTx.GasTipCap().Cmp(tx.GasTipCap()) > 0, "new tx tip must be larger")
} }
// TestIncreaseGasPriceNotExponential asserts that if the L1 basefee & tip remain the
// same, repeated calls to IncreaseGasPrice do not continually increase the gas price.
func TestIncreaseGasPriceNotExponential(t *testing.T) {
t.Parallel()
borkedBackend := failingBackend{
gasTip: big.NewInt(10),
baseFee: big.NewInt(45),
}
feeCap := CalcGasFeeCap(borkedBackend.baseFee, borkedBackend.gasTip)
mgr := &SimpleTxManager{
Config: Config{
ResubmissionTimeout: time.Second,
ReceiptQueryInterval: 50 * time.Millisecond,
NumConfirmations: 1,
SafeAbortNonceTooLowCount: 3,
Signer: func(ctx context.Context, from common.Address, tx *types.Transaction) (*types.Transaction, error) {
return tx, nil
},
From: common.Address{},
},
name: "TEST",
backend: &borkedBackend,
l: testlog.Logger(t, log.LvlCrit),
}
tx := types.NewTx(&types.DynamicFeeTx{
GasTipCap: big.NewInt(10),
GasFeeCap: big.NewInt(100),
})
// Run IncreaseGasPrice a bunch of times in a row to simulate a very fast resubmit loop.
for i := 0; i < 20; i++ {
ctx := context.Background()
newTx, err := mgr.IncreaseGasPrice(ctx, tx)
require.NoError(t, err)
require.True(t, newTx.GasFeeCap().Cmp(feeCap) == 0, "new tx fee cap must be equal L1")
require.True(t, newTx.GasTipCap().Cmp(borkedBackend.gasTip) == 0, "new tx tip must be equal L1")
tx = newTx
}
}
// TestIncreaseGasPriceUseLargeIncrease asserts that if the suggest gas tip // TestIncreaseGasPriceUseLargeIncrease asserts that if the suggest gas tip
// returned from L1 is much larger than the required price bump the L1 value // returned from L1 is much larger than the required price bump the L1 value
// is used instead of the price bump // is used instead of the price bump
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment