Skip to content
2 changes: 1 addition & 1 deletion op-chain-ops/cmd/check-ecotone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ func checkBlobTxDenial(ctx context.Context, env *actionEnv) error {
for i := 0; i < 4096; i++ {
blob[32*i] &= 0b0011_1111
}
sidecar, blobHashes, err := txmgr.MakeSidecar([]*eth.Blob{&blob})
sidecar, blobHashes, err := txmgr.MakeSidecar([]*eth.Blob{&blob}, false)
if err != nil {
return fmt.Errorf("failed to make sidecar: %w", err)
}
Expand Down
7 changes: 5 additions & 2 deletions op-e2e/actions/helpers/l2_batcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ type BatcherCfg struct {

DataAvailabilityType batcherFlags.DataAvailabilityType
AltDA AltDAInputSetter

EnableCellProofs bool
}

func DefaultBatcherCfg(dp *e2eutils.DeployParams) *BatcherCfg {
Expand All @@ -71,6 +73,7 @@ func DefaultBatcherCfg(dp *e2eutils.DeployParams) *BatcherCfg {
MaxL1TxSize: 128_000,
BatcherKey: dp.Secrets.Batcher,
DataAvailabilityType: batcherFlags.CalldataType,
EnableCellProofs: false, // TODO change to true when Osaka activates on L1
}
}

Expand Down Expand Up @@ -372,7 +375,7 @@ func (s *L2Batcher) ActL2BatchSubmitRaw(t Testing, payload []byte, txOpts ...fun
} else if s.l2BatcherCfg.DataAvailabilityType == batcherFlags.BlobsType {
var b eth.Blob
require.NoError(t, b.FromData(payload), "must turn data into blob")
sidecar, blobHashes, err := txmgr.MakeSidecar([]*eth.Blob{&b})
sidecar, blobHashes, err := txmgr.MakeSidecar([]*eth.Blob{&b}, s.l2BatcherCfg.EnableCellProofs)
require.NoError(t, err)
require.NotNil(t, pendingHeader.ExcessBlobGas, "need L1 header with 4844 properties")
blobBaseFee := eth.CalcBlobFeeDefault(pendingHeader)
Expand Down Expand Up @@ -456,7 +459,7 @@ func (s *L2Batcher) ActL2BatchSubmitMultiBlob(t Testing, numBlobs int) {
require.NoError(t, err, "need l1 pending header for gas price estimation")
gasFeeCap := new(big.Int).Add(gasTipCap, new(big.Int).Mul(pendingHeader.BaseFee, big.NewInt(2)))

sidecar, blobHashes, err := txmgr.MakeSidecar(blobs)
sidecar, blobHashes, err := txmgr.MakeSidecar(blobs, s.l2BatcherCfg.EnableCellProofs)
require.NoError(t, err)
require.NotNil(t, pendingHeader.ExcessBlobGas, "need L1 header with 4844 properties")
blobBaseFee := eth.CalcBlobFeeDefault(pendingHeader)
Expand Down
17 changes: 17 additions & 0 deletions op-service/txmgr/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
TxNotInMempoolTimeoutFlagName = "txmgr.not-in-mempool-timeout"
ReceiptQueryIntervalFlagName = "txmgr.receipt-query-interval"
AlreadyPublishedCustomErrsFlagName = "txmgr.already-published-custom-errs"
EnableCellProofsFlagName = "txmgr.enable-cell-proofs"
)

var (
Expand Down Expand Up @@ -76,6 +77,7 @@ type DefaultFlagValues struct {
TxSendTimeout time.Duration
TxNotInMempoolTimeout time.Duration
ReceiptQueryInterval time.Duration
EnableCellProofs bool
}

var (
Expand All @@ -94,6 +96,7 @@ var (
TxSendTimeout: 0, // Try sending txs indefinitely, to preserve tx ordering for Holocene
TxNotInMempoolTimeout: 2 * time.Minute,
ReceiptQueryInterval: 12 * time.Second,
EnableCellProofs: false, // Ater Osaka activates on L1, this should be set to true
}
DefaultChallengerFlagValues = DefaultFlagValues{
NumConfirmations: uint64(3),
Expand Down Expand Up @@ -238,6 +241,12 @@ func CLIFlagsWithDefaults(envPrefix string, defaults DefaultFlagValues) []cli.Fl
Usage: "List of custom RPC error messages that indicate that a transaction has already been published.",
EnvVars: prefixEnvVars("TXMGR_ALREADY_PUBLISHED_CUSTOM_ERRS"),
},
&cli.BoolFlag{
Name: EnableCellProofsFlagName,
Usage: "Enable cell proofs in blob transactions for Fusaka (EIP-7742) compatibility",
Value: false,
EnvVars: prefixEnvVars("TXMGR_ENABLE_CELL_PROOFS"),
},
}, opsigner.CLIFlags(envPrefix, "")...)
}

Expand Down Expand Up @@ -266,6 +275,7 @@ type CLIConfig struct {
TxSendTimeout time.Duration
TxNotInMempoolTimeout time.Duration
AlreadyPublishedCustomErrs []string
EnableCellProofs bool
}

func NewCLIConfig(l1RPCURL string, defaults DefaultFlagValues) CLIConfig {
Expand All @@ -285,6 +295,7 @@ func NewCLIConfig(l1RPCURL string, defaults DefaultFlagValues) CLIConfig {
TxSendTimeout: defaults.TxSendTimeout,
TxNotInMempoolTimeout: defaults.TxNotInMempoolTimeout,
ReceiptQueryInterval: defaults.ReceiptQueryInterval,
EnableCellProofs: defaults.EnableCellProofs,
SignerCLIConfig: opsigner.NewCLIConfig(),
}
}
Expand Down Expand Up @@ -367,6 +378,7 @@ func ReadCLIConfig(ctx *cli.Context) CLIConfig {
TxSendTimeout: ctx.Duration(TxSendTimeoutFlagName),
TxNotInMempoolTimeout: ctx.Duration(TxNotInMempoolTimeoutFlagName),
AlreadyPublishedCustomErrs: ctx.StringSlice(AlreadyPublishedCustomErrsFlagName),
EnableCellProofs: ctx.Bool(EnableCellProofsFlagName),
}
}

