Commit bb7597c1 authored by Conner Fromknecht's avatar Conner Fromknecht

feat: add telemetry to go batch-submitter

parent c2230e99
......@@ -119,8 +119,6 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
log.Root().SetHandler(log.LvlFilterHandler(logLevel, logHandler))
log.Info("Config", "config", fmt.Sprintf("%#v", cfg))
// Parse sequencer private key and CTC contract address.
sequencerPrivKey, ctcAddress, err := parseWalletPrivKeyAndContractAddr(
"Sequencer", cfg.Mnemonic, cfg.SequencerHDPath,
......@@ -171,7 +169,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
var batchTxService *Service
if cfg.RunTxBatchSubmitter {
batchTxDriver, err := sequencer.NewDriver(sequencer.Config{
Name: "SEQUENCER",
Name: "Sequencer",
L1Client: l1Client,
L2Client: l2Client,
BlockOffset: cfg.BlockOffset,
......@@ -196,7 +194,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
var batchStateService *Service
if cfg.RunStateBatchSubmitter {
batchStateDriver, err := proposer.NewDriver(proposer.Config{
Name: "PROPOSER",
Name: "Proposer",
L1Client: l1Client,
L2Client: l2Client,
BlockOffset: cfg.BlockOffset,
......
......@@ -5,9 +5,11 @@ import (
"crypto/ecdsa"
"fmt"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
......@@ -36,6 +38,7 @@ type Driver struct {
sccContract *scc.StateCommitmentChain
ctcContract *ctc.CanonicalTransactionChain
walletAddr common.Address
metrics *metrics.Metrics
}
func NewDriver(cfg Config) (*Driver, error) {
......@@ -60,6 +63,7 @@ func NewDriver(cfg Config) (*Driver, error) {
sccContract: sccContract,
ctcContract: ctcContract,
walletAddr: walletAddr,
metrics: metrics.NewMetrics(cfg.Name),
}, nil
}
......@@ -73,6 +77,11 @@ func (d *Driver) WalletAddr() common.Address {
return d.walletAddr
}
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() *metrics.Metrics {
return d.metrics
}
// GetBatchBlockRange returns the start and end L2 block heights that need to be
// processed. Note that the end value is *exclusive*, therefore if the returned
// values are identical nothing needs to be processed.
......@@ -121,6 +130,8 @@ func (d *Driver) SubmitBatchTx(
ctx context.Context,
start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) {
batchTxBuildStart := time.Now()
var blocks []*l2types.Block
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
......@@ -139,6 +150,10 @@ func (d *Driver) SubmitBatchTx(
stateRoots = append(stateRoots, block.Root())
}
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
)
......
......@@ -7,8 +7,10 @@ import (
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi"
......@@ -43,6 +45,7 @@ type Driver struct {
rawCtcContract *bind.BoundContract
walletAddr common.Address
ctcABI *abi.ABI
metrics *metrics.Metrics
}
func NewDriver(cfg Config) (*Driver, error) {
......@@ -78,6 +81,7 @@ func NewDriver(cfg Config) (*Driver, error) {
rawCtcContract: rawCtcContract,
walletAddr: walletAddr,
ctcABI: ctcABI,
metrics: metrics.NewMetrics(cfg.Name),
}, nil
}
......@@ -91,6 +95,11 @@ func (d *Driver) WalletAddr() common.Address {
return d.walletAddr
}
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() *metrics.Metrics {
return d.metrics
}
// GetBatchBlockRange returns the start and end L2 block heights that need to be
// processed. Note that the end value is *exclusive*, therefore if the returned
// values are identical nothing needs to be processed.
......@@ -136,6 +145,8 @@ func (d *Driver) SubmitBatchTx(
log.Info(name+" submitting batch tx", "start", start, "end", end,
"gasPrice", gasPrice)
batchTxBuildStart := time.Now()
var blocks []*l2types.Block
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
......@@ -176,6 +187,11 @@ func (d *Driver) SubmitBatchTx(
panic("call data too large")
}
// Record the batch_tx_build_time.
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
log.Info(name+" batch call data", "data", hex.EncodeToString(batchCallData))
opts, err := bind.NewKeyedTransactorWithChainID(
......
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type Metrics struct {
// ETHBalance tracks the amount of ETH in the submitter's account.
ETHBalance prometheus.Gauge
// BatchSizeInBytes tracks the size of batch submission transactions.
BatchSizeInBytes prometheus.Histogram
// NumTxPerBatch tracks the number of L2 transactions in each batch
// submission.
NumTxPerBatch prometheus.Histogram
// SubmissionGasUsed tracks the amount of gas used to submit each batch.
SubmissionGasUsed prometheus.Histogram
// BatchsSubmitted tracks the total number of successful batch submissions.
BatchesSubmitted prometheus.Counter
// FailedSubmissions tracks the total number of failed batch submissions.
FailedSubmissions prometheus.Counter
// BatchTxBuildTime tracks the duration it takes to construct a batch
// transaction.
BatchTxBuildTime prometheus.Gauge
// BatchConfirmationTime tracks the duration it takes to confirm a batch
// transaction.
BatchConfirmationTime prometheus.Gauge
}
func NewMetrics(subsystem string) *Metrics {
return &Metrics{
ETHBalance: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_eth_balance",
Help: "ETH balance of the batch submitter",
Subsystem: subsystem,
}),
BatchSizeInBytes: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_batch_size_in_bytes",
Help: "Size of batches in bytes",
Subsystem: subsystem,
}),
NumTxPerBatch: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_num_txs_per_batch",
Help: "Number of transaction in each batch",
Subsystem: subsystem,
}),
SubmissionGasUsed: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_submission_gas_used",
Help: "Gas used to submit each batch",
Subsystem: subsystem,
}),
BatchesSubmitted: promauto.NewCounter(prometheus.CounterOpts{
Name: "batch_submitter_batches_submitted",
Help: "Count of batches submitted",
Subsystem: subsystem,
}),
FailedSubmissions: promauto.NewCounter(prometheus.CounterOpts{
Name: "batch_submitter_failed_submissions",
Help: "Count of failed batch submissions",
Subsystem: subsystem,
}),
BatchTxBuildTime: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_batch_tx_build_time",
Help: "Time to construct batch transactions",
Subsystem: subsystem,
}),
BatchConfirmationTime: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_batch_confirmation_time",
Help: "Time to confirm batch transactions",
Subsystem: subsystem,
}),
}
}
......@@ -6,6 +6,7 @@ import (
"sync"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -13,6 +14,11 @@ import (
"github.com/ethereum/go-ethereum/log"
)
var (
// weiToGwei is the conversion rate from wei to gwei.
weiToGwei = new(big.Float).SetFloat64(1e-18)
)
// Driver is an interface for creating and submitting batch transactions for a
// specific contract.
type Driver interface {
......@@ -23,6 +29,9 @@ type Driver interface {
// fees.
WalletAddr() common.Address
// Metrics returns the subservice telemetry object.
Metrics() *metrics.Metrics
// GetBatchBlockRange returns the start and end L2 block heights that
// need to be processed. Note that the end value is *exclusive*,
// therefore if the returned values are identical nothing needs to be
......@@ -52,6 +61,7 @@ type Service struct {
cancel func()
txMgr txmgr.TxManager
metrics *metrics.Metrics
wg sync.WaitGroup
}
......@@ -68,6 +78,7 @@ func NewService(cfg ServiceConfig) *Service {
ctx: ctx,
cancel: cancel,
txMgr: txMgr,
metrics: cfg.Driver.Metrics(),
}
}
......@@ -91,21 +102,35 @@ func (s *Service) eventLoop() {
for {
select {
case <-time.After(s.cfg.PollInterval):
log.Info(name + " fetching current block range")
// Record the submitter's current ETH balance. This is done first in
// case any of the remaining steps fail, we can at least have an
// accurate view of the submitter's balance.
balance, err := s.cfg.L1Client.BalanceAt(
s.ctx, s.cfg.Driver.WalletAddr(), nil,
)
if err != nil {
log.Error(name+" unable to get current balance", "err", err)
continue
}
s.metrics.ETHBalance.Set(weiToGwei64(balance))
// Determine the range of L2 blocks that the batch submitter has not
// processed, and needs to take action on.
log.Info(name + " fetching current block range")
start, end, err := s.cfg.Driver.GetBatchBlockRange(s.ctx)
if err != nil {
log.Error(name+" unable to get block range", "err", err)
continue
}
log.Info(name+" block range", "start", start, "end", end)
// No new updates.
if start.Cmp(end) == 0 {
log.Info(name+" no updates", "start", start, "end", end)
continue
}
log.Info(name+" block range", "start", start, "end", end)
// Query for the submitter's current nonce.
nonce64, err := s.cfg.L1Client.NonceAt(
s.ctx, s.cfg.Driver.WalletAddr(), nil,
)
......@@ -116,6 +141,8 @@ func (s *Service) eventLoop() {
}
nonce := new(big.Int).SetUint64(nonce64)
// Construct the transaction submission clousure that will attempt
// to send the next transaction at the given nonce and gas price.
sendTx := func(
ctx context.Context,
gasPrice *big.Int,
......@@ -123,20 +150,38 @@ func (s *Service) eventLoop() {
log.Info(name+" attempting batch tx", "start", start,
"end", end, "nonce", nonce,
"gasPrice", gasPrice)
return s.cfg.Driver.SubmitBatchTx(
tx, err := s.cfg.Driver.SubmitBatchTx(
ctx, start, end, nonce, gasPrice,
)
if err != nil {
return nil, err
}
s.metrics.BatchSizeInBytes.Observe(float64(tx.Size()))
return tx, nil
}
// Wait until one of our submitted transactions confirms. If no
// receipt is received it's likely our gas price was too low.
batchConfirmationStart := time.Now()
receipt, err := s.txMgr.Send(s.ctx, sendTx)
if err != nil {
log.Error(name+" unable to publish batch tx",
"err", err)
s.metrics.FailedSubmissions.Inc()
continue
}
// The transaction was successfully submitted.
log.Info(name+" batch tx successfully published",
"tx_hash", receipt.TxHash)
batchConfirmationTime := time.Since(batchConfirmationStart) /
time.Millisecond
s.metrics.BatchConfirmationTime.Set(float64(batchConfirmationTime))
s.metrics.BatchesSubmitted.Inc()
s.metrics.SubmissionGasUsed.Observe(float64(receipt.GasUsed))
case err := <-s.ctx.Done():
log.Error(name+" service shutting down", "err", err)
......@@ -144,3 +189,10 @@ func (s *Service) eventLoop() {
}
}
}
func weiToGwei64(wei *big.Int) float64 {
gwei := new(big.Float).SetInt(wei)
gwei.Mul(gwei, weiToGwei)
gwei64, _ := gwei.Float64()
return gwei64
}
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