core/txpool/blobpool: also migrate limbo

This commit is contained in:
MariusVanDerWijden 2025-09-12 14:48:31 +02:00
parent ace873c1d0
commit e6ea290f8e
4 changed files with 66 additions and 38 deletions

View file

@ -398,36 +398,10 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser
// Create new slotter for pre-Osaka blob configuration. // Create new slotter for pre-Osaka blob configuration.
slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config()))
// Check if we need to migrate our blob db to the new slotter. // See if we need to migrate the limbo after fusaka.
if p.chain.Config().OsakaTime != nil { slotter, err = tryMigrate(p.chain.Config(), slotter, p.config.Datadir)
// Open the store using the version slotter to see if any version has been if err != nil {
// written. return err
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()))
} }
// Index all transactions on disk and delete anything unprocessable // 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 // Pool initialized, attach the blob limbo to it to track blobs included
// recently but not yet finalized // 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 { if err != nil {
p.Close() p.Close()
return err return err

View file

@ -1175,8 +1175,9 @@ func TestBillyMigration(t *testing.T) {
storage := t.TempDir() storage := t.TempDir()
os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700)
os.MkdirAll(filepath.Join(storage, limboedTransactionStore), 0700)
// Create the billy with the old slotter // 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) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, oldSlotter, nil)
// Create transactions from a few accounts. // Create transactions from a few accounts.

View file

@ -20,8 +20,10 @@ import (
"errors" "errors"
"github.com/ethereum/go-ethereum/common" "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/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/billy" "github.com/holiman/billy"
) )
@ -48,11 +50,21 @@ type limbo struct {
} }
// newLimbo opens and indexes a set of limboed blob transactions. // 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{ l := &limbo{
index: make(map[common.Hash]uint64), index: make(map[common.Hash]uint64),
groups: make(map[uint64]map[uint64]common.Hash), 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 // Index all limboed blobs on disk and delete anything unprocessable
var fails []uint64 var fails []uint64
index := func(id uint64, size uint32, data []byte) { index := func(id uint64, size uint32, data []byte) {
@ -60,7 +72,7 @@ func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) {
fails = append(fails, id) 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -16,7 +16,48 @@
package blobpool 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 // newSlotter creates a helper method for the Billy datastore that returns the
// individual shelf sizes used to store transactions in. // 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 // 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 // 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. // 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(txAvgSize)
slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return 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 // 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 // captures the notion that blob transactions with more blobs are also more likely to
// to have more calldata. // to have more calldata.
func newSlotterEIP7594(maxBlobsPerTransaction int) func() (uint32, bool) { func newSlotterEIP7594(maxBlobsPerTransaction int) billy.SlotSizeFn {
slotsize := uint32(txAvgSize) slotsize := uint32(txAvgSize)
slotsize -= uint32(blobSize) + txBlobOverhead // underflows, it's ok, will overflow back in the first return 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 // newVersionSlotter creates a slotter with a single 8 byte shelf to store
// version metadata in. // version metadata in.
func newVersionSlotter() func() (uint32, bool) { func newVersionSlotter() billy.SlotSizeFn {
return func() (size uint32, done bool) { return func() (size uint32, done bool) {
return 8, true return 8, true
} }