diff --git a/mempool/cat/pool.go b/mempool/cat/pool.go index 30e8b38696..d462f5a6b6 100644 --- a/mempool/cat/pool.go +++ b/mempool/cat/pool.go @@ -415,12 +415,14 @@ func (txmp *TxPool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs { var keep []types.Tx //nolint:prealloc for _, w := range txmp.allEntriesSorted() { // N.B. When computing byte size, we need to include the overhead for - // encoding as protobuf to send to the application. - totalGas += w.gasWanted - totalBytes += types.ComputeProtoSizeForTxs([]types.Tx{w.tx}) - if (maxGas >= 0 && totalGas > maxGas) || (maxBytes >= 0 && totalBytes > maxBytes) { - break + // encoding as protobuf to send to the application. This actually overestimates it + // as we add the proto overhead to each transaction + txBytes := types.ComputeProtoSizeForTxs([]types.Tx{w.tx}) + if (maxGas >= 0 && totalGas+w.gasWanted > maxGas) || (maxBytes >= 0 && totalBytes+txBytes > maxBytes) { + continue } + totalBytes += txBytes + totalGas += w.gasWanted keep = append(keep, w.tx) } return keep diff --git a/mempool/cat/pool_test.go b/mempool/cat/pool_test.go index 3b9eccb2e6..45eb6d4811 100644 --- a/mempool/cat/pool_test.go +++ b/mempool/cat/pool_test.go @@ -374,6 +374,28 @@ func TestTxPool_ReapMaxBytesMaxGas(t *testing.T) { require.Len(t, reapedTxs, 25) } +func TestTxMempoolTxLargerThanMaxBytes(t *testing.T) { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + txmp := setup(t, 0) + bigPrefix := make([]byte, 100) + _, err := rng.Read(bigPrefix) + require.NoError(t, err) + // large high priority tx + bigTx := []byte(fmt.Sprintf("sender-1-1=%X=2", bigPrefix)) + smallPrefix := make([]byte, 20) + _, err = rng.Read(smallPrefix) + require.NoError(t, err) + // smaller low priority tx with different sender + smallTx := []byte(fmt.Sprintf("sender-2-1=%X=1", smallPrefix)) + require.NoError(t, txmp.CheckTx(bigTx, nil, mempool.TxInfo{SenderID: 1})) + require.NoError(t, txmp.CheckTx(smallTx, nil, mempool.TxInfo{SenderID: 1})) + + // reap by max bytes less than the large tx + reapedTxs := txmp.ReapMaxBytesMaxGas(100, -1) + require.Len(t, reapedTxs, 1) + require.Equal(t, types.Tx(smallTx), reapedTxs[0]) +} + func TestTxPool_ReapMaxTxs(t *testing.T) { txmp := setup(t, 0) txs := checkTxs(t, txmp, 100, 0) diff --git a/mempool/v1/mempool.go b/mempool/v1/mempool.go index a07b685477..120549abf8 100644 --- a/mempool/v1/mempool.go +++ b/mempool/v1/mempool.go @@ -330,12 +330,14 @@ func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs { var keep []types.Tx //nolint:prealloc for _, w := range txmp.allEntriesSorted() { // N.B. When computing byte size, we need to include the overhead for - // encoding as protobuf to send to the application. - totalGas += w.gasWanted - totalBytes += types.ComputeProtoSizeForTxs([]types.Tx{w.tx}) - if (maxGas >= 0 && totalGas > maxGas) || (maxBytes >= 0 && totalBytes > maxBytes) { - break + // encoding as protobuf to send to the application. This actually overestimates it + // as we add the proto overhead to each transaction + txBytes := types.ComputeProtoSizeForTxs([]types.Tx{w.tx}) + if (maxGas >= 0 && totalGas+w.gasWanted > maxGas) || (maxBytes >= 0 && totalBytes+txBytes > maxBytes) { + continue } + totalBytes += txBytes + totalGas += w.gasWanted keep = append(keep, w.tx) } return keep diff --git a/mempool/v1/mempool_test.go b/mempool/v1/mempool_test.go index 0f8291fa8a..4b27c20b1e 100644 --- a/mempool/v1/mempool_test.go +++ b/mempool/v1/mempool_test.go @@ -355,6 +355,28 @@ func TestTxMempool_ReapMaxBytesMaxGas(t *testing.T) { require.Len(t, reapedTxs, 25) } +func TestTxMempoolTxLargerThanMaxBytes(t *testing.T) { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + txmp := setup(t, 0) + bigPrefix := make([]byte, 100) + _, err := rng.Read(bigPrefix) + require.NoError(t, err) + // large high priority tx + bigTx := []byte(fmt.Sprintf("sender-1-1=%X=2", bigPrefix)) + smallPrefix := make([]byte, 20) + _, err = rng.Read(smallPrefix) + require.NoError(t, err) + // smaller low priority tx with different sender + smallTx := []byte(fmt.Sprintf("sender-2-1=%X=1", smallPrefix)) + require.NoError(t, txmp.CheckTx(bigTx, nil, mempool.TxInfo{SenderID: 1})) + require.NoError(t, txmp.CheckTx(smallTx, nil, mempool.TxInfo{SenderID: 1})) + + // reap by max bytes less than the large tx + reapedTxs := txmp.ReapMaxBytesMaxGas(100, -1) + require.Len(t, reapedTxs, 1) + require.Equal(t, types.Tx(smallTx), reapedTxs[0]) +} + func TestTxMempool_ReapMaxTxs(t *testing.T) { txmp := setup(t, 0) tTxs := checkTxs(t, txmp, 100, 0)