This commit is contained in:
Richard Creighton 2026-05-22 11:32:31 +08:00 committed by GitHub
commit ed0cfb4199
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 94 additions and 4 deletions

View file

@ -1367,6 +1367,54 @@ func TestLegacyTxConversion(t *testing.T) {
verifyPoolInternals(t, pool) verifyPoolInternals(t, pool)
} }
// TestLegacyLimboBlobConversion verifies that limbo entries stored in the
// legacy *types.Transaction RLP format remain available after opening the limbo
// with the blobTxForPool storage format.
func TestLegacyLimboBlobConversion(t *testing.T) {
storage := t.TempDir()
limbodir := filepath.Join(storage, limboedTransactionStore)
if err := os.MkdirAll(limbodir, 0700); err != nil {
t.Fatalf("failed to create limbo dir: %v", err)
}
store, err := billy.Open(billy.Options{Path: limbodir}, newSlotter(params.BlobTxMaxBlobs), nil)
if err != nil {
t.Fatalf("failed to open limbo store: %v", err)
}
key, _ := crypto.GenerateKey()
tx := makeMultiBlobTx(0, 1, 1000, 100, 2, 0, key, types.BlobSidecarVersion0)
legacy, err := rlp.EncodeToBytes(&legacyLimboBlob{
TxHash: tx.Hash(),
Block: 7,
Tx: tx,
})
if err != nil {
t.Fatalf("failed to encode legacy limbo blob: %v", err)
}
if _, err := store.Put(legacy); err != nil {
t.Fatalf("failed to put legacy limbo blob: %v", err)
}
store.Close()
limbo, err := newLimbo(params.TestChainConfig, limbodir)
if err != nil {
t.Fatalf("failed to open limbo: %v", err)
}
defer limbo.Close()
ptx, err := limbo.pull(tx.Hash())
if err != nil {
t.Fatalf("failed to pull legacy limbo blob: %v", err)
}
got := ptx.ToTx()
if got.Hash() != tx.Hash() {
t.Fatalf("limbo tx hash mismatch: have %s, want %s", got.Hash(), tx.Hash())
}
if got.BlobTxSidecar() == nil {
t.Fatalf("limbo tx lost sidecar")
}
}
// TestBlobCountLimit tests the blobpool enforced limits on the max blob count. // TestBlobCountLimit tests the blobpool enforced limits on the max blob count.
func TestBlobCountLimit(t *testing.T) { func TestBlobCountLimit(t *testing.T) {
var ( var (

View file

@ -36,6 +36,12 @@ type limboBlob struct {
Ptx *blobTxForPool Ptx *blobTxForPool
} }
type legacyLimboBlob struct {
TxHash common.Hash // Owner transaction's hash to support resurrecting reorged txs
Block uint64 // Block in which the blob transaction was included
Tx *types.Transaction
}
// limbo is a light, indexed database to temporarily store recently included // limbo is a light, indexed database to temporarily store recently included
// blobs until they are finalized. The purpose is to support small reorgs, which // blobs until they are finalized. The purpose is to support small reorgs, which
// would require pulling back up old blobs (which aren't part of the chain). // would require pulling back up old blobs (which aren't part of the chain).
@ -97,8 +103,8 @@ func (l *limbo) Close() error {
// parseBlob is a callback method on limbo creation that gets called for each // parseBlob is a callback method on limbo creation that gets called for each
// limboed blob on disk to create the in-memory metadata index. // limboed blob on disk to create the in-memory metadata index.
func (l *limbo) parseBlob(id uint64, data []byte) error { func (l *limbo) parseBlob(id uint64, data []byte) error {
item := new(limboBlob) item, err := decodeLimboBlob(data)
if err := rlp.DecodeBytes(data, item); err != nil { if err != nil {
// This path is impossible unless the disk data representation changes // This path is impossible unless the disk data representation changes
// across restarts. For that ever improbable case, recover gracefully // across restarts. For that ever improbable case, recover gracefully
// by ignoring this data entry. // by ignoring this data entry.
@ -122,6 +128,42 @@ func (l *limbo) parseBlob(id uint64, data []byte) error {
return nil return nil
} }
func decodeLimboBlob(data []byte) (*limboBlob, error) {
item := new(limboBlob)
err := rlp.DecodeBytes(data, item)
if err == nil {
return validateLimboBlob(item)
}
legacy := new(legacyLimboBlob)
if legacyErr := rlp.DecodeBytes(data, legacy); legacyErr != nil {
return nil, errors.Join(err, legacyErr)
}
if legacy.Tx == nil {
return nil, errors.New("missing limbo transaction")
}
if legacy.Tx.Hash() != legacy.TxHash {
return nil, errors.New("limbo transaction hash mismatch")
}
if legacy.Tx.BlobTxSidecar() == nil {
return nil, errors.New("missing limbo blob sidecar")
}
return &limboBlob{
TxHash: legacy.TxHash,
Block: legacy.Block,
Ptx: newBlobTxForPool(legacy.Tx),
}, nil
}
func validateLimboBlob(item *limboBlob) (*limboBlob, error) {
if item.Ptx == nil || item.Ptx.Tx == nil {
return nil, errors.New("missing limbo transaction")
}
if item.Ptx.Tx.Hash() != item.TxHash {
return nil, errors.New("limbo transaction hash mismatch")
}
return item, nil
}
// finalize evicts all blobs belonging to a recently finalized block or older. // finalize evicts all blobs belonging to a recently finalized block or older.
func (l *limbo) finalize(final *types.Header) { func (l *limbo) finalize(final *types.Header) {
// Just in case there's no final block yet (network not yet merged, weird // Just in case there's no final block yet (network not yet merged, weird
@ -222,8 +264,8 @@ func (l *limbo) getAndDrop(id uint64) (*limboBlob, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
item := new(limboBlob) item, err := decodeLimboBlob(data)
if err = rlp.DecodeBytes(data, item); err != nil { if err != nil {
return nil, err return nil, err
} }
delete(l.index, item.TxHash) delete(l.index, item.TxHash)