Commit b1391700 authored by joohhnnn's avatar joohhnnn

op-proposer: service lifecycle cleanup

parent 3c10d0c7
...@@ -59,18 +59,22 @@ func (f fakeTxMgr) Send(_ context.Context, _ txmgr.TxCandidate) (*types.Receipt, ...@@ -59,18 +59,22 @@ func (f fakeTxMgr) Send(_ context.Context, _ txmgr.TxCandidate) (*types.Receipt,
} }
func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Client, rollupCl *sources.RollupClient) *L2Proposer { func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Client, rollupCl *sources.RollupClient) *L2Proposer {
proposerCfg := proposer.Config{ proposerConfig := proposer.ProposerConfig{
L2OutputOracleAddr: cfg.OutputOracleAddr,
PollInterval: time.Second, PollInterval: time.Second,
NetworkTimeout: time.Second, NetworkTimeout: time.Second,
L2OutputOracleAddr: cfg.OutputOracleAddr,
AllowNonFinalized: cfg.AllowNonFinalized,
}
driverSetup := proposer.DriverSetup{
Log: log,
Metr: metrics.NoopMetrics,
Cfg: proposerConfig,
Txmgr: fakeTxMgr{from: crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey)},
L1Client: l1, L1Client: l1,
RollupClient: rollupCl, RollupClient: rollupCl,
AllowNonFinalized: cfg.AllowNonFinalized,
// We use custom signing here instead of using the transaction manager.
TxManager: fakeTxMgr{from: crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey)},
} }
dr, err := proposer.NewL2OutputSubmitter(proposerCfg, log, metrics.NoopMetrics) dr, err := proposer.NewL2OutputSubmitter(driverSetup)
require.NoError(t, err) require.NoError(t, err)
contract, err := bindings.NewL2OutputOracleCaller(cfg.OutputOracleAddr, l1) contract, err := bindings.NewL2OutputOracleCaller(cfg.OutputOracleAddr, l1)
require.NoError(t, err) require.NoError(t, err)
......
...@@ -532,7 +532,7 @@ func setupDisputeGameForInvalidOutputRoot(t *testing.T, outputRoot common.Hash) ...@@ -532,7 +532,7 @@ func setupDisputeGameForInvalidOutputRoot(t *testing.T, outputRoot common.Hash)
l2oo.WaitForProposals(ctx, 1) l2oo.WaitForProposals(ctx, 1)
// Stop the honest output submitter so we can publish invalid outputs // Stop the honest output submitter so we can publish invalid outputs
sys.L2OutputSubmitter.Stop() sys.L2OutputSubmitter.Driver().StopL2OutputSubmitting()
sys.L2OutputSubmitter = nil sys.L2OutputSubmitter = nil
// Submit an invalid output root // Submit an invalid output root
......
...@@ -51,7 +51,6 @@ import ( ...@@ -51,7 +51,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
proposermetrics "github.com/ethereum-optimism/optimism/op-proposer/metrics"
l2os "github.com/ethereum-optimism/optimism/op-proposer/proposer" l2os "github.com/ethereum-optimism/optimism/op-proposer/proposer"
"github.com/ethereum-optimism/optimism/op-service/cliapp" "github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/ethereum-optimism/optimism/op-service/clock" "github.com/ethereum-optimism/optimism/op-service/clock"
...@@ -257,7 +256,7 @@ type System struct { ...@@ -257,7 +256,7 @@ type System struct {
Clients map[string]*ethclient.Client Clients map[string]*ethclient.Client
RawClients map[string]*rpc.Client RawClients map[string]*rpc.Client
RollupNodes map[string]*rollupNode.OpNode RollupNodes map[string]*rollupNode.OpNode
L2OutputSubmitter *l2os.L2OutputSubmitter L2OutputSubmitter *l2os.ProposerService
BatchSubmitter *bss.BatcherService BatchSubmitter *bss.BatcherService
Mocknet mocknet.Mocknet Mocknet mocknet.Mocknet
...@@ -278,7 +277,7 @@ func (sys *System) Close() { ...@@ -278,7 +277,7 @@ func (sys *System) Close() {
postCancel() // immediate shutdown, no allowance for idling postCancel() // immediate shutdown, no allowance for idling
if sys.L2OutputSubmitter != nil { if sys.L2OutputSubmitter != nil {
sys.L2OutputSubmitter.Stop() _ = sys.L2OutputSubmitter.Kill()
} }
if sys.BatchSubmitter != nil { if sys.BatchSubmitter != nil {
_ = sys.BatchSubmitter.Kill() _ = sys.BatchSubmitter.Kill()
...@@ -663,7 +662,7 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -663,7 +662,7 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
} }
// L2Output Submitter // L2Output Submitter
sys.L2OutputSubmitter, err = l2os.NewL2OutputSubmitterFromCLIConfig(l2os.CLIConfig{ proposerCLIConfig := &l2os.CLIConfig{
L1EthRpc: sys.EthInstances["l1"].WSEndpoint(), L1EthRpc: sys.EthInstances["l1"].WSEndpoint(),
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(), RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(), L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(),
...@@ -674,15 +673,18 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -674,15 +673,18 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
Level: log.LvlInfo, Level: log.LvlInfo,
Format: oplog.FormatText, Format: oplog.FormatText,
}, },
}, sys.cfg.Loggers["proposer"], proposermetrics.NoopMetrics) }
proposer, err := l2os.ProposerServiceFromCLIConfig(context.Background(), "0.0.1", proposerCLIConfig, sys.cfg.Loggers["proposer"])
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to setup l2 output submitter: %w", err) return nil, fmt.Errorf("unable to setup l2 output submitter: %w", err)
} }
if err := sys.L2OutputSubmitter.Start(); err != nil { if err := proposer.Start(context.Background()); err != nil {
return nil, fmt.Errorf("unable to start l2 output submitter: %w", err) return nil, fmt.Errorf("unable to start l2 output submitter: %w", err)
} }
sys.L2OutputSubmitter = proposer
batchType := derive.SingularBatchType batchType := derive.SingularBatchType
if os.Getenv("OP_E2E_USE_SPAN_BATCH") == "true" { if os.Getenv("OP_E2E_USE_SPAN_BATCH") == "true" {
batchType = derive.SpanBatchType batchType = derive.SpanBatchType
......
...@@ -259,7 +259,7 @@ func testFaultProofProgramScenario(t *testing.T, ctx context.Context, sys *Syste ...@@ -259,7 +259,7 @@ func testFaultProofProgramScenario(t *testing.T, ctx context.Context, sys *Syste
t.Log("Shutting down network") t.Log("Shutting down network")
// Shutdown the nodes from the actual chain. Should now be able to run using only the pre-fetched data. // Shutdown the nodes from the actual chain. Should now be able to run using only the pre-fetched data.
require.NoError(t, sys.BatchSubmitter.Kill()) require.NoError(t, sys.BatchSubmitter.Kill())
sys.L2OutputSubmitter.Stop() sys.L2OutputSubmitter.Driver().StopL2OutputSubmitting()
sys.L2OutputSubmitter = nil sys.L2OutputSubmitter = nil
for _, node := range sys.EthInstances { for _, node := range sys.EthInstances {
node.Close() node.Close()
......
...@@ -30,7 +30,7 @@ func main() { ...@@ -30,7 +30,7 @@ func main() {
app.Name = "op-proposer" app.Name = "op-proposer"
app.Usage = "L2Output Submitter" app.Usage = "L2Output Submitter"
app.Description = "Service for generating and submitting L2 Output checkpoints to the L2OutputOracle contract" app.Description = "Service for generating and submitting L2 Output checkpoints to the L2OutputOracle contract"
app.Action = curryMain(Version) app.Action = cliapp.LifecycleCmd(proposer.Main(Version))
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
{ {
Name: "doc", Name: "doc",
...@@ -44,10 +44,3 @@ func main() { ...@@ -44,10 +44,3 @@ func main() {
} }
} }
// curryMain transforms the proposer.Main function into an app.Action
// This is done to capture the Version of the proposer.
func curryMain(version string) func(ctx *cli.Context) error {
return func(ctx *cli.Context) error {
return proposer.Main(version, ctx)
}
}
package metrics package metrics
import ( import (
"context" "io"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
...@@ -10,13 +10,15 @@ import ( ...@@ -10,13 +10,15 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/httputil"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
) )
const Namespace = "op_proposer" const Namespace = "op_proposer"
// implements the Registry getter, for metrics HTTP server to hook into
var _ opmetrics.RegistryMetricer = (*Metrics)(nil)
type Metricer interface { type Metricer interface {
RecordInfo(version string) RecordInfo(version string)
RecordUp() RecordUp()
...@@ -27,6 +29,10 @@ type Metricer interface { ...@@ -27,6 +29,10 @@ type Metricer interface {
// Record Tx metrics // Record Tx metrics
txmetrics.TxMetricer txmetrics.TxMetricer
opmetrics.RPCMetricer
StartBalanceMetrics(l log.Logger, client *ethclient.Client, account common.Address) io.Closer
RecordL2BlocksProposed(l2ref eth.L2BlockRef) RecordL2BlocksProposed(l2ref eth.L2BlockRef)
} }
...@@ -78,18 +84,12 @@ func NewMetrics(procName string) *Metrics { ...@@ -78,18 +84,12 @@ func NewMetrics(procName string) *Metrics {
} }
} }
func (m *Metrics) Start(host string, port int) (*httputil.HTTPServer, error) { func (m *Metrics) Registry() *prometheus.Registry {
return opmetrics.StartServer(m.registry, host, port) return m.registry
} }
func (m *Metrics) StartBalanceMetrics(ctx context.Context, func (m *Metrics) StartBalanceMetrics(l log.Logger, client *ethclient.Client, account common.Address) io.Closer {
l log.Logger, client *ethclient.Client, account common.Address) { return opmetrics.LaunchBalanceMetrics(l, m.registry, m.ns, client, account)
// TODO(7684): util was refactored to close, but ctx is still being used by caller for shutdown
balanceMetric := opmetrics.LaunchBalanceMetrics(l, m.registry, m.ns, client, account)
go func() {
<-ctx.Done()
_ = balanceMetric.Close()
}()
} }
// RecordInfo sets a pseudo-metric that contains versioning and // RecordInfo sets a pseudo-metric that contains versioning and
......
package metrics package metrics
import ( import (
"io"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
...@@ -9,6 +15,7 @@ import ( ...@@ -9,6 +15,7 @@ import (
type noopMetrics struct { type noopMetrics struct {
opmetrics.NoopRefMetrics opmetrics.NoopRefMetrics
txmetrics.NoopTxMetrics txmetrics.NoopTxMetrics
opmetrics.NoopRPCMetrics
} }
var NoopMetrics Metricer = new(noopMetrics) var NoopMetrics Metricer = new(noopMetrics)
...@@ -17,3 +24,7 @@ func (*noopMetrics) RecordInfo(version string) {} ...@@ -17,3 +24,7 @@ func (*noopMetrics) RecordInfo(version string) {}
func (*noopMetrics) RecordUp() {} func (*noopMetrics) RecordUp() {}
func (*noopMetrics) RecordL2BlocksProposed(l2ref eth.L2BlockRef) {} func (*noopMetrics) RecordL2BlocksProposed(l2ref eth.L2BlockRef) {}
func (*noopMetrics) StartBalanceMetrics(log.Logger, *ethclient.Client, common.Address) io.Closer {
return nil
}
\ No newline at end of file
...@@ -3,12 +3,9 @@ package proposer ...@@ -3,12 +3,9 @@ package proposer
import ( import (
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-proposer/flags" "github.com/ethereum-optimism/optimism/op-proposer/flags"
"github.com/ethereum-optimism/optimism/op-service/sources"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
...@@ -17,18 +14,6 @@ import ( ...@@ -17,18 +14,6 @@ import (
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
// Config contains the well typed fields that are used to initialize the output submitter.
// It is intended for programmatic use.
type Config struct {
L2OutputOracleAddr common.Address
PollInterval time.Duration
NetworkTimeout time.Duration
TxManager txmgr.TxManager
L1Client *ethclient.Client
RollupClient *sources.RollupClient
AllowNonFinalized bool
}
// CLIConfig is a well typed config that is parsed from the CLI params. // CLIConfig is a well typed config that is parsed from the CLI params.
// This also contains config options for auxiliary services. // This also contains config options for auxiliary services.
// It is transformed into a `Config` before the L2 output submitter is started. // It is transformed into a `Config` before the L2 output submitter is started.
...@@ -63,7 +48,7 @@ type CLIConfig struct { ...@@ -63,7 +48,7 @@ type CLIConfig struct {
PprofConfig oppprof.CLIConfig PprofConfig oppprof.CLIConfig
} }
func (c CLIConfig) Check() error { func (c *CLIConfig) Check() error {
if err := c.RPCConfig.Check(); err != nil { if err := c.RPCConfig.Check(); err != nil {
return err return err
} }
...@@ -80,8 +65,8 @@ func (c CLIConfig) Check() error { ...@@ -80,8 +65,8 @@ func (c CLIConfig) Check() error {
} }
// NewConfig parses the Config from the provided flags or environment variables. // NewConfig parses the Config from the provided flags or environment variables.
func NewConfig(ctx *cli.Context) CLIConfig { func NewConfig(ctx *cli.Context) *CLIConfig {
return CLIConfig{ return &CLIConfig{
// Required Flags // Required Flags
L1EthRpc: ctx.String(flags.L1EthRpcFlag.Name), L1EthRpc: ctx.String(flags.L1EthRpcFlag.Name),
RollupRpc: ctx.String(flags.RollupRpcFlag.Name), RollupRpc: ctx.String(flags.RollupRpcFlag.Name),
......
This diff is collapsed.
This diff is collapsed.
package rpc
import (
"context"
"github.com/ethereum/go-ethereum/log"
gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-service/rpc"
)
type ProposerDriver interface {
StartL2OutputSubmitting() error
StopL2OutputSubmitting() error
}
type adminAPI struct {
*rpc.CommonAdminAPI
b ProposerDriver
}
func NewAdminAPI(dr ProposerDriver, m metrics.RPCMetricer, log log.Logger) *adminAPI {
return &adminAPI{
CommonAdminAPI: rpc.NewCommonAdminAPI(m, log),
b: dr,
}
}
func GetAdminAPI(api *adminAPI) gethrpc.API {
return gethrpc.API{
Namespace: "admin",
Service: api,
}
}
func (a *adminAPI) StartProposer(_ context.Context) error {
return a.b.StartL2OutputSubmitting()
}
func (a *adminAPI) StopProposer(ctx context.Context) error {
return a.b.StopL2OutputSubmitting()
}
package proposer
import (
"context"
"errors"
"fmt"
"io"
"net"
"strconv"
"sync/atomic"
"time"
"github.com/ethereum-optimism/optimism/op-proposer/metrics"
"github.com/ethereum-optimism/optimism/op-proposer/proposer/rpc"
opservice "github.com/ethereum-optimism/optimism/op-service"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/httputil"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type ProposerConfig struct{
// How frequently to poll L2 for new finalized outputs
PollInterval time.Duration
NetworkTimeout time.Duration
L2OutputOracleAddr common.Address
// AllowNonFinalized enables the proposal of safe, but non-finalized L2 blocks.
// The L1 block-hash embedded in the proposal TX is checked and should ensure the proposal
// is never valid on an alternative L1 chain that would produce different L2 data.
// This option is not necessary when higher proposal latency is acceptable and L1 is healthy.
AllowNonFinalized bool
}
type ProposerService struct{
Log log.Logger
Metrics metrics.Metricer
ProposerConfig
TxManager txmgr.TxManager
L1Client *ethclient.Client
RollupClient *sources.RollupClient
driver *L2OutputSubmitter
Version string
pprofSrv *httputil.HTTPServer
metricsSrv *httputil.HTTPServer
rpcServer *oprpc.Server
balanceMetricer io.Closer
stopped atomic.Bool
}
// ProposerServiceFromCLIConfig creates a new ProposerService from a CLIConfig.
// The service components are fully started, except for the driver,
// which will not be submitting state (if it was configured to) until the Start part of the lifecycle.
func ProposerServiceFromCLIConfig(ctx context.Context, version string, cfg *CLIConfig, log log.Logger) (*ProposerService, error) {
var ps ProposerService
if err := ps.initFromCLIConfig(ctx, version, cfg, log); err != nil {
return nil, errors.Join(err, ps.Stop(ctx)) // try to clean up our failed initialization attempt
}
return &ps, nil
}
func (ps *ProposerService) initFromCLIConfig(ctx context.Context, version string, cfg *CLIConfig, log log.Logger) error {
ps.Version = version
ps.Log = log
ps.initMetrics(cfg)
ps.PollInterval = cfg.PollInterval
ps.NetworkTimeout = cfg.TxMgrConfig.NetworkTimeout
ps.AllowNonFinalized = cfg.AllowNonFinalized
if err := ps.initRPCClients(ctx, cfg); err != nil {
return err
}
if err := ps.initTxManager(cfg); err != nil {
return fmt.Errorf("failed to init Tx manager: %w", err)
}
ps.initBalanceMonitor(cfg)
if err := ps.initMetricsServer(cfg); err != nil {
return fmt.Errorf("failed to start metrics server: %w", err)
}
if err := ps.initPProf(cfg); err != nil {
return fmt.Errorf("failed to start pprof server: %w", err)
}
if err := ps.initL2ooAddress(cfg); err != nil {
return fmt.Errorf("failed to init L2ooAddress: %w", err)
}
if err := ps.initDriver(); err != nil {
return fmt.Errorf("failed to init Driver: %w", err)
}
if err := ps.initRPCServer(cfg); err != nil {
return fmt.Errorf("failed to start RPC server: %w", err)
}
ps.Metrics.RecordInfo(ps.Version)
ps.Metrics.RecordUp()
return nil
}
func (ps *ProposerService) initRPCClients(ctx context.Context, cfg *CLIConfig) error {
l1Client, err := dial.DialEthClientWithTimeout(ctx, dial.DefaultDialTimeout, ps.Log, cfg.L1EthRpc)
if err != nil {
return fmt.Errorf("failed to dial L1 RPC: %w", err)
}
ps.L1Client = l1Client
rollupClient, err := dial.DialRollupClientWithTimeout(ctx, dial.DefaultDialTimeout, ps.Log, cfg.RollupRpc)
if err != nil {
return fmt.Errorf("failed to dial L2 rollup-client RPC: %w", err)
}
ps.RollupClient = rollupClient
return nil
}
func (ps *ProposerService) initMetrics(cfg *CLIConfig) {
if cfg.MetricsConfig.Enabled {
procName := "default"
ps.Metrics = metrics.NewMetrics(procName)
} else {
ps.Metrics = metrics.NoopMetrics
}
}
// initBalanceMonitor depends on Metrics, L1Client and TxManager to start background-monitoring of the Proposer balance.
func (ps *ProposerService) initBalanceMonitor(cfg *CLIConfig) {
if cfg.MetricsConfig.Enabled {
ps.balanceMetricer = ps.Metrics.StartBalanceMetrics(ps.Log, ps.L1Client, ps.TxManager.From())
}
}
func (ps *ProposerService) initTxManager(cfg *CLIConfig) error {
txManager, err := txmgr.NewSimpleTxManager("proposer", ps.Log, ps.Metrics, cfg.TxMgrConfig)
if err != nil {
return err
}
ps.TxManager = txManager
return nil
}
func (ps *ProposerService) initPProf(cfg *CLIConfig) error {
if !cfg.PprofConfig.Enabled {
return nil
}
log.Debug("starting pprof server", "addr", net.JoinHostPort(cfg.PprofConfig.ListenAddr, strconv.Itoa(cfg.PprofConfig.ListenPort)))
srv, err := oppprof.StartServer(cfg.PprofConfig.ListenAddr, cfg.PprofConfig.ListenPort)
if err != nil {
return err
}
ps.pprofSrv = srv
log.Info("started pprof server", "addr", srv.Addr())
return nil
}
func (ps *ProposerService) initMetricsServer(cfg *CLIConfig) error {
if !cfg.MetricsConfig.Enabled {
ps.Log.Info("metrics disabled")
return nil
}
m, ok := ps.Metrics.(opmetrics.RegistryMetricer)
if !ok {
return fmt.Errorf("metrics were enabled, but metricer %T does not expose registry for metrics-server", ps.Metrics)
}
ps.Log.Debug("starting metrics server", "addr", cfg.MetricsConfig.ListenAddr, "port", cfg.MetricsConfig.ListenPort)
metricsSrv, err := opmetrics.StartServer(m.Registry(), cfg.MetricsConfig.ListenAddr, cfg.MetricsConfig.ListenPort)
if err != nil {
return fmt.Errorf("failed to start metrics server: %w", err)
}
ps.Log.Info("started metrics server", "addr", metricsSrv.Addr())
ps.metricsSrv = metricsSrv
return nil
}
func (ps *ProposerService) initL2ooAddress(cfg *CLIConfig) error {
l2ooAddress, err := opservice.ParseAddress(cfg.L2OOAddress)
if err != nil {
return nil
}
ps.L2OutputOracleAddr = l2ooAddress
return nil
}
func (ps *ProposerService) initDriver() error {
driver, err := NewL2OutputSubmitter(DriverSetup{
Log: ps.Log,
Metr: ps.Metrics,
Cfg: ps.ProposerConfig,
Txmgr: ps.TxManager,
L1Client: ps.L1Client,
RollupClient: ps.RollupClient,
})
if err != nil {
return err
}
ps.driver = driver
return nil
}
func (ps *ProposerService) initRPCServer(cfg *CLIConfig) error {
server := oprpc.NewServer(
cfg.RPCConfig.ListenAddr,
cfg.RPCConfig.ListenPort,
ps.Version,
oprpc.WithLogger(ps.Log),
)
if cfg.RPCConfig.EnableAdmin {
adminAPI := rpc.NewAdminAPI(ps.driver, ps.Metrics, ps.Log)
server.AddAPI(rpc.GetAdminAPI(adminAPI))
ps.Log.Info("Admin RPC enabled")
}
ps.Log.Info("Starting JSON-RPC server")
if err := server.Start(); err != nil {
return fmt.Errorf("unable to start RPC server: %w", err)
}
ps.rpcServer = server
return nil
}
// Start runs once upon start of the proposer lifecycle,
// and starts L2Output-submission work if the proposer is configured to start submit data on startup.
func (ps *ProposerService) Start(_ context.Context) error {
ps.driver.Log.Info("Starting Proposer")
return ps.driver.StartL2OutputSubmitting()
}
func (ps *ProposerService) Stopped() bool {
return ps.stopped.Load()
}
// Kill is a convenience method to forcefully, non-gracefully, stop the ProposerService.
func (ps *ProposerService) Kill() error {
ctx, cancel := context.WithCancel(context.Background())
cancel()
return ps.Stop(ctx)
}
// Stop fully stops the L2Output-submitter and all its resources gracefully. After stopping, it cannot be restarted.
// See driver.StopL2OutputSubmitting to temporarily stop the L2Output submitter.
func (ps *ProposerService) Stop(ctx context.Context) error {
if ps.stopped.Load() {
return errors.New("already stopped")
}
ps.Log.Info("Stopping Proposer")
var result error
if ps.driver != nil {
if err := ps.driver.StopL2OutputSubmittingIfRunning(); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop L2Output submitting: %w", err))
}
}
if ps.rpcServer != nil {
// TODO(7685): the op-service RPC server is not built on top of op-service httputil Server, and has poor shutdown
if err := ps.rpcServer.Stop(); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop RPC server: %w", err))
}
}
if ps.pprofSrv != nil {
if err := ps.pprofSrv.Stop(ctx); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop PProf server: %w", err))
}
}
if ps.balanceMetricer != nil {
if err := ps.balanceMetricer.Close(); err != nil {
result = errors.Join(result, fmt.Errorf("failed to close balance metricer: %w", err))
}
}
if ps.metricsSrv != nil {
if err := ps.metricsSrv.Stop(ctx); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop metrics server: %w", err))
}
}
if ps.L1Client != nil {
ps.L1Client.Close()
}
if ps.RollupClient != nil {
ps.RollupClient.Close()
}
if result == nil {
ps.stopped.Store(true)
ps.Log.Info("L2Output Submitter stopped")
}
return result
}
var _ cliapp.Lifecycle = (*ProposerService)(nil)
// Driver returns the handler on the L2Output-submitter driver element,
// to start/stop/restart the L2Output-submission work, for use in testing.
func (ps *ProposerService) Driver() rpc.ProposerDriver {
return ps.driver
}
\ No newline at end of file
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