From e6ea290f8e1916cb3b40bd0ec64a9d0ca94f788b Mon Sep 17 00:00:00 2001 From: MariusVanDerWijden Date: Fri, 12 Sep 2025 14:48:31 +0200 Subject: [PATCH] core/txpool/blobpool: also migrate limbo --- core/txpool/blobpool/blobpool.go | 36 +++----------------- core/txpool/blobpool/blobpool_test.go | 3 +- core/txpool/blobpool/limbo.go | 16 +++++++-- core/txpool/blobpool/slotter.go | 49 ++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 38 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 25165a18b7..52e290f488 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -398,36 +398,10 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser // Create new slotter for pre-Osaka blob configuration. slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) - // Check if we need to migrate our blob db to the new slotter. - if p.chain.Config().OsakaTime != nil { - // Open the store using the version slotter to see if any version has been - // written. - var version int - index := func(_ uint64, _ uint32, blob []byte) { - version = max(version, parseSlotterVersion(blob)) - } - store, err := billy.Open(billy.Options{Path: queuedir}, newVersionSlotter(), index) - if err != nil { - return err - } - store.Close() - - // If the version found is less than the currently configured store version, - // perform a migration then write the updated version of the store. - if version < storeVersion { - newSlotter := newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) - if err := billy.Migrate(billy.Options{Path: queuedir, Repair: true}, slotter, newSlotter); err != nil { - return err - } - store, err = billy.Open(billy.Options{Path: queuedir}, newVersionSlotter(), nil) - if err != nil { - return err - } - writeSlotterVersion(store, storeVersion) - store.Close() - } - // Set the slotter to the format now that the Osaka is active. - slotter = newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) + // See if we need to migrate the limbo after fusaka. + slotter, err = tryMigrate(p.chain.Config(), slotter, p.config.Datadir) + if err != nil { + return err } // Index all transactions on disk and delete anything unprocessable @@ -470,7 +444,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser // Pool initialized, attach the blob limbo to it to track blobs included // recently but not yet finalized - p.limbo, err = newLimbo(limbodir, eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) + p.limbo, err = newLimbo(p.chain.Config(), limbodir) if err != nil { p.Close() return err diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 054591196f..e46529a241 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1175,8 +1175,9 @@ func TestBillyMigration(t *testing.T) { storage := t.TempDir() os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, limboedTransactionStore), 0700) // Create the billy with the old slotter - oldSlotter := newSlotter(6) + oldSlotter := newSlotterEIP7594(6) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, oldSlotter, nil) // Create transactions from a few accounts. diff --git a/core/txpool/blobpool/limbo.go b/core/txpool/blobpool/limbo.go index 99d1b4ad6b..50c40c9d83 100644 --- a/core/txpool/blobpool/limbo.go +++ b/core/txpool/blobpool/limbo.go @@ -20,8 +20,10 @@ import ( "errors" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/billy" ) @@ -48,11 +50,21 @@ type limbo struct { } // newLimbo opens and indexes a set of limboed blob transactions. -func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) { +func newLimbo(config *params.ChainConfig, datadir string) (*limbo, error) { l := &limbo{ index: make(map[common.Hash]uint64), groups: make(map[uint64]map[uint64]common.Hash), } + + // Create new slotter for pre-Osaka blob configuration. + slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(config)) + + // See if we need to migrate the limbo after fusaka. + slotter, err := tryMigrate(config, slotter, datadir) + if err != nil { + return nil, err + } + // Index all limboed blobs on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, data []byte) { @@ -60,7 +72,7 @@ func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) { fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, newSlotter(maxBlobsPerTransaction), index) + store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, slotter, index) if err != nil { return nil, err } diff --git a/core/txpool/blobpool/slotter.go b/core/txpool/blobpool/slotter.go index e05bf6a3db..9b793e366c 100644 --- a/core/txpool/blobpool/slotter.go +++ b/core/txpool/blobpool/slotter.go @@ -16,7 +16,48 @@ package blobpool -import "github.com/holiman/billy" +import ( + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/billy" +) + +// tryMigrate checks if the billy needs to be migrated and migrates if needed. +// Returns a slotter that can be used for the database. +func tryMigrate(config *params.ChainConfig, slotter billy.SlotSizeFn, datadir string) (billy.SlotSizeFn, error) { + // Check if we need to migrate our blob db to the new slotter. + if config.OsakaTime != nil { + // Open the store using the version slotter to see if any version has been + // written. + var version int + index := func(_ uint64, _ uint32, blob []byte) { + version = max(version, parseSlotterVersion(blob)) + } + store, err := billy.Open(billy.Options{Path: datadir}, newVersionSlotter(), index) + if err != nil { + return nil, err + } + store.Close() + + // If the version found is less than the currently configured store version, + // perform a migration then write the updated version of the store. + if version < storeVersion { + newSlotter := newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(config)) + if err := billy.Migrate(billy.Options{Path: datadir, Repair: true}, slotter, newSlotter); err != nil { + return nil, err + } + store, err = billy.Open(billy.Options{Path: datadir}, newVersionSlotter(), nil) + if err != nil { + return nil, err + } + writeSlotterVersion(store, storeVersion) + store.Close() + } + // Set the slotter to the format now that the Osaka is active. + slotter = newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(config)) + } + return slotter, nil +} // newSlotter creates a helper method for the Billy datastore that returns the // individual shelf sizes used to store transactions in. @@ -27,7 +68,7 @@ import "github.com/holiman/billy" // The slotter also creates a shelf for 0-blob transactions. Whilst those are not // allowed in the current protocol, having an empty shelf is not a relevant use // of resources, but it makes stress testing with junk transactions simpler. -func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) { +func newSlotter(maxBlobsPerTransaction int) billy.SlotSizeFn { slotsize := uint32(txAvgSize) slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return @@ -45,7 +86,7 @@ func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) { // This slotter adds a dynamic overhead component to the slotter, which also // captures the notion that blob transactions with more blobs are also more likely to // to have more calldata. -func newSlotterEIP7594(maxBlobsPerTransaction int) func() (uint32, bool) { +func newSlotterEIP7594(maxBlobsPerTransaction int) billy.SlotSizeFn { slotsize := uint32(txAvgSize) slotsize -= uint32(blobSize) + txBlobOverhead // underflows, it's ok, will overflow back in the first return @@ -59,7 +100,7 @@ func newSlotterEIP7594(maxBlobsPerTransaction int) func() (uint32, bool) { // newVersionSlotter creates a slotter with a single 8 byte shelf to store // version metadata in. -func newVersionSlotter() func() (uint32, bool) { +func newVersionSlotter() billy.SlotSizeFn { return func() (size uint32, done bool) { return 8, true }