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
15 changes: 11 additions & 4 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ const (
// limit can never hurt.
txMaxSize = 1024 * 1024

// maxBlobsPerTx is the maximum number of blobs that a single transaction can
// carry. We choose a smaller limit than the protocol-permitted MaxBlobsPerBlock
// in order to ensure network and txpool stability.
// Note: if you increase this, validation will fail on txMaxSize.
maxBlobsPerTx = 7

// maxTxsPerAccount is the maximum number of blob transactions admitted from
// a single account. The limit is enforced to minimize the DoS potential of
// a private tx cancelling publicly propagated blobs.
Expand Down Expand Up @@ -1095,10 +1101,11 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
// and does not require the pool mutex to be held.
func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error {
opts := &txpool.ValidationOptions{
Config: p.chain.Config(),
Accept: 1 << types.BlobTxType,
MaxSize: txMaxSize,
MinTip: p.gasTip.ToBig(),
Config: p.chain.Config(),
Accept: 1 << types.BlobTxType,
MaxSize: txMaxSize,
MinTip: p.gasTip.ToBig(),
MaxBlobCount: maxBlobsPerTx,
}
return txpool.ValidateTransaction(tx, p.head, p.signer, opts)
}
Expand Down
59 changes: 59 additions & 0 deletions core/txpool/blobpool/blobpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,65 @@ func TestChangingSlotterSize(t *testing.T) {
}
}

// TestBlobCountLimit tests the blobpool enforced limits on the max blob count.
func TestBlobCountLimit(t *testing.T) {
var (
key1, _ = crypto.GenerateKey()
key2, _ = crypto.GenerateKey()

addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
)

statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
statedb.Commit(0, true, false)

// Make Prague-enabled custom chain config.
cancunTime := uint64(0)
pragueTime := uint64(0)
config := &params.ChainConfig{
ChainID: big.NewInt(1),
LondonBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
CancunTime: &cancunTime,
PragueTime: &pragueTime,
BlobScheduleConfig: &params.BlobScheduleConfig{
Cancun: params.DefaultCancunBlobConfig,
Prague: params.DefaultPragueBlobConfig,
},
}
chain := &testBlockChain{
config: config,
basefee: uint256.NewInt(1050),
blobfee: uint256.NewInt(105),
statedb: statedb,
}
pool := New(Config{Datadir: t.TempDir()}, chain, nil)
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
t.Fatalf("failed to create blob pool: %v", err)
}

// Attempt to add transactions.
var (
tx1 = makeMultiBlobTx(0, 1, 1000, 100, 7, key1)
tx2 = makeMultiBlobTx(0, 1, 800, 70, 8, key2)
)
errs := pool.Add([]*types.Transaction{tx1, tx2}, true)

// Check that first succeeds second fails.
if errs[0] != nil {
t.Fatalf("expected tx with 7 blobs to succeed")
}
if !errors.Is(errs[1], txpool.ErrTxBlobLimitExceeded) {
t.Fatalf("expected tx with 8 blobs to fail, got: %v", errs[1])
}

verifyPoolInternals(t, pool)
pool.Close()
}

// Tests that adding transaction will correctly store it in the persistent store
// and update all the indices.
//
Expand Down
4 changes: 4 additions & 0 deletions core/txpool/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ var (
// making the transaction invalid, rather a DOS protection.
ErrOversizedData = errors.New("oversized data")

// ErrTxBlobLimitExceeded is returned if a transaction would exceed the number
// of blobs allowed by blobpool.
ErrTxBlobLimitExceeded = errors.New("transaction blob limit exceeded")

// ErrAlreadyReserved is returned if the sender address has a pending transaction
// in a different subpool. For example, this error is returned in response to any
// input transaction of non-blob type when a blob transaction from this sender
Expand Down
10 changes: 7 additions & 3 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ var (
type ValidationOptions struct {
Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules

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

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