From 61c8293a2db91c418fa3bbd233470f0546516bfb Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 6 Mar 2026 10:26:43 +0100 Subject: [PATCH] core/txpool/blobpool: fix slotter closure consumption bug billy.SlotSizeFn is a stateful closure that advances an internal counter on each call. Storing it as p.slotter and calling getSlotSize(p.slotter, ...) on every addLocked permanently mutates the closure state. After the first call, all subsequent slot size lookups start from the wrong position and return incorrect sizes. Replace the slotter field with a factory function (newSlotter) that creates a fresh slotter instance on each call to getSlotSize. Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index b3b1f934d3..0dd341939c 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -346,10 +346,10 @@ type BlobPool struct { reserver txpool.Reserver // Address reserver to ensure exclusivity across subpools hasPendingAuth func(common.Address) bool // Determine whether the specified address has a pending 7702-auth - store billy.Database // Persistent data store for the tx metadata and blobs - slotter billy.SlotSizeFn // Slotter function to determine the shelf sizes for the billy database - stored uint64 // Useful data size of all transactions on disk - limbo *limbo // Persistent data store for the non-finalized blobs + store billy.Database // Persistent data store for the tx metadata and blobs + newSlotter func() billy.SlotSizeFn // Factory to create fresh slotter instances for slot size lookups + stored uint64 // Useful data size of all transactions on disk + limbo *limbo // Persistent data store for the non-finalized blobs gapped map[common.Address][]*types.Transaction // Transactions that are currently gapped (nonce too high) gappedSource map[common.Hash]common.Address // Source of gapped transactions to allow rechecking on inclusion @@ -436,13 +436,18 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser p.state = state // Create new slotter for pre-Osaka blob configuration. - p.slotter = newSlotter(params.BlobTxMaxBlobs) + slotter := newSlotter(params.BlobTxMaxBlobs) + p.newSlotter = func() billy.SlotSizeFn { return newSlotter(params.BlobTxMaxBlobs) } // See if we need to migrate the queue blob store after fusaka - p.slotter, err = tryMigrate(p.chain.Config(), p.slotter, queuedir) + slotter, err = tryMigrate(p.chain.Config(), slotter, queuedir) if err != nil { return err } + // Update the slotter factory if Osaka is active + if p.chain.Config().OsakaTime != nil { + p.newSlotter = func() billy.SlotSizeFn { return newSlotterEIP7594(params.BlobTxMaxBlobs) } + } // Index all transactions on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, blob []byte) { @@ -450,7 +455,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, p.slotter, index) + store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, slotter, index) if err != nil { return err } @@ -1622,7 +1627,7 @@ func (p *BlobPool) addLocked(tx *types.Transaction, checkGapped bool) (err error log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", err) return err } - storageSizeDiff, err := getSlotSize(p.slotter, uint32(len(blob))) + storageSizeDiff, err := getSlotSize(p.newSlotter(), uint32(len(blob))) if err != nil { // This should also not happen at this stage log.Warn("Dropping blob transaction due to size", "tx", tx.Hash(), "size", meta.size, "err", err)