This commit is contained in:
ozpool 2026-05-21 21:53:42 -07:00 committed by GitHub
commit 29c8720c46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 100 additions and 3 deletions

View file

@ -202,16 +202,37 @@ func (l *limbo) update(txhash common.Hash, block uint64) {
return
}
// Retrieve the old blobs from the data store and write them back with a new
// block number. IF anything fails, there's not much to do, go on.
item, err := l.getAndDrop(id)
// block number. The new entry is written before the old one is dropped: blob
// sidecars are not stored in chain state, so the limbo is the only place
// from which a re-injection on a reorg-rollback can recover them. Dropping
// the existing entry first (as the previous implementation did) would lose
// the blob outright if the subsequent setAndIndex call failed.
data, err := l.store.Get(id)
if err != nil {
log.Error("Failed to get and drop limboed blobs", "tx", txhash, "id", id, "err", err)
log.Error("Failed to load limboed blobs", "tx", txhash, "id", id, "err", err)
return
}
item := new(limboBlob)
if err := rlp.DecodeBytes(data, item); err != nil {
log.Error("Failed to decode limboed blobs", "tx", txhash, "id", id, "err", err)
return
}
if err := l.setAndIndex(item.Ptx, block); err != nil {
log.Error("Failed to set and index limboed blobs", "tx", txhash, "err", err)
return
}
// The new entry is now durable in the store and indexed under the new
// block; setAndIndex has overwritten l.index[txhash] with the new id, so
// clean up the old store entry and group mapping. A failure to drop the
// old store id only leaks a billy slot - the blob is still preserved
// under the new id.
if err := l.store.Delete(id); err != nil {
log.Error("Failed to drop superseded limbo entry", "tx", txhash, "old-id", id, "err", err)
}
delete(l.groups[item.Block], id)
if len(l.groups[item.Block]) == 0 {
delete(l.groups, item.Block)
}
log.Trace("Blob transaction updated in limbo", "tx", txhash, "old-block", item.Block, "new-block", block)
}

View file

@ -0,0 +1,76 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package blobpool
import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
// TestLimboUpdateRoundTrip checks that update() relocates a tracked blob
// transaction to a new block while keeping it pullable.
//
// This is the regression test for #34944. The previous implementation deleted
// the existing limbo entry before inserting the relocated one, so a failure of
// the second step would have permanently dropped the blob sidecar. The current
// implementation writes the new entry first and only drops the old one once
// the new one is durable; the round-trip below exercises that happy path.
func TestLimboUpdateRoundTrip(t *testing.T) {
limbo, err := newLimbo(params.MainnetChainConfig, t.TempDir())
if err != nil {
t.Fatalf("failed to open limbo: %v", err)
}
defer limbo.Close()
key, _ := crypto.GenerateKey()
tx := makeTx(0, 1, 1, 1, key)
hash := tx.Hash()
if err := limbo.push(newBlobTxForPool(tx), 100); err != nil {
t.Fatalf("push failed: %v", err)
}
if _, ok := limbo.index[hash]; !ok {
t.Fatalf("tx not indexed after push")
}
oldID := limbo.index[hash]
if _, ok := limbo.groups[100][oldID]; !ok {
t.Fatalf("tx not in groups[100] after push")
}
limbo.update(hash, 101)
if _, ok := limbo.groups[100]; ok {
t.Fatalf("old block group not cleaned up after update")
}
newID, ok := limbo.index[hash]
if !ok {
t.Fatalf("tx no longer indexed after update")
}
if _, ok := limbo.groups[101][newID]; !ok {
t.Fatalf("tx not in groups[101] after update")
}
pulled, err := limbo.pull(hash)
if err != nil {
t.Fatalf("pull after update failed: %v", err)
}
if pulled.Tx.Hash() != hash {
t.Fatalf("pulled tx hash mismatch: got %x want %x", pulled.Tx.Hash(), hash)
}
}