add: remove parity cells before storing blobs to the blobpool

This commit is contained in:
healthykim 2025-09-11 16:43:05 +09:00
parent 435ad70195
commit 61c55eb1e5
5 changed files with 61 additions and 16 deletions

View file

@ -174,7 +174,7 @@ func (ptx *PooledBlobTx) Convert() (*types.Transaction, error) {
return nil, errors.New("cell sidecar missing") return nil, errors.New("cell sidecar missing")
} }
cellSidecar := ptx.Sidecar cellSidecar := ptx.Sidecar
blobs, err := kzg4844.RecoverBlobs(cellSidecar.Cells, cellSidecar.CellIndices.Indices()) blobs, err := kzg4844.RecoverBlobs(cellSidecar.Cells, cellSidecar.Custody.Indices())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -183,6 +183,35 @@ func (ptx *PooledBlobTx) Convert() (*types.Transaction, error) {
return ptx.Transaction.WithBlobTxSidecar(sidecar), nil return ptx.Transaction.WithBlobTxSidecar(sidecar), nil
} }
func (ptx *PooledBlobTx) RemoveParity() error {
sc := ptx.Sidecar
custodySize := sc.Custody.OneCount()
if custodySize == 0 {
return errors.New("blobless transaction")
}
blobCount := len(sc.Cells) / custodySize
var cellsWithoutParity []kzg4844.Cell
pos := 0
for range blobCount {
for bit := 0; bit < kzg4844.CellsPerBlob; bit++ {
if sc.Custody.IsSet(uint(bit)) {
cell := sc.Cells[pos]
pos++
if bit < kzg4844.DataPerBlob {
cellsWithoutParity = append(cellsWithoutParity, cell)
}
}
}
}
sc.Cells = cellsWithoutParity
for bit := 64; bit < kzg4844.CellsPerBlob; bit++ {
sc.Custody.Clear(uint(bit))
}
return nil
}
// BlobPool is the transaction pool dedicated to EIP-4844 blob transactions. // BlobPool is the transaction pool dedicated to EIP-4844 blob transactions.
// //
// Blob transactions are special snowflakes that are designed for a very specific // Blob transactions are special snowflakes that are designed for a very specific
@ -1204,9 +1233,11 @@ func (p *BlobPool) checkDelegationLimit(tx *types.Transaction) error {
// validateTx checks whether a transaction is valid according to the consensus // validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size). // rules and adheres to some heuristic limits of the local node (price and size).
func (p *BlobPool) validateTx(tx *types.Transaction, cellSidecars *types.BlobTxCellSidecar) error { func (p *BlobPool) validateTx(tx *types.Transaction, cellSidecar *types.BlobTxCellSidecar) error {
if tx.BlobTxSidecar() != nil || cellSidecar == nil {
if err := txpool.ValidateBlobSidecar(tx, cellSidecars, p.head, &txpool.ValidationOptions{ return errors.New("malformed transaction and cellSidecar")
}
if err := txpool.ValidateBlobSidecar(tx, cellSidecar, p.head, &txpool.ValidationOptions{
Config: p.chain.Config(), Config: p.chain.Config(),
MaxBlobCount: maxBlobsPerTx, MaxBlobCount: maxBlobsPerTx,
}); err != nil { }); err != nil {
@ -1607,6 +1638,9 @@ func (p *BlobPool) add(tx *types.Transaction, cellSidecar *types.BlobTxCellSidec
// Transaction permitted into the pool from a nonce and cost perspective, // Transaction permitted into the pool from a nonce and cost perspective,
// insert it into the database and update the indices // insert it into the database and update the indices
pooledTx := NewPooledBlobTx(tx, cellSidecar) pooledTx := NewPooledBlobTx(tx, cellSidecar)
if pooledTx.RemoveParity() != nil {
return err
}
blob, err := rlp.EncodeToBytes(pooledTx) blob, err := rlp.EncodeToBytes(pooledTx)
if err != nil { if err != nil {

View file

@ -150,7 +150,7 @@ func ValidateBlobSidecar(tx *types.Transaction, sidecar *types.BlobTxCellSidecar
return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice) return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice)
} }
// Verify whether the blob count is consistent with other parts of the sidecar and the transaction // Verify whether the blob count is consistent with other parts of the sidecar and the transaction
blobCount := len(sidecar.Cells) / sidecar.CellIndices.OneCount() blobCount := len(sidecar.Cells) / sidecar.Custody.OneCount()
hashes := tx.BlobHashes() hashes := tx.BlobHashes()
if blobCount == 0 { if blobCount == 0 {
return errors.New("blobless blob transaction") return errors.New("blobless blob transaction")
@ -181,7 +181,7 @@ func validateBlobSidecarLegacy(sidecar *types.BlobTxCellSidecar, hashes []common
if len(sidecar.Proofs) != len(hashes) { if len(sidecar.Proofs) != len(hashes) {
return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)) return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes))
} }
blobs, err := kzg4844.RecoverBlobs(sidecar.Cells, sidecar.CellIndices.Indices()) blobs, err := kzg4844.RecoverBlobs(sidecar.Cells, sidecar.Custody.Indices())
if err != nil { if err != nil {
return err return err
} }
@ -200,7 +200,7 @@ func validateBlobSidecarOsaka(sidecar *types.BlobTxCellSidecar, hashes []common.
if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob { if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob {
return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob) return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob)
} }
return kzg4844.VerifyCells(sidecar.Cells, sidecar.Commitments, sidecar.Proofs, sidecar.CellIndices.Indices()) return kzg4844.VerifyCells(sidecar.Cells, sidecar.Commitments, sidecar.Proofs, sidecar.Custody.Indices())
} }
// ValidationOptionsWithState define certain differences between stateful transaction // ValidationOptionsWithState define certain differences between stateful transaction

View file

@ -3,15 +3,15 @@ package types
import ( import (
"errors" "errors"
"math/bits" "math/bits"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
) )
// `CustodyBitmap` is a bitmap to represent which custody index to store (little endian) // `CustodyBitmap` is a bitmap to represent which custody index to store (little endian)
type CustodyBitmap [16]byte type CustodyBitmap [16]byte
const CustodySize = 128
func (b CustodyBitmap) IsSet(i uint) bool { func (b CustodyBitmap) IsSet(i uint) bool {
if i >= CustodySize { if i >= uint(kzg4844.CellsPerBlob) {
return false return false
} }
index := i / 8 index := i / 8
@ -21,7 +21,7 @@ func (b CustodyBitmap) IsSet(i uint) bool {
// Set ith bit // Set ith bit
func (b *CustodyBitmap) Set(i uint) error { func (b *CustodyBitmap) Set(i uint) error {
if i >= CustodySize { if i >= uint(kzg4844.CellsPerBlob) {
return errors.New("bit index out of range") return errors.New("bit index out of range")
} }
index := i / 8 index := i / 8
@ -32,7 +32,7 @@ func (b *CustodyBitmap) Set(i uint) error {
// Clear ith bit // Clear ith bit
func (b *CustodyBitmap) Clear(i uint) error { func (b *CustodyBitmap) Clear(i uint) error {
if i >= CustodySize { if i >= uint(kzg4844.CellsPerBlob) {
return errors.New("bit index out of range") return errors.New("bit index out of range")
} }
index := i / 8 index := i / 8
@ -67,7 +67,7 @@ func (b CustodyBitmap) Indices() []uint64 {
func (b CustodyBitmap) SetIndices(indices []uint64) error { func (b CustodyBitmap) SetIndices(indices []uint64) error {
for _, i := range indices { for _, i := range indices {
if i >= CustodySize { if i >= uint64(kzg4844.CellsPerBlob) {
return errors.New("bit index out of range") return errors.New("bit index out of range")
} }
byteIdx := i / 8 byteIdx := i / 8
@ -83,3 +83,10 @@ func (b CustodyBitmap) SetAll() CustodyBitmap {
} }
return b return b
} }
func (b CustodyBitmap) SetData() CustodyBitmap {
for i := 0; i < kzg4844.DataPerBlob/8; i++ {
b[i] = 0xFF
}
return b
}

View file

@ -186,7 +186,7 @@ func (sc *BlobTxSidecar) ToBlobTxCellSidecar() (*BlobTxCellSidecar, error) {
Cells: cells, Cells: cells,
Commitments: sc.Commitments, Commitments: sc.Commitments,
Proofs: sc.Proofs, Proofs: sc.Proofs,
CellIndices: CustodyBitmap{}.SetAll(), Custody: CustodyBitmap{}.SetAll(),
}, nil }, nil
} }
@ -195,7 +195,7 @@ type BlobTxCellSidecar struct {
Cells []kzg4844.Cell Cells []kzg4844.Cell
Commitments []kzg4844.Commitment Commitments []kzg4844.Commitment
Proofs []kzg4844.Proof Proofs []kzg4844.Proof
CellIndices CustodyBitmap Custody CustodyBitmap
} }
// ValidateBlobCommitmentHashes checks whether the given hashes correspond to the // ValidateBlobCommitmentHashes checks whether the given hashes correspond to the

View file

@ -37,7 +37,11 @@ var (
cellT = reflect.TypeFor[Cell]() cellT = reflect.TypeFor[Cell]()
) )
const CellProofsPerBlob = 128 const (
CellProofsPerBlob = 128
CellsPerBlob = 128
DataPerBlob = 64
)
type Cell [2048]byte type Cell [2048]byte