Skip to content

Commit e801571

Browse files
committed
core/txpool/blobpool: remove legacy sidecar conversion
1 parent 657c99f commit e801571

File tree

6 files changed

+25
-880
lines changed

6 files changed

+25
-880
lines changed

core/txpool/blobpool/blobpool.go

Lines changed: 18 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ import (
2121
"container/heap"
2222
"errors"
2323
"fmt"
24-
"maps"
2524
"math"
2625
"math/big"
2726
"os"
2827
"path/filepath"
29-
"slices"
3028
"sort"
3129
"sync"
3230
"sync/atomic"
@@ -337,9 +335,8 @@ type BlobPool struct {
337335
stored uint64 // Useful data size of all transactions on disk
338336
limbo *limbo // Persistent data store for the non-finalized blobs
339337

340-
signer types.Signer // Transaction signer to use for sender recovery
341-
chain BlockChain // Chain object to access the state through
342-
cQueue *conversionQueue // The queue for performing legacy sidecar conversion (TODO: remove after Osaka)
338+
signer types.Signer // Transaction signer to use for sender recovery
339+
chain BlockChain // Chain object to access the state through
343340

344341
head atomic.Pointer[types.Header] // Current head of the chain
345342
state *state.StateDB // Current state at the head of the chain
@@ -368,7 +365,6 @@ func New(config Config, chain BlockChain, hasPendingAuth func(common.Address) bo
368365
hasPendingAuth: hasPendingAuth,
369366
signer: types.LatestSigner(chain.Config()),
370367
chain: chain,
371-
cQueue: newConversionQueue(), // Deprecate it after the osaka fork
372368
lookup: newLookup(),
373369
index: make(map[common.Address][]*blobTxMeta),
374370
spent: make(map[common.Address]*uint256.Int),
@@ -485,9 +481,6 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser
485481

486482
// Close closes down the underlying persistent store.
487483
func (p *BlobPool) Close() error {
488-
// Terminate the conversion queue
489-
p.cQueue.close()
490-
491484
var errs []error
492485
if p.limbo != nil { // Close might be invoked due to error in constructor, before p,limbo is set
493486
if err := p.limbo.Close(); err != nil {
@@ -885,172 +878,6 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
885878
basefeeGauge.Update(int64(basefee.Uint64()))
886879
blobfeeGauge.Update(int64(blobfee.Uint64()))
887880
p.updateStorageMetrics()
888-
889-
// Perform the conversion logic at the fork boundary
890-
if !p.chain.Config().IsOsaka(oldHead.Number, oldHead.Time) && p.chain.Config().IsOsaka(newHead.Number, newHead.Time) {
891-
// Deep copy all indexed transaction metadata.
892-
var (
893-
ids = make(map[common.Address]map[uint64]uint64)
894-
txs = make(map[common.Address]map[uint64]common.Hash)
895-
)
896-
for sender, list := range p.index {
897-
ids[sender] = make(map[uint64]uint64)
898-
txs[sender] = make(map[uint64]common.Hash)
899-
for _, m := range list {
900-
ids[sender][m.nonce] = m.id
901-
txs[sender][m.nonce] = m.hash
902-
}
903-
}
904-
// Initiate the background conversion thread.
905-
p.cQueue.launchBillyConversion(func() {
906-
p.convertLegacySidecars(ids, txs)
907-
})
908-
}
909-
}
910-
911-
// compareAndSwap checks if the specified transaction is still tracked in the pool
912-
// and replace the metadata accordingly. It should only be used in the fork boundary
913-
// bulk conversion. If it fails for some reason, the subsequent txs won't be dropped
914-
// for simplicity which we assume it's very likely to happen.
915-
//
916-
// The returned flag indicates whether the replacement succeeded.
917-
func (p *BlobPool) compareAndSwap(address common.Address, hash common.Hash, blob []byte, oldID uint64, oldStorageSize uint32) bool {
918-
p.lock.Lock()
919-
defer p.lock.Unlock()
920-
921-
newId, err := p.store.Put(blob)
922-
if err != nil {
923-
log.Error("Failed to store transaction", "hash", hash, "err", err)
924-
return false
925-
}
926-
newSize := uint64(len(blob))
927-
newStorageSize := p.store.Size(newId)
928-
929-
// Terminate the procedure if the transaction was already evicted. The
930-
// newly added blob should be removed before return.
931-
if !p.lookup.update(hash, newId, newSize) {
932-
if derr := p.store.Delete(newId); derr != nil {
933-
log.Error("Failed to delete the dangling blob tx", "err", derr)
934-
} else {
935-
log.Warn("Deleted the dangling blob tx", "id", newId)
936-
}
937-
return false
938-
}
939-
// Update the metadata of blob transaction
940-
for _, meta := range p.index[address] {
941-
if meta.hash == hash {
942-
meta.id = newId
943-
meta.version = types.BlobSidecarVersion1
944-
meta.storageSize = newStorageSize
945-
meta.size = newSize
946-
947-
p.stored += uint64(newStorageSize)
948-
p.stored -= uint64(oldStorageSize)
949-
break
950-
}
951-
}
952-
if err := p.store.Delete(oldID); err != nil {
953-
log.Error("Failed to delete the legacy transaction", "hash", hash, "id", oldID, "err", err)
954-
}
955-
return true
956-
}
957-
958-
// convertLegacySidecar fetches transaction data from the store, performs an
959-
// on-the-fly conversion. This function is intended for use only during the
960-
// Osaka fork transition period.
961-
//
962-
// The returned flag indicates whether the replacement succeeds or not.
963-
func (p *BlobPool) convertLegacySidecar(sender common.Address, hash common.Hash, id uint64) bool {
964-
start := time.Now()
965-
966-
// Retrieves the legacy blob transaction from the underlying store with
967-
// read lock held, preventing any potential data race around the slot
968-
// specified by the id.
969-
p.lock.RLock()
970-
data, err := p.store.Get(id)
971-
if err != nil {
972-
p.lock.RUnlock()
973-
// The transaction may have been evicted simultaneously, safe to skip conversion.
974-
log.Debug("Blob transaction is missing", "hash", hash, "id", id, "err", err)
975-
return false
976-
}
977-
oldStorageSize := p.store.Size(id)
978-
p.lock.RUnlock()
979-
980-
// Decode the transaction, the failure is not expected and report the error
981-
// loudly if possible. If the blob transaction in this slot is corrupted,
982-
// leave it in the store, it will be dropped during the next pool
983-
// initialization.
984-
var tx types.Transaction
985-
if err = rlp.DecodeBytes(data, &tx); err != nil {
986-
log.Error("Blob transaction is corrupted", "hash", hash, "id", id, "err", err)
987-
return false
988-
}
989-
990-
// Skip conversion if the transaction does not match the expected hash, or if it was
991-
// already converted. This can occur if the original transaction was evicted from the
992-
// pool and the slot was reused by a new one.
993-
if tx.Hash() != hash {
994-
log.Warn("Blob transaction was replaced", "hash", hash, "id", id, "stored", tx.Hash())
995-
return false
996-
}
997-
sc := tx.BlobTxSidecar()
998-
if sc.Version >= types.BlobSidecarVersion1 {
999-
log.Debug("Skipping conversion of blob tx", "hash", hash, "id", id)
1000-
return false
1001-
}
1002-
1003-
// Perform the sidecar conversion, the failure is not expected and report the error
1004-
// loudly if possible.
1005-
if err := tx.BlobTxSidecar().ToV1(); err != nil {
1006-
log.Error("Failed to convert blob transaction", "hash", hash, "err", err)
1007-
return false
1008-
}
1009-
1010-
// Encode the converted transaction, the failure is not expected and report
1011-
// the error loudly if possible.
1012-
blob, err := rlp.EncodeToBytes(&tx)
1013-
if err != nil {
1014-
log.Error("Failed to encode blob transaction", "hash", tx.Hash(), "err", err)
1015-
return false
1016-
}
1017-
1018-
// Replace the legacy blob transaction with the converted format.
1019-
if !p.compareAndSwap(sender, hash, blob, id, oldStorageSize) {
1020-
log.Error("Failed to replace the legacy transaction", "hash", hash)
1021-
return false
1022-
}
1023-
log.Debug("Converted legacy blob transaction", "hash", hash, "elapsed", common.PrettyDuration(time.Since(start)))
1024-
return true
1025-
}
1026-
1027-
// convertLegacySidecars converts all given transactions to sidecar version 1.
1028-
//
1029-
// If any of them fails to be converted, the subsequent transactions will still
1030-
// be processed, as we assume the failure is very unlikely to happen. If happens,
1031-
// these transactions will be stuck in the pool until eviction.
1032-
func (p *BlobPool) convertLegacySidecars(ids map[common.Address]map[uint64]uint64, txs map[common.Address]map[uint64]common.Hash) {
1033-
var (
1034-
start = time.Now()
1035-
success int
1036-
failure int
1037-
)
1038-
for addr, list := range txs {
1039-
// Transactions evicted from the pool must be contiguous, if in any case,
1040-
// the transactions are gapped with each other, they will be discarded.
1041-
nonces := slices.Collect(maps.Keys(list))
1042-
slices.Sort(nonces)
1043-
1044-
// Convert the txs with nonce order
1045-
for _, nonce := range nonces {
1046-
if p.convertLegacySidecar(addr, list[nonce], ids[addr][nonce]) {
1047-
success++
1048-
} else {
1049-
failure++
1050-
}
1051-
}
1052-
}
1053-
log.Info("Completed blob transaction conversion", "discarded", failure, "injected", success, "elapsed", common.PrettyDuration(time.Since(start)))
1054881
}
1055882

1056883
// reorg assembles all the transactors and missing transactions between an old
@@ -1530,8 +1357,8 @@ func (p *BlobPool) GetMetadata(hash common.Hash) *txpool.TxMetadata {
15301357
//
15311358
// The version argument specifies the type of proofs to return, either the
15321359
// blob proofs (version 0) or the cell proofs (version 1). Proofs conversion is
1533-
// CPU intensive, so only done if explicitly requested with the convert flag.
1534-
func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte, convert bool) ([]*kzg4844.Blob, []kzg4844.Commitment, [][]kzg4844.Proof, error) {
1360+
// CPU intensive and prohibited in the blobpool explicitly.
1361+
func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blob, []kzg4844.Commitment, [][]kzg4844.Proof, error) {
15351362
var (
15361363
blobs = make([]*kzg4844.Blob, len(vhashes))
15371364
commitments = make([]kzg4844.Commitment, len(vhashes))
@@ -1582,7 +1409,7 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte, convert bool) (
15821409
}
15831410
// Mark hash as seen.
15841411
filled[hash] = struct{}{}
1585-
if sidecar.Version != version && !convert {
1412+
if sidecar.Version != version {
15861413
// Skip blobs with incompatible version. Note we still track the blob hash
15871414
// in `filled` here, ensuring that we do not resolve this tx another time.
15881415
continue
@@ -1591,29 +1418,13 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte, convert bool) (
15911418
var pf []kzg4844.Proof
15921419
switch version {
15931420
case types.BlobSidecarVersion0:
1594-
if sidecar.Version == types.BlobSidecarVersion0 {
1595-
pf = []kzg4844.Proof{sidecar.Proofs[i]}
1596-
} else {
1597-
proof, err := kzg4844.ComputeBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i])
1598-
if err != nil {
1599-
return nil, nil, nil, err
1600-
}
1601-
pf = []kzg4844.Proof{proof}
1602-
}
1421+
pf = []kzg4844.Proof{sidecar.Proofs[i]}
16031422
case types.BlobSidecarVersion1:
1604-
if sidecar.Version == types.BlobSidecarVersion0 {
1605-
cellProofs, err := kzg4844.ComputeCellProofs(&sidecar.Blobs[i])
1606-
if err != nil {
1607-
return nil, nil, nil, err
1608-
}
1609-
pf = cellProofs
1610-
} else {
1611-
cellProofs, err := sidecar.CellProofsAt(i)
1612-
if err != nil {
1613-
return nil, nil, nil, err
1614-
}
1615-
pf = cellProofs
1423+
cellProofs, err := sidecar.CellProofsAt(i)
1424+
if err != nil {
1425+
return nil, nil, nil, err
16161426
}
1427+
pf = cellProofs
16171428
}
16181429
for _, index := range list {
16191430
blobs[index] = &sidecar.Blobs[i]
@@ -1640,45 +1451,26 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
16401451
return available
16411452
}
16421453

1643-
// preCheck performs the static validation upon the provided tx and converts
1644-
// the legacy sidecars if Osaka fork has been activated with a short time window.
1454+
// preCheck performs the static validation upon the provided tx.
16451455
//
16461456
// This function is pure static and lock free.
16471457
func (p *BlobPool) preCheck(tx *types.Transaction) error {
16481458
var (
1649-
head = p.head.Load()
1650-
isOsaka = p.chain.Config().IsOsaka(head.Number, head.Time)
1651-
deadline time.Time
1459+
head = p.head.Load()
1460+
version = types.BlobSidecarVersion0
16521461
)
1653-
if isOsaka {
1654-
deadline = time.Unix(int64(*p.chain.Config().OsakaTime), 0).Add(conversionTimeWindow)
1462+
if p.chain.Config().IsOsaka(head.Number, head.Time) {
1463+
version = types.BlobSidecarVersion1
16551464
}
16561465
// Validate the transaction statically at first to avoid unnecessary
16571466
// conversion. This step doesn't require lock protection.
16581467
if err := p.ValidateTxBasics(tx); err != nil {
16591468
return err
16601469
}
1661-
// Before the Osaka fork, reject the blob txs with cell proofs
1662-
if !isOsaka {
1663-
if tx.BlobTxSidecar().Version == types.BlobSidecarVersion0 {
1664-
return nil
1665-
} else {
1666-
return errors.New("cell proof is not supported yet")
1667-
}
1668-
}
1669-
// After the Osaka fork, reject the legacy blob txs if the conversion
1670-
// time window is passed.
1671-
if tx.BlobTxSidecar().Version == types.BlobSidecarVersion1 {
1672-
return nil
1673-
}
1674-
if head.Time > uint64(deadline.Unix()) {
1675-
return errors.New("legacy blob tx is not supported")
1470+
if tx.BlobTxSidecar().Version != version {
1471+
return fmt.Errorf("sidecar version is not supported, got: %d, want: %d", tx.BlobTxSidecar().Version, version)
16761472
}
1677-
// Convert the legacy sidecar after Osaka fork. This could be a long
1678-
// procedure which takes a few seconds, even minutes if there is a long
1679-
// queue. Fortunately it will only block the routine of the source peer
1680-
// announcing the tx, without affecting other parts.
1681-
return p.cQueue.convert(tx)
1473+
return nil
16821474
}
16831475

16841476
// Add inserts a set of blob transactions into the pool if they pass validation (both

0 commit comments

Comments
 (0)