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
7 changes: 7 additions & 0 deletions node/cmd/node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
node "morph-l2/node/core"
"morph-l2/node/db"
"morph-l2/node/derivation"
"morph-l2/node/devtool"
"morph-l2/node/flags"
"morph-l2/node/hakeeper"
"morph-l2/node/l1sequencer"
Expand Down Expand Up @@ -91,6 +92,12 @@ func L2NodeMain(ctx *cli.Context) error {
"network", sequencerUpgradeNetwork(ctx),
"upgradeTimeMs", upgrade.UpgradeBlockTime())

// TEST-ONLY (devnet/testnet): optionally start directly in sequencer mode
// (pre-set the upgrade block height, skip the PBFT phase). No-op unless
// --startInSequencerMode / MORPH_NODE_START_IN_SEQUENCER_MODE is set. Must run
// before the upgrade store is wired (SetupNode below). See node/devtool.
devtool.ApplyStartInSequencerMode(ctx, nodeConfig.Logger)

home, err := homeDir(ctx)
if err != nil {
return err
Expand Down
56 changes: 56 additions & 0 deletions node/devtool/start_in_sequencer_mode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Package devtool holds TEST / TESTNET-ONLY developer helpers for devnet and HA
// testnet bring-up. Nothing in this package may be enabled on a production network.
//
// DO NOT ENABLE IN PRODUCTION.
package devtool

import (
tmlog "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/upgrade"
"github.com/urfave/cli"

"morph-l2/node/flags"
)

// startInSequencerModeFlag, when set, makes the node start directly in sequencer
// mode (skipping the pre-upgrade PBFT phase) by pre-setting the consensus upgrade
// block height to 0. Default is false, so production and ordinary devnet runs are
// unaffected.
//
// TEST/TESTNET-ONLY: never enable on a production network.
var startInSequencerModeFlag = cli.BoolFlag{
Name: "startInSequencerMode",
Usage: "[TEST-ONLY] start directly in sequencer mode (pre-set upgrade block height = 0, skip the PBFT phase); never enable in production",
EnvVar: "MORPH_NODE_START_IN_SEQUENCER_MODE",
}

// init self-registers the flag into the shared flag list so that cmd/node picks it
// up via `app.Flags = flags.Flags` without editing node/flags/flags.go. It runs
// because cmd/node imports this package to call ApplyStartInSequencerMode.
func init() {
flags.Flags = append(flags.Flags, startInSequencerModeFlag)
}

// ApplyStartInSequencerMode optionally pre-sets the consensus upgrade block height
// to 0 so that IsUpgraded(1) == true and the node starts the sequencer routines
// directly, never entering the PBFT consensus reactor. Note the tendermint node
// itself still starts; only the pre-upgrade PBFT consensus phase is skipped.
//
// Ordering requirement: this MUST run before the upgrade store is wired (i.e.
// before SetupNode / node startup). At that point upgrade's store is still nil,
// so SetUpgradeBlockHeight only sets the in-memory value and does NOT persist it.
// On a fresh DB the later SetStore finds the key absent and keeps this value.
// Because this mode never enters PBFT, the height is never persisted, so every
// restart simply re-applies it here — making the switch idempotent across restarts
// (verified against upgrade.SetStore / SetUpgradeBlockHeight).
//
// TEST/TESTNET-ONLY. No-op unless --startInSequencerMode /
// MORPH_NODE_START_IN_SEQUENCER_MODE is set.
func ApplyStartInSequencerMode(ctx *cli.Context, logger tmlog.Logger) {
if !ctx.GlobalBool(startInSequencerModeFlag.Name) {
return
}
upgrade.SetUpgradeBlockHeight(0)
Comment on lines +49 to +53

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject the test-only switch on production networks.

Line 50 accepts this switch for every network. Line 53 then bypasses the PBFT phase. Comments do not enforce the testnet-only restriction.

Reject startup when this switch is set for a production network before upgrade.SetUpgradeBlockHeight(0) runs. This prevents a deployment setting from changing production consensus behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@node/devtool/start_in_sequencer_mode.go` around lines 49 - 53, Update
ApplyStartInSequencerMode to reject the startInSequencerModeFlag when running on
a production network before calling upgrade.SetUpgradeBlockHeight(0). Preserve
the existing no-op behavior when the flag is unset, and use the existing
network/environment detection and error-reporting mechanisms to prevent startup
from continuing in production.

logger.Info("[TEST-ONLY] start-in-sequencer-mode: pre-set upgrade block height, starting directly in sequencer mode (PBFT phase skipped)",
"upgradeBlockHeight", upgrade.UpgradeBlockHeight())
}
12 changes: 12 additions & 0 deletions ops/docker/docker-compose-cluster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ services:
environment:
- MORPH_NODE_SEQUENCER_PRIVATE_KEY=${SEQUENCER_PRIVATE_KEY}
- MORPH_NODE_SEQUENCER_UPGRADE_TIME=${SEQUENCER_UPGRADE_TIME:-0}
# TEST-ONLY: this HA cluster defaults to starting directly in sequencer mode
# (skip the PBFT phase). Set START_IN_SEQUENCER_MODE=false to instead run the
# normal PBFT->sequencer upgrade path. Never enable on a production network.
- MORPH_NODE_START_IN_SEQUENCER_MODE=${START_IN_SEQUENCER_MODE:-true}
- MORPH_NODE_L2_ETH_RPC=http://ha-geth-0:8545
- MORPH_NODE_L2_ENGINE_RPC=http://ha-geth-0:8551
- MORPH_NODE_L2_ENGINE_AUTH=${JWT_SECRET_PATH}
Expand Down Expand Up @@ -132,6 +136,10 @@ services:
environment:
- MORPH_NODE_SEQUENCER_PRIVATE_KEY=${SEQUENCER_PRIVATE_KEY}
- MORPH_NODE_SEQUENCER_UPGRADE_TIME=${SEQUENCER_UPGRADE_TIME:-0}
# TEST-ONLY: this HA cluster defaults to starting directly in sequencer mode
# (skip the PBFT phase). Set START_IN_SEQUENCER_MODE=false to instead run the
# normal PBFT->sequencer upgrade path. Never enable on a production network.
- MORPH_NODE_START_IN_SEQUENCER_MODE=${START_IN_SEQUENCER_MODE:-true}
- MORPH_NODE_L2_ETH_RPC=http://ha-geth-1:8545
- MORPH_NODE_L2_ENGINE_RPC=http://ha-geth-1:8551
- MORPH_NODE_L2_ENGINE_AUTH=${JWT_SECRET_PATH}
Expand Down Expand Up @@ -172,6 +180,10 @@ services:
environment:
- MORPH_NODE_SEQUENCER_PRIVATE_KEY=${SEQUENCER_PRIVATE_KEY}
- MORPH_NODE_SEQUENCER_UPGRADE_TIME=${SEQUENCER_UPGRADE_TIME:-0}
# TEST-ONLY: this HA cluster defaults to starting directly in sequencer mode
# (skip the PBFT phase). Set START_IN_SEQUENCER_MODE=false to instead run the
# normal PBFT->sequencer upgrade path. Never enable on a production network.
- MORPH_NODE_START_IN_SEQUENCER_MODE=${START_IN_SEQUENCER_MODE:-true}
- MORPH_NODE_L2_ETH_RPC=http://ha-geth-2:8545
- MORPH_NODE_L2_ENGINE_RPC=http://ha-geth-2:8551
- MORPH_NODE_L2_ENGINE_AUTH=${JWT_SECRET_PATH}
Expand Down
6 changes: 6 additions & 0 deletions ops/docker/docker-compose-devnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ services:
- MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT}
- MORPH_NODE_SEQUENCER_PRIVATE_KEY=${ACTIVE_SEQUENCER_PRIVATE_KEY}
- MORPH_NODE_SEQUENCER_UPGRADE_TIME=${SEQUENCER_UPGRADE_TIME:-0}
# TEST-ONLY: make node-0 a local-verify follower of the start-in-sequencer-mode
# ha cluster. verify_mode=local drives P2P block-sync from the ha peers; born
# skips the PBFT/upgrade path so it matches the cluster's consensus mode.
# Never enable on a production network.
- MORPH_NODE_DERIVATION_VERIFY_MODE=local
- MORPH_NODE_START_IN_SEQUENCER_MODE=${START_IN_SEQUENCER_MODE:-true}
Comment on lines +188 to +193

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the startup and signer-construction symbols.
ast-grep outline node/cmd/node/main.go --items all --match 'L2NodeMain|initL1SequencerComponents'

# Inspect the private-key configuration path and signer creation.
rg -n -C 8 --glob '*.go' \
  'func initL1SequencerComponents\b|SequencerPrivateKey|SEQUENCER_PRIVATE_KEY|MORPH_NODE_SEQUENCER_PRIVATE_KEY' \
  node

# Confirm the effective node-0 configuration.
sed -n '174,200p' ops/docker/docker-compose-devnet.yml

Repository: morph-l2/morph

Length of output: 6204


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how main.go uses the signer and sequencer mode flags, and how initL1SequencerComponents builds the signer.
sed -n '59,180p' node/cmd/node/main.go
sed -n '373,480p' node/cmd/node/main.go

# Map definitions for signer construction helpers and local-verify / sequencer mode behavior.
rg -n -C 6 --glob '*.go' \
  'DerivationVerifyMode|START_IN_SEQUENCER_MODE|StartInSequencerMode|local-verify|local_verify|SequencerPrivateKey|New.*Signer|signer|NewSigner' \
  node

Repository: morph-l2/morph

Length of output: 48725


Keep node-0 from becoming a sequencer.

MORPH_NODE_SEQUENCER_PRIVATE_KEY makes initL1SequencerComponents create a LocalSigner. A non-nil signer skips derivation and starts in sequencer mode, so node-0 is not the intended local-verify P2P follower. Remove or override this env var for this service.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ops/docker/docker-compose-devnet.yml` around lines 188 - 193, Update the
node-0 service configuration near MORPH_NODE_DERIVATION_VERIFY_MODE so
MORPH_NODE_SEQUENCER_PRIVATE_KEY is removed or explicitly overridden, ensuring
initL1SequencerComponents does not create a LocalSigner and node-0 remains a
local-verify P2P follower rather than starting as a sequencer.

volumes:
- ".devnet/node0:${NODE_DATA_DIR}"
- "${PWD}/jwt-secret.txt:${JWT_SECRET_PATH}"
Expand Down
Loading