Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2cdfb07
update l2 RetryableClient
Dec 11, 2025
7c11f8c
fmt
Dec 11, 2025
98925c5
clean
Dec 11, 2025
c34bee9
fix mpt time
Dec 11, 2025
e39a59a
fix
Dec 15, 2025
105c335
Merge branch 'main' into mpt-switch
curryxbo Dec 15, 2025
a3d76f2
clean
Dec 17, 2025
8ae80bd
fix and test
Dec 22, 2025
d5f2d8d
clean
Dec 22, 2025
fa2307f
update
Jan 9, 2026
c38ea30
refactor
Jan 12, 2026
8b2a833
chore: remove mpt-switch-test from git tracking
Jan 12, 2026
c9f508d
chore: revert go-ethereum submodule to previous version
Jan 12, 2026
0968304
fix
Jan 12, 2026
f4d7b0c
rollback skip bls verify
Jan 13, 2026
32512d0
fix
Jan 13, 2026
b5e977c
clean
Jan 13, 2026
5f6ad17
clean
Jan 13, 2026
8631ca8
Merge branch 'main' into mpt-switch
Jan 13, 2026
5dea362
update geth version
Jan 13, 2026
772df41
Fix NewRetryableClient (#856)
curryxbo Jan 15, 2026
75181a7
Fix swtich time (#862)
curryxbo Jan 28, 2026
6120b0a
add mpt upgrade code for batch finalize
Kukoomomo Jan 29, 2026
89218d0
fix log level
Jan 29, 2026
0a63c6b
fix log level
Jan 29, 2026
a438b20
Force batch points around MPT fork to isolate the first post-fork blo…
FletcherMan Jan 30, 2026
8abcf7c
update submitter mpt config
Kukoomomo Jan 30, 2026
d6dd008
Merge branch 'mpt-switch' of github.com:morph-l2/morph into mpt-switch
Kukoomomo Jan 30, 2026
abcc805
update submitter mpt config
Kukoomomo Jan 30, 2026
57cf1e4
add bls key fork height flag (#863)
FletcherMan Feb 3, 2026
0f74ddb
Add retry for fetch eth_config (#865)
curryxbo Feb 4, 2026
0ec7bda
Merge branch 'main' into mpt-switch
curryxbo Feb 6, 2026
fd31a72
update geth version
Feb 6, 2026
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ contracts/mainnet.json
.env

# logs
*.log
*.log

# mpt-switch-test (local testing only)
ops/mpt-switch-test
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
################## update dependencies ####################
ETHEREUM_SUBMODULE_COMMIT_OR_TAG := morph-v2.1.0
ETHEREUM_TARGET_VERSION := v1.10.14-0.20251219060125-03910bc750a2
ETHEREUM_TARGET_VERSION := v1.10.14-0.20260206065010-7b2a9bb930e3
TENDERMINT_TARGET_VERSION := v0.3.3


ETHEREUM_MODULE_NAME := github.com/morph-l2/go-ethereum
TENDERMINT_MODULE_NAME := github.com/morph-l2/tendermint

Expand Down
2 changes: 1 addition & 1 deletion bindings/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.24.0

replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3.3

require github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2
require github.com/morph-l2/go-ethereum v1.10.14-0.20260206065010-7b2a9bb930e3

require (
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
Expand Down
4 changes: 2 additions & 2 deletions bindings/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2 h1:FUv9gtnvF+1AVrkoNGYbVOesi7E+STjdfD2mcqVaEY0=
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/morph-l2/go-ethereum v1.10.14-0.20260206065010-7b2a9bb930e3 h1:xGYu0xpTuIc2uN1A4Ig1I6ZN6CkqFZXlfXmPpyTtN3I=
github.com/morph-l2/go-ethereum v1.10.14-0.20260206065010-7b2a9bb930e3/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
Expand Down
2 changes: 1 addition & 1 deletion contracts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3.

require (
github.com/iden3/go-iden3-crypto v0.0.16
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2
github.com/morph-l2/go-ethereum v1.10.14-0.20260206065010-7b2a9bb930e3
github.com/stretchr/testify v1.10.0
)

Expand Down
4 changes: 2 additions & 2 deletions contracts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2 h1:FUv9gtnvF+1AVrkoNGYbVOesi7E+STjdfD2mcqVaEY0=
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/morph-l2/go-ethereum v1.10.14-0.20260206065010-7b2a9bb930e3 h1:xGYu0xpTuIc2uN1A4Ig1I6ZN6CkqFZXlfXmPpyTtN3I=
github.com/morph-l2/go-ethereum v1.10.14-0.20260206065010-7b2a9bb930e3/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
Expand Down
10 changes: 2 additions & 8 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,6 @@ github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phm
github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI=
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY=
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCroCIB1foO/yQ36swABL8aOVeDpgg=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKjNQOdARott/jP7BnUQ9Ah7KaZCvw=
Expand Down Expand Up @@ -1007,8 +1005,8 @@ github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8q
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morph-l2/go-ethereum v1.10.14-0.20251125061742-69718a9dcab9/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2 h1:FUv9gtnvF+1AVrkoNGYbVOesi7E+STjdfD2mcqVaEY0=
github.com/morph-l2/go-ethereum v1.10.14-0.20251219060125-03910bc750a2/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/morph-l2/go-ethereum v1.10.14-0.20260206063816-522b70a5f16f h1:e8gfduHc4AKlR0fD6J3HXveP2Gp4PMvN2UfA9CYEvEc=
github.com/morph-l2/go-ethereum v1.10.14-0.20260206063816-522b70a5f16f/go.mod h1:tiFPeidxjoCmLj18ne9H3KQdIGTCvRC30qlef06Fd9M=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
Expand Down Expand Up @@ -1245,8 +1243,6 @@ github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iL
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI=
github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y=
github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y=
Expand All @@ -1265,8 +1261,6 @@ github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtX
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6 h1:YdYsPAZ2pC6Tow/nPZOPQ96O3hm/ToAkGsPLzedXERk=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o=
Expand Down
84 changes: 84 additions & 0 deletions node/core/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ func (e *Executor) CalculateCapWithProposalBlock(currentBlockBytes []byte, curre
return false, err
}

// MPT fork: force batch points on the 1st and 2nd post-fork blocks, so the 1st post-fork block
// becomes a single-block batch: [H1, H2).
force, err := e.forceBatchPointForMPTFork(height, block.Timestamp, block.StateRoot, block.Hash)
if err != nil {
return false, err
}
if force {
e.logger.Info("MPT fork: force batch point", "height", height, "timestamp", block.Timestamp)
return true, nil
}

var exceeded bool
if e.isBatchUpgraded(block.Timestamp) {
exceeded, err = e.batchingCache.batchData.WillExceedCompressedSizeLimit(e.batchingCache.currentBlockContext, e.batchingCache.currentTxsPayload)
Expand All @@ -187,6 +198,79 @@ func (e *Executor) CalculateCapWithProposalBlock(currentBlockBytes []byte, curre
return exceeded, err
}

// forceBatchPointForMPTFork forces batch points at the 1st and 2nd block after the MPT fork time.
//
// Design goals:
// - Minimal change: only affects batch-point decision logic.
// - Stability: CalculateCapWithProposalBlock can be called multiple times at the same height; return must be consistent.
// - Performance: after handling (or skipping beyond) the fork boundary, no more HeaderByNumber calls are made.
func (e *Executor) forceBatchPointForMPTFork(height uint64, blockTime uint64, stateRoot common.Hash, blockHash common.Hash) (bool, error) {
// If we already decided to force at this height, keep returning true without extra RPCs.
if e.mptForkForceHeight == height && height != 0 {
return true, nil
}
// If fork boundary is already handled and this isn't a forced height, fast exit.
if e.mptForkStage >= 2 {
return false, nil
}

// Ensure we have fork time cached (0 means disabled).
if e.mptForkTime == 0 {
e.mptForkTime = e.l2Client.MPTForkTime()
}
forkTime := e.mptForkTime
if forkTime == 0 || blockTime < forkTime {
return false, nil
}
if height == 0 {
return false, nil
}

// Check parent block time to detect the 1st post-fork block (H1).
parent, err := e.l2Client.HeaderByNumber(context.Background(), big.NewInt(int64(height-1)))
if err != nil {
return false, err
}
if parent.Time < forkTime {
// Log H1 (the 1st post-fork block) state root
// This stateRoot is intended to be used as the Rollup contract "genesis state root"
// when we reset/re-initialize the genesis state root during the MPT upgrade.
e.logger.Info(
"MPT_FORK_H1_GENESIS_STATE_ROOT",
"height", height,
"timestamp", blockTime,
"forkTime", forkTime,
"stateRoot", stateRoot.Hex(),
"blockHash", blockHash.Hex(),
)
e.mptForkStage = 1
e.mptForkForceHeight = height
return true, nil
}

// If parent is already post-fork, we may be at the 2nd post-fork block (H2) or later.
if height < 2 {
// We cannot be H2; mark done to avoid future calls.
e.mptForkStage = 2
return false, nil
}

grandParent, err := e.l2Client.HeaderByNumber(context.Background(), big.NewInt(int64(height-2)))
if err != nil {
return false, err
}
if grandParent.Time < forkTime {
// This is H2 (2nd post-fork block).
e.mptForkStage = 2
e.mptForkForceHeight = height
return true, nil
}

// Beyond H2: nothing to do (can't retroactively fix). Mark done for performance.
e.mptForkStage = 2
return false, nil
}

func (e *Executor) AppendBlsData(height int64, batchHash []byte, data l2node.BlsData) error {
if len(batchHash) != 32 {
return fmt.Errorf("wrong batchHash length. expected: 32, actual: %d", len(batchHash))
Expand Down
16 changes: 16 additions & 0 deletions node/core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (

type Config struct {
L2 *types.L2Config `json:"l2"`
L2Next *types.L2Config `json:"l2_next,omitempty"` // optional, for geth upgrade switch
L2CrossDomainMessengerAddress common.Address `json:"cross_domain_messenger_address"`
SequencerAddress common.Address `json:"sequencer_address"`
GovAddress common.Address `json:"gov_address"`
Expand All @@ -46,6 +47,7 @@ type Config struct {
func DefaultConfig() *Config {
return &Config{
L2: new(types.L2Config),
L2Next: nil, // optional, only for upgrade switch
Logger: tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)),
MaxL1MessageNumPerBlock: 100,
L2CrossDomainMessengerAddress: predeploys.L2CrossDomainMessengerAddr,
Expand Down Expand Up @@ -126,6 +128,16 @@ func (c *Config) SetCliContext(ctx *cli.Context) error {
c.L2.EngineAddr = l2EngineAddr
c.L2.JwtSecret = secret

// L2Next is optional - only for upgrade switch (e.g., ZK to MPT)
l2NextEthAddr := ctx.GlobalString(flags.L2NextEthAddr.Name)
l2NextEngineAddr := ctx.GlobalString(flags.L2NextEngineAddr.Name)
if l2NextEthAddr != "" && l2NextEngineAddr != "" {
c.L2Next = &types.L2Config{
EthAddr: l2NextEthAddr,
EngineAddr: l2NextEngineAddr,
JwtSecret: secret, // same secret
}
}
if ctx.GlobalIsSet(flags.MaxL1MessageNumPerBlock.Name) {
c.MaxL1MessageNumPerBlock = ctx.GlobalUint64(flags.MaxL1MessageNumPerBlock.Name)
if c.MaxL1MessageNumPerBlock == 0 {
Expand Down Expand Up @@ -161,6 +173,10 @@ func (c *Config) SetCliContext(ctx *cli.Context) error {
c.DevSequencer = ctx.GlobalBool(flags.DevSequencer.Name)
}

if ctx.GlobalIsSet(flags.BlsKeyCheckForkHeight.Name) {
c.BlsKeyCheckForkHeight = ctx.GlobalUint64(flags.BlsKeyCheckForkHeight.Name)
}

// setup batch upgrade index and fork heights
switch {
case ctx.GlobalIsSet(flags.MainnetFlag.Name):
Expand Down
76 changes: 70 additions & 6 deletions node/core/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ type Executor struct {
rollupABI *abi.ABI
batchingCache *BatchingCache

// MPT fork handling: force batch points at the 1st and 2nd block after fork.
// This state machine exists to avoid repeated HeaderByNumber calls after the fork is handled,
// while keeping results stable if CalculateCapWithProposalBlock is called multiple times at the same height.
mptForkTime uint64 // cached from geth eth_config.morph.mptForkTime (0 means disabled/unknown)
mptForkStage uint8 // 0: not handled, 1: forced H1, 2: done (forced H2 or skipped beyond H2)
mptForkForceHeight uint64 // if equals current height, must return true (stability across multiple calls)

logger tmlog.Logger
metrics *Metrics
}
Expand All @@ -71,6 +78,7 @@ func getNextL1MsgIndex(client *types.RetryableClient) (uint64, error) {
func NewExecutor(newSyncFunc NewSyncerFunc, config *Config, tmPubKey crypto.PubKey) (*Executor, error) {
logger := config.Logger
logger = logger.With("module", "executor")
// L2 geth endpoint (required - current geth)
aClient, err := authclient.DialContext(context.Background(), config.L2.EngineAddr, config.L2.JwtSecret)
if err != nil {
return nil, err
Expand All @@ -80,7 +88,31 @@ func NewExecutor(newSyncFunc NewSyncerFunc, config *Config, tmPubKey crypto.PubK
return nil, err
}

l2Client := types.NewRetryableClient(aClient, eClient, config.Logger)
// L2Next endpoint (optional - for upgrade switch)
var aNextClient *authclient.Client
var eNextClient *ethclient.Client
if config.L2Next != nil && config.L2Next.EngineAddr != "" && config.L2Next.EthAddr != "" {
aNextClient, err = authclient.DialContext(context.Background(), config.L2Next.EngineAddr, config.L2Next.JwtSecret)
if err != nil {
return nil, err
}
eNextClient, err = ethclient.Dial(config.L2Next.EthAddr)
if err != nil {
return nil, err
}
Comment on lines +91 to +102
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Close aNextClient when eNextClient dial fails.

Line 100 returns on error but leaves the auth client open.

🧹 Proposed fix
eNextClient, err = ethclient.Dial(config.L2Next.EthAddr)
if err != nil {
+	aNextClient.Close()
	return nil, err
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// L2Next endpoint (optional - for upgrade switch)
var aNextClient *authclient.Client
var eNextClient *ethclient.Client
if config.L2Next != nil && config.L2Next.EngineAddr != "" && config.L2Next.EthAddr != "" {
aNextClient, err = authclient.DialContext(context.Background(), config.L2Next.EngineAddr, config.L2Next.JwtSecret)
if err != nil {
return nil, err
}
eNextClient, err = ethclient.Dial(config.L2Next.EthAddr)
if err != nil {
return nil, err
}
// L2Next endpoint (optional - for upgrade switch)
var aNextClient *authclient.Client
var eNextClient *ethclient.Client
if config.L2Next != nil && config.L2Next.EngineAddr != "" && config.L2Next.EthAddr != "" {
aNextClient, err = authclient.DialContext(context.Background(), config.L2Next.EngineAddr, config.L2Next.JwtSecret)
if err != nil {
return nil, err
}
eNextClient, err = ethclient.Dial(config.L2Next.EthAddr)
if err != nil {
aNextClient.Close()
return nil, err
}
🤖 Prompt for AI Agents
In `@node/core/executor.go` around lines 91 - 102, When dialing the L2Next
clients, if authclient.DialContext succeeds (aNextClient is non-nil) but
ethclient.Dial (eNextClient) fails, ensure you close the opened aNextClient
before returning the error; update the block that calls authclient.DialContext
and ethclient.Dial in executor.go to call aNextClient.Close() (or the
appropriate Close/Disconnect method on authclient.Client) just prior to
returning the error from ethclient.Dial so the auth client is not leaked.

logger.Info("L2Next geth configured (upgrade switch enabled)", "engineAddr", config.L2Next.EngineAddr, "ethAddr", config.L2Next.EthAddr)
} else {
logger.Info("L2Next geth not configured (no upgrade switch)")
}

// Fetch geth config at startup (with retry to wait for geth)
gethCfg, err := types.FetchGethConfigWithRetry(config.L2.EthAddr, logger)
if err != nil {
return nil, fmt.Errorf("failed to fetch geth config: %w", err)
}
logger.Info("Geth config fetched", "switchTime", gethCfg.SwitchTime, "useZktrie", gethCfg.UseZktrie)

l2Client := types.NewRetryableClient(aClient, eClient, aNextClient, eNextClient, gethCfg.SwitchTime, logger)
index, err := getNextL1MsgIndex(l2Client)
if err != nil {
return nil, err
Expand Down Expand Up @@ -123,6 +155,7 @@ func NewExecutor(newSyncFunc NewSyncerFunc, config *Config, tmPubKey crypto.PubK
batchingCache: NewBatchingCache(),
UpgradeBatchTime: config.UpgradeBatchTime,
blsKeyCheckForkHeight: config.BlsKeyCheckForkHeight,
mptForkTime: l2Client.MPTForkTime(),
logger: logger,
metrics: PrometheusMetrics("morphnode"),
}
Expand Down Expand Up @@ -283,16 +316,39 @@ func (e *Executor) DeliverBlock(txs [][]byte, metaData []byte, consensusData l2n
}

if wrappedBlock.Number <= height {
e.logger.Info("ignore it, the block was delivered", "block number", wrappedBlock.Number)
if e.devSequencer {
return nil, consensusData.ValidatorSet, nil
e.logger.Info("block already delivered by geth (via P2P sync)", "block_number", wrappedBlock.Number)
// Even if block was already delivered (e.g., synced via P2P), we still need to check
// if MPT switch should happen, otherwise sentry nodes won't switch to the correct geth.
e.l2Client.EnsureSwitched(context.Background(), wrappedBlock.Timestamp, wrappedBlock.Number)

// After switch, re-check height from the new geth client
// The block might exist in legacy geth but not in target geth after switch
newHeight, err := e.l2Client.BlockNumber(context.Background())
if err != nil {
return nil, nil, err
}
if wrappedBlock.Number > newHeight {
e.logger.Info("block not in target geth after switch, need to deliver",
"block_number", wrappedBlock.Number,
"old_height", height,
"new_height", newHeight)
// Update height and continue to deliver the block
height = newHeight
} else {
if e.devSequencer {
return nil, consensusData.ValidatorSet, nil
}
return e.getParamsAndValsAtHeight(int64(wrappedBlock.Number))
}
return e.getParamsAndValsAtHeight(int64(wrappedBlock.Number))
}

// We only accept the continuous blocks for now.
// It acts like full sync. Snap sync is not enabled until the Geth enables snapshot with zkTrie
if wrappedBlock.Number > height+1 {
e.logger.Error("!!! CRITICAL: Geth is behind - node BLOCKED !!!",
"consensus_block", wrappedBlock.Number,
"geth_height", height,
"action", "Switch to MPT-compatible geth IMMEDIATELY")
return nil, nil, types.ErrWrongBlockNumber
}

Expand Down Expand Up @@ -324,7 +380,15 @@ func (e *Executor) DeliverBlock(txs [][]byte, metaData []byte, consensusData l2n
}
err = e.l2Client.NewL2Block(context.Background(), l2Block, batchHash)
if err != nil {
e.logger.Error("failed to NewL2Block", "error", err)
e.logger.Error("========================================")
e.logger.Error("CRITICAL: Failed to deliver block to geth!")
e.logger.Error("========================================")
e.logger.Error("failed to NewL2Block",
"error", err,
"block_number", l2Block.Number,
"block_timestamp", l2Block.Timestamp)
e.logger.Error("HINT: If this occurs after MPT upgrade, your geth node may not support MPT blocks. " +
"Please ensure you are running an MPT-compatible geth node.")
return nil, nil, err
}

Expand Down
Loading
Loading