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

use configurable gas estimation

parent 6c244bc7
...@@ -33,6 +33,14 @@ type Config struct { ...@@ -33,6 +33,14 @@ type Config struct {
TxManagerConfig txmgr.Config TxManagerConfig txmgr.Config
NetworkTimeout time.Duration 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 // RollupConfig is queried at startup
Rollup *rollup.Config Rollup *rollup.Config
...@@ -111,6 +119,14 @@ type CLIConfig struct { ...@@ -111,6 +119,14 @@ type CLIConfig struct {
/* Optional Params */ /* 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 is the maximum size of a batch tx submitted to L1.
MaxL1TxSize uint64 MaxL1TxSize uint64
...@@ -169,6 +185,8 @@ func NewConfig(ctx *cli.Context) CLIConfig { ...@@ -169,6 +185,8 @@ func NewConfig(ctx *cli.Context) CLIConfig {
ResubmissionTimeout: ctx.GlobalDuration(flags.ResubmissionTimeoutFlag.Name), ResubmissionTimeout: ctx.GlobalDuration(flags.ResubmissionTimeoutFlag.Name),
/* Optional Flags */ /* Optional Flags */
OfflineGasEstimation: ctx.GlobalBool(flags.OfflineGasEstimationFlag.Name),
TxManagerTimeout: ctx.GlobalDuration(flags.TxManagerTimeoutFlag.Name),
MaxChannelDuration: ctx.GlobalUint64(flags.MaxChannelDurationFlag.Name), MaxChannelDuration: ctx.GlobalUint64(flags.MaxChannelDurationFlag.Name),
MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeBytesFlag.Name), MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeBytesFlag.Name),
TargetL1TxSize: ctx.GlobalUint64(flags.TargetL1TxSizeBytesFlag.Name), TargetL1TxSize: ctx.GlobalUint64(flags.TargetL1TxSizeBytesFlag.Name),
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "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/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -88,6 +89,7 @@ func NewBatchSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger, m metrics.Metri ...@@ -88,6 +89,7 @@ func NewBatchSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger, m metrics.Metri
RollupNode: rollupClient, RollupNode: rollupClient,
PollInterval: cfg.PollInterval, PollInterval: cfg.PollInterval,
TxManagerConfig: txManagerConfig, TxManagerConfig: txManagerConfig,
TxManagerTimeout: cfg.TxManagerTimeout,
From: fromAddress, From: fromAddress,
Rollup: rcfg, Rollup: rcfg,
Channel: ChannelConfig{ Channel: ChannelConfig{
...@@ -310,6 +312,7 @@ func (l *BatchSubmitter) loop() { ...@@ -310,6 +312,7 @@ func (l *BatchSubmitter) loop() {
l.log.Error("unable to get tx data", "err", err) l.log.Error("unable to get tx data", "err", err)
break break
} }
// Record TX Status // Record TX Status
if receipt, err := l.SendTransaction(l.ctx, txdata.Bytes()); err != nil { if receipt, err := l.SendTransaction(l.ctx, txdata.Bytes()); err != nil {
l.recordFailedTx(txdata.ID(), err) l.recordFailedTx(txdata.ID(), err)
...@@ -338,6 +341,17 @@ func (l *BatchSubmitter) loop() { ...@@ -338,6 +341,17 @@ func (l *BatchSubmitter) loop() {
// This is a blocking method. It should not be called concurrently. // This is a blocking method. It should not be called concurrently.
// TODO: where to put concurrent transaction handling logic. // TODO: where to put concurrent transaction handling logic.
func (l *BatchSubmitter) SendTransaction(ctx context.Context, data []byte) (*types.Receipt, error) { 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{ tx, err := l.txMgr.CraftTx(ctx, txmgr.TxCandidate{
Recipient: l.Rollup.BatchInboxAddress, Recipient: l.Rollup.BatchInboxAddress,
TxData: data, TxData: data,
...@@ -345,14 +359,14 @@ func (l *BatchSubmitter) SendTransaction(ctx context.Context, data []byte) (*typ ...@@ -345,14 +359,14 @@ func (l *BatchSubmitter) SendTransaction(ctx context.Context, data []byte) (*typ
ChainID: l.Rollup.L1ChainID, ChainID: l.Rollup.L1ChainID,
// Explicit instantiation here so we can make a note that a gas // Explicit instantiation here so we can make a note that a gas
// limit of 0 will cause the [txmgr] to estimate the gas limit. // limit of 0 will cause the [txmgr] to estimate the gas limit.
GasLimit: 0, GasLimit: gas,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create tx: %w", err) return nil, fmt.Errorf("failed to create tx: %w", err)
} }
// TODO: Select a timeout that makes sense here. // Send the transaction through the txmgr
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute) ctx, cancel := context.WithTimeout(ctx, l.TxManagerTimeout)
defer cancel() defer cancel()
if receipt, err := l.txMgr.Send(ctx, tx); err != nil { if receipt, err := l.txMgr.Send(ctx, tx); err != nil {
l.log.Warn("unable to publish tx", "err", err, "data_size", len(data)) l.log.Warn("unable to publish tx", "err", err, "data_size", len(data))
......
package flags package flags
import ( import (
"time"
"github.com/urfave/cli" "github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/op-batcher/rpc" "github.com/ethereum-optimism/optimism/op-batcher/rpc"
...@@ -74,7 +76,17 @@ var ( ...@@ -74,7 +76,17 @@ var (
} }
/* Optional flags */ /* 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{ MaxChannelDurationFlag = cli.Uint64Flag{
Name: "max-channel-duration", Name: "max-channel-duration",
Usage: "The maximum duration of L1-blocks to keep a channel open. 0 to disable.", Usage: "The maximum duration of L1-blocks to keep a channel open. 0 to disable.",
......
...@@ -8,8 +8,8 @@ import ( ...@@ -8,8 +8,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "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/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -104,6 +104,9 @@ type ETHBackend interface { ...@@ -104,6 +104,9 @@ type ETHBackend interface {
// NonceAt returns the account nonce of the given account. // 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. // 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) 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 // SimpleTxManager is a implementation of TxManager that performs linear fee
...@@ -185,13 +188,25 @@ func (m *SimpleTxManager) CraftTx(ctx context.Context, candidate TxCandidate) (* ...@@ -185,13 +188,25 @@ func (m *SimpleTxManager) CraftTx(ctx context.Context, candidate TxCandidate) (*
Data: candidate.TxData, Data: candidate.TxData,
} }
// Calculate the intrinsic gas for the transaction
m.l.Info("creating tx", "to", rawTx.To, "from", candidate.From) 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 { 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 rawTx.Gas = gas
}
ctx, cancel = context.WithTimeout(ctx, m.Config.NetworkTimeout) ctx, cancel = context.WithTimeout(ctx, m.Config.NetworkTimeout)
defer cancel() defer cancel()
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils" "github.com/ethereum-optimism/optimism/op-node/testutils"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" 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/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -175,6 +176,10 @@ func (b *mockBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*typ ...@@ -175,6 +176,10 @@ func (b *mockBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*typ
}, nil }, 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) { func (b *mockBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
tip, _ := b.g.sample() tip, _ := b.g.sample()
return tip, nil return tip, nil
...@@ -580,6 +585,10 @@ func (b *failingBackend) SuggestGasTipCap(_ context.Context) (*big.Int, error) { ...@@ -580,6 +585,10 @@ func (b *failingBackend) SuggestGasTipCap(_ context.Context) (*big.Int, error) {
return b.gasTip, nil 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) { func (b *failingBackend) NonceAt(_ context.Context, _ common.Address, _ *big.Int) (uint64, error) {
return 0, errors.New("unimplemented") 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