Expand Down Expand Up @@ -460,6 +472,7 @@ func NewConfig(cfg CLIConfig, l log.Logger) (*Config, error) {
res.MinTipCap.Store(minTipCap)
res.MaxTipCap.Store(maxTipCap)
res.MinBlobTxFee.Store(defaultMinBlobTxFee)
res.EnableCellProofs.Store(cfg.EnableCellProofs)

return &res, nil
}
Expand Down Expand Up @@ -498,6 +511,10 @@ type Config struct {

MinBlobTxFee atomic.Pointer[big.Int]

// EnableCellProofs determines whether to use cell proofs (Version1 sidecars)
// for Fusaka (EIP-7742) compatibility. If false, uses legacy blob proofs (Version0).
EnableCellProofs atomic.Bool

// ChainID is the chain ID of the L1 chain.
ChainID *big.Int

Expand Down
2 changes: 1 addition & 1 deletion op-service/txmgr/test_txmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (m *TestTxManager) makeStuckTx(ctx context.Context, candidate TxCandidate)
var sidecar *types.BlobTxSidecar
var blobHashes []common.Hash
if len(candidate.Blobs) > 0 {
if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs); err != nil {
if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs, m.cfg.EnableCellProofs.Load()); err != nil {
return nil, err
}
}
Expand Down
54 changes: 44 additions & 10 deletions op-service/txmgr/txmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (m *SimpleTxManager) Close() {
}

func (m *SimpleTxManager) txLogger(tx *types.Transaction, logGas bool) log.Logger {
fields := []any{"tx", tx.Hash(), "nonce", tx.Nonce()}
fields := []any{"tx", tx.Hash().Hex(), "nonce", tx.Nonce()}
if logGas {
fields = append(fields, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap(), "gasLimit", tx.Gas())
}
Expand Down Expand Up @@ -363,7 +363,9 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (*
if candidate.To == nil {
return nil, errors.New("blob txs cannot deploy contracts")
}
if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs); err != nil {
// Use configuration to determine whether to enable cell proofs
enableCellProofs := m.cfg.EnableCellProofs.Load()
if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs, enableCellProofs); err != nil {
return nil, fmt.Errorf("failed to make sidecar: %w", err)
}
}
Expand Down Expand Up @@ -491,10 +493,21 @@ func (m *SimpleTxManager) SetBumpFeeRetryTime(val time.Duration) {
}

// MakeSidecar builds & returns the BlobTxSidecar and corresponding blob hashes from the raw blob
// data.
func MakeSidecar(blobs []*eth.Blob) (*types.BlobTxSidecar, []common.Hash, error) {
sidecar := &types.BlobTxSidecar{}
// data with configurable cell proof support.
func MakeSidecar(blobs []*eth.Blob, enableCellProofs bool) (*types.BlobTxSidecar, []common.Hash, error) {
var sidecar *types.BlobTxSidecar
if enableCellProofs {
sidecar = &types.BlobTxSidecar{
Version: types.BlobSidecarVersion1, // Use Version1 for cell proofs (Fusaka compatibility)
}
} else {
sidecar = &types.BlobTxSidecar{
Version: types.BlobSidecarVersion0, // Use Version0 for legacy blob proofs
}
}

blobHashes := make([]common.Hash, 0, len(blobs))

for i, blob := range blobs {
rawBlob := blob.KZGBlob()
sidecar.Blobs = append(sidecar.Blobs, *rawBlob)
Expand All @@ -503,13 +516,34 @@ func MakeSidecar(blobs []*eth.Blob) (*types.BlobTxSidecar, []common.Hash, error)
return nil, nil, fmt.Errorf("cannot compute KZG commitment of blob %d in tx candidate: %w", i, err)
}
sidecar.Commitments = append(sidecar.Commitments, commitment)
proof, err := kzg4844.ComputeBlobProof(rawBlob, commitment)
if err != nil {
return nil, nil, fmt.Errorf("cannot compute KZG proof for fast commitment verification of blob %d in tx candidate: %w", i, err)
}
sidecar.Proofs = append(sidecar.Proofs, proof)
blobHashes = append(blobHashes, eth.KZGToVersionedHash(commitment))
}

// Compute proofs based on the sidecar version
if enableCellProofs {
// Version1: Use cell proofs for Fusaka compatibility
allCellProofs := make([]kzg4844.Proof, 0, len(blobs)*kzg4844.CellProofsPerBlob)
for i, blob := range blobs {
rawBlob := blob.KZGBlob()
cellProofs, err := kzg4844.ComputeCellProofs(rawBlob)
if err != nil {
return nil, nil, fmt.Errorf("cannot compute KZG cell proofs for blob %d in tx candidate: %w", i, err)
}
allCellProofs = append(allCellProofs, cellProofs...)
}
sidecar.Proofs = allCellProofs
} else {
// Version0: Use legacy blob proofs
for i, blob := range blobs {
rawBlob := blob.KZGBlob()
proof, err := kzg4844.ComputeBlobProof(rawBlob, sidecar.Commitments[i])
if err != nil {
return nil, nil, fmt.Errorf("cannot compute KZG proof for fast commitment verification of blob %d in tx candidate: %w", i, err)
}
sidecar.Proofs = append(sidecar.Proofs, proof)
}
}

return sidecar, blobHashes, nil
}

Expand Down
17 changes: 16 additions & 1 deletion op-service/txmgr/txmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1736,7 +1736,9 @@ func TestMakeSidecar(t *testing.T) {
for i := 0; i < 4096; i++ {
blob[32*i] &= 0b0011_1111
}
sidecar, hashes, err := MakeSidecar([]*eth.Blob{&blob})

// Pre Fusaka, blob proof sidecar is Version0
sidecar, hashes, err := MakeSidecar([]*eth.Blob{&blob}, false)
require.NoError(t, err)
require.Equal(t, len(hashes), 1)
require.Equal(t, len(sidecar.Blobs), len(hashes))
Expand All @@ -1747,6 +1749,19 @@ func TestMakeSidecar(t *testing.T) {
require.NoError(t, eth.VerifyBlobProof((*eth.Blob)(&sidecar.Blobs[i]), commit, sidecar.Proofs[i]), "proof must be valid")
require.Equal(t, hashes[i], eth.KZGToVersionedHash(commit))
}

// Post Fusaka, blob proof sidecar is Version1
sidecar, hashes, err = MakeSidecar([]*eth.Blob{&blob}, true)
require.NoError(t, err)
require.Equal(t, len(hashes), 1)
require.Equal(t, len(sidecar.Blobs), len(hashes))
require.Equal(t, len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob)
require.Equal(t, len(sidecar.Commitments), len(hashes))

require.NoError(t, kzg4844.VerifyCellProofs(sidecar.Blobs, sidecar.Commitments, sidecar.Proofs), "cell proof must be valid")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems that the blob verification in l1 beacon client also needs to support cell proofs
https://github.com/ethereum-optimism/optimism/blob/develop/op-service/sources/l1_beacon_client.go#L300

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for raising this @pandainzoo. I don't think any changes are necessary here, in fact. Although when submitting blob transactions, it is true that blob proofs will be replaced by cell proofs, the beacon API actually remains unchanged with Fulu https://ethereum.github.io/beacon-APIs/#/Beacon/getBlobSidecars. So we will get a blob proof returned and can continue to verify it in the way we currently do.

Relevant PR here from lighthouse sigp/lighthouse#6755

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the answer

for i, commit := range sidecar.Commitments {
require.Equal(t, hashes[i], eth.KZGToVersionedHash(commit))
}
}

func TestSendAsyncUnbufferedChan(t *testing.T) {
Expand Down