Skip to content

Commit 20ad4f5

Browse files
core/txpool: add explicit max blob count limit (#31837)
Fixes #31792. --------- Co-authored-by: lightclient <lightclient@protonmail.com>
1 parent 0287666 commit 20ad4f5

File tree

4 files changed

+81
-7
lines changed

4 files changed

+81
-7
lines changed

core/txpool/blobpool/blobpool.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ const (
6262
// limit can never hurt.
6363
txMaxSize = 1024 * 1024
6464

65+
// maxBlobsPerTx is the maximum number of blobs that a single transaction can
66+
// carry. We choose a smaller limit than the protocol-permitted MaxBlobsPerBlock
67+
// in order to ensure network and txpool stability.
68+
// Note: if you increase this, validation will fail on txMaxSize.
69+
maxBlobsPerTx = 7
70+
6571
// maxTxsPerAccount is the maximum number of blob transactions admitted from
6672
// a single account. The limit is enforced to minimize the DoS potential of
6773
// a private tx cancelling publicly propagated blobs.
@@ -1095,10 +1101,11 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
10951101
// and does not require the pool mutex to be held.
10961102
func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error {
10971103
opts := &txpool.ValidationOptions{
1098-
Config: p.chain.Config(),
1099-
Accept: 1 << types.BlobTxType,
1100-
MaxSize: txMaxSize,
1101-
MinTip: p.gasTip.ToBig(),
1104+
Config: p.chain.Config(),
1105+
Accept: 1 << types.BlobTxType,
1106+
MaxSize: txMaxSize,
1107+
MinTip: p.gasTip.ToBig(),
1108+
MaxBlobCount: maxBlobsPerTx,
11021109
}
11031110
return txpool.ValidateTransaction(tx, p.head, p.signer, opts)
11041111
}

core/txpool/blobpool/blobpool_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,65 @@ func TestChangingSlotterSize(t *testing.T) {
11421142
}
11431143
}
11441144

1145+
// TestBlobCountLimit tests the blobpool enforced limits on the max blob count.
1146+
func TestBlobCountLimit(t *testing.T) {
1147+
var (
1148+
key1, _ = crypto.GenerateKey()
1149+
key2, _ = crypto.GenerateKey()
1150+
1151+
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
1152+
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
1153+
)
1154+
1155+
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
1156+
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
1157+
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
1158+
statedb.Commit(0, true, false)
1159+
1160+
// Make Prague-enabled custom chain config.
1161+
cancunTime := uint64(0)
1162+
pragueTime := uint64(0)
1163+
config := &params.ChainConfig{
1164+
ChainID: big.NewInt(1),
1165+
LondonBlock: big.NewInt(0),
1166+
BerlinBlock: big.NewInt(0),
1167+
CancunTime: &cancunTime,
1168+
PragueTime: &pragueTime,
1169+
BlobScheduleConfig: &params.BlobScheduleConfig{
1170+
Cancun: params.DefaultCancunBlobConfig,
1171+
Prague: params.DefaultPragueBlobConfig,
1172+
},
1173+
}
1174+
chain := &testBlockChain{
1175+
config: config,
1176+
basefee: uint256.NewInt(1050),
1177+
blobfee: uint256.NewInt(105),
1178+
statedb: statedb,
1179+
}
1180+
pool := New(Config{Datadir: t.TempDir()}, chain, nil)
1181+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
1182+
t.Fatalf("failed to create blob pool: %v", err)
1183+
}
1184+
1185+
// Attempt to add transactions.
1186+
var (
1187+
tx1 = makeMultiBlobTx(0, 1, 1000, 100, 7, key1)
1188+
tx2 = makeMultiBlobTx(0, 1, 800, 70, 8, key2)
1189+
)
1190+
errs := pool.Add([]*types.Transaction{tx1, tx2}, true)
1191+
1192+
// Check that first succeeds second fails.
1193+
if errs[0] != nil {
1194+
t.Fatalf("expected tx with 7 blobs to succeed")
1195+
}
1196+
if !errors.Is(errs[1], txpool.ErrTxBlobLimitExceeded) {
1197+
t.Fatalf("expected tx with 8 blobs to fail, got: %v", errs[1])
1198+
}
1199+
1200+
verifyPoolInternals(t, pool)
1201+
pool.Close()
1202+
}
1203+
11451204
// Tests that adding transaction will correctly store it in the persistent store
11461205
// and update all the indices.
11471206
//

core/txpool/errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ var (
5858
// making the transaction invalid, rather a DOS protection.
5959
ErrOversizedData = errors.New("oversized data")
6060

61+
// ErrTxBlobLimitExceeded is returned if a transaction would exceed the number
62+
// of blobs allowed by blobpool.
63+
ErrTxBlobLimitExceeded = errors.New("transaction blob limit exceeded")
64+
6165
// ErrAlreadyReserved is returned if the sender address has a pending transaction
6266
// in a different subpool. For example, this error is returned in response to any
6367
// input transaction of non-blob type when a blob transaction from this sender

core/txpool/validation.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ var (
4242
type ValidationOptions struct {
4343
Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules
4444

45-
Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool
46-
MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle
47-
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
45+
Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool
46+
MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle
47+
MaxBlobCount int // Maximum number of blobs allowed per transaction
48+
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
4849
}
4950

5051
// ValidationFunction is an method type which the pools use to perform the tx-validations which do not
@@ -63,6 +64,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
6364
if opts.Accept&(1<<tx.Type()) == 0 {
6465
return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
6566
}
67+
if blobCount := len(tx.BlobHashes()); blobCount > opts.MaxBlobCount {
68+
return fmt.Errorf("%w: blob count %v, limit %v", ErrTxBlobLimitExceeded, blobCount, opts.MaxBlobCount)
69+
}
6670
// Before performing any expensive validations, sanity check that the tx is
6771
// smaller than the maximum limit the pool can meaningfully handle
6872
if tx.Size() > opts.MaxSize {

0 commit comments

Comments
 (0)