Commit 6d5857cb authored by Andreas Bigger's avatar Andreas Bigger

use configurable gas estimation

parent 6c244bc7
......@@ -33,6 +33,14 @@ type Config struct {
TxManagerConfig txmgr.Config
NetworkTimeout time.Duration
// TxManagerTimeout is the maximum amount of time
// the driver should wait for the [txmgr] to send a transaction.
TxManagerTimeout time.Duration
// OfflineGasEstimation specifies whether the batcher should calculate
// gas estimations offline using the [core.IntrinsicGas] function.
OfflineGasEstimation bool
// RollupConfig is queried at startup
Rollup *rollup.Config
......@@ -111,6 +119,14 @@ type CLIConfig struct {
/* Optional Params */
// TxManagerTimeout is the max amount of time to wait for the [txmgr].
// This will default to: 10 * time.Minute.
TxManagerTimeout time.Duration
// OfflineGasEstimation specifies whether the batcher should calculate
// gas estimations offline using the [core.IntrinsicGas] function.
OfflineGasEstimation bool
// MaxL1TxSize is the maximum size of a batch tx submitted to L1.
MaxL1TxSize uint64
......@@ -169,6 +185,8 @@ func NewConfig(ctx *cli.Context) CLIConfig {
ResubmissionTimeout: ctx.GlobalDuration(flags.ResubmissionTimeoutFlag.Name),
/* Optional Flags */
OfflineGasEstimation: ctx.GlobalBool(flags.OfflineGasEstimationFlag.Name),
TxManagerTimeout: ctx.GlobalDuration(flags.TxManagerTimeoutFlag.Name),
MaxChannelDuration: ctx.GlobalUint64(flags.MaxChannelDurationFlag.Name),
MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeBytesFlag.Name),
TargetL1TxSize: ctx.GlobalUint64(flags.TargetL1TxSizeBytesFlag.Name),
......
......@@ -15,6 +15,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
......@@ -88,6 +89,7 @@ func NewBatchSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger, m metrics.Metri
RollupNode: rollupClient,
PollInterval: cfg.PollInterval,
TxManagerConfig: txManagerConfig,
TxManagerTimeout: cfg.TxManagerTimeout,
From: fromAddress,
Rollup: rcfg,
Channel: ChannelConfig{
......@@ -310,6 +312,7 @@ func (l *BatchSubmitter) loop() {
l.log.Error("unable to get tx data", "err", err)
break
}
// Record TX Status
if receipt, err := l.SendTransaction(l.ctx, txdata.Bytes()); err != nil {
l.recordFailedTx(txdata.ID(), err)
......@@ -338,6 +341,17 @@ func (l *BatchSubmitter) loop() {
// This is a blocking method. It should not be called concurrently.
// TODO: where to put concurrent transaction handling logic.
func (l *BatchSubmitter) SendTransaction(ctx context.Context, data []byte) (*types.Receipt, error) {
// Do the gas estimation offline if specified
var gas uint64
if l.OfflineGasEstimation {
intrinsicGas, err := core.IntrinsicGas(data, nil, false, true, true, false)
if err != nil {
return nil, fmt.Errorf("failed to calculate intrinsic gas: %w", err)
}
gas = intrinsicGas
}
// Create the transaction
tx, err := l.txMgr.CraftTx(ctx, txmgr.TxCandidate{
Recipient: l.Rollup.BatchInboxAddress,
TxData: data,
......@@ -345,14 +359,14 @@ func (l *BatchSubmitter) SendTransaction(ctx context.Context, data []byte) (*typ
ChainID: l.Rollup.L1ChainID,
// Explicit instantiation here so we can make a note that a gas
// limit of 0 will cause the [txmgr] to estimate the gas limit.
GasLimit: 0,
GasLimit: gas,
})
if err != nil {
return nil, fmt.Errorf("failed to create tx: %w", err)
}
// TODO: Select a timeout that makes sense here.
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
// Send the transaction through the txmgr
ctx, cancel := context.WithTimeout(ctx, l.TxManagerTimeout)
defer cancel()
if receipt, err := l.txMgr.Send(ctx, tx); err != nil {
l.log.Warn("unable to publish tx", "err", err, "data_size", len(data))
......
package flags
import (
"time"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/op-batcher/rpc"
......@@ -74,7 +76,17 @@ var (
}
/* Optional flags */
OfflineGasEstimationFlag = cli.BoolFlag{
Name: "offline-gas-estimation",
Usage: "Whether to use offline gas estimation",
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "OFFLINE_GAS_ESTIMATION"),
}
TxManagerTimeoutFlag = cli.DurationFlag{
Name: "tx-manager-timeout",
Usage: "Maximum duration to wait for L1 transactions, including resubmissions",
Value: 10 * time.Minute,
EnvVar: opservice.PrefixEnvVar(envVarPrefix, "TX_MANAGER_TIMEOUT"),
}
MaxChannelDurationFlag = cli.Uint64Flag{
Name: "max-channel-duration",
Usage: "The maximum duration of L1-blocks to keep a channel open. 0 to disable.",
......
......@@ -8,8 +8,8 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
......@@ -104,6 +104,9 @@ type ETHBackend interface {
// NonceAt returns the account nonce of the given account.
// The block number can be nil, in which case the nonce is taken from the latest known block.
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
/// EstimateGas returns an estimate of the amount of gas needed to execute the given
/// transaction against the current pending block.
EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error)
}
// SimpleTxManager is a implementation of TxManager that performs linear fee
......@@ -185,13 +188,25 @@ func (m *SimpleTxManager) CraftTx(ctx context.Context, candidate TxCandidate) (*
Data: candidate.TxData,
}
// Calculate the intrinsic gas for the transaction
m.l.Info("creating tx", "to", rawTx.To, "from", candidate.From)
gas, err := core.IntrinsicGas(rawTx.Data, nil, false, true, true, false)
// If the gas limit is set, we can use that as the gas
if candidate.GasLimit != 0 {
rawTx.Gas = candidate.GasLimit
} else {
// Calculate the intrinsic gas for the transaction
gas, err := m.backend.EstimateGas(ctx, ethereum.CallMsg{
From: candidate.From,
To: &candidate.Recipient,
GasFeeCap: gasFeeCap,
GasTipCap: gasFeeCap,
Data: rawTx.Data,
})
if err != nil {
return nil, fmt.Errorf("failed to calculate intrinsic gas: %w", err)
return nil, fmt.Errorf("failed to estimate gas: %w", err)
}
rawTx.Gas = gas
}
ctx, cancel = context.WithTimeout(ctx, m.Config.NetworkTimeout)
defer cancel()
......
......@@ -14,6 +14,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
......@@ -175,6 +176,10 @@ func (b *mockBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*typ
}, nil
}
func (b *mockBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
return b.g.basefee().Uint64(), nil
}
func (b *mockBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
tip, _ := b.g.sample()
return tip, nil
......@@ -580,6 +585,10 @@ func (b *failingBackend) SuggestGasTipCap(_ context.Context) (*big.Int, error) {
return b.gasTip, nil
}
func (b *failingBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
return b.baseFee.Uint64(), nil
}
func (b *failingBackend) NonceAt(_ context.Context, _ common.Address, _ *big.Int) (uint64, error) {
return 0, errors.New("unimplemented")
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment