diff --git a/go.mod b/go.mod index 826e3bddc27f..743034cfc9b3 100644 --- a/go.mod +++ b/go.mod @@ -90,6 +90,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/ethstorage/da-server v0.0.0-20240628094857-ed2ee4ff52d9 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/ferranbt/fastssz v0.1.2 // indirect @@ -205,6 +206,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/urfave/cli v1.22.14 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.uber.org/automaxprocs v1.5.2 // indirect diff --git a/go.sum b/go.sum index 190e9eaaaa93..298a3b03d0c0 100644 --- a/go.sum +++ b/go.sum @@ -181,6 +181,8 @@ github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-2024052213450 github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240522134500-19555bdbdc95/go.mod h1:7xh2awFQqsiZxFrHKTgEd+InVfDRrkKVUIuK8SAFHp0= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethstorage/da-server v0.0.0-20240628094857-ed2ee4ff52d9 h1:yRJRj81rP83R5dZEsYitAFcqRagK/e+UxhiSSK4/Wds= +github.com/ethstorage/da-server v0.0.0-20240628094857-ed2ee4ff52d9/go.mod h1:YeveZNzQFdseeqS0mQavqfyMI+U9l1GQu0CEwezTZA8= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= @@ -780,6 +782,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= @@ -797,6 +800,8 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= +github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index 3ad4c4db527a..7cd250cc2f65 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -289,6 +289,9 @@ type DeployConfig struct { // UseInterop is a flag that indicates if the system is using interop UseInterop bool `json:"useInterop,omitempty"` + // l2 blob related configs + EnableL2Blob bool `json:"enable_l2_blob,omitempty"` + DACURLS []string `json:"dac_urls,omitempty"` } // Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy @@ -618,6 +621,15 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas } } + var l2BlobConfig *rollup.L2BlobConfig + if d.EnableL2Blob { + l2BlobConfig = &rollup.L2BlobConfig{ + EnableL2Blob: true, + } + if len(d.DACURLS) > 0 { + l2BlobConfig.DACConfig = &rollup.DACConfig{URLS: d.DACURLS} + } + } return &rollup.Config{ Genesis: rollup.Genesis{ L1: eth.BlockID{ @@ -652,6 +664,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas FjordTime: d.FjordTime(l1StartBlock.Time()), InteropTime: d.InteropTime(l1StartBlock.Time()), PlasmaConfig: plasma, + L2BlobConfig: l2BlobConfig, }, nil } diff --git a/op-node/p2p/gossip.go b/op-node/p2p/gossip.go index 89920867eb60..6c6bdd6cf53d 100644 --- a/op-node/p2p/gossip.go +++ b/op-node/p2p/gossip.go @@ -362,13 +362,13 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti if blockVersion.HasBlobProperties() { // [REJECT] if the block is on a topic >= V3 and has a blob gas used value that is not zero - if payload.BlobGasUsed == nil || (payload.BlobGasUsed != nil && *payload.BlobGasUsed != 0) { + if payload.BlobGasUsed == nil || (!cfg.IsL2BlobEnabled() && *payload.BlobGasUsed != 0) { log.Warn("payload is on v3 topic, but has non-zero blob gas used", "bad_hash", payload.BlockHash.String(), "blob_gas_used", payload.BlobGasUsed) return pubsub.ValidationReject } // [REJECT] if the block is on a topic >= V3 and has an excess blob gas value that is not zero - if payload.ExcessBlobGas == nil || (payload.ExcessBlobGas != nil && *payload.ExcessBlobGas != 0) { + if payload.ExcessBlobGas == nil || (!cfg.IsL2BlobEnabled() && *payload.ExcessBlobGas != 0) { log.Warn("payload is on v3 topic, but has non-zero excess blob gas", "bad_hash", payload.BlockHash.String(), "excess_blob_gas", payload.ExcessBlobGas) return pubsub.ValidationReject } diff --git a/op-node/rollup/derive/engine_controller.go b/op-node/rollup/derive/engine_controller.go index c9f802c467b1..1c38c2f526d2 100644 --- a/op-node/rollup/derive/engine_controller.go +++ b/op-node/rollup/derive/engine_controller.go @@ -74,6 +74,8 @@ type EngineController struct { buildingInfo eth.PayloadInfo buildingSafe bool safeAttrs *AttributesWithParent + + dacClient rollup.DACClient } func NewEngineController(engine ExecEngine, log log.Logger, metrics Metrics, rollupCfg *rollup.Config, syncMode sync.Mode) *EngineController { @@ -82,6 +84,10 @@ func NewEngineController(engine ExecEngine, log log.Logger, metrics Metrics, rol syncStatus = syncStatusWillStartEL } + var dacClient rollup.DACClient + if dacConfig := rollupCfg.DACConfig(); dacConfig != nil { + dacClient = dacConfig.Client() + } return &EngineController{ engine: engine, log: log, @@ -91,6 +97,7 @@ func NewEngineController(engine ExecEngine, log log.Logger, metrics Metrics, rol syncMode: syncMode, syncStatus: syncStatus, clock: clock.SystemClock, + dacClient: dacClient, } } @@ -212,7 +219,7 @@ func (e *EngineController) ConfirmPayload(ctx context.Context, agossip async.Asy } // Update the safe head if the payload is built with the last attributes in the batch. updateSafe := e.buildingSafe && e.safeAttrs != nil && e.safeAttrs.isLastInSpan - envelope, errTyp, err := confirmPayload(ctx, e.log, e.engine, fc, e.buildingInfo, updateSafe, agossip, sequencerConductor) + envelope, errTyp, err := confirmPayload(ctx, e.log, e.engine, fc, e.buildingInfo, updateSafe, agossip, sequencerConductor, e.dacClient) if err != nil { return nil, errTyp, fmt.Errorf("failed to complete building on top of L2 chain %s, id: %s, error (%d): %w", e.buildingOnto, e.buildingInfo.ID, errTyp, err) } diff --git a/op-node/rollup/derive/engine_update.go b/op-node/rollup/derive/engine_update.go index b23a27004004..99af5f6ad0f3 100644 --- a/op-node/rollup/derive/engine_update.go +++ b/op-node/rollup/derive/engine_update.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/async" "github.com/ethereum-optimism/optimism/op-node/rollup/conductor" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -128,6 +129,7 @@ func confirmPayload( updateSafe bool, agossip async.AsyncGossiper, sequencerConductor conductor.SequencerConductor, + dacClient rollup.DACClient, ) (out *eth.ExecutionPayloadEnvelope, errTyp BlockInsertionErrType, err error) { var envelope *eth.ExecutionPayloadEnvelope // if the payload is available from the async gossiper, it means it was not yet imported, so we reuse it @@ -150,6 +152,18 @@ func confirmPayload( if err := sanityCheckPayload(payload); err != nil { return nil, BlockInsertPayloadErr, err } + if envelope.BlobsBundle != nil && len(envelope.BlobsBundle.Blobs) > 0 { + // When updateSafe, it must be deriving instead of sequencing. Deriving is based on onchain-data which doesn't contain L2 blob. + if updateSafe { + return nil, BlockInsertPayloadErr, fmt.Errorf("got blobs when updateSafe") + } + if dacClient != nil { + err = dacClient.UploadBlobs(ctx, envelope) + if err != nil { + return nil, BlockInsertTemporaryErr, fmt.Errorf("UploadBlobs failed: %w", err) + } + } + } if err := sequencerConductor.CommitUnsafePayload(ctx, envelope); err != nil { return nil, BlockInsertTemporaryErr, fmt.Errorf("failed to commit unsafe payload to conductor: %w", err) } diff --git a/op-node/rollup/derive/span_batch_tx.go b/op-node/rollup/derive/span_batch_tx.go index cd0a471ab266..872c7190c72e 100644 --- a/op-node/rollup/derive/span_batch_tx.go +++ b/op-node/rollup/derive/span_batch_tx.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" ) type spanBatchTxData interface { @@ -45,6 +46,18 @@ type spanBatchDynamicFeeTxData struct { func (txData *spanBatchDynamicFeeTxData) txType() byte { return types.DynamicFeeTxType } +type spanBatchBlobTxData struct { + Value *uint256.Int + GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas + GasFeeCap *uint256.Int // a.k.a. maxFeePerGas + Data []byte + AccessList types.AccessList + BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas + BlobHashes []common.Hash +} + +func (txData *spanBatchBlobTxData) txType() byte { return types.BlobTxType } + // Type returns the transaction type. func (tx *spanBatchTx) Type() uint8 { return tx.inner.txType() @@ -93,6 +106,13 @@ func (tx *spanBatchTx) decodeTyped(b []byte) (spanBatchTxData, error) { return nil, fmt.Errorf("failed to decode spanBatchDynamicFeeTxData: %w", err) } return &inner, nil + case types.BlobTxType: + var inner spanBatchBlobTxData + err := rlp.DecodeBytes(b[1:], &inner) + if err != nil { + return nil, fmt.Errorf("failed to decode spanBatchBlobTxData: %w", err) + } + return &inner, nil default: return nil, types.ErrTxTypeNotSupported } @@ -168,6 +188,40 @@ func (tx *spanBatchTx) convertToFullTx(nonce, gas uint64, to *common.Address, ch R: R, S: S, } + case types.BlobTxType: + if to == nil { + return nil, fmt.Errorf("invalid blob tx: to can't be nil") + } + VU256, overflow := uint256.FromBig(V) + if overflow { + return nil, fmt.Errorf("invalid blob tx: V overflow:%v", V) + } + RU256, overflow := uint256.FromBig(R) + if overflow { + return nil, fmt.Errorf("invalid blob tx: R overflow:%v", R) + } + SU256, overflow := uint256.FromBig(S) + if overflow { + return nil, fmt.Errorf("invalid blob tx: S overflow:%v", R) + } + batchTxInner := tx.inner.(*spanBatchBlobTxData) + inner = &types.BlobTx{ + ChainID: uint256.MustFromBig(chainID), + Nonce: nonce, + GasTipCap: batchTxInner.GasTipCap, + GasFeeCap: batchTxInner.GasFeeCap, + Gas: gas, + To: *to, + Value: batchTxInner.Value, + Data: batchTxInner.Data, + AccessList: batchTxInner.AccessList, + BlobFeeCap: batchTxInner.BlobFeeCap, + BlobHashes: batchTxInner.BlobHashes, + V: VU256, + R: RU256, + S: SU256, + } + default: return nil, fmt.Errorf("invalid tx type: %d", tx.Type()) } @@ -199,6 +253,32 @@ func newSpanBatchTx(tx types.Transaction) (*spanBatchTx, error) { Data: tx.Data(), AccessList: tx.AccessList(), } + case types.BlobTxType: + gasTipCap, overflow := uint256.FromBig(tx.GasTipCap()) + if overflow { + return nil, fmt.Errorf("tx.GasTipCap() overflow: %v", tx.GasTipCap()) + } + gasFeeCap, overflow := uint256.FromBig(tx.GasFeeCap()) + if overflow { + return nil, fmt.Errorf("tx.GasFeeCap() overflow: %v", tx.GasFeeCap()) + } + value, overflow := uint256.FromBig(tx.Value()) + if overflow { + return nil, fmt.Errorf("tx.Value() overflow: %v", tx.Value()) + } + blobFeeCap, overflow := uint256.FromBig(tx.BlobGasFeeCap()) + if overflow { + return nil, fmt.Errorf("tx.BlobGasFeeCap() overflow: %v", tx.BlobGasFeeCap()) + } + inner = &spanBatchBlobTxData{ + Value: value, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Data: tx.Data(), + AccessList: tx.AccessList(), + BlobFeeCap: blobFeeCap, + BlobHashes: tx.BlobHashes(), + } default: return nil, fmt.Errorf("invalid tx type: %d", tx.Type()) } diff --git a/op-node/rollup/derive/span_batch_txs.go b/op-node/rollup/derive/span_batch_txs.go index 305aafefe9da..59157ede44d8 100644 --- a/op-node/rollup/derive/span_batch_txs.go +++ b/op-node/rollup/derive/span_batch_txs.go @@ -271,6 +271,8 @@ func (btx *spanBatchTxs) recoverV(chainID *big.Int) error { v = bit case types.DynamicFeeTxType: v = bit + case types.BlobTxType: + v = bit default: return fmt.Errorf("invalid tx type: %d", txType) } @@ -386,6 +388,8 @@ func convertVToYParity(v uint64, txType int) (uint, error) { yParityBit = uint(v) case types.DynamicFeeTxType: yParityBit = uint(v) + case types.BlobTxType: + yParityBit = uint(v) default: return 0, fmt.Errorf("invalid tx type: %d", txType) } diff --git a/op-node/rollup/derive/span_batch_txs_test.go b/op-node/rollup/derive/span_batch_txs_test.go index 20f03899413d..642791d6a93e 100644 --- a/op-node/rollup/derive/span_batch_txs_test.go +++ b/op-node/rollup/derive/span_batch_txs_test.go @@ -333,6 +333,7 @@ func TestSpanBatchTxsRecoverV(t *testing.T) { chainID := big.NewInt(rng.Int63n(1000)) londonSigner := types.NewLondonSigner(chainID) + cancunSigner := types.NewCancunSigner(chainID) totalblockTxCount := 20 + rng.Intn(100) cases := []txTypeTest{ @@ -340,6 +341,7 @@ func TestSpanBatchTxsRecoverV(t *testing.T) { {"legacy tx", testutils.RandomLegacyTx, londonSigner}, {"access list tx", testutils.RandomAccessListTx, londonSigner}, {"dynamic fee tx", testutils.RandomDynamicFeeTx, londonSigner}, + {"blob tx", testutils.RandomBlobTx, cancunSigner}, } for _, testCase := range cases { diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index a72a0c07e003..be7f428fe3ee 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/ethstorage/da-server/pkg/da/client" plasma "github.com/ethereum-optimism/optimism/op-plasma" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -140,6 +141,36 @@ type Config struct { // LegacyUsePlasma is activated when the chain is in plasma mode. LegacyUsePlasma bool `json:"use_plasma,omitempty"` + + L2BlobConfig *L2BlobConfig `json:"l2_blob_config,omitempty"` +} + +type L2BlobConfig struct { + DACConfig *DACConfig `json:"dac_config,omitempty"` + EnableL2Blob bool `json:"enable_l2_blob,omitempty"` +} +type DACConfig struct { + URLS []string +} + +type DACClient interface { + UploadBlobs(context.Context, *eth.ExecutionPayloadEnvelope) error +} + +func (dacConfig *DACConfig) Client() DACClient { + + return client.New(dacConfig.URLS) +} + +func (cfg *Config) IsL2BlobEnabled() bool { + return cfg.L2BlobConfig != nil && cfg.L2BlobConfig.EnableL2Blob +} + +func (cfg *Config) DACConfig() *DACConfig { + if cfg.L2BlobConfig == nil { + return nil + } + return cfg.L2BlobConfig.DACConfig } // ValidateL1Config checks L1 config variables for errors. diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 81a95fc968f8..70fd3fcf81e8 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -161,8 +161,9 @@ type ( ) type ExecutionPayloadEnvelope struct { - ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot,omitempty"` - ExecutionPayload *ExecutionPayload `json:"executionPayload"` + ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot,omitempty"` + ExecutionPayload *ExecutionPayload `json:"executionPayload"` + BlobsBundle *engine.BlobsBundleV1 `json:"blobsBundle"` } type ExecutionPayload struct { @@ -238,6 +239,8 @@ func (envelope *ExecutionPayloadEnvelope) CheckBlockHash() (actual common.Hash, Nonce: types.BlockNonce{}, // zeroed, proof-of-work legacy BaseFee: (*uint256.Int)(&payload.BaseFeePerGas).ToBig(), ParentBeaconRoot: envelope.ParentBeaconBlockRoot, + BlobGasUsed: (*uint64)(payload.BlobGasUsed), + ExcessBlobGas: (*uint64)(payload.ExcessBlobGas), } if payload.CanyonBlock() { diff --git a/op-service/sources/engine_client.go b/op-service/sources/engine_client.go index 183356f03c53..6ac9793a9824 100644 --- a/op-service/sources/engine_client.go +++ b/op-service/sources/engine_client.go @@ -127,6 +127,8 @@ func (s *EngineAPIClient) NewPayload(ctx context.Context, payload *eth.Execution var err error switch method := s.evp.NewPayloadVersion(uint64(payload.Timestamp)); method { case eth.NewPayloadV3: + // now we pass empty array to skip checking versionedHashes + // TODO: sync with OP upstream once they support L2 blob tx err = s.RPC.CallContext(execCtx, &result, string(method), payload, []common.Hash{}, parentBeaconBlockRoot) case eth.NewPayloadV2: err = s.RPC.CallContext(execCtx, &result, string(method), payload) diff --git a/op-service/testutils/random.go b/op-service/testutils/random.go index f065d2583765..2fd9f8aa6e09 100644 --- a/op-service/testutils/random.go +++ b/op-service/testutils/random.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" + "github.com/holiman/uint256" ) func RandomBool(rng *rand.Rand) bool { @@ -225,6 +226,30 @@ func RandomDynamicFeeTx(rng *rand.Rand, signer types.Signer) *types.Transaction return RandomDynamicFeeTxWithBaseFee(rng, baseFee, signer) } +func RandomBlobTx(rng *rand.Rand, signer types.Signer) *types.Transaction { + baseFee := new(big.Int).SetUint64(rng.Uint64()) + key := InsecureRandomKey(rng) + tip := big.NewInt(rng.Int63n(10 * params.GWei)) + txData := &types.BlobTx{ + ChainID: uint256.MustFromBig(signer.ChainID()), + Nonce: rng.Uint64(), + GasTipCap: uint256.MustFromBig(tip), + GasFeeCap: uint256.MustFromBig(new(big.Int).Add(baseFee, tip)), + Gas: params.TxGas + uint64(rng.Int63n(2_000_000)), + To: RandomAddress(rng), + Value: uint256.MustFromBig(RandomETH(rng, 10)), + Data: RandomData(rng, rng.Intn(1000)), + AccessList: nil, + BlobFeeCap: uint256.MustFromBig(baseFee), + BlobHashes: []common.Hash{RandomHash(rng)}, + } + tx, err := types.SignNewTx(key, signer, txData) + if err != nil { + panic(err) + } + return tx +} + func RandomReceipt(rng *rand.Rand, signer types.Signer, tx *types.Transaction, txIndex uint64, cumulativeGasUsed uint64) *types.Receipt { gasUsed := params.TxGas + uint64(rng.Int63n(int64(tx.Gas()-params.TxGas+1))) logs := make([]*types.Log, rng.Intn(10)) diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index b3d2e4e70e15..89454d03dc26 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -260,34 +260,39 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (* gasLimit := candidate.GasLimit + var sidecar *types.BlobTxSidecar + var blobHashes []common.Hash + if len(candidate.Blobs) > 0 { + if candidate.To == nil { + return nil, errors.New("blob txs cannot deploy contracts") + } + if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs); err != nil { + return nil, fmt.Errorf("failed to make sidecar: %w", err) + } + } + // If the gas limit is set, we can use that as the gas if gasLimit == 0 { // Calculate the intrinsic gas for the transaction - gas, err := m.backend.EstimateGas(ctx, ethereum.CallMsg{ + callMsg := ethereum.CallMsg{ From: m.cfg.From, To: candidate.To, GasTipCap: gasTipCap, GasFeeCap: gasFeeCap, Data: candidate.TxData, Value: candidate.Value, - }) + } + if len(blobHashes) > 0 { + callMsg.BlobGasFeeCap = blobBaseFee + callMsg.BlobHashes = blobHashes + } + gas, err := m.backend.EstimateGas(ctx, callMsg) if err != nil { return nil, fmt.Errorf("failed to estimate gas: %w", err) } gasLimit = gas } - var sidecar *types.BlobTxSidecar - var blobHashes []common.Hash - if len(candidate.Blobs) > 0 { - if candidate.To == nil { - return nil, errors.New("blob txs cannot deploy contracts") - } - if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs); err != nil { - return nil, fmt.Errorf("failed to make sidecar: %w", err) - } - } - var txMessage types.TxData if sidecar != nil { if blobBaseFee == nil { @@ -646,14 +651,19 @@ func (m *SimpleTxManager) increaseGasPrice(ctx context.Context, tx *types.Transa } // Re-estimate gaslimit in case things have changed or a previous gaslimit estimate was wrong - gas, err := m.backend.EstimateGas(ctx, ethereum.CallMsg{ + callMsg := ethereum.CallMsg{ From: m.cfg.From, To: tx.To(), GasTipCap: bumpedTip, GasFeeCap: bumpedFee, Data: tx.Data(), Value: tx.Value(), - }) + } + if len(tx.BlobHashes()) > 0 { + callMsg.BlobGasFeeCap = tx.BlobGasFeeCap() + callMsg.BlobHashes = tx.BlobHashes() + } + gas, err := m.backend.EstimateGas(ctx, callMsg) if err != nil { // If this is a transaction resubmission, we sometimes see this outcome because the // original tx can get included in a block just before the above call. In this case the