Skip to content
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
22 changes: 15 additions & 7 deletions sei-tendermint/internal/mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,8 @@ func (txmp *TxMempool) Flush() {
}

// ReapMaxBytesMaxGas returns a list of transactions within the provided size
// and gas constraints. Transaction are retrieved in priority order.
// and gas constraints. The returned list starts with EVM transactions (in priority order),
// followed by non-EVM transactions (in priority order).
// There are 4 types of constraints.
// 1. maxBytes - stops pulling txs from mempool once maxBytes is hit. Can be set to -1 to be ignored.
// 2. maxGasWanted - stops pulling txs from mempool once total gas wanted exceeds maxGasWanted.
Expand All @@ -561,11 +562,13 @@ func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGasWanted, maxGasEstimate
totalSize int64
)

var txs []types.Tx
var evmTxs []types.Tx
var nonEvmTxs []types.Tx
numTxs := 0
encounteredGasUnfit := false
if uint64(txmp.NumTxsNotPending()) < txmp.config.TxNotifyThreshold {
// do not reap anything if threshold is not met
return txs
return []types.Tx{}
}
txmp.priorityIndex.ForEachTx(func(wtx *WrappedTx) bool {
size := types.ComputeProtoSizeForTxs([]types.Tx{wtx.tx})
Expand Down Expand Up @@ -593,7 +596,7 @@ func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGasWanted, maxGasEstimate

if maxGasWantedExceeded || maxGasEstimatedExceeded {
// skip this unfit-by-gas tx once and attempt to pull up to 10 smaller ones
if !encounteredGasUnfit && len(txs) < MinTxsToPeek {
if !encounteredGasUnfit && numTxs < MinTxsToPeek {
encounteredGasUnfit = true
return true
}
Expand All @@ -605,14 +608,19 @@ func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGasWanted, maxGasEstimate
totalGasWanted = prospectiveGasWanted
totalGasEstimated = prospectiveGasEstimated

txs = append(txs, wtx.tx)
if encounteredGasUnfit && len(txs) >= MinTxsToPeek {
if wtx.isEVM {
evmTxs = append(evmTxs, wtx.tx)
} else {
nonEvmTxs = append(nonEvmTxs, wtx.tx)
}
numTxs++
if encounteredGasUnfit && numTxs >= MinTxsToPeek {
return false
}
return true
})

return txs
return append(evmTxs, nonEvmTxs...)
}

// ReapMaxTxs returns a list of transactions within the provided number of
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note the ReapMaxTxs method's behaviour is not changed by this PR, since that method is used for RPC etc. support (and not for block creation).

Expand Down
69 changes: 69 additions & 0 deletions sei-tendermint/internal/mempool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,3 +1213,72 @@ func TestMempoolExpiration(t *testing.T) {
require.Equal(t, 0, txmp.expirationIndex.Size())
require.Equal(t, 0, txmp.txStore.Size())
}

// TestReapMaxBytesMaxGas_EVMFirst verifies that ReapMaxBytesMaxGas returns
// EVM transactions first, followed by non-EVM transactions.
func TestReapMaxBytesMaxGas_EVMFirst(t *testing.T) {
ctx := t.Context()

client := abciclient.NewLocalClient(log.NewNopLogger(), &application{Application: kvstore.NewApplication()})
if err := client.Start(ctx); err != nil {
t.Fatal(err)
}
t.Cleanup(client.Wait)

txmp := setup(t, client, 0)
peerID := uint16(1)

evmAddress1 := "0xeD23B3A9DE15e92B9ef9540E587B3661E15A12fA"
evmAddress2 := "0xfD23B3A9DE15e92B9ef9540E587B3661E15A12fA"
evmAddress3 := "0xaD23B3A9DE15e92B9ef9540E587B3661E15A12fA"

// Set up priorities so that pure priority ordering would interleave EVM and non-EVM:
// Priority order: EVM(100), non-EVM(90), EVM(80), non-EVM(70), EVM(60)
txsToAdd := [][]byte{
[]byte(fmt.Sprintf("evm-sender=%s=%d=%d", evmAddress1, 100, 0)), // EVM, priority 100
[]byte("sender-1=key1=90"), // non-EVM, priority 90
[]byte(fmt.Sprintf("evm-sender=%s=%d=%d", evmAddress2, 80, 0)), // EVM, priority 80
[]byte("sender-2=key2=70"), // non-EVM, priority 70
[]byte(fmt.Sprintf("evm-sender=%s=%d=%d", evmAddress3, 60, 0)), // EVM, priority 60
}

for _, tx := range txsToAdd {
require.NoError(t, txmp.CheckTx(ctx, tx, nil, TxInfo{SenderID: peerID}))
}

require.Equal(t, 5, txmp.Size())

// Reap all transactions
reapedTxs := txmp.ReapMaxBytesMaxGas(-1, -1, -1)
require.Len(t, reapedTxs, 5)

// Verify EVM transactions come first, then non-EVM
// Find the boundary between EVM and non-EVM transactions
evmCount := 0
nonEvmStartIdx := -1
for i, tx := range reapedTxs {
isEVM := strings.HasPrefix(string(tx), "evm")
if isEVM {
evmCount++
// After we've seen non-EVM, we shouldn't see any more EVM
require.Equal(t, -1, nonEvmStartIdx, "EVM transaction found after non-EVM transaction at index %d: %s", i, string(tx))
} else {
if nonEvmStartIdx == -1 {
nonEvmStartIdx = i
}
}
}

// We should have exactly 3 EVM transactions first, then 2 non-EVM
require.Equal(t, 3, evmCount, "Expected 3 EVM transactions")
require.Equal(t, 3, nonEvmStartIdx, "Expected non-EVM transactions to start at index 3")

// Verify the first 3 transactions are EVM
require.True(t, strings.HasPrefix(string(reapedTxs[0]), "evm"), "First tx should be EVM: %s", string(reapedTxs[0]))
require.True(t, strings.HasPrefix(string(reapedTxs[1]), "evm"), "Second tx should be EVM: %s", string(reapedTxs[1]))
require.True(t, strings.HasPrefix(string(reapedTxs[2]), "evm"), "Third tx should be EVM: %s", string(reapedTxs[2]))

// Verify the last 2 transactions are non-EVM
require.True(t, strings.HasPrefix(string(reapedTxs[3]), "sender"), "Fourth tx should be non-EVM: %s", string(reapedTxs[3]))
require.True(t, strings.HasPrefix(string(reapedTxs[4]), "sender"), "Fifth tx should be non-EVM: %s", string(reapedTxs[4]))
}
Loading