Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion node/cmd/node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
tmlog "github.com/tendermint/tendermint/libs/log"
tmnode "github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/privval"
tmsequencer "github.com/tendermint/tendermint/sequencer"
"github.com/urfave/cli"

"morph-l2/bindings/bindings"
Expand All @@ -25,6 +26,7 @@ import (
"morph-l2/node/db"
"morph-l2/node/derivation"
"morph-l2/node/flags"
"morph-l2/node/hakeeper"
"morph-l2/node/l1sequencer"
"morph-l2/node/sequencer"
"morph-l2/node/sequencer/mock"
Expand Down Expand Up @@ -69,6 +71,7 @@ func L2NodeMain(ctx *cli.Context) error {
tracker *l1sequencer.L1Tracker
verifier *l1sequencer.SequencerVerifier
signer l1sequencer.Signer
haService *hakeeper.HAService

nodeConfig = node.DefaultConfig()
)
Expand Down Expand Up @@ -152,14 +155,25 @@ func L2NodeMain(ctx *cli.Context) error {
if err != nil {
return err
}
haService, err = initHAService(ctx, home, nodeConfig.Logger)
if err != nil {
return err
}

if isMockSequencer {
ms, err = mock.NewSequencer(executor)
if err != nil {
return err
}
go ms.Start()
} else {
tmNode, err = sequencer.SetupNode(tmCfg, tmVal, executor, nodeConfig.Logger, verifier, signer)
// Convert typed nil (*HAService)(nil) to untyped nil interface to avoid
// Go's nil interface gotcha: a typed nil satisfies (ha != nil) checks.
var ha tmsequencer.SequencerHA
if haService != nil {
ha = haService
}
tmNode, err = sequencer.SetupNode(tmCfg, tmVal, executor, nodeConfig.Logger, verifier, signer, ha)
if err != nil {
return fmt.Errorf("failed to setup consensus node: %v", err)
}
Expand Down Expand Up @@ -212,6 +226,57 @@ func L2NodeMain(ctx *cli.Context) error {
return nil
}

// initHAService builds the HA config and creates the HAService.
// Loading order: defaults → config file → flag overrides → auto-resolve → validate.
// Returns nil (no error) if HA is not enabled.
func initHAService(ctx *cli.Context, home string, logger tmlog.Logger) (*hakeeper.HAService, error) {
cfg := hakeeper.DefaultConfig()

if cfgPath := ctx.GlobalString(flags.SequencerHAConfig.Name); cfgPath != "" {
if err := cfg.LoadFile(cfgPath); err != nil {
return nil, fmt.Errorf("HA config: %w", err)
}
}

if ctx.GlobalBool(flags.SequencerHAEnabled.Name) {
cfg.Enabled = true
}
if ctx.GlobalBool(flags.SequencerHABootstrap.Name) {
cfg.Bootstrap = true
}
if addrs := ctx.GlobalStringSlice(flags.SequencerHAJoin.Name); len(addrs) > 0 {
cfg.JoinAddrs = addrs
}
if id := ctx.GlobalString(flags.SequencerHAServerID.Name); id != "" {
cfg.ServerID = id
}
if addr := ctx.GlobalString(flags.SequencerHAAdvertisedAddr.Name); addr != "" {
cfg.Consensus.AdvertisedAddr = addr
}
if token := ctx.GlobalString(flags.SequencerHARPCToken.Name); token != "" {
cfg.RPC.Token = token
}

if !cfg.Enabled {
return nil, nil
}

// Propagate node log level to Raft internal logger
if logLevel := ctx.GlobalString(flags.LogLevel.Name); logLevel == "debug" {
cfg.Debug = true
}

if err := cfg.Resolve(home); err != nil {
return nil, fmt.Errorf("HA config resolve: %w", err)
}
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("HA config: %w", err)
}

cfg.LogEffectiveConfig(logger)
return hakeeper.New(cfg, logger.With("module", "hakeeper"))
}

// initL1SequencerComponents initializes all L1 sequencer related components:
// - L1Tracker: monitors L1 sync status
// - SequencerCache: caches L1 sequencer address (nil if contract not configured)
Expand Down
44 changes: 44 additions & 0 deletions node/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,43 @@ var (
EnvVar: prefixEnvVar("SEQUENCER_PRIVATE_KEY"),
}

// Sequencer HA flags (all prefixed with ha.)
SequencerHAEnabled = cli.BoolFlag{
Name: "ha.enabled",
Usage: "Enable sequencer HA mode (overrides config file).",
EnvVar: prefixEnvVar("HA_ENABLED"),
}
SequencerHAConfig = cli.StringFlag{
Name: "ha.config",
Usage: "Path to sequencer HA config file (TOML). If not set, HA is disabled.",
EnvVar: prefixEnvVar("HA_CONFIG"),
}
SequencerHABootstrap = cli.BoolFlag{
Name: "ha.bootstrap",
Usage: "Bootstrap a new Raft cluster as leader (overrides config file).",
EnvVar: prefixEnvVar("HA_BOOTSTRAP"),
}
SequencerHAJoin = cli.StringSliceFlag{
Name: "ha.join",
Usage: "Management RPC addresses of existing cluster nodes to join (comma-separated, overrides config file).",
EnvVar: prefixEnvVar("HA_JOIN"),
}
SequencerHAServerID = cli.StringFlag{
Name: "ha.server-id",
Usage: "Unique server ID for this node (overrides config file; defaults to hostname).",
EnvVar: prefixEnvVar("HA_SERVER_ID"),
}
SequencerHAAdvertisedAddr = cli.StringFlag{
Name: "ha.advertised-addr",
Usage: "Raft advertised address (host:port). Supports hostname (e.g. node-0:9400) or IP. Auto-detected if not set.",
EnvVar: prefixEnvVar("HA_ADVERTISED_ADDR"),
}
SequencerHARPCToken = cli.StringFlag{
Name: "ha.rpc-token",
Usage: "Auth token for HAKeeper RPC write APIs. If empty, auth is disabled.",
EnvVar: prefixEnvVar("HA_RPC_TOKEN"),
}

// Batch rules
UpgradeBatchTime = cli.Uint64Flag{
Name: "upgrade.batchTime",
Expand Down Expand Up @@ -398,6 +435,13 @@ var Flags = []cli.Flag{
L1SequencerContractAddr,
L1SyncLagThreshold,
SequencerPrivateKey,
SequencerHAEnabled,
SequencerHAConfig,
SequencerHABootstrap,
SequencerHAJoin,
SequencerHAServerID,
SequencerHAAdvertisedAddr,
SequencerHARPCToken,

// batch rules
UpgradeBatchTime,
Expand Down
8 changes: 8 additions & 0 deletions node/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ require (

require (
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/boltdb/bolt v1.3.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
Expand All @@ -47,6 +49,7 @@ require (
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/go-kit/log v0.2.1 // indirect
Expand All @@ -63,7 +66,12 @@ require (
github.com/gtank/merlin v0.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-bexpr v0.1.13 // indirect
github.com/hashicorp/go-hclog v1.6.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/raft v1.7.1
github.com/hashicorp/raft-boltdb/v2 v2.3.0
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/iden3/go-iden3-crypto v0.0.16 // indirect
Expand Down
Loading
Loading