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

[R4R] fix: L1Cost subtracted repeatedly && test cast #90

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 5 additions & 28 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if err != nil {
return ErrInvalidSender
}
// Drop non-local transactions under our own minimal accepted gas price or tip

if tx.GasTipCapIntCmp(pool.gasPrice) < 0 {
return ErrUnderpriced
}
Expand All @@ -689,10 +689,6 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
cost := tx.Cost()
var l1Cost *big.Int
if l1Cost = pool.l1CostFn(tx.RollupDataGas(), tx.IsDepositTx(), tx.To()); l1Cost != nil { // add rollup cost
cost = cost.Add(cost, l1Cost)
}

metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx,
pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time),
Expand Down Expand Up @@ -734,17 +730,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
userBalance = new(big.Int).Add(userBalance, sponsorCostSum)
sum := new(big.Int).Add(cost, list.totalcost)
if repl := list.txs.Get(tx.Nonce()); repl != nil {
// Deduct the cost of a transaction replaced by this
replCost := repl.Cost()
if replL1Cost := pool.l1CostFn(repl.RollupDataGas(), repl.IsDepositTx(), repl.To()); replL1Cost != nil { // add rollup cost
replCost = replCost.Add(replCost, replL1Cost)
}
replMetaTxParams, err := types.DecodeAndVerifyMetaTxParams(repl,
pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time),
pool.chainconfig.IsMetaTxV3(pool.chain.CurrentBlock().Time))
if err != nil {
return err
}
replCost := repl.Cost()
if replMetaTxParams != nil {
replTxGasCost := new(big.Int).Sub(repl.Cost(), repl.Value())
sponsorAmount, _ := types.CalculateSponsorPercentAmount(replMetaTxParams, replTxGasCost)
Expand All @@ -771,6 +763,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {

gasRemaining := big.NewInt(int64(tx.Gas() - intrGas*tokenRatio))
baseFee := pool.chain.CurrentBlock().BaseFee
l1Cost := pool.l1CostFn(tx.RollupDataGas(), tx.IsDepositTx(), tx.To())

if tx.Type() == types.LegacyTxType {
if tx.GasPrice().Cmp(baseFee) < 0 {
Expand Down Expand Up @@ -1544,10 +1537,6 @@ func (pool *TxPool) validateMetaTxList(list *list) ([]*types.Transaction, *big.I
continue
}
txGasCost := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
l1Cost := pool.l1CostFn(tx.RollupDataGas(), tx.IsDepositTx(), tx.To())
if l1Cost != nil {
txGasCost = new(big.Int).Add(txGasCost, l1Cost) // gas fee sponsor must sponsor additional l1Cost fee
}
sponsorAmount, _ := types.CalculateSponsorPercentAmount(metaTxParams, txGasCost)
var sponsorAmountAccumulated *big.Int
sponsorAmountAccumulated, ok := sponsorCostSumPerSponsor[metaTxParams.GasFeeSponsor]
Expand Down Expand Up @@ -1595,13 +1584,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
log.Trace("Removed invalid queued meta transaction", "count", len(invalidMetaTxs))
balance := pool.currentState.GetBalance(addr)
balance = new(big.Int).Add(balance, sponsorCostSum)
if !list.Empty() {
// Reduce the cost-cap by L1 rollup cost of the first tx if necessary. Other txs will get filtered out afterwards.
el := list.txs.FirstElement()
if l1Cost := pool.l1CostFn(el.RollupDataGas(), el.IsDepositTx(), el.To()); l1Cost != nil {
balance = new(big.Int).Sub(balance, l1Cost) // negative big int is fine
}
}

// Drop all transactions that are too costly (low balance or out of gas)
drops, _ := list.Filter(balance, pool.currentMaxGas)
for _, tx := range drops {
Expand Down Expand Up @@ -1808,13 +1791,7 @@ func (pool *TxPool) demoteUnexecutables() {
}
balance := pool.currentState.GetBalance(addr)
balance = new(big.Int).Add(balance, sponsorCostSum)
if !list.Empty() {
// Reduce the cost-cap by L1 rollup cost of the first tx if necessary. Other txs will get filtered out afterwards.
el := list.txs.FirstElement()
if l1Cost := pool.l1CostFn(el.RollupDataGas(), el.IsDepositTx(), el.To()); l1Cost != nil {
balance = new(big.Int).Sub(balance, l1Cost) // negative big int is fine
}
}

// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
drops, invalids := list.Filter(balance, pool.currentMaxGas)
for _, tx := range drops {
Expand Down
91 changes: 13 additions & 78 deletions core/txpool/txpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,22 @@ type testBlockChain struct {
gasLimit atomic.Uint64
statedb *state.StateDB
chainHeadFeed *event.Feed
basefee *big.Int
}

func newTestBlockChain(gasLimit uint64, statedb *state.StateDB, chainHeadFeed *event.Feed) *testBlockChain {
bc := testBlockChain{statedb: statedb, chainHeadFeed: new(event.Feed)}
bc.gasLimit.Store(gasLimit)
// Set a basefee of 1, avoiding nil pointer dereferences
bc.basefee = big.NewInt(1)
return &bc
}

func (bc *testBlockChain) CurrentBlock() *types.Header {
return &types.Header{
Number: new(big.Int),
GasLimit: bc.gasLimit.Load(),
BaseFee: bc.basefee,
}
}

Expand Down Expand Up @@ -300,6 +304,9 @@ func TestInvalidTransactions(t *testing.T) {

balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
testAddBalance(pool, from, balance)

// set fake token ratio to 1
pool.currentState.SetState(types.GasOracleAddr, types.TokenRatioSlot, common.BigToHash(big.NewInt(1)))
if err := pool.AddRemote(tx); !errors.Is(err, core.ErrIntrinsicGas) {
t.Error("expected", core.ErrIntrinsicGas, "got", err)
}
Expand All @@ -316,8 +323,8 @@ func TestInvalidTransactions(t *testing.T) {
if err := pool.AddRemote(tx); err != ErrUnderpriced {
t.Error("expected", ErrUnderpriced, "got", err)
}
if err := pool.AddLocal(tx); err != nil {
t.Error("expected", nil, "got", err)
if err := pool.AddLocal(tx); err != ErrUnderpriced {
t.Error("expected", ErrUnderpriced, "got", err)
}
}

Expand Down Expand Up @@ -1418,20 +1425,6 @@ func TestRepricing(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
tx := pricedTransaction(1, 100000, big.NewInt(1), keys[3])
if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err)
}
if pending, _ = pool.Stats(); pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
}
if err := validateEvents(events, 1); err != nil {
t.Fatalf("post-reprice local event firing failed: %v", err)
}
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(2), keys[0])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
Expand Down Expand Up @@ -1542,20 +1535,6 @@ func TestRepricingDynamicFee(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
tx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[3])
if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err)
}
if pending, _ = pool.Stats(); pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
}
if err := validateEvents(events, 1); err != nil {
t.Fatalf("post-reprice local event firing failed: %v", err)
}
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
tx = pricedTransaction(1, 100000, big.NewInt(2), keys[0])
if err := pool.AddRemote(tx); err != nil {
Expand Down Expand Up @@ -1598,23 +1577,23 @@ func TestRepricingKeepsLocals(t *testing.T) {
// Create transaction (both pending and queued) with a linearly growing gasprice
for i := uint64(0); i < 500; i++ {
// Add pending transaction.
pendingTx := pricedTransaction(i, 100000, big.NewInt(int64(i)), keys[2])
pendingTx := pricedTransaction(i, 100000, big.NewInt(int64(i+1)), keys[2])
if err := pool.AddLocal(pendingTx); err != nil {
t.Fatal(err)
}
// Add queued transaction.
queuedTx := pricedTransaction(i+501, 100000, big.NewInt(int64(i)), keys[2])
queuedTx := pricedTransaction(i+501, 100000, big.NewInt(int64(i+1)), keys[2])
if err := pool.AddLocal(queuedTx); err != nil {
t.Fatal(err)
}

// Add pending dynamic fee transaction.
pendingTx = dynamicFeeTx(i, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1])
pendingTx = dynamicFeeTx(i, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i+1)), keys[1])
if err := pool.AddLocal(pendingTx); err != nil {
t.Fatal(err)
}
// Add queued dynamic fee transaction.
queuedTx = dynamicFeeTx(i+501, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1])
queuedTx = dynamicFeeTx(i+501, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i+1)), keys[1])
if err := pool.AddLocal(queuedTx); err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -1739,28 +1718,6 @@ func TestUnderpricing(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to append underpriced local transaction: %v", err)
}
ltx = pricedTransaction(0, 100000, big.NewInt(0), keys[3])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err)
}
pending, queued = pool.Stats()
if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
}
if queued != 1 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
if err := validateEvents(events, 2); err != nil {
t.Fatalf("local event firing failed: %v", err)
}
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}

// Tests that more expensive transactions push out cheap ones from the pool, but
Expand Down Expand Up @@ -1916,28 +1873,6 @@ func TestUnderpricingDynamicFee(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to append underpriced local transaction: %v", err)
}
ltx = dynamicFeeTx(0, 100000, big.NewInt(0), big.NewInt(0), keys[3])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err)
}
pending, queued = pool.Stats()
if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
}
if queued != 1 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
if err := validateEvents(events, 2); err != nil {
t.Fatalf("local event firing failed: %v", err)
}
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}

// Tests whether highest fee cap transaction is retained after a batch of high effective
Expand Down