From c4be2bbd19c93e4112eccfd41f0ead6d36ca21c1 Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 20 Mar 2025 15:53:12 -0700 Subject: [PATCH] Implement fulu related change to getblobsv1 and getpayloadv4 --- beacon/engine/types.go | 7 +++- core/txpool/blobpool/blobpool.go | 65 ++++++++++++++++++++++++++++++++ core/txpool/validation.go | 12 +++--- eth/catalyst/api.go | 29 +++++++++++--- 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 984090ef89..32cb7ca152 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -116,11 +116,13 @@ type BlobsBundleV1 struct { Commitments []hexutil.Bytes `json:"commitments"` Proofs []hexutil.Bytes `json:"proofs"` Blobs []hexutil.Bytes `json:"blobs"` + CellProofs []hexutil.Bytes `json:"cellProofs"` // Added in Osaka for 7594 } type BlobAndProofV1 struct { - Blob hexutil.Bytes `json:"blob"` - Proof hexutil.Bytes `json:"proof"` + Blob hexutil.Bytes `json:"blob"` + Proof hexutil.Bytes `json:"proof"` + CellProofs []hexutil.Bytes `json:"cellProofs"` // Added in Osaka for 7594 } // JSON type overrides for ExecutionPayloadEnvelope. @@ -326,6 +328,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. Commitments: make([]hexutil.Bytes, 0), Blobs: make([]hexutil.Bytes, 0), Proofs: make([]hexutil.Bytes, 0), + CellProofs: make([]hexutil.Bytes, 0), } for _, sidecar := range sidecars { for j := range sidecar.Blobs { diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 59a5645040..96f59f132f 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -29,6 +29,8 @@ import ( "sync" "time" + gokzg4844 "github.com/crate-crypto/go-eth-kzg" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" @@ -1239,7 +1241,12 @@ func (p *BlobPool) GetRLP(hash common.Hash) []byte { // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. +// After Osaka, the proofs are returned as cell proofs. func (p *BlobPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { + if p.chain.Config().IsOsaka(p.head.Number, p.head.Time) { + return p.GetBlobsWithCellProofs(vhashes) + } + // Create a map of the blob hash to indices for faster fills var ( blobs = make([]*kzg4844.Blob, len(vhashes)) @@ -1285,6 +1292,64 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844. } } } + + return blobs, proofs +} + +// GetBlobsWithCellProofs returns a number of blobs and cell proofs for the given versioned hashes after Osaka. +func (p *BlobPool) GetBlobsWithCellProofs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { + var ( + blobs = make([]*kzg4844.Blob, len(vhashes)) + proofs = make([]*kzg4844.Proof, len(vhashes)*gokzg4844.CellsPerExtBlob) + ) + + index := make(map[common.Hash]int) + for i, vhash := range vhashes { + index[vhash] = i + } + + for i, vhash := range vhashes { + if blobs[i] != nil { + continue + } + + p.lock.RLock() + id, exists := p.lookup.storeidOfBlob(vhash) + if !exists { + p.lock.RUnlock() + continue + } + data, err := p.store.Get(id) + p.lock.RUnlock() + + if err != nil { + log.Error("Tracked blob transaction missing from store", "id", id, "err", err) + continue + } + item := new(types.Transaction) + if err = rlp.DecodeBytes(data, item); err != nil { + log.Error("Blobs corrupted for traced transaction", "id", id, "err", err) + continue + } + sidecar := item.BlobTxSidecar() + if len(sidecar.Blobs)*gokzg4844.CellsPerExtBlob != len(sidecar.Proofs) { + log.Warn("potentially old blob tx with blob proofs, dropping it") + //TODO: implement dropping of blob txs + continue + } + for j, blobhash := range item.BlobHashes() { + if idx, ok := index[blobhash]; ok { + blobs[idx] = &sidecar.Blobs[j] + + proofStart := idx * gokzg4844.CellsPerExtBlob + sidecarStart := j * gokzg4844.CellsPerExtBlob + for k := 0; k < gokzg4844.CellsPerExtBlob; k++ { + proofs[proofStart+k] = &sidecar.Proofs[sidecarStart+k] + } + } + } + } + return blobs, proofs } diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 740245fd78..0b348b1f73 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -169,7 +169,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types func validateBlobSidecar(cfg *params.ChainConfig, time uint64, hashes []common.Hash, sidecar *types.BlobTxSidecar) error { if len(sidecar.Blobs) != len(hashes) { - return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", ErrInvalidBlobSidecar, len(sidecar.Blobs), len(hashes)) + return fmt.Errorf("%w: invalid number of %d blobs compared to %d blob hashes", ErrInvalidBlobSidecar, len(sidecar.Blobs), len(hashes)) } if err := sidecar.ValidateBlobCommitmentHashes(hashes); err != nil { return err @@ -178,7 +178,7 @@ func validateBlobSidecar(cfg *params.ChainConfig, time uint64, hashes []common.H fork := cfg.LatestFork(time) if fork >= forks.Osaka { if len(sidecar.Blobs)*gokzg4844.CellsPerExtBlob != len(sidecar.Proofs) { - return fmt.Errorf("invalid number of %d cell proofs compared to %d blobs", ErrInvalidBlobSidecar, len(sidecar.Proofs), len(sidecar.Blobs)) + return fmt.Errorf("%w: invalid number of %d cell proofs compared to %d blobs", ErrInvalidBlobSidecar, len(sidecar.Proofs), len(sidecar.Blobs)) } // Prepare cells and cell indices for each blob @@ -189,7 +189,7 @@ func validateBlobSidecar(cfg *params.ChainConfig, time uint64, hashes []common.H // Compute cells for the currentblob blobCells, err := kzg4844.ComputeCells(&sidecar.Blobs[i]) if err != nil { - return fmt.Errorf("failed to compute cells for blob %d: %v", ErrInvalidBlobSidecar, i, err) + return fmt.Errorf("%w: failed to compute cells for blob %d: %v", ErrInvalidBlobSidecar, i, err) } cells = append(cells, blobCells...) @@ -199,18 +199,18 @@ func validateBlobSidecar(cfg *params.ChainConfig, time uint64, hashes []common.H } if err := kzg4844.VerifyCellKZGProofBatch(sidecar.Commitments, cellIndices, cells, sidecar.Proofs); err != nil { - return fmt.Errorf("failed to verify cell proofs: %v", ErrInvalidBlobSidecar, err) + return fmt.Errorf("%w: failed to verify cell proofs: %v", ErrInvalidBlobSidecar, err) } } else { // older folks if len(sidecar.Proofs) != len(hashes) { - return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", ErrInvalidBlobSidecar, len(sidecar.Proofs), len(hashes)) + return fmt.Errorf("%w: invalid number of %d blob proofs compared to %d blob hashes", ErrInvalidBlobSidecar, len(sidecar.Proofs), len(hashes)) } // Blob commitments match with the hashes in the transaction, verify the // blobs themselves via KZG for i := range sidecar.Blobs { if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil { - return fmt.Errorf("invalid blob %d: %v", ErrInvalidBlobSidecar, i, err) + return fmt.Errorf("%w: invalid blob %d: %v", ErrInvalidBlobSidecar, i, err) } } } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index e6f29c970b..52c790f15c 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -24,6 +24,8 @@ import ( "sync" "time" + gokzg4844 "github.com/crate-crypto/go-eth-kzg" + "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -536,14 +538,31 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo res := make([]*engine.BlobAndProofV1, len(hashes)) blobs, proofs := api.eth.TxPool().GetBlobs(hashes) - for i := 0; i < len(blobs); i++ { - if blobs[i] != nil { - res[i] = &engine.BlobAndProofV1{ - Blob: (*blobs[i])[:], - Proof: (*proofs[i])[:], + // after Osaka, the proofs are returned as cell proofs. + if len(blobs) != len(proofs) { + for i := range blobs { + if blobs[i] != nil { + cellProofs := make([]hexutil.Bytes, gokzg4844.CellsPerExtBlob) + for j := range cellProofs { + cellProofs[j] = hexutil.Bytes((*proofs[i*gokzg4844.CellsPerExtBlob+j])[:]) + } + res[i] = &engine.BlobAndProofV1{ + Blob: (*blobs[i])[:], + CellProofs: cellProofs, + } + } + } + } else { + for i := 0; i < len(blobs); i++ { + if blobs[i] != nil { + res[i] = &engine.BlobAndProofV1{ + Blob: (*blobs[i])[:], + Proof: (*proofs[i])[:], + } } } } + return res, nil